a bit of SVG shape conversion

sprite centering
This commit is contained in:
Jindra Petk
2013-01-20 10:38:17 +01:00
parent 01f5480399
commit 50217b77d9
13 changed files with 392 additions and 17 deletions

View File

@@ -14,8 +14,6 @@
* 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.asdec.gui;
import com.jpexs.asdec.SWF;
@@ -28,6 +26,11 @@ import com.jpexs.asdec.tags.DefineBitsLosslessTag;
import com.jpexs.asdec.tags.DefineBitsTag;
import com.jpexs.asdec.tags.DefineMorphShape2Tag;
import com.jpexs.asdec.tags.DefineMorphShapeTag;
import com.jpexs.asdec.tags.DefineShape2Tag;
import com.jpexs.asdec.tags.DefineShape3Tag;
import com.jpexs.asdec.tags.DefineShape4Tag;
import com.jpexs.asdec.tags.DefineShapeTag;
import com.jpexs.asdec.tags.DefineSpriteTag;
import com.jpexs.asdec.tags.DefineTextTag;
import com.jpexs.asdec.tags.EndTag;
import com.jpexs.asdec.tags.JPEGTablesTag;
@@ -132,7 +135,7 @@ public class TagPanel extends JPanel implements ListSelectionListener {
swtPanel.setBackground(Color.white);
displayPanel.add(swtPanel, CARDFLASHPANEL);
}
imagePanel = new ImagePanel();
imagePanel = new ImagePanel();
displayPanel.add(imagePanel, CARDIMAGEPANEL);
displayPanel.add(new JPanel(), CARDEMPTYPANEL);
CardLayout cl = (CardLayout) (displayPanel.getLayout());
@@ -217,8 +220,13 @@ public class TagPanel extends JPanel implements ListSelectionListener {
mat.hasScale = false;
mat.translateX = 0;
mat.translateY = 0;
if (tagObj instanceof BoundedTag) {
RECT r = ((BoundedTag) tagObj).getRect();
if ((tagObj instanceof BoundedTag) || (tagObj instanceof DefineSpriteTag)) {
RECT r = null;
if (tagObj instanceof DefineSpriteTag) {
r = ((DefineSpriteTag) tagObj).getRect(characters);
} else {
r = ((BoundedTag) tagObj).getRect();
}
mat.translateX = -r.Xmin;
mat.translateY = -r.Ymin;
mat.translateX = mat.translateX + width / 2 - r.getWidth() / 2;

View File

@@ -14,21 +14,24 @@
* 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.asdec.tags;
import com.jpexs.asdec.Main;
import com.jpexs.asdec.SWFInputStream;
import com.jpexs.asdec.SWFOutputStream;
import com.jpexs.asdec.abc.CopyOutputStream;
import com.jpexs.asdec.tags.base.BoundedTag;
import com.jpexs.asdec.tags.base.CharacterTag;
import com.jpexs.asdec.tags.base.Container;
import com.jpexs.asdec.types.MATRIX;
import com.jpexs.asdec.types.RECT;
import java.awt.Point;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -57,6 +60,64 @@ public class DefineSpriteTag extends CharacterTag implements Container {
return spriteId;
}
private RECT getCharacterBounds(HashMap<Integer, CharacterTag> allCharacters, Set<Integer> characters) {
RECT ret = new RECT(Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE,Integer.MIN_VALUE);
for (int c : characters) {
Tag t = allCharacters.get(c);
RECT r = null;
if (t instanceof BoundedTag) {
r = ((BoundedTag) t).getRect();
} else if (t instanceof DefineSpriteTag) {
r = ((DefineSpriteTag) t).getRect(allCharacters);
}
if (r != null) {
ret.Xmin = Math.min(r.Xmin, ret.Xmin);
ret.Ymin = Math.min(r.Ymin, ret.Ymin);
ret.Xmax = Math.max(r.Xmax, ret.Xmax);
ret.Ymax = Math.max(r.Ymax, ret.Ymax);
}
}
return ret;
}
public RECT getRect(HashMap<Integer, CharacterTag> characters) {
RECT ret = new RECT(Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE,Integer.MIN_VALUE);
for (Tag t : subTags) {
Set<Integer> needed = t.getNeededCharacters();
if(needed.isEmpty()){
continue;
}
RECT r = getCharacterBounds(characters, needed);
MATRIX m = null;
if (t instanceof PlaceObjectTag) {
m = ((PlaceObjectTag) t).matrix;
}
if (t instanceof PlaceObject2Tag) {
if (((PlaceObject2Tag) t).placeFlagHasMatrix) {
m = ((PlaceObject2Tag) t).matrix;
}
}
if (t instanceof PlaceObject3Tag) {
if (((PlaceObject3Tag) t).placeFlagHasMatrix) {
m = ((PlaceObject3Tag) t).matrix;
}
}
if (m != null) {
Point topleft = m.apply(new Point(r.Xmin, r.Ymin));
Point bottomright = m.apply(new Point(r.Xmax, r.Ymax));
r.Xmin=Math.min(topleft.x,bottomright.x);
r.Ymin=Math.min(topleft.y,bottomright.y);
r.Xmax=Math.max(topleft.x,bottomright.x);
r.Ymax=Math.max(topleft.y,bottomright.y);
}
ret.Xmin = Math.min(r.Xmin, ret.Xmin);
ret.Ymin = Math.min(r.Ymin, ret.Ymin);
ret.Xmax = Math.max(r.Xmax, ret.Xmax);
ret.Ymax = Math.max(r.Ymax, ret.Ymax);
}
return ret;
}
/**
* Constructor
*

View File

@@ -23,7 +23,7 @@ package com.jpexs.asdec.types;
* @author JPEXS
*/
public class LINESTYLEARRAY {
public LINESTYLE lineStyles[];
public LINESTYLE2 lineStyles2[];
}

View File

@@ -14,10 +14,10 @@
* 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.asdec.types;
import java.awt.Point;
/**
* Represents a standard 2x3 transformation matrix of the sort commonly used in
* 2D graphics
@@ -66,4 +66,15 @@ public class MATRIX {
public String toString() {
return "[MATRIX scale:" + scaleX + "," + scaleY + ", rotate:" + rotateSkew0 + "," + rotateSkew1 + ", translate:" + translateX + "," + translateY + "]";
}
private float toFloat(int i){
return ((float)i)/(1<<16);
}
public Point apply(Point p) {
Point ret = new Point();
ret.x = (int)(p.x * (hasScale?toFloat(scaleX):1) + p.y * (hasRotate?toFloat(rotateSkew1):0) + translateX);
ret.y = (int) (p.x * (hasRotate?toFloat(rotateSkew0):0) + p.y * (hasScale?toFloat(scaleY):1) + translateY);
return ret;
}
}

View File

@@ -46,4 +46,20 @@ public class RGB {
public RGB() {
}
public String toHexRGB(){
String rh=Integer.toHexString(red);
if(rh.length()<2){
rh="0"+rh;
}
String gh=Integer.toHexString(green);
if(gh.length()<2){
gh="0"+gh;
}
String bh=Integer.toHexString(blue);
if(bh.length()<2){
bh="0"+bh;
}
return "#"+rh+gh+bh;
}
}

View File

@@ -41,4 +41,20 @@ public class RGBA {
* Alpha value defining opacity
*/
public int alpha;
public String toHexRGB(){
String rh=Integer.toHexString(red);
if(rh.length()<2){
rh="0"+rh;
}
String gh=Integer.toHexString(green);
if(gh.length()<2){
gh="0"+gh;
}
String bh=Integer.toHexString(blue);
if(bh.length()<2){
bh="0"+bh;
}
return "#"+rh+gh+bh;
}
}

View File

@@ -30,4 +30,17 @@ public class SHAPE {
public int numFillBits;
public int numLineBits;
public List<SHAPERECORD> shapeRecords;
/**
* EXPERIMENTAL
* @param shapeNum
* @return
*/
public String toSVG(int shapeNum){
return SHAPERECORD.shapeToSVG(shapeNum,null, null, numFillBits, numLineBits, shapeRecords);
}
public RECT getBounds(){
return SHAPERECORD.getBounds(shapeRecords);
}
}

View File

@@ -32,4 +32,17 @@ public class SHAPEWITHSTYLE {
public int numFillBits;
public int numLineBits;
public List<SHAPERECORD> shapeRecords;
/**
* EXPERIMENTAL
* @param shapeNum
* @return
*/
public String toSVG(int shapeNum){
return SHAPERECORD.shapeToSVG(shapeNum,fillStyles, lineStyles, numFillBits, numLineBits, shapeRecords);
}
public RECT getBounds(){
return SHAPERECORD.getBounds(shapeRecords);
}
}

View File

@@ -22,7 +22,7 @@ package com.jpexs.asdec.types.shaperecords;
*
* @author JPEXS
*/
public class CurvedEdgeRecord implements SHAPERECORD {
public class CurvedEdgeRecord extends SHAPERECORD {
public int typeFlag = 1;
public int straightFlag = 0;

View File

@@ -22,7 +22,7 @@ package com.jpexs.asdec.types.shaperecords;
*
* @author JPEXS
*/
public class EndShapeRecord implements SHAPERECORD {
public class EndShapeRecord extends SHAPERECORD {
public int typeFlag = 0;
public int endOfShape = 0;

View File

@@ -14,13 +14,250 @@
* 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.asdec.types.shaperecords;
import com.jpexs.asdec.types.FILLSTYLE;
import com.jpexs.asdec.types.FILLSTYLEARRAY;
import com.jpexs.asdec.types.LINESTYLE;
import com.jpexs.asdec.types.LINESTYLE2;
import com.jpexs.asdec.types.LINESTYLEARRAY;
import com.jpexs.asdec.types.RECT;
import com.jpexs.asdec.types.RGB;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
/**
*
* @author JPEXS
*/
public interface SHAPERECORD {
public abstract class SHAPERECORD {
public static float twipToPixel(int twip) {
return ((float) twip) / 20.0f;
}
private static class Path {
public LINESTYLE lineStyle;
public LINESTYLE2 lineStyle2;
public boolean useLineStyle2;
public FILLSTYLE fillStyle0;
public FILLSTYLE fillStyle1;
public String points;
}
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 StyleChangeRecord) {
StyleChangeRecord scr = (StyleChangeRecord) r;
if (scr.stateMoveTo) {
x = scr.moveDeltaX;
y = scr.moveDeltaY;
started = true;
}
}
if (r instanceof StraightEdgeRecord) {
StraightEdgeRecord ser = (StraightEdgeRecord) r;
if (ser.generalLineFlag) {
x += ser.deltaX;
y += ser.deltaY;
started = true;
} else if (ser.vertLineFlag) {
y += ser.deltaY;
started = true;
} else {
x += ser.deltaX;
started = true;
}
}
if (r instanceof CurvedEdgeRecord) {
CurvedEdgeRecord cer = (CurvedEdgeRecord) r;
x += cer.controlDeltaX + cer.anchorDeltaX;
y += cer.controlDeltaY + cer.anchorDeltaY;
started = true;
}
if (x > max_x) {
max_x = x;
}
if (y > max_y) {
max_y = y;
}
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);
}
/**
* EXPERIMENTAL - convert shape to SVG
*
* TODO: Fix fill styles
*
* @param shapeNum
* @param fillStyles
* @param lineStylesList
* @param numFillBits
* @param numLineBits
* @param records
* @return Shape converted to SVG
*/
public static String shapeToSVG(int shapeNum, FILLSTYLEARRAY fillStyles, LINESTYLEARRAY lineStylesList, int numFillBits, int numLineBits, List<SHAPERECORD> records) {
String ret = "";
List<Path> paths = new ArrayList<Path>();
Path path = new Path();
boolean styleChanged = false;
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 StyleChangeRecord) {
StyleChangeRecord scr = (StyleChangeRecord) r;
if (scr.stateNewStyles) {
fillStyles = scr.fillStyles;
lineStylesList = scr.lineStyles;
numFillBits = scr.numFillBits;
numLineBits = scr.numLineBits;
}
if (scr.stateFillStyle0) {
styleChanged = true;
if (scr.fillStyle0 == 0) {
path.fillStyle0 = null;
} else {
path.fillStyle0 = fillStyles.fillStyles[scr.fillStyle0 - 1];
}
}
if (scr.stateFillStyle1) {
styleChanged = true;
if (scr.fillStyle1 == 0) {
path.fillStyle1 = null;
} else {
path.fillStyle1 = fillStyles.fillStyles[scr.fillStyle1 - 1];
}
}
if (scr.stateLineStyle) {
styleChanged = true;
if (shapeNum>=4) {
path.useLineStyle2 = true;
if (scr.lineStyle == 0) {
path.lineStyle2 = null;
} else {
path.lineStyle2 = lineStylesList.lineStyles2[scr.lineStyle - 1];
}
} else {
path.useLineStyle2 = false;
if (scr.lineStyle == 0) {
path.lineStyle = null;
} else {
path.lineStyle = lineStylesList.lineStyles[scr.lineStyle - 1];
}
}
}
if (scr.stateMoveTo) {
ret += "M " + twipToPixel(scr.moveDeltaX) + " " + twipToPixel(scr.moveDeltaY);
x = scr.moveDeltaX;
y = scr.moveDeltaY;
started = true;
}
} else if (styleChanged) {
styleChanged = false;
path.points = ret;
ret = "M " + twipToPixel(x) + " " + twipToPixel(y);
paths.add(path);
path = new Path();
}
if (r instanceof StraightEdgeRecord) {
StraightEdgeRecord ser = (StraightEdgeRecord) r;
if (ser.generalLineFlag) {
ret += "l " + twipToPixel(ser.deltaX) + " " + twipToPixel(ser.deltaY);
x += ser.deltaX;
y += ser.deltaY;
started = true;
} else if (ser.vertLineFlag) {
ret += "v " + twipToPixel(ser.deltaY);
y += ser.deltaY;
started = true;
} else {
ret += "h " + twipToPixel(ser.deltaX);
x += ser.deltaX;
started = true;
}
}
if (r instanceof CurvedEdgeRecord) {
CurvedEdgeRecord cer = (CurvedEdgeRecord) r;
ret += "q " + twipToPixel(cer.controlDeltaX) + " " + twipToPixel(cer.controlDeltaY) + " " + twipToPixel(cer.controlDeltaX + cer.anchorDeltaX) + " " + twipToPixel(cer.controlDeltaY + cer.anchorDeltaY);
x += cer.controlDeltaX + cer.anchorDeltaX;
y += cer.controlDeltaY + cer.anchorDeltaY;
started = true;
}
if (x > max_x) {
max_x = x;
}
if (y > max_y) {
max_y = y;
}
if (started) {
if (y < min_y) {
min_y = y;
}
if (x < min_x) {
min_x = x;
}
}
}
path.points = ret;
paths.add(path);
ret = "";
for (Path p : paths) {
String params = "";
String f = "";
if (p.fillStyle0 != null) {
if (p.fillStyle0.fillStyleType == FILLSTYLE.SOLID) {
f = " fill=\"" + ((shapeNum >= 3) ? p.fillStyle0.colorA.toHexRGB() : p.fillStyle0.color.toHexRGB()) + "\"";
}
} else if (p.fillStyle1 != null) {
if (p.fillStyle1.fillStyleType == FILLSTYLE.SOLID) {
f = " fill=\"" + ((shapeNum >= 3) ? p.fillStyle1.colorA.toHexRGB() : p.fillStyle1.color.toHexRGB()) + "\"";
}
}
if (p.fillStyle0 != null && p.fillStyle1 != null) {
Random rnd = new Random();
f = " fill=\"" + (new RGB(rnd.nextInt(256), rnd.nextInt(256), rnd.nextInt(256))).toHexRGB() + "\"";
}
params += f;
if ((!p.useLineStyle2) && p.lineStyle != null) {
params += " stroke=\"" + ((shapeNum >= 3) ? p.lineStyle.colorA.toHexRGB() : p.lineStyle.color.toHexRGB()) + "\"";
}
if (p.useLineStyle2 && p.lineStyle2 != null) {
params += " stroke-width=\"" + twipToPixel(p.lineStyle2.width) + "\" stroke=\"" + p.lineStyle2.color.toHexRGB() + "\"";
}
ret += "<path" + params + " d=\"" + p.points + "\"/>";
}
ret = "<?xml version='1.0' encoding='UTF-8' ?> \n"
+ "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.0//EN\" \"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\">\n"
+ "<svg width=\"" + (int) Math.ceil(twipToPixel(max_x)) + "\"\n"
+ " height=\"" + (int) Math.ceil(twipToPixel(max_y)) + "\"\n"
+ " viewBox=\"" + (int) Math.ceil(twipToPixel(min_x)) + " " + (int) Math.ceil(twipToPixel(min_y)) + " " + (int) Math.ceil(twipToPixel(max_x)) + " " + (int) Math.ceil(twipToPixel(max_y) + 50) + "\"\n"
+ " xmlns=\"http://www.w3.org/2000/svg\"\n"
+ " xmlns:xlink=\"http://www.w3.org/1999/xlink\">" + ret + ""
+ "</svg>";
return ret;
}
}

View File

@@ -22,7 +22,7 @@ package com.jpexs.asdec.types.shaperecords;
*
* @author JPEXS
*/
public class StraightEdgeRecord implements SHAPERECORD {
public class StraightEdgeRecord extends SHAPERECORD {
public int typeFlag = 1;
public int straightFlag = 1;

View File

@@ -25,7 +25,7 @@ import com.jpexs.asdec.types.LINESTYLEARRAY;
*
* @author JPEXS
*/
public class StyleChangeRecord implements SHAPERECORD {
public class StyleChangeRecord extends SHAPERECORD {
public int typeFlag = 0;
public boolean stateNewStyles;