common code from DefineMorphShapeX tags moved to base class

This commit is contained in:
honfika@gmail.com
2015-05-14 13:57:44 +02:00
parent 0f3e6876e2
commit 26a5316b31
4 changed files with 301 additions and 604 deletions

View File

@@ -19,42 +19,20 @@ 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.Matrix;
import com.jpexs.decompiler.flash.exporters.commonshape.SVGExporter;
import com.jpexs.decompiler.flash.exporters.morphshape.CanvasMorphShapeExporter;
import com.jpexs.decompiler.flash.exporters.morphshape.SVGMorphShapeExporter;
import com.jpexs.decompiler.flash.exporters.shape.BitmapExporter;
import com.jpexs.decompiler.flash.exporters.shape.SVGShapeExporter;
import com.jpexs.decompiler.flash.tags.base.BoundedTag;
import com.jpexs.decompiler.flash.tags.base.MorphShapeTag;
import com.jpexs.decompiler.flash.tags.base.RenderContext;
import com.jpexs.decompiler.flash.types.BasicType;
import com.jpexs.decompiler.flash.types.ColorTransform;
import com.jpexs.decompiler.flash.types.FILLSTYLEARRAY;
import com.jpexs.decompiler.flash.types.LINESTYLEARRAY;
import com.jpexs.decompiler.flash.types.MORPHFILLSTYLE;
import com.jpexs.decompiler.flash.types.MORPHFILLSTYLEARRAY;
import com.jpexs.decompiler.flash.types.MORPHLINESTYLE2;
import com.jpexs.decompiler.flash.types.MORPHLINESTYLEARRAY;
import com.jpexs.decompiler.flash.types.RECT;
import com.jpexs.decompiler.flash.types.SHAPE;
import com.jpexs.decompiler.flash.types.SHAPEWITHSTYLE;
import com.jpexs.decompiler.flash.types.annotations.Reserved;
import com.jpexs.decompiler.flash.types.annotations.SWFType;
import com.jpexs.decompiler.flash.types.shaperecords.CurvedEdgeRecord;
import com.jpexs.decompiler.flash.types.shaperecords.EndShapeRecord;
import com.jpexs.decompiler.flash.types.shaperecords.SHAPERECORD;
import com.jpexs.decompiler.flash.types.shaperecords.StraightEdgeRecord;
import com.jpexs.decompiler.flash.types.shaperecords.StyleChangeRecord;
import com.jpexs.helpers.ByteArrayRange;
import com.jpexs.helpers.SerializableImage;
import java.awt.Shape;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
/**
*
@@ -63,13 +41,6 @@ import java.util.Set;
*/
public class DefineMorphShape2Tag extends MorphShapeTag {
@SWFType(BasicType.UI16)
public int characterId;
public RECT startBounds;
public RECT endBounds;
public RECT startEdgeBounds;
public RECT endEdgeBounds;
@@ -82,64 +53,8 @@ public class DefineMorphShape2Tag extends MorphShapeTag {
public boolean usesScalingStrokes;
public MORPHFILLSTYLEARRAY morphFillStyles;
public MORPHLINESTYLEARRAY morphLineStyles;
public SHAPE startEdges;
public SHAPE endEdges;
public static final int ID = 84;
public static final int MAX_RATIO = 65535;
@Override
public void getNeededCharacters(Set<Integer> needed) {
morphFillStyles.getNeededCharacters(needed);
startEdges.getNeededCharacters(needed);
endEdges.getNeededCharacters(needed);
}
@Override
public boolean replaceCharacter(int oldCharacterId, int newCharacterId) {
boolean modified = false;
modified |= morphFillStyles.replaceCharacter(oldCharacterId, newCharacterId);
modified |= startEdges.replaceCharacter(oldCharacterId, newCharacterId);
modified |= endEdges.replaceCharacter(oldCharacterId, newCharacterId);
if (modified) {
setModified(true);
}
return modified;
}
@Override
public boolean removeCharacter(int characterId) {
boolean modified = false;
modified |= morphFillStyles.removeCharacter(characterId);
modified |= startEdges.removeCharacter(characterId);
modified |= endEdges.removeCharacter(characterId);
if (modified) {
setModified(true);
}
return modified;
}
@Override
public RECT getRect(Set<BoundedTag> added) {
RECT rect = new RECT();
rect.Xmin = Math.min(startBounds.Xmin, endBounds.Xmin);
rect.Ymin = Math.min(startBounds.Ymin, endBounds.Ymin);
rect.Xmax = Math.max(startBounds.Xmax, endBounds.Xmax);
rect.Ymax = Math.max(startBounds.Ymax, endBounds.Ymax);
return rect;
}
@Override
public int getCharacterId() {
return characterId;
}
/**
* Gets data bytes
*
@@ -216,221 +131,15 @@ public class DefineMorphShape2Tag extends MorphShapeTag {
reserved = (int) sis.readUB(6, "reserved");
usesNonScalingStrokes = sis.readUB(1, "usesNonScalingStrokes") == 1;
usesScalingStrokes = sis.readUB(1, "usesScalingStrokes") == 1;
long offset = sis.readUI32("offset");
long offset = sis.readUI32("offset"); // ignore
morphFillStyles = sis.readMORPHFILLSTYLEARRAY("morphFillStyles");
morphLineStyles = sis.readMORPHLINESTYLEARRAY(2, "morphLineStyles");
startEdges = sis.readSHAPE(2, true, "startEdges");
endEdges = sis.readSHAPE(2, true, "endEdges");
}
@Override
public RECT getStartBounds() {
return startBounds;
}
@Override
public RECT getEndBounds() {
return endBounds;
}
@Override
public MORPHFILLSTYLEARRAY getFillStyles() {
return morphFillStyles;
}
@Override
public MORPHLINESTYLEARRAY getLineStyles() {
return morphLineStyles;
}
@Override
public SHAPE getStartEdges() {
return startEdges;
}
@Override
public SHAPE getEndEdges() {
return endEdges;
}
@Override
public int getShapeNum() {
return 2;
}
@Override
public SHAPEWITHSTYLE getShapeAtRatio(int ratio) {
List<SHAPERECORD> finalRecords = new ArrayList<>();
FILLSTYLEARRAY fillStyles = morphFillStyles.getFillStylesAt(ratio);
LINESTYLEARRAY lineStyles = morphLineStyles.getLineStylesAt(getShapeNum(), ratio);
int startPosX = 0, startPosY = 0;
int endPosX = 0, endPosY = 0;
int posX = 0, posY = 0;
for (int startIndex = 0, endIndex = 0;
startIndex < startEdges.shapeRecords.size()
&& endIndex < endEdges.shapeRecords.size(); startIndex++, endIndex++) {
SHAPERECORD edge1 = startEdges.shapeRecords.get(startIndex);
SHAPERECORD edge2 = endEdges.shapeRecords.get(endIndex);
if (edge1 instanceof StyleChangeRecord || edge2 instanceof StyleChangeRecord) {
StyleChangeRecord scr1;
if (edge1 instanceof StyleChangeRecord) {
scr1 = (StyleChangeRecord) edge1;
if (scr1.stateMoveTo) {
startPosX = scr1.moveDeltaX;
startPosY = scr1.moveDeltaY;
}
} else {
scr1 = new StyleChangeRecord();
startIndex--;
}
StyleChangeRecord scr2;
if (edge2 instanceof StyleChangeRecord) {
scr2 = (StyleChangeRecord) edge2;
if (scr2.stateMoveTo) {
endPosX = scr2.moveDeltaX;
endPosY = scr2.moveDeltaY;
}
} else {
scr2 = new StyleChangeRecord();
endIndex--;
}
StyleChangeRecord scr = scr1.clone();
if (scr1.stateMoveTo || scr2.stateMoveTo) {
scr.moveDeltaX = startPosX + (endPosX - startPosX) * ratio / 65535;
scr.moveDeltaY = startPosY + (endPosY - startPosY) * ratio / 65535;
scr.stateMoveTo = scr.moveDeltaX != posX || scr.moveDeltaY != posY;
}
finalRecords.add(scr);
continue;
}
if (edge1 instanceof EndShapeRecord) {
finalRecords.add(edge1);
break;
}
if (edge2 instanceof EndShapeRecord) {
finalRecords.add(edge2);
break;
}
if (edge1 instanceof CurvedEdgeRecord || edge2 instanceof CurvedEdgeRecord) {
CurvedEdgeRecord cer1 = null;
if (edge1 instanceof CurvedEdgeRecord) {
cer1 = (CurvedEdgeRecord) edge1;
} else if (edge1 instanceof StraightEdgeRecord) {
cer1 = SHAPERECORD.straightToCurve((StraightEdgeRecord) edge1);
}
CurvedEdgeRecord cer2 = null;
if (edge2 instanceof CurvedEdgeRecord) {
cer2 = (CurvedEdgeRecord) edge2;
} else if (edge2 instanceof StraightEdgeRecord) {
cer2 = SHAPERECORD.straightToCurve((StraightEdgeRecord) edge2);
}
if ((cer2 == null) || (cer1 == null)) {
continue;
}
CurvedEdgeRecord cer = new CurvedEdgeRecord();
cer.controlDeltaX = cer1.controlDeltaX + (cer2.controlDeltaX - cer1.controlDeltaX) * ratio / 65535;
cer.controlDeltaY = cer1.controlDeltaY + (cer2.controlDeltaY - cer1.controlDeltaY) * ratio / 65535;
cer.anchorDeltaX = cer1.anchorDeltaX + (cer2.anchorDeltaX - cer1.anchorDeltaX) * ratio / 65535;
cer.anchorDeltaY = cer1.anchorDeltaY + (cer2.anchorDeltaY - cer1.anchorDeltaY) * ratio / 65535;
startPosX += cer1.controlDeltaX + cer1.anchorDeltaX;
startPosY += cer1.controlDeltaY + cer1.anchorDeltaY;
endPosX += cer2.controlDeltaX + cer2.anchorDeltaX;
endPosY += cer2.controlDeltaY + cer2.anchorDeltaY;
posX += cer.controlDeltaX + cer.anchorDeltaX;
posY += cer.controlDeltaY + cer.anchorDeltaY;
finalRecords.add(cer);
} else {
StraightEdgeRecord ser1 = null;
if (edge1 instanceof StraightEdgeRecord) {
ser1 = (StraightEdgeRecord) edge1;
}
StraightEdgeRecord ser2 = null;
if (edge2 instanceof StraightEdgeRecord) {
ser2 = (StraightEdgeRecord) edge2;
}
if ((ser2 == null) || (ser1 == null)) {
continue;
}
StraightEdgeRecord ser = new StraightEdgeRecord();
ser.generalLineFlag = true;
ser.vertLineFlag = false;
ser.deltaX = ser1.deltaX + (ser2.deltaX - ser1.deltaX) * ratio / 65535;
ser.deltaY = ser1.deltaY + (ser2.deltaY - ser1.deltaY) * ratio / 65535;
startPosX += ser1.deltaX;
startPosY += ser1.deltaY;
endPosX += ser2.deltaX;
endPosY += ser2.deltaY;
posX += ser.deltaX;
posY += ser.deltaX;
finalRecords.add(ser);
}
}
SHAPEWITHSTYLE shape = new SHAPEWITHSTYLE();
shape.fillStyles = fillStyles;
shape.lineStyles = lineStyles;
shape.shapeRecords = finalRecords;
return shape;
}
@Override
public int getUsedParameters() {
return PARAMETER_RATIO;
}
@Override
public void toImage(int frame, int time, int ratio, RenderContext renderContext, SerializableImage image, Matrix transformation, ColorTransform colorTransform) {
SHAPEWITHSTYLE shape = getShapeAtRatio(ratio);
// 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);
}
@Override
public void toSVG(SVGExporter exporter, int ratio, ColorTransform colorTransform, int level, double zoom) {
if (ratio == -2) {
SHAPEWITHSTYLE beginShapes = getShapeAtRatio(0);
SHAPEWITHSTYLE endShapes = getShapeAtRatio(65535);
SVGMorphShapeExporter shapeExporter = new SVGMorphShapeExporter(swf, beginShapes, endShapes, exporter, null, colorTransform, zoom);
shapeExporter.export();
} else {
SHAPEWITHSTYLE shapes = getShapeAtRatio(ratio);
SVGShapeExporter shapeExporter = new SVGShapeExporter(swf, shapes, exporter, null, colorTransform, zoom);
shapeExporter.export();
}
}
@Override
public int getNumFrames() {
return 65536;
}
@Override
public boolean isSingleFrame() {
// Morpshape is a single frame specified with the ratio
return true;
}
@Override
public Shape getOutline(int frame, int time, int ratio, RenderContext renderContext, Matrix transformation) {
return transformation.toTransform().createTransformedShape(getShapeAtRatio(ratio).getOutline());
}
@Override
public String toHtmlCanvas(double unitDivisor) {
CanvasMorphShapeExporter cmse = new CanvasMorphShapeExporter(swf, getShapeAtRatio(0), getShapeAtRatio(MAX_RATIO), new ColorTransform(), unitDivisor, 0, 0);
cmse.export();
return cmse.getShapeData();
}
@Override
public void setCharacterId(int characterId) {
this.characterId = characterId;
}
}

View File

@@ -19,41 +19,17 @@ 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.Matrix;
import com.jpexs.decompiler.flash.exporters.commonshape.SVGExporter;
import com.jpexs.decompiler.flash.exporters.morphshape.CanvasMorphShapeExporter;
import com.jpexs.decompiler.flash.exporters.morphshape.SVGMorphShapeExporter;
import com.jpexs.decompiler.flash.exporters.shape.BitmapExporter;
import com.jpexs.decompiler.flash.exporters.shape.SVGShapeExporter;
import com.jpexs.decompiler.flash.tags.base.BoundedTag;
import com.jpexs.decompiler.flash.tags.base.MorphShapeTag;
import com.jpexs.decompiler.flash.tags.base.RenderContext;
import com.jpexs.decompiler.flash.types.BasicType;
import com.jpexs.decompiler.flash.types.ColorTransform;
import com.jpexs.decompiler.flash.types.FILLSTYLEARRAY;
import com.jpexs.decompiler.flash.types.LINESTYLEARRAY;
import com.jpexs.decompiler.flash.types.MORPHFILLSTYLE;
import com.jpexs.decompiler.flash.types.MORPHFILLSTYLEARRAY;
import com.jpexs.decompiler.flash.types.MORPHLINESTYLE;
import com.jpexs.decompiler.flash.types.MORPHLINESTYLEARRAY;
import com.jpexs.decompiler.flash.types.RECT;
import com.jpexs.decompiler.flash.types.SHAPE;
import com.jpexs.decompiler.flash.types.SHAPEWITHSTYLE;
import com.jpexs.decompiler.flash.types.annotations.SWFType;
import com.jpexs.decompiler.flash.types.shaperecords.CurvedEdgeRecord;
import com.jpexs.decompiler.flash.types.shaperecords.EndShapeRecord;
import com.jpexs.decompiler.flash.types.shaperecords.SHAPERECORD;
import com.jpexs.decompiler.flash.types.shaperecords.StraightEdgeRecord;
import com.jpexs.decompiler.flash.types.shaperecords.StyleChangeRecord;
import com.jpexs.helpers.ByteArrayRange;
import com.jpexs.helpers.SerializableImage;
import java.awt.Shape;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
/**
*
@@ -62,61 +38,8 @@ import java.util.Set;
*/
public class DefineMorphShapeTag extends MorphShapeTag {
@SWFType(BasicType.UI16)
public int characterId;
public RECT startBounds;
public RECT endBounds;
public MORPHFILLSTYLEARRAY morphFillStyles;
public MORPHLINESTYLEARRAY morphLineStyles;
public SHAPE startEdges;
public SHAPE endEdges;
public static final int ID = 46;
public static final int MAX_RATIO = 65535;
@Override
public void getNeededCharacters(Set<Integer> needed) {
morphFillStyles.getNeededCharacters(needed);
startEdges.getNeededCharacters(needed);
endEdges.getNeededCharacters(needed);
}
@Override
public boolean replaceCharacter(int oldCharacterId, int newCharacterId) {
boolean modified = false;
modified |= morphFillStyles.replaceCharacter(oldCharacterId, newCharacterId);
modified |= startEdges.replaceCharacter(oldCharacterId, newCharacterId);
modified |= endEdges.replaceCharacter(oldCharacterId, newCharacterId);
if (modified) {
setModified(true);
}
return modified;
}
@Override
public boolean removeCharacter(int characterId) {
boolean modified = false;
modified |= morphFillStyles.removeCharacter(characterId);
modified |= startEdges.removeCharacter(characterId);
modified |= endEdges.removeCharacter(characterId);
if (modified) {
setModified(true);
}
return modified;
}
@Override
public int getCharacterId() {
return characterId;
}
/**
* Gets data bytes
*
@@ -136,11 +59,10 @@ public class DefineMorphShapeTag extends MorphShapeTag {
sos2.writeMORPHFILLSTYLEARRAY(morphFillStyles, 1);
sos2.writeMORPHLINESTYLEARRAY(morphLineStyles, 1);
sos2.writeSHAPE(startEdges, 1);
byte[] d = baos2.toByteArray();
sos.writeUI32(d.length);
sos.write(d);
byte[] ba2 = baos2.toByteArray();
sos.writeUI32(ba2.length);
sos.write(ba2);
sos.writeSHAPE(endEdges, 1);
} catch (IOException e) {
throw new Error("This should never happen.", e);
}
@@ -189,224 +111,8 @@ public class DefineMorphShapeTag extends MorphShapeTag {
endEdges = sis.readSHAPE(1, true, "endEdges");
}
@Override
public RECT getRect(Set<BoundedTag> added) {
RECT rect = new RECT();
rect.Xmin = Math.min(startBounds.Xmin, endBounds.Xmin);
rect.Ymin = Math.min(startBounds.Ymin, endBounds.Ymin);
rect.Xmax = Math.max(startBounds.Xmax, endBounds.Xmax);
rect.Ymax = Math.max(startBounds.Ymax, endBounds.Ymax);
return rect;
}
@Override
public RECT getStartBounds() {
return startBounds;
}
@Override
public RECT getEndBounds() {
return endBounds;
}
@Override
public MORPHFILLSTYLEARRAY getFillStyles() {
return morphFillStyles;
}
@Override
public MORPHLINESTYLEARRAY getLineStyles() {
return morphLineStyles;
}
@Override
public SHAPE getStartEdges() {
return startEdges;
}
@Override
public SHAPE getEndEdges() {
return endEdges;
}
@Override
public int getShapeNum() {
return 1;
}
@Override
public SHAPEWITHSTYLE getShapeAtRatio(int ratio) {
List<SHAPERECORD> finalRecords = new ArrayList<>();
FILLSTYLEARRAY fillStyles = morphFillStyles.getFillStylesAt(ratio);
LINESTYLEARRAY lineStyles = morphLineStyles.getLineStylesAt(getShapeNum(), ratio);
int startPosX = 0, startPosY = 0;
int endPosX = 0, endPosY = 0;
int posX = 0, posY = 0;
for (int startIndex = 0, endIndex = 0;
startIndex < startEdges.shapeRecords.size()
&& endIndex < endEdges.shapeRecords.size(); startIndex++, endIndex++) {
SHAPERECORD edge1 = startEdges.shapeRecords.get(startIndex);
SHAPERECORD edge2 = endEdges.shapeRecords.get(endIndex);
if (edge1 instanceof StyleChangeRecord || edge2 instanceof StyleChangeRecord) {
StyleChangeRecord scr1;
if (edge1 instanceof StyleChangeRecord) {
scr1 = (StyleChangeRecord) edge1;
if (scr1.stateMoveTo) {
startPosX = scr1.moveDeltaX;
startPosY = scr1.moveDeltaY;
}
} else {
scr1 = new StyleChangeRecord();
startIndex--;
}
StyleChangeRecord scr2;
if (edge2 instanceof StyleChangeRecord) {
scr2 = (StyleChangeRecord) edge2;
if (scr2.stateMoveTo) {
endPosX = scr2.moveDeltaX;
endPosY = scr2.moveDeltaY;
}
} else {
scr2 = new StyleChangeRecord();
endIndex--;
}
StyleChangeRecord scr = scr1.clone();
if (scr1.stateMoveTo || scr2.stateMoveTo) {
scr.moveDeltaX = startPosX + (endPosX - startPosX) * ratio / MAX_RATIO;
scr.moveDeltaY = startPosY + (endPosY - startPosY) * ratio / MAX_RATIO;
scr.stateMoveTo = scr.moveDeltaX != posX || scr.moveDeltaY != posY;
}
finalRecords.add(scr);
continue;
}
if (edge1 instanceof EndShapeRecord) {
finalRecords.add(edge1);
break;
}
if (edge2 instanceof EndShapeRecord) {
finalRecords.add(edge2);
break;
}
if (edge1 instanceof CurvedEdgeRecord || edge2 instanceof CurvedEdgeRecord) {
CurvedEdgeRecord cer1 = null;
if (edge1 instanceof CurvedEdgeRecord) {
cer1 = (CurvedEdgeRecord) edge1;
} else if (edge1 instanceof StraightEdgeRecord) {
cer1 = SHAPERECORD.straightToCurve((StraightEdgeRecord) edge1);
}
CurvedEdgeRecord cer2 = null;
if (edge2 instanceof CurvedEdgeRecord) {
cer2 = (CurvedEdgeRecord) edge2;
} else if (edge2 instanceof StraightEdgeRecord) {
cer2 = SHAPERECORD.straightToCurve((StraightEdgeRecord) edge2);
}
if ((cer2 == null) || (cer1 == null)) {
continue;
}
CurvedEdgeRecord cer = new CurvedEdgeRecord();
cer.controlDeltaX = cer1.controlDeltaX + (cer2.controlDeltaX - cer1.controlDeltaX) * ratio / MAX_RATIO;
cer.controlDeltaY = cer1.controlDeltaY + (cer2.controlDeltaY - cer1.controlDeltaY) * ratio / MAX_RATIO;
cer.anchorDeltaX = cer1.anchorDeltaX + (cer2.anchorDeltaX - cer1.anchorDeltaX) * ratio / MAX_RATIO;
cer.anchorDeltaY = cer1.anchorDeltaY + (cer2.anchorDeltaY - cer1.anchorDeltaY) * ratio / MAX_RATIO;
startPosX += cer1.controlDeltaX + cer1.anchorDeltaX;
startPosY += cer1.controlDeltaY + cer1.anchorDeltaY;
endPosX += cer2.controlDeltaX + cer2.anchorDeltaX;
endPosY += cer2.controlDeltaY + cer2.anchorDeltaY;
posX += cer.controlDeltaX + cer.anchorDeltaX;
posY += cer.controlDeltaY + cer.anchorDeltaY;
finalRecords.add(cer);
} else {
StraightEdgeRecord ser1 = null;
if (edge1 instanceof StraightEdgeRecord) {
ser1 = (StraightEdgeRecord) edge1;
}
StraightEdgeRecord ser2 = null;
if (edge2 instanceof StraightEdgeRecord) {
ser2 = (StraightEdgeRecord) edge2;
}
if ((ser2 == null) || (ser1 == null)) {
continue;
}
StraightEdgeRecord ser = new StraightEdgeRecord();
ser.generalLineFlag = true;
ser.vertLineFlag = false;
ser.deltaX = ser1.deltaX + (ser2.deltaX - ser1.deltaX) * ratio / MAX_RATIO;
ser.deltaY = ser1.deltaY + (ser2.deltaY - ser1.deltaY) * ratio / MAX_RATIO;
startPosX += ser1.deltaX;
startPosY += ser1.deltaY;
endPosX += ser2.deltaX;
endPosY += ser2.deltaY;
posX += ser.deltaX;
posY += ser.deltaX;
finalRecords.add(ser);
}
}
SHAPEWITHSTYLE shape = new SHAPEWITHSTYLE();
shape.fillStyles = fillStyles;
shape.lineStyles = lineStyles;
shape.shapeRecords = finalRecords;
return shape;
}
@Override
public int getUsedParameters() {
return PARAMETER_RATIO;
}
@Override
public void toImage(int frame, int time, int ratio, RenderContext renderContext, SerializableImage image, Matrix transformation, ColorTransform colorTransform) {
SHAPEWITHSTYLE shape = getShapeAtRatio(ratio);
// shapeNum: 3
// 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);
}
@Override
public void toSVG(SVGExporter exporter, int ratio, ColorTransform colorTransform, int level, double zoom) {
if (ratio == -2) {
SHAPEWITHSTYLE beginShapes = getShapeAtRatio(0);
SHAPEWITHSTYLE endShapes = getShapeAtRatio(65535);
SVGMorphShapeExporter shapeExporter = new SVGMorphShapeExporter(swf, beginShapes, endShapes, exporter, null, colorTransform, zoom);
shapeExporter.export();
} else {
SHAPEWITHSTYLE shapes = getShapeAtRatio(ratio);
SVGShapeExporter shapeExporter = new SVGShapeExporter(swf, shapes, exporter, null, colorTransform, zoom);
shapeExporter.export();
}
}
@Override
public int getNumFrames() {
return 65536;
}
@Override
public boolean isSingleFrame() {
// Morpshape is a single frame specified with the ratio
return true;
}
@Override
public Shape getOutline(int frame, int time, int ratio, RenderContext renderContext, Matrix transformation) {
return transformation.toTransform().createTransformedShape(getShapeAtRatio(ratio).getOutline());
}
@Override
public String toHtmlCanvas(double unitDivisor) {
CanvasMorphShapeExporter cmse = new CanvasMorphShapeExporter(swf, getShapeAtRatio(0), getShapeAtRatio(MAX_RATIO), new ColorTransform(), unitDivisor, 0, 0);
cmse.export();
return cmse.getShapeData();
}
@Override
public void setCharacterId(int characterId) {
this.characterId = characterId;
}
}

View File

@@ -21,9 +21,11 @@ import com.jpexs.decompiler.flash.SWFInputStream;
import com.jpexs.decompiler.flash.tags.base.StaticTextTag;
import com.jpexs.decompiler.flash.types.MATRIX;
import com.jpexs.decompiler.flash.types.RECT;
import com.jpexs.decompiler.flash.types.TEXTRECORD;
import com.jpexs.helpers.ByteArrayRange;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
*
@@ -49,6 +51,16 @@ public class DefineTextTag extends StaticTextTag {
advanceBits = 0;
}
public DefineTextTag(SWF swf, int characterID, RECT textBounds, MATRIX textMatrix, List<TEXTRECORD> textRecords) {
super(swf, ID, "DefineText", null);
this.characterID = characterID;
this.textBounds = textBounds;
this.textMatrix = textMatrix;
this.textRecords = textRecords;
this.glyphBits = 0;
this.advanceBits = 0;
}
/**
* Constructor
*

View File

@@ -17,13 +17,35 @@
package com.jpexs.decompiler.flash.tags.base;
import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.exporters.commonshape.Matrix;
import com.jpexs.decompiler.flash.exporters.commonshape.SVGExporter;
import com.jpexs.decompiler.flash.exporters.morphshape.CanvasMorphShapeExporter;
import com.jpexs.decompiler.flash.exporters.morphshape.SVGMorphShapeExporter;
import com.jpexs.decompiler.flash.exporters.shape.BitmapExporter;
import com.jpexs.decompiler.flash.exporters.shape.SVGShapeExporter;
import static com.jpexs.decompiler.flash.tags.base.DrawableTag.PARAMETER_RATIO;
import com.jpexs.decompiler.flash.types.BasicType;
import com.jpexs.decompiler.flash.types.ColorTransform;
import com.jpexs.decompiler.flash.types.FILLSTYLEARRAY;
import com.jpexs.decompiler.flash.types.LINESTYLEARRAY;
import com.jpexs.decompiler.flash.types.MORPHFILLSTYLEARRAY;
import com.jpexs.decompiler.flash.types.MORPHLINESTYLEARRAY;
import com.jpexs.decompiler.flash.types.RECT;
import com.jpexs.decompiler.flash.types.SHAPE;
import com.jpexs.decompiler.flash.types.SHAPEWITHSTYLE;
import com.jpexs.decompiler.flash.types.annotations.SWFType;
import com.jpexs.decompiler.flash.types.shaperecords.CurvedEdgeRecord;
import com.jpexs.decompiler.flash.types.shaperecords.EndShapeRecord;
import com.jpexs.decompiler.flash.types.shaperecords.SHAPERECORD;
import com.jpexs.decompiler.flash.types.shaperecords.StraightEdgeRecord;
import com.jpexs.decompiler.flash.types.shaperecords.StyleChangeRecord;
import com.jpexs.helpers.ByteArrayRange;
import com.jpexs.helpers.SerializableImage;
import java.awt.Shape;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
*
@@ -31,28 +53,276 @@ import java.util.HashSet;
*/
public abstract class MorphShapeTag extends CharacterTag implements DrawableTag {
public static final int MAX_RATIO = 65535;
@SWFType(BasicType.UI16)
public int characterId;
public RECT startBounds;
public RECT endBounds;
public MORPHFILLSTYLEARRAY morphFillStyles;
public MORPHLINESTYLEARRAY morphLineStyles;
public SHAPE startEdges;
public SHAPE endEdges;
public MorphShapeTag(SWF swf, int id, String name, ByteArrayRange data) {
super(swf, id, name, data);
}
public abstract RECT getStartBounds();
public abstract RECT getEndBounds();
public abstract MORPHFILLSTYLEARRAY getFillStyles();
public abstract MORPHLINESTYLEARRAY getLineStyles();
public abstract SHAPE getStartEdges();
public abstract SHAPE getEndEdges();
public abstract int getShapeNum();
public abstract SHAPEWITHSTYLE getShapeAtRatio(int ratio);
@Override
public RECT getRect() {
return getRect(new HashSet<BoundedTag>());
return getRect(new HashSet<>());
}
@Override
public void getNeededCharacters(Set<Integer> needed) {
morphFillStyles.getNeededCharacters(needed);
startEdges.getNeededCharacters(needed);
endEdges.getNeededCharacters(needed);
}
@Override
public boolean replaceCharacter(int oldCharacterId, int newCharacterId) {
boolean modified = false;
modified |= morphFillStyles.replaceCharacter(oldCharacterId, newCharacterId);
modified |= startEdges.replaceCharacter(oldCharacterId, newCharacterId);
modified |= endEdges.replaceCharacter(oldCharacterId, newCharacterId);
if (modified) {
setModified(true);
}
return modified;
}
@Override
public boolean removeCharacter(int characterId) {
boolean modified = false;
modified |= morphFillStyles.removeCharacter(characterId);
modified |= startEdges.removeCharacter(characterId);
modified |= endEdges.removeCharacter(characterId);
if (modified) {
setModified(true);
}
return modified;
}
@Override
public int getCharacterId() {
return characterId;
}
@Override
public RECT getRect(Set<BoundedTag> added) {
RECT rect = new RECT();
rect.Xmin = Math.min(startBounds.Xmin, endBounds.Xmin);
rect.Ymin = Math.min(startBounds.Ymin, endBounds.Ymin);
rect.Xmax = Math.max(startBounds.Xmax, endBounds.Xmax);
rect.Ymax = Math.max(startBounds.Ymax, endBounds.Ymax);
return rect;
}
public RECT getStartBounds() {
return startBounds;
}
public RECT getEndBounds() {
return endBounds;
}
public MORPHFILLSTYLEARRAY getFillStyles() {
return morphFillStyles;
}
public MORPHLINESTYLEARRAY getLineStyles() {
return morphLineStyles;
}
public SHAPE getStartEdges() {
return startEdges;
}
public SHAPE getEndEdges() {
return endEdges;
}
public SHAPEWITHSTYLE getShapeAtRatio(int ratio) {
List<SHAPERECORD> finalRecords = new ArrayList<>();
FILLSTYLEARRAY fillStyles = morphFillStyles.getFillStylesAt(ratio);
LINESTYLEARRAY lineStyles = morphLineStyles.getLineStylesAt(getShapeNum(), ratio);
int startPosX = 0, startPosY = 0;
int endPosX = 0, endPosY = 0;
int posX = 0, posY = 0;
for (int startIndex = 0, endIndex = 0;
startIndex < startEdges.shapeRecords.size()
&& endIndex < endEdges.shapeRecords.size(); startIndex++, endIndex++) {
SHAPERECORD edge1 = startEdges.shapeRecords.get(startIndex);
SHAPERECORD edge2 = endEdges.shapeRecords.get(endIndex);
if (edge1 instanceof StyleChangeRecord || edge2 instanceof StyleChangeRecord) {
StyleChangeRecord scr1;
if (edge1 instanceof StyleChangeRecord) {
scr1 = (StyleChangeRecord) edge1;
if (scr1.stateMoveTo) {
startPosX = scr1.moveDeltaX;
startPosY = scr1.moveDeltaY;
}
} else {
scr1 = new StyleChangeRecord();
startIndex--;
}
StyleChangeRecord scr2;
if (edge2 instanceof StyleChangeRecord) {
scr2 = (StyleChangeRecord) edge2;
if (scr2.stateMoveTo) {
endPosX = scr2.moveDeltaX;
endPosY = scr2.moveDeltaY;
}
} else {
scr2 = new StyleChangeRecord();
endIndex--;
}
StyleChangeRecord scr = scr1.clone();
if (scr1.stateMoveTo || scr2.stateMoveTo) {
scr.moveDeltaX = startPosX + (endPosX - startPosX) * ratio / MAX_RATIO;
scr.moveDeltaY = startPosY + (endPosY - startPosY) * ratio / MAX_RATIO;
scr.stateMoveTo = scr.moveDeltaX != posX || scr.moveDeltaY != posY;
}
finalRecords.add(scr);
continue;
}
if (edge1 instanceof EndShapeRecord) {
finalRecords.add(edge1);
break;
}
if (edge2 instanceof EndShapeRecord) {
finalRecords.add(edge2);
break;
}
if (edge1 instanceof CurvedEdgeRecord || edge2 instanceof CurvedEdgeRecord) {
CurvedEdgeRecord cer1 = null;
if (edge1 instanceof CurvedEdgeRecord) {
cer1 = (CurvedEdgeRecord) edge1;
} else if (edge1 instanceof StraightEdgeRecord) {
cer1 = SHAPERECORD.straightToCurve((StraightEdgeRecord) edge1);
}
CurvedEdgeRecord cer2 = null;
if (edge2 instanceof CurvedEdgeRecord) {
cer2 = (CurvedEdgeRecord) edge2;
} else if (edge2 instanceof StraightEdgeRecord) {
cer2 = SHAPERECORD.straightToCurve((StraightEdgeRecord) edge2);
}
if ((cer2 == null) || (cer1 == null)) {
continue;
}
CurvedEdgeRecord cer = new CurvedEdgeRecord();
cer.controlDeltaX = cer1.controlDeltaX + (cer2.controlDeltaX - cer1.controlDeltaX) * ratio / MAX_RATIO;
cer.controlDeltaY = cer1.controlDeltaY + (cer2.controlDeltaY - cer1.controlDeltaY) * ratio / MAX_RATIO;
cer.anchorDeltaX = cer1.anchorDeltaX + (cer2.anchorDeltaX - cer1.anchorDeltaX) * ratio / MAX_RATIO;
cer.anchorDeltaY = cer1.anchorDeltaY + (cer2.anchorDeltaY - cer1.anchorDeltaY) * ratio / MAX_RATIO;
startPosX += cer1.controlDeltaX + cer1.anchorDeltaX;
startPosY += cer1.controlDeltaY + cer1.anchorDeltaY;
endPosX += cer2.controlDeltaX + cer2.anchorDeltaX;
endPosY += cer2.controlDeltaY + cer2.anchorDeltaY;
posX += cer.controlDeltaX + cer.anchorDeltaX;
posY += cer.controlDeltaY + cer.anchorDeltaY;
finalRecords.add(cer);
} else {
StraightEdgeRecord ser1 = null;
if (edge1 instanceof StraightEdgeRecord) {
ser1 = (StraightEdgeRecord) edge1;
}
StraightEdgeRecord ser2 = null;
if (edge2 instanceof StraightEdgeRecord) {
ser2 = (StraightEdgeRecord) edge2;
}
if ((ser2 == null) || (ser1 == null)) {
continue;
}
StraightEdgeRecord ser = new StraightEdgeRecord();
ser.generalLineFlag = true;
ser.vertLineFlag = false;
ser.deltaX = ser1.deltaX + (ser2.deltaX - ser1.deltaX) * ratio / MAX_RATIO;
ser.deltaY = ser1.deltaY + (ser2.deltaY - ser1.deltaY) * ratio / MAX_RATIO;
startPosX += ser1.deltaX;
startPosY += ser1.deltaY;
endPosX += ser2.deltaX;
endPosY += ser2.deltaY;
posX += ser.deltaX;
posY += ser.deltaX;
finalRecords.add(ser);
}
}
SHAPEWITHSTYLE shape = new SHAPEWITHSTYLE();
shape.fillStyles = fillStyles;
shape.lineStyles = lineStyles;
shape.shapeRecords = finalRecords;
return shape;
}
@Override
public int getUsedParameters() {
return PARAMETER_RATIO;
}
@Override
public void toImage(int frame, int time, int ratio, RenderContext renderContext, SerializableImage image, Matrix transformation, 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);
}
@Override
public void toSVG(SVGExporter exporter, int ratio, ColorTransform colorTransform, int level, double zoom) {
if (ratio == -2) {
SHAPEWITHSTYLE beginShapes = getShapeAtRatio(0);
SHAPEWITHSTYLE endShapes = getShapeAtRatio(65535);
SVGMorphShapeExporter shapeExporter = new SVGMorphShapeExporter(swf, beginShapes, endShapes, exporter, null, colorTransform, zoom);
shapeExporter.export();
} else {
SHAPEWITHSTYLE shapes = getShapeAtRatio(ratio);
SVGShapeExporter shapeExporter = new SVGShapeExporter(swf, shapes, exporter, null, colorTransform, zoom);
shapeExporter.export();
}
}
@Override
public int getNumFrames() {
return 65536;
}
@Override
public boolean isSingleFrame() {
// Morpshape is a single frame specified with the ratio
return true;
}
@Override
public Shape getOutline(int frame, int time, int ratio, RenderContext renderContext, Matrix transformation) {
return transformation.toTransform().createTransformedShape(getShapeAtRatio(ratio).getOutline());
}
@Override
public String toHtmlCanvas(double unitDivisor) {
CanvasMorphShapeExporter cmse = new CanvasMorphShapeExporter(swf, getShapeAtRatio(0), getShapeAtRatio(MAX_RATIO), new ColorTransform(), unitDivisor, 0, 0);
cmse.export();
return cmse.getShapeData();
}
@Override
public void setCharacterId(int characterId) {
this.characterId = characterId;
}
}