mirror of
https://git.huckle.dev/Huckles-Minecraft-Archive/jpexs-decompiler.git
synced 2026-06-04 15:14:49 +00:00
Merge origin/master
This commit is contained in:
@@ -432,4 +432,10 @@ public class DefineButtonTag extends ButtonTag implements ASMSource {
|
||||
public Tag getSourceTag() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSourceTag(Tag t) {
|
||||
//nothing
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -211,4 +211,9 @@ public class DoActionTag extends Tag implements ASMSource {
|
||||
public Tag getSourceTag() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSourceTag(Tag t) {
|
||||
//nothing
|
||||
}
|
||||
}
|
||||
|
||||
@@ -249,4 +249,9 @@ public class DoInitActionTag extends Tag implements CharacterIdTag, ASMSource {
|
||||
public Tag getSourceTag() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSourceTag(Tag t) {
|
||||
//nothing
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,7 +137,6 @@ public class PlaceObject2Tag extends PlaceObjectTypeTag implements ASMSourceCont
|
||||
* @since SWF 5 If PlaceFlagHasClipActions, Clip Actions Data
|
||||
*/
|
||||
@Conditional("placeFlagHasClipActions")
|
||||
@HideInRawEdit //TODO: make editable
|
||||
public CLIPACTIONS clipActions;
|
||||
|
||||
/**
|
||||
|
||||
@@ -33,6 +33,7 @@ import com.jpexs.decompiler.flash.types.annotations.Conditional;
|
||||
import com.jpexs.decompiler.flash.types.annotations.HideInRawEdit;
|
||||
import com.jpexs.decompiler.flash.types.annotations.Internal;
|
||||
import com.jpexs.decompiler.flash.types.annotations.Reserved;
|
||||
import com.jpexs.decompiler.flash.types.annotations.SWFArray;
|
||||
import com.jpexs.decompiler.flash.types.annotations.SWFType;
|
||||
import com.jpexs.decompiler.flash.types.filters.FILTER;
|
||||
import com.jpexs.helpers.ByteArrayRange;
|
||||
@@ -185,6 +186,7 @@ public class PlaceObject3Tag extends PlaceObjectTypeTag implements ASMSourceCont
|
||||
* If PlaceFlagHasFilterList, List of filters on this object
|
||||
*/
|
||||
@Conditional("placeFlagHasFilterList")
|
||||
@SWFArray("filter")
|
||||
public List<FILTER> surfaceFilterList;
|
||||
|
||||
/**
|
||||
@@ -206,7 +208,6 @@ public class PlaceObject3Tag extends PlaceObjectTypeTag implements ASMSourceCont
|
||||
* @since SWF 5 If PlaceFlagHasClipActions, Clip Actions Data
|
||||
*/
|
||||
@Conditional(value = "placeFlagHasClipActions", minSwfVersion = 5)
|
||||
@HideInRawEdit //TODO: make editable
|
||||
public CLIPACTIONS clipActions;
|
||||
|
||||
/**
|
||||
|
||||
@@ -33,6 +33,7 @@ import com.jpexs.decompiler.flash.types.annotations.Conditional;
|
||||
import com.jpexs.decompiler.flash.types.annotations.HideInRawEdit;
|
||||
import com.jpexs.decompiler.flash.types.annotations.Internal;
|
||||
import com.jpexs.decompiler.flash.types.annotations.Reserved;
|
||||
import com.jpexs.decompiler.flash.types.annotations.SWFArray;
|
||||
import com.jpexs.decompiler.flash.types.annotations.SWFType;
|
||||
import com.jpexs.decompiler.flash.types.filters.FILTER;
|
||||
import com.jpexs.helpers.ByteArrayRange;
|
||||
@@ -185,6 +186,7 @@ public class PlaceObject4Tag extends PlaceObjectTypeTag implements ASMSourceCont
|
||||
* If PlaceFlagHasFilterList, List of filters on this object
|
||||
*/
|
||||
@Conditional("placeFlagHasFilterList")
|
||||
@SWFArray("filter")
|
||||
public List<FILTER> surfaceFilterList;
|
||||
|
||||
/**
|
||||
@@ -206,7 +208,6 @@ public class PlaceObject4Tag extends PlaceObjectTypeTag implements ASMSourceCont
|
||||
* @since SWF 5 If PlaceFlagHasClipActions, Clip Actions Data
|
||||
*/
|
||||
@Conditional(value = "placeFlagHasClipActions", minSwfVersion = 5)
|
||||
@HideInRawEdit //TODO: make editable
|
||||
public CLIPACTIONS clipActions;
|
||||
|
||||
/**
|
||||
|
||||
@@ -12,10 +12,12 @@
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library.
|
||||
* License along with this library.
|
||||
*/
|
||||
package com.jpexs.decompiler.flash.tags.base;
|
||||
|
||||
import com.jpexs.decompiler.flash.DisassemblyListener;
|
||||
import com.jpexs.decompiler.flash.SWF;
|
||||
import com.jpexs.decompiler.flash.action.Action;
|
||||
import com.jpexs.decompiler.flash.action.ActionList;
|
||||
import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode;
|
||||
@@ -84,4 +86,7 @@ public interface ASMSource extends Exportable {
|
||||
public String removePrefixAndSuffix(String source);
|
||||
|
||||
public Tag getSourceTag();
|
||||
|
||||
public void setSourceTag(Tag t);
|
||||
|
||||
}
|
||||
|
||||
@@ -45,19 +45,26 @@ import java.util.logging.Logger;
|
||||
*/
|
||||
public class BUTTONCONDACTION implements ASMSource, Serializable {
|
||||
|
||||
private final SWF swf;
|
||||
private SWF swf;
|
||||
|
||||
private final Tag tag;
|
||||
private Tag tag;
|
||||
|
||||
@Override
|
||||
public SWF getSwf() {
|
||||
return swf;
|
||||
}
|
||||
|
||||
// Constructor for Generic tag editor. TODO:Handle this somehow better
|
||||
// Constructor for Generic tag editor.
|
||||
public BUTTONCONDACTION() {
|
||||
swf = null;
|
||||
tag = null;
|
||||
actionBytes = new ByteArrayRange(new byte[0]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSourceTag(Tag t) {
|
||||
this.tag = t;
|
||||
this.swf = t.getSwf();
|
||||
}
|
||||
|
||||
public BUTTONCONDACTION(SWF swf, SWFInputStream sis, Tag tag) throws IOException {
|
||||
|
||||
@@ -19,8 +19,17 @@ package com.jpexs.decompiler.flash.types;
|
||||
import com.jpexs.decompiler.flash.tags.DefineButton2Tag;
|
||||
import com.jpexs.decompiler.flash.types.annotations.Conditional;
|
||||
import com.jpexs.decompiler.flash.types.annotations.Reserved;
|
||||
import com.jpexs.decompiler.flash.types.annotations.SWFArray;
|
||||
import com.jpexs.decompiler.flash.types.annotations.SWFType;
|
||||
import com.jpexs.decompiler.flash.types.filters.BEVELFILTER;
|
||||
import com.jpexs.decompiler.flash.types.filters.BLURFILTER;
|
||||
import com.jpexs.decompiler.flash.types.filters.COLORMATRIXFILTER;
|
||||
import com.jpexs.decompiler.flash.types.filters.CONVOLUTIONFILTER;
|
||||
import com.jpexs.decompiler.flash.types.filters.DROPSHADOWFILTER;
|
||||
import com.jpexs.decompiler.flash.types.filters.FILTER;
|
||||
import com.jpexs.decompiler.flash.types.filters.GLOWFILTER;
|
||||
import com.jpexs.decompiler.flash.types.filters.GRADIENTBEVELFILTER;
|
||||
import com.jpexs.decompiler.flash.types.filters.GRADIENTGLOWFILTER;
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
@@ -91,6 +100,7 @@ public class BUTTONRECORD implements Serializable {
|
||||
* If within DefineButton2Tag and buttonHasFilterList: List of filters on
|
||||
* this button
|
||||
*/
|
||||
@SWFArray("filter")
|
||||
public List<FILTER> filterList;
|
||||
|
||||
/**
|
||||
|
||||
@@ -89,10 +89,10 @@ public class CLIPACTIONRECORD implements ASMSource, Serializable {
|
||||
};
|
||||
|
||||
@Internal
|
||||
private final SWF swf;
|
||||
private SWF swf;
|
||||
|
||||
@Internal
|
||||
private final Tag tag;
|
||||
private Tag tag;
|
||||
|
||||
// Constructor for Generic tag editor. TODO:Handle this somehow better
|
||||
public CLIPACTIONRECORD() {
|
||||
@@ -290,4 +290,10 @@ public class CLIPACTIONRECORD implements ASMSource, Serializable {
|
||||
public Tag getSourceTag() {
|
||||
return tag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSourceTag(Tag t) {
|
||||
this.tag = t;
|
||||
this.swf = t.getSwf();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,8 @@
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library.
|
||||
* License along with this library.
|
||||
*/
|
||||
package com.jpexs.decompiler.flash.types.annotations;
|
||||
|
||||
import com.jpexs.decompiler.flash.types.BasicType;
|
||||
|
||||
@@ -12,11 +12,13 @@
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library.
|
||||
* License along with this library.
|
||||
*/
|
||||
package com.jpexs.decompiler.flash.types.filters;
|
||||
|
||||
import com.jpexs.decompiler.flash.types.BasicType;
|
||||
import com.jpexs.decompiler.flash.types.annotations.SWFType;
|
||||
import com.jpexs.helpers.ConcreteClasses;
|
||||
import com.jpexs.helpers.SerializableImage;
|
||||
import java.io.Serializable;
|
||||
|
||||
@@ -25,6 +27,7 @@ import java.io.Serializable;
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
@ConcreteClasses({BLURFILTER.class, GRADIENTBEVELFILTER.class, GLOWFILTER.class, GRADIENTGLOWFILTER.class, COLORMATRIXFILTER.class, CONVOLUTIONFILTER.class, BEVELFILTER.class, DROPSHADOWFILTER.class})
|
||||
public abstract class FILTER implements Serializable {
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,487 +1,489 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2015 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.types.shaperecords;
|
||||
|
||||
import com.jpexs.decompiler.flash.SWF;
|
||||
import com.jpexs.decompiler.flash.SWFOutputStream;
|
||||
import com.jpexs.decompiler.flash.exporters.commonshape.Matrix;
|
||||
import com.jpexs.decompiler.flash.exporters.shape.BitmapExporter;
|
||||
import com.jpexs.decompiler.flash.helpers.FontHelper;
|
||||
import com.jpexs.decompiler.flash.tags.base.NeedsCharacters;
|
||||
import com.jpexs.decompiler.flash.tags.base.TextTag;
|
||||
import com.jpexs.decompiler.flash.types.ColorTransform;
|
||||
import com.jpexs.decompiler.flash.types.MATRIX;
|
||||
import com.jpexs.decompiler.flash.types.RECT;
|
||||
import com.jpexs.decompiler.flash.types.RGB;
|
||||
import com.jpexs.decompiler.flash.types.RGBA;
|
||||
import com.jpexs.decompiler.flash.types.SHAPE;
|
||||
import com.jpexs.helpers.SerializableImage;
|
||||
import java.awt.Color;
|
||||
import java.awt.Font;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.Shape;
|
||||
import java.awt.font.GlyphVector;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.geom.PathIterator;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public abstract class SHAPERECORD implements Cloneable, NeedsCharacters, Serializable {
|
||||
|
||||
public static final int MAX_CHARACTERS_IN_FONT_PREVIEW = 400;
|
||||
|
||||
public static final boolean DRAW_BOUNDING_BOX = false;
|
||||
|
||||
public abstract void calculateBits();
|
||||
|
||||
@Override
|
||||
public void getNeededCharacters(Set<Integer> needed) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean replaceCharacter(int oldCharacterId, int newCharacterId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeCharacter(int characterId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public abstract int changeX(int x);
|
||||
|
||||
public abstract int changeY(int y);
|
||||
|
||||
public abstract void flip();
|
||||
|
||||
public static RECT getBounds(List<SHAPERECORD> records) {
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
int max_x = 0;
|
||||
int max_y = 0;
|
||||
int min_x = Integer.MAX_VALUE;
|
||||
int min_y = Integer.MAX_VALUE;
|
||||
boolean started = false;
|
||||
for (SHAPERECORD r : records) {
|
||||
if (r instanceof CurvedEdgeRecord) {
|
||||
CurvedEdgeRecord curverEdge = (CurvedEdgeRecord) r;
|
||||
int x2 = x + curverEdge.controlDeltaX;
|
||||
int y2 = y + curverEdge.controlDeltaY;
|
||||
if (x2 > max_x) {
|
||||
max_x = x2;
|
||||
}
|
||||
if (y2 > max_y) {
|
||||
max_y = y2;
|
||||
}
|
||||
if (started) {
|
||||
if (y2 < min_y) {
|
||||
min_y = y2;
|
||||
}
|
||||
if (x2 < min_x) {
|
||||
min_x = x2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
x = r.changeX(x);
|
||||
y = r.changeY(y);
|
||||
if (x > max_x) {
|
||||
max_x = x;
|
||||
}
|
||||
if (y > max_y) {
|
||||
max_y = y;
|
||||
}
|
||||
if (r.isMove()) {
|
||||
started = true;
|
||||
}
|
||||
if (started) {
|
||||
if (y < min_y) {
|
||||
min_y = y;
|
||||
}
|
||||
if (x < min_x) {
|
||||
min_x = x;
|
||||
}
|
||||
}
|
||||
}
|
||||
return new RECT(min_x, max_x, min_y, max_y);
|
||||
}
|
||||
|
||||
public static CurvedEdgeRecord straightToCurve(StraightEdgeRecord ser) {
|
||||
CurvedEdgeRecord ret = new CurvedEdgeRecord();
|
||||
ret.controlDeltaX = ser.deltaX / 2;
|
||||
ret.controlDeltaY = ser.deltaY / 2;
|
||||
ret.anchorDeltaX = ser.deltaX - ret.controlDeltaX;
|
||||
ret.anchorDeltaY = ser.deltaY - ret.controlDeltaY;
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static void shapeListToImage(SWF swf, List<SHAPE> shapes, SerializableImage image, int frame, Color color, ColorTransform colorTransform) {
|
||||
if (shapes.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
int prevWidth = image.getWidth();
|
||||
int prevHeight = image.getHeight();
|
||||
|
||||
int maxw = 0;
|
||||
int maxh = 0;
|
||||
int minXMin = 0;
|
||||
int minYMin = 0;
|
||||
for (SHAPE s : shapes) {
|
||||
RECT r = SHAPERECORD.getBounds(s.shapeRecords);
|
||||
if (r.Xmax < r.Xmin || r.Ymax < r.Ymin) {
|
||||
continue;
|
||||
}
|
||||
if (r.getWidth() > maxw) {
|
||||
maxw = r.getWidth();
|
||||
}
|
||||
if (r.getHeight() > maxh) {
|
||||
maxh = r.getHeight();
|
||||
}
|
||||
if (r.Xmin < minXMin) {
|
||||
minXMin = r.Xmin;
|
||||
}
|
||||
if (r.Ymin < minYMin) {
|
||||
minYMin = r.Ymin;
|
||||
}
|
||||
}
|
||||
|
||||
int shapeCount = Math.min(MAX_CHARACTERS_IN_FONT_PREVIEW, shapes.size());
|
||||
int frameCount = (shapes.size() - 1) / MAX_CHARACTERS_IN_FONT_PREVIEW + 1;
|
||||
if (frameCount < 1) {
|
||||
frameCount = 1;
|
||||
}
|
||||
if (frame >= frameCount) {
|
||||
frame = frameCount - 1;
|
||||
}
|
||||
int cols = (int) Math.ceil(Math.sqrt(shapeCount));
|
||||
int pos = frame * MAX_CHARACTERS_IN_FONT_PREVIEW;
|
||||
int w2 = (int) (prevWidth * SWF.unitDivisor / cols);
|
||||
int h2 = (int) (prevHeight * SWF.unitDivisor / cols);
|
||||
|
||||
if (maxw == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
int mh = maxh * w2 / maxw;
|
||||
int mw;
|
||||
if (mh > h2) {
|
||||
mw = maxw * h2 / maxh;
|
||||
mh = h2;
|
||||
} else {
|
||||
mw = w2;
|
||||
}
|
||||
|
||||
float ratio = (float) mw / (float) maxw;
|
||||
|
||||
loopy:
|
||||
for (int y = 0; y < cols; y++) {
|
||||
for (int x = 0; x < cols; x++) {
|
||||
if (pos >= shapes.size()) {
|
||||
break loopy;
|
||||
}
|
||||
|
||||
// shapeNum: 1
|
||||
SHAPE shape = shapes.get(pos);
|
||||
List<SHAPERECORD> records = shape.shapeRecords;
|
||||
RECT bounds = SHAPERECORD.getBounds(records);
|
||||
|
||||
int w1 = bounds.getWidth();
|
||||
int h1 = bounds.getHeight();
|
||||
|
||||
double w = ratio * w1;
|
||||
double h = ratio * h1;
|
||||
double px = x * w2 + w2 / 2 - w / 2 - minXMin * ratio;
|
||||
double py = y * h2 - minYMin * ratio;
|
||||
|
||||
Matrix transformation = new Matrix();
|
||||
transformation.translate(px, py);
|
||||
transformation = transformation.concatenate(Matrix.getScaleInstance(ratio));
|
||||
BitmapExporter.export(swf, shape, color, image, transformation, colorTransform);
|
||||
|
||||
// draw bounding boxes
|
||||
if (DRAW_BOUNDING_BOX) {
|
||||
RGB borderColor = new RGBA(Color.black);
|
||||
RGB fillColor = new RGBA(new Color(255, 255, 255, 0));
|
||||
transformation = Matrix.getTranslateInstance(bounds.Xmin, bounds.Ymin).preConcatenate(transformation);
|
||||
TextTag.drawBorder(swf, image, borderColor, fillColor, bounds, new MATRIX(), transformation, colorTransform);
|
||||
}
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public abstract boolean isMove();
|
||||
|
||||
public static List<SHAPE> systemFontCharactersToSHAPES(Font font, int fontSize, String characters) {
|
||||
List<SHAPE> ret = new ArrayList<>();
|
||||
for (int i = 0; i < characters.length(); i++) {
|
||||
ret.add(systemFontCharacterToSHAPE(font, fontSize, characters.charAt(i)));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static SHAPE systemFontCharacterToSHAPE(Font font, int fontSize, char character) {
|
||||
return fontCharacterToSHAPE(font, fontSize, character);
|
||||
}
|
||||
|
||||
public static SHAPE fontCharacterToSHAPE(final Font font, float fontSize, char character) {
|
||||
int multiplier = 1;
|
||||
if (fontSize > 1024) {
|
||||
multiplier = (int) (fontSize / 1024);
|
||||
fontSize = 1024;
|
||||
}
|
||||
List<SHAPERECORD> retList = new ArrayList<>();
|
||||
Font f = font.deriveFont(fontSize);
|
||||
GlyphVector v = FontHelper.createGlyphVector(f, character);
|
||||
Shape shp = v.getOutline();
|
||||
double[] points = new double[6];
|
||||
int lastX = 0;
|
||||
int lastY = 0;
|
||||
int startX = 0;
|
||||
int startY = 0;
|
||||
for (PathIterator it = shp.getPathIterator(null); !it.isDone(); it.next()) {
|
||||
int type = it.currentSegment(points);
|
||||
switch (type) {
|
||||
case PathIterator.SEG_MOVETO:
|
||||
StyleChangeRecord scr = new StyleChangeRecord();
|
||||
scr.stateMoveTo = true;
|
||||
scr.moveDeltaX = multiplier * (int) Math.round(points[0]);
|
||||
scr.moveDeltaY = multiplier * (int) Math.round(points[1]);
|
||||
scr.moveBits = SWFOutputStream.getNeededBitsS(scr.moveDeltaX, scr.moveDeltaY);
|
||||
retList.add(scr);
|
||||
lastX = (int) Math.round(points[0]);
|
||||
lastY = (int) Math.round(points[1]);
|
||||
startX = lastX;
|
||||
startY = lastY;
|
||||
break;
|
||||
case PathIterator.SEG_LINETO:
|
||||
StraightEdgeRecord ser = new StraightEdgeRecord();
|
||||
ser.deltaX = multiplier * (((int) Math.round(points[0])) - lastX);
|
||||
ser.deltaY = multiplier * (((int) Math.round(points[1])) - lastY);
|
||||
|
||||
ser.generalLineFlag = ser.deltaX != 0 && ser.deltaY != 0;
|
||||
if (ser.deltaX == 0) {
|
||||
ser.vertLineFlag = true;
|
||||
}
|
||||
ser.numBits = SWFOutputStream.getNeededBitsS(ser.deltaX, ser.deltaY) - 2;
|
||||
if (ser.numBits < 0) {
|
||||
ser.numBits = 0;
|
||||
}
|
||||
retList.add(ser);
|
||||
lastX = (int) Math.round(points[0]);
|
||||
lastY = (int) Math.round(points[1]);
|
||||
break;
|
||||
case PathIterator.SEG_CUBICTO:
|
||||
double[] cubicCoords = new double[]{
|
||||
lastX, lastY,
|
||||
Math.round(points[0]), Math.round(points[1]),
|
||||
Math.round(points[2]), Math.round(points[3]),
|
||||
Math.round(points[4]), Math.round(points[5])
|
||||
};
|
||||
double[][] quadCoords = approximateCubic(cubicCoords);
|
||||
for (int i = 0; i < quadCoords.length; i++) {
|
||||
CurvedEdgeRecord cer = new CurvedEdgeRecord();
|
||||
cer.controlDeltaX = multiplier * (((int) Math.round(quadCoords[i][0])) - lastX);
|
||||
cer.controlDeltaY = multiplier * (((int) Math.round(quadCoords[i][1])) - lastY);
|
||||
cer.anchorDeltaX = multiplier * (((int) Math.round(quadCoords[i][2])) - ((int) Math.round(quadCoords[i][0])));
|
||||
cer.anchorDeltaY = multiplier * (((int) Math.round(quadCoords[i][3])) - ((int) Math.round(quadCoords[i][1])));
|
||||
cer.numBits = SWFOutputStream.getNeededBitsS(cer.controlDeltaX, cer.controlDeltaY, cer.anchorDeltaX, cer.anchorDeltaY) - 2;
|
||||
if (cer.numBits < 0) {
|
||||
cer.numBits = 0;
|
||||
}
|
||||
lastX = (int) Math.round(quadCoords[i][2]);
|
||||
lastY = (int) Math.round(quadCoords[i][3]);
|
||||
retList.add(cer);
|
||||
}
|
||||
break;
|
||||
case PathIterator.SEG_QUADTO:
|
||||
CurvedEdgeRecord cer = new CurvedEdgeRecord();
|
||||
cer.controlDeltaX = multiplier * (((int) Math.round(points[0])) - lastX);
|
||||
cer.controlDeltaY = multiplier * (((int) Math.round(points[1])) - lastY);
|
||||
cer.anchorDeltaX = multiplier * (((int) Math.round(points[2])) - (int) Math.round(points[0]));
|
||||
cer.anchorDeltaY = multiplier * (((int) Math.round(points[3])) - (int) Math.round(points[1]));
|
||||
cer.numBits = SWFOutputStream.getNeededBitsS(cer.controlDeltaX, cer.controlDeltaY, cer.anchorDeltaX, cer.anchorDeltaY) - 2;
|
||||
if (cer.numBits < 0) {
|
||||
cer.numBits = 0;
|
||||
}
|
||||
retList.add(cer);
|
||||
lastX = (int) Math.round(points[2]);
|
||||
lastY = (int) Math.round(points[3]);
|
||||
break;
|
||||
case PathIterator.SEG_CLOSE: //Closing line back to last SEG_MOVETO
|
||||
if ((startX == lastX) && (startY == lastY)) {
|
||||
break;
|
||||
}
|
||||
StraightEdgeRecord closeSer = new StraightEdgeRecord();
|
||||
closeSer.generalLineFlag = true;
|
||||
closeSer.deltaX = multiplier * ((int) Math.round((startX - lastX)));
|
||||
closeSer.deltaY = multiplier * ((int) Math.round((startY - lastY)));
|
||||
closeSer.numBits = SWFOutputStream.getNeededBitsS(closeSer.deltaX, closeSer.deltaY) - 2;
|
||||
if (closeSer.numBits < 0) {
|
||||
closeSer.numBits = 0;
|
||||
}
|
||||
retList.add(closeSer);
|
||||
lastX = startX;
|
||||
lastY = startY;
|
||||
break;
|
||||
}
|
||||
}
|
||||
SHAPE shape = new SHAPE();
|
||||
StyleChangeRecord init;
|
||||
if (!retList.isEmpty() && retList.get(0) instanceof StyleChangeRecord) {
|
||||
init = (StyleChangeRecord) retList.get(0);
|
||||
} else {
|
||||
init = new StyleChangeRecord();
|
||||
retList.add(0, init);
|
||||
}
|
||||
|
||||
retList.add(new EndShapeRecord());
|
||||
init.stateFillStyle0 = true;
|
||||
init.fillStyle0 = 1;
|
||||
shape.shapeRecords = retList;
|
||||
shape.numFillBits = 1;
|
||||
shape.numLineBits = 0;
|
||||
return shape;
|
||||
}
|
||||
|
||||
// Taken from org.apache.fop.afp.util
|
||||
public static double[][] approximateCubic(double[] cubicControlPointCoords) {
|
||||
if (cubicControlPointCoords.length < 8) {
|
||||
throw new IllegalArgumentException("Must have at least 8 coordinates");
|
||||
}
|
||||
|
||||
//extract point objects from source array
|
||||
Point2D p0 = new Point2D.Double(cubicControlPointCoords[0], cubicControlPointCoords[1]);
|
||||
Point2D p1 = new Point2D.Double(cubicControlPointCoords[2], cubicControlPointCoords[3]);
|
||||
Point2D p2 = new Point2D.Double(cubicControlPointCoords[4], cubicControlPointCoords[5]);
|
||||
Point2D p3 = new Point2D.Double(cubicControlPointCoords[6], cubicControlPointCoords[7]);
|
||||
|
||||
//calculates the useful base points
|
||||
Point2D pa = getPointOnSegment(p0, p1, 3.0 / 4.0);
|
||||
Point2D pb = getPointOnSegment(p3, p2, 3.0 / 4.0);
|
||||
|
||||
//get 1/16 of the [P3, P0] segment
|
||||
double dx = (p3.getX() - p0.getX()) / 16.0;
|
||||
double dy = (p3.getY() - p0.getY()) / 16.0;
|
||||
|
||||
//calculates control point 1
|
||||
Point2D pc1 = getPointOnSegment(p0, p1, 3.0 / 8.0);
|
||||
|
||||
//calculates control point 2
|
||||
Point2D pc2 = getPointOnSegment(pa, pb, 3.0 / 8.0);
|
||||
pc2 = movePoint(pc2, -dx, -dy);
|
||||
|
||||
//calculates control point 3
|
||||
Point2D pc3 = getPointOnSegment(pb, pa, 3.0 / 8.0);
|
||||
pc3 = movePoint(pc3, dx, dy);
|
||||
|
||||
//calculates control point 4
|
||||
Point2D pc4 = getPointOnSegment(p3, p2, 3.0 / 8.0);
|
||||
|
||||
//calculates the 3 anchor points
|
||||
Point2D pa1 = getMidPoint(pc1, pc2);
|
||||
Point2D pa2 = getMidPoint(pa, pb);
|
||||
Point2D pa3 = getMidPoint(pc3, pc4);
|
||||
|
||||
//return the points for the four quadratic curves
|
||||
return new double[][]{
|
||||
{pc1.getX(), pc1.getY(), pa1.getX(), pa1.getY()},
|
||||
{pc2.getX(), pc2.getY(), pa2.getX(), pa2.getY()},
|
||||
{pc3.getX(), pc3.getY(), pa3.getX(), pa3.getY()},
|
||||
{pc4.getX(), pc4.getY(), p3.getX(), p3.getY()}};
|
||||
}
|
||||
|
||||
private static Point2D.Double movePoint(Point2D point, double dx, double dy) {
|
||||
return new Point2D.Double(point.getX() + dx, point.getY() + dy);
|
||||
}
|
||||
|
||||
private static Point2D getMidPoint(Point2D p0, Point2D p1) {
|
||||
return getPointOnSegment(p0, p1, 0.5);
|
||||
}
|
||||
|
||||
private static Point2D getPointOnSegment(Point2D p0, Point2D p1, double ratio) {
|
||||
double x = p0.getX() + ((p1.getX() - p0.getX()) * ratio);
|
||||
double y = p0.getY() + ((p1.getY() - p0.getY()) * ratio);
|
||||
return new Point2D.Double(x, y);
|
||||
}
|
||||
|
||||
public static SHAPE resizeSHAPE(SHAPE shp, double multiplier) {
|
||||
SHAPE ret = new SHAPE();
|
||||
ret.numFillBits = shp.numFillBits;
|
||||
ret.numLineBits = shp.numLineBits;
|
||||
List<SHAPERECORD> recs = new ArrayList<>();
|
||||
for (SHAPERECORD r : shp.shapeRecords) {
|
||||
SHAPERECORD c = r.clone();
|
||||
if (c instanceof StyleChangeRecord) {
|
||||
StyleChangeRecord scr = (StyleChangeRecord) c;
|
||||
scr.moveDeltaX = (int) (multiplier * scr.moveDeltaX);
|
||||
scr.moveDeltaY = (int) (multiplier * scr.moveDeltaY);
|
||||
scr.calculateBits();
|
||||
}
|
||||
if (c instanceof CurvedEdgeRecord) {
|
||||
CurvedEdgeRecord cer = (CurvedEdgeRecord) c;
|
||||
cer.controlDeltaX = (int) (multiplier * cer.controlDeltaX);
|
||||
cer.controlDeltaY = (int) (multiplier * cer.controlDeltaY);
|
||||
cer.anchorDeltaX = (int) (multiplier * cer.anchorDeltaX);
|
||||
cer.anchorDeltaY = (int) (multiplier * cer.anchorDeltaY);
|
||||
cer.calculateBits();
|
||||
}
|
||||
if (c instanceof StraightEdgeRecord) {
|
||||
StraightEdgeRecord ser = (StraightEdgeRecord) c;
|
||||
ser.deltaX = (int) (multiplier * ser.deltaX);
|
||||
ser.deltaY = (int) (multiplier * ser.deltaY);
|
||||
ser.calculateBits();
|
||||
}
|
||||
recs.add(c);
|
||||
}
|
||||
ret.shapeRecords = recs;
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static Shape moveShapeToStart(Shape s) {
|
||||
Rectangle bds = s.getBounds();
|
||||
s = AffineTransform.getTranslateInstance(-bds.x, -bds.y).createTransformedShape(s);
|
||||
return s;
|
||||
}
|
||||
|
||||
public static Shape twipToPixelShape(Shape s) {
|
||||
Rectangle bds = s.getBounds();
|
||||
int dx = -bds.x - bds.width / 2;
|
||||
int dy = -bds.y - bds.height / 2;
|
||||
s = AffineTransform.getTranslateInstance(dx, dy).createTransformedShape(s);
|
||||
s = AffineTransform.getScaleInstance(1 / SWF.unitDivisor, 1 / SWF.unitDivisor).createTransformedShape(s);
|
||||
s = AffineTransform.getTranslateInstance(-dx / SWF.unitDivisor, -dy / SWF.unitDivisor).createTransformedShape(s);
|
||||
return s;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SHAPERECORD clone() {
|
||||
try {
|
||||
return (SHAPERECORD) super.clone();
|
||||
} catch (CloneNotSupportedException ex) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (C) 2010-2015 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.types.shaperecords;
|
||||
|
||||
import com.jpexs.decompiler.flash.SWF;
|
||||
import com.jpexs.decompiler.flash.SWFOutputStream;
|
||||
import com.jpexs.decompiler.flash.exporters.commonshape.Matrix;
|
||||
import com.jpexs.decompiler.flash.exporters.shape.BitmapExporter;
|
||||
import com.jpexs.decompiler.flash.helpers.FontHelper;
|
||||
import com.jpexs.decompiler.flash.tags.base.NeedsCharacters;
|
||||
import com.jpexs.decompiler.flash.tags.base.TextTag;
|
||||
import com.jpexs.decompiler.flash.types.ColorTransform;
|
||||
import com.jpexs.decompiler.flash.types.MATRIX;
|
||||
import com.jpexs.decompiler.flash.types.RECT;
|
||||
import com.jpexs.decompiler.flash.types.RGB;
|
||||
import com.jpexs.decompiler.flash.types.RGBA;
|
||||
import com.jpexs.decompiler.flash.types.SHAPE;
|
||||
import com.jpexs.helpers.ConcreteClasses;
|
||||
import com.jpexs.helpers.SerializableImage;
|
||||
import java.awt.Color;
|
||||
import java.awt.Font;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.Shape;
|
||||
import java.awt.font.GlyphVector;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.geom.PathIterator;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
@ConcreteClasses({CurvedEdgeRecord.class, StraightEdgeRecord.class, StyleChangeRecord.class, EndShapeRecord.class})
|
||||
public abstract class SHAPERECORD implements Cloneable, NeedsCharacters, Serializable {
|
||||
|
||||
public static final int MAX_CHARACTERS_IN_FONT_PREVIEW = 400;
|
||||
|
||||
public static final boolean DRAW_BOUNDING_BOX = false;
|
||||
|
||||
public abstract void calculateBits();
|
||||
|
||||
@Override
|
||||
public void getNeededCharacters(Set<Integer> needed) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean replaceCharacter(int oldCharacterId, int newCharacterId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeCharacter(int characterId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public abstract int changeX(int x);
|
||||
|
||||
public abstract int changeY(int y);
|
||||
|
||||
public abstract void flip();
|
||||
|
||||
public static RECT getBounds(List<SHAPERECORD> records) {
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
int max_x = 0;
|
||||
int max_y = 0;
|
||||
int min_x = Integer.MAX_VALUE;
|
||||
int min_y = Integer.MAX_VALUE;
|
||||
boolean started = false;
|
||||
for (SHAPERECORD r : records) {
|
||||
if (r instanceof CurvedEdgeRecord) {
|
||||
CurvedEdgeRecord curverEdge = (CurvedEdgeRecord) r;
|
||||
int x2 = x + curverEdge.controlDeltaX;
|
||||
int y2 = y + curverEdge.controlDeltaY;
|
||||
if (x2 > max_x) {
|
||||
max_x = x2;
|
||||
}
|
||||
if (y2 > max_y) {
|
||||
max_y = y2;
|
||||
}
|
||||
if (started) {
|
||||
if (y2 < min_y) {
|
||||
min_y = y2;
|
||||
}
|
||||
if (x2 < min_x) {
|
||||
min_x = x2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
x = r.changeX(x);
|
||||
y = r.changeY(y);
|
||||
if (x > max_x) {
|
||||
max_x = x;
|
||||
}
|
||||
if (y > max_y) {
|
||||
max_y = y;
|
||||
}
|
||||
if (r.isMove()) {
|
||||
started = true;
|
||||
}
|
||||
if (started) {
|
||||
if (y < min_y) {
|
||||
min_y = y;
|
||||
}
|
||||
if (x < min_x) {
|
||||
min_x = x;
|
||||
}
|
||||
}
|
||||
}
|
||||
return new RECT(min_x, max_x, min_y, max_y);
|
||||
}
|
||||
|
||||
public static CurvedEdgeRecord straightToCurve(StraightEdgeRecord ser) {
|
||||
CurvedEdgeRecord ret = new CurvedEdgeRecord();
|
||||
ret.controlDeltaX = ser.deltaX / 2;
|
||||
ret.controlDeltaY = ser.deltaY / 2;
|
||||
ret.anchorDeltaX = ser.deltaX - ret.controlDeltaX;
|
||||
ret.anchorDeltaY = ser.deltaY - ret.controlDeltaY;
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static void shapeListToImage(SWF swf, List<SHAPE> shapes, SerializableImage image, int frame, Color color, ColorTransform colorTransform) {
|
||||
if (shapes.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
int prevWidth = image.getWidth();
|
||||
int prevHeight = image.getHeight();
|
||||
|
||||
int maxw = 0;
|
||||
int maxh = 0;
|
||||
int minXMin = 0;
|
||||
int minYMin = 0;
|
||||
for (SHAPE s : shapes) {
|
||||
RECT r = SHAPERECORD.getBounds(s.shapeRecords);
|
||||
if (r.Xmax < r.Xmin || r.Ymax < r.Ymin) {
|
||||
continue;
|
||||
}
|
||||
if (r.getWidth() > maxw) {
|
||||
maxw = r.getWidth();
|
||||
}
|
||||
if (r.getHeight() > maxh) {
|
||||
maxh = r.getHeight();
|
||||
}
|
||||
if (r.Xmin < minXMin) {
|
||||
minXMin = r.Xmin;
|
||||
}
|
||||
if (r.Ymin < minYMin) {
|
||||
minYMin = r.Ymin;
|
||||
}
|
||||
}
|
||||
|
||||
int shapeCount = Math.min(MAX_CHARACTERS_IN_FONT_PREVIEW, shapes.size());
|
||||
int frameCount = (shapes.size() - 1) / MAX_CHARACTERS_IN_FONT_PREVIEW + 1;
|
||||
if (frameCount < 1) {
|
||||
frameCount = 1;
|
||||
}
|
||||
if (frame >= frameCount) {
|
||||
frame = frameCount - 1;
|
||||
}
|
||||
int cols = (int) Math.ceil(Math.sqrt(shapeCount));
|
||||
int pos = frame * MAX_CHARACTERS_IN_FONT_PREVIEW;
|
||||
int w2 = (int) (prevWidth * SWF.unitDivisor / cols);
|
||||
int h2 = (int) (prevHeight * SWF.unitDivisor / cols);
|
||||
|
||||
if (maxw == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
int mh = maxh * w2 / maxw;
|
||||
int mw;
|
||||
if (mh > h2) {
|
||||
mw = maxw * h2 / maxh;
|
||||
mh = h2;
|
||||
} else {
|
||||
mw = w2;
|
||||
}
|
||||
|
||||
float ratio = (float) mw / (float) maxw;
|
||||
|
||||
loopy:
|
||||
for (int y = 0; y < cols; y++) {
|
||||
for (int x = 0; x < cols; x++) {
|
||||
if (pos >= shapes.size()) {
|
||||
break loopy;
|
||||
}
|
||||
|
||||
// shapeNum: 1
|
||||
SHAPE shape = shapes.get(pos);
|
||||
List<SHAPERECORD> records = shape.shapeRecords;
|
||||
RECT bounds = SHAPERECORD.getBounds(records);
|
||||
|
||||
int w1 = bounds.getWidth();
|
||||
int h1 = bounds.getHeight();
|
||||
|
||||
double w = ratio * w1;
|
||||
double h = ratio * h1;
|
||||
double px = x * w2 + w2 / 2 - w / 2 - minXMin * ratio;
|
||||
double py = y * h2 - minYMin * ratio;
|
||||
|
||||
Matrix transformation = new Matrix();
|
||||
transformation.translate(px, py);
|
||||
transformation = transformation.concatenate(Matrix.getScaleInstance(ratio));
|
||||
BitmapExporter.export(swf, shape, color, image, transformation, colorTransform);
|
||||
|
||||
// draw bounding boxes
|
||||
if (DRAW_BOUNDING_BOX) {
|
||||
RGB borderColor = new RGBA(Color.black);
|
||||
RGB fillColor = new RGBA(new Color(255, 255, 255, 0));
|
||||
transformation = Matrix.getTranslateInstance(bounds.Xmin, bounds.Ymin).preConcatenate(transformation);
|
||||
TextTag.drawBorder(swf, image, borderColor, fillColor, bounds, new MATRIX(), transformation, colorTransform);
|
||||
}
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public abstract boolean isMove();
|
||||
|
||||
public static List<SHAPE> systemFontCharactersToSHAPES(Font font, int fontSize, String characters) {
|
||||
List<SHAPE> ret = new ArrayList<>();
|
||||
for (int i = 0; i < characters.length(); i++) {
|
||||
ret.add(systemFontCharacterToSHAPE(font, fontSize, characters.charAt(i)));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static SHAPE systemFontCharacterToSHAPE(Font font, int fontSize, char character) {
|
||||
return fontCharacterToSHAPE(font, fontSize, character);
|
||||
}
|
||||
|
||||
public static SHAPE fontCharacterToSHAPE(final Font font, float fontSize, char character) {
|
||||
int multiplier = 1;
|
||||
if (fontSize > 1024) {
|
||||
multiplier = (int) (fontSize / 1024);
|
||||
fontSize = 1024;
|
||||
}
|
||||
List<SHAPERECORD> retList = new ArrayList<>();
|
||||
Font f = font.deriveFont(fontSize);
|
||||
GlyphVector v = FontHelper.createGlyphVector(f, character);
|
||||
Shape shp = v.getOutline();
|
||||
double[] points = new double[6];
|
||||
int lastX = 0;
|
||||
int lastY = 0;
|
||||
int startX = 0;
|
||||
int startY = 0;
|
||||
for (PathIterator it = shp.getPathIterator(null); !it.isDone(); it.next()) {
|
||||
int type = it.currentSegment(points);
|
||||
switch (type) {
|
||||
case PathIterator.SEG_MOVETO:
|
||||
StyleChangeRecord scr = new StyleChangeRecord();
|
||||
scr.stateMoveTo = true;
|
||||
scr.moveDeltaX = multiplier * (int) Math.round(points[0]);
|
||||
scr.moveDeltaY = multiplier * (int) Math.round(points[1]);
|
||||
scr.moveBits = SWFOutputStream.getNeededBitsS(scr.moveDeltaX, scr.moveDeltaY);
|
||||
retList.add(scr);
|
||||
lastX = (int) Math.round(points[0]);
|
||||
lastY = (int) Math.round(points[1]);
|
||||
startX = lastX;
|
||||
startY = lastY;
|
||||
break;
|
||||
case PathIterator.SEG_LINETO:
|
||||
StraightEdgeRecord ser = new StraightEdgeRecord();
|
||||
ser.deltaX = multiplier * (((int) Math.round(points[0])) - lastX);
|
||||
ser.deltaY = multiplier * (((int) Math.round(points[1])) - lastY);
|
||||
|
||||
ser.generalLineFlag = ser.deltaX != 0 && ser.deltaY != 0;
|
||||
if (ser.deltaX == 0) {
|
||||
ser.vertLineFlag = true;
|
||||
}
|
||||
ser.numBits = SWFOutputStream.getNeededBitsS(ser.deltaX, ser.deltaY) - 2;
|
||||
if (ser.numBits < 0) {
|
||||
ser.numBits = 0;
|
||||
}
|
||||
retList.add(ser);
|
||||
lastX = (int) Math.round(points[0]);
|
||||
lastY = (int) Math.round(points[1]);
|
||||
break;
|
||||
case PathIterator.SEG_CUBICTO:
|
||||
double[] cubicCoords = new double[]{
|
||||
lastX, lastY,
|
||||
Math.round(points[0]), Math.round(points[1]),
|
||||
Math.round(points[2]), Math.round(points[3]),
|
||||
Math.round(points[4]), Math.round(points[5])
|
||||
};
|
||||
double[][] quadCoords = approximateCubic(cubicCoords);
|
||||
for (int i = 0; i < quadCoords.length; i++) {
|
||||
CurvedEdgeRecord cer = new CurvedEdgeRecord();
|
||||
cer.controlDeltaX = multiplier * (((int) Math.round(quadCoords[i][0])) - lastX);
|
||||
cer.controlDeltaY = multiplier * (((int) Math.round(quadCoords[i][1])) - lastY);
|
||||
cer.anchorDeltaX = multiplier * (((int) Math.round(quadCoords[i][2])) - ((int) Math.round(quadCoords[i][0])));
|
||||
cer.anchorDeltaY = multiplier * (((int) Math.round(quadCoords[i][3])) - ((int) Math.round(quadCoords[i][1])));
|
||||
cer.numBits = SWFOutputStream.getNeededBitsS(cer.controlDeltaX, cer.controlDeltaY, cer.anchorDeltaX, cer.anchorDeltaY) - 2;
|
||||
if (cer.numBits < 0) {
|
||||
cer.numBits = 0;
|
||||
}
|
||||
lastX = (int) Math.round(quadCoords[i][2]);
|
||||
lastY = (int) Math.round(quadCoords[i][3]);
|
||||
retList.add(cer);
|
||||
}
|
||||
break;
|
||||
case PathIterator.SEG_QUADTO:
|
||||
CurvedEdgeRecord cer = new CurvedEdgeRecord();
|
||||
cer.controlDeltaX = multiplier * (((int) Math.round(points[0])) - lastX);
|
||||
cer.controlDeltaY = multiplier * (((int) Math.round(points[1])) - lastY);
|
||||
cer.anchorDeltaX = multiplier * (((int) Math.round(points[2])) - (int) Math.round(points[0]));
|
||||
cer.anchorDeltaY = multiplier * (((int) Math.round(points[3])) - (int) Math.round(points[1]));
|
||||
cer.numBits = SWFOutputStream.getNeededBitsS(cer.controlDeltaX, cer.controlDeltaY, cer.anchorDeltaX, cer.anchorDeltaY) - 2;
|
||||
if (cer.numBits < 0) {
|
||||
cer.numBits = 0;
|
||||
}
|
||||
retList.add(cer);
|
||||
lastX = (int) Math.round(points[2]);
|
||||
lastY = (int) Math.round(points[3]);
|
||||
break;
|
||||
case PathIterator.SEG_CLOSE: //Closing line back to last SEG_MOVETO
|
||||
if ((startX == lastX) && (startY == lastY)) {
|
||||
break;
|
||||
}
|
||||
StraightEdgeRecord closeSer = new StraightEdgeRecord();
|
||||
closeSer.generalLineFlag = true;
|
||||
closeSer.deltaX = multiplier * ((int) Math.round((startX - lastX)));
|
||||
closeSer.deltaY = multiplier * ((int) Math.round((startY - lastY)));
|
||||
closeSer.numBits = SWFOutputStream.getNeededBitsS(closeSer.deltaX, closeSer.deltaY) - 2;
|
||||
if (closeSer.numBits < 0) {
|
||||
closeSer.numBits = 0;
|
||||
}
|
||||
retList.add(closeSer);
|
||||
lastX = startX;
|
||||
lastY = startY;
|
||||
break;
|
||||
}
|
||||
}
|
||||
SHAPE shape = new SHAPE();
|
||||
StyleChangeRecord init;
|
||||
if (!retList.isEmpty() && retList.get(0) instanceof StyleChangeRecord) {
|
||||
init = (StyleChangeRecord) retList.get(0);
|
||||
} else {
|
||||
init = new StyleChangeRecord();
|
||||
retList.add(0, init);
|
||||
}
|
||||
|
||||
retList.add(new EndShapeRecord());
|
||||
init.stateFillStyle0 = true;
|
||||
init.fillStyle0 = 1;
|
||||
shape.shapeRecords = retList;
|
||||
shape.numFillBits = 1;
|
||||
shape.numLineBits = 0;
|
||||
return shape;
|
||||
}
|
||||
|
||||
// Taken from org.apache.fop.afp.util
|
||||
public static double[][] approximateCubic(double[] cubicControlPointCoords) {
|
||||
if (cubicControlPointCoords.length < 8) {
|
||||
throw new IllegalArgumentException("Must have at least 8 coordinates");
|
||||
}
|
||||
|
||||
//extract point objects from source array
|
||||
Point2D p0 = new Point2D.Double(cubicControlPointCoords[0], cubicControlPointCoords[1]);
|
||||
Point2D p1 = new Point2D.Double(cubicControlPointCoords[2], cubicControlPointCoords[3]);
|
||||
Point2D p2 = new Point2D.Double(cubicControlPointCoords[4], cubicControlPointCoords[5]);
|
||||
Point2D p3 = new Point2D.Double(cubicControlPointCoords[6], cubicControlPointCoords[7]);
|
||||
|
||||
//calculates the useful base points
|
||||
Point2D pa = getPointOnSegment(p0, p1, 3.0 / 4.0);
|
||||
Point2D pb = getPointOnSegment(p3, p2, 3.0 / 4.0);
|
||||
|
||||
//get 1/16 of the [P3, P0] segment
|
||||
double dx = (p3.getX() - p0.getX()) / 16.0;
|
||||
double dy = (p3.getY() - p0.getY()) / 16.0;
|
||||
|
||||
//calculates control point 1
|
||||
Point2D pc1 = getPointOnSegment(p0, p1, 3.0 / 8.0);
|
||||
|
||||
//calculates control point 2
|
||||
Point2D pc2 = getPointOnSegment(pa, pb, 3.0 / 8.0);
|
||||
pc2 = movePoint(pc2, -dx, -dy);
|
||||
|
||||
//calculates control point 3
|
||||
Point2D pc3 = getPointOnSegment(pb, pa, 3.0 / 8.0);
|
||||
pc3 = movePoint(pc3, dx, dy);
|
||||
|
||||
//calculates control point 4
|
||||
Point2D pc4 = getPointOnSegment(p3, p2, 3.0 / 8.0);
|
||||
|
||||
//calculates the 3 anchor points
|
||||
Point2D pa1 = getMidPoint(pc1, pc2);
|
||||
Point2D pa2 = getMidPoint(pa, pb);
|
||||
Point2D pa3 = getMidPoint(pc3, pc4);
|
||||
|
||||
//return the points for the four quadratic curves
|
||||
return new double[][]{
|
||||
{pc1.getX(), pc1.getY(), pa1.getX(), pa1.getY()},
|
||||
{pc2.getX(), pc2.getY(), pa2.getX(), pa2.getY()},
|
||||
{pc3.getX(), pc3.getY(), pa3.getX(), pa3.getY()},
|
||||
{pc4.getX(), pc4.getY(), p3.getX(), p3.getY()}};
|
||||
}
|
||||
|
||||
private static Point2D.Double movePoint(Point2D point, double dx, double dy) {
|
||||
return new Point2D.Double(point.getX() + dx, point.getY() + dy);
|
||||
}
|
||||
|
||||
private static Point2D getMidPoint(Point2D p0, Point2D p1) {
|
||||
return getPointOnSegment(p0, p1, 0.5);
|
||||
}
|
||||
|
||||
private static Point2D getPointOnSegment(Point2D p0, Point2D p1, double ratio) {
|
||||
double x = p0.getX() + ((p1.getX() - p0.getX()) * ratio);
|
||||
double y = p0.getY() + ((p1.getY() - p0.getY()) * ratio);
|
||||
return new Point2D.Double(x, y);
|
||||
}
|
||||
|
||||
public static SHAPE resizeSHAPE(SHAPE shp, double multiplier) {
|
||||
SHAPE ret = new SHAPE();
|
||||
ret.numFillBits = shp.numFillBits;
|
||||
ret.numLineBits = shp.numLineBits;
|
||||
List<SHAPERECORD> recs = new ArrayList<>();
|
||||
for (SHAPERECORD r : shp.shapeRecords) {
|
||||
SHAPERECORD c = r.clone();
|
||||
if (c instanceof StyleChangeRecord) {
|
||||
StyleChangeRecord scr = (StyleChangeRecord) c;
|
||||
scr.moveDeltaX = (int) (multiplier * scr.moveDeltaX);
|
||||
scr.moveDeltaY = (int) (multiplier * scr.moveDeltaY);
|
||||
scr.calculateBits();
|
||||
}
|
||||
if (c instanceof CurvedEdgeRecord) {
|
||||
CurvedEdgeRecord cer = (CurvedEdgeRecord) c;
|
||||
cer.controlDeltaX = (int) (multiplier * cer.controlDeltaX);
|
||||
cer.controlDeltaY = (int) (multiplier * cer.controlDeltaY);
|
||||
cer.anchorDeltaX = (int) (multiplier * cer.anchorDeltaX);
|
||||
cer.anchorDeltaY = (int) (multiplier * cer.anchorDeltaY);
|
||||
cer.calculateBits();
|
||||
}
|
||||
if (c instanceof StraightEdgeRecord) {
|
||||
StraightEdgeRecord ser = (StraightEdgeRecord) c;
|
||||
ser.deltaX = (int) (multiplier * ser.deltaX);
|
||||
ser.deltaY = (int) (multiplier * ser.deltaY);
|
||||
ser.calculateBits();
|
||||
}
|
||||
recs.add(c);
|
||||
}
|
||||
ret.shapeRecords = recs;
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static Shape moveShapeToStart(Shape s) {
|
||||
Rectangle bds = s.getBounds();
|
||||
s = AffineTransform.getTranslateInstance(-bds.x, -bds.y).createTransformedShape(s);
|
||||
return s;
|
||||
}
|
||||
|
||||
public static Shape twipToPixelShape(Shape s) {
|
||||
Rectangle bds = s.getBounds();
|
||||
int dx = -bds.x - bds.width / 2;
|
||||
int dy = -bds.y - bds.height / 2;
|
||||
s = AffineTransform.getTranslateInstance(dx, dy).createTransformedShape(s);
|
||||
s = AffineTransform.getScaleInstance(1 / SWF.unitDivisor, 1 / SWF.unitDivisor).createTransformedShape(s);
|
||||
s = AffineTransform.getTranslateInstance(-dx / SWF.unitDivisor, -dy / SWF.unitDivisor).createTransformedShape(s);
|
||||
return s;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SHAPERECORD clone() {
|
||||
try {
|
||||
return (SHAPERECORD) super.clone();
|
||||
} catch (CloneNotSupportedException ex) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
33
libsrc/ffdec_lib/src/com/jpexs/helpers/ConcreteClasses.java
Normal file
33
libsrc/ffdec_lib/src/com/jpexs/helpers/ConcreteClasses.java
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2015 JPEXS
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.jpexs.helpers;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
public @interface ConcreteClasses {
|
||||
|
||||
Class<?>[] value();
|
||||
}
|
||||
@@ -170,7 +170,7 @@ public class ReflectionTools {
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static boolean addToList(Object object, Field field, int index) {
|
||||
public static boolean addToList(Object object, Field field, int index, Class<?> cls) {
|
||||
if (!List.class.isAssignableFrom(field.getType())) {
|
||||
return false;
|
||||
}
|
||||
@@ -184,7 +184,7 @@ public class ReflectionTools {
|
||||
ParameterizedType listType = (ParameterizedType) field.getGenericType();
|
||||
Class<?> parameterClass = (Class<?>) listType.getActualTypeArguments()[0];
|
||||
try {
|
||||
Object val = newInstanceOf(parameterClass);
|
||||
Object val = newInstanceOf(cls == null ? parameterClass : cls);
|
||||
if (val == null) {
|
||||
return false;
|
||||
}
|
||||
@@ -213,7 +213,7 @@ public class ReflectionTools {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static boolean addToArray(Object object, Field field, int index, boolean notnull) {
|
||||
public static boolean addToArray(Object object, Field field, int index, boolean notnull, Class<?> cls) {
|
||||
if (!field.getType().isArray()) {
|
||||
return false;
|
||||
}
|
||||
@@ -228,7 +228,7 @@ public class ReflectionTools {
|
||||
Object val = null;
|
||||
if (!componentClass.isPrimitive()) {
|
||||
try {
|
||||
val = newInstanceOf(componentClass);
|
||||
val = newInstanceOf(cls == null ? componentClass : cls);
|
||||
} catch (InstantiationException | IllegalAccessException ex) {
|
||||
logger.log(Level.SEVERE, null, ex);
|
||||
return false;
|
||||
@@ -261,13 +261,13 @@ public class ReflectionTools {
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean addToField(Object object, Field field, int index, boolean notnull) {
|
||||
public static boolean addToField(Object object, Field field, int index, boolean notnull, Class<?> cls) {
|
||||
if (List.class.isAssignableFrom(field.getType())) {
|
||||
return addToList(object, field, index);
|
||||
return addToList(object, field, index, cls);
|
||||
}
|
||||
|
||||
if (field.getType().isArray()) {
|
||||
return addToArray(object, field, index, notnull);
|
||||
return addToArray(object, field, index, notnull, cls);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user