tags) {
- return SHAPERECORD.shapeToImage(tags, shapeNum, fillStyles, lineStyles, numFillBits, numLineBits, shapeRecords);
+ return SHAPERECORD.shapeToImage(tags, shapeNum, fillStyles, lineStyles, /*numFillBits, numLineBits,*/ shapeRecords);
}
public RECT getBounds() {
diff --git a/trunk/src/com/jpexs/decompiler/flash/types/filters/BEVELFILTER.java b/trunk/src/com/jpexs/decompiler/flash/types/filters/BEVELFILTER.java
index 0061959c9..5ef751efa 100644
--- a/trunk/src/com/jpexs/decompiler/flash/types/filters/BEVELFILTER.java
+++ b/trunk/src/com/jpexs/decompiler/flash/types/filters/BEVELFILTER.java
@@ -17,6 +17,7 @@
package com.jpexs.decompiler.flash.types.filters;
import com.jpexs.decompiler.flash.types.RGBA;
+import java.awt.image.BufferedImage;
/**
* The Bevel filter creates a smooth bevel on display list objects.
@@ -80,4 +81,15 @@ public class BEVELFILTER extends FILTER {
public BEVELFILTER() {
super(3);
}
+
+ @Override
+ public BufferedImage apply(BufferedImage src) {
+ int type = Filtering.INNER;
+ if (onTop && !innerShadow) {
+ type = Filtering.FULL;
+ } else if (!innerShadow) {
+ type = Filtering.OUTER;
+ }
+ return Filtering.bevel(src, (int) blurX, (int) blurY, strength, type, highlightColor.toColor(), shadowColor.toColor(), (int) (angle * 180 / Math.PI), (float) distance, knockout, passes);
+ }
}
diff --git a/trunk/src/com/jpexs/decompiler/flash/types/filters/BLURFILTER.java b/trunk/src/com/jpexs/decompiler/flash/types/filters/BLURFILTER.java
index cb546b26a..5c3cae7a3 100644
--- a/trunk/src/com/jpexs/decompiler/flash/types/filters/BLURFILTER.java
+++ b/trunk/src/com/jpexs/decompiler/flash/types/filters/BLURFILTER.java
@@ -16,6 +16,8 @@
*/
package com.jpexs.decompiler.flash.types.filters;
+import java.awt.image.BufferedImage;
+
/**
* Blur filter based on a sub-pixel precise median filter
*
@@ -39,4 +41,9 @@ public class BLURFILTER extends FILTER {
public BLURFILTER() {
super(1);
}
+
+ @Override
+ public BufferedImage apply(BufferedImage src) {
+ return Filtering.blur(src, (int) blurX, (int) blurY, passes);
+ }
}
diff --git a/trunk/src/com/jpexs/decompiler/flash/types/filters/BlendComposite.java b/trunk/src/com/jpexs/decompiler/flash/types/filters/BlendComposite.java
new file mode 100644
index 000000000..a208e9cbb
--- /dev/null
+++ b/trunk/src/com/jpexs/decompiler/flash/types/filters/BlendComposite.java
@@ -0,0 +1,336 @@
+/*
+ * Copyright (C) 2013 JPEXS
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package com.jpexs.decompiler.flash.types.filters;
+
+import java.awt.Composite;
+import java.awt.CompositeContext;
+import java.awt.RenderingHints;
+import java.awt.image.ColorModel;
+import java.awt.image.DataBuffer;
+import java.awt.image.DirectColorModel;
+import java.awt.image.Raster;
+import java.awt.image.RasterFormatException;
+import java.awt.image.WritableRaster;
+
+/**
+ *
+ * @author JPEXS
+ */
+public final class BlendComposite implements Composite {
+
+ public enum BlendingMode {
+
+ LAYER, //TODO!
+ DARKEN,
+ MULTIPLY,
+ LIGHTEN,
+ SCREEN,
+ OVERLAY,
+ HARD_LIGHT,
+ ADD,
+ SUBTRACT,
+ DIFFERENCE,
+ INVERT,
+ ALPHA,
+ ERASE
+ }
+ public static final BlendComposite Alpha = new BlendComposite(BlendingMode.ALPHA);
+ public static final BlendComposite Erase = new BlendComposite(BlendingMode.ERASE);
+ public static final BlendComposite Invert = new BlendComposite(BlendingMode.INVERT);
+ public static final BlendComposite Multiply = new BlendComposite(BlendingMode.MULTIPLY);
+ public static final BlendComposite Screen = new BlendComposite(BlendingMode.SCREEN);
+ public static final BlendComposite Darken = new BlendComposite(BlendingMode.DARKEN);
+ public static final BlendComposite Lighten = new BlendComposite(BlendingMode.LIGHTEN);
+ public static final BlendComposite Overlay = new BlendComposite(BlendingMode.OVERLAY);
+ public static final BlendComposite HardLight = new BlendComposite(BlendingMode.HARD_LIGHT);
+ public static final BlendComposite Difference = new BlendComposite(BlendingMode.DIFFERENCE);
+ public static final BlendComposite Add = new BlendComposite(BlendingMode.ADD);
+ public static final BlendComposite Subtract = new BlendComposite(BlendingMode.SUBTRACT);
+ private final float alpha;
+ private final BlendingMode mode;
+
+ private BlendComposite(BlendingMode mode) {
+ this(mode, 1.0f);
+ }
+
+ private BlendComposite(BlendingMode mode, float alpha) {
+ this.mode = mode;
+
+ if (alpha < 0.0f || alpha > 1.0f) {
+ throw new IllegalArgumentException(
+ "alpha must be comprised between 0.0f and 1.0f");
+ }
+ this.alpha = alpha;
+ }
+
+ public float getAlpha() {
+ return alpha;
+ }
+
+ /**
+ * Returns the blending mode of this composite.
+ *
+ * @return the blending mode used by this object
+ */
+ public BlendingMode getMode() {
+ return mode;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int hashCode() {
+ return Float.floatToIntBits(alpha) * 31 + mode.ordinal();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof BlendComposite)) {
+ return false;
+ }
+
+ BlendComposite bc = (BlendComposite) obj;
+ return mode == bc.mode && alpha == bc.alpha;
+ }
+
+ private static boolean checkComponentsOrder(ColorModel cm) {
+ if (cm instanceof DirectColorModel
+ && cm.getTransferType() == DataBuffer.TYPE_INT) {
+ DirectColorModel directCM = (DirectColorModel) cm;
+
+ return directCM.getRedMask() == 0x00FF0000
+ && directCM.getGreenMask() == 0x0000FF00
+ && directCM.getBlueMask() == 0x000000FF
+ && (directCM.getNumComponents() != 4
+ || directCM.getAlphaMask() == 0xFF000000);
+ }
+
+ return false;
+ }
+
+ @Override
+ public CompositeContext createContext(ColorModel srcColorModel,
+ ColorModel dstColorModel,
+ RenderingHints hints) {
+ if (!checkComponentsOrder(srcColorModel)
+ || !checkComponentsOrder(dstColorModel)) {
+ throw new RasterFormatException("Incompatible color models");
+ }
+
+ return new BlendingContext(this);
+ }
+
+ private static final class BlendingContext implements CompositeContext {
+
+ private final Blender blender;
+ private final BlendComposite composite;
+
+ private BlendingContext(BlendComposite composite) {
+ this.composite = composite;
+ this.blender = Blender.getBlenderFor(composite);
+ }
+
+ @Override
+ public void dispose() {
+ }
+
+ @Override
+ public void compose(Raster src, Raster dstIn, WritableRaster dstOut) {
+ int width = Math.min(src.getWidth(), dstIn.getWidth());
+ int height = Math.min(src.getHeight(), dstIn.getHeight());
+
+ float alpha = composite.getAlpha();
+
+ int[] result = new int[4];
+ int[] srcPixel = new int[4];
+ int[] dstPixel = new int[4];
+ int[] srcPixels = new int[width];
+ int[] dstPixels = new int[width];
+
+
+ for (int y = 0; y < height; y++) {
+ src.getDataElements(0, y, width, 1, srcPixels);
+ dstIn.getDataElements(0, y, width, 1, dstPixels);
+ for (int x = 0; x < width; x++) {
+ int pixel = srcPixels[x];
+ srcPixel[0] = (pixel >> 16) & 0xFF;
+ srcPixel[1] = (pixel >> 8) & 0xFF;
+ srcPixel[2] = (pixel) & 0xFF;
+ srcPixel[3] = (pixel >> 24) & 0xFF;
+
+ pixel = dstPixels[x];
+ dstPixel[0] = (pixel >> 16) & 0xFF;
+ dstPixel[1] = (pixel >> 8) & 0xFF;
+ dstPixel[2] = (pixel) & 0xFF;
+ dstPixel[3] = (pixel >> 24) & 0xFF;
+
+ blender.blend(srcPixel, dstPixel, result);
+
+ dstPixels[x] = ((int) (dstPixel[3] + (result[3] - dstPixel[3]) * alpha) & 0xFF) << 24
+ | ((int) (dstPixel[0] + (result[0] - dstPixel[0]) * alpha) & 0xFF) << 16
+ | ((int) (dstPixel[1] + (result[1] - dstPixel[1]) * alpha) & 0xFF) << 8
+ | (int) (dstPixel[2] + (result[2] - dstPixel[2]) * alpha) & 0xFF;
+ }
+ dstOut.setDataElements(0, y, width, 1, dstPixels);
+ }
+ }
+ }
+
+ private static abstract class Blender {
+
+ public abstract void blend(int[] src, int[] dst, int[] result);
+
+ public static Blender getBlenderFor(BlendComposite composite) {
+ switch (composite.getMode()) {
+ case ADD:
+ return new Blender() {
+ @Override
+ public void blend(int[] src, int[] dst, int[] result) {
+ result[0] = Math.min(255, src[0] + dst[0]);
+ result[1] = Math.min(255, src[1] + dst[1]);
+ result[2] = Math.min(255, src[2] + dst[2]);
+ result[3] = Math.min(255, src[3] + dst[3]);
+ }
+ };
+ case INVERT:
+ return new Blender() {
+ @Override
+ public void blend(int[] src, int[] dst, int[] result) {
+ result[0] = 255 - dst[0];
+ result[1] = 255 - dst[1];
+ result[2] = 255 - dst[2];
+ result[3] = src[3];
+ }
+ };
+ case ALPHA:
+ return new Blender() {
+ @Override
+ public void blend(int[] src, int[] dst, int[] result) {
+ result[0] = src[0];
+ result[1] = src[1];
+ result[2] = src[2];
+ result[3] = dst[3]; //?
+ }
+ };
+ case ERASE:
+ return new Blender() {
+ @Override
+ public void blend(int[] src, int[] dst, int[] result) {
+ result[0] = src[0];
+ result[1] = src[1];
+ result[2] = src[2];
+ result[3] = 255 - dst[3]; //?
+ }
+ };
+
+ case DARKEN:
+ return new Blender() {
+ @Override
+ public void blend(int[] src, int[] dst, int[] result) {
+ result[0] = Math.min(src[0], dst[0]);
+ result[1] = Math.min(src[1], dst[1]);
+ result[2] = Math.min(src[2], dst[2]);
+ result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255);
+ }
+ };
+ case DIFFERENCE:
+ return new Blender() {
+ @Override
+ public void blend(int[] src, int[] dst, int[] result) {
+ result[0] = Math.abs(dst[0] - src[0]);
+ result[1] = Math.abs(dst[1] - src[1]);
+ result[2] = Math.abs(dst[2] - src[2]);
+ result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255);
+ }
+ };
+
+ case HARD_LIGHT:
+ return new Blender() {
+ @Override
+ public void blend(int[] src, int[] dst, int[] result) {
+ result[0] = src[0] < 128 ? dst[0] * src[0] >> 7
+ : 255 - ((255 - src[0]) * (255 - dst[0]) >> 7);
+ result[1] = src[1] < 128 ? dst[1] * src[1] >> 7
+ : 255 - ((255 - src[1]) * (255 - dst[1]) >> 7);
+ result[2] = src[2] < 128 ? dst[2] * src[2] >> 7
+ : 255 - ((255 - src[2]) * (255 - dst[2]) >> 7);
+ result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255);
+ }
+ };
+
+ case LIGHTEN:
+ return new Blender() {
+ @Override
+ public void blend(int[] src, int[] dst, int[] result) {
+ result[0] = Math.max(src[0], dst[0]);
+ result[1] = Math.max(src[1], dst[1]);
+ result[2] = Math.max(src[2], dst[2]);
+ result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255);
+ }
+ };
+ case MULTIPLY:
+ return new Blender() {
+ @Override
+ public void blend(int[] src, int[] dst, int[] result) {
+ result[0] = (src[0] * dst[0]) >> 8;
+ result[1] = (src[1] * dst[1]) >> 8;
+ result[2] = (src[2] * dst[2]) >> 8;
+ result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255);
+ }
+ };
+
+ case OVERLAY:
+ return new Blender() {
+ @Override
+ public void blend(int[] src, int[] dst, int[] result) {
+ result[0] = dst[0] < 128 ? dst[0] * src[0] >> 7
+ : 255 - ((255 - dst[0]) * (255 - src[0]) >> 7);
+ result[1] = dst[1] < 128 ? dst[1] * src[1] >> 7
+ : 255 - ((255 - dst[1]) * (255 - src[1]) >> 7);
+ result[2] = dst[2] < 128 ? dst[2] * src[2] >> 7
+ : 255 - ((255 - dst[2]) * (255 - src[2]) >> 7);
+ result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255);
+ }
+ };
+ case SCREEN:
+ return new Blender() {
+ @Override
+ public void blend(int[] src, int[] dst, int[] result) {
+ result[0] = 255 - ((255 - src[0]) * (255 - dst[0]) >> 8);
+ result[1] = 255 - ((255 - src[1]) * (255 - dst[1]) >> 8);
+ result[2] = 255 - ((255 - src[2]) * (255 - dst[2]) >> 8);
+ result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255);
+ }
+ };
+
+ case SUBTRACT:
+ return new Blender() {
+ @Override
+ public void blend(int[] src, int[] dst, int[] result) {
+ result[0] = Math.max(0, src[0] + dst[0] - 256);
+ result[1] = Math.max(0, src[1] + dst[1] - 256);
+ result[2] = Math.max(0, src[2] + dst[2] - 256);
+ result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255);
+ }
+ };
+ }
+ throw new IllegalArgumentException("Blender not implemented for "
+ + composite.getMode().name());
+ }
+ }
+}
diff --git a/trunk/src/com/jpexs/decompiler/flash/types/filters/COLORMATRIXFILTER.java b/trunk/src/com/jpexs/decompiler/flash/types/filters/COLORMATRIXFILTER.java
index 0f4cc836f..6456ceca2 100644
--- a/trunk/src/com/jpexs/decompiler/flash/types/filters/COLORMATRIXFILTER.java
+++ b/trunk/src/com/jpexs/decompiler/flash/types/filters/COLORMATRIXFILTER.java
@@ -16,6 +16,8 @@
*/
package com.jpexs.decompiler.flash.types.filters;
+import java.awt.image.BufferedImage;
+
/**
* Applies a color transformation on the pixels of a display list object
*
@@ -34,4 +36,16 @@ public class COLORMATRIXFILTER extends FILTER {
public COLORMATRIXFILTER() {
super(6);
}
+
+ @Override
+ public BufferedImage apply(BufferedImage src) {
+ float matrix2[][] = new float[4][4];
+ for (int y = 0; y < 4; y++) {
+ for (int x = 0; x < 4; x++) {
+ matrix2[y][x] = matrix[y * 5 + x];
+ }
+ }
+ //matrix2[4][4] = 1;
+ return Filtering.colorMatrix(src, matrix2);
+ }
}
diff --git a/trunk/src/com/jpexs/decompiler/flash/types/filters/CONVOLUTIONFILTER.java b/trunk/src/com/jpexs/decompiler/flash/types/filters/CONVOLUTIONFILTER.java
index de5cef6a0..4e036ba42 100644
--- a/trunk/src/com/jpexs/decompiler/flash/types/filters/CONVOLUTIONFILTER.java
+++ b/trunk/src/com/jpexs/decompiler/flash/types/filters/CONVOLUTIONFILTER.java
@@ -17,6 +17,7 @@
package com.jpexs.decompiler.flash.types.filters;
import com.jpexs.decompiler.flash.types.RGBA;
+import java.awt.image.BufferedImage;
/**
* Two-dimensional discrete convolution filter.
@@ -64,4 +65,17 @@ public class CONVOLUTIONFILTER extends FILTER {
public CONVOLUTIONFILTER() {
super(5);
}
+
+ @Override
+ public BufferedImage apply(BufferedImage src) {
+ int height = matrix.length;
+ int width = matrix[0].length;
+ float matrix2[] = new float[width * height];
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ matrix2[y * width + x] = matrix[x][y] * divisor + bias;
+ }
+ }
+ return Filtering.convolution(src, matrix2, width, height);
+ }
}
diff --git a/trunk/src/com/jpexs/decompiler/flash/types/filters/DROPSHADOWFILTER.java b/trunk/src/com/jpexs/decompiler/flash/types/filters/DROPSHADOWFILTER.java
index eda42fb14..c4493b7eb 100644
--- a/trunk/src/com/jpexs/decompiler/flash/types/filters/DROPSHADOWFILTER.java
+++ b/trunk/src/com/jpexs/decompiler/flash/types/filters/DROPSHADOWFILTER.java
@@ -17,6 +17,7 @@
package com.jpexs.decompiler.flash.types.filters;
import com.jpexs.decompiler.flash.types.RGBA;
+import java.awt.image.BufferedImage;
/**
* Drop shadow filter based on the same median filter as the blur filter
@@ -72,4 +73,9 @@ public class DROPSHADOWFILTER extends FILTER {
public DROPSHADOWFILTER() {
super(0);
}
+
+ @Override
+ public BufferedImage apply(BufferedImage src) {
+ return Filtering.dropShadow(src, (int) blurX, (int) blurY, (int) (angle * 180 / Math.PI), distance, dropShadowColor.toColor(), innerShadow, passes, strength, knockout);
+ }
}
diff --git a/trunk/src/com/jpexs/decompiler/flash/types/filters/FILTER.java b/trunk/src/com/jpexs/decompiler/flash/types/filters/FILTER.java
index d9d89aa1a..b38c61cd7 100644
--- a/trunk/src/com/jpexs/decompiler/flash/types/filters/FILTER.java
+++ b/trunk/src/com/jpexs/decompiler/flash/types/filters/FILTER.java
@@ -16,6 +16,8 @@
*/
package com.jpexs.decompiler.flash.types.filters;
+import java.awt.image.BufferedImage;
+
/**
* Bitmap filter
*
@@ -36,4 +38,8 @@ public class FILTER {
public FILTER(int id) {
this.id = id;
}
+
+ public BufferedImage apply(BufferedImage src) {
+ return src;
+ }
}
diff --git a/trunk/src/com/jpexs/decompiler/flash/types/filters/Filtering.java b/trunk/src/com/jpexs/decompiler/flash/types/filters/Filtering.java
new file mode 100644
index 000000000..4ac7a0e75
--- /dev/null
+++ b/trunk/src/com/jpexs/decompiler/flash/types/filters/Filtering.java
@@ -0,0 +1,490 @@
+/*
+ * Copyright (C) 2010-2013 JPEXS
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package com.jpexs.decompiler.flash.types.filters;
+
+import java.awt.AlphaComposite;
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.LinearGradientPaint;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.RenderingHints;
+import java.awt.geom.AffineTransform;
+import java.awt.image.BandCombineOp;
+import java.awt.image.BufferedImage;
+import java.awt.image.BufferedImageOp;
+import java.awt.image.ConvolveOp;
+import java.awt.image.Kernel;
+import java.awt.image.Raster;
+import java.awt.image.WritableRaster;
+
+/**
+ *
+ * @author JPEXS
+ */
+public class Filtering {
+
+ public static final int INNER = 1;
+ public static final int OUTER = 2;
+ public static final int FULL = 3;
+
+ private static void boxBlurHorizontal(int[] pixels, int mask[], int w, int h, int radius) {
+ int index = 0;
+ int[] newColors = new int[w];
+
+ for (int y = 0; y < h; y++) {
+ int hits = 0;
+ long r = 0;
+ long g = 0;
+ long b = 0;
+ long a = 0;
+ for (int x = -radius; x < w; x++) {
+ int oldPixel = x - radius - 1;
+ if (oldPixel >= 0) {
+
+ int color = pixels[index + oldPixel];
+ if ((mask == null) || (((mask[index + oldPixel] >> 24) & 0xff) > 0)) {
+ if (color != 0) {
+ a -= (color >> 24) & 0xff;
+ r -= ((color >> 16) & 0xff);
+ g -= ((color >> 8) & 0xff);
+ b -= ((color) & 0xff);
+
+ }
+ hits--;
+ }
+ }
+
+ int newPixel = x + radius;
+ if (newPixel < w) {
+ int color = pixels[index + newPixel];
+ if ((mask == null) || (((mask[index + newPixel] >> 24) & 0xff) > 0)) {
+ if (color != 0) {
+ a += (color >> 24) & 0xff;
+ r += ((color >> 16) & 0xff);
+ g += ((color >> 8) & 0xff);
+ b += ((color) & 0xff);
+ }
+ hits++;
+ }
+ }
+
+ if (x >= 0) {
+ if ((mask == null) || (((mask[index + x] >> 24) & 0xff) > 0)) {
+ if (hits == 0) {
+ newColors[x] = 0;
+ } else {
+ newColors[x] = new Color((int) (r / hits) & 0xff, (int) (g / hits) & 0xff, (int) (b / hits) & 0xff, (int) (a / hits)).getRGB();
+
+ }
+ } else {
+ newColors[x] = 0;
+ }
+ }
+ }
+
+ for (int x = 0; x < w; x++) {
+ pixels[index + x] = newColors[x];
+ }
+
+ index += w;
+ }
+ }
+
+ private static void boxBlurVertical(int[] pixels, int mask[], int w, int h, int radius) {
+ int[] newColors = new int[h];
+ int oldPixelOffset = -(radius + 1) * w;
+ int newPixelOffset = (radius) * w;
+
+ for (int x = 0; x < w; x++) {
+ int hits = 0;
+ long r = 0;
+ long g = 0;
+ long b = 0;
+ long a = 0;
+ int index = -radius * w + x;
+ for (int y = -radius; y < h; y++) {
+ int oldPixel = y - radius - 1;
+ if (oldPixel >= 0) {
+ int color = pixels[index + oldPixelOffset];
+ if ((mask == null) || (((mask[index + oldPixelOffset] >> 24) & 0xff) > 0)) {
+ if (color != 0) {
+ a -= (color >> 24) & 0xff;
+ r -= ((color >> 16) & 0xff);
+ g -= ((color >> 8) & 0xff);
+ b -= ((color) & 0xff);
+
+ }
+ hits--;
+ }
+
+ }
+
+ int newPixel = y + radius;
+ if (newPixel < h) {
+ if ((mask == null) || (((mask[index + newPixelOffset] >> 24) & 0xff) > 0)) {
+ int color = pixels[index + newPixelOffset];
+ if (color != 0) {
+ a += (color >> 24) & 0xff;
+ r += ((color >> 16) & 0xff);
+ g += ((color >> 8) & 0xff);
+ b += ((color) & 0xff);
+
+
+ }
+ hits++;
+ }
+ }
+
+ if (y >= 0) {
+ if ((mask == null) || (((mask[y * w + x] >> 24) & 0xff) > 0)) {
+ if (hits == 0) {
+ newColors[y] = 0;
+ } else {
+ newColors[y] = new Color((int) (r / hits) & 0xff, (int) (g / hits) & 0xff, (int) (b / hits) & 0xff, (int) (a / hits) & 0xff).getRGB();
+ }
+ } else {
+ newColors[y] = 0;
+ }
+ }
+
+ index += w;
+ }
+
+ for (int y = 0; y < h; y++) {
+ pixels[y * w + x] = newColors[y];
+ }
+ }
+ }
+
+ private static void premultiply(int p[]) {
+ int length = p.length;
+ int offset = 0;
+ length += offset;
+ for (int i = offset; i < length; i++) {
+ int rgb = p[i];
+ int a = rgb >> 24 & 0xff;
+ int r = rgb >> 16 & 0xff;
+ int g = rgb >> 8 & 0xff;
+ int b = rgb & 0xff;
+ float f = (float) a * 0.003921569F;
+ r = (int) ((float) r * f);
+ g = (int) ((float) g * f);
+ b = (int) ((float) b * f);
+ p[i] = a << 24 | r << 16 | g << 8 | b;
+ }
+ }
+
+ private static void unpremultiply(int p[]) {
+ int length = p.length;
+ int offset = 0;
+ length += offset;
+ for (int i = offset; i < length; i++) {
+ int rgb = p[i];
+ int a = rgb >> 24 & 0xff;
+ int r = rgb >> 16 & 0xff;
+ int g = rgb >> 8 & 0xff;
+ int b = rgb & 0xff;
+ if (a == 0 || a == 255) {
+ continue;
+ }
+ float f = 255F / (float) a;
+ r = (int) ((float) r * f);
+ g = (int) ((float) g * f);
+ b = (int) ((float) b * f);
+ if (r > 255) {
+ r = 255;
+ }
+ if (g > 255) {
+ g = 255;
+ }
+ if (b > 255) {
+ b = 255;
+ }
+ p[i] = a << 24 | r << 16 | g << 8 | b;
+ }
+ }
+
+ public static BufferedImage blur(BufferedImage src, int hRadius, int vRadius, int iterations) {
+ return blur(src, hRadius, vRadius, iterations, null);
+ }
+
+ private static BufferedImage blur(BufferedImage src, int hRadius, int vRadius, int iterations, int[] mask) {
+ int width = src.getWidth();
+ int height = src.getHeight();
+
+ BufferedImage dst = new BufferedImage(width, height, src.getType());
+
+ int[] inPixels = getRGB(src, 0, 0, width, height);
+ premultiply(inPixels);
+
+ for (int i = 0; i < iterations; i++) {
+ boxBlurHorizontal(inPixels, mask, width, height, hRadius / 2);
+ boxBlurVertical(inPixels, mask, width, height, vRadius / 2);
+ }
+ unpremultiply(inPixels);
+ setRGB(dst, 0, 0, width, height, inPixels);
+ return dst;
+ }
+
+ public static BufferedImage bevel(BufferedImage src, int blurX, int blurY, float strength, int type, Color highlightColor, Color shadowColor, float angle, float distance, boolean knockout, int iterations) {
+ return gradientBevel(src, new Color[]{
+ highlightColor,
+ new Color(highlightColor.getRed(), highlightColor.getGreen(), highlightColor.getBlue(), 0),
+ new Color(shadowColor.getRed(), shadowColor.getGreen(), shadowColor.getBlue(), 0),
+ shadowColor
+ }, new float[]{0, 127f / 255f, 128f / 255f, 1}, blurX, blurY, strength, type, angle, distance, knockout, iterations);
+ }
+
+ public static BufferedImage gradientBevel(BufferedImage src, Color[] colors, float ratios[], int blurX, int blurY, float strength, int type, float angle, float distance, boolean knockout, int iterations) {
+ int width = src.getWidth();
+ int height = src.getHeight();
+ BufferedImage retImg = new BufferedImage(width, height, src.getType());
+ if (type == FULL) {
+ BufferedImage partIn = gradientBevel(src, colors, ratios, blurX, blurY, strength, INNER, angle, distance, true, iterations);
+ BufferedImage partOut = gradientBevel(src, colors, ratios, blurX, blurY, strength, OUTER, angle, distance, true, iterations);
+ Graphics2D g = (Graphics2D) retImg.getGraphics();
+ g.drawImage(partIn, 0, 0, null);
+ g.setComposite(AlphaComposite.SrcOver);
+ g.drawImage(partOut, 0, 0, null);
+ } else {
+ boolean inner = type == INNER;
+
+ int srcPixels[] = getRGB(src, 0, 0, width, height);
+ BufferedImage gradient = new BufferedImage(512, 1, BufferedImage.TYPE_INT_ARGB);
+ Graphics2D gg = (Graphics2D) gradient.getGraphics();
+ Point pnt1 = new Point(0, 0);
+ Point pnt2 = new Point(512, 0);
+ gg.setPaint(new LinearGradientPaint(inner ? pnt1 : pnt2, inner ? pnt2 : pnt1, ratios, colors));
+ gg.fill(new Rectangle(512, 1));
+ int gradientPixels[] = getRGB(gradient, 0, 0, gradient.getWidth(), gradient.getHeight());
+
+ BufferedImage hilightIm = dropShadow(src, blurX, blurY, angle, distance, Color.black, inner, iterations, strength, true);//new DropShadowFilter(blurX, blurY, strength, inner ? highlightColor : shadowColor, angle, distance, inner, true, iterations).filter(src);
+ BufferedImage shadowIm = dropShadow(src, blurX, blurY, angle + 180, distance, Color.black, inner, iterations, strength, true); //new DropShadowFilter(blurX, blurY, strength, inner ? shadowColor : highlightColor, angle + 180, distance, inner, true, iterations).filter(src);
+ int hilight[] = getRGB(hilightIm, 0, 0, width, height);
+ int shadow[] = getRGB(shadowIm, 0, 0, width, height);
+ for (int i = 0; i < srcPixels.length; i++) {
+ int ha = (hilight[i] >> 24) & 0xff;
+ int sa = (shadow[i] >> 24) & 0xff;
+ hilight[i] = gradientPixels[255 - ha];
+ shadow[i] = gradientPixels[256 + sa];
+ }
+ for (int i = 0; i < srcPixels.length; i++) {
+ int ah = (hilight[i] >> 24) & 0xff;
+ int as = (shadow[i] >> 24) & 0xff;
+ int ao = (srcPixels[i] >> 24) & 0xff;
+ if ((ao == 0) || ((ah > 0) && (as > 0))) {
+ hilight[i] = (hilight[i] & 0x00ffffff) + ((255 - as) << 24);
+ shadow[i] = (shadow[i] & 0x00ffffff) + ((255 - ah) << 24);
+ }
+ }
+ setRGB(shadowIm, 0, 0, width, height, shadow);
+ setRGB(hilightIm, 0, 0, width, height, hilight);
+ shadow = getRGB(shadowIm, 0, 0, width, height);
+ hilight = getRGB(hilightIm, 0, 0, width, height);
+ int ret[] = new int[width * height];
+ for (int i = 0; i < ret.length; i++) {
+ int ah = (hilight[i] >> 24) & 0xff;
+ int as = (shadow[i] >> 24) & 0xff;
+ if (as >= ah) {
+ ret[i] = (shadow[i] & 0x00ffffff) + ((as - ah) << 24);
+ } else {
+ ret[i] = (hilight[i] & 0x00ffffff) + ((ah - as) << 24);
+ }
+ }
+ setRGB(retImg, 0, 0, width, height, ret);
+ }
+ if (!knockout) {
+ Graphics2D g = (Graphics2D) retImg.getGraphics();
+ g.setComposite(AlphaComposite.DstOver);
+ g.drawImage(src, 0, 0, null);
+ }
+ return retImg;
+ }
+
+ public static BufferedImage glow(BufferedImage src, int blurX, int blurY, float strength, Color color, boolean inner, boolean knockout, int iterations) {
+ return dropShadow(src, blurX, blurY, 45, 0, color, inner, iterations, strength, knockout);
+ }
+
+ public static BufferedImage dropShadow(BufferedImage src, int blurX, int blurY, float angle, double distance, Color color, boolean inner, int iterations, float strength, boolean knockout) {
+ return gradientGlow(src, blurX, blurY, angle, distance, new Color[]{new Color(color.getRed(), color.getGreen(), color.getBlue(), 0), color}, new float[]{0, 1}, inner ? INNER : OUTER, iterations, strength, knockout);
+ }
+
+ public static BufferedImage gradientGlow(BufferedImage src, int blurX, int blurY, float angle, double distance, Color colors[], float ratios[], int type, int iterations, float strength, boolean knockout) {
+ int width = src.getWidth();
+ int height = src.getHeight();
+ BufferedImage retImg = new BufferedImage(width, height, src.getType());
+
+ if (type == FULL) {
+ BufferedImage partIn = gradientGlow(src, blurX, blurY, angle, distance, colors, ratios, INNER, iterations, strength, true);
+ BufferedImage partOut = gradientGlow(src, blurX, blurY, angle, distance, colors, ratios, OUTER, iterations, strength, true);
+ Graphics2D g = (Graphics2D) retImg.getGraphics();
+ g.drawImage(partIn, 0, 0, null);
+ g.setComposite(AlphaComposite.SrcOver);
+ g.drawImage(partOut, 0, 0, null);
+ } else {
+ boolean inner = type == INNER;
+
+ BufferedImage gradient = new BufferedImage(256, 1, BufferedImage.TYPE_INT_ARGB);
+ Graphics2D gg = (Graphics2D) gradient.getGraphics();
+ gg.setPaint(new LinearGradientPaint(new Point(0, 0), new Point(256, 0), ratios, colors));
+ gg.fill(new Rectangle(256, 1));
+ int gradientPixels[] = getRGB(gradient, 0, 0, gradient.getWidth(), gradient.getHeight());
+
+
+ double angleRad = angle / 180 * Math.PI;
+ double moveX = (distance * Math.cos(angleRad));
+ double moveY = (distance * Math.sin(angleRad));
+
+ int srcPixels[] = getRGB(src, 0, 0, width, height);
+ int revPixels[] = new int[srcPixels.length];
+ for (int i = 0; i < srcPixels.length; i++) {
+ int alpha = (srcPixels[i] >> 24) & 0xff;
+ alpha = 255 - alpha;
+ revPixels[i] = (srcPixels[i] & 0x00ffffff) + (alpha << 24);
+ }
+ int shadow[] = new int[srcPixels.length];
+ for (int i = 0; i < srcPixels.length; i++) {
+ int alpha = (srcPixels[i] >> 24) & 0xff;
+ if (inner) {
+ alpha = 255 - alpha;
+ }
+ shadow[i] = 0 + (Math.round(alpha * strength) << 24);
+ }
+ Color colorFirst = Color.black;
+ Color colorAlpha = new Color(0, 0, 0, 0);
+ shadow = moveRGB(width, height, shadow, moveX, moveY, inner ? colorFirst : colorAlpha);
+
+
+
+
+ setRGB(retImg, 0, 0, width, height, shadow);
+
+
+ retImg = blur(retImg, blurX, blurY, iterations, inner ? srcPixels : revPixels);//new BoxBlurFilter(blurX, blurY, iterations, inner ? srcPixels : revPixels).filter(ret);
+ shadow = getRGB(retImg, 0, 0, width, height);
+
+
+ for (int i = 0; i < shadow.length; i++) {
+ int a = (shadow[i] >> 24) & 0xff;
+ shadow[i] = gradientPixels[a];
+ }
+
+ for (int i = 0; i < shadow.length; i++) {
+ int srcA = (srcPixels[i] >> 24) & 0xff;
+ if (!inner) {
+ srcA = 255 - srcA;
+ }
+ int shadA = (shadow[i] >> 24) & 0xff;
+ shadow[i] = (shadow[i] & 0x00ffffff) + (Math.min(srcA, shadA) << 24);
+ }
+
+
+
+
+ setRGB(retImg, 0, 0, width, height, shadow);
+ }
+ if (!knockout) {
+ Graphics2D g = (Graphics2D) retImg.getGraphics();
+ //g.setComposite(inner ? AlphaComposite.DstOver : AlphaComposite.SrcOver);
+ g.setComposite(AlphaComposite.DstOver);
+ g.drawImage(src, 0, 0, null);
+ }
+
+ return retImg;
+ }
+
+ public static int[] getRGB(BufferedImage image, int x, int y, int width, int height) {
+ int type = image.getType();
+ if (type == BufferedImage.TYPE_INT_ARGB || type == BufferedImage.TYPE_INT_RGB) {
+ return (int[]) image.getRaster().getDataElements(x, y, width, height, null);
+ }
+ return image.getRGB(x, y, width, height, null, 0, width);
+ }
+
+ public static void setRGB(BufferedImage image, int x, int y, int width, int height, int[] pixels) {
+ int type = image.getType();
+ if (type == BufferedImage.TYPE_INT_ARGB || type == BufferedImage.TYPE_INT_RGB) {
+ image.getRaster().setDataElements(x, y, width, height, pixels);
+ } else {
+ image.setRGB(x, y, width, height, pixels, 0, width);
+ }
+ }
+
+ private static int[] moveRGB(int width, int height, int rgb[], double deltaX, double deltaY, Color fill) {
+ BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
+ setRGB(img, 0, 0, width, height, rgb);
+ BufferedImage retImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
+ Graphics2D g = (Graphics2D) retImg.getGraphics();
+ g.setPaint(fill);
+ g.fillRect(0, 0, width, height);
+ g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
+ g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
+ g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+
+ g.setTransform(AffineTransform.getTranslateInstance(deltaX, deltaY));
+ g.setComposite(AlphaComposite.Src);
+ g.drawImage(img, 0, 0, null);
+ return getRGB(retImg, 0, 0, width, height);
+ }
+
+ public static BufferedImage convolution(BufferedImage src, float matrix[], int w, int h) {
+ BufferedImage dst = new BufferedImage(src.getWidth(), src.getHeight(), src.getType());
+ BufferedImageOp op = new ConvolveOp(new Kernel(w, h, matrix), ConvolveOp.EDGE_ZERO_FILL, new RenderingHints(null));
+ op.filter(src, dst);
+ return dst;
+ }
+
+ public static BufferedImage colorMatrix(BufferedImage src, float[][] matrix) {
+ BandCombineOp changeColors = new BandCombineOp(matrix, new RenderingHints(null));
+ Raster sourceRaster = src.getRaster();
+ WritableRaster displayRaster = sourceRaster.createCompatibleWritableRaster();
+ changeColors.filter(sourceRaster, displayRaster);
+ return new BufferedImage(src.getColorModel(), displayRaster, true, null);
+ }
+
+ private static int cut(double val) {
+ int i = (int) val;
+ if (i < 0) {
+ i = 0;
+ }
+ if (i > 255) {
+ i = 255;
+ }
+ return i;
+ }
+
+ public static BufferedImage colorEffect(BufferedImage src,
+ int redAddTerm, int greenAddTerm, int blueAddTerm, int alphaAddTerm,
+ int redMultTerm, int greenMultTerm, int blueMultTerm, int alphaMultTerm) {
+ BufferedImage dst = new BufferedImage(src.getWidth(), src.getHeight(), src.getType());
+ int rgb[] = getRGB(src, 0, 0, src.getWidth(), src.getHeight());
+ for (int i = 0; i < rgb.length; i++) {
+ int a = (rgb[i] >> 24) & 0xff;
+ int r = (rgb[i] >> 16) & 0xff;
+ int g = (rgb[i] >> 8) & 0xff;
+ int b = (rgb[i]) & 0xff;
+ r = Math.max(0, Math.min(((r * redMultTerm) / 256) + redAddTerm, 255));
+ g = Math.max(0, Math.min(((g * greenMultTerm) / 256) + greenAddTerm, 255));
+ b = Math.max(0, Math.min(((b * blueMultTerm) / 256) + blueAddTerm, 255));
+ a = Math.max(0, Math.min(((a * alphaMultTerm) / 256) + alphaAddTerm, 255));
+ rgb[i] = (a << 24) | (r << 16) | (g << 8) | (b);
+ }
+ setRGB(dst, 0, 0, src.getWidth(), src.getHeight(), rgb);
+ return dst;
+ }
+}
diff --git a/trunk/src/com/jpexs/decompiler/flash/types/filters/GLOWFILTER.java b/trunk/src/com/jpexs/decompiler/flash/types/filters/GLOWFILTER.java
index 660ac7306..da655b5d1 100644
--- a/trunk/src/com/jpexs/decompiler/flash/types/filters/GLOWFILTER.java
+++ b/trunk/src/com/jpexs/decompiler/flash/types/filters/GLOWFILTER.java
@@ -17,6 +17,7 @@
package com.jpexs.decompiler.flash.types.filters;
import com.jpexs.decompiler.flash.types.RGBA;
+import java.awt.image.BufferedImage;
/**
* Glow filter
@@ -64,4 +65,9 @@ public class GLOWFILTER extends FILTER {
public GLOWFILTER() {
super(2);
}
+
+ @Override
+ public BufferedImage apply(BufferedImage src) {
+ return Filtering.glow(src, (int) blurX, (int) blurY, strength, glowColor.toColor(), innerGlow, knockout, passes);
+ }
}
diff --git a/trunk/src/com/jpexs/decompiler/flash/types/filters/GRADIENTBEVELFILTER.java b/trunk/src/com/jpexs/decompiler/flash/types/filters/GRADIENTBEVELFILTER.java
index 61ee02500..94f19710a 100644
--- a/trunk/src/com/jpexs/decompiler/flash/types/filters/GRADIENTBEVELFILTER.java
+++ b/trunk/src/com/jpexs/decompiler/flash/types/filters/GRADIENTBEVELFILTER.java
@@ -17,6 +17,10 @@
package com.jpexs.decompiler.flash.types.filters;
import com.jpexs.decompiler.flash.types.RGBA;
+import java.awt.Color;
+import java.awt.image.BufferedImage;
+import java.util.ArrayList;
+import java.util.List;
/**
* Bevel filter with gradient instead of single color
@@ -77,4 +81,29 @@ public class GRADIENTBEVELFILTER extends FILTER {
public GRADIENTBEVELFILTER() {
super(7);
}
+
+ @Override
+ public BufferedImage apply(BufferedImage src) {
+ List colors = new ArrayList();
+ List ratios = new ArrayList();
+ for (int i = 0; i < gradientColors.length; i++) {
+ if ((i > 0) && (gradientRatio[i - 1] == gradientRatio[i])) {
+ continue;
+ }
+ colors.add(gradientColors[i].toColor());
+ ratios.add(gradientRatio[i] / 255f);
+ }
+ int type = Filtering.INNER;
+ if (onTop && !innerShadow) {
+ type = Filtering.FULL;
+ } else if (!innerShadow) {
+ type = Filtering.OUTER;
+ }
+
+ float ratiosAr[] = new float[ratios.size()];
+ for (int i = 0; i < ratios.size(); i++) {
+ ratiosAr[i] = ratios.get(i);
+ }
+ return Filtering.gradientBevel(src, colors.toArray(new Color[colors.size()]), ratiosAr, (int) blurX, (int) blurY, strength, type, (int) (angle * 180 / Math.PI), (float) distance, knockout, passes);
+ }
}
diff --git a/trunk/src/com/jpexs/decompiler/flash/types/filters/GRADIENTGLOWFILTER.java b/trunk/src/com/jpexs/decompiler/flash/types/filters/GRADIENTGLOWFILTER.java
index 11ed9a9a4..557ec338a 100644
--- a/trunk/src/com/jpexs/decompiler/flash/types/filters/GRADIENTGLOWFILTER.java
+++ b/trunk/src/com/jpexs/decompiler/flash/types/filters/GRADIENTGLOWFILTER.java
@@ -17,6 +17,10 @@
package com.jpexs.decompiler.flash.types.filters;
import com.jpexs.decompiler.flash.types.RGBA;
+import java.awt.Color;
+import java.awt.image.BufferedImage;
+import java.util.ArrayList;
+import java.util.List;
/**
* Glow filter with gradient instead of single color
@@ -80,4 +84,29 @@ public class GRADIENTGLOWFILTER extends FILTER {
public GRADIENTGLOWFILTER() {
super(4);
}
+
+ @Override
+ public BufferedImage apply(BufferedImage src) {
+ List colors = new ArrayList();
+ List ratios = new ArrayList();
+ for (int i = 0; i < gradientColors.length; i++) {
+ if ((i > 0) && (gradientRatio[i - 1] == gradientRatio[i])) {
+ continue;
+ }
+ colors.add(gradientColors[i].toColor());
+ ratios.add(gradientRatio[i] / 255f);
+ }
+ int type = Filtering.INNER;
+ if (onTop && !innerShadow) {
+ type = Filtering.FULL;
+ } else if (!innerShadow) {
+ type = Filtering.OUTER;
+ }
+
+ float ratiosAr[] = new float[ratios.size()];
+ for (int i = 0; i < ratios.size(); i++) {
+ ratiosAr[i] = ratios.get(i);
+ }
+ return Filtering.gradientGlow(src, (int) blurX, (int) blurY, (int) (angle * 180 / Math.PI), distance, colors.toArray(new Color[colors.size()]), ratiosAr, type, passes, strength, knockout);
+ }
}
diff --git a/trunk/src/com/jpexs/decompiler/flash/types/shaperecords/CurvedEdgeRecord.java b/trunk/src/com/jpexs/decompiler/flash/types/shaperecords/CurvedEdgeRecord.java
index e4a945a4f..9bf4b88dd 100644
--- a/trunk/src/com/jpexs/decompiler/flash/types/shaperecords/CurvedEdgeRecord.java
+++ b/trunk/src/com/jpexs/decompiler/flash/types/shaperecords/CurvedEdgeRecord.java
@@ -16,6 +16,8 @@
*/
package com.jpexs.decompiler.flash.types.shaperecords;
+import com.jpexs.decompiler.flash.SWF;
+
/**
*
* @author JPEXS
@@ -37,7 +39,7 @@ public class CurvedEdgeRecord extends SHAPERECORD {
@Override
public String toSWG(int oldX, int oldY) {
- return "Q " + twipToPixel(oldX + controlDeltaX) + " " + twipToPixel(oldY + controlDeltaY) + " " + twipToPixel(oldX + controlDeltaX + anchorDeltaX) + " " + twipToPixel(oldY + controlDeltaY + anchorDeltaY);
+ return "Q " + SWF.twipToPixel(oldX + controlDeltaX) + " " + SWF.twipToPixel(oldY + controlDeltaY) + " " + SWF.twipToPixel(oldX + controlDeltaX + anchorDeltaX) + " " + SWF.twipToPixel(oldY + controlDeltaY + anchorDeltaY);
}
diff --git a/trunk/src/com/jpexs/decompiler/flash/types/shaperecords/SHAPERECORD.java b/trunk/src/com/jpexs/decompiler/flash/types/shaperecords/SHAPERECORD.java
index bbe0ccc24..2f7dcb686 100644
--- a/trunk/src/com/jpexs/decompiler/flash/types/shaperecords/SHAPERECORD.java
+++ b/trunk/src/com/jpexs/decompiler/flash/types/shaperecords/SHAPERECORD.java
@@ -16,6 +16,7 @@
*/
package com.jpexs.decompiler.flash.types.shaperecords;
+import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.tags.Tag;
import com.jpexs.decompiler.flash.tags.base.ImageTag;
import com.jpexs.decompiler.flash.tags.base.NeedsCharacters;
@@ -25,7 +26,6 @@ import com.jpexs.decompiler.flash.types.GRADIENT;
import com.jpexs.decompiler.flash.types.LINESTYLE;
import com.jpexs.decompiler.flash.types.LINESTYLE2;
import com.jpexs.decompiler.flash.types.LINESTYLEARRAY;
-import com.jpexs.decompiler.flash.types.MATRIX;
import com.jpexs.decompiler.flash.types.RECT;
import com.jpexs.decompiler.flash.types.RGB;
import com.jpexs.decompiler.flash.types.RGBA;
@@ -37,11 +37,13 @@ import java.awt.MultipleGradientPaint.CycleMethod;
import java.awt.Point;
import java.awt.RadialGradientPaint;
import java.awt.Rectangle;
+import java.awt.RenderingHints;
import java.awt.TexturePaint;
import java.awt.geom.AffineTransform;
import java.awt.geom.GeneralPath;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -54,9 +56,7 @@ import java.util.logging.Logger;
*/
public abstract class SHAPERECORD implements Cloneable, NeedsCharacters {
- public static float twipToPixel(int twip) {
- return ((float) twip) / 20.0f;
- }
+ private static final float DESCALE = 20; //20
@Override
public Set getNeededCharacters() {
@@ -75,7 +75,7 @@ public abstract class SHAPERECORD implements Cloneable, NeedsCharacters {
public void draw(int startX, int startY, Graphics2D g, int shapeNum) {
AffineTransform oldAf = g.getTransform();
- AffineTransform trans20 = AffineTransform.getScaleInstance(1 / 20.0, 1 / 20.0);
+ AffineTransform trans20 = AffineTransform.getScaleInstance(1 / DESCALE, 1 / DESCALE);
//g.setTransform(trans20);
boolean ok = false;
if (shapeNum == 4) {
@@ -108,9 +108,9 @@ public abstract class SHAPERECORD implements Cloneable, NeedsCharacters {
break;
}
if (joinStyle == BasicStroke.JOIN_MITER) {
- g.setStroke(new BasicStroke(lineStyle2.width / 20, capStyle, joinStyle, lineStyle2.miterLimitFactor));
+ g.setStroke(new BasicStroke(lineStyle2.width / DESCALE, capStyle, joinStyle, lineStyle2.miterLimitFactor));
} else {
- g.setStroke(new BasicStroke(lineStyle2.width / 20, capStyle, joinStyle));
+ g.setStroke(new BasicStroke(lineStyle2.width / DESCALE, capStyle, joinStyle));
}
ok = true;
}
@@ -118,7 +118,7 @@ public abstract class SHAPERECORD implements Cloneable, NeedsCharacters {
if (lineStyle == null) {
ok = false;
} else {
- g.setStroke(new BasicStroke(lineStyle.width / 20, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
+ g.setStroke(new BasicStroke(lineStyle.width / DESCALE, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
ok = true;
}
}
@@ -129,29 +129,11 @@ public abstract class SHAPERECORD implements Cloneable, NeedsCharacters {
g.setClip(null);
}
- private AffineTransform matrixToTransform(MATRIX mat) {
- return new AffineTransform(mat.getScaleXFloat(), mat.getRotateSkew0Float(),
- mat.getRotateSkew1Float(), mat.getScaleYFloat(),
- mat.translateX, mat.translateY);
- //mat.translateX,mat.translateY);
- /*AffineTransform move=AffineTransform.getTranslateInstance(mat.translateX, mat.translateY);
- AffineTransform rotate =AffineTransform.getRotateInstance(mat.getRotateSkew0Float(), mat.getRotateSkew1Float());
- AffineTransform scale=AffineTransform.getScaleInstance(mat.getScaleXFloat(), mat.getScaleYFloat());
- AffineTransform af=scale;
- AffineTransform scale20=AffineTransform.getScaleInstance(1/20.0, 1/20.0);
-
- //af.concatenate(move);
- //af.concatenate(scale20);
- af.concatenate(rotate);
- af.concatenate(scale);
- return af;*/
- }
-
public void fill(List tags, int startX, int startY, Graphics2D g, int shapeNum) {
AffineTransform oldAf = g.getTransform();
- AffineTransform trans20 = AffineTransform.getScaleInstance(1 / 20.0, 1 / 20.0);
+ AffineTransform trans20 = AffineTransform.getScaleInstance(1 / DESCALE, 1 / DESCALE);
g.setTransform(trans20);
- int maxRepeat = 200;
+ int maxRepeat = 10; //TODO:better handle gradient repeating
boolean ok = false;
switch (fillStyle0.fillStyleType) {
case FILLSTYLE.NON_SMOOTHED_REPEATING_BITMAP:
@@ -170,9 +152,9 @@ public abstract class SHAPERECORD implements Cloneable, NeedsCharacters {
}
if (image != null) {
g.setClip(toGeneralPath(startX, startY));
- AffineTransform btrans = matrixToTransform(fillStyle0.bitmapMatrix);
- btrans.preConcatenate(AffineTransform.getScaleInstance(1 / 20.0, 1 / 20.0));
- btrans.preConcatenate(AffineTransform.getTranslateInstance(startX / 20, startY / 20));
+ AffineTransform btrans = SWF.matrixToTransform(fillStyle0.bitmapMatrix);
+ btrans.preConcatenate(AffineTransform.getScaleInstance(1 / DESCALE, 1 / DESCALE));
+ btrans.preConcatenate(AffineTransform.getTranslateInstance(startX / DESCALE, startY / DESCALE));
g.setTransform(btrans);
BufferedImage img = image.getImage(tags);
g.setPaint(new TexturePaint(img, new Rectangle(img.getWidth(), img.getHeight())));
@@ -202,9 +184,9 @@ public abstract class SHAPERECORD implements Cloneable, NeedsCharacters {
GeneralPath focPath = toGeneralPath(startX, startY);
g.fill(focPath);
g.setClip(focPath);
- AffineTransform focTrans = matrixToTransform(fillStyle0.gradientMatrix);
- focTrans.preConcatenate(AffineTransform.getScaleInstance(1 / 20.0, 1 / 20.0));
- focTrans.preConcatenate(AffineTransform.getTranslateInstance(startX / 20, startY / 20));
+ AffineTransform focTrans = SWF.matrixToTransform(fillStyle0.gradientMatrix);
+ focTrans.preConcatenate(AffineTransform.getScaleInstance(1 / DESCALE, 1 / DESCALE));
+ focTrans.preConcatenate(AffineTransform.getTranslateInstance(startX / DESCALE, startY / DESCALE));
g.setTransform(focTrans);
CycleMethod cm = CycleMethod.NO_CYCLE;
if (fillStyle0.focalGradient.spreadMode == GRADIENT.SPREAD_PAD_MODE) {
@@ -242,9 +224,9 @@ public abstract class SHAPERECORD implements Cloneable, NeedsCharacters {
GeneralPath path = toGeneralPath(startX, startY);
g.fill(path);
g.setClip(path);
- AffineTransform trans = matrixToTransform(fillStyle0.gradientMatrix);
- trans.preConcatenate(AffineTransform.getScaleInstance(1 / 20.0, 1 / 20.0));
- trans.preConcatenate(AffineTransform.getTranslateInstance(startX / 20, startY / 20));
+ AffineTransform trans = SWF.matrixToTransform(fillStyle0.gradientMatrix);
+ trans.preConcatenate(AffineTransform.getScaleInstance(1 / DESCALE, 1 / DESCALE));
+ trans.preConcatenate(AffineTransform.getTranslateInstance(startX / DESCALE, startY / DESCALE));
g.setTransform(trans);
CycleMethod cmRad = CycleMethod.NO_CYCLE;
@@ -275,9 +257,9 @@ public abstract class SHAPERECORD implements Cloneable, NeedsCharacters {
GeneralPath pathLin = toGeneralPath(startX, startY);
g.fill(pathLin);
g.setClip(pathLin);
- AffineTransform transLin = matrixToTransform(fillStyle0.gradientMatrix);
- transLin.preConcatenate(AffineTransform.getScaleInstance(1 / 20.0, 1 / 20.0));
- transLin.preConcatenate(AffineTransform.getTranslateInstance(startX / 20, startY / 20));
+ AffineTransform transLin = SWF.matrixToTransform(fillStyle0.gradientMatrix);
+ transLin.preConcatenate(AffineTransform.getScaleInstance(1 / DESCALE, 1 / DESCALE));
+ transLin.preConcatenate(AffineTransform.getTranslateInstance(startX / DESCALE, startY / DESCALE));
g.setTransform(transLin);
CycleMethod cmLin = CycleMethod.NO_CYCLE;
@@ -367,7 +349,7 @@ public abstract class SHAPERECORD implements Cloneable, NeedsCharacters {
params += " stroke=\"" + ((shapeNum >= 3) ? lineStyle.colorA.toHexRGB() : lineStyle.color.toHexRGB()) + "\"";
}
if (useLineStyle2 && lineStyle2 != null) {
- params += " stroke-width=\"" + twipToPixel(lineStyle2.width) + "\"" + (lineStyle2.color != null ? " stroke=\"" + lineStyle2.color.toHexRGB() + "\"" : "");
+ params += " stroke-width=\"" + SWF.twipToPixel(lineStyle2.width) + "\"" + (lineStyle2.color != null ? " stroke=\"" + lineStyle2.color.toHexRGB() + "\"" : "");
}
String points = "";
int x = 0;
@@ -422,7 +404,7 @@ public abstract class SHAPERECORD implements Cloneable, NeedsCharacters {
return new RECT(min_x, max_x, min_y, max_y);
}
- private static List getPaths(RECT bounds, int shapeNum, FILLSTYLEARRAY fillStyles, LINESTYLEARRAY lineStylesList, int numFillBits, int numLineBits, List records) {
+ private static List getPaths(RECT bounds, int shapeNum, FILLSTYLEARRAY fillStyles, LINESTYLEARRAY lineStylesList, List records) {
List paths = new ArrayList();
Path path = new Path();
int x = 0;
@@ -441,18 +423,18 @@ public abstract class SHAPERECORD implements Cloneable, NeedsCharacters {
if (scr.stateNewStyles) {
fillStyles = scr.fillStyles;
lineStylesList = scr.lineStyles;
- numFillBits = scr.numFillBits;
- numLineBits = scr.numLineBits;
+ //numFillBits = scr.numFillBits;
+ //numLineBits = scr.numLineBits;
}
if (scr.stateFillStyle0) {
- if (scr.fillStyle0 == 0) {
+ if ((scr.fillStyle0 == 0) || (fillStyles == null)) {
path.fillStyle0 = null;
} else {
path.fillStyle0 = fillStyles.fillStyles[scr.fillStyle0 - 1];
}
}
if (scr.stateFillStyle1) {
- if (scr.fillStyle1 == 0) {
+ if ((scr.fillStyle1 == 0) || (fillStyles == null)) {
path.fillStyle1 = null;
} else {
path.fillStyle1 = fillStyles.fillStyles[scr.fillStyle1 - 1];
@@ -500,6 +482,9 @@ public abstract class SHAPERECORD implements Cloneable, NeedsCharacters {
paths.add(path);
List paths2 = new ArrayList();
for (Path p : paths) {
+ if ((p.fillStyle0 == null) && (p.fillStyle1 == null)) {
+ paths2.add(p);
+ }
if (p.fillStyle0 != null) {
paths2.add(p);
}
@@ -570,32 +555,63 @@ public abstract class SHAPERECORD implements Cloneable, NeedsCharacters {
public static String shapeToSVG(int shapeNum, FILLSTYLEARRAY fillStyles, LINESTYLEARRAY lineStylesList, int numFillBits, int numLineBits, List records) {
String ret = "";
RECT bounds = new RECT();
- List paths = getPaths(bounds, shapeNum, fillStyles, lineStylesList, numFillBits, numLineBits, records);
+ List paths = getPaths(bounds, shapeNum, fillStyles, lineStylesList, /*numFillBits, numLineBits,*/ records);
ret = "";
for (Path p : paths) {
ret += p.toSVG(shapeNum);
}
ret = " \n"
+ "\n"
- + "";
return ret;
}
+ private static HashMap cache = new HashMap();
- public static BufferedImage shapeToImage(List tags, int shapeNum, FILLSTYLEARRAY fillStyles, LINESTYLEARRAY lineStylesList, int numFillBits, int numLineBits, List records) {
+ public static BufferedImage shapeToImage(List tags, int shapeNum, FILLSTYLEARRAY fillStyles, LINESTYLEARRAY lineStylesList, List records) {
+ return shapeToImage(tags, shapeNum, fillStyles, lineStylesList, records, Color.black);
+ }
+
+ public static List shapeToPaths(List tags, int shapeNum, List records) {
RECT rect = new RECT();
- List paths = getPaths(rect, shapeNum, fillStyles, lineStylesList, numFillBits, numLineBits, records);
- BufferedImage ret = new BufferedImage(rect.getWidth() / 20 + 2, rect.getHeight() / 20 + 2, BufferedImage.TYPE_4BYTE_ABGR);
- Graphics2D g = (Graphics2D) ret.getGraphics();
+ List paths = getPaths(rect, shapeNum, null, null, records);
+ List ret = new ArrayList();
for (Path p : paths) {
- p.drawTo(tags, -rect.Xmin, -rect.Ymin, g, shapeNum);
+ ret.add(p.toGeneralPath(-rect.Xmin, -rect.Ymin));
}
return ret;
}
+ public static BufferedImage shapeToImage(List tags, int shapeNum, FILLSTYLEARRAY fillStyles, LINESTYLEARRAY lineStylesList, List records, Color defaultColor) {
+ String key = "shape_" + records.hashCode() + "_" + defaultColor.hashCode();
+ if (cache.containsKey(key)) {
+ return cache.get(key);
+ }
+ RECT rect = new RECT();
+ List paths = getPaths(rect, shapeNum, fillStyles, lineStylesList, /*numFillBits, numLineBits,*/ records);
+ BufferedImage ret = new BufferedImage(
+ //(int)((rect.Xmax-rect.Xmin)/DESCALE),(int)((rect.Ymax-rect.Ymin)/DESCALE)
+ (int) (rect.getWidth() / DESCALE + 2), (int) (rect.getHeight() / DESCALE + 2), BufferedImage.TYPE_INT_ARGB);
+ Graphics2D g = (Graphics2D) ret.getGraphics();
+ g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
+ g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
+ g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+ for (Path p : paths) {
+ if (p.fillStyle0 == null) {
+ p.fillStyle0 = new FILLSTYLE();
+ p.fillStyle0.fillStyleType = FILLSTYLE.SOLID;
+ p.fillStyle0.color = new RGB(defaultColor);
+ p.fillStyle0.colorA = new RGBA(defaultColor);
+ }
+ p.drawTo(tags, -rect.Xmin, -rect.Ymin/*-rect.Xmin, -rect.Ymin*/, g, shapeNum);
+ }
+ cache.put(key, ret);
+ return ret;
+ }
+
public abstract boolean isMove();
}
diff --git a/trunk/src/com/jpexs/decompiler/flash/types/shaperecords/StraightEdgeRecord.java b/trunk/src/com/jpexs/decompiler/flash/types/shaperecords/StraightEdgeRecord.java
index cecf43171..e9db41b07 100644
--- a/trunk/src/com/jpexs/decompiler/flash/types/shaperecords/StraightEdgeRecord.java
+++ b/trunk/src/com/jpexs/decompiler/flash/types/shaperecords/StraightEdgeRecord.java
@@ -16,6 +16,8 @@
*/
package com.jpexs.decompiler.flash.types.shaperecords;
+import com.jpexs.decompiler.flash.SWF;
+
/**
*
* @author JPEXS
@@ -38,11 +40,11 @@ public class StraightEdgeRecord extends SHAPERECORD {
@Override
public String toSWG(int oldX, int oldY) {
if (generalLineFlag) {
- return "L " + twipToPixel(oldX + deltaX) + " " + twipToPixel(oldY + deltaY);
+ return "L " + SWF.twipToPixel(oldX + deltaX) + " " + SWF.twipToPixel(oldY + deltaY);
} else if (vertLineFlag) {
- return "V " + twipToPixel(oldY + deltaY);
+ return "V " + SWF.twipToPixel(oldY + deltaY);
} else {
- return "H " + twipToPixel(oldX + deltaX);
+ return "H " + SWF.twipToPixel(oldX + deltaX);
}
}
diff --git a/trunk/src/com/jpexs/decompiler/flash/types/shaperecords/StyleChangeRecord.java b/trunk/src/com/jpexs/decompiler/flash/types/shaperecords/StyleChangeRecord.java
index 48d8f163b..b5f27260b 100644
--- a/trunk/src/com/jpexs/decompiler/flash/types/shaperecords/StyleChangeRecord.java
+++ b/trunk/src/com/jpexs/decompiler/flash/types/shaperecords/StyleChangeRecord.java
@@ -16,6 +16,7 @@
*/
package com.jpexs.decompiler.flash.types.shaperecords;
+import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.types.FILLSTYLEARRAY;
import com.jpexs.decompiler.flash.types.LINESTYLEARRAY;
import java.util.Set;
@@ -62,7 +63,7 @@ public class StyleChangeRecord extends SHAPERECORD {
@Override
public String toSWG(int oldX, int oldY) {
if (stateMoveTo) {
- return "M " + twipToPixel(moveDeltaX) + " " + twipToPixel(moveDeltaY);
+ return "M " + SWF.twipToPixel(moveDeltaX) + " " + SWF.twipToPixel(moveDeltaY);
}
return "";
}