Merge origin/master

This commit is contained in:
Jindra Petřík
2016-02-13 11:49:17 +01:00
20 changed files with 678 additions and 546 deletions

View File

@@ -75,9 +75,7 @@ import com.jpexs.decompiler.flash.configuration.Configuration;
import com.jpexs.decompiler.flash.dumpview.DumpInfo;
import com.jpexs.decompiler.flash.dumpview.DumpInfoSwfNode;
import com.jpexs.decompiler.flash.ecma.Null;
import com.jpexs.decompiler.flash.exporters.commonshape.ExportRectangle;
import com.jpexs.decompiler.flash.exporters.commonshape.Matrix;
import com.jpexs.decompiler.flash.exporters.commonshape.SVGExporter;
import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode;
import com.jpexs.decompiler.flash.exporters.script.AS2ScriptExporter;
import com.jpexs.decompiler.flash.exporters.script.AS3ScriptExporter;
@@ -129,11 +127,8 @@ import com.jpexs.decompiler.flash.tags.base.SoundTag;
import com.jpexs.decompiler.flash.tags.base.TextTag;
import com.jpexs.decompiler.flash.tags.enums.ImageFormat;
import com.jpexs.decompiler.flash.timeline.AS2Package;
import com.jpexs.decompiler.flash.timeline.Clip;
import com.jpexs.decompiler.flash.timeline.DepthState;
import com.jpexs.decompiler.flash.timeline.Frame;
import com.jpexs.decompiler.flash.timeline.FrameScript;
import com.jpexs.decompiler.flash.timeline.SvgClip;
import com.jpexs.decompiler.flash.timeline.TagScript;
import com.jpexs.decompiler.flash.timeline.Timeline;
import com.jpexs.decompiler.flash.timeline.Timelined;
@@ -145,8 +140,6 @@ import com.jpexs.decompiler.flash.types.RECT;
import com.jpexs.decompiler.flash.types.SHAPE;
import com.jpexs.decompiler.flash.types.annotations.Internal;
import com.jpexs.decompiler.flash.types.annotations.SWFField;
import com.jpexs.decompiler.flash.types.filters.BlendComposite;
import com.jpexs.decompiler.flash.types.filters.FILTER;
import com.jpexs.decompiler.flash.xfl.FLAVersion;
import com.jpexs.decompiler.flash.xfl.XFLConverter;
import com.jpexs.decompiler.graph.DottedChain;
@@ -166,15 +159,11 @@ import com.jpexs.helpers.ProgressListener;
import com.jpexs.helpers.SerializableImage;
import com.jpexs.helpers.utf8.Utf8Helper;
import java.awt.AlphaComposite;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
@@ -195,7 +184,6 @@ import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.Stack;
import java.util.TreeMap;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -2661,114 +2649,13 @@ public final class SWF implements SWFContainerItem, Timelined {
return ret;
}
public static void frameToSvg(Timeline timeline, int frame, int time, DepthState stateUnderCursor, int mouseButton, SVGExporter exporter, ColorTransform colorTransform, int level, double zoom) throws IOException {
if (timeline.getFrameCount() <= frame) {
return;
}
Frame frameObj = timeline.getFrame(frame);
List<SvgClip> clips = new ArrayList<>();
List<String> prevClips = new ArrayList<>();
int maxDepth = timeline.getMaxDepth();
for (int i = 1; i <= maxDepth; i++) {
for (int c = 0; c < clips.size(); c++) {
if (clips.get(c).depth == i) {
exporter.setClip(prevClips.get(c));
prevClips.remove(c);
clips.remove(c);
}
}
if (!frameObj.layers.containsKey(i)) {
continue;
}
DepthState layer = frameObj.layers.get(i);
if (!timeline.swf.getCharacters().containsKey(layer.characterId)) {
continue;
}
if (!layer.isVisible) {
continue;
}
CharacterTag character = timeline.swf.getCharacter(layer.characterId);
ColorTransform clrTrans = colorTransform;
if (layer.colorTransForm != null && layer.blendMode <= 1) { // Normal blend mode
clrTrans = clrTrans == null ? layer.colorTransForm : colorTransform.merge(layer.colorTransForm);
}
if (character instanceof DrawableTag) {
DrawableTag drawable = (DrawableTag) character;
String assetName;
Tag drawableTag = (Tag) drawable;
RECT boundRect = drawable.getRect();
if (exporter.exportedTags.containsKey(drawableTag)) {
assetName = exporter.exportedTags.get(drawableTag);
} else {
assetName = getTagIdPrefix(drawableTag, exporter);
exporter.exportedTags.put(drawableTag, assetName);
exporter.createDefGroup(new ExportRectangle(boundRect), assetName);
drawable.toSVG(exporter, layer.ratio, clrTrans, level + 1, zoom);
exporter.endGroup();
}
ExportRectangle rect = new ExportRectangle(boundRect);
// TODO: if (layer.filters != null)
// TODO: if (layer.blendMode > 1)
if (layer.clipDepth > -1) {
String clipName = exporter.getUniqueId("clipPath");
exporter.createClipPath(new Matrix(), clipName);
SvgClip clip = new SvgClip(clipName, layer.clipDepth);
clips.add(clip);
prevClips.add(exporter.getClip());
Matrix mat = Matrix.getTranslateInstance(rect.xMin, rect.yMin).preConcatenate(new Matrix(layer.matrix));
exporter.addUse(mat, boundRect, assetName, layer.instanceName);
exporter.setClip(clip.shape);
exporter.endGroup();
} else {
Matrix mat = Matrix.getTranslateInstance(rect.xMin, rect.yMin).preConcatenate(new Matrix(layer.matrix));
exporter.addUse(mat, boundRect, assetName, layer.instanceName);
}
}
}
}
private static String getTagIdPrefix(Tag tag, SVGExporter exporter) {
if (tag instanceof ShapeTag) {
return exporter.getUniqueId("shape");
}
if (tag instanceof MorphShapeTag) {
return exporter.getUniqueId("morphshape");
}
if (tag instanceof DefineSpriteTag) {
return exporter.getUniqueId("sprite");
}
if (tag instanceof TextTag) {
return exporter.getUniqueId("text");
}
if (tag instanceof ButtonTag) {
return exporter.getUniqueId("button");
}
return exporter.getUniqueId("tag");
}
public static SerializableImage frameToImageGet(Timeline timeline, int frame, int time, Point cursorPosition, int mouseButton, RECT displayRect, Matrix transformation, Matrix absoluteTransformation, ColorTransform colorTransform, Color backGroundColor, boolean useCache, double zoom) {
SWF swf = timeline.swf;
String key = "frame_" + frame + "_" + time + "_" + timeline.id + "_" + swf.hashCode() + "_" + zoom;
SerializableImage image;
if (useCache) {
image = swf.getFromCache(key);
if (image != null) {
return image;
}
}
public static SerializableImage frameToImageGet(Timeline timeline, int frame, int time, Point cursorPosition, int mouseButton, RECT displayRect, Matrix transformation, Matrix absoluteTransformation, ColorTransform colorTransform, Color backGroundColor, double zoom) {
if (timeline.getFrameCount() == 0) {
return new SerializableImage(1, 1, SerializableImage.TYPE_INT_ARGB);
}
RECT rect = displayRect;
image = new SerializableImage((int) (rect.getWidth() * zoom / SWF.unitDivisor) + 1,
SerializableImage image = new SerializableImage((int) (rect.getWidth() * zoom / SWF.unitDivisor) + 1,
(int) (rect.getHeight() * zoom / SWF.unitDivisor) + 1, SerializableImage.TYPE_INT_ARGB);
if (backGroundColor == null) {
image.fillTransparent();
@@ -2785,303 +2672,11 @@ public final class SWF implements SWFContainerItem, Timelined {
RenderContext renderContext = new RenderContext();
renderContext.cursorPosition = cursorPosition;
renderContext.mouseButton = mouseButton;
frameToImage(timeline, frame, time, renderContext, image, false, m, absoluteTransformation, colorTransform);
if (useCache) {
swf.putToCache(key, image);
}
timeline.toImage(frame, time, 0, renderContext, image, false, m, absoluteTransformation, colorTransform);
return image;
}
public static void framesToImage(Timeline timeline, List<SerializableImage> ret, int startFrame, int stopFrame, RenderContext renderContext, RECT displayRect, int totalFrameCount, Stack<Integer> visited, Matrix transformation, Matrix absoluteTransformation, ColorTransform colorTransform, double zoom) {
RECT rect = displayRect;
for (int f = 0; f < timeline.getFrameCount(); f++) {
SerializableImage image = new SerializableImage((int) (rect.getWidth() / SWF.unitDivisor) + 1,
(int) (rect.getHeight() / SWF.unitDivisor) + 1, SerializableImage.TYPE_INT_ARGB);
image.fillTransparent();
Matrix m = new Matrix();
m.translate(-rect.Xmin, -rect.Ymin);
frameToImage(timeline, f, 0, renderContext, image, false, m, absoluteTransformation, colorTransform);
ret.add(image);
}
}
public static void frameToImage(Timeline timeline, int frame, int time, RenderContext renderContext, SerializableImage image, boolean isClip, Matrix transformation, Matrix absoluteTransformation, ColorTransform colorTransform) {
double unzoom = SWF.unitDivisor;
if (timeline.getFrameCount() <= frame) {
return;
}
Frame frameObj = timeline.getFrame(frame);
Graphics2D g = (Graphics2D) image.getGraphics();
g.setPaint(frameObj.backgroundColor.toColor());
g.fill(new Rectangle(image.getWidth(), image.getHeight()));
g.setTransform(transformation.toTransform());
List<Clip> clips = new ArrayList<>();
List<Shape> prevClips = new ArrayList<>();
int maxDepth = timeline.getMaxDepth();
for (int i = 1; i <= maxDepth; i++) {
for (int c = 0; c < clips.size(); c++) {
if (clips.get(c).depth == i) {
g.setClip(prevClips.get(c));
prevClips.remove(c);
clips.remove(c);
}
}
if (!frameObj.layers.containsKey(i)) {
continue;
}
DepthState layer = frameObj.layers.get(i);
if (!timeline.swf.getCharacters().containsKey(layer.characterId)) {
continue;
}
if (!layer.isVisible) {
continue;
}
CharacterTag character = timeline.swf.getCharacter(layer.characterId);
Matrix layerMatrix = new Matrix(layer.matrix);
Matrix mat = transformation.concatenate(layerMatrix);
Matrix absMat = absoluteTransformation.concatenate(layerMatrix);
ColorTransform clrTrans = colorTransform;
if (layer.colorTransForm != null && layer.blendMode <= 1) { // Normal blend mode
clrTrans = clrTrans == null ? layer.colorTransForm : colorTransform.merge(layer.colorTransForm);
}
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 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);
}
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) {
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 (timeline.fontFrameNum != -1) {
dframe = timeline.fontFrameNum;
} 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, 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);
}
} else if (character instanceof BoundedTag) {
showPlaceholder = true;
}
if (showPlaceholder) {
Matrix mat2 = mat.clone();
mat2.translateX /= unzoom;
mat2.translateY /= unzoom;
AffineTransform trans = mat2.toTransform();
g.setTransform(trans);
BoundedTag b = (BoundedTag) character;
g.setPaint(new Color(255, 255, 255, 128));
g.setComposite(BlendComposite.Invert);
RECT r = b.getRect();
int div = (int) unzoom;
g.drawString(character.toString(), r.Xmin / div + 3, r.Ymin / div + 15);
g.draw(new Rectangle(r.Xmin / div, r.Ymin / div, r.getWidth() / div, r.getHeight() / div));
g.drawLine(r.Xmin / div, r.Ymin / div, r.Xmax / div, r.Ymax / div);
g.drawLine(r.Xmax / div, r.Ymin / div, r.Xmin / div, r.Ymax / div);
g.setComposite(AlphaComposite.Dst);
}
}
g.setTransform(AffineTransform.getScaleInstance(1, 1));
}
private void removeTagWithDependenciesFromTimeline(Tag toRemove, Timeline timeline) {
Map<Integer, Integer> stage = new HashMap<>();
Set<Integer> dependingChars = new HashSet<>();

View File

@@ -16,6 +16,7 @@
*/
package com.jpexs.decompiler.flash.abc;
import com.jpexs.decompiler.flash.EndOfStreamException;
import com.jpexs.decompiler.flash.EventListener;
import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.abc.avm2.AVM2Code;
@@ -54,6 +55,7 @@ import com.jpexs.decompiler.flash.abc.usages.MethodParamsMultinameUsage;
import com.jpexs.decompiler.flash.abc.usages.MethodReturnTypeMultinameUsage;
import com.jpexs.decompiler.flash.abc.usages.MultinameUsage;
import com.jpexs.decompiler.flash.abc.usages.TypeNameMultinameUsage;
import com.jpexs.decompiler.flash.dumpview.DumpInfo;
import com.jpexs.decompiler.flash.helpers.SWFDecompilerPlugin;
import com.jpexs.decompiler.flash.tags.ABCContainerTag;
import com.jpexs.decompiler.flash.tags.Tag;
@@ -543,29 +545,36 @@ public class ABC {
int bodies_count = ais.readU30("bodies_count");
bodies = new ArrayList<>(bodies_count);
for (int i = 0; i < bodies_count; i++) {
DumpInfo di = ais.dumpInfo;
ais.newDumpLevel("method_body", "method_body_info");
MethodBody mb = new MethodBody(this, null, null, null); // do not create Traits in constructor
mb.method_info = ais.readU30("method_info");
mb.max_stack = ais.readU30("max_stack");
mb.max_regs = ais.readU30("max_regs");
mb.init_scope_depth = ais.readU30("init_scope_depth");
mb.max_scope_depth = ais.readU30("max_scope_depth");
int code_length = ais.readU30("code_length");
mb.setCodeBytes(ais.readBytes(code_length, "code"));
int ex_count = ais.readU30("ex_count");
mb.exceptions = new ABCException[ex_count];
for (int j = 0; j < ex_count; j++) {
ABCException abce = new ABCException();
abce.start = ais.readU30("start");
abce.end = ais.readU30("end");
abce.target = ais.readU30("target");
abce.type_index = ais.readU30("type_index");
abce.name_index = ais.readU30("name_index");
mb.exceptions[j] = abce;
try {
mb.method_info = ais.readU30("method_info");
mb.max_stack = ais.readU30("max_stack");
mb.max_regs = ais.readU30("max_regs");
mb.init_scope_depth = ais.readU30("init_scope_depth");
mb.max_scope_depth = ais.readU30("max_scope_depth");
int code_length = ais.readU30("code_length");
mb.setCodeBytes(ais.readBytes(code_length, "code"));
int ex_count = ais.readU30("ex_count");
mb.exceptions = new ABCException[ex_count];
for (int j = 0; j < ex_count; j++) {
ABCException abce = new ABCException();
abce.start = ais.readU30("start");
abce.end = ais.readU30("end");
abce.target = ais.readU30("target");
abce.type_index = ais.readU30("type_index");
abce.name_index = ais.readU30("name_index");
mb.exceptions[j] = abce;
}
mb.traits = ais.readTraits("traits");
bodies.add(mb);
ais.endDumpLevel();
} catch (EndOfStreamException ex) {
logger.log(Level.SEVERE, "MethodBody reading: End of stream", ex);
ais.endDumpLevelUntil(di);
break;
}
mb.traits = ais.readTraits("traits");
bodies.add(mb);
ais.endDumpLevel();
SWFDecompilerPlugin.fireMethodBodyParsed(mb, swf);
}

View File

@@ -200,7 +200,8 @@ public class FrameExporter {
if (fbackgroundColor != null) {
exporter.setBackGroundColor(fbackgroundColor);
}
SWF.frameToSvg(tim, frame, 0, null, 0, exporter, null, 0, settings.zoom);
tim.toSVG(frame, 0, null, 0, exporter, null, 0, settings.zoom);
fos.write(Utf8Helper.getBytes(exporter.getSVG()));
}
ret.add(f);
@@ -359,7 +360,7 @@ public class FrameExporter {
}
int fframe = fframes.get(pos++);
BufferedImage result = SWF.frameToImageGet(ftim, fframe, fframe, null, 0, ftim.displayRect, new Matrix(), new Matrix(), null, fbackgroundColor, false, settings.zoom).getBufferedImage();
BufferedImage result = SWF.frameToImageGet(ftim, fframe, fframe, null, 0, ftim.displayRect, new Matrix(), new Matrix(), null, fbackgroundColor, settings.zoom).getBufferedImage();
if (evl != null) {
evl.handleExportedEvent("frame", pos, fframes.size(), tagName);

View File

@@ -50,7 +50,7 @@ public class DefineFont2Tag extends FontTag {
public static final String NAME = "DefineFont2";
@SWFType(BasicType.UI16)
public int fontId;
public int fontID;
public boolean fontFlagsHasLayout;
@@ -106,7 +106,7 @@ public class DefineFont2Tag extends FontTag {
*/
public DefineFont2Tag(SWF swf) {
super(swf, ID, NAME, null);
fontId = swf.getNextCharacterId();
fontID = swf.getNextCharacterId();
languageCode = new LANGCODE();
fontName = "New font";
glyphShapeTable = new ArrayList<>();
@@ -127,7 +127,7 @@ public class DefineFont2Tag extends FontTag {
@Override
public final void readData(SWFInputStream sis, ByteArrayRange data, int level, boolean parallel, boolean skipUnusualTags, boolean lazy) throws IOException {
fontId = sis.readUI16("fontId");
fontID = sis.readUI16("fontId");
fontFlagsHasLayout = sis.readUB(1, "fontFlagsHasLayout") == 1;
fontFlagsShiftJIS = sis.readUB(1, "fontFlagsShiftJIS") == 1;
fontFlagsSmallText = sis.readUB(1, "fontFlagsSmallText") == 1;
@@ -246,7 +246,7 @@ public class DefineFont2Tag extends FontTag {
@Override
public void getData(SWFOutputStream sos) throws IOException {
checkWideParameters();
sos.writeUI16(fontId);
sos.writeUI16(fontID);
sos.writeUB(1, fontFlagsHasLayout ? 1 : 0);
sos.writeUB(1, fontFlagsShiftJIS ? 1 : 0);
sos.writeUB(1, fontFlagsSmallText ? 1 : 0);
@@ -348,12 +348,12 @@ public class DefineFont2Tag extends FontTag {
@Override
public int getCharacterId() {
return fontId;
return fontID;
}
@Override
public void setCharacterId(int characterId) {
this.fontId = characterId;
this.fontID = characterId;
}
@Override
@@ -467,7 +467,7 @@ public class DefineFont2Tag extends FontTag {
}
if (!exists) {
shiftGlyphIndices(fontId, pos);
shiftGlyphIndices(fontID, pos);
glyphShapeTable.add(pos, shp);
codeTable.add(pos, (int) character);
} else {

View File

@@ -50,7 +50,7 @@ public class DefineFont3Tag extends FontTag {
public static final String NAME = "DefineFont3";
@SWFType(BasicType.UI16)
public int fontId;
public int fontID;
public boolean fontFlagsHasLayout;
@@ -106,7 +106,7 @@ public class DefineFont3Tag extends FontTag {
*/
public DefineFont3Tag(SWF swf) {
super(swf, ID, NAME, null);
fontId = swf.getNextCharacterId();
fontID = swf.getNextCharacterId();
languageCode = new LANGCODE();
fontName = "New font";
glyphShapeTable = new ArrayList<>();
@@ -120,7 +120,7 @@ public class DefineFont3Tag extends FontTag {
@Override
public final void readData(SWFInputStream sis, ByteArrayRange data, int level, boolean parallel, boolean skipUnusualTags, boolean lazy) throws IOException {
fontId = sis.readUI16("fontId");
fontID = sis.readUI16("fontId");
fontFlagsHasLayout = sis.readUB(1, "fontFlagsHasLayout") == 1;
fontFlagsShiftJIS = sis.readUB(1, "fontFlagsShiftJIS") == 1;
fontFlagsSmallText = sis.readUB(1, "fontFlagsSmallText") == 1;
@@ -246,7 +246,7 @@ public class DefineFont3Tag extends FontTag {
}
byte[] baGlyphShapes = baosGlyphShapes.toByteArray();
sos.writeUI16(fontId);
sos.writeUI16(fontID);
sos.writeUB(1, fontFlagsHasLayout ? 1 : 0);
sos.writeUB(1, fontFlagsShiftJIS ? 1 : 0);
sos.writeUB(1, fontFlagsSmallText ? 1 : 0);
@@ -341,12 +341,12 @@ public class DefineFont3Tag extends FontTag {
@Override
public int getCharacterId() {
return fontId;
return fontID;
}
@Override
public void setCharacterId(int characterId) {
this.fontId = characterId;
this.fontID = characterId;
}
@Override
@@ -435,7 +435,7 @@ public class DefineFont3Tag extends FontTag {
Tag t = swf.getTags().get(i);
if (t instanceof DefineFontAlignZonesTag) {
DefineFontAlignZonesTag fa = (DefineFontAlignZonesTag) t;
if (fa.fontID == fontId) {
if (fa.fontID == fontID) {
swf.removeTag(t);
i--;
}
@@ -460,7 +460,7 @@ public class DefineFont3Tag extends FontTag {
}
if (!exists) {
shiftGlyphIndices(fontId, pos);
shiftGlyphIndices(fontID, pos);
glyphShapeTable.add(pos, shp);
codeTable.add(pos, (int) character);
} else {

View File

@@ -19,7 +19,7 @@ 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.tags.base.CharacterIdTag;
import com.jpexs.decompiler.flash.tags.base.FontInfoTag;
import com.jpexs.decompiler.flash.types.BasicType;
import com.jpexs.decompiler.flash.types.LANGCODE;
import com.jpexs.decompiler.flash.types.annotations.Reserved;
@@ -36,15 +36,12 @@ import java.util.List;
* @author JPEXS
*/
@SWFVersion(from = 6)
public class DefineFontInfo2Tag extends Tag implements CharacterIdTag {
public class DefineFontInfo2Tag extends FontInfoTag {
public static final int ID = 62;
public static final String NAME = "DefineFontInfo2";
@SWFType(BasicType.UI16)
public int fontID;
public String fontName;
@Reserved
@@ -143,12 +140,38 @@ public class DefineFontInfo2Tag extends Tag implements CharacterIdTag {
}
@Override
public int getCharacterId() {
return fontID;
public List<Integer> getCodeTable() {
return codeTable;
}
@Override
public void setCharacterId(int characterId) {
this.fontID = characterId;
public void addCharacter(int index, int character) {
codeTable.add(index, character);
setModified(true);
}
@Override
public String getFontName() {
return fontName;
}
@Override
public boolean getFontFlagsBold() {
return fontFlagsBold;
}
@Override
public void setFontFlagsBold(boolean value) {
fontFlagsBold = value;
}
@Override
public boolean getFontFlagsItalic() {
return fontFlagsItalic;
}
@Override
public void setFontFlagsItalic(boolean value) {
fontFlagsItalic = value;
}
}

View File

@@ -19,7 +19,7 @@ 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.tags.base.CharacterIdTag;
import com.jpexs.decompiler.flash.tags.base.FontInfoTag;
import com.jpexs.decompiler.flash.types.BasicType;
import com.jpexs.decompiler.flash.types.annotations.Reserved;
import com.jpexs.decompiler.flash.types.annotations.SWFType;
@@ -35,15 +35,12 @@ import java.util.List;
* @author JPEXS
*/
@SWFVersion(from = 1)
public class DefineFontInfoTag extends Tag implements CharacterIdTag {
public class DefineFontInfoTag extends FontInfoTag {
public static final int ID = 13;
public static final String NAME = "DefineFontInfo";
@SWFType(BasicType.UI16)
public int fontId;
public String fontName;
@Reserved
@@ -90,7 +87,7 @@ public class DefineFontInfoTag extends Tag implements CharacterIdTag {
@Override
public final void readData(SWFInputStream sis, ByteArrayRange data, int level, boolean parallel, boolean skipUnusualTags, boolean lazy) throws IOException {
fontId = sis.readUI16("fontId");
fontID = sis.readUI16("fontId");
if (swf.version >= 6) {
fontName = sis.readNetString("fontName", Utf8Helper.charset);
} else {
@@ -121,7 +118,7 @@ public class DefineFontInfoTag extends Tag implements CharacterIdTag {
*/
@Override
public void getData(SWFOutputStream sos) throws IOException {
sos.writeUI16(fontId);
sos.writeUI16(fontID);
if (swf.version >= 6) {
sos.writeNetString(fontName, Utf8Helper.charset);
} else {
@@ -144,12 +141,38 @@ public class DefineFontInfoTag extends Tag implements CharacterIdTag {
}
@Override
public int getCharacterId() {
return fontId;
public List<Integer> getCodeTable() {
return codeTable;
}
@Override
public void setCharacterId(int characterId) {
this.fontId = characterId;
public void addCharacter(int index, int character) {
codeTable.add(index, character);
setModified(true);
}
@Override
public String getFontName() {
return fontName;
}
@Override
public boolean getFontFlagsBold() {
return fontFlagsBold;
}
@Override
public void setFontFlagsBold(boolean value) {
fontFlagsBold = value;
}
@Override
public boolean getFontFlagsItalic() {
return fontFlagsItalic;
}
@Override
public void setFontFlagsItalic(boolean value) {
fontFlagsItalic = value;
}
}

View File

@@ -19,6 +19,8 @@ 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.tags.base.CharacterIdTag;
import com.jpexs.decompiler.flash.tags.base.FontInfoTag;
import com.jpexs.decompiler.flash.tags.base.FontTag;
import com.jpexs.decompiler.flash.types.BasicType;
import com.jpexs.decompiler.flash.types.SHAPE;
@@ -50,10 +52,7 @@ public class DefineFontTag extends FontTag {
public List<SHAPE> glyphShapeTable;
@Internal
private DefineFontInfoTag fontInfoTag = null;
@Internal
private DefineFontInfo2Tag fontInfo2Tag = null;
private FontInfoTag fontInfoTag = null;
/**
* Constructor
@@ -139,17 +138,14 @@ public class DefineFontTag extends FontTag {
private void ensureFontInfo() {
if (fontInfoTag == null) {
for (Tag t : swf.getTags()) {
if (t instanceof DefineFontInfoTag) {
if (((DefineFontInfoTag) t).fontId == fontId) {
fontInfoTag = (DefineFontInfoTag) t;
break;
}
}
if (t instanceof DefineFontInfo2Tag) {
if (((DefineFontInfo2Tag) t).fontID == fontId) {
fontInfo2Tag = (DefineFontInfo2Tag) t;
break;
List<CharacterIdTag> characterIdTags = swf.getCharacterIdTags(fontId);
if (characterIdTags != null) {
for (CharacterIdTag t : characterIdTags) {
if (t instanceof FontInfoTag) {
if (((FontInfoTag) t).fontID == fontId) {
fontInfoTag = (FontInfoTag) t;
break;
}
}
}
}
@@ -159,10 +155,8 @@ public class DefineFontTag extends FontTag {
@Override
public char glyphToChar(int glyphIndex) {
ensureFontInfo();
if (fontInfo2Tag != null) {
return (char) (int) fontInfo2Tag.codeTable.get(glyphIndex);
} else if (fontInfoTag != null) {
return (char) (int) fontInfoTag.codeTable.get(glyphIndex);
if (fontInfoTag != null) {
return (char) (int) fontInfoTag.getCodeTable().get(glyphIndex);
} else {
return '?';
}
@@ -171,10 +165,8 @@ public class DefineFontTag extends FontTag {
@Override
public int charToGlyph(char c) {
ensureFontInfo();
if (fontInfo2Tag != null) {
return fontInfo2Tag.codeTable.indexOf((int) c);
} else if (fontInfoTag != null) {
return fontInfoTag.codeTable.indexOf((int) c);
if (fontInfoTag != null) {
return fontInfoTag.getCodeTable().indexOf((int) c);
}
return -1;
@@ -198,33 +190,24 @@ public class DefineFontTag extends FontTag {
@Override
public String getFontNameIntag() {
ensureFontInfo();
if (fontInfo2Tag != null) {
return fontInfo2Tag.fontName;
}
if (fontInfoTag != null) {
return fontInfoTag.fontName;
return fontInfoTag.getFontName();
}
return null;
}
@Override
public boolean isBold() {
if (fontInfo2Tag != null) {
return fontInfo2Tag.fontFlagsBold;
}
if (fontInfoTag != null) {
return fontInfoTag.fontFlagsBold;
return fontInfoTag.getFontFlagsBold();
}
return false;
}
@Override
public boolean isItalic() {
if (fontInfo2Tag != null) {
return fontInfo2Tag.fontFlagsItalic;
}
if (fontInfoTag != null) {
return fontInfoTag.fontFlagsItalic;
return fontInfoTag.getFontFlagsItalic();
}
return false;
}
@@ -236,12 +219,12 @@ public class DefineFontTag extends FontTag {
@Override
public boolean isBoldEditable() {
return fontInfo2Tag != null || fontInfoTag != null;
return fontInfoTag != null;
}
@Override
public boolean isItalicEditable() {
return fontInfo2Tag != null || fontInfoTag != null;
return fontInfoTag != null;
}
@Override
@@ -250,21 +233,15 @@ public class DefineFontTag extends FontTag {
@Override
public void setBold(boolean value) {
if (fontInfo2Tag != null) {
fontInfo2Tag.fontFlagsBold = value;
}
if (fontInfoTag != null) {
fontInfoTag.fontFlagsBold = value;
fontInfoTag.setFontFlagsBold(value);
}
}
@Override
public void setItalic(boolean value) {
if (fontInfo2Tag != null) {
fontInfo2Tag.fontFlagsItalic = value;
}
if (fontInfoTag != null) {
fontInfoTag.fontFlagsItalic = value;
fontInfoTag.setFontFlagsItalic(value);
}
}
@@ -291,33 +268,36 @@ public class DefineFontTag extends FontTag {
@Override
public void addCharacter(char character, Font font) {
SHAPE shp = SHAPERECORD.fontCharacterToSHAPE(font, (int) Math.round(getDivider() * 1024), character);
List<Integer> codeTable = new ArrayList<>();
ensureFontInfo();
if (fontInfoTag != null) {
codeTable = fontInfoTag.codeTable;
}
if (fontInfo2Tag != null) {
codeTable = fontInfo2Tag.codeTable;
}
int code = (int) character;
int pos = -1;
boolean exists = false;
for (int i = 0; i < codeTable.size(); i++) {
if (codeTable.get(i) >= code) {
if (codeTable.get(i) == code) {
exists = true;
if (fontInfoTag != null) {
List<Integer> codeTable = fontInfoTag.getCodeTable();
for (int i = 0; i < codeTable.size(); i++) {
if (codeTable.get(i) >= code) {
if (codeTable.get(i) == code) {
exists = true;
}
pos = i;
break;
}
pos = i;
break;
}
if (pos == -1) {
pos = codeTable.size();
}
} else {
pos = 0;
}
if (pos == -1) {
pos = codeTable.size();
}
if (!exists) {
shiftGlyphIndices(fontId, pos);
glyphShapeTable.add(pos, shp);
codeTable.add(pos, (int) character);
if (fontInfoTag != null) {
fontInfoTag.addCharacter(pos, (int) character);
}
} else {
glyphShapeTable.set(pos, shp);
}
@@ -335,12 +315,7 @@ public class DefineFontTag extends FontTag {
StringBuilder ret = new StringBuilder();
ensureFontInfo();
if (fontInfoTag != null) {
for (int i : fontInfoTag.codeTable) {
ret.append((char) i);
}
}
if (fontInfo2Tag != null) {
for (int i : fontInfo2Tag.codeTable) {
for (int i : fontInfoTag.getCodeTable()) {
ret.append((char) i);
}
}

View File

@@ -367,12 +367,12 @@ public class DefineSpriteTag extends CharacterTag implements DrawableTag, Timeli
@Override
public void toImage(int frame, int time, int ratio, RenderContext renderContext, SerializableImage image, boolean isClip, Matrix transformation, Matrix absoluteTransformation, ColorTransform colorTransform) {
SWF.frameToImage(getTimeline(), frame, time, renderContext, image, isClip, transformation, absoluteTransformation, colorTransform);
getTimeline().toImage(frame, time, ratio, renderContext, image, isClip, transformation, absoluteTransformation, colorTransform);
}
@Override
public void toSVG(SVGExporter exporter, int ratio, ColorTransform colorTransform, int level, double zoom) throws IOException {
SWF.frameToSvg(getTimeline(), 0, 0, null, 0, exporter, colorTransform, level + 1, zoom);
getTimeline().toSVG(0, 0, null, 0, exporter, colorTransform, level + 1, zoom);
}
@Override

View File

@@ -92,6 +92,7 @@ public class DoABC2Tag extends Tag implements ABCContainerTag {
name = sis.readString("name");
ABCInputStream ais = new ABCInputStream(sis.getBaseStream());
// put it to the dumpview:
sis.readByteRangeEx(sis.available(), "abcBytes");
abc = new ABC(ais, swf, this);

View File

@@ -93,12 +93,12 @@ public abstract class ButtonTag extends CharacterTag implements DrawableTag, Tim
@Override
public void toImage(int frame, int time, int ratio, RenderContext renderContext, SerializableImage image, boolean isClip, Matrix transformation, Matrix absoluteTransformation, ColorTransform colorTransform) {
SWF.frameToImage(getTimeline(), frame, time, renderContext, image, isClip, transformation, absoluteTransformation, colorTransform);
getTimeline().toImage(frame, time, ratio, renderContext, image, isClip, transformation, absoluteTransformation, colorTransform);
}
@Override
public void toSVG(SVGExporter exporter, int ratio, ColorTransform colorTransform, int level, double zoom) throws IOException {
SWF.frameToSvg(getTimeline(), 0, 0, null, 0, exporter, colorTransform, level + 1, zoom);
getTimeline().toSVG(0, 0, null, 0, exporter, colorTransform, level + 1, zoom);
}
public DefineButtonSoundTag getSounds() {

View File

@@ -0,0 +1,62 @@
/*
* Copyright (C) 2010-2016 JPEXS, All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3.0 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library.
*/
package com.jpexs.decompiler.flash.tags.base;
import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.tags.Tag;
import com.jpexs.decompiler.flash.types.BasicType;
import com.jpexs.decompiler.flash.types.annotations.SWFType;
import com.jpexs.helpers.ByteArrayRange;
import java.util.List;
/**
*
* @author JPEXS
*/
public abstract class FontInfoTag extends Tag implements CharacterIdTag {
@SWFType(BasicType.UI16)
public int fontID;
public FontInfoTag(SWF swf, int id, String name, ByteArrayRange data) {
super(swf, id, name, data);
}
public abstract List<Integer> getCodeTable();
public abstract void addCharacter(int index, int character);
@Override
public int getCharacterId() {
return fontID;
}
@Override
public void setCharacterId(int characterId) {
this.fontID = characterId;
}
public abstract String getFontName();
public abstract boolean getFontFlagsBold();
public abstract void setFontFlagsBold(boolean value);
public abstract boolean getFontFlagsItalic();
public abstract void setFontFlagsItalic(boolean value);
}

View File

@@ -371,7 +371,7 @@ public final class DefineCompactedFont extends FontTag {
@Override
public FontTag toClassicFont() {
DefineFont2Tag ret = new DefineFont2Tag(swf);
ret.fontId = getFontId();
ret.fontID = getFontId();
ret.fontFlagsBold = isBold();
ret.fontFlagsItalic = isItalic();
ret.fontFlagsWideOffsets = true;

View File

@@ -18,7 +18,9 @@ package com.jpexs.decompiler.flash.timeline;
import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.exporters.FrameExporter;
import com.jpexs.decompiler.flash.exporters.commonshape.ExportRectangle;
import com.jpexs.decompiler.flash.exporters.commonshape.Matrix;
import com.jpexs.decompiler.flash.exporters.commonshape.SVGExporter;
import com.jpexs.decompiler.flash.tags.DefineSpriteTag;
import com.jpexs.decompiler.flash.tags.DoActionTag;
import com.jpexs.decompiler.flash.tags.DoInitActionTag;
@@ -31,24 +33,38 @@ import com.jpexs.decompiler.flash.tags.StartSoundTag;
import com.jpexs.decompiler.flash.tags.Tag;
import com.jpexs.decompiler.flash.tags.base.ASMSource;
import com.jpexs.decompiler.flash.tags.base.ASMSourceContainer;
import com.jpexs.decompiler.flash.tags.base.BoundedTag;
import com.jpexs.decompiler.flash.tags.base.ButtonTag;
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.MorphShapeTag;
import com.jpexs.decompiler.flash.tags.base.PlaceObjectTypeTag;
import com.jpexs.decompiler.flash.tags.base.RemoveTag;
import com.jpexs.decompiler.flash.tags.base.RenderContext;
import com.jpexs.decompiler.flash.tags.base.ShapeTag;
import com.jpexs.decompiler.flash.tags.base.SoundStreamHeadTypeTag;
import com.jpexs.decompiler.flash.tags.base.TextTag;
import com.jpexs.decompiler.flash.types.CLIPACTIONS;
import com.jpexs.decompiler.flash.types.ColorTransform;
import com.jpexs.decompiler.flash.types.MATRIX;
import com.jpexs.decompiler.flash.types.RECT;
import com.jpexs.decompiler.flash.types.filters.BlendComposite;
import com.jpexs.decompiler.flash.types.filters.FILTER;
import com.jpexs.helpers.Helper;
import com.jpexs.helpers.SerializableImage;
import java.awt.AlphaComposite;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.Area;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -531,7 +547,385 @@ public class Timeline {
}
public void toImage(int frame, int time, int ratio, RenderContext renderContext, SerializableImage image, boolean isClip, Matrix transformation, Matrix absoluteTransformation, ColorTransform colorTransform) {
SWF.frameToImage(this, frame, time, renderContext, image, isClip, transformation, absoluteTransformation, colorTransform);
double unzoom = SWF.unitDivisor;
if (getFrameCount() <= frame) {
return;
}
Frame frameObj = getFrame(frame);
Graphics2D g = (Graphics2D) image.getGraphics();
g.setPaint(frameObj.backgroundColor.toColor());
g.fill(new Rectangle(image.getWidth(), image.getHeight()));
g.setTransform(transformation.toTransform());
List<Clip> clips = new ArrayList<>();
List<Shape> prevClips = new ArrayList<>();
int maxDepth = getMaxDepth();
for (int i = 1; i <= maxDepth; i++) {
for (int c = 0; c < clips.size(); c++) {
if (clips.get(c).depth == i) {
g.setClip(prevClips.get(c));
prevClips.remove(c);
clips.remove(c);
}
}
if (!frameObj.layers.containsKey(i)) {
continue;
}
DepthState layer = frameObj.layers.get(i);
if (!swf.getCharacters().containsKey(layer.characterId)) {
continue;
}
if (!layer.isVisible) {
continue;
}
CharacterTag character = swf.getCharacter(layer.characterId);
Matrix layerMatrix = new Matrix(layer.matrix);
Matrix mat = transformation.concatenate(layerMatrix);
Matrix absMat = absoluteTransformation.concatenate(layerMatrix);
ColorTransform clrTrans = colorTransform;
if (layer.colorTransForm != null && layer.blendMode <= 1) { // Normal blend mode
clrTrans = clrTrans == null ? layer.colorTransForm : colorTransform.merge(layer.colorTransForm);
}
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 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);
}
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) {
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;
} 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, 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);
}
} else if (character instanceof BoundedTag) {
showPlaceholder = true;
}
if (showPlaceholder) {
AffineTransform trans = mat.preConcatenate(Matrix.getScaleInstance(1 / SWF.unitDivisor)).toTransform();
g.setTransform(trans);
BoundedTag b = (BoundedTag) character;
g.setPaint(new Color(255, 255, 255, 128));
g.setComposite(BlendComposite.Invert);
g.setStroke(new BasicStroke((int) SWF.unitDivisor));
RECT r = b.getRect();
g.setFont(g.getFont().deriveFont((float) (12 * SWF.unitDivisor)));
g.drawString(character.toString(), r.Xmin + (int) (3 * SWF.unitDivisor), r.Ymin + (int) (15 * SWF.unitDivisor));
g.draw(new Rectangle(r.Xmin, r.Ymin, r.getWidth(), r.getHeight()));
g.drawLine(r.Xmin, r.Ymin, r.Xmax, r.Ymax);
g.drawLine(r.Xmax, r.Ymin, r.Xmin, r.Ymax);
g.setComposite(AlphaComposite.Dst);
/*Matrix mat2 = mat.clone();
mat2.translateX /= unzoom;
mat2.translateY /= unzoom;
AffineTransform trans = mat2.toTransform();
g.setTransform(trans);
BoundedTag b = (BoundedTag) character;
g.setPaint(new Color(255, 255, 255, 128));
g.setComposite(BlendComposite.Invert);
RECT r = b.getRect();
int div = (int) unzoom;
g.drawString(character.toString(), r.Xmin / div + 3, r.Ymin / div + 15);
g.draw(new Rectangle(r.Xmin / div, r.Ymin / div, r.getWidth() / div, r.getHeight() / div));
g.drawLine(r.Xmin / div, r.Ymin / div, r.Xmax / div, r.Ymax / div);
g.drawLine(r.Xmax / div, r.Ymin / div, r.Xmin / div, r.Ymax / div);
g.setComposite(AlphaComposite.Dst);*/
}
}
g.setTransform(AffineTransform.getScaleInstance(1, 1));
}
public void toSVG(int frame, int time, DepthState stateUnderCursor, int mouseButton, SVGExporter exporter, ColorTransform colorTransform, int level, double zoom) throws IOException {
if (getFrameCount() <= frame) {
return;
}
Frame frameObj = getFrame(frame);
List<SvgClip> clips = new ArrayList<>();
List<String> prevClips = new ArrayList<>();
int maxDepth = getMaxDepth();
for (int i = 1; i <= maxDepth; i++) {
for (int c = 0; c < clips.size(); c++) {
if (clips.get(c).depth == i) {
exporter.setClip(prevClips.get(c));
prevClips.remove(c);
clips.remove(c);
}
}
if (!frameObj.layers.containsKey(i)) {
continue;
}
DepthState layer = frameObj.layers.get(i);
if (!swf.getCharacters().containsKey(layer.characterId)) {
continue;
}
if (!layer.isVisible) {
continue;
}
CharacterTag character = swf.getCharacter(layer.characterId);
ColorTransform clrTrans = colorTransform;
if (layer.colorTransForm != null && layer.blendMode <= 1) { // Normal blend mode
clrTrans = clrTrans == null ? layer.colorTransForm : colorTransform.merge(layer.colorTransForm);
}
if (character instanceof DrawableTag) {
DrawableTag drawable = (DrawableTag) character;
String assetName;
Tag drawableTag = (Tag) drawable;
RECT boundRect = drawable.getRect();
if (exporter.exportedTags.containsKey(drawableTag)) {
assetName = exporter.exportedTags.get(drawableTag);
} else {
assetName = getTagIdPrefix(drawableTag, exporter);
exporter.exportedTags.put(drawableTag, assetName);
exporter.createDefGroup(new ExportRectangle(boundRect), assetName);
drawable.toSVG(exporter, layer.ratio, clrTrans, level + 1, zoom);
exporter.endGroup();
}
ExportRectangle rect = new ExportRectangle(boundRect);
// TODO: if (layer.filters != null)
// TODO: if (layer.blendMode > 1)
if (layer.clipDepth > -1) {
String clipName = exporter.getUniqueId("clipPath");
exporter.createClipPath(new Matrix(), clipName);
SvgClip clip = new SvgClip(clipName, layer.clipDepth);
clips.add(clip);
prevClips.add(exporter.getClip());
Matrix mat = Matrix.getTranslateInstance(rect.xMin, rect.yMin).preConcatenate(new Matrix(layer.matrix));
exporter.addUse(mat, boundRect, assetName, layer.instanceName);
exporter.setClip(clip.shape);
exporter.endGroup();
} else {
Matrix mat = Matrix.getTranslateInstance(rect.xMin, rect.yMin).preConcatenate(new Matrix(layer.matrix));
exporter.addUse(mat, boundRect, assetName, layer.instanceName);
}
}
}
}
private static String getTagIdPrefix(Tag tag, SVGExporter exporter) {
if (tag instanceof ShapeTag) {
return exporter.getUniqueId("shape");
}
if (tag instanceof MorphShapeTag) {
return exporter.getUniqueId("morphshape");
}
if (tag instanceof DefineSpriteTag) {
return exporter.getUniqueId("sprite");
}
if (tag instanceof TextTag) {
return exporter.getUniqueId("text");
}
if (tag instanceof ButtonTag) {
return exporter.getUniqueId("button");
}
return exporter.getUniqueId("tag");
}
public void toHtmlCanvas(StringBuilder result, double unitDivisor, List<Integer> frames) {
@@ -665,8 +1059,7 @@ public class Timeline {
CharacterTag character = swf.getCharacter(layer.characterId);
if (character instanceof DrawableTag) {
DrawableTag drawable = (DrawableTag) character;
Matrix m = new Matrix(layer.matrix);
m = m.preConcatenate(transformation);
Matrix m = transformation.concatenate(new Matrix(layer.matrix));
int drawableFrameCount = drawable.getNumFrames();
if (drawableFrameCount == 0) {

View File

@@ -418,8 +418,10 @@ public class XFLConverter {
private static String convertShape(HashMap<Integer, CharacterTag> characters, MATRIX mat, int shapeNum, List<SHAPERECORD> shapeRecords, FILLSTYLEARRAY fillStyles, LINESTYLEARRAY lineStyles, boolean morphshape, boolean useLayers) {
StringBuilder ret = new StringBuilder();
List<String> layers = getShapeLayers(characters, mat, shapeNum, shapeRecords, fillStyles, lineStyles, morphshape);
if (layers.size() == 1 && !useLayers) {
ret.append(layers.get(0));
if (!useLayers) {
for (int l = layers.size() - 1; l >= 0; l--) {
ret.append(layers.get(l));
}
} else {
int layer = 1;
for (int l = layers.size() - 1; l >= 0; l--) {