diff --git a/trunk/src/com/jpexs/decompiler/flash/exporters/ShapeExporter.java b/trunk/src/com/jpexs/decompiler/flash/exporters/ShapeExporter.java
index 9c3f4e041..9cdce23a1 100644
--- a/trunk/src/com/jpexs/decompiler/flash/exporters/ShapeExporter.java
+++ b/trunk/src/com/jpexs/decompiler/flash/exporters/ShapeExporter.java
@@ -24,6 +24,7 @@ import com.jpexs.decompiler.flash.exporters.commonshape.Matrix;
import com.jpexs.decompiler.flash.exporters.commonshape.SVGExporterContext;
import com.jpexs.decompiler.flash.exporters.modes.ShapeExportMode;
import com.jpexs.decompiler.flash.exporters.settings.ShapeExportSettings;
+import com.jpexs.decompiler.flash.exporters.shape.CanvasShapeExporter;
import com.jpexs.decompiler.flash.tags.Tag;
import com.jpexs.decompiler.flash.tags.base.CharacterTag;
import com.jpexs.decompiler.flash.tags.base.ShapeTag;
@@ -68,6 +69,9 @@ public class ShapeExporter {
if (settings.mode == ShapeExportMode.PNG) {
ext = "png";
}
+ if (settings.mode == ShapeExportMode.CANVAS) {
+ ext = "html";
+ }
final File file = new File(outdir + File.separator + characterID + "." + ext);
final int fcharacterID = characterID;
@@ -92,6 +96,13 @@ public class ShapeExporter {
st.toImage(0, 0, 0, null, 0, img, m, new CXFORMWITHALPHA());
ImageIO.write(img.getBufferedImage(), "PNG", new FileOutputStream(file));
break;
+ case CANVAS:
+ try (FileOutputStream fos = new FileOutputStream(file)) {
+ CanvasShapeExporter cse = new CanvasShapeExporter(((Tag)st).getSwf(),st.getShapes(), new CXFORMWITHALPHA());
+ cse.export();
+ fos.write(Utf8Helper.getBytes(cse.getHtml()));
+ }
+ break;
}
}
diff --git a/trunk/src/com/jpexs/decompiler/flash/exporters/modes/ShapeExportMode.java b/trunk/src/com/jpexs/decompiler/flash/exporters/modes/ShapeExportMode.java
index 50002fb90..b34a24510 100644
--- a/trunk/src/com/jpexs/decompiler/flash/exporters/modes/ShapeExportMode.java
+++ b/trunk/src/com/jpexs/decompiler/flash/exporters/modes/ShapeExportMode.java
@@ -23,5 +23,6 @@ package com.jpexs.decompiler.flash.exporters.modes;
public enum ShapeExportMode {
SVG,
- PNG
+ PNG,
+ CANVAS
}
diff --git a/trunk/src/com/jpexs/decompiler/flash/exporters/shape/CanvasShapeExporter.java b/trunk/src/com/jpexs/decompiler/flash/exporters/shape/CanvasShapeExporter.java
new file mode 100644
index 000000000..72f5194ba
--- /dev/null
+++ b/trunk/src/com/jpexs/decompiler/flash/exporters/shape/CanvasShapeExporter.java
@@ -0,0 +1,322 @@
+/*
+ * Copyright (C) 2010-2014 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 .
+ */
+package com.jpexs.decompiler.flash.exporters.shape;
+
+import com.jpexs.decompiler.flash.SWF;
+import com.jpexs.decompiler.flash.exporters.commonshape.Matrix;
+import com.jpexs.decompiler.flash.exporters.commonshape.Point;
+import com.jpexs.decompiler.flash.tags.Tag;
+import com.jpexs.decompiler.flash.tags.base.ImageTag;
+import com.jpexs.decompiler.flash.types.ColorTransform;
+import com.jpexs.decompiler.flash.types.FILLSTYLE;
+import com.jpexs.decompiler.flash.types.GRADRECORD;
+import com.jpexs.decompiler.flash.types.LINESTYLE2;
+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.Helper;
+import com.jpexs.helpers.SerializableImage;
+import java.awt.geom.AffineTransform;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Locale;
+import javax.imageio.ImageIO;
+import javax.xml.bind.DatatypeConverter;
+
+/**
+ *
+ * @author JPEXS, Claus Wahlers
+ */
+public class CanvasShapeExporter extends ShapeExporterBase {
+
+
+ protected String pathData = "";
+ protected String shapeData = "";
+ protected String html = "";
+ protected String strokeData = "";
+ protected String fillData = "";
+ protected double deltaX = 0;
+ protected double deltaY = 0;
+ protected Matrix fillMatrix = null;
+ protected String lastRadColor = null;
+ protected SWF swf;
+
+ public String getHtml() {
+ return html;
+ }
+
+
+
+
+ public CanvasShapeExporter(SWF swf, SHAPE shape, ColorTransform colorTransform) {
+ super(shape, colorTransform);
+ deltaX = -shape.getBounds().Xmin;
+ deltaY = -shape.getBounds().Ymin;
+ this.swf = swf;
+ }
+
+ @Override
+ public void beginShape() {
+ shapeData = "";
+ }
+
+ @Override
+ public void endShape() {
+ RECT r = shape.getBounds();
+ int width = (int)roundPixels20(r.getWidth() / SWF.unitDivisor);
+ int height = (int)roundPixels20(r.getHeight() / SWF.unitDivisor);
+ html = "\r\n" +
+ "\r\n" +
+ "
\r\n" +
+ "\r\n" +
+ "\r\n" +
+ "\r\n" +
+ "\r\n"+
+ "\r\n"+
+ "";
+ }
+
+ @Override
+ public void beginFills() {
+ }
+
+ @Override
+ public void endFills() {
+ }
+
+ @Override
+ public void beginLines() {
+ }
+
+ @Override
+ public void endLines() {
+ finalizePath();
+ }
+
+ @Override
+ public void beginFill(RGB color) {
+ finalizePath();
+ fillData += "ctx.fillStyle=\""+color(color)+"\";\r\n";
+ }
+
+ @Override
+ public void beginGradientFill(int type, GRADRECORD[] gradientRecords, Matrix matrix, int spreadMethod, int interpolationMethod, float focalPointRatio) {
+ finalizePath();
+
+
+ //TODO: repeating (spread mode)
+
+ if(type == FILLSTYLE.LINEAR_GRADIENT){
+ Point start = matrix.transform(new Point(-16384, 0));
+ Point end = matrix.transform(new Point(16384, 0));
+ fillData+="var grd=ctx.createLinearGradient("+Double.toString(start.x/SWF.unitDivisor)+","+Double.toString(start.y/SWF.unitDivisor)+","+Double.toString(end.x/SWF.unitDivisor)+","+Double.toString(end.y/SWF.unitDivisor)+");\r\n";
+ }else{
+ matrix.translateX /= SWF.unitDivisor;
+ matrix.translateY /= SWF.unitDivisor;
+ matrix.scaleX /= SWF.unitDivisor;
+ matrix.scaleY /= SWF.unitDivisor;
+ matrix.rotateSkew0 /= SWF.unitDivisor;
+ matrix.rotateSkew1 /= SWF.unitDivisor;
+ fillMatrix = matrix;
+ fillData+="var grd=ctx.createRadialGradient(0,0,0,0,0,16384);\r\n";
+ }
+ for(GRADRECORD r:gradientRecords){
+ fillData+="grd.addColorStop("+Double.toString(r.ratio / 255.0)+",\""+color(r.color)+"\");\r\n";
+ lastRadColor = color(r.color);
+ }
+ fillData+="ctx.fillStyle = grd;\r\n";
+ }
+
+ private String color(RGB rgb){
+ if(rgb instanceof RGBA){
+ RGBA rgba = (RGBA)rgb;
+ return "rgba("+rgba.red+","+rgba.green+","+rgba.blue+","+rgba.getAlphaFloat()+")";
+ }else{
+ return rgb.toHexRGB();
+ }
+
+ }
+
+ @Override
+ public void beginBitmapFill(int bitmapId, Matrix matrix, boolean repeat, boolean smooth, ColorTransform colorTransform) {
+ finalizePath();
+ ImageTag image = null;
+ for (Tag t : swf.tags) {
+ if (t instanceof ImageTag) {
+ ImageTag i = (ImageTag) t;
+ if (i.getCharacterId() == bitmapId) {
+ image = i;
+ break;
+ }
+ }
+ }
+ if (image != null) {
+ SerializableImage img = image.getImage();
+ if (img != null) {
+ colorTransform.apply(img);
+ String format = image.getImageFormat();
+ InputStream imageStream = image.getImageData();
+ byte[] imageData;
+ if (imageStream != null) {
+ imageData = Helper.readStream(image.getImageData());
+ } else {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ try {
+ ImageIO.write(img.getBufferedImage(), format.toUpperCase(Locale.ENGLISH), baos);
+ } catch (IOException ex) {
+ }
+ imageData = baos.toByteArray();
+ }
+ String base64ImgData = DatatypeConverter.printBase64Binary(imageData);
+ if (matrix != null) {
+ matrix.translateX /= SWF.unitDivisor;
+ matrix.translateY /= SWF.unitDivisor;
+ matrix.scaleX /= SWF.unitDivisor;
+ matrix.scaleY /= SWF.unitDivisor;
+ matrix.rotateSkew0 /= SWF.unitDivisor;
+ matrix.rotateSkew1 /= SWF.unitDivisor;
+ fillMatrix = matrix;
+
+ }
+
+ fillData += "var img = document.createElement(\"img\"); img.src=\"data:image/" + format + ";base64," + base64ImgData+"\";\r\n";
+ fillData += "var pat=ctx.createPattern(img,\"repeat\");\r\n";
+ fillData += "ctx.fillStyle = pat;\r\n";
+ }
+ }
+ }
+
+ @Override
+ public void endFill() {
+ finalizePath();
+ }
+
+ @Override
+ public void lineStyle(double thickness, RGB color, boolean pixelHinting, String scaleMode, int startCaps, int endCaps, int joints, int miterLimit) {
+ finalizePath();
+ thickness /= SWF.unitDivisor;
+
+ strokeData += "ctx.strokeStyle=\""+color(color)+"\";\r\n";
+ strokeData += "ctx.lineWidth="+Double.toString(thickness == 0 ? 1 : thickness)+";\r\n";
+ switch (startCaps) {
+ case LINESTYLE2.NO_CAP:
+ strokeData += "ctx.lineCap=\"butt\";\r\n";
+ break;
+ case LINESTYLE2.SQUARE_CAP:
+ strokeData += "ctx.lineCap=\"square\";\r\n";
+ break;
+ default:
+ strokeData += "ctx.lineCap=\"round\";\r\n";
+ break;
+ }
+ switch (joints) {
+ case LINESTYLE2.BEVEL_JOIN:
+ strokeData += "ctx.lineJoin=\"bevel\";\r\n";
+ break;
+ case LINESTYLE2.ROUND_JOIN:
+ strokeData += "ctx.lineJoin=\"round\";\r\n";
+ break;
+ default:
+ strokeData += "ctx.lineJoin=\"miter\";\r\n";
+ strokeData += "ctx.miterLimit=" + Integer.toString(miterLimit)+";\r\n";
+ break;
+ }
+ }
+
+ @Override
+ public void lineGradientStyle(int type, GRADRECORD[] gradientRecords, Matrix matrix, int spreadMethod, int interpolationMethod, float focalPointRatio) {
+ //TODO
+ }
+
+ @Override
+ public void moveTo(double x, double y) {
+ x += deltaX;
+ y += deltaY;
+ pathData += "ctx.moveTo("
+ + roundPixels20(x / SWF.unitDivisor) + ","
+ + roundPixels20(y / SWF.unitDivisor) + ");\r\n";
+ }
+
+ @Override
+ public void lineTo(double x, double y) {
+ x += deltaX;
+ y += deltaY;
+ pathData += "ctx.lineTo(" + roundPixels20(x / SWF.unitDivisor) + ","
+ + roundPixels20(y / SWF.unitDivisor) + ");\r\n";
+ }
+
+ @Override
+ public void curveTo(double controlX, double controlY, double anchorX, double anchorY) {
+ controlX += deltaX;
+ anchorX += deltaX;
+ controlY += deltaY;
+ anchorY += deltaY;
+ pathData += "ctx.quadraticCurveTo("+roundPixels20(controlX / SWF.unitDivisor) + ","
+ + roundPixels20(controlY / SWF.unitDivisor) + ","
+ + roundPixels20(anchorX / SWF.unitDivisor) + ","
+ + roundPixels20(anchorY / SWF.unitDivisor) + ");\r\n";
+ }
+
+ protected void finalizePath() {
+ if(!"".equals(pathData)){
+ pathData = "ctx.setTransform(1,0,0,1,0,0);\r\nctx.beginPath();\r\n"+pathData+"ctx.closePath();\r\n"+strokeData;
+ if(!"".equals(strokeData)){
+ pathData += "ctx.stroke();\r\n";
+ }
+ if(fillMatrix!=null){
+ if(lastRadColor!=null){
+ pathData+="ctx.fillStyle=\""+lastRadColor+"\";\r\n ctx.fill(\"evenodd\");\r\n";
+ }
+ pathData += "ctx.save();\r\n";
+ pathData += "ctx.clip();\r\n";
+ pathData += "ctx.setTransform("+fillMatrix.scaleX+","+fillMatrix.rotateSkew0+","+fillMatrix.rotateSkew1+","+fillMatrix.scaleY+","+fillMatrix.translateX+","+fillMatrix.translateY+");\r\n";
+ pathData += fillData;
+ pathData += "ctx.fillRect(-16384,-16384,2*16384,2*16384);\r\n";
+ pathData += "ctx.restore();\r\n";
+ shapeData += pathData;
+ }else{
+ if(!"".equals(fillData)){
+ pathData += "ctx.fill(\"evenodd\");\r\n";
+ }
+ shapeData += fillData+pathData;
+ }
+ }
+
+ pathData = "";
+ fillData = "";
+ strokeData = "";
+ fillMatrix = null;
+ lastRadColor = null;
+ }
+
+ protected double roundPixels20(double pixels) {
+ return Math.round(pixels * 100) / 100.0;
+ }
+
+ protected double roundPixels400(double pixels) {
+ return Math.round(pixels * 10000) / 10000.0;
+ }
+}
diff --git a/trunk/src/com/jpexs/decompiler/flash/gui/locales/ExportDialog.properties b/trunk/src/com/jpexs/decompiler/flash/gui/locales/ExportDialog.properties
index 781539ec9..f5183e4ee 100644
--- a/trunk/src/com/jpexs/decompiler/flash/gui/locales/ExportDialog.properties
+++ b/trunk/src/com/jpexs/decompiler/flash/gui/locales/ExportDialog.properties
@@ -15,6 +15,7 @@
shapes = Shapes
shapes.svg = SVG
shapes.png = PNG
+shapes.canvas = HTML5 Canvas
texts = Texts
texts.plain = Plain text