Replacing morphshape from start/end shape.

Fixed Display of morphshape end shape to be exactly at 65535 ratio
This commit is contained in:
Jindra Petřík
2023-11-05 21:11:10 +01:00
parent 247b1c17a4
commit 377c5ac458
23 changed files with 1249 additions and 99 deletions
+2 -1
View File
@@ -13,7 +13,7 @@ All notable changes to this project will be documented in this file.
- [#2105] GFX - Basic tag info - [#2105] GFX - Basic tag info
- Context menu items to create new tags (shape, morphshape, sprite, image, movie, sound, binaryData) from files - Context menu items to create new tags (shape, morphshape, sprite, image, movie, sound, binaryData) from files
and using font embed dialog for fonts and using font embed dialog for fonts
- Replacing morphshapes (currently only same shape for start/end) - Replacing morphshapes
### Fixed ### Fixed
- [#1306], [#1768] Maximizing window on other than main monitor - [#1306], [#1768] Maximizing window on other than main monitor
@@ -24,6 +24,7 @@ All notable changes to this project will be documented in this file.
- [#2104] Empty texts import - [#2104] Empty texts import
- Centered start playing triangle (Playing on demand) - Centered start playing triangle (Playing on demand)
- miterLimitFactor is FIXED8 value in MORPHLINESTYLE2 - miterLimitFactor is FIXED8 value in MORPHLINESTYLE2
- Display of morphshape end shape to be exactly at 65535 ratio
### Changed ### Changed
- Basic tag info panel always visible even when nothing to display (to avoid flickering) - Basic tag info panel always visible even when nothing to display (to avoid flickering)
@@ -966,12 +966,7 @@ public final class Configuration {
@ConfigurationDefaultBoolean(true) @ConfigurationDefaultBoolean(true)
@ConfigurationCategory("ui") @ConfigurationCategory("ui")
public static ConfigurationItem<Boolean> displayAs3PCodePanel = null; public static ConfigurationItem<Boolean> displayAs3PCodePanel = null;
@ConfigurationDefaultBoolean(true)
@ConfigurationName("warning.replace.morphshape")
@ConfigurationCategory("ui")
public static ConfigurationItem<Boolean> warningReplaceMorphShape = null;
private enum OSId { private enum OSId {
WINDOWS, OSX, UNIX WINDOWS, OSX, UNIX
@@ -45,9 +45,19 @@ public class CurvedEdge extends StraightEdge implements IEdge {
return new CurvedEdge(toX, toY, controlX, controlY, fromX, fromY, lineStyleIdx, newFillStyleIdx); return new CurvedEdge(toX, toY, controlX, controlY, fromX, fromY, lineStyleIdx, newFillStyleIdx);
} }
@Override
public IEdge sameWithNewFillStyle(int newFillStyleIdx) {
return new CurvedEdge(fromX, fromY, controlX, controlY, toX, toY, lineStyleIdx, newFillStyleIdx);
}
@Override @Override
public String toString() { public String toString() {
return "curved[" + fromX / 20f + "," + fromY / 20f + " -> " + toX / 20f + "," + toY / 20f + " control:" + controlX / 20f + "," + controlY / 20f + "]"; return "curved[" + fromX / 20f + "," + fromY / 20f + " -> " + toX / 20f + "," + toY / 20f + " control:" + controlX / 20f + "," + controlY / 20f + "]";
} }
@Override
public IEdge reverse() {
return new CurvedEdge(toX, toY, controlX, controlY, fromX, fromY, lineStyleIdx, getFillStyleIdx());
}
} }
@@ -35,4 +35,8 @@ public interface IEdge {
public int getFillStyleIdx(); public int getFillStyleIdx();
public IEdge reverseWithNewFillStyle(int newFillStyleIdx); public IEdge reverseWithNewFillStyle(int newFillStyleIdx);
public IEdge reverse();
public IEdge sameWithNewFillStyle(int newFillStyleIdx);
} }
@@ -64,10 +64,8 @@ public abstract class ShapeExporterBase implements IShapeExporter {
private final ColorTransform colorTransform; private final ColorTransform colorTransform;
private boolean canUseSmoothing = true; private boolean canUseSmoothing = true;
protected int windingRule; protected int windingRule;
public ShapeExporterBase(int windingRule, int shapeNum, SWF swf, SHAPE shape, ColorTransform colorTransform) { public ShapeExporterBase(int windingRule, int shapeNum, SWF swf, SHAPE shape, ColorTransform colorTransform) {
this.shape = shape; this.shape = shape;
@@ -83,4 +83,15 @@ public class StraightEdge implements IEdge {
return "straight[" + fromX / 20f + "," + fromY / 20f + " -> " + toX / 20f + "," + toY / 20f + "]"; return "straight[" + fromX / 20f + "," + fromY / 20f + " -> " + toX / 20f + "," + toY / 20f + "]";
} }
@Override
public IEdge sameWithNewFillStyle(int newFillStyleIdx) {
return new StraightEdge(fromX, fromY, toX, toY, lineStyleIdx, newFillStyleIdx);
}
@Override
public IEdge reverse() {
return new StraightEdge(toX, toY, fromX, fromY, lineStyleIdx, getFillStyleIdx());
}
} }
@@ -0,0 +1,335 @@
/*
* Copyright (C) 2010-2023 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.importers.morphshape;
import com.jpexs.decompiler.flash.SWFOutputStream;
import com.jpexs.decompiler.flash.math.BezierEdge;
import com.jpexs.decompiler.flash.tags.DefineMorphShape2Tag;
import com.jpexs.decompiler.flash.tags.base.ShapeTag;
import com.jpexs.decompiler.flash.types.FILLSTYLE;
import com.jpexs.decompiler.flash.types.LINESTYLE2;
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.SHAPE;
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.Helper;
import com.jpexs.helpers.Reference;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
*
* @author JPEXS
*/
public class MorphShapeGenerator {
public void generate(DefineMorphShape2Tag morphShape, ShapeTag startShape, ShapeTag endShape) throws StyleMismatchException {
ShapeForMorphExporter startExport = new ShapeForMorphExporter(startShape);
startExport.export();
ShapeForMorphExporter endExport = new ShapeForMorphExporter(endShape);
endExport.export();
List<List<BezierEdge>> startBeziers = new ArrayList<>();
List<List<BezierEdge>> endBeziers = new ArrayList<>();
List<FILLSTYLE> startFillStyles = new ArrayList<>();
List<Integer> startFillstyleIndices = new ArrayList<>();
List<LINESTYLE2> startLineStyles = new ArrayList<>();
List<Integer> startLineStyleIndices = new ArrayList<>();
List<FILLSTYLE> endFillStyles = new ArrayList<>();
List<LINESTYLE2> endLineStyles = new ArrayList<>();
Set<Integer> usedBs = new HashSet<>();
List<Integer> startShapeIndices = new ArrayList<>();
List<Integer> endShapeIndices = new ArrayList<>();
for (int a = 0; a < startExport.shapes.size(); a++) {
double minDistance = Double.MAX_VALUE;
double minDistanceNoUsed = Double.MAX_VALUE;
int selectedB = -1;
int selectedBNoUsed = -1;
int startFillStyleIndex = startExport.fillStyleIndices.get(a);
FILLSTYLE startFillStyle = startFillStyleIndex == -1 ? null : startExport.fillStyles.get(startFillStyleIndex);
int startLineStyleIndex = startExport.lineStyleIndices.get(a);
LINESTYLE2 startLineStyle = startLineStyleIndex == -1 ? null : startExport.lineStyles.get(startLineStyleIndex);
for (int b = 0; b < endExport.shapes.size(); b++) {
int endFillStyleIndex = endExport.fillStyleIndices.get(b);
FILLSTYLE endFillStyle = endFillStyleIndex == -1 ? null : endExport.fillStyles.get(endFillStyleIndex);
int endLineStyleIndex = endExport.lineStyleIndices.get(b);
LINESTYLE2 endLineStyle = endLineStyleIndex == -1 ? null : endExport.lineStyles.get(endLineStyleIndex);
if (((endFillStyle == null && startFillStyle == null)
|| (startFillStyle != null
&& endFillStyle != null
&& startFillStyle.isCompatibleFillStyle(endFillStyle)))
&& ((endLineStyle == null && startLineStyle == null)
|| (startLineStyle != null
&& endLineStyle != null
&& startLineStyle.isCompatibleLineStyle(endLineStyle)))) {
double distance = startExport.centralPos.get(a).distance(endExport.centralPos.get(b));
if (distance < minDistance) {
minDistance = distance;
selectedB = b;
}
if (distance < minDistanceNoUsed && !usedBs.contains(b)) {
minDistanceNoUsed = distance;
selectedBNoUsed = b;
}
}
}
if (selectedB == -1) {
throw new StyleMismatchException();
}
if (selectedBNoUsed != -1) {
selectedB = selectedBNoUsed;
}
startShapeIndices.add(a);
endShapeIndices.add(selectedB);
usedBs.add(selectedB);
}
if (usedBs.size() < endExport.shapes.size()) {
for (int b = 0; b < endExport.shapes.size(); b++) {
if (!usedBs.contains(b)) {
double minDistance = Double.MAX_VALUE;
int selectedA = -1;
int endFillStyleIndex = endExport.fillStyleIndices.get(b);
FILLSTYLE endFillStyle = endFillStyleIndex == -1 ? null : endExport.fillStyles.get(endFillStyleIndex);
int endLineStyleIndex = endExport.lineStyleIndices.get(b);
LINESTYLE2 endLineStyle = endLineStyleIndex == -1 ? null : endExport.lineStyles.get(endLineStyleIndex);
for (int a = 0; a < startExport.shapes.size(); a++) {
int startFillStyleIndex = startExport.fillStyleIndices.get(a);
FILLSTYLE startFillStyle = startFillStyleIndex == -1 ? null : startExport.fillStyles.get(startFillStyleIndex);
int startLineStyleIndex = startExport.lineStyleIndices.get(a);
LINESTYLE2 startLineStyle = startLineStyleIndex == -1 ? null : startExport.lineStyles.get(startLineStyleIndex);
if (((endFillStyle == null && startFillStyle == null)
|| (startFillStyle != null
&& endFillStyle != null
&& startFillStyle.isCompatibleFillStyle(endFillStyle)))
&& ((endLineStyle == null && startLineStyle == null)
|| (startLineStyle != null
&& endLineStyle != null
&& startLineStyle.isCompatibleLineStyle(endLineStyle)))) {
double distance = startExport.centralPos.get(a).distance(endExport.centralPos.get(b));
if (distance < minDistance) {
minDistance = distance;
selectedA = a;
}
}
}
if (selectedA == -1) {
throw new StyleMismatchException();
}
startShapeIndices.add(selectedA);
endShapeIndices.add(b);
}
}
}
for (int i = 0; i < startShapeIndices.size(); i++) {
int a = startShapeIndices.get(i);
int b = endShapeIndices.get(i);
List<BezierEdge> shapeStart = Helper.deepCopy(startExport.shapes.get(a));
split(shapeStart, startExport.pointsPosPercent.get(a), endExport.pointsPosPercent.get(b));
List<BezierEdge> shapeEnd = Helper.deepCopy(endExport.shapes.get(b));
split(shapeEnd, endExport.pointsPosPercent.get(b), startExport.pointsPosPercent.get(a));
startBeziers.add(shapeStart);
endBeziers.add(shapeEnd);
if (startExport.fillStyleIndices.get(a) != -1) {
startFillStyles.add(startExport.fillStyles.get(startExport.fillStyleIndices.get(a)));
}
startFillstyleIndices.add(startExport.fillStyleIndices.get(a));
if (startExport.lineStyleIndices.get(a) != -1) {
startLineStyles.add(startExport.lineStyles.get(startExport.lineStyleIndices.get(a)));
}
startLineStyleIndices.add(startExport.lineStyleIndices.get(a));
if (endExport.fillStyleIndices.get(b) != -1) {
endFillStyles.add(endExport.fillStyles.get(endExport.fillStyleIndices.get(b)));
}
if (endExport.lineStyleIndices.get(b) != -1) {
endLineStyles.add(endExport.lineStyles.get(endExport.lineStyleIndices.get(b)));
}
}
List<SHAPERECORD> startRecords = new ArrayList<>();
List<SHAPERECORD> endRecords = new ArrayList<>();
MORPHFILLSTYLEARRAY morphFillStyleArray = new MORPHFILLSTYLEARRAY();
morphFillStyleArray.fillStyles = new MORPHFILLSTYLE[startFillStyles.size()];
MORPHLINESTYLEARRAY morphLineStyleArray = new MORPHLINESTYLEARRAY();
morphLineStyleArray.lineStyles2 = new MORPHLINESTYLE2[startLineStyles.size()];
for (int i = 0; i < startFillStyles.size(); i++) {
FILLSTYLE fsStart = startFillStyles.get(i);
FILLSTYLE fsEnd = endFillStyles.get(i);
MORPHFILLSTYLE morphFillStyle = fsStart.toMorphStyle(fsEnd);
if (morphFillStyle == null) {
throw new StyleMismatchException();
}
morphFillStyleArray.fillStyles[i] = morphFillStyle;
}
for (int i = 0; i < startLineStyles.size(); i++) {
LINESTYLE2 lsStart = startLineStyles.get(i);
LINESTYLE2 lsEnd = endLineStyles.get(i);
MORPHLINESTYLE2 morphLineStyle = lsStart.toMorphLineStyle2(lsEnd);
if (morphLineStyle == null) {
throw new StyleMismatchException();
}
morphLineStyleArray.lineStyles2[i] = morphLineStyle;
if (morphLineStyle.noHScaleFlag || morphLineStyle.noVScaleFlag) {
morphShape.usesNonScalingStrokes = true;
}
if (!morphLineStyle.noHScaleFlag && !morphLineStyle.noVScaleFlag) {
morphShape.usesScalingStrokes = true;
}
}
for (int i = 0; i < startBeziers.size(); i++) {
List<BezierEdge> beList = startBeziers.get(i);
StyleChangeRecord scr = new StyleChangeRecord();
scr.stateFillStyle0 = true;
if (startFillstyleIndices.get(i) != -1) {
scr.fillStyle0 = startFillstyleIndices.get(i) + 1;
} else {
scr.fillStyle0 = 0;
}
scr.stateLineStyle = true;
if (startLineStyleIndices.get(i) != -1) {
scr.lineStyle = startLineStyleIndices.get(i) + 1;
} else {
scr.lineStyle = 0;
}
startRecords.add(scr);
BezierEdge firstBe = beList.get(0);
StyleChangeRecord scrMove = new StyleChangeRecord();
scrMove.stateMoveTo = true;
scrMove.moveDeltaX = (int) Math.round(firstBe.getBeginPoint().getX());
scrMove.moveDeltaY = (int) Math.round(firstBe.getBeginPoint().getY());
startRecords.add(scrMove);
for (BezierEdge be : beList) {
SHAPERECORD rec = bezierToRecord(be);
startRecords.add(rec);
}
}
startRecords.add(new EndShapeRecord());
for (int i = 0; i < endBeziers.size(); i++) {
List<BezierEdge> beList = endBeziers.get(i);
BezierEdge firstBe = beList.get(0);
StyleChangeRecord scrMove = new StyleChangeRecord();
scrMove.stateMoveTo = true;
scrMove.moveDeltaX = (int) Math.round(firstBe.getBeginPoint().getX());
scrMove.moveDeltaY = (int) Math.round(firstBe.getBeginPoint().getY());
endRecords.add(scrMove);
for (BezierEdge be : beList) {
SHAPERECORD rec = bezierToRecord(be);
endRecords.add(rec);
}
}
endRecords.add(new EndShapeRecord());
morphShape.morphFillStyles = morphFillStyleArray;
morphShape.morphLineStyles = morphLineStyleArray;
morphShape.startEdges = new SHAPE();
morphShape.startEdges.shapeRecords = startRecords;
morphShape.startEdges.numFillBits = SWFOutputStream.getNeededBitsU(morphFillStyleArray.fillStyles.length);
morphShape.startEdges.numLineBits = SWFOutputStream.getNeededBitsU(morphLineStyleArray.lineStyles2.length);
morphShape.endEdges = new SHAPE();
morphShape.endEdges.numFillBits = 0;
morphShape.endEdges.numLineBits = 0;
morphShape.endEdges.shapeRecords = endRecords;
morphShape.setModified(true);
morphShape.updateBounds();
}
private void split(List<BezierEdge> shape, List<Double> originalPointsPosPercent, List<Double> newPointPosPercent) {
List<Double> pointPointsPercent = new ArrayList<>(originalPointsPosPercent);
int nppPos = 0;
for (int i = 0; i < shape.size() && nppPos < newPointPosPercent.size(); i++) {
BezierEdge be = shape.get(i);
double pointPosPct = pointPointsPercent.get(i);
double pointPosNextPct = pointPointsPercent.get(i + 1);
double pct = newPointPosPercent.get(nppPos);
if (pct > pointPosPct && pct < pointPosNextPct) {
double deltaPct = pointPosNextPct - pointPosPct;
double newPct = pct - pointPosPct;
double insidePct = newPct / deltaPct;
Reference<BezierEdge> leftRef = new Reference<>(null);
Reference<BezierEdge> rightRef = new Reference<>(null);
be.split(insidePct, leftRef, rightRef);
shape.remove(i);
shape.add(i, leftRef.getVal());
shape.add(i + 1, rightRef.getVal());
pointPointsPercent.add(i + 1, pct);
nppPos++;
} else if (pct == pointPosPct) {
nppPos++;
i--;
}
}
}
private SHAPERECORD bezierToRecord(BezierEdge be) {
if (be.points.size() == 2) {
StraightEdgeRecord ser = new StraightEdgeRecord();
ser.deltaX = (int) Math.round(be.points.get(1).getX() - be.points.get(0).getX());
ser.deltaY = (int) Math.round(be.points.get(1).getY() - be.points.get(0).getY());
ser.generalLineFlag = true;
ser.simplify();
return ser;
}
if (be.points.size() == 3) {
CurvedEdgeRecord cer = new CurvedEdgeRecord();
cer.controlDeltaX = (int) Math.round(be.points.get(1).getX() - be.points.get(0).getX());
cer.controlDeltaY = (int) Math.round(be.points.get(1).getY() - be.points.get(0).getY());
cer.anchorDeltaX = (int) Math.round(be.points.get(2).getX() - be.points.get(1).getX());
cer.anchorDeltaY = (int) Math.round(be.points.get(2).getY() - be.points.get(1).getY());
return cer;
}
return null;
}
}
@@ -0,0 +1,430 @@
/*
* Copyright (C) 2010-2023 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.importers.morphshape;
import com.jpexs.decompiler.flash.exporters.commonshape.Matrix;
import com.jpexs.decompiler.flash.exporters.shape.ShapeExporterBase;
import com.jpexs.decompiler.flash.math.BezierEdge;
import com.jpexs.decompiler.flash.tags.base.ShapeTag;
import com.jpexs.decompiler.flash.types.ColorTransform;
import com.jpexs.decompiler.flash.types.FILLSTYLE;
import com.jpexs.decompiler.flash.types.FOCALGRADIENT;
import com.jpexs.decompiler.flash.types.GRADIENT;
import com.jpexs.decompiler.flash.types.GRADRECORD;
import com.jpexs.decompiler.flash.types.LINESTYLE2;
import com.jpexs.decompiler.flash.types.RGB;
import com.jpexs.decompiler.flash.types.RGBA;
import com.jpexs.helpers.Helper;
import com.jpexs.helpers.Reference;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.List;
/**
*
* @author JPEXS
*/
public class ShapeForMorphExporter extends ShapeExporterBase {
public List<List<BezierEdge>> shapes = new ArrayList<>();
public List<Integer> fillStyleIndices = new ArrayList<>();
public List<Integer> lineStyleIndices = new ArrayList<>();
public List<List<Double>> pointsPosPercent = new ArrayList<>();
public List<Point2D.Double> centralPos = new ArrayList<>();
private final List<Point2D.Double> pointsSum = new ArrayList<>();
private final List<Integer> segmentCount = new ArrayList<>();
private final List<List<Double>> bezierLengths = new ArrayList<>();
private List<BezierEdge> currentShape = new ArrayList<>();
private List<Double> currentBezierLengths = new ArrayList<>();
private Point2D.Double currentPointsSum = new Point2D.Double();
private int currentSegmentCount = 0;
private int currentLineStyle = -1;
private int currentFillStyle = -1;
double currentShapeLen = 0;
public List<FILLSTYLE> fillStyles = new ArrayList<>();
public List<LINESTYLE2> lineStyles = new ArrayList<>();
private double lastX = 0;
private double lastY = 0;
public ShapeForMorphExporter(ShapeTag shape) {
super(ShapeTag.WIND_EVEN_ODD, shape.getShapeNum(), shape.getSwf(), shape.shapes, new ColorTransform());
}
@Override
public void beginShape() {
shapes = new ArrayList<>();
}
@Override
public void endShape() {
endCurrent();
for (int i = 0; i < segmentCount.size(); i++) {
Point2D.Double center = new Point2D.Double();
center.x = pointsSum.get(i).x / (double) segmentCount.get(i);
center.y = pointsSum.get(i).y / (double) segmentCount.get(i);
centralPos.add(center);
}
for (int i = 0; i < shapes.size(); i++) {
List<BezierEdge> shape = shapes.get(i);
List<Point2D> points = new ArrayList<>();
BezierEdge be = null;
for (int j = 0; j < shape.size(); j++) {
be = shape.get(j);
points.add(new Point2D.Double(
be.getBeginPoint().getX() - centralPos.get(i).getX(),
be.getBeginPoint().getY() - centralPos.get(i).getY()
));
}
points.add(new Point2D.Double(
be.getEndPoint().getX() - centralPos.get(i).getX(),
be.getEndPoint().getY() - centralPos.get(i).getY()
));
double w = 0;
for (int j = 0; j < points.size(); j++) {
int secondPoint = j + 1 == points.size() ? 0 : j + 1;
double x = points.get(j).getX();
double xp1 = points.get(secondPoint).getX();
double y = points.get(j).getY();
double yp1 = points.get(secondPoint).getY();
if (y * yp1 < 0) {
double r = x + ((y * (xp1 - x)) / (y - yp1));
if (r > 0) {
if (y < 0) {
w = w + 1;
} else {
w = w - 1;
}
}
} else if ((y == 0) && (x > 0)) {
if (yp1 > 0) {
w = w + .5;
} else {
w = w - .5;
}
} else if ((yp1 == 0) && (xp1 > 0)) {
if (y < 0) {
w = w + .5;
} else {
w = w - .5;
}
}
}
if (w > 0) {
//clockwise
} else {
//counter clock wise
List<BezierEdge> newShape = new ArrayList<>();
List<Double> newBeLength = new ArrayList<>();
for (int j = shape.size() - 1; j >= 0; j--) {
newShape.add(shape.get(j).reverse());
newBeLength.add(bezierLengths.get(i).get(j));
}
shape.clear();
shape.addAll(newShape);
bezierLengths.get(i).clear();
bezierLengths.get(i).addAll(newBeLength);
}
}
for (int i = 0; i < shapes.size(); i++) {
List<BezierEdge> shape = shapes.get(i);
//closed shape
if (shape.get(0).getBeginPoint().equals(shape.get(shape.size() - 1).getEndPoint())) {
//Find most top left point
double minX = Double.MAX_VALUE;
double minY = Double.MAX_VALUE;
for (int j = 0; j < shape.size(); j++) {
if (shape.get(j).getBeginPoint().getX() < minX) {
minX = shape.get(j).getBeginPoint().getX();
}
if (shape.get(j).getBeginPoint().getY() < minY) {
minY = shape.get(j).getBeginPoint().getY();
}
}
double minDist = Double.MAX_VALUE;
int minPos = -1;
for (int j = 0; j < shape.size(); j++) {
double dist = shape.get(j).getBeginPoint().distance(minX, minY);
if (dist < minDist) {
minDist = dist;
minPos = j;
}
}
if (minPos > -1) {
//Rearange shape to start with the top left point
for (int j = 0; j < minPos; j++) {
shape.add(shape.remove(0));
bezierLengths.get(i).add(bezierLengths.get(i).remove(0));
}
}
}
}
for (List<Double> pp : bezierLengths) {
List<Double> ppPercent = new ArrayList<>();
double len = 0.0;
for (double bLength : pp) {
len += bLength;
}
double pos = 0;
for (double bLength : pp) {
double pct = roundPct(pos / len);
pos += bLength;
ppPercent.add(pct);
}
ppPercent.add(1.0);
pointsPosPercent.add(ppPercent);
}
}
@Override
public void beginAliasedFills() {
}
@Override
public void beginFills() {
}
@Override
public void endFills() {
endCurrent();
}
@Override
public void beginLines() {
currentShape = null;
}
@Override
public void endLines(boolean close) {
endCurrent();
}
@Override
public void beginFill(RGB color) {
endCurrent();
currentShape = new ArrayList<>();
currentFillStyle = fillStyles.size();
FILLSTYLE fillStyle = new FILLSTYLE();
fillStyle.fillStyleType = FILLSTYLE.SOLID;
fillStyle.color = color;
fillStyles.add(fillStyle);
}
@Override
public void beginGradientFill(int type, GRADRECORD[] gradientRecords, Matrix matrix, int spreadMethod, int interpolationMethod, float focalPointRatio) {
endCurrent();
currentShape = new ArrayList<>();
currentFillStyle = fillStyles.size();
FILLSTYLE fillStyle = new FILLSTYLE();
fillStyle.fillStyleType = type;
fillStyle.gradient = focalPointRatio == 0 ? new FOCALGRADIENT() : new GRADIENT();
fillStyle.gradient.gradientRecords = Helper.deepCopy(gradientRecords);
fillStyle.gradientMatrix = matrix.toMATRIX();
fillStyle.gradient.spreadMode = spreadMethod;
fillStyle.gradient.interpolationMode = interpolationMethod;
if (focalPointRatio != 0) {
((FOCALGRADIENT) fillStyle.gradient).focalPoint = focalPointRatio;
}
fillStyles.add(fillStyle);
}
@Override
public void beginBitmapFill(int bitmapId, Matrix matrix, boolean repeat, boolean smooth, ColorTransform colorTransform) {
endCurrent();
currentShape = new ArrayList<>();
currentFillStyle = fillStyles.size();
FILLSTYLE fillStyle = new FILLSTYLE();
if (repeat) {
if (smooth) {
fillStyle.fillStyleType = FILLSTYLE.REPEATING_BITMAP;
} else {
fillStyle.fillStyleType = FILLSTYLE.NON_SMOOTHED_REPEATING_BITMAP;
}
} else {
if (smooth) {
fillStyle.fillStyleType = FILLSTYLE.CLIPPED_BITMAP;
} else {
fillStyle.fillStyleType = FILLSTYLE.NON_SMOOTHED_CLIPPED_BITMAP;
}
}
fillStyle.bitmapMatrix = matrix.toMATRIX();
fillStyle.bitmapId = bitmapId;
fillStyles.add(fillStyle);
}
@Override
public void endFill() {
endCurrent();
}
private void endCurrent() {
if (currentShape != null && !currentShape.isEmpty()) {
shapes.add(currentShape);
bezierLengths.add(currentBezierLengths);
pointsSum.add(currentPointsSum);
segmentCount.add(currentSegmentCount);
fillStyleIndices.add(currentFillStyle);
lineStyleIndices.add(currentLineStyle);
}
currentShapeLen = 0;
currentShape = new ArrayList<>();
currentBezierLengths = new ArrayList<>();
currentPointsSum = new Point2D.Double();
currentSegmentCount = 0;
currentFillStyle = -1;
currentLineStyle = -1;
}
@Override
public void lineStyle(double thickness, RGB color, boolean pixelHinting, String scaleMode, int startCaps, int endCaps, int joints, float miterLimit) {
endCurrent();
currentLineStyle = lineStyles.size();
LINESTYLE2 lineStyle = new LINESTYLE2();
lineStyle.width = (int) thickness;
lineStyle.color = new RGBA(color);
lineStyle.pixelHintingFlag = pixelHinting;
switch (scaleMode) {
case "NONE":
lineStyle.noHScaleFlag = true;
lineStyle.noVScaleFlag = true;
break;
case "VERTICAL":
lineStyle.noHScaleFlag = true;
break;
case "HORIZONTAL":
lineStyle.noVScaleFlag = true;
break;
}
lineStyle.startCapStyle = startCaps;
lineStyle.endCapStyle = endCaps;
lineStyle.joinStyle = joints;
lineStyle.miterLimitFactor = miterLimit;
lineStyles.add(lineStyle);
}
@Override
public void lineGradientStyle(int type, GRADRECORD[] gradientRecords, Matrix matrix, int spreadMethod, int interpolationMethod, float focalPointRatio) {
LINESTYLE2 lineStyle = lineStyles.get(lineStyles.size() - 1);
lineStyle.hasFillFlag = true;
FILLSTYLE fillStyle = new FILLSTYLE();
fillStyle.fillStyleType = type;
fillStyle.gradient = focalPointRatio == 0 ? new FOCALGRADIENT() : new GRADIENT();
fillStyle.gradient.gradientRecords = Helper.deepCopy(gradientRecords);
fillStyle.gradientMatrix = matrix.toMATRIX();
fillStyle.gradient.spreadMode = spreadMethod;
fillStyle.gradient.interpolationMode = interpolationMethod;
if (focalPointRatio != 0) {
((FOCALGRADIENT) fillStyle.gradient).focalPoint = focalPointRatio;
}
lineStyle.fillType = fillStyle;
}
@Override
public void lineBitmapStyle(int bitmapId, Matrix matrix, boolean repeat, boolean smooth, ColorTransform colorTransform) {
LINESTYLE2 lineStyle = lineStyles.get(lineStyles.size() - 1);
lineStyle.hasFillFlag = true;
FILLSTYLE fillStyle = new FILLSTYLE();
if (repeat) {
if (smooth) {
fillStyle.fillStyleType = FILLSTYLE.REPEATING_BITMAP;
} else {
fillStyle.fillStyleType = FILLSTYLE.NON_SMOOTHED_REPEATING_BITMAP;
}
} else {
if (smooth) {
fillStyle.fillStyleType = FILLSTYLE.CLIPPED_BITMAP;
} else {
fillStyle.fillStyleType = FILLSTYLE.NON_SMOOTHED_CLIPPED_BITMAP;
}
}
fillStyle.bitmapMatrix = matrix.toMATRIX();
fillStyle.bitmapId = bitmapId;
lineStyle.fillType = fillStyle;
}
@Override
public void moveTo(double x, double y) {
int backupFillStyle = currentFillStyle;
int backupLineStyle = currentLineStyle;
endCurrent();
currentFillStyle = backupFillStyle;
currentLineStyle = backupLineStyle;
lastX = (int) x;
lastY = (int) y;
//currentPointsPos.add(0.0);
}
@Override
public void lineTo(double x, double y) {
if (x == lastX && y == lastY) {
return;
}
BezierEdge be = new BezierEdge(lastX, lastY, x, y);
currentPointsSum.x += x; // - lastX;
currentPointsSum.y += y; // - lastY;
currentShape.add(be);
lastX = x;
lastY = y;
currentBezierLengths.add(be.length());
currentShapeLen += be.length();
currentSegmentCount++;
}
@Override
public void curveTo(double controlX, double controlY, double anchorX, double anchorY) {
if (anchorX == lastX && anchorY == lastY) {
return;
}
BezierEdge be = new BezierEdge(lastX, lastY, controlX, controlY, anchorX, anchorY);
currentShape.add(be);
currentPointsSum.x += anchorX; // - lastX;
currentPointsSum.y += anchorY; // - lastY;
lastX = anchorX;
lastY = anchorY;
currentBezierLengths.add(be.length());
currentShapeLen += be.length();
currentSegmentCount++;
}
private double roundPct(double pct) {
double precision = 1000000d;
return Math.round(pct * precision) / precision;
}
}
@@ -0,0 +1,25 @@
/*
* Copyright (C) 2010-2023 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.importers.morphshape;
/**
*
* @author JPEXS
*/
public class StyleMismatchException extends Exception {
}
@@ -226,6 +226,11 @@ public class SvgImporter {
transform = Matrix.getScaleInstance(ratioX, ratioY); transform = Matrix.getScaleInstance(ratioX, ratioY);
transform.translate(origXmin / SWF.unitDivisor / ratioX, origYmin / SWF.unitDivisor / ratioY); transform.translate(origXmin / SWF.unitDivisor / ratioX, origYmin / SWF.unitDivisor / ratioY);
} }
transform = transform.preConcatenate(Matrix.getTranslateInstance(-viewBox.x, -viewBox.y));
if (viewBox.height != 0 && viewBox.width != 0) {
transform = transform.preConcatenate(Matrix.getScaleInstance(width / viewBox.width, height / viewBox.height));
}
processSvgObject(idMap, shapeNum, shapes, rootElement, transform, style, morphShape); processSvgObject(idMap, shapeNum, shapes, rootElement, transform, style, morphShape);
} catch (SAXException | IOException | ParserConfigurationException ex) { } catch (SAXException | IOException | ParserConfigurationException ex) {
@@ -19,14 +19,16 @@ package com.jpexs.decompiler.flash.math;
import com.jpexs.helpers.Reference; import com.jpexs.helpers.Reference;
import java.awt.geom.Point2D; import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D; import java.awt.geom.Rectangle2D;
import java.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Stack;
/** /**
* *
* @author JPEXS * @author JPEXS
*/ */
public class BezierEdge { public class BezierEdge implements Serializable {
public List<Point2D> points = new ArrayList<>(); public List<Point2D> points = new ArrayList<>();
@@ -45,6 +47,14 @@ public class BezierEdge {
points.add(new Point2D.Double(x1, y1)); points.add(new Point2D.Double(x1, y1));
} }
public Point2D getBeginPoint() {
return points.get(0);
}
public Point2D getEndPoint() {
return points.get(points.size() - 1);
}
public Point2D pointAt(double t) { public Point2D pointAt(double t) {
if (points.size() == 2) { if (points.size() == 2) {
double x = (1 - t) * points.get(0).getX() + t * points.get(1).getX(); double x = (1 - t) * points.get(0).getX() + t * points.get(1).getX();
@@ -189,6 +199,30 @@ public class BezierEdge {
} }
return ok; return ok;
} }
public double length() {
double distance = 0;
double epsilon = 1;
Stack<BezierEdge> parts = new Stack<BezierEdge>();
parts.push(this);
while (!parts.isEmpty()) {
BezierEdge curve = parts.pop();
double d = curve.points.get(0).distance(curve.points.get(curve.points.size() - 1));
if (d < epsilon) {
distance += d;
} else {
Reference<BezierEdge> leftRef = new Reference<>(null);
Reference<BezierEdge> rightRef = new Reference<>(null);
curve.split(0.5, leftRef, rightRef);
parts.add(leftRef.getVal());
parts.add(rightRef.getVal());
}
}
return distance;
}
@Override @Override
public String toString() { public String toString() {
@@ -207,6 +241,14 @@ public class BezierEdge {
left.setVal(new BezierEdge(leftPoints)); left.setVal(new BezierEdge(leftPoints));
right.setVal(new BezierEdge(rightPoints)); right.setVal(new BezierEdge(rightPoints));
} }
public BezierEdge reverse() {
List<Point2D> revPoints = new ArrayList<>();
for (int i = points.size() - 1; i >= 0; i--) {
revPoints.add(points.get(i));
}
return new BezierEdge(revPoints);
}
public static void main(String[] args) { public static void main(String[] args) {
List<Double> t1 = new ArrayList<>(); List<Double> t1 = new ArrayList<>();
@@ -245,6 +287,10 @@ public class BezierEdge {
); );
System.out.println("Intersection point: " + c); System.out.println("Intersection point: " + c);
BezierEdge be = new BezierEdge(0, 0, 100, 50, 0, 100);
System.out.println("be5.dist: " + be.length());
//Rectangle2D out = new Rectangle2D.Double(); //Rectangle2D out = new Rectangle2D.Double();
//rectIntersection(new Rectangle2D.Double(0,0,50,50), new Rectangle2D.Double(0,50,50,50), out); //rectIntersection(new Rectangle2D.Double(0,0,50,50), new Rectangle2D.Double(0,50,50,50), out);
@@ -84,7 +84,7 @@ public abstract class ShapeTag extends DrawableTag implements LazyObject {
public abstract int getShapeNum(); public abstract int getShapeNum();
public SHAPEWITHSTYLE getShapes() { public synchronized SHAPEWITHSTYLE getShapes() {
if (shapes == null && shapeData != null) { if (shapes == null && shapeData != null) {
try { try {
SWFInputStream sis = new SWFInputStream(swf, shapeData.getArray(), 0, shapeData.getPos() + shapeData.getLength()); SWFInputStream sis = new SWFInputStream(swf, shapeData.getArray(), 0, shapeData.getPos() + shapeData.getLength());
@@ -151,6 +151,30 @@ public class FILLSTYLE implements NeedsCharacters, FieldChangeObserver, Serializ
gradient = g; gradient = g;
} }
} }
}
public boolean isCompatibleFillStyle(FILLSTYLE otherFillStyle) {
if (fillStyleType != otherFillStyle.fillStyleType) {
return false;
}
switch (fillStyleType) {
case CLIPPED_BITMAP:
case NON_SMOOTHED_CLIPPED_BITMAP:
case NON_SMOOTHED_REPEATING_BITMAP:
case REPEATING_BITMAP:
if (bitmapId != otherFillStyle.bitmapId) {
return false;
}
break;
case LINEAR_GRADIENT:
case RADIAL_GRADIENT:
case FOCAL_RADIAL_GRADIENT:
if (!gradient.isCompatibleGradient(otherFillStyle.gradient)) {
return false;
}
break;
}
return true;
} }
public MORPHFILLSTYLE toMorphStyle() { public MORPHFILLSTYLE toMorphStyle() {
@@ -171,4 +195,30 @@ public class FILLSTYLE implements NeedsCharacters, FieldChangeObserver, Serializ
return morphFillStyle; return morphFillStyle;
} }
public MORPHFILLSTYLE toMorphStyle(FILLSTYLE endFillStyle) {
if (!isCompatibleFillStyle(endFillStyle)) {
return null;
}
MORPHFILLSTYLE morphFillStyle = new MORPHFILLSTYLE();
morphFillStyle.bitmapId = bitmapId;
if (bitmapMatrix != null) {
morphFillStyle.startBitmapMatrix = new MATRIX(bitmapMatrix);
}
if (endFillStyle.bitmapMatrix != null) {
morphFillStyle.endBitmapMatrix = new MATRIX(endFillStyle.bitmapMatrix);
}
if (color != null) {
morphFillStyle.startColor = new RGBA(color);
}
if (endFillStyle.color != null) {
morphFillStyle.endColor = new RGBA(endFillStyle.color);
}
morphFillStyle.fillStyleType = fillStyleType;
if (gradient != null) {
morphFillStyle.gradient = gradient.toMorphGradient(endFillStyle.gradient);
}
return morphFillStyle;
}
} }
@@ -61,7 +61,20 @@ public class GRADIENT implements Serializable {
public static final int INTERPOLATION_RESERVED2 = 3; public static final int INTERPOLATION_RESERVED2 = 3;
@SWFArray(value = "record") @SWFArray(value = "record")
public GRADRECORD[] gradientRecords = new GRADRECORD[0]; public GRADRECORD[] gradientRecords = new GRADRECORD[0];
public boolean isCompatibleGradient(GRADIENT otherGradient) {
if (interpolationMode != otherGradient.interpolationMode) {
return false;
}
if (spreadMode != otherGradient.spreadMode) {
return false;
}
if (gradientRecords.length != otherGradient.gradientRecords.length) {
return false;
}
return true;
}
public MORPHGRADIENT toMorphGradient() { public MORPHGRADIENT toMorphGradient() {
MORPHGRADIENT morphGradient = new MORPHGRADIENT(); MORPHGRADIENT morphGradient = new MORPHGRADIENT();
@@ -73,4 +86,18 @@ public class GRADIENT implements Serializable {
} }
return morphGradient; return morphGradient;
} }
public MORPHGRADIENT toMorphGradient(GRADIENT endGradient) {
if (!isCompatibleGradient(endGradient)) {
return null;
}
MORPHGRADIENT morphGradient = new MORPHGRADIENT();
morphGradient.interPolationMode = interpolationMode;
morphGradient.spreadMode = spreadMode;
morphGradient.gradientRecords = new MORPHGRADRECORD[gradientRecords.length];
for (int i = 0; i < gradientRecords.length; i++) {
morphGradient.gradientRecords[i] = gradientRecords[i].toMorphGradRecord();
}
return morphGradient;
}
} }
@@ -46,4 +46,13 @@ public class GRADRECORD implements Serializable {
morphGradRecord.endRatio = ratio; morphGradRecord.endRatio = ratio;
return morphGradRecord; return morphGradRecord;
} }
public MORPHGRADRECORD toMorphGradRecord(GRADRECORD endGradRecord) {
MORPHGRADRECORD morphGradRecord = new MORPHGRADRECORD();
morphGradRecord.startColor = new RGBA(color);
morphGradRecord.endColor = new RGBA(endGradRecord.color);
morphGradRecord.startRatio = ratio;
morphGradRecord.endRatio = endGradRecord.ratio;
return morphGradRecord;
}
} }
@@ -141,6 +141,44 @@ public class LINESTYLE2 implements NeedsCharacters, Serializable, ILINESTYLE {
@Override @Override
public void setWidth(int width) { public void setWidth(int width) {
this.width = width; this.width = width;
}
public boolean isCompatibleLineStyle(LINESTYLE2 otherLineStyle) {
if (startCapStyle != otherLineStyle.startCapStyle) {
return false;
}
if (endCapStyle != otherLineStyle.endCapStyle) {
return false;
}
if (joinStyle != otherLineStyle.joinStyle) {
return false;
}
if (hasFillFlag != otherLineStyle.hasFillFlag) {
return false;
}
if (noVScaleFlag != otherLineStyle.noVScaleFlag) {
return false;
}
if (pixelHintingFlag != otherLineStyle.pixelHintingFlag) {
return false;
}
if (noClose != otherLineStyle.noClose) {
return false;
}
if (miterLimitFactor != otherLineStyle.miterLimitFactor) {
return false;
}
if (hasFillFlag) {
if (!fillType.isCompatibleFillStyle(otherLineStyle.fillType)) {
return false;
}
}
return true;
} }
public MORPHLINESTYLE2 toMorphLineStyle2() { public MORPHLINESTYLE2 toMorphLineStyle2() {
@@ -165,4 +203,33 @@ public class LINESTYLE2 implements NeedsCharacters, Serializable, ILINESTYLE {
} }
return morphLineStyle2; return morphLineStyle2;
} }
public MORPHLINESTYLE2 toMorphLineStyle2(LINESTYLE2 endLineStyle) {
if (!isCompatibleLineStyle(endLineStyle)) {
return null;
}
MORPHLINESTYLE2 morphLineStyle2 = new MORPHLINESTYLE2();
morphLineStyle2.startWidth = width;
morphLineStyle2.endWidth = endLineStyle.width;
morphLineStyle2.startCapStyle = startCapStyle;
morphLineStyle2.joinStyle = joinStyle;
morphLineStyle2.hasFillFlag = hasFillFlag;
morphLineStyle2.noHScaleFlag = noHScaleFlag;
morphLineStyle2.noVScaleFlag = noVScaleFlag;
morphLineStyle2.pixelHintingFlag = pixelHintingFlag;
morphLineStyle2.noClose = noClose;
morphLineStyle2.endCapStyle = endCapStyle;
morphLineStyle2.miterLimitFactor = miterLimitFactor;
if (color != null) {
morphLineStyle2.startColor = new RGBA(color);
}
if (endLineStyle.color != null) {
morphLineStyle2.endColor = new RGBA(endLineStyle.color);
}
if (hasFillFlag) {
morphLineStyle2.fillType = fillType.toMorphStyle(endLineStyle.fillType);
}
return morphLineStyle2;
}
} }
+176 -64
View File
@@ -132,6 +132,8 @@ import com.jpexs.decompiler.flash.importers.SpriteImporter;
import com.jpexs.decompiler.flash.importers.SwfXmlImporter; import com.jpexs.decompiler.flash.importers.SwfXmlImporter;
import com.jpexs.decompiler.flash.importers.SymbolClassImporter; import com.jpexs.decompiler.flash.importers.SymbolClassImporter;
import com.jpexs.decompiler.flash.importers.TextImporter; import com.jpexs.decompiler.flash.importers.TextImporter;
import com.jpexs.decompiler.flash.importers.morphshape.MorphShapeGenerator;
import com.jpexs.decompiler.flash.importers.morphshape.StyleMismatchException;
import com.jpexs.decompiler.flash.importers.svg.SvgImporter; import com.jpexs.decompiler.flash.importers.svg.SvgImporter;
import com.jpexs.decompiler.flash.search.ABCSearchResult; import com.jpexs.decompiler.flash.search.ABCSearchResult;
import com.jpexs.decompiler.flash.search.ActionSearchResult; import com.jpexs.decompiler.flash.search.ActionSearchResult;
@@ -142,7 +144,10 @@ import com.jpexs.decompiler.flash.tags.DefineBitsJPEG3Tag;
import com.jpexs.decompiler.flash.tags.DefineBitsJPEG4Tag; import com.jpexs.decompiler.flash.tags.DefineBitsJPEG4Tag;
import com.jpexs.decompiler.flash.tags.DefineBitsTag; import com.jpexs.decompiler.flash.tags.DefineBitsTag;
import com.jpexs.decompiler.flash.tags.DefineFont4Tag; import com.jpexs.decompiler.flash.tags.DefineFont4Tag;
import com.jpexs.decompiler.flash.tags.DefineMorphShape2Tag;
import com.jpexs.decompiler.flash.tags.DefineMorphShapeTag;
import com.jpexs.decompiler.flash.tags.DefineShape2Tag; import com.jpexs.decompiler.flash.tags.DefineShape2Tag;
import com.jpexs.decompiler.flash.tags.DefineShape4Tag;
import com.jpexs.decompiler.flash.tags.DefineSpriteTag; import com.jpexs.decompiler.flash.tags.DefineSpriteTag;
import com.jpexs.decompiler.flash.tags.DefineVideoStreamTag; import com.jpexs.decompiler.flash.tags.DefineVideoStreamTag;
import com.jpexs.decompiler.flash.tags.DoActionTag; import com.jpexs.decompiler.flash.tags.DoActionTag;
@@ -371,8 +376,8 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se
private static final int DETAILCARDTAGINFO = 0; private static final int DETAILCARDTAGINFO = 0;
private static final int DETAILCARDEMPTYPANEL = -1; private static final int DETAILCARDEMPTYPANEL = -1;
private static final int DETAILCARDDEBUGSTACKFRAME = 2; private static final int DETAILCARDDEBUGSTACKFRAME = 2;
private static final String SPLIT_PANE1 = "SPLITPANE1"; private static final String SPLIT_PANE1 = "SPLITPANE1";
@@ -413,7 +418,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se
private final PreviewPanel dumpPreviewPanel; private final PreviewPanel dumpPreviewPanel;
private final TagInfoPanel tagInfoPanel; private final TagInfoPanel tagInfoPanel;
private final DebugStackPanel debugStackPanel; private final DebugStackPanel debugStackPanel;
private TreePanelMode treePanelMode; private TreePanelMode treePanelMode;
@@ -1028,7 +1033,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se
public String translate(String key) { public String translate(String key) {
return mainFrame.translate(key); return mainFrame.translate(key);
} }
public MainPanel(MainFrame mainFrame, MainFrameMenu mainMenu, FlashPlayerPanel flashPanel, FlashPlayerPanel previewFlashPanel) { public MainPanel(MainFrame mainFrame, MainFrameMenu mainMenu, FlashPlayerPanel flashPanel, FlashPlayerPanel previewFlashPanel) {
super(); super();
@@ -1050,9 +1055,9 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se
whitePanel.setBackground(Color.white); whitePanel.setBackground(Color.white);
} }
detailPanel.add(whitePanel, DETAILCARDEMPTYPANEL); detailPanel.add(whitePanel, DETAILCARDEMPTYPANEL);
*/ */
tagInfoPanel = new TagInfoPanel(this); tagInfoPanel = new TagInfoPanel(this);
debugStackPanel = new DebugStackPanel(); debugStackPanel = new DebugStackPanel();
Main.getDebugHandler().addBreakListener(new DebuggerHandler.BreakListener() { Main.getDebugHandler().addBreakListener(new DebuggerHandler.BreakListener() {
@Override @Override
@@ -1061,8 +1066,8 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se
@Override @Override
public void run() { public void run() {
showDetail(DETAILCARDDEBUGSTACKFRAME); showDetail(DETAILCARDDEBUGSTACKFRAME);
} }
}); });
} }
@Override @Override
@@ -1071,14 +1076,14 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se
@Override @Override
public void run() { public void run() {
showDetail(DETAILCARDEMPTYPANEL); showDetail(DETAILCARDEMPTYPANEL);
} }
}); });
} }
}); });
Main.getDebugHandler().addConnectionListener(new DebuggerHandler.ConnectionListener() { Main.getDebugHandler().addConnectionListener(new DebuggerHandler.ConnectionListener() {
@Override @Override
public void connected() { public void connected() {
} }
@Override @Override
@@ -1087,11 +1092,11 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se
@Override @Override
public void run() { public void run() {
reload(true); reload(true);
} }
}); });
} }
}); });
UIManager.getDefaults().put("TreeUI", BasicTreeUI.class.getName()); UIManager.getDefaults().put("TreeUI", BasicTreeUI.class.getName());
tagTree = new TagTree(null, this); tagTree = new TagTree(null, this);
tagTree.addTreeSelectionListener(this); tagTree.addTreeSelectionListener(this);
@@ -1454,7 +1459,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se
public ABCPanel getABCPanel() { public ABCPanel getABCPanel() {
if (abcPanel == null) { if (abcPanel == null) {
abcPanel = new ABCPanel(this); abcPanel = new ABCPanel(this);
displayPanel.add(abcPanel, CARDACTIONSCRIPT3PANEL); displayPanel.add(abcPanel, CARDACTIONSCRIPT3PANEL);
} }
return abcPanel; return abcPanel;
@@ -1854,19 +1859,19 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se
if (constants.getMultiname(multiNameIndex).name_index > 0) { if (constants.getMultiname(multiNameIndex).name_index > 0) {
oldName = constants.getString(constants.getMultiname(multiNameIndex).name_index); oldName = constants.getString(constants.getMultiname(multiNameIndex).name_index);
} }
String scriptName = abcPanel.decompiledTextArea.getScriptLeaf().getClassPath().toString(); String scriptName = abcPanel.decompiledTextArea.getScriptLeaf().getClassPath().toString();
String newName = ViewMessages.showInputDialog(this, translate("rename.enternew"), oldName); String newName = ViewMessages.showInputDialog(this, translate("rename.enternew"), oldName);
if (newName != null) { if (newName != null) {
if (!oldName.equals(newName)) { if (!oldName.equals(newName)) {
if (oldName.equals(abcPanel.decompiledTextArea.getScriptLeaf().getClassPath().className)) { if (oldName.equals(abcPanel.decompiledTextArea.getScriptLeaf().getClassPath().className)) {
scriptName = abcPanel.decompiledTextArea.getScriptLeaf().getClassPath().packageStr.add(newName, "").toPrintableString(true); scriptName = abcPanel.decompiledTextArea.getScriptLeaf().getClassPath().packageStr.add(newName, "").toPrintableString(true);
} }
final String fScriptName = scriptName; final String fScriptName = scriptName;
int mulCount = 0; int mulCount = 0;
for (ABCContainerTag cnt : abcList) { for (ABCContainerTag cnt : abcList) {
ABC abc = cnt.getABC(); ABC abc = cnt.getABC();
@@ -1941,17 +1946,17 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se
Set<Openable> usedOpenables = new HashSet<>(); Set<Openable> usedOpenables = new HashSet<>();
Set<OpenableList> usedOpenableLists = new HashSet<>(); Set<OpenableList> usedOpenableLists = new HashSet<>();
Map<OpenableList, Map<String, Integer>> usedSwfIdsInBundles = new HashMap<>(); Map<OpenableList, Map<String, Integer>> usedSwfIdsInBundles = new HashMap<>();
for (TreeItem d : sel) { for (TreeItem d : sel) {
Openable selectedNodeOpenable = d.getOpenable(); Openable selectedNodeOpenable = d.getOpenable();
usedOpenables.add(selectedNodeOpenable); usedOpenables.add(selectedNodeOpenable);
OpenableList list = selectedNodeOpenable.getOpenableList(); OpenableList list = selectedNodeOpenable.getOpenableList();
if (list != null) { if (list != null) {
usedOpenableLists.add(list); usedOpenableLists.add(list);
} }
} }
Map<String, Integer> usedSwfsIds = new HashMap<>(); Map<String, Integer> usedSwfsIds = new HashMap<>();
@@ -2074,13 +2079,13 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se
} }
String selFile2; String selFile2;
if (usedOpenables.size() > 1) { if (usedOpenables.size() > 1) {
if (usedOpenableLists.size() > 1 && openable.getOpenableList() != null && openable.getOpenableList().isBundle()) { if (usedOpenableLists.size() > 1 && openable.getOpenableList() != null && openable.getOpenableList().isBundle()) {
if (!usedSwfIdsInBundles.containsKey(openable.getOpenableList())) { if (!usedSwfIdsInBundles.containsKey(openable.getOpenableList())) {
usedSwfIdsInBundles.put(openable.getOpenableList(), new HashMap<>()); usedSwfIdsInBundles.put(openable.getOpenableList(), new HashMap<>());
} }
selFile2 = selFile + File.separator + openable.getOpenableList().name + File.separator + Helper.getNextId(openable.getTitleOrShortFileName(), usedSwfIdsInBundles.get(openable.getOpenableList())); selFile2 = selFile + File.separator + openable.getOpenableList().name + File.separator + Helper.getNextId(openable.getTitleOrShortFileName(), usedSwfIdsInBundles.get(openable.getOpenableList()));
} else { } else {
selFile2 = selFile + File.separator + Helper.getNextId(openable.getTitleOrShortFileName(), usedSwfsIds); selFile2 = selFile + File.separator + Helper.getNextId(openable.getTitleOrShortFileName(), usedSwfsIds);
} }
} else { } else {
@@ -2547,16 +2552,16 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se
} }
} }
abcPanel.detailPanel.methodTraitPanel.methodCodePanel.gotoInstrLine(line); abcPanel.detailPanel.methodTraitPanel.methodCodePanel.gotoInstrLine(line);
} else { } else {
abcPanel.decompiledTextArea.gotoLine(line); abcPanel.decompiledTextArea.gotoLine(line);
} }
scrollPosStorage.saveScrollPos(oldItem); scrollPosStorage.saveScrollPos(oldItem);
refreshBreakPoints(); refreshBreakPoints();
} }
}); });
} }
}); });
} else if (actionPanel != null && !swf.isAS3()) { } else if (actionPanel != null && !swf.isAS3()) {
actionPanel.addScriptListener(new Runnable() { actionPanel.addScriptListener(new Runnable() {
@Override @Override
@@ -2571,11 +2576,11 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se
actionPanel.decompiledEditor.gotoLine(line); actionPanel.decompiledEditor.gotoLine(line);
} }
scrollPosStorage.saveScrollPos(oldItem); scrollPosStorage.saveScrollPos(oldItem);
refreshBreakPoints(); refreshBreakPoints();
} }
}); });
} }
}); });
} }
gotoScriptName(swf, scriptName); gotoScriptName(swf, scriptName);
} }
@@ -2627,12 +2632,12 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se
String rawScriptName = scriptName; String rawScriptName = scriptName;
if (rawScriptName.startsWith("#PCODE ")) { if (rawScriptName.startsWith("#PCODE ")) {
rawScriptName = rawScriptName.substring("#PCODE ".length()); rawScriptName = rawScriptName.substring("#PCODE ".length());
} }
Map<String, ASMSource> asms = swf.getASMs(true); Map<String, ASMSource> asms = swf.getASMs(true);
if (asms.containsKey(rawScriptName)) { if (asms.containsKey(rawScriptName)) {
oldItem = null; oldItem = null;
getCurrentTree().setSelectionPath(null); getCurrentTree().setSelectionPath(null);
setTagTreeSelectedNode(getCurrentTree(), asms.get(rawScriptName)); setTagTreeSelectedNode(getCurrentTree(), asms.get(rawScriptName));
} }
/*if (actionPanel != null && asms.containsKey(rawScriptName)) { /*if (actionPanel != null && asms.containsKey(rawScriptName)) {
actionPanel.setSource(asms.get(rawScriptName), true); actionPanel.setSource(asms.get(rawScriptName), true);
@@ -3090,7 +3095,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se
Helper.decompilationErrorAdd = AppStrings.translate(Configuration.autoDeobfuscate.get() ? "deobfuscation.comment.failed" : "deobfuscation.comment.tryenable"); Helper.decompilationErrorAdd = AppStrings.translate(Configuration.autoDeobfuscate.get() ? "deobfuscation.comment.failed" : "deobfuscation.comment.tryenable");
clearAllScriptCache(); clearAllScriptCache();
updateClassesList(); updateClassesList();
reload(true); reload(true);
} }
public void renameColliding(final Openable openable) { public void renameColliding(final Openable openable) {
@@ -3170,7 +3175,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se
@Override @Override
protected void done() { protected void done() {
} }
}.execute(); }.execute();
@@ -4275,7 +4280,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se
reload(true); reload(true);
pinsPanel.refresh(); pinsPanel.refresh();
} }
public void refreshTree() { public void refreshTree() {
refreshTree(new SWF[0]); refreshTree(new SWF[0]);
} }
@@ -4425,7 +4430,99 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se
replace(items, false); replace(items, false);
} }
public boolean replace(List<TreeItem> items, boolean create) { public boolean replaceMorphShape(MorphShapeTag morphShape, boolean create) {
File fileStart = showImportFileChooser("filter.images|*.jpg;*.jpeg;*.gif;*.png;*.bmp;*.svg", true, AppStrings.translate("dialog.morphshape.startShape"));
if (fileStart == null) {
return false;
}
File fileEnd = showImportFileChooser("filter.images|*.jpg;*.jpeg;*.gif;*.png;*.bmp;*.svg", true, AppStrings.translate("dialog.morphshape.endShape"));
if (fileEnd == null) {
fileEnd = fileStart;
}
DefineShape4Tag shapeStart = new DefineShape4Tag(morphShape.getSwf());
SWF.addTagBefore(shapeStart, morphShape);
DefineShape4Tag shapeEnd = new DefineShape4Tag(morphShape.getSwf());
SWF.addTagBefore(shapeEnd, morphShape);
File selfileStart = Helper.fixDialogFile(fileStart);
byte[] dataStart = null;
String svgTextStart = null;
boolean svgWarningShown = false;
if (".svg".equals(Path.getExtension(selfileStart))) {
svgTextStart = Helper.readTextFile(selfileStart.getAbsolutePath());
showSvgImportWarning();
svgWarningShown = true;
} else {
dataStart = Helper.readFile(selfileStart.getAbsolutePath());
}
File selfileEnd = Helper.fixDialogFile(fileEnd);
byte[] dataEnd = null;
String svgTextEnd = null;
if (".svg".equals(Path.getExtension(selfileEnd))) {
svgTextEnd = Helper.readTextFile(selfileEnd.getAbsolutePath());
if (!svgWarningShown) {
showSvgImportWarning();
}
} else {
dataEnd = Helper.readFile(selfileEnd.getAbsolutePath());
}
try {
Tag newStartTag;
if (svgTextStart != null) {
newStartTag = new SvgImporter().importSvg(shapeStart, svgTextStart, false);
} else {
newStartTag = new ShapeImporter().importImage(shapeStart, dataStart, 0, false);
}
newStartTag.getTimelined().removeTag(newStartTag);
Tag newEndTag;
if (svgTextStart != null) {
newEndTag = new SvgImporter().importSvg(shapeEnd, svgTextEnd, false);
} else {
newEndTag = new ShapeImporter().importImage(shapeEnd, dataEnd, 0, false);
}
newEndTag.getTimelined().removeTag(newEndTag);
if (morphShape instanceof DefineMorphShapeTag) {
DefineMorphShape2Tag morphShape2 = new DefineMorphShape2Tag(morphShape.getSwf());
morphShape.getTimelined().replaceTag(morphShape, morphShape2);
morphShape2.setTimelined(morphShape.getTimelined());
morphShape = morphShape2;
}
MorphShapeGenerator gen = new MorphShapeGenerator();
try {
gen.generate((DefineMorphShape2Tag) morphShape, shapeStart, shapeEnd);
} catch (StyleMismatchException sme) {
ViewMessages.showMessageDialog(this, AppStrings.translate("error.morphshape.incompatible"), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE);
return false;
}
SWF swf = morphShape.getSwf();
swf.resetTimeline();
refreshTree(swf);
/*if (newTag != null) {
setTagTreeSelectedNode(getCurrentTree(), newTag);
}*/
swf.clearImageCache();
swf.clearShapeCache();
} catch (IOException ex) {
logger.log(Level.SEVERE, "Invalid image", ex);
ViewMessages.showMessageDialog(MainPanel.this, translate("error.image.invalid"), translate("error"), JOptionPane.ERROR_MESSAGE);
}
reload(true);
return true;
}
public boolean replace(List<TreeItem> items, boolean create) {
if (items.isEmpty()) { if (items.isEmpty()) {
return false; return false;
} }
@@ -4442,10 +4539,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se
file = showImportFileChooser("filter.images|*.jpg;*.jpeg;*.gif;*.png;*.bmp;*.svg", true); file = showImportFileChooser("filter.images|*.jpg;*.jpeg;*.gif;*.png;*.bmp;*.svg", true);
} }
if (ti0 instanceof MorphShapeTag) { if (ti0 instanceof MorphShapeTag) {
if (ViewMessages.showConfirmDialog(this, AppStrings.translate("warning.replace.morphshape"), AppStrings.translate("message.warning"), JOptionPane.WARNING_MESSAGE, JOptionPane.WARNING_MESSAGE, Configuration.warningReplaceMorphShape, JOptionPane.OK_OPTION) != JOptionPane.OK_OPTION) { return replaceMorphShape((MorphShapeTag) ti0, create);
return false;
}
file = showImportFileChooser("filter.images|*.jpg;*.jpeg;*.gif;*.png;*.bmp;*.svg", true);
} }
if (ti0 instanceof DefineVideoStreamTag) { if (ti0 instanceof DefineVideoStreamTag) {
file = showImportFileChooser("filter.movies|*.flv", false); file = showImportFileChooser("filter.movies|*.flv", false);
@@ -4543,14 +4637,14 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se
if (svgText != null) { if (svgText != null) {
newTag = new SvgImporter().importSvg((ShapeTag) st, svgText); newTag = new SvgImporter().importSvg((ShapeTag) st, svgText);
} else { } else {
newTag = new ShapeImporter().importImage((ShapeTag) st, data); newTag = new ShapeImporter().importImage((ShapeTag) st, data);
} }
} }
if (st instanceof MorphShapeTag) { if (st instanceof MorphShapeTag) {
if (svgText != null) { if (svgText != null) {
newTag = new SvgImporter().importSvg((MorphShapeTag) st, svgText); newTag = new SvgImporter().importSvg((MorphShapeTag) st, svgText);
} else { } else {
newTag = new ShapeImporter().importImage((MorphShapeTag) st, data); newTag = new ShapeImporter().importImage((MorphShapeTag) st, data);
} }
} }
SWF swf = st.getSwf(); SWF swf = st.getSwf();
@@ -4628,7 +4722,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se
} }
return false; return false;
} }
public void replaceSpriteWithGifButtonActionPerformed(TreeItem item) { public void replaceSpriteWithGifButtonActionPerformed(TreeItem item) {
replaceSpriteWithGif(item); replaceSpriteWithGif(item);
} }
@@ -4660,14 +4754,14 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se
Tag newTag = null; Tag newTag = null;
if (st instanceof ShapeTag) { if (st instanceof ShapeTag) {
if (svgText != null) { if (svgText != null) {
newTag = new SvgImporter().importSvg((ShapeTag) st, svgText, false); newTag = new SvgImporter().importSvg((ShapeTag) st, svgText, false);
} else { } else {
newTag = new ShapeImporter().importImage((ShapeTag) st, data, 0, false); newTag = new ShapeImporter().importImage((ShapeTag) st, data, 0, false);
} }
} }
if (st instanceof MorphShapeTag) { if (st instanceof MorphShapeTag) {
if (svgText != null) { if (svgText != null) {
newTag = new SvgImporter().importSvg((MorphShapeTag) st, svgText, false); newTag = new SvgImporter().importSvg((MorphShapeTag) st, svgText, false);
} else { } else {
newTag = new ShapeImporter().importImage((MorphShapeTag) st, data, 0, false); newTag = new ShapeImporter().importImage((MorphShapeTag) st, data, 0, false);
} }
@@ -4757,6 +4851,10 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se
} }
public File showImportFileChooser(String filter, boolean imagePreview) { public File showImportFileChooser(String filter, boolean imagePreview) {
return showImportFileChooser(filter, imagePreview, null);
}
public File showImportFileChooser(String filter, boolean imagePreview, String title) {
String[] filterArray = filter.length() > 0 ? filter.split("\\|") : new String[0]; String[] filterArray = filter.length() > 0 ? filter.split("\\|") : new String[0];
JFileChooser fc = new JFileChooser(); JFileChooser fc = new JFileChooser();
@@ -4816,6 +4914,10 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se
first = false; first = false;
} }
if (title != null) {
fc.setDialogTitle(title);
}
if (fc.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) { if (fc.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
File result = fc.getSelectedFile(); File result = fc.getSelectedFile();
Configuration.lastOpenDir.set(Helper.fixDialogFile(result).getParentFile().getAbsolutePath()); Configuration.lastOpenDir.set(Helper.fixDialogFile(result).getParentFile().getAbsolutePath());
@@ -4823,7 +4925,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se
} }
return null; return null;
} }
private void showDetail(int card) { private void showDetail(int card) {
if (debugStackPanel.isActive() && card != DETAILCARDDEBUGSTACKFRAME) { if (debugStackPanel.isActive() && card != DETAILCARDDEBUGSTACKFRAME) {
@@ -4832,9 +4934,9 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se
detailPanel.removeAll(); detailPanel.removeAll();
int pos = 0; int pos = 0;
if (card == DETAILCARDTAGINFO) { if (card == DETAILCARDTAGINFO) {
detailPanel.addTab(AppStrings.translate("taginfo.header"), tagInfoPanel); detailPanel.addTab(AppStrings.translate("taginfo.header"), tagInfoPanel);
if (card == DETAILCARDTAGINFO) { if (card == DETAILCARDTAGINFO) {
detailPanel.setSelectedIndex(pos); detailPanel.setSelectedIndex(pos);
} }
@@ -4845,15 +4947,15 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se
detailPanel.addTab(AppStrings.translate("traits"), abcPanel.navigatorPanel); detailPanel.addTab(AppStrings.translate("traits"), abcPanel.navigatorPanel);
if (card == DETAILCARDAS3NAVIGATOR) { if (card == DETAILCARDAS3NAVIGATOR) {
detailPanel.setSelectedIndex(pos); detailPanel.setSelectedIndex(pos);
} }
pos++; pos++;
} }
} }
if (debugStackPanel.isActive()) { if (debugStackPanel.isActive()) {
detailPanel.addTab(AppStrings.translate("callStack.header"), debugStackPanel); detailPanel.addTab(AppStrings.translate("callStack.header"), debugStackPanel);
if (card == DETAILCARDDEBUGSTACKFRAME) { if (card == DETAILCARDDEBUGSTACKFRAME) {
detailPanel.setSelectedIndex(pos); detailPanel.setSelectedIndex(pos);
} }
pos++; pos++;
} }
if (currentView != VIEW_DUMP) { if (currentView != VIEW_DUMP) {
@@ -5121,10 +5223,10 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se
showTreePanelCard(DUMP_VIEW); showTreePanelCard(DUMP_VIEW);
treePanelMode = TreePanelMode.DUMP_TREE; treePanelMode = TreePanelMode.DUMP_TREE;
//showDetail(DETAILCARDEMPTYPANEL); //showDetail(DETAILCARDEMPTYPANEL);
showDetail(DETAILCARDEMPTYPANEL); showDetail(DETAILCARDEMPTYPANEL);
reload(true); reload(true);
updateUiWithCurrentOpenable(); updateUiWithCurrentOpenable();
detailPanel.setVisible(false); detailPanel.setVisible(false);
return true; return true;
case VIEW_RESOURCES: case VIEW_RESOURCES:
pinsPanel.setVisible(true); pinsPanel.setVisible(true);
@@ -5181,7 +5283,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se
} }
showTreePanelCard(TAGLIST_VIEW); showTreePanelCard(TAGLIST_VIEW);
treePanelMode = TreePanelMode.TAGLIST_TREE; treePanelMode = TreePanelMode.TAGLIST_TREE;
detailPanel.setVisible(true); detailPanel.setVisible(true);
refreshPins(); refreshPins();
reload(true); reload(true);
updateUiWithCurrentOpenable(); updateUiWithCurrentOpenable();
@@ -5971,7 +6073,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se
return new Timelined() { return new Timelined() {
private Timeline tim; private Timeline tim;
@Override @Override
public Timeline getTimeline() { public Timeline getTimeline() {
if (tim == null) { if (tim == null) {
@@ -5991,6 +6093,8 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se
} }
} }
private void initTimeline(Timeline timeline) { private void initTimeline(Timeline timeline) {
if (tag instanceof MorphShapeTag) { if (tag instanceof MorphShapeTag) {
timeline.frameRate = PreviewExporter.MORPH_SHAPE_ANIMATION_FRAME_RATE; timeline.frameRate = PreviewExporter.MORPH_SHAPE_ANIMATION_FRAME_RATE;
@@ -6005,6 +6109,14 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se
f.layersChanged = true; f.layersChanged = true;
timeline.addFrame(f); timeline.addFrame(f);
} }
Frame f = new Frame(timeline, framesCnt);
DepthState ds = new DepthState(tag.getSwf(), f);
ds.characterId = ((CharacterTag) tag).getCharacterId();
ds.matrix = new MATRIX();
ds.ratio = 65535;
f.layers.put(1, ds);
f.layersChanged = true;
timeline.addFrame(f);
} else if (tag instanceof FontTag) { } else if (tag instanceof FontTag) {
int pageCount = PreviewPanel.getFontPageCount((FontTag) tag); int pageCount = PreviewPanel.getFontPageCount((FontTag) tag);
int frame = fontFrameNum; int frame = fontFrameNum;
@@ -6129,7 +6241,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se
setDropTarget(null); setDropTarget(null);
disposeInner(this); disposeInner(this);
Helper.emptyObject(this); Helper.emptyObject(this);
} }
public void updateMissingNeededCharacters() { public void updateMissingNeededCharacters() {
if (calculateMissingNeededThread != null) { if (calculateMissingNeededThread != null) {
@@ -6179,7 +6291,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se
}*/ }*/
} }
} }
private void calculateMissingNeededCharacters() { private void calculateMissingNeededCharacters() {
Map<TreeItem, Set<Integer>> missingNeededCharacters = new WeakHashMap<>(); Map<TreeItem, Set<Integer>> missingNeededCharacters = new WeakHashMap<>();
Map<TreeItem, Set<Integer>> neededCharacters = new WeakHashMap<>(); Map<TreeItem, Set<Integer>> neededCharacters = new WeakHashMap<>();
@@ -6304,7 +6416,7 @@ public final class MainPanel extends JPanel implements TreeSelectionListener, Se
} }
return dialog; return dialog;
} }
public boolean fontEmbed(TreeItem item, boolean create) { public boolean fontEmbed(TreeItem item, boolean create) {
return previewPanel.getFontPanel().fontEmbed(item, create); return previewPanel.getFontPanel().fontEmbed(item, create);
} }
@@ -190,6 +190,8 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel
// Image tag buttons // Image tag buttons
private JButton replaceShapeButton; private JButton replaceShapeButton;
private JButton replaceMorphShapeButton;
private JButton replaceShapeUpdateBoundsButton; private JButton replaceShapeUpdateBoundsButton;
@@ -1302,6 +1304,16 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel
} }
}); });
replaceShapeButton.setVisible(false); replaceShapeButton.setVisible(false);
replaceMorphShapeButton = new JButton(mainPanel.translate("button.replace"), View.getIcon("importmorphshape16"));
replaceMorphShapeButton.setMargin(new Insets(3, 3, 3, 10));
replaceMorphShapeButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
mainPanel.replaceMorphShape((MorphShapeTag) mainPanel.getCurrentTree().getCurrentTreeItem(), false);
}
});
replaceMorphShapeButton.setVisible(false);
replaceShapeUpdateBoundsButton = new JButton(mainPanel.translate("button.replaceNoFill"), View.getIcon("importshape16")); replaceShapeUpdateBoundsButton = new JButton(mainPanel.translate("button.replaceNoFill"), View.getIcon("importshape16"));
replaceShapeUpdateBoundsButton.setMargin(new Insets(3, 3, 3, 10)); replaceShapeUpdateBoundsButton.setMargin(new Insets(3, 3, 3, 10));
@@ -1363,6 +1375,7 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel
displayEditButtonsPanel.add(displayEditEditPointsButton); displayEditButtonsPanel.add(displayEditEditPointsButton);
//displayEditButtonsPanel.add(fixPathsButton); //displayEditButtonsPanel.add(fixPathsButton);
displayEditButtonsPanel.add(replaceShapeButton); displayEditButtonsPanel.add(replaceShapeButton);
displayEditButtonsPanel.add(replaceMorphShapeButton);
displayEditButtonsPanel.add(replaceShapeUpdateBoundsButton); displayEditButtonsPanel.add(replaceShapeUpdateBoundsButton);
displayEditButtonsPanel.add(morphShowPanel); displayEditButtonsPanel.add(morphShowPanel);
return displayEditButtonsPanel; return displayEditButtonsPanel;
@@ -1723,10 +1736,11 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel
replaceImageButton.setVisible(showImage); replaceImageButton.setVisible(showImage);
replaceImageAlphaButton.setVisible(showAlpha); replaceImageAlphaButton.setVisible(showAlpha);
replaceSpriteButton.setVisible(showSprite); replaceSpriteButton.setVisible(showSprite);
replaceShapeButton.setVisible(showShape || showMorphShape); replaceShapeButton.setVisible(showShape);
replaceMorphShapeButton.setVisible(showMorphShape);
morphShowPanel.setVisible(showMorphShape); morphShowPanel.setVisible(showMorphShape);
displayEditEditPointsButton.setVisible(showShape || showMorphShape); displayEditEditPointsButton.setVisible(showShape || showMorphShape);
replaceShapeUpdateBoundsButton.setVisible(showShape || showMorphShape); replaceShapeUpdateBoundsButton.setVisible(showShape);
replaceSoundButton.setVisible(showSound); replaceSoundButton.setVisible(showSound);
replaceMovieButton.setVisible(showMovie); replaceMovieButton.setVisible(showMovie);
prevFontsButton.setVisible(false); prevFontsButton.setVisible(false);
@@ -2316,6 +2330,11 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel
replaceShapeUpdateBoundsButton.setVisible(true); replaceShapeUpdateBoundsButton.setVisible(true);
displayEditEditPointsButton.setVisible(true); displayEditEditPointsButton.setVisible(true);
} }
if (displayEditTag instanceof MorphShapeTag) {
replaceMorphShapeButton.setVisible(true);
displayEditEditPointsButton.setVisible(true);
}
if (displayEditTag instanceof DefineSpriteTag) { if (displayEditTag instanceof DefineSpriteTag) {
replaceSpriteButton.setVisible(true); replaceSpriteButton.setVisible(true);
@@ -2365,6 +2384,7 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel
displayEditSaveButton.setVisible(true); displayEditSaveButton.setVisible(true);
displayEditCancelButton.setVisible(true); displayEditCancelButton.setVisible(true);
replaceShapeButton.setVisible(false); replaceShapeButton.setVisible(false);
replaceMorphShapeButton.setVisible(false);
replaceShapeUpdateBoundsButton.setVisible(false); replaceShapeUpdateBoundsButton.setVisible(false);
displayEditEditPointsButton.setVisible(false); displayEditEditPointsButton.setVisible(false);
mainPanel.setEditingStatus(); mainPanel.setEditingStatus();
@@ -2396,6 +2416,7 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel
displayEditSaveButton.setVisible(true); displayEditSaveButton.setVisible(true);
displayEditCancelButton.setVisible(true); displayEditCancelButton.setVisible(true);
replaceShapeButton.setVisible(false); replaceShapeButton.setVisible(false);
replaceMorphShapeButton.setVisible(false);
replaceShapeUpdateBoundsButton.setVisible(false); replaceShapeUpdateBoundsButton.setVisible(false);
displayEditEditPointsButton.setVisible(false); displayEditEditPointsButton.setVisible(false);
@@ -2496,6 +2517,7 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel
displayEditCancelButton.setVisible(true); displayEditCancelButton.setVisible(true);
replaceShapeButton.setVisible(false); replaceShapeButton.setVisible(false);
replaceMorphShapeButton.setVisible(false);
replaceShapeUpdateBoundsButton.setVisible(false); replaceShapeUpdateBoundsButton.setVisible(false);
displayEditEditPointsButton.setVisible(false); displayEditEditPointsButton.setVisible(false);
@@ -2792,6 +2814,7 @@ public class PreviewPanel extends JPersistentSplitPane implements TagEditorPanel
if (displayEditTag instanceof MorphShapeTag) { if (displayEditTag instanceof MorphShapeTag) {
morphShowPanel.setVisible(true); morphShowPanel.setVisible(true);
replaceMorphShapeButton.setVisible(true);
displayEditEditPointsButton.setVisible(true); displayEditEditPointsButton.setVisible(true);
} }
@@ -740,6 +740,3 @@ config.description.displayAs12PCodePanel = Show panel with disassembled P-code a
config.name.displayAs3PCodePanel = Show AS3 P-code panel config.name.displayAs3PCodePanel = Show AS3 P-code panel
config.description.displayAs3PCodePanel = Show panel with disassembled P-code instructions for ActionScript 3 config.description.displayAs3PCodePanel = Show panel with disassembled P-code instructions for ActionScript 3
config.name.warning.replace.morphshape = Warn on replace morphshape
config.description.warning.replace.morphshape = Show warning on replacing morphshape about same start and end shape
@@ -730,6 +730,3 @@ config.description.displayAs12PCodePanel = Zobrazit panel s akcemi disassemblova
config.name.displayAs3PCodePanel = Zobrazit AS3 panel s P-k\u00f3dem config.name.displayAs3PCodePanel = Zobrazit AS3 panel s P-k\u00f3dem
config.description.displayAs3PCodePanel = Zobrazit panel s instrukcemi disassemblovan\u00e9ho P-k\u00f3du pro ActionScript 3 config.description.displayAs3PCodePanel = Zobrazit panel s instrukcemi disassemblovan\u00e9ho P-k\u00f3du pro ActionScript 3
config.name.warning.replace.morphshape = Varovat p\u0159i nahrazov\u00e1n\u00ed morphshape
config.description.warning.replace.morphshape = Zobrazovat varov\u00e1n\u00ed p\u0159i nahrazov\u00e1n\u00ed morphshape o tom, \u017ee bude startov\u00e1n\u00ed a koncov\u00fd tvar stejn\u00fd
@@ -1204,8 +1204,9 @@ tag.movie.create = Create movie from file...
tag.sound.create = Create sound from file... tag.sound.create = Create sound from file...
tag.font.create = Create font using dialog... tag.font.create = Create font using dialog...
tag.binaryData.create = Create binary data from file... tag.binaryData.create = Create binary data from file...
tag.morphshape.create = Create morphshape from shape file... tag.morphshape.create = Create morphshape from shape files...
warning.replace.morphshape = In the following dialog you select a shape file.\r\n \ dialog.morphshape.startShape = Select start shape.
That shape would be set as start and also the end shape of the morphshape.\r\n\ dialog.morphshape.endShape = Select end shape. Click cancel to make end shape same as the start shape.
You won't see any morphing animation until you edit start/end shape to be different.
error.morphshape.incompatible = Cannot create morphshape: Start and end shape have incompatible fill/line styles.
@@ -1181,8 +1181,9 @@ tag.movie.create = Vytvo\u0159it video ze souboru...
tag.sound.create = Vytvo\u0159it zvuk ze souboru... tag.sound.create = Vytvo\u0159it zvuk ze souboru...
tag.font.create = Vytvo\u0159it p\u00edsmo pomoc\u00ed dialogu... tag.font.create = Vytvo\u0159it p\u00edsmo pomoc\u00ed dialogu...
tag.binaryData.create = Vytvo\u0159it bin\u00e1rn\u00ed data ze souboru... tag.binaryData.create = Vytvo\u0159it bin\u00e1rn\u00ed data ze souboru...
tag.morphshape.create = Vytvo\u0159it morphshape ze souboru tvaru... tag.morphshape.create = Vytvo\u0159it morphshape ze soubor\u016f tvar\u016f...
warning.replace.morphshape = V n\u00e1sleduj\u00edc\u00edm dialogu vyberete soubor s tvarem.\r\n \ dialog.morphshape.startShape = Vyberte po\u010d\u00e1te\u010dn\u00ed tvar.
Ten tvar bude nastaven jako po\u010d\u00e1te\u010dn\u00ed a tak\u00e9 koncov\u00fd tvar tohot morphshape.\r\n\ dialog.morphshape.endShape = Vyberte koncov\u00fd tvar. Klikn\u011bte na Storno pro nastaven\u00ed stejn\u00e9ho jako je po\u010d\u00e1te\u010dn\u00ed.
Neuvid\u00edte \u017e\u00e1dnou morfuj\u00edc\u00ed animaci do doby ne\u017e uprav\u00edte po\u010d\u00e1te\u010dn\u00ed/koncov\u00fd tvar tak, aby byly odli\u0161n\u00e9.
error.morphshape.incompatible = Nelze vytvo\u0159it morphshape: Po\u010d\u00e1te\u010dn\u00ed a koncov\u00fd tvar maj\u00ed nekompatibiln\u00ed styl v\u00fdpln\u011b/ohrani\u010den\u00ed.
@@ -1039,11 +1039,15 @@ public class TagTreeContextMenu extends JPopupMenu {
replaceMenuItem.setVisible(true); replaceMenuItem.setVisible(true);
} }
if (canReplace.test(it -> (it instanceof ShapeTag) || (it instanceof MorphShapeTag))) { if (canReplace.test(it -> it instanceof ShapeTag)) {
replaceMenuItem.setVisible(true); replaceMenuItem.setVisible(true);
replaceNoFillMenuItem.setVisible(true); replaceNoFillMenuItem.setVisible(true);
} }
if (canReplace.test(it -> it instanceof MorphShapeTag)) {
replaceMenuItem.setVisible(true);
}
if (canReplace.test(it -> it instanceof DefineBinaryDataTag)) { if (canReplace.test(it -> it instanceof DefineBinaryDataTag)) {
replaceMenuItem.setVisible(true); replaceMenuItem.setVisible(true);
} }
@@ -1660,11 +1664,11 @@ public class TagTreeContextMenu extends JPopupMenu {
case TagTreeModel.FOLDER_MORPHSHAPES: case TagTreeModel.FOLDER_MORPHSHAPES:
addTagMenu.addSeparator(); addTagMenu.addSeparator();
JMenuItem createMorphShapeItem = new JMenuItem(AppStrings.translate("tag.morphshape.create")); JMenuItem createMorphShapeItem = new JMenuItem(AppStrings.translate("tag.morphshape.create"));
createMorphShapeItem.setIcon(View.getIcon("importshape16")); createMorphShapeItem.setIcon(View.getIcon("importmorphshape16"));
createMorphShapeItem.addActionListener((ActionEvent ae) -> { createMorphShapeItem.addActionListener((ActionEvent ae) -> {
listener.call(ae, item, DefineMorphShape2Tag.class, TreeNodeType.MORPH_SHAPE); listener.call(ae, item, DefineMorphShape2Tag.class, TreeNodeType.MORPH_SHAPE);
}); });
addTagMenu.add(createMorphShapeItem); addTagMenu.add(createMorphShapeItem);
break; break;
} }
} }
@@ -3822,10 +3826,12 @@ public class TagTreeContextMenu extends JPopupMenu {
case SPRITE: case SPRITE:
remove = !mainPanel.replaceSpriteWithGif(tag); remove = !mainPanel.replaceSpriteWithGif(tag);
break; break;
case SHAPE: case SHAPE:
case MORPH_SHAPE:
remove = !mainPanel.replaceNoFill(tag); remove = !mainPanel.replaceNoFill(tag);
break; break;
case MORPH_SHAPE:
remove = !mainPanel.replaceMorphShape((MorphShapeTag) tag, true);
break;
case FONT: case FONT:
remove = !mainPanel.fontEmbed(tag, true); remove = !mainPanel.fontEmbed(tag, true);
break; break;