stroke scaling modes for canvas export

This commit is contained in:
Jindra Petřík
2014-09-27 22:22:32 +02:00
parent 5ed73223a5
commit 5dc150ca29
4 changed files with 194 additions and 45 deletions

View File

@@ -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.exporters.morphshape;
import com.jpexs.decompiler.flash.SWF;
@@ -39,6 +40,10 @@ import com.jpexs.helpers.SerializableImage;
*/
public class CanvasMorphShapeExporter extends MorphShapeExporterBase {
protected static final String DRAW_COMMAND_M = "M";
protected static final String DRAW_COMMAND_L = "L";
protected static final String DRAW_COMMAND_Q = "Q";
protected String currentDrawCommand = "";
protected double deltaX = 0;
protected double deltaY = 0;
protected String pathData = "";
@@ -262,8 +267,9 @@ public class CanvasMorphShapeExporter extends MorphShapeExporterBase {
@Override
public void lineStyle(double thickness, double thicknessEnd, RGB color, RGB colorEnd, boolean pixelHinting, String scaleMode, int startCaps, int endCaps, int joints, int miterLimit) {
finalizePath();
finalizePath();
thickness /= unitDivisor;
thickness /= SWF.unitDivisor;
thicknessEnd /= SWF.unitDivisor;
strokeData += "\tvar scaleMode = \""+scaleMode+"\";\r\n";
if (color != null) { //for gradient line fill
strokeData += "\tctx.strokeStyle=" + useRatioColor(color, colorEnd) + ";\r\n";
}
@@ -370,35 +376,43 @@ public class CanvasMorphShapeExporter extends MorphShapeExporterBase {
preStrokeData += "\tlcanvas.height=canvas.height;\r\n";
preStrokeData += "\tvar lctx = lcanvas.getContext(\"2d\");\r\n";
preStrokeData += "\tenhanceContext(lctx);\r\n";
preStrokeData += "\tenhanceContext(lctx);\r\n";
preStrokeData += "\tlctx.applyTransforms(ctx._matrix);\r\n";
preStrokeData += "\tctx = lctx;\r\n";
strokeData = preStrokeData + strokeData;
}
@Override
public void moveTo(double x, double y, double x2, double y2) {
currentDrawCommand = DRAW_COMMAND_M;
pathData += currentDrawCommand + " ";
x += deltaX;
y += deltaY;
x2 += deltaX;
y2 += deltaY;
y2 += deltaY;
pathData += "\tctx.moveTo("
+ useRatioPos(x, x2) + ","
pathData += Helper.doubleStr(x/unitDivisor) + " " + Helper.doubleStr(x2/unitDivisor) + " "
+ Helper.doubleStr(y/unitDivisor) + " " + Helper.doubleStr(y2/unitDivisor) + " ";
}
@Override
public void lineTo(double x, double y, double x2, double y2) {
if (!currentDrawCommand.equals(DRAW_COMMAND_L)) {
currentDrawCommand = DRAW_COMMAND_L;
pathData += currentDrawCommand + " ";
}
x += deltaX;
y += deltaY;
x2 += deltaX;
y2 += deltaY;
y2 += deltaY;
pathData += "\tctx.lineTo("
+ useRatioPos(x, x2) + ","
pathData += Helper.doubleStr(x/unitDivisor) + " " + Helper.doubleStr(x2/unitDivisor) + " "
+ Helper.doubleStr(y/unitDivisor) + " " + Helper.doubleStr(y2/unitDivisor) + " ";
}
@Override
public void curveTo(double controlX, double controlY, double anchorX, double anchorY, double controlX2, double controlY2, double anchorX2, double anchorY2) {
if (!currentDrawCommand.equals(DRAW_COMMAND_Q)) {
currentDrawCommand = DRAW_COMMAND_Q;
pathData += currentDrawCommand + " ";
}
controlX += deltaX;
anchorX += deltaX;
controlY += deltaY;
@@ -409,28 +423,29 @@ public class CanvasMorphShapeExporter extends MorphShapeExporterBase {
controlY2 += deltaY;
anchorY2 += deltaY;
pathData += "\tctx.quadraticCurveTo(" + useRatioPos(controlX, controlX2) + ","
+ useRatioPos(controlY, controlY2) + ","
+ useRatioPos(anchorX, anchorX2) + ","
pathData += Helper.doubleStr(controlX/unitDivisor) + " " + Helper.doubleStr(controlX2/unitDivisor) + " " +
Helper.doubleStr(controlY/unitDivisor) + " " + Helper.doubleStr(controlY2/unitDivisor) + " " +
Helper.doubleStr(anchorX/unitDivisor) + " " + Helper.doubleStr(anchorX2/unitDivisor) + " " +
Helper.doubleStr(anchorY/unitDivisor) + " " + Helper.doubleStr(anchorY2/unitDivisor) + " ";
}
protected void finalizePath() {
if (!"".equals(pathData)) {
if (!"".equals(pathData)) {
String drawStroke = "\tdrawMorphPath(ctx,\"" + pathData.trim() + "\",ratio,true,scaleMode);\r\n";
String drawFill = "\tdrawMorphPath(ctx,\"" + pathData.trim() + "\",ratio,false);\r\n";;
pathData = "";
if (lineFillData != null) {
String preLineFillData = "";
preLineFillData += "\tvar oldctx = ctx;\r\n";
preLineFillData += "\tctx.save();\r\n";
preLineFillData += strokeData;
preLineFillData += strokeData;
preLineFillData += pathData;
preLineFillData += drawStroke;
preLineFillData += "\tvar lfcanvas = document.createElement(\"canvas\");\r\n";
preLineFillData += "\tlfcanvas.width = canvas.width;\r\n";
preLineFillData += "\tlfcanvas.height = canvas.height;\r\n";
preLineFillData += "\tvar lfctx = lfcanvas.getContext(\"2d\");\r\n";
preLineFillData += "\tenhanceContext(lfctx);\r\n";
preLineFillData += "\tenhanceContext(lfctx);\r\n";
preLineFillData += "\tlfctx.applyTransforms(ctx._matrix);\r\n";
preLineFillData += "\tctx = lfctx;";
if (lineLastRadColor != null) {
preLineFillData += "\tctx.fillStyle=" + lineLastRadColor + ";\r\n ctx.fill(\"evenodd\");\r\n";
@@ -479,12 +494,12 @@ public class CanvasMorphShapeExporter extends MorphShapeExporterBase {
shapeData += pathData;
} else {
if (!"".equals(fillData)) {
if (!"".equals(fillData)) {
pathData += drawFill + "\r\n\tctx.fill(\"evenodd\");\r\n";
}
shapeData += fillData + pathData;
}
if (!"".equals(strokeData)) {
if (!"".equals(strokeData)) {
shapeData += drawStroke+"\r\n"; //"\tctx.stroke();\r\n";
} else if (lineFillData != null) {
shapeData += lineFillData;
}

View File

@@ -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.exporters.morphshape;
import com.jpexs.decompiler.flash.exporters.commonshape.Matrix;
@@ -396,9 +397,9 @@ public abstract class MorphShapeExporterBase implements IMorphShapeExporter {
if (lineStyle2.noHScaleFlag && lineStyle2.noVScaleFlag) {
scaleMode = "NONE";
} else if (lineStyle2.noHScaleFlag) {
} else if (lineStyle2.noHScaleFlag) {
scaleMode = "HORIZONTAL";
scaleMode = "VERTICAL";
} else if (lineStyle2.noVScaleFlag) {
scaleMode = "HORIZONTAL";
}
pixelHintingFlag = lineStyle2.pixelHintingFlag;
startCapStyle = lineStyle2.startCapStyle;

View File

@@ -286,7 +286,8 @@ public class CanvasShapeExporter extends ShapeExporterBase {
@Override
public void lineStyle(double thickness, RGB color, boolean pixelHinting, String scaleMode, int startCaps, int endCaps, int joints, int miterLimit) {
finalizePath();
thickness /= unitDivisor;
thickness /= SWF.unitDivisor;
strokeData += "\tvar scaleMode = \""+scaleMode+"\";\r\n";
if (color != null) { //gradient lines have no color
strokeData += "\tctx.strokeStyle=" + color(color) + ";\r\n";
@@ -374,7 +375,7 @@ public class CanvasShapeExporter extends ShapeExporterBase {
preStrokeData += "\tlcanvas.height=canvas.height;\r\n";
preStrokeData += "\tvar lctx = lcanvas.getContext(\"2d\");\r\n";
preStrokeData += "\tenhanceContext(lctx);\r\n";
preStrokeData += "\tlctx.applyTransforms(ctx._matrices);\r\n";
preStrokeData += "\tlctx.applyTransforms(ctx._matrix);\r\n";
preStrokeData += "\tctx = lctx;\r\n";
strokeData = preStrokeData + strokeData;
}
@@ -419,21 +420,21 @@ public class CanvasShapeExporter extends ShapeExporterBase {
protected void finalizePath() {
if (!"".equals(pathData)) {
pathData = "\tdrawPath(ctx,\"" + pathData + "\");\r\n";
String drawStroke = "\tdrawPath(ctx,\"" + pathData.trim() + "\",true,scaleMode);\r\n";
String drawFill = "\tdrawPath(ctx,\"" + pathData.trim() + "\",false);\r\n";;
pathData = "";
if (lineFillData != null) {
String preLineFillData = "";
preLineFillData += "\tvar oldctx = ctx;\r\n";
preLineFillData += "\tctx.save();\r\n";
preLineFillData += strokeData;
preLineFillData += pathData;
preLineFillData += "\tctx.stroke();\r\n";
preLineFillData += drawStroke;
preLineFillData += "\tvar lfcanvas = document.createElement(\"canvas\");\r\n";
preLineFillData += "\tlfcanvas.width = canvas.width;\r\n";
preLineFillData += "\tlfcanvas.height=canvas.height;\r\n";
preLineFillData += "\tvar lfctx = lfcanvas.getContext(\"2d\");\r\n";
preLineFillData += "\tenhanceContext(lfctx);\r\n";
preLineFillData += "\tlfctx.applyTransforms(ctx._matrices);\r\n";
preLineFillData += "\tlfctx.applyTransforms(ctx._matrix);\r\n";
preLineFillData += "\tctx = lfctx;";
if (lineLastRadColor != null) {
preLineFillData += "\tctx.fillStyle=" + lineLastRadColor + ";\r\n\tctx.fill(\"evenodd\");\r\n";
@@ -482,12 +483,12 @@ public class CanvasShapeExporter extends ShapeExporterBase {
shapeData += pathData;
} else {
if (!"".equals(fillData)) {
pathData += "\tctx.fill(\"evenodd\");\r\n";
pathData += drawFill + "\tctx.fill(\"evenodd\");\r\n";
}
shapeData += fillData + pathData;
}
if (!"".equals(strokeData)) {
shapeData += "\tctx.stroke();\r\n";
shapeData += drawStroke +"\r\n";
} else if (lineFillData != null) {
shapeData += lineFillData;
}

View File

@@ -771,17 +771,37 @@ BlendModes.blendCanvas = function(src, dst, result, modeIndex) {
};
function concatMatrix(m1,m2) {
var result= [1,0,0,1,0,0];
var scaleX = 0;
var rotateSkew0 = 1;
var rotateSkew1= 2;
var scaleY = 3;
var translateX = 4;
var translateY = 5;
result[scaleX] = m2[scaleX] * m1[scaleX] + m2[rotateSkew1] * m1[rotateSkew0];
result[rotateSkew0] = m2[rotateSkew0] * m1[scaleX] + m2[scaleY] * m1[rotateSkew0];
result[rotateSkew1] = m2[scaleX] * m1[rotateSkew1] + m2[rotateSkew1] * m1[scaleY];
result[scaleY] = m2[rotateSkew0] * m1[rotateSkew1] + m2[scaleY] * m1[scaleY];
result[translateX] = m2[scaleX] * m1[translateX] + m2[rotateSkew1] * m1[translateY] + m2[translateX];
result[translateY] = m2[rotateSkew0] * m1[translateX] + m2[scaleY] * m1[translateY] + m2[translateY];
return result;
}
var enhanceContext = function(context) {
var m = [1, 0, 0, 1, 0, 0];
context._matrices = [m];
context._matrix = m;
//the stack of saved matrices
context._savedMatrices = [[m]];
context._savedMatrices = [m]; //[[m]];
var super_ = context.__proto__;
context.__proto__ = ({
save: function() {
this._savedMatrices.push(this._matrices.slice());
this._savedMatrices.push(this._matrix); //.slice()
super_.save.call(this);
},
//if the stack of matrices we're managing doesn't have a saved matrix,
@@ -790,7 +810,7 @@ var enhanceContext = function(context) {
if (this._savedMatrices.length == 0)
return;
super_.restore.call(this);
this._matrices = this._savedMatrices.pop();
this._matrix = this._savedMatrices.pop();
},
scale: function(x, y) {
super_.scale.call(this, x, y);
@@ -802,22 +822,24 @@ var enhanceContext = function(context) {
super_.translate.call(this, x, y);
},
transform: function(a, b, c, d, e, f) {
this._matrices.push([a, b, c, d, e, f]);
this._matrix = concatMatrix(this._matrix,[a,b,c,d,e,f]);
super_.transform.call(this, a, b, c, d, e, f);
},
setTransform: function(a, b, c, d, e, f) {
this._matrices = [];
this._matrices.push([a, b, c, d, e, f]);
this._matrix = [a, b, c, d, e, f];
super_.setTransform.call(this, a, b, c, d, e, f);
},
resetTransform: function() {
super_.resetTransform.call(this);
},
applyTransforms: function(m) {
this.setTransform(1, 0, 0, 1, 0, 0);
for (var i = 0; i < m.length; i++) {
this.transform(m[i][0], m[i][1], m[i][2], m[i][3], m[i][4], m[i][5]);
}
this.setTransform(m[0], m[1], m[2], m[3], m[4], m[5])
},
applyTransformToPoint: function(p){
var ret = {};
ret.x = this._matrix[0]*p.x + this._matrix[1]*p.y + this._matrix[4];
ret.y = this._matrix[2]*p.x + this._matrix[3]*p.y + this._matrix[5];
return ret;
},
__proto__: super_
});
@@ -964,10 +986,116 @@ function stopDrag(e) {
}
function drawPath(ctx, p) {
ctx.beginPath();
function drawMorphPath(ctx, p, ratio, doStroke, scaleMode){
var parts = p.split(" ");
var len = parts.length;
if(doStroke){
for (var i = 0; i < len; i++) {
switch(parts[i]){
case '':
break;
case 'L':
case 'M':
case 'Q':
break;
default:
var k = ctx.applyTransformToPoint({x:parts[i],y:parts[i+2]}); parts[i] = k.x; parts[i+2] = k.y;
k = ctx.applyTransformToPoint({x:parts[i+1],y:parts[i+3]}); parts[i+1] = k.x; parts[i+3] = k.y;
i+=3;
}
}
switch(scaleMode){
case "NONE":
break;
case "NORMAL":
ctx.lineWidth*=20*Math.max(ctx._matrix[0],ctx._matrix[3]);
break;
case "VERTICAL":
ctx.lineWidth*=20*ctx._matrix[3];
break;
case "HORIZONTAL":
ctx.lineWidth*=20*ctx._matrix[0];
break;
}
ctx.save();
ctx.setTransform(1,0,0,1,0,0);
}
ctx.beginPath();
var drawCommand = "";
for (var i = 0; i < len; i++) {
switch (parts[i]) {
case 'L':
case 'M':
case 'Q':
drawCommand = parts[i];
break;
default:
switch (drawCommand) {
case 'L':
ctx.lineTo(useRatio(parts[i],parts[i+1],ratio), useRatio(parts[i + 2],parts[i + 3],ratio));
i += 3;
break;
case 'M':
ctx.moveTo(useRatio(parts[i],parts[i+1],ratio), useRatio(parts[i + 2],parts[i + 3],ratio));
i += 3;
break;
case 'Q':
ctx.quadraticCurveTo(useRatio(parts[i],parts[i+1],ratio),useRatio(parts[i+2],parts[i+3],ratio),
useRatio(parts[i+4],parts[i+5],ratio),useRatio(parts[i+6],parts[i+7],ratio));
i += 7;
break;
}
break;
}
}
if(doStroke){
ctx.stroke();
ctx.restore();
}
}
function useRatio(v1,v2,ratio){
return v1*1+(v2-v1)*ratio/65535;
}
function drawPath(ctx, p, doStroke, scaleMode) {
var parts = p.split(" ");
var len = parts.length;
if(doStroke){
for (var i = 0; i < len; i++) {
switch(parts[i]){
case 'L':
case 'M':
case 'Q':
break;
default:
var k = ctx.applyTransformToPoint({x:parts[i],y:parts[i+1]});
parts[i] = k.x;
parts[i+1] = k.y;
i++;
}
}
switch(scaleMode){
case "NONE":
break;
case "NORMAL":
ctx.lineWidth*=20*Math.max(ctx._matrix[0],ctx._matrix[3]);
break;
case "VERTICAL":
ctx.lineWidth*=20*ctx._matrix[3];
break;
case "HORIZONTAL":
ctx.lineWidth*=20*ctx._matrix[0];
break;
}
ctx.save();
ctx.setTransform(1,0,0,1,0,0);
}
ctx.beginPath();
var drawCommand = "";
for (var i = 0; i < len; i++) {
switch (parts[i]) {
@@ -994,4 +1122,8 @@ function drawPath(ctx, p) {
break;
}
}
if(doStroke){
ctx.stroke();
ctx.restore();
}
}