mirror of
https://git.huckle.dev/Huckles-Minecraft-Archive/jpexs-decompiler.git
synced 2026-05-31 11:44:36 +00:00
#1156 9-slice scaling - better stroke scaling
This commit is contained in:
@@ -122,7 +122,7 @@ public class ShapeExporter {
|
||||
Matrix m = new Matrix();
|
||||
m.translate(-rect.Xmin, -rect.Ymin);
|
||||
m.scale(settings.zoom);
|
||||
st.toImage(0, 0, 0, new RenderContext(), img, false, m, new Matrix(), m, new CXFORMWITHALPHA());
|
||||
st.toImage(0, 0, 0, new RenderContext(), img, false, m, m, m, new CXFORMWITHALPHA());
|
||||
if (settings.mode == ShapeExportMode.PNG) {
|
||||
ImageHelper.write(img.getBufferedImage(), ImageFormat.PNG, file);
|
||||
} else {
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
package com.jpexs.decompiler.flash.exporters.commonshape;
|
||||
|
||||
import com.jpexs.decompiler.flash.types.RECT;
|
||||
import java.awt.Point;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
|
||||
/**
|
||||
@@ -64,8 +63,14 @@ public class ExportRectangle {
|
||||
}
|
||||
|
||||
public boolean contains(Point point) {
|
||||
int x = point.x;
|
||||
int y = point.y;
|
||||
double x = point.x;
|
||||
double y = point.y;
|
||||
return xMin <= x && xMax >= x && yMin <= y && yMax >= y;
|
||||
}
|
||||
|
||||
public boolean contains(java.awt.Point point) {
|
||||
double x = point.x;
|
||||
double y = point.y;
|
||||
return xMin <= x && xMax >= x && yMin <= y && yMax >= y;
|
||||
}
|
||||
|
||||
|
||||
@@ -83,6 +83,8 @@ public class BitmapExporter extends ShapeExporterBase {
|
||||
|
||||
private Stroke defaultStroke;
|
||||
|
||||
private Matrix strokeTransformation;
|
||||
|
||||
private class TransformedStroke implements Stroke {
|
||||
|
||||
/**
|
||||
@@ -134,9 +136,9 @@ public class BitmapExporter extends ShapeExporterBase {
|
||||
}
|
||||
}
|
||||
|
||||
public static void export(SWF swf, SHAPE shape, Color defaultColor, SerializableImage image, Matrix transformation, ColorTransform colorTransform) {
|
||||
public static void export(SWF swf, SHAPE shape, Color defaultColor, SerializableImage image, Matrix transformation, Matrix strokeTransformation, ColorTransform colorTransform) {
|
||||
BitmapExporter exporter = new BitmapExporter(swf, shape, defaultColor, colorTransform);
|
||||
exporter.exportTo(image, transformation);
|
||||
exporter.exportTo(image, transformation, strokeTransformation);
|
||||
}
|
||||
|
||||
private BitmapExporter(SWF swf, SHAPE shape, Color defaultColor, ColorTransform colorTransform) {
|
||||
@@ -145,8 +147,12 @@ public class BitmapExporter extends ShapeExporterBase {
|
||||
this.defaultColor = defaultColor;
|
||||
}
|
||||
|
||||
private void exportTo(SerializableImage image, Matrix transformation) {
|
||||
private void exportTo(SerializableImage image, Matrix transformation, Matrix strokeTransformation) {
|
||||
this.image = image;
|
||||
this.strokeTransformation = strokeTransformation.clone();
|
||||
this.strokeTransformation.scaleX /= SWF.unitDivisor;
|
||||
this.strokeTransformation.scaleY /= SWF.unitDivisor;
|
||||
|
||||
graphics = (Graphics2D) image.getGraphics();
|
||||
AffineTransform at = transformation.toTransform();
|
||||
at.preConcatenate(AffineTransform.getScaleInstance(1 / SWF.unitDivisor, 1 / SWF.unitDivisor));
|
||||
@@ -354,13 +360,13 @@ public class BitmapExporter extends ShapeExporterBase {
|
||||
}
|
||||
switch (scaleMode) {
|
||||
case "VERTICAL":
|
||||
thickness *= graphics.getTransform().getScaleY();
|
||||
thickness *= strokeTransformation.scaleY;
|
||||
break;
|
||||
case "HORIZONTAL":
|
||||
thickness *= graphics.getTransform().getScaleX();
|
||||
thickness *= strokeTransformation.scaleX;
|
||||
break;
|
||||
case "NORMAL":
|
||||
thickness *= Math.max(graphics.getTransform().getScaleX(), graphics.getTransform().getScaleY());
|
||||
thickness *= Math.max(strokeTransformation.scaleX, strokeTransformation.scaleY);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -376,7 +382,9 @@ public class BitmapExporter extends ShapeExporterBase {
|
||||
|
||||
// Do not scale strokes automatically:
|
||||
try {
|
||||
lineStroke = new TransformedStroke(lineStroke, graphics.getTransform());
|
||||
AffineTransform t = (AffineTransform) graphics.getTransform().clone();
|
||||
t.translate(-0.5, -0.5);
|
||||
lineStroke = new TransformedStroke(lineStroke, t);
|
||||
} catch (NoninvertibleTransformException net) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
@@ -22,6 +22,8 @@ import com.jpexs.decompiler.flash.types.ColorTransform;
|
||||
import com.jpexs.decompiler.flash.types.GRADRECORD;
|
||||
import com.jpexs.decompiler.flash.types.RGB;
|
||||
import com.jpexs.decompiler.flash.types.SHAPE;
|
||||
import java.awt.BasicStroke;
|
||||
import java.awt.Shape;
|
||||
import java.awt.geom.GeneralPath;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -33,12 +35,19 @@ import java.util.List;
|
||||
public class PathExporter extends ShapeExporterBase {
|
||||
|
||||
private final List<GeneralPath> paths = new ArrayList<>();
|
||||
private final List<GeneralPath> strokes = new ArrayList<>();
|
||||
private double thickness = 0;
|
||||
|
||||
private GeneralPath path = new GeneralPath(GeneralPath.WIND_EVEN_ODD);
|
||||
|
||||
public static List<GeneralPath> export(SWF swf, SHAPE shape) {
|
||||
return export(swf, shape, new ArrayList<>());
|
||||
}
|
||||
|
||||
public static List<GeneralPath> export(SWF swf, SHAPE shape, List<GeneralPath> strokes) {
|
||||
PathExporter exporter = new PathExporter(swf, shape, null);
|
||||
exporter.export();
|
||||
strokes.addAll(exporter.strokes);
|
||||
return exporter.paths;
|
||||
}
|
||||
|
||||
@@ -104,6 +113,7 @@ public class PathExporter extends ShapeExporterBase {
|
||||
@Override
|
||||
public void lineStyle(double thickness, RGB color, boolean pixelHinting, String scaleMode, int startCaps, int endCaps, int joints, float miterLimit) {
|
||||
finalizePath();
|
||||
this.thickness = thickness;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -127,6 +137,11 @@ public class PathExporter extends ShapeExporterBase {
|
||||
}
|
||||
|
||||
protected void finalizePath() {
|
||||
if (thickness == 0) {
|
||||
strokes.add(new GeneralPath());
|
||||
} else {
|
||||
strokes.add(new GeneralPath(new BasicStroke((float) (thickness)).createStrokedShape(path)));
|
||||
}
|
||||
paths.add(path);
|
||||
path = new GeneralPath(GeneralPath.WIND_EVEN_ODD); //For correct intersections display
|
||||
}
|
||||
|
||||
@@ -1040,7 +1040,7 @@ public class SvgImporter {
|
||||
st = (DefineShape4Tag) (new SvgImporter().importSvg(st, svgDataS));
|
||||
swf.addTag(st);
|
||||
SerializableImage si = new SerializableImage(480, 360, BufferedImage.TYPE_4BYTE_ABGR);
|
||||
BitmapExporter.export(swf, st.shapes, Color.yellow, si, new Matrix(), null);
|
||||
BitmapExporter.export(swf, st.shapes, Color.yellow, si, new Matrix(), new Matrix(), null);
|
||||
List<Tag> li = new ArrayList<>();
|
||||
li.add(st);
|
||||
ImageIO.write(si.getBufferedImage(), "PNG", new File(name + ".imported.png"));
|
||||
|
||||
@@ -916,7 +916,7 @@ public class DefineEditTextTag extends TextTag {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toImage(int frame, int time, int ratio, RenderContext renderContext, SerializableImage image, boolean isClip, Matrix transformation, Matrix prevTransformation, Matrix absoluteTransformation, ColorTransform colorTransform) {
|
||||
public void toImage(int frame, int time, int ratio, RenderContext renderContext, SerializableImage image, boolean isClip, Matrix transformation, Matrix strokeTransformation, Matrix absoluteTransformation, ColorTransform colorTransform) {
|
||||
render(TextRenderMode.BITMAP, image, null, null, transformation, colorTransform, 1);
|
||||
}
|
||||
|
||||
|
||||
@@ -19,12 +19,24 @@ package com.jpexs.decompiler.flash.tags;
|
||||
import com.jpexs.decompiler.flash.SWF;
|
||||
import com.jpexs.decompiler.flash.SWFInputStream;
|
||||
import com.jpexs.decompiler.flash.SWFOutputStream;
|
||||
import com.jpexs.decompiler.flash.exporters.commonshape.ExportRectangle;
|
||||
import com.jpexs.decompiler.flash.exporters.commonshape.Matrix;
|
||||
import com.jpexs.decompiler.flash.exporters.commonshape.Point;
|
||||
import com.jpexs.decompiler.flash.tags.base.CharacterIdTag;
|
||||
import com.jpexs.decompiler.flash.tags.base.CharacterTag;
|
||||
import com.jpexs.decompiler.flash.tags.base.DrawableTag;
|
||||
import com.jpexs.decompiler.flash.tags.base.RenderContext;
|
||||
import com.jpexs.decompiler.flash.types.BasicType;
|
||||
import com.jpexs.decompiler.flash.types.RECT;
|
||||
import com.jpexs.decompiler.flash.types.annotations.SWFType;
|
||||
import com.jpexs.decompiler.flash.types.annotations.SWFVersion;
|
||||
import com.jpexs.helpers.ByteArrayRange;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.Shape;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.geom.GeneralPath;
|
||||
import java.awt.geom.PathIterator;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
@@ -85,4 +97,134 @@ public class DefineScalingGridTag extends Tag implements CharacterIdTag {
|
||||
public void setCharacterId(int characterId) {
|
||||
this.characterId = characterId;
|
||||
}
|
||||
|
||||
private static double roundPixels(double v) {
|
||||
return v; //Math.rint(v / SWF.unitDivisor) * SWF.unitDivisor;
|
||||
}
|
||||
|
||||
private static double roundPixels20(double v) {
|
||||
return Math.rint(v / SWF.unitDivisor) * SWF.unitDivisor;
|
||||
}
|
||||
|
||||
private static Matrix rectToRectMatrix(ExportRectangle fromRect, ExportRectangle toRect) {
|
||||
Matrix toOrigin = Matrix.getTranslateInstance(roundPixels(-fromRect.xMin), roundPixels(-fromRect.yMin));
|
||||
Matrix scale = new Matrix();
|
||||
scale.scaleX = roundPixels(toRect.getWidth()) / roundPixels(fromRect.getWidth());
|
||||
scale.scaleY = roundPixels(toRect.getHeight()) / roundPixels(fromRect.getHeight());
|
||||
Matrix toDest = Matrix.getTranslateInstance(roundPixels(toRect.xMin), roundPixels(toRect.yMin));
|
||||
return toOrigin.preConcatenate(scale).preConcatenate(toDest);
|
||||
}
|
||||
|
||||
public RECT getRect() {
|
||||
Shape s = getOutline(0, 0, 0, new RenderContext(), new Matrix(), new Matrix(), true);
|
||||
if (s == null) {
|
||||
return null;
|
||||
}
|
||||
Rectangle r = s.getBounds();
|
||||
return new RECT(r.x, r.x + r.width, r.y, r.y + r.height);
|
||||
}
|
||||
|
||||
public Shape getOutline(int frame, int time, int ratio, RenderContext renderContext, Matrix transformation, Matrix prevTransform, boolean stroked) {
|
||||
CharacterTag ct = swf.getCharacter(characterId);
|
||||
if (ct == null) {
|
||||
return null;
|
||||
}
|
||||
if (!(ct instanceof DrawableTag)) {
|
||||
return null;
|
||||
}
|
||||
double[] coords = new double[6];
|
||||
|
||||
DrawableTag dt = (DrawableTag) ct;
|
||||
Shape path = dt.getOutline(frame, time, ratio, renderContext, transformation, stroked);
|
||||
PathIterator iterator = path.getPathIterator(new AffineTransform());
|
||||
GeneralPath gp = new GeneralPath(GeneralPath.WIND_EVEN_ODD);
|
||||
ExportRectangle boundsRect = new ExportRectangle(dt.getRect());
|
||||
ExportRectangle scalingGrid = new ExportRectangle(splitter);
|
||||
|
||||
ExportRectangle[] sourceRect = new ExportRectangle[9];
|
||||
ExportRectangle[] targetRect = new ExportRectangle[9];
|
||||
Matrix[] transforms = new Matrix[9];
|
||||
|
||||
getSlices(transformation.transform(boundsRect), boundsRect, scalingGrid, sourceRect, targetRect, transforms);
|
||||
|
||||
while (!iterator.isDone()) {
|
||||
int type = iterator.currentSegment(coords);
|
||||
for (int i = 0; i < 6; i += 2) {
|
||||
double x = coords[i];
|
||||
double y = coords[i + 1];
|
||||
for (int s = 0; s < 9; s++) {
|
||||
Point p = new Point(x, y);
|
||||
if (sourceRect[s].contains(p)) {
|
||||
p = transforms[s].transform(p);
|
||||
coords[i] = p.x;
|
||||
coords[i + 1] = p.y;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
switch (type) {
|
||||
case PathIterator.SEG_MOVETO:
|
||||
gp.moveTo(coords[0], coords[1]);
|
||||
break;
|
||||
case PathIterator.SEG_LINETO:
|
||||
gp.lineTo(coords[0], coords[1]);
|
||||
break;
|
||||
case PathIterator.SEG_QUADTO:
|
||||
gp.quadTo(coords[0], coords[1], coords[2], coords[3]);
|
||||
break;
|
||||
case PathIterator.SEG_CUBICTO:
|
||||
gp.curveTo(coords[0], coords[1], coords[2], coords[3], coords[4], coords[5]);
|
||||
break;
|
||||
case PathIterator.SEG_CLOSE:
|
||||
gp.closePath();
|
||||
break;
|
||||
}
|
||||
iterator.next();
|
||||
}
|
||||
return gp;
|
||||
}
|
||||
|
||||
public static void getSlices(ExportRectangle targetBounds, ExportRectangle boundsRect, ExportRectangle scalingGrid, ExportRectangle[] sourceRect, ExportRectangle[] targetRect, Matrix[] transforms) {
|
||||
|
||||
double src_x[] = new double[]{boundsRect.xMin, scalingGrid.xMin, scalingGrid.xMax, boundsRect.xMax};
|
||||
double dst_x[] = new double[]{targetBounds.xMin, targetBounds.xMin + scalingGrid.xMin, targetBounds.xMax - (boundsRect.xMax - scalingGrid.xMax), targetBounds.xMax};
|
||||
|
||||
double src_y[] = new double[]{boundsRect.yMin, scalingGrid.yMin, scalingGrid.yMax, boundsRect.yMax};
|
||||
double dst_y[] = new double[]{targetBounds.yMin, targetBounds.yMin + scalingGrid.yMin, targetBounds.yMax - (boundsRect.yMax - scalingGrid.yMax), targetBounds.yMax};
|
||||
|
||||
int pos = 0;
|
||||
for (int sy = 0; sy < 3; sy++) {
|
||||
for (int sx = 0; sx < 3; sx++) {
|
||||
sourceRect[pos] = new ExportRectangle(src_x[sx], src_y[sy], src_x[sx + 1], src_y[sy + 1]);
|
||||
targetRect[pos] = new ExportRectangle(dst_x[sx], dst_y[sy], dst_x[sx + 1], dst_y[sy + 1]);
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < targetRect.length; i++) {
|
||||
|
||||
/* sourceRect[i].xMax = roundPixels20(sourceRect[i].xMax);
|
||||
sourceRect[i].yMax = roundPixels20(sourceRect[i].yMax);
|
||||
sourceRect[i].xMin = roundPixels20(sourceRect[i].xMin);
|
||||
sourceRect[i].yMin = roundPixels20(sourceRect[i].yMin);
|
||||
*/
|
||||
//System.out.println("source[" + i + "]=" + sourceRect[i]);
|
||||
//System.out.println("target[" + i + "]=" + targetRect[i]);
|
||||
|
||||
/*targetRect[i].xMax = roundPixels20(targetRect[i].xMax);
|
||||
targetRect[i].yMax = roundPixels20(targetRect[i].yMax);
|
||||
targetRect[i].xMin = roundPixels20(targetRect[i].xMin);
|
||||
targetRect[i].yMin = roundPixels20(targetRect[i].yMin);
|
||||
*/
|
||||
transforms[i] = rectToRectMatrix(sourceRect[i], targetRect[i]);
|
||||
|
||||
targetRect[i].xMax = Math.rint(targetRect[i].xMax / SWF.unitDivisor);
|
||||
targetRect[i].yMax = Math.rint(targetRect[i].yMax / SWF.unitDivisor);
|
||||
targetRect[i].xMin = Math.rint(targetRect[i].xMin / SWF.unitDivisor);
|
||||
targetRect[i].yMin = Math.rint(targetRect[i].yMin / SWF.unitDivisor);
|
||||
|
||||
//targetRect[i].xMax += maxStroke;
|
||||
//Round to pixel boundary
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -361,13 +361,13 @@ public class DefineSpriteTag extends CharacterTag implements DrawableTag, Timeli
|
||||
}
|
||||
|
||||
@Override
|
||||
public Shape getOutline(int frame, int time, int ratio, RenderContext renderContext, Matrix transformation) {
|
||||
return getTimeline().getOutline(frame, time, renderContext, transformation);
|
||||
public Shape getOutline(int frame, int time, int ratio, RenderContext renderContext, Matrix transformation, boolean stroked) {
|
||||
return getTimeline().getOutline(frame, time, renderContext, transformation, stroked);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toImage(int frame, int time, int ratio, RenderContext renderContext, SerializableImage image, boolean isClip, Matrix transformation, Matrix prevTransformation, Matrix absoluteTransformation, ColorTransform colorTransform) {
|
||||
getTimeline().toImage(frame, time, renderContext, image, isClip, transformation, prevTransformation, absoluteTransformation, colorTransform);
|
||||
public void toImage(int frame, int time, int ratio, RenderContext renderContext, SerializableImage image, boolean isClip, Matrix transformation, Matrix strokeTransformation, Matrix absoluteTransformation, ColorTransform colorTransform) {
|
||||
getTimeline().toImage(frame, time, renderContext, image, isClip, transformation, strokeTransformation, absoluteTransformation, colorTransform);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -87,13 +87,13 @@ public abstract class ButtonTag extends CharacterTag implements DrawableTag, Tim
|
||||
}
|
||||
|
||||
@Override
|
||||
public Shape getOutline(int frame, int time, int ratio, RenderContext renderContext, Matrix transformation) {
|
||||
return getTimeline().getOutline(frame, time, renderContext, transformation);
|
||||
public Shape getOutline(int frame, int time, int ratio, RenderContext renderContext, Matrix transformation, boolean stroked) {
|
||||
return getTimeline().getOutline(frame, time, renderContext, transformation, stroked);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toImage(int frame, int time, int ratio, RenderContext renderContext, SerializableImage image, boolean isClip, Matrix transformation, Matrix prevTransformation, Matrix absoluteTransformation, ColorTransform colorTransform) {
|
||||
getTimeline().toImage(frame, time, renderContext, image, isClip, transformation, prevTransformation, absoluteTransformation, colorTransform);
|
||||
public void toImage(int frame, int time, int ratio, RenderContext renderContext, SerializableImage image, boolean isClip, Matrix transformation, Matrix strokeTransformation, Matrix absoluteTransformation, ColorTransform colorTransform) {
|
||||
getTimeline().toImage(frame, time, renderContext, image, isClip, transformation, strokeTransformation, absoluteTransformation, colorTransform);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -17,8 +17,11 @@
|
||||
package com.jpexs.decompiler.flash.tags.base;
|
||||
|
||||
import com.jpexs.decompiler.flash.SWF;
|
||||
import com.jpexs.decompiler.flash.exporters.commonshape.ExportRectangle;
|
||||
import com.jpexs.decompiler.flash.tags.DefineScalingGridTag;
|
||||
import com.jpexs.decompiler.flash.tags.Tag;
|
||||
import com.jpexs.helpers.ByteArrayRange;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -74,4 +77,14 @@ public abstract class CharacterTag extends Tag implements CharacterIdTag {
|
||||
public String getExportName() {
|
||||
return exportName;
|
||||
}
|
||||
|
||||
public DefineScalingGridTag getScalingGridTag() {
|
||||
List<CharacterIdTag> mtags = swf.getCharacterIdTags(getCharacterId());
|
||||
for (CharacterIdTag ct : mtags) {
|
||||
if (ct instanceof DefineScalingGridTag) {
|
||||
return (DefineScalingGridTag) ct;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ public interface DrawableTag extends BoundedTag {
|
||||
|
||||
public int getUsedParameters();
|
||||
|
||||
public Shape getOutline(int frame, int time, int ratio, RenderContext renderContext, Matrix transformation);
|
||||
public Shape getOutline(int frame, int time, int ratio, RenderContext renderContext, Matrix transformation, boolean stroked);
|
||||
|
||||
public void toImage(int frame, int time, int ratio, RenderContext renderContext, SerializableImage image, boolean isClip, Matrix transformation, Matrix prevTransformation, Matrix absoluteTransformation, ColorTransform colorTransform);
|
||||
|
||||
|
||||
@@ -317,13 +317,13 @@ public abstract class FontTag extends CharacterTag implements AloneTag, Drawable
|
||||
}
|
||||
|
||||
@Override
|
||||
public Shape getOutline(int frame, int time, int ratio, RenderContext renderContext, Matrix transformation) {
|
||||
public Shape getOutline(int frame, int time, int ratio, RenderContext renderContext, Matrix transformation, boolean stroked) {
|
||||
RECT r = getRect();
|
||||
return new Area(new Rectangle(r.Xmin, r.Ymin, r.getWidth(), r.getHeight()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toImage(int frame, int time, int ratio, RenderContext renderContext, SerializableImage image, boolean isClip, Matrix transformation, Matrix prevTransformation, Matrix absoluteTransformation, ColorTransform colorTransform) {
|
||||
public void toImage(int frame, int time, int ratio, RenderContext renderContext, SerializableImage image, boolean isClip, Matrix transformation, Matrix strokeTransformation, Matrix absoluteTransformation, ColorTransform colorTransform) {
|
||||
SHAPERECORD.shapeListToImage(swf, getGlyphShapeTable(), image, frame, Color.black, colorTransform);
|
||||
}
|
||||
|
||||
|
||||
@@ -278,13 +278,13 @@ public abstract class ImageTag extends CharacterTag implements DrawableTag {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Shape getOutline(int frame, int time, int ratio, RenderContext renderContext, Matrix transformation) {
|
||||
return transformation.toTransform().createTransformedShape(getShape().getOutline(swf));
|
||||
public Shape getOutline(int frame, int time, int ratio, RenderContext renderContext, Matrix transformation, boolean stroked) {
|
||||
return transformation.toTransform().createTransformedShape(getShape().getOutline(swf, stroked));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toImage(int frame, int time, int ratio, RenderContext renderContext, SerializableImage image, boolean isClip, Matrix transformation, Matrix prevTransformation, Matrix absoluteTransformation, ColorTransform colorTransform) {
|
||||
BitmapExporter.export(swf, getShape(), null, image, transformation, colorTransform);
|
||||
public void toImage(int frame, int time, int ratio, RenderContext renderContext, SerializableImage image, boolean isClip, Matrix transformation, Matrix strokeTransformation, Matrix absoluteTransformation, ColorTransform colorTransform) {
|
||||
BitmapExporter.export(swf, getShape(), null, image, transformation, strokeTransformation, colorTransform);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -279,13 +279,13 @@ public abstract class MorphShapeTag extends CharacterTag implements DrawableTag
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toImage(int frame, int time, int ratio, RenderContext renderContext, SerializableImage image, boolean isClip, Matrix transformation, Matrix prevTransformation, Matrix absoluteTransformation, ColorTransform colorTransform) {
|
||||
public void toImage(int frame, int time, int ratio, RenderContext renderContext, SerializableImage image, boolean isClip, Matrix transformation, Matrix strokeTransformation, Matrix absoluteTransformation, ColorTransform colorTransform) {
|
||||
SHAPEWITHSTYLE shape = getShapeAtRatio(ratio);
|
||||
// morphShape using shapeNum=3, morphShape2 using shapeNum=4
|
||||
// todo: Currently the generated image is not cached, because the cache
|
||||
// key contains the hashCode of the finalRecord object, and it is always
|
||||
// recreated
|
||||
BitmapExporter.export(swf, shape, null, image, transformation, colorTransform);
|
||||
BitmapExporter.export(swf, shape, null, image, transformation, strokeTransformation, colorTransform);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -314,8 +314,8 @@ public abstract class MorphShapeTag extends CharacterTag implements DrawableTag
|
||||
}
|
||||
|
||||
@Override
|
||||
public Shape getOutline(int frame, int time, int ratio, RenderContext renderContext, Matrix transformation) {
|
||||
return transformation.toTransform().createTransformedShape(getShapeAtRatio(ratio).getOutline(swf));
|
||||
public Shape getOutline(int frame, int time, int ratio, RenderContext renderContext, Matrix transformation, boolean stroked) {
|
||||
return transformation.toTransform().createTransformedShape(getShapeAtRatio(ratio).getOutline(swf, stroked));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -127,13 +127,13 @@ public abstract class ShapeTag extends CharacterTag implements DrawableTag, Lazy
|
||||
}
|
||||
|
||||
@Override
|
||||
public Shape getOutline(int frame, int time, int ratio, RenderContext renderContext, Matrix transformation) {
|
||||
return transformation.toTransform().createTransformedShape(getShapes().getOutline(swf));
|
||||
public Shape getOutline(int frame, int time, int ratio, RenderContext renderContext, Matrix transformation, boolean stroked) {
|
||||
return transformation.toTransform().createTransformedShape(getShapes().getOutline(swf, stroked));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toImage(int frame, int time, int ratio, RenderContext renderContext, SerializableImage image, boolean isClip, Matrix transformation, Matrix prevTransformation, Matrix absoluteTransformation, ColorTransform colorTransform) {
|
||||
BitmapExporter.export(swf, getShapes(), null, image, transformation, colorTransform);
|
||||
public void toImage(int frame, int time, int ratio, RenderContext renderContext, SerializableImage image, boolean isClip, Matrix transformation, Matrix strokeTransformation, Matrix absoluteTransformation, ColorTransform colorTransform) {
|
||||
BitmapExporter.export(swf, getShapes(), null, image, transformation, strokeTransformation, colorTransform);
|
||||
if (Configuration._debugMode.get()) { // show control points
|
||||
List<GeneralPath> paths = PathExporter.export(swf, getShapes());
|
||||
double[] coords = new double[6];
|
||||
|
||||
@@ -652,7 +652,7 @@ public abstract class StaticTextTag extends TextTag {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toImage(int frame, int time, int ratio, RenderContext renderContext, SerializableImage image, boolean isClip, Matrix transformation, Matrix prevTransformation, Matrix absoluteTransformation, ColorTransform colorTransform) {
|
||||
public void toImage(int frame, int time, int ratio, RenderContext renderContext, SerializableImage image, boolean isClip, Matrix transformation, Matrix strokeTransformation, Matrix absoluteTransformation, ColorTransform colorTransform) {
|
||||
staticTextToImage(swf, textRecords, getTextNum(), image, textMatrix, transformation, colorTransform);
|
||||
/*try {
|
||||
TextTag originalTag = (TextTag) getOriginalTag();
|
||||
|
||||
@@ -392,7 +392,7 @@ public abstract class TextTag extends CharacterTag implements DrawableTag {
|
||||
Graphics2D g = (Graphics2D) image.getGraphics();
|
||||
Matrix mat = transformation.clone();
|
||||
mat = mat.concatenate(new Matrix(textMatrix));
|
||||
BitmapExporter.export(swf, getBorderShape(borderColor, fillColor, rect), null, image, mat, colorTransform);
|
||||
BitmapExporter.export(swf, getBorderShape(borderColor, fillColor, rect), null, image, mat, mat, colorTransform);
|
||||
}
|
||||
|
||||
public static void drawBorderHtmlCanvas(SWF swf, StringBuilder result, RGB borderColor, RGB fillColor, RECT rect, MATRIX textMatrix, ColorTransform colorTransform, double unitDivisor) {
|
||||
@@ -470,7 +470,7 @@ public abstract class TextTag extends CharacterTag implements DrawableTag {
|
||||
}
|
||||
|
||||
if (shape != null) {
|
||||
BitmapExporter.export(swf, shape, textColor2, image, mat, colorTransform);
|
||||
BitmapExporter.export(swf, shape, textColor2, image, mat, mat, colorTransform);
|
||||
if (SHAPERECORD.DRAW_BOUNDING_BOX) {
|
||||
RGB borderColor = new RGBA(Color.black);
|
||||
RGB fillColor = new RGBA(new Color(255, 255, 255, 0));
|
||||
@@ -736,7 +736,7 @@ public abstract class TextTag extends CharacterTag implements DrawableTag {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Shape getOutline(int frame, int time, int ratio, RenderContext renderContext, Matrix transformation) {
|
||||
public Shape getOutline(int frame, int time, int ratio, RenderContext renderContext, Matrix transformation, boolean stroked) {
|
||||
RECT r = getBounds();
|
||||
Shape shp = new Rectangle.Double(r.Xmin, r.Ymin, r.getWidth(), r.getHeight());
|
||||
return transformation.toTransform().createTransformedShape(shp); //TODO: match character shapes (?)
|
||||
|
||||
@@ -64,9 +64,9 @@ import java.awt.RenderingHints;
|
||||
import java.awt.Shape;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.geom.Area;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
@@ -74,6 +74,9 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.Stack;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -549,27 +552,16 @@ public class Timeline {
|
||||
return modified;
|
||||
}
|
||||
|
||||
private Matrix rectToRectMatrix(ExportRectangle fromRect, ExportRectangle toRect) {
|
||||
Matrix toOrigin = Matrix.getTranslateInstance(-fromRect.xMin, -fromRect.yMin);
|
||||
Matrix scale = new Matrix();
|
||||
scale.scaleX = toRect.getWidth() / fromRect.getWidth();
|
||||
scale.scaleY = toRect.getHeight() / fromRect.getHeight();
|
||||
Matrix toDest = Matrix.getTranslateInstance(toRect.xMin, toRect.yMin);
|
||||
return toOrigin.preConcatenate(scale).preConcatenate(toDest);
|
||||
}
|
||||
|
||||
public double roundToPixel(double val) {
|
||||
return Math.rint(val / SWF.unitDivisor) * SWF.unitDivisor;
|
||||
}
|
||||
|
||||
public void toImage(int frame, int time, RenderContext renderContext, SerializableImage image, boolean isClip, Matrix transformation, Matrix prevTransformation, Matrix absoluteTransformation, ColorTransform colorTransform) {
|
||||
/*public void toImage(int frame, int time, RenderContext renderContext, SerializableImage image, boolean isClip, Matrix transformation, Matrix prevTransformation, Matrix absoluteTransformation, ColorTransform colorTransform) {
|
||||
ExportRectangle scalingGrid = null;
|
||||
if (timelined instanceof CharacterTag) {
|
||||
List<CharacterIdTag> mtags = swf.getCharacterIdTags(((CharacterTag) timelined).getCharacterId());
|
||||
for (CharacterIdTag ct : mtags) {
|
||||
if (ct instanceof DefineScalingGridTag) {
|
||||
scalingGrid = new ExportRectangle(((DefineScalingGridTag) ct).splitter);
|
||||
}
|
||||
DefineScalingGridTag sgt = ((CharacterTag) timelined).getScalingGridTag();
|
||||
if (sgt != null) {
|
||||
scalingGrid = new ExportRectangle(sgt.splitter);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -588,71 +580,221 @@ public class Timeline {
|
||||
|
||||
ExportRectangle boundsRect = new ExportRectangle(timelined.getRect());
|
||||
|
||||
/*
|
||||
|
||||
0 | 1 | 2
|
||||
------------
|
||||
3 | 4 | 5
|
||||
------------
|
||||
6 | 7 | 8
|
||||
*/
|
||||
ExportRectangle[] targetRect = new ExportRectangle[9];
|
||||
ExportRectangle[] sourceRect = new ExportRectangle[9];
|
||||
Matrix[] transforms = new Matrix[9];
|
||||
|
||||
sourceRect[0] = new ExportRectangle(0, 0, scalingGrid.xMin, scalingGrid.yMin);
|
||||
targetRect[0] = new ExportRectangle(0, 0, scalingGrid.xMin, scalingGrid.yMin);
|
||||
ExportRectangle targetRect[] = new ExportRectangle[9];
|
||||
ExportRectangle sourceRect[] = new ExportRectangle[9];
|
||||
Matrix transforms[] = new Matrix[9];
|
||||
|
||||
sourceRect[1] = new ExportRectangle(scalingGrid.xMin, 0, scalingGrid.xMax, scalingGrid.yMin);
|
||||
targetRect[1] = new ExportRectangle(scalingGrid.xMin, 0, boundsRect.xMax * transformation.scaleX - (boundsRect.xMax - scalingGrid.xMax), scalingGrid.yMin);
|
||||
DefineScalingGridTag.getSlices(transformation, prevScale, boundsRect, scalingGrid, sourceRect, targetRect, transforms);
|
||||
|
||||
sourceRect[2] = new ExportRectangle(scalingGrid.xMax, 0, boundsRect.xMax, scalingGrid.yMin);
|
||||
targetRect[2] = new ExportRectangle(
|
||||
boundsRect.xMax * transformation.scaleX - (boundsRect.xMax - scalingGrid.xMax),
|
||||
0,
|
||||
boundsRect.xMax * transformation.scaleX, scalingGrid.yMin);
|
||||
|
||||
sourceRect[3] = new ExportRectangle(0, scalingGrid.yMin, scalingGrid.xMin, scalingGrid.yMax);
|
||||
targetRect[3] = new ExportRectangle(0, scalingGrid.yMin, scalingGrid.xMin, boundsRect.yMax * transformation.scaleY - (boundsRect.yMax - scalingGrid.yMax));
|
||||
|
||||
sourceRect[4] = new ExportRectangle(scalingGrid.xMin, scalingGrid.yMin, scalingGrid.xMax, scalingGrid.yMax);
|
||||
targetRect[4] = new ExportRectangle(scalingGrid.xMin, scalingGrid.yMin, boundsRect.xMax * transformation.scaleX - (boundsRect.xMax - scalingGrid.xMax), boundsRect.yMax * transformation.scaleY - (boundsRect.yMax - scalingGrid.yMax));
|
||||
|
||||
sourceRect[5] = new ExportRectangle(scalingGrid.xMax, scalingGrid.yMin, boundsRect.xMax, scalingGrid.yMax);
|
||||
targetRect[5] = new ExportRectangle(
|
||||
boundsRect.xMax * transformation.scaleX - (boundsRect.xMax - scalingGrid.xMax),
|
||||
scalingGrid.yMin,
|
||||
boundsRect.xMax * transformation.scaleX,
|
||||
boundsRect.yMax * transformation.scaleY - (boundsRect.yMax - scalingGrid.yMax)
|
||||
);
|
||||
|
||||
sourceRect[6] = new ExportRectangle(0, scalingGrid.yMax, scalingGrid.xMin, boundsRect.yMax);
|
||||
targetRect[6] = new ExportRectangle(0, boundsRect.yMax * transformation.scaleY - (boundsRect.yMax - scalingGrid.yMax), scalingGrid.xMin, boundsRect.yMax * transformation.scaleY);
|
||||
|
||||
sourceRect[7] = new ExportRectangle(scalingGrid.xMin, scalingGrid.yMax, scalingGrid.xMax, boundsRect.yMax);
|
||||
targetRect[7] = new ExportRectangle(scalingGrid.xMin, boundsRect.yMax * transformation.scaleY - (boundsRect.yMax - scalingGrid.yMax), boundsRect.xMax * transformation.scaleX - (boundsRect.xMax - scalingGrid.xMax), boundsRect.yMax * transformation.scaleY);
|
||||
|
||||
sourceRect[8] = new ExportRectangle(scalingGrid.xMax, scalingGrid.yMax, boundsRect.xMax, boundsRect.yMax);
|
||||
targetRect[8] = new ExportRectangle(
|
||||
boundsRect.xMax * transformation.scaleX - (boundsRect.xMax - scalingGrid.xMax),
|
||||
boundsRect.yMax * transformation.scaleY - (boundsRect.yMax - scalingGrid.yMax),
|
||||
boundsRect.xMax * transformation.scaleX,
|
||||
boundsRect.yMax * transformation.scaleY);
|
||||
for (int i = 0; i < targetRect.length; i++) {
|
||||
targetRect[i] = prevScale.transform(targetRect[i]);
|
||||
//targetRect[i] = absDiffTransform.transform(targetRect[i]);
|
||||
transforms[i] = rectToRectMatrix(sourceRect[i], targetRect[i]);
|
||||
//Round to pixel boundary
|
||||
targetRect[i].xMax = Math.rint(targetRect[i].xMax / SWF.unitDivisor);
|
||||
targetRect[i].yMax = Math.rint(targetRect[i].yMax / SWF.unitDivisor);
|
||||
targetRect[i].xMin = Math.rint(targetRect[i].xMin / SWF.unitDivisor);
|
||||
targetRect[i].yMin = Math.rint(targetRect[i].yMin / SWF.unitDivisor);
|
||||
}
|
||||
for (int i = 0; i < targetRect.length; i++) {
|
||||
toImage(frame, time, renderContext, image, isClip, transforms[i], absoluteTransformation, colorTransform, targetRect[i]);
|
||||
}
|
||||
}*/
|
||||
private void drawDrawable(Matrix strokeTransform, DepthState layer, Matrix layerMatrix, Graphics2D g, ColorTransform colorTransForm, int blendMode, List<Clip> clips, List<Shape> prevClips, Matrix transformation, boolean isClip, int clipDepth, Matrix absMat, int time, int ratio, RenderContext renderContext, SerializableImage image, DrawableTag drawable, List<FILTER> filters, double unzoom, ColorTransform clrTrans) {
|
||||
Matrix drawMatrix = new Matrix();
|
||||
int drawableFrameCount = drawable.getNumFrames();
|
||||
if (drawableFrameCount == 0) {
|
||||
drawableFrameCount = 1;
|
||||
}
|
||||
|
||||
RECT boundRect = drawable.getRect();
|
||||
|
||||
ExportRectangle rect = new ExportRectangle(boundRect);
|
||||
Matrix mat = transformation.concatenate(layerMatrix);
|
||||
rect = mat.transform(rect);
|
||||
Matrix m = mat.clone();
|
||||
if (filters != null && filters.size() > 0) {
|
||||
// calculate size after applying the filters
|
||||
double deltaXMax = 0;
|
||||
double deltaYMax = 0;
|
||||
for (FILTER filter : filters) {
|
||||
double x = filter.getDeltaX();
|
||||
double y = filter.getDeltaY();
|
||||
deltaXMax = Math.max(x, deltaXMax);
|
||||
deltaYMax = Math.max(y, deltaYMax);
|
||||
}
|
||||
rect.xMin -= deltaXMax * unzoom;
|
||||
rect.xMax += deltaXMax * unzoom;
|
||||
rect.yMin -= deltaYMax * unzoom;
|
||||
rect.yMax += deltaYMax * unzoom;
|
||||
}
|
||||
|
||||
rect.xMin -= 1 * unzoom;
|
||||
rect.yMin -= 1 * unzoom;
|
||||
rect.xMin = Math.max(0, rect.xMin);
|
||||
rect.yMin = Math.max(0, rect.yMin);
|
||||
|
||||
int newWidth = (int) (rect.getWidth() / unzoom);
|
||||
int newHeight = (int) (rect.getHeight() / unzoom);
|
||||
int deltaX = (int) (rect.xMin / unzoom);
|
||||
int deltaY = (int) (rect.yMin / unzoom);
|
||||
newWidth = Math.min(image.getWidth() - deltaX, newWidth) + 1;
|
||||
newHeight = Math.min(image.getHeight() - deltaY, newHeight) + 1;
|
||||
|
||||
if (newWidth <= 0 || newHeight <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
m.translate(-rect.xMin, -rect.yMin);
|
||||
//strokeTransform = strokeTransform.clone();
|
||||
//strokeTransform.translate(-rect.xMin, -rect.yMin);
|
||||
drawMatrix.translate(rect.xMin, rect.yMin);
|
||||
|
||||
SerializableImage img = null;
|
||||
String cacheKey = null;
|
||||
if (drawable instanceof ShapeTag) {
|
||||
cacheKey = ((ShapeTag) drawable).getCharacterId() + m.toString() + (clrTrans == null ? "" : clrTrans.toString());
|
||||
//img = renderContext.shapeCache.get(cacheKey);
|
||||
}
|
||||
|
||||
int dframe;
|
||||
if (fontFrameNum != -1) {
|
||||
dframe = fontFrameNum;
|
||||
} else {
|
||||
dframe = time % drawableFrameCount;
|
||||
}
|
||||
|
||||
if (drawable instanceof ButtonTag) {
|
||||
dframe = ButtonTag.FRAME_UP;
|
||||
if (renderContext.cursorPosition != null) {
|
||||
Shape buttonShape = drawable.getOutline(ButtonTag.FRAME_HITTEST, time, ratio, renderContext, absMat, true);
|
||||
if (buttonShape.contains(renderContext.cursorPosition)) {
|
||||
renderContext.mouseOverButton = (ButtonTag) drawable;
|
||||
if (renderContext.mouseButton > 0) {
|
||||
dframe = ButtonTag.FRAME_DOWN;
|
||||
} else {
|
||||
dframe = ButtonTag.FRAME_OVER;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int stateCount = renderContext.stateUnderCursor == null ? 0 : renderContext.stateUnderCursor.size();
|
||||
if (img == null) {
|
||||
img = new SerializableImage(newWidth, newHeight, SerializableImage.TYPE_INT_ARGB);
|
||||
img.fillTransparent();
|
||||
|
||||
drawable.toImage(dframe, time, ratio, renderContext, img, isClip || clipDepth > -1, m, strokeTransform, absMat, clrTrans);
|
||||
|
||||
if (cacheKey != null) {
|
||||
renderContext.shapeCache.put(cacheKey, img);
|
||||
}
|
||||
}
|
||||
if (filters != null) {
|
||||
for (FILTER filter : filters) {
|
||||
img = filter.apply(img);
|
||||
}
|
||||
}
|
||||
if (blendMode > 1) {
|
||||
if (colorTransForm != null) {
|
||||
img = colorTransForm.apply(img);
|
||||
}
|
||||
}
|
||||
|
||||
drawMatrix.translateX /= unzoom;
|
||||
drawMatrix.translateY /= unzoom;
|
||||
AffineTransform trans = drawMatrix.toTransform();
|
||||
|
||||
switch (blendMode) {
|
||||
case 0:
|
||||
case 1:
|
||||
g.setComposite(AlphaComposite.SrcOver);
|
||||
break;
|
||||
case 2: // Layer
|
||||
g.setComposite(AlphaComposite.SrcOver);
|
||||
break;
|
||||
case 3:
|
||||
g.setComposite(BlendComposite.Multiply);
|
||||
break;
|
||||
case 4:
|
||||
g.setComposite(BlendComposite.Screen);
|
||||
break;
|
||||
case 5:
|
||||
g.setComposite(BlendComposite.Lighten);
|
||||
break;
|
||||
case 6:
|
||||
g.setComposite(BlendComposite.Darken);
|
||||
break;
|
||||
case 7:
|
||||
g.setComposite(BlendComposite.Difference);
|
||||
break;
|
||||
case 8:
|
||||
g.setComposite(BlendComposite.Add);
|
||||
break;
|
||||
case 9:
|
||||
g.setComposite(BlendComposite.Subtract);
|
||||
break;
|
||||
case 10:
|
||||
g.setComposite(BlendComposite.Invert);
|
||||
break;
|
||||
case 11:
|
||||
g.setComposite(BlendComposite.Alpha);
|
||||
break;
|
||||
case 12:
|
||||
g.setComposite(BlendComposite.Erase);
|
||||
break;
|
||||
case 13:
|
||||
g.setComposite(BlendComposite.Overlay);
|
||||
break;
|
||||
case 14:
|
||||
g.setComposite(BlendComposite.HardLight);
|
||||
break;
|
||||
default: // Not implemented
|
||||
g.setComposite(AlphaComposite.SrcOver);
|
||||
break;
|
||||
}
|
||||
|
||||
if (clipDepth > -1) {
|
||||
BufferedImage mask = new BufferedImage(image.getWidth(), image.getHeight(), image.getType());
|
||||
Graphics2D gm = (Graphics2D) mask.getGraphics();
|
||||
gm.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
|
||||
gm.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
|
||||
gm.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
gm.setComposite(AlphaComposite.Src);
|
||||
gm.setColor(new Color(0, 0, 0, 0f));
|
||||
gm.fillRect(0, 0, image.getWidth(), image.getHeight());
|
||||
gm.setTransform(trans);
|
||||
gm.drawImage(img.getBufferedImage(), 0, 0, null);
|
||||
Clip clip = new Clip(Helper.imageToShape(mask), clipDepth); // Maybe we can get current outline instead converting from image (?)
|
||||
clips.add(clip);
|
||||
prevClips.add(g.getClip());
|
||||
g.setTransform(AffineTransform.getTranslateInstance(0, 0));
|
||||
g.setClip(clip.shape);
|
||||
} else {
|
||||
if (renderContext.cursorPosition != null) {
|
||||
if (drawable instanceof DefineSpriteTag) {
|
||||
if (renderContext.stateUnderCursor.size() > stateCount) {
|
||||
renderContext.stateUnderCursor.add(layer);
|
||||
}
|
||||
} else if (absMat.transform(new ExportRectangle(boundRect)).contains(renderContext.cursorPosition)) {
|
||||
Shape shape = drawable.getOutline(dframe, time, layer.ratio, renderContext, absMat, true);
|
||||
if (shape.contains(renderContext.cursorPosition)) {
|
||||
renderContext.stateUnderCursor.add(layer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (renderContext.borderImage != null) {
|
||||
Graphics2D g2 = (Graphics2D) renderContext.borderImage.getGraphics();
|
||||
g2.setPaint(Color.red);
|
||||
g2.setStroke(new BasicStroke(2));
|
||||
Shape shape = drawable.getOutline(dframe, time, layer.ratio, renderContext, absMat.preConcatenate(Matrix.getScaleInstance(1 / SWF.unitDivisor)), true);
|
||||
g2.draw(shape);
|
||||
}
|
||||
|
||||
g.setTransform(trans);
|
||||
g.drawImage(img.getBufferedImage(), 0, 0, null);
|
||||
}
|
||||
}
|
||||
|
||||
public void toImage(int frame, int time, RenderContext renderContext, SerializableImage image, boolean isClip, Matrix transformation, Matrix absoluteTransformation, ColorTransform colorTransform, ExportRectangle clipRect) {
|
||||
public void toImage(int frame, int time, RenderContext renderContext, SerializableImage image, boolean isClip, Matrix transformation, Matrix strokeTransformation, Matrix absoluteTransformation, ColorTransform colorTransform) {
|
||||
double unzoom = SWF.unitDivisor;
|
||||
if (getFrameCount() <= frame) {
|
||||
return;
|
||||
@@ -660,13 +802,14 @@ public class Timeline {
|
||||
|
||||
Frame frameObj = getFrame(frame);
|
||||
Graphics2D g = (Graphics2D) image.getGraphics();
|
||||
Shape prevClip = g.getClip();
|
||||
//if (targetRect != null) {
|
||||
// g.setClip(new Rectangle2D.Double(targetRect.xMin, targetRect.yMin, targetRect.getWidth(), targetRect.getHeight()));
|
||||
//}
|
||||
|
||||
g.setPaint(frameObj.backgroundColor.toColor());
|
||||
g.fill(new Rectangle(image.getWidth(), image.getHeight()));
|
||||
|
||||
Shape prevClip = g.getClip();
|
||||
if (clipRect != null) {
|
||||
g.setClip(new Rectangle2D.Double(clipRect.xMin, clipRect.yMin, clipRect.getWidth(), clipRect.getHeight()));
|
||||
}
|
||||
g.setTransform(transformation.toTransform());
|
||||
List<Clip> clips = new ArrayList<>();
|
||||
List<Shape> prevClips = new ArrayList<>();
|
||||
@@ -703,210 +846,52 @@ public class Timeline {
|
||||
|
||||
boolean showPlaceholder = false;
|
||||
if (character instanceof DrawableTag) {
|
||||
DrawableTag drawable = (DrawableTag) character;
|
||||
Matrix drawMatrix = new Matrix();
|
||||
int drawableFrameCount = drawable.getNumFrames();
|
||||
if (drawableFrameCount == 0) {
|
||||
drawableFrameCount = 1;
|
||||
|
||||
RECT scalingRect = null;
|
||||
DefineScalingGridTag sgt = character.getScalingGridTag();
|
||||
if (sgt != null) {
|
||||
scalingRect = sgt.splitter;
|
||||
}
|
||||
|
||||
RECT boundRect = drawable.getRect();
|
||||
ExportRectangle rect = new ExportRectangle(boundRect);
|
||||
rect = mat.transform(rect);
|
||||
Matrix m = mat.clone();
|
||||
if (layer.filters != null && layer.filters.size() > 0) {
|
||||
// calculate size after applying the filters
|
||||
double deltaXMax = 0;
|
||||
double deltaYMax = 0;
|
||||
for (FILTER filter : layer.filters) {
|
||||
double x = filter.getDeltaX();
|
||||
double y = filter.getDeltaY();
|
||||
deltaXMax = Math.max(x, deltaXMax);
|
||||
deltaYMax = Math.max(y, deltaYMax);
|
||||
if (scalingRect != null) {
|
||||
ExportRectangle sourceRect[] = new ExportRectangle[9];
|
||||
ExportRectangle targetRect[] = new ExportRectangle[9];
|
||||
Matrix transforms[] = new Matrix[9];
|
||||
//mat => image
|
||||
//t =>
|
||||
//Matrix tx = transformation.concatenate(layerMatrix);
|
||||
DrawableTag dr = (DrawableTag) character;
|
||||
ExportRectangle boundsRect = new ExportRectangle(dr.getRect());
|
||||
ExportRectangle targetBoundsRect = layerMatrix.transform(boundsRect);
|
||||
DefineScalingGridTag.getSlices(targetBoundsRect, boundsRect, new ExportRectangle(scalingRect), sourceRect, targetRect, transforms);
|
||||
Shape c = g.getClip();
|
||||
AffineTransform origTransform = g.getTransform();
|
||||
for (int s = 0; s < 9; s++) {
|
||||
g.setTransform(new AffineTransform());
|
||||
ExportRectangle p1 = transformation.transform(targetRect[s]);
|
||||
g.setClip(c);
|
||||
|
||||
Rectangle2D r = new Rectangle2D.Double(p1.xMin, p1.yMin, p1.getWidth(), p1.getHeight());
|
||||
g.setClip(r);
|
||||
drawDrawable(strokeTransformation.preConcatenate(layerMatrix), layer, transforms[s], g, colorTransform, layer.blendMode, clips, prevClips, transformation.clone(), isClip, layer.clipDepth, absMat, layer.time, layer.ratio, renderContext, image, (DrawableTag) character, layer.filters, unzoom, clrTrans);
|
||||
|
||||
}
|
||||
rect.xMin -= deltaXMax * unzoom;
|
||||
rect.xMax += deltaXMax * unzoom;
|
||||
rect.yMin -= deltaYMax * unzoom;
|
||||
rect.yMax += deltaYMax * unzoom;
|
||||
}
|
||||
g.setClip(c);
|
||||
|
||||
rect.xMin -= 1 * unzoom;
|
||||
rect.yMin -= 1 * unzoom;
|
||||
rect.xMin = Math.max(0, rect.xMin);
|
||||
rect.yMin = Math.max(0, rect.yMin);
|
||||
/*
|
||||
for (int s = 0; s < 9; s++) {
|
||||
g.setTransform(new AffineTransform());
|
||||
ExportRectangle p1 = transformation.transform(targetRect[s]);
|
||||
g.setClip(c);
|
||||
|
||||
int newWidth = (int) (rect.getWidth() / unzoom);
|
||||
int newHeight = (int) (rect.getHeight() / unzoom);
|
||||
int deltaX = (int) (rect.xMin / unzoom);
|
||||
int deltaY = (int) (rect.yMin / unzoom);
|
||||
newWidth = Math.min(image.getWidth() - deltaX, newWidth) + 1;
|
||||
newHeight = Math.min(image.getHeight() - deltaY, newHeight) + 1;
|
||||
Rectangle2D r = new Rectangle2D.Double(p1.xMin, p1.yMin, p1.getWidth(), p1.getHeight());
|
||||
g.setColor(Color.blue);
|
||||
g.draw(r);
|
||||
|
||||
if (newWidth <= 0 || newHeight <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
m.translate(-rect.xMin, -rect.yMin);
|
||||
drawMatrix.translate(rect.xMin, rect.yMin);
|
||||
|
||||
SerializableImage img = null;
|
||||
String cacheKey = null;
|
||||
if (drawable instanceof ShapeTag) {
|
||||
cacheKey = ((ShapeTag) drawable).getCharacterId() + m.toString() + (clrTrans == null ? "" : clrTrans.toString());
|
||||
img = renderContext.shapeCache.get(cacheKey);
|
||||
}
|
||||
|
||||
int dframe;
|
||||
if (fontFrameNum != -1) {
|
||||
dframe = fontFrameNum;
|
||||
}*/
|
||||
g.setTransform(origTransform);
|
||||
} else {
|
||||
dframe = time % drawableFrameCount;
|
||||
}
|
||||
|
||||
if (character instanceof ButtonTag) {
|
||||
dframe = ButtonTag.FRAME_UP;
|
||||
if (renderContext.cursorPosition != null) {
|
||||
Shape buttonShape = drawable.getOutline(ButtonTag.FRAME_HITTEST, time, layer.ratio, renderContext, absMat);
|
||||
if (buttonShape.contains(renderContext.cursorPosition)) {
|
||||
renderContext.mouseOverButton = (ButtonTag) character;
|
||||
if (renderContext.mouseButton > 0) {
|
||||
dframe = ButtonTag.FRAME_DOWN;
|
||||
} else {
|
||||
dframe = ButtonTag.FRAME_OVER;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int stateCount = renderContext.stateUnderCursor == null ? 0 : renderContext.stateUnderCursor.size();
|
||||
if (img == null) {
|
||||
img = new SerializableImage(newWidth, newHeight, SerializableImage.TYPE_INT_ARGB);
|
||||
img.fillTransparent();
|
||||
|
||||
drawable.toImage(dframe, time, layer.ratio, renderContext, img, isClip || layer.clipDepth > -1, m, transformation, absMat, clrTrans);
|
||||
|
||||
if (cacheKey != null) {
|
||||
renderContext.shapeCache.put(cacheKey, img);
|
||||
}
|
||||
}
|
||||
|
||||
/*//if (renderContext.stateUnderCursor == layer) {
|
||||
if (true) {
|
||||
BufferedImage bi = img.getBufferedImage();
|
||||
ColorModel cm = bi.getColorModel();
|
||||
boolean isAlphaPremultiplied = cm.isAlphaPremultiplied();
|
||||
WritableRaster raster = bi.copyData(null);
|
||||
img = new SerializableImage(new BufferedImage(cm, raster, isAlphaPremultiplied, null));
|
||||
Graphics2D gg = (Graphics2D) img.getGraphics();
|
||||
gg.setStroke(new BasicStroke(3));
|
||||
gg.setPaint(Color.red);
|
||||
gg.setTransform(AffineTransform.getTranslateInstance(0, 0));
|
||||
gg.draw(SHAPERECORD.twipToPixelShape(drawable.getOutline(dframe, layer.time + time, layer.ratio, renderContext, m)));
|
||||
}*/
|
||||
if (layer.filters != null) {
|
||||
for (FILTER filter : layer.filters) {
|
||||
img = filter.apply(img);
|
||||
}
|
||||
}
|
||||
if (layer.blendMode > 1) {
|
||||
if (layer.colorTransForm != null) {
|
||||
img = layer.colorTransForm.apply(img);
|
||||
}
|
||||
}
|
||||
|
||||
drawMatrix.translateX /= unzoom;
|
||||
drawMatrix.translateY /= unzoom;
|
||||
AffineTransform trans = drawMatrix.toTransform();
|
||||
|
||||
switch (layer.blendMode) {
|
||||
case 0:
|
||||
case 1:
|
||||
g.setComposite(AlphaComposite.SrcOver);
|
||||
break;
|
||||
case 2: // Layer
|
||||
g.setComposite(AlphaComposite.SrcOver);
|
||||
break;
|
||||
case 3:
|
||||
g.setComposite(BlendComposite.Multiply);
|
||||
break;
|
||||
case 4:
|
||||
g.setComposite(BlendComposite.Screen);
|
||||
break;
|
||||
case 5:
|
||||
g.setComposite(BlendComposite.Lighten);
|
||||
break;
|
||||
case 6:
|
||||
g.setComposite(BlendComposite.Darken);
|
||||
break;
|
||||
case 7:
|
||||
g.setComposite(BlendComposite.Difference);
|
||||
break;
|
||||
case 8:
|
||||
g.setComposite(BlendComposite.Add);
|
||||
break;
|
||||
case 9:
|
||||
g.setComposite(BlendComposite.Subtract);
|
||||
break;
|
||||
case 10:
|
||||
g.setComposite(BlendComposite.Invert);
|
||||
break;
|
||||
case 11:
|
||||
g.setComposite(BlendComposite.Alpha);
|
||||
break;
|
||||
case 12:
|
||||
g.setComposite(BlendComposite.Erase);
|
||||
break;
|
||||
case 13:
|
||||
g.setComposite(BlendComposite.Overlay);
|
||||
break;
|
||||
case 14:
|
||||
g.setComposite(BlendComposite.HardLight);
|
||||
break;
|
||||
default: // Not implemented
|
||||
g.setComposite(AlphaComposite.SrcOver);
|
||||
break;
|
||||
}
|
||||
|
||||
if (layer.clipDepth > -1) {
|
||||
BufferedImage mask = new BufferedImage(image.getWidth(), image.getHeight(), image.getType());
|
||||
Graphics2D gm = (Graphics2D) mask.getGraphics();
|
||||
gm.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
|
||||
gm.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
|
||||
gm.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
gm.setComposite(AlphaComposite.Src);
|
||||
gm.setColor(new Color(0, 0, 0, 0f));
|
||||
gm.fillRect(0, 0, image.getWidth(), image.getHeight());
|
||||
gm.setTransform(trans);
|
||||
gm.drawImage(img.getBufferedImage(), 0, 0, null);
|
||||
Clip clip = new Clip(Helper.imageToShape(mask), layer.clipDepth); // Maybe we can get current outline instead converting from image (?)
|
||||
clips.add(clip);
|
||||
prevClips.add(g.getClip());
|
||||
g.setTransform(AffineTransform.getTranslateInstance(0, 0));
|
||||
g.setClip(clip.shape);
|
||||
} else {
|
||||
if (renderContext.cursorPosition != null) {
|
||||
if (drawable instanceof DefineSpriteTag) {
|
||||
if (renderContext.stateUnderCursor.size() > stateCount) {
|
||||
renderContext.stateUnderCursor.add(layer);
|
||||
}
|
||||
} else if (absMat.transform(new ExportRectangle(boundRect)).contains(renderContext.cursorPosition)) {
|
||||
Shape shape = drawable.getOutline(dframe, time, layer.ratio, renderContext, absMat);
|
||||
if (shape.contains(renderContext.cursorPosition)) {
|
||||
renderContext.stateUnderCursor.add(layer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (renderContext.borderImage != null) {
|
||||
Graphics2D g2 = (Graphics2D) renderContext.borderImage.getGraphics();
|
||||
g2.setPaint(Color.red);
|
||||
g2.setStroke(new BasicStroke(2));
|
||||
Shape shape = drawable.getOutline(dframe, time, layer.ratio, renderContext, absMat.preConcatenate(Matrix.getScaleInstance(1 / SWF.unitDivisor)));
|
||||
g2.draw(shape);
|
||||
}
|
||||
|
||||
g.setTransform(trans);
|
||||
g.drawImage(img.getBufferedImage(), 0, 0, null);
|
||||
drawDrawable(strokeTransformation, layer, layerMatrix, g, colorTransform, layer.blendMode, clips, prevClips, transformation.clone(), isClip, layer.clipDepth, absMat, layer.time, layer.ratio, renderContext, image, (DrawableTag) character, layer.filters, unzoom, clrTrans);
|
||||
}
|
||||
} else if (character instanceof BoundedTag) {
|
||||
showPlaceholder = true;
|
||||
@@ -1075,7 +1060,7 @@ public class Timeline {
|
||||
}
|
||||
}
|
||||
|
||||
public Shape getOutline(int frame, int time, RenderContext renderContext, Matrix transformation) {
|
||||
public Shape getOutline(int frame, int time, RenderContext renderContext, Matrix transformation, boolean stroked) {
|
||||
Frame fr = getFrame(frame);
|
||||
Area area = new Area();
|
||||
Stack<Clip> clips = new Stack<>();
|
||||
@@ -1112,7 +1097,7 @@ public class Timeline {
|
||||
dframe = ButtonTag.FRAME_UP;
|
||||
if (renderContext.cursorPosition != null) {
|
||||
ButtonTag buttonTag = (ButtonTag) character;
|
||||
Shape buttonShape = buttonTag.getOutline(ButtonTag.FRAME_HITTEST, time, layer.ratio, renderContext, m);
|
||||
Shape buttonShape = buttonTag.getOutline(ButtonTag.FRAME_HITTEST, time, layer.ratio, renderContext, m, stroked);
|
||||
if (buttonShape.contains(renderContext.cursorPosition)) {
|
||||
if (renderContext.mouseButton > 0) {
|
||||
dframe = ButtonTag.FRAME_DOWN;
|
||||
@@ -1123,7 +1108,7 @@ public class Timeline {
|
||||
}
|
||||
}
|
||||
|
||||
Shape cshape = ((DrawableTag) character).getOutline(dframe, time, layer.ratio, renderContext, m);
|
||||
Shape cshape = ((DrawableTag) character).getOutline(dframe, time, layer.ratio, renderContext, m, stroked);
|
||||
Area addArea = new Area(cshape);
|
||||
if (currentClip != null) {
|
||||
Area a = new Area(new Rectangle(displayRect.Xmin, displayRect.Ymin, displayRect.getWidth(), displayRect.getHeight()));
|
||||
|
||||
@@ -77,16 +77,23 @@ public class SHAPE implements NeedsCharacters, Serializable {
|
||||
return SHAPERECORD.getBounds(shapeRecords);
|
||||
}
|
||||
|
||||
public Shape getOutline(SWF swf) {
|
||||
public Shape getOutline(SWF swf, boolean stroked) {
|
||||
if (cachedOutline != null) {
|
||||
return cachedOutline;
|
||||
}
|
||||
|
||||
List<GeneralPath> paths = PathExporter.export(swf, this);
|
||||
List<GeneralPath> strokes = new ArrayList<>();
|
||||
List<GeneralPath> paths = PathExporter.export(swf, this, strokes);
|
||||
|
||||
Area area = new Area();
|
||||
for (GeneralPath path : paths) {
|
||||
area.add(new Area(path));
|
||||
}
|
||||
if (stroked) {
|
||||
for (GeneralPath path : strokes) {
|
||||
area.add(new Area(path));
|
||||
}
|
||||
}
|
||||
|
||||
cachedOutline = area;
|
||||
return area;
|
||||
|
||||
@@ -220,7 +220,7 @@ public abstract class SHAPERECORD implements Cloneable, NeedsCharacters, Seriali
|
||||
Matrix transformation = new Matrix();
|
||||
transformation.translate(px, py);
|
||||
transformation = transformation.concatenate(Matrix.getScaleInstance(ratio));
|
||||
BitmapExporter.export(swf, shape, color, image, transformation, colorTransform);
|
||||
BitmapExporter.export(swf, shape, color, image, transformation, transformation, colorTransform);
|
||||
|
||||
// draw bounding boxes
|
||||
if (DRAW_BOUNDING_BOX) {
|
||||
|
||||
Reference in New Issue
Block a user