blob: 283042bbe9e0dae2f211223f3b08d2b24738ba64 [file] [log] [blame]
function makeMatrix(xx, xy, yx, yy, tx, ty) {
const m = new DOMMatrix();
m.a = xx;
m.b = xy;
m.c = yx;
m.d = yy;
m.e = tx;
m.f = ty;
return m;
}
function initializeCanvas2DRenderer() {
const RenderPaintStyle = Module.RenderPaintStyle;
const FillRule = Module.FillRule;
const RenderPath = Module.RenderPath;
const RenderImage = Module.RenderImage;
const RenderPaint = Module.RenderPaint;
const Renderer = Module.Renderer;
const StrokeCap = Module.StrokeCap;
const StrokeJoin = Module.StrokeJoin;
const BlendMode = Module.BlendMode;
const fill = RenderPaintStyle.fill;
const stroke = RenderPaintStyle.stroke;
const evenOdd = FillRule.evenOdd;
const nonZero = FillRule.nonZero;
function _canvasBlend(value) {
switch (value) {
case BlendMode.srcOver:
return "source-over";
case BlendMode.screen:
return "screen";
case BlendMode.overlay:
return "overlay";
case BlendMode.darken:
return "darken";
case BlendMode.lighten:
return "lighten";
case BlendMode.colorDodge:
return "color-dodge";
case BlendMode.colorBurn:
return "color-burn";
case BlendMode.hardLight:
return "hard-light";
case BlendMode.softLight:
return "soft-light";
case BlendMode.difference:
return "difference";
case BlendMode.exclusion:
return "exclusion";
case BlendMode.multiply:
return "multiply";
case BlendMode.hue:
return "hue";
case BlendMode.saturation:
return "saturation";
case BlendMode.color:
return "color";
case BlendMode.luminosity:
return "luminosity";
}
}
var CanvasRenderPath = RenderPath.extend("CanvasRenderPath", {
"__construct": function () {
this["__parent"]["__construct"].call(this);
this._path2D = new Path2D();
},
"rewind": function () {
this._path2D = new Path2D();
},
"addPath": function (path, xx, xy, yx, yy, tx, ty) {
this._path2D["addPath"](path._path2D, makeMatrix(xx, xy, yx, yy, tx, ty));
},
"fillRule": function (fillRule) {
this._fillRule = fillRule;
},
"moveTo": function (x, y) {
this._path2D["moveTo"](x, y);
},
"lineTo": function (x, y) {
this._path2D["lineTo"](x, y);
},
"cubicTo": function (ox, oy, ix, iy, x, y) {
this._path2D["bezierCurveTo"](ox, oy, ix, iy, x, y);
},
"close": function () {
this._path2D["closePath"]();
},
});
function _colorStyle(value) {
return (
"rgba(" +
((0x00ff0000 & value) >>> 16) +
"," +
((0x0000ff00 & value) >>> 8) +
"," +
((0x000000ff & value) >>> 0) +
"," +
((0xff000000 & value) >>> 24) / 0xff +
")"
);
}
var CanvasRenderPaint = RenderPaint.extend("CanvasRenderPaint", {
"__construct": function () {
this["__parent"]["__construct"].call(this);
this._value = "rgba(0, 0, 0, 1)";
this._thickness = 1;
this._join = "bevel";
this._cap = "round";
this._style = RenderPaintStyle.fill;
this._blend = BlendMode.srcOver;
this._gradient = null;
},
"color": function (value) {
this._value = _colorStyle(value);
},
"thickness": function (value) {
this._thickness = value;
},
"join": function (value) {
switch (value) {
case StrokeJoin.miter:
this._join = "miter";
break;
case StrokeJoin.round:
this._join = "round";
break;
case StrokeJoin.bevel:
this._join = "bevel";
break;
}
},
"cap": function (value) {
switch (value) {
case StrokeCap.butt:
this._cap = "butt";
break;
case StrokeCap.round:
this._cap = "round";
break;
case StrokeCap.square:
this._cap = "square";
break;
}
},
"style": function (value) {
this._style = value;
},
"blendMode": function (value) {
this._blend = _canvasBlend(value);
},
"clearGradient": function () {
this._gradient = null;
},
"linearGradient": function (sx, sy, ex, ey) {
this._gradient = {
sx,
sy,
ex,
ey,
stops: [],
};
},
"radialGradient": function (sx, sy, ex, ey) {
this._gradient = {
sx,
sy,
ex,
ey,
stops: [],
isRadial: true,
};
},
"addStop": function (color, stop) {
this._gradient.stops.push({
color,
stop,
});
},
"completeGradient": function () {},
// https://github.com/rive-app/rive/issues/3816: The fill rule (and only the fill rule) on a
// path object can mutate before flush(). To work around this, we capture the fill rule at
// draw time. It's a little awkward having a fill rule here even though we might be a
// stroke, so we probably want to rework this.
"draw": function (ctx, path2D, fillRule) {
let _style = this._style;
let _value = this._value;
let _gradient = this._gradient;
let _blend = this._blend;
ctx["globalCompositeOperation"] = _blend;
if (_gradient != null) {
const sx = _gradient.sx;
const sy = _gradient.sy;
const ex = _gradient.ex;
const ey = _gradient.ey;
const stops = _gradient.stops;
if (_gradient.isRadial) {
var dx = ex - sx;
var dy = ey - sy;
var radius = Math.sqrt(dx * dx + dy * dy);
_value = ctx["createRadialGradient"](sx, sy, 0, sx, sy, radius);
} else {
_value = ctx["createLinearGradient"](sx, sy, ex, ey);
}
for (let i = 0, l = stops["length"]; i < l; i++) {
const value = stops[i];
const stop = value.stop;
const color = value.color;
_value["addColorStop"](stop, _colorStyle(color));
}
this._value = _value;
this._gradient = null;
}
switch (_style) {
case stroke:
ctx["strokeStyle"] = _value;
ctx["lineWidth"] = this._thickness;
ctx["lineCap"] = this._cap;
ctx["lineJoin"] = this._join;
ctx["stroke"](path2D);
break;
case fill:
ctx["fillStyle"] = _value;
ctx["fill"](path2D, fillRule);
break;
}
},
});
const INITIAL_ATLAS_SIZE = 512;
let _rectanizer = null;
let _atlasMeshList = [];
let _atlasNumTotalVertexFloats = 0;
let _atlasNumTotalIndices = 0;
var CanvasRenderer = (Module.CanvasRenderer = Renderer.extend("Renderer", {
"__construct": function (canvas) {
this["__parent"]["__construct"].call(this);
this._ctx = canvas["getContext"]("2d");
this._canvas = canvas;
},
"save": function () {
this._ctx["save"]();
},
"restore": function () {
this._ctx["restore"]();
},
"transform": function (xx, xy, yx, yy, tx, ty) {
this._ctx["transform"](xx, xy, yx, yy, tx, ty);
},
"rotate": function (angle) {
const sin = Math.sin(angle);
const cos = Math.cos(angle);
this.transform(cos, sin, -sin, cos, 0, 0);
},
"_drawPath": function (path, paint) {
const fillRule = path._fillRule === evenOdd ? "evenodd" : "nonzero";
paint["draw"](this._ctx, path._path2D, fillRule);
},
"_drawImage": function (image, blend, opacity) {},
"_getMatrix": function (out) {},
"_drawImageMesh": function (
image,
blend,
opacity,
vtx,
uv,
indices,
meshMinX,
meshMinY,
meshMaxX,
meshMaxY
) {},
"_clipPath": function (path) {
const fillRule = path._fillRule === evenOdd ? "evenodd" : "nonzero";
this._ctx["clip"](path._path2D, fillRule);
},
"clear": function () {
this._ctx["clearRect"](
0,
0,
this._canvas["width"],
this._canvas["height"]
);
},
"flush": function () {},
"translate": function (x, y) {
this.transform(1, 0, 0, 1, x, y);
},
}));
Module.renderFactory = {
makeRenderPaint: function () {
return new CanvasRenderPaint();
},
makeRenderPath: function () {
return new CanvasRenderPath();
},
makeRenderImage: function () {
return null; //new CanvasRenderImage();
},
makeRenderer: function (canvasIDPtr, canvasIDLength) {
const utf8 = new Uint8Array(canvasIDLength);
utf8.set(
Module.HEAPU8.subarray(canvasIDPtr, canvasIDPtr + canvasIDLength)
);
idString = new TextDecoder().decode(utf8);
return new CanvasRenderer(document.getElementById(idString));
},
};
let load = Module["load"];
let loadContext = null;
Module["load"] = function (bytes) {
return new Promise(function (resolve, reject) {
let result = null;
loadContext = {
total: 0,
loaded: 0,
ready: function () {
resolve(result);
},
};
result = load(bytes);
if (loadContext.total == 0) {
resolve(result);
}
});
};
}