[canvaskit] Add gradients and clips
Refactors things inside to support both color and gradient as _fillStyle/_strokeStyle. Bug: skia: Change-Id: I364ceb7d55c41e11161d5577dcd1611a592bbc29 Reviewed-on: https://skia-review.googlesource.com/c/173421 Reviewed-by: Kevin Lubick <kjlubick@google.com>
This commit is contained in:
parent
62f64b5a05
commit
eb2f6b0adb
@ -28,6 +28,8 @@
|
||||
<canvas id=api4_c width=300 height=300></canvas>
|
||||
<img id=api5 width=300 height=300>
|
||||
<canvas id=api5_c width=300 height=300></canvas>
|
||||
<img id=api6 width=300 height=300>
|
||||
<canvas id=api6_c width=300 height=300></canvas>
|
||||
|
||||
<h2> CanvasKit draws Paths to the browser</h2>
|
||||
<canvas id=vertex1 width=300 height=300></canvas>
|
||||
@ -95,6 +97,7 @@
|
||||
CanvasAPI3(CanvasKit);
|
||||
CanvasAPI4(CanvasKit);
|
||||
CanvasAPI5(CanvasKit);
|
||||
CanvasAPI6(CanvasKit);
|
||||
|
||||
VertexAPI1(CanvasKit);
|
||||
VertexAPI2(CanvasKit, bonesImage);
|
||||
@ -673,6 +676,54 @@
|
||||
document.getElementById('api5').src = skcanvas.toDataURL();
|
||||
}
|
||||
|
||||
function CanvasAPI6(CanvasKit) {
|
||||
let skcanvas = CanvasKit.MakeCanvas(600, 600);
|
||||
let realCanvas = document.getElementById('api6_c');
|
||||
realCanvas.width = 600;
|
||||
realCanvas.height = 600;
|
||||
|
||||
for (let canvas of [skcanvas, realCanvas]) {
|
||||
let ctx = canvas.getContext('2d');
|
||||
|
||||
var rgradient = ctx.createRadialGradient(200, 300, 10, 100, 100, 300);
|
||||
|
||||
// Add three color stops
|
||||
rgradient.addColorStop(0, 'red');
|
||||
rgradient.addColorStop(0.7, 'white');
|
||||
rgradient.addColorStop(1, 'blue');
|
||||
|
||||
ctx.fillStyle = rgradient;
|
||||
ctx.globalAlpha = 0.7;
|
||||
ctx.fillRect(0, 0, 600, 600);
|
||||
ctx.globalAlpha = 0.95;
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.arc(300, 100, 90, 0, Math.PI*1.66);
|
||||
ctx.closePath();
|
||||
ctx.strokeStyle = 'yellow';
|
||||
ctx.lineWidth = 5;
|
||||
ctx.stroke();
|
||||
ctx.save();
|
||||
ctx.clip();
|
||||
|
||||
var lgradient = ctx.createLinearGradient(200, 20, 420, 40);
|
||||
|
||||
// Add three color stops
|
||||
lgradient.addColorStop(0, 'green');
|
||||
lgradient.addColorStop(0.5, 'cyan');
|
||||
lgradient.addColorStop(1, 'orange');
|
||||
|
||||
ctx.fillStyle = lgradient;
|
||||
|
||||
ctx.fillRect(200, 30, 200, 300);
|
||||
|
||||
ctx.restore();
|
||||
ctx.fillRect(550, 550, 40, 40);
|
||||
|
||||
}
|
||||
document.getElementById('api6').src = skcanvas.toDataURL();
|
||||
}
|
||||
|
||||
function NimaExample(CanvasKit, nimaFile, nimaTexture) {
|
||||
if (!CanvasKit || !nimaFile || !nimaTexture) {
|
||||
return;
|
||||
|
@ -455,6 +455,7 @@ EMSCRIPTEN_BINDINGS(Skia) {
|
||||
|
||||
return SkImageShader::Make(img, tx, ty, nullptr);
|
||||
}), allow_raw_pointers());
|
||||
// Allow localMatrix to be optional, so we have 2 declarations of these gradients
|
||||
function("_MakeLinearGradientShader", optional_override([](SkPoint start, SkPoint end,
|
||||
uintptr_t /* SkColor* */ cPtr, uintptr_t /* SkScalar* */ pPtr,
|
||||
int count, SkShader::TileMode mode, uint32_t flags)->sk_sp<SkShader> {
|
||||
@ -502,6 +503,35 @@ EMSCRIPTEN_BINDINGS(Skia) {
|
||||
return SkGradientShader::MakeRadial(center, radius, colors, positions, count,
|
||||
mode, flags, &localMatrix);
|
||||
}), allow_raw_pointers());
|
||||
function("_MakeTwoPointConicalGradientShader", optional_override([](
|
||||
SkPoint start, SkScalar startRadius,
|
||||
SkPoint end, SkScalar endRadius,
|
||||
uintptr_t /* SkColor* */ cPtr, uintptr_t /* SkScalar* */ pPtr,
|
||||
int count, SkShader::TileMode mode, uint32_t flags)->sk_sp<SkShader> {
|
||||
// See comment above for uintptr_t explanation
|
||||
const SkColor* colors = reinterpret_cast<const SkColor*> (cPtr);
|
||||
const SkScalar* positions = reinterpret_cast<const SkScalar*>(pPtr);
|
||||
|
||||
return SkGradientShader::MakeTwoPointConical(start, startRadius, end, endRadius,
|
||||
colors, positions, count, mode,
|
||||
flags, nullptr);
|
||||
}), allow_raw_pointers());
|
||||
function("_MakeTwoPointConicalGradientShader", optional_override([](
|
||||
SkPoint start, SkScalar startRadius,
|
||||
SkPoint end, SkScalar endRadius,
|
||||
uintptr_t /* SkColor* */ cPtr, uintptr_t /* SkScalar* */ pPtr,
|
||||
int count, SkShader::TileMode mode, uint32_t flags,
|
||||
const SimpleMatrix& lm)->sk_sp<SkShader> {
|
||||
// See comment above for uintptr_t explanation
|
||||
const SkColor* colors = reinterpret_cast<const SkColor*> (cPtr);
|
||||
const SkScalar* positions = reinterpret_cast<const SkScalar*>(pPtr);
|
||||
|
||||
SkMatrix localMatrix = toSkMatrix(lm);
|
||||
return SkGradientShader::MakeTwoPointConical(start, startRadius, end, endRadius,
|
||||
colors, positions, count, mode,
|
||||
flags, &localMatrix);
|
||||
}), allow_raw_pointers());
|
||||
|
||||
function("_MakeSkVertices", optional_override([](SkVertices::VertexMode mode, int vertexCount,
|
||||
uintptr_t /* SkPoint* */ pPtr, uintptr_t /* SkPoint* */ tPtr,
|
||||
uintptr_t /* SkColor* */ cPtr,
|
||||
@ -526,6 +556,7 @@ EMSCRIPTEN_BINDINGS(Skia) {
|
||||
// Add a optional_override to change it out.
|
||||
self.clear(SkColor(color));
|
||||
}))
|
||||
.function("clipPath", select_overload<void (const SkPath&, SkClipOp, bool)>(&SkCanvas::clipPath))
|
||||
.function("drawPaint", &SkCanvas::drawPaint)
|
||||
.function("drawPath", &SkCanvas::drawPath)
|
||||
.function("drawRect", &SkCanvas::drawRect)
|
||||
@ -546,6 +577,7 @@ EMSCRIPTEN_BINDINGS(Skia) {
|
||||
}))
|
||||
.function("drawVertices", select_overload<void (const sk_sp<SkVertices>&, SkBlendMode, const SkPaint&)>(&SkCanvas::drawVertices))
|
||||
.function("flush", &SkCanvas::flush)
|
||||
.function("restore", &SkCanvas::restore)
|
||||
.function("rotate", select_overload<void (SkScalar, SkScalar, SkScalar)>(&SkCanvas::rotate))
|
||||
.function("save", &SkCanvas::save)
|
||||
.function("scale", &SkCanvas::scale)
|
||||
@ -728,6 +760,10 @@ EMSCRIPTEN_BINDINGS(Skia) {
|
||||
.value("Outer", SkBlurStyle::kOuter_SkBlurStyle)
|
||||
.value("Inner", SkBlurStyle::kInner_SkBlurStyle);
|
||||
|
||||
enum_<SkClipOp>("ClipOp")
|
||||
.value("Difference", SkClipOp::kDifference)
|
||||
.value("Intersect", SkClipOp::kIntersect);
|
||||
|
||||
enum_<SkPath::FillType>("FillType")
|
||||
.value("Winding", SkPath::FillType::kWinding_FillType)
|
||||
.value("EvenOdd", SkPath::FillType::kEvenOdd_FillType)
|
||||
|
@ -31,6 +31,7 @@ var CanvasKit = {
|
||||
MakeCanvas: function() {},
|
||||
MakeCanvasSurface: function() {},
|
||||
MakeImageShader: function() {},
|
||||
/** @return {LinearCanvasGradient} */
|
||||
MakeLinearGradientShader: function() {},
|
||||
MakeNimaActor: function() {},
|
||||
MakeRadialGradientShader: function() {},
|
||||
@ -38,6 +39,8 @@ var CanvasKit = {
|
||||
MakeSkDashPathEffect: function() {},
|
||||
MakeSkVertices: function() {},
|
||||
MakeSurface: function() {},
|
||||
/** @return {RadialCanvasGradient} */
|
||||
MakeTwoPointConicalGradientShader: function() {},
|
||||
MakeWebGLCanvasSurface: function() {},
|
||||
currentContext: function() {},
|
||||
getColorComponents: function() {},
|
||||
@ -54,6 +57,7 @@ var CanvasKit = {
|
||||
_MakeRadialGradientShader: function() {},
|
||||
_MakeSkDashPathEffect: function() {},
|
||||
_MakeSkVertices: function() {},
|
||||
_MakeTwoPointConicalGradientShader: function() {},
|
||||
_getRasterN32PremulSurface: function() {},
|
||||
_getWebGLSurface: function() {},
|
||||
|
||||
@ -78,6 +82,7 @@ var CanvasKit = {
|
||||
SkCanvas: {
|
||||
// public API (from C++ bindings)
|
||||
clear: function() {},
|
||||
clipPath: function() {},
|
||||
drawPaint: function() {},
|
||||
drawPath: function() {},
|
||||
drawRect: function() {},
|
||||
@ -85,6 +90,7 @@ var CanvasKit = {
|
||||
drawText: function() {},
|
||||
drawVertices: function() {},
|
||||
flush: function() {},
|
||||
restore: function() {},
|
||||
rotate: function() {},
|
||||
save: function() {},
|
||||
scale: function() {},
|
||||
@ -261,6 +267,11 @@ var CanvasKit = {
|
||||
Inner: {},
|
||||
},
|
||||
|
||||
ClipOp: {
|
||||
Difference: {},
|
||||
Intersect: {},
|
||||
},
|
||||
|
||||
FillType: {
|
||||
Winding: {},
|
||||
EvenOdd: {},
|
||||
@ -279,6 +290,14 @@ var CanvasKit = {
|
||||
StrokeAndFill: {},
|
||||
},
|
||||
|
||||
PathOp: {
|
||||
Difference: {},
|
||||
Intersect: {},
|
||||
Union: {},
|
||||
XOR: {},
|
||||
ReverseDifference: {},
|
||||
},
|
||||
|
||||
StrokeCap: {
|
||||
Butt: {},
|
||||
Round: {},
|
||||
@ -291,6 +310,18 @@ var CanvasKit = {
|
||||
Bevel: {},
|
||||
},
|
||||
|
||||
TileMode: {
|
||||
Clamp: {},
|
||||
Repeat: {},
|
||||
Mirror: {},
|
||||
},
|
||||
|
||||
VertexMode: {
|
||||
Triangles: {},
|
||||
TrianglesStrip: {},
|
||||
TriangleFan: {},
|
||||
},
|
||||
|
||||
// Things Enscriptem adds for us
|
||||
|
||||
/** Represents the heap of the WASM code
|
||||
@ -356,6 +387,7 @@ StrokeOpts.prototype.miter_limit;
|
||||
StrokeOpts.prototype.cap;
|
||||
StrokeOpts.prototype.join;
|
||||
|
||||
// Define everything created in the canvas2d spec here
|
||||
var HTMLCanvas = {};
|
||||
HTMLCanvas.prototype.getContext = function() {};
|
||||
HTMLCanvas.prototype.toDataURL = function() {};
|
||||
@ -369,7 +401,10 @@ CanvasRenderingContext2D.prototype.beginPath = function() {};
|
||||
CanvasRenderingContext2D.prototype.bezierCurveTo = function() {};
|
||||
CanvasRenderingContext2D.prototype.clearHitRegions = function() {};
|
||||
CanvasRenderingContext2D.prototype.clearRect = function() {};
|
||||
CanvasRenderingContext2D.prototype.clip = function() {};
|
||||
CanvasRenderingContext2D.prototype.closePath = function() {};
|
||||
CanvasRenderingContext2D.prototype.createLinearGradient = function() {};
|
||||
CanvasRenderingContext2D.prototype.createRadialGradient = function() {};
|
||||
CanvasRenderingContext2D.prototype.drawFocusIfNeeded = function() {};
|
||||
CanvasRenderingContext2D.prototype.ellipse = function() {};
|
||||
CanvasRenderingContext2D.prototype.fill = function() {};
|
||||
@ -396,6 +431,11 @@ CanvasRenderingContext2D.prototype.strokeText = function() {};
|
||||
CanvasRenderingContext2D.prototype.transform = function() {};
|
||||
CanvasRenderingContext2D.prototype.translate = function() {};
|
||||
|
||||
var LinearCanvasGradient = {};
|
||||
LinearCanvasGradient.prototype.addColorStop = function() {};
|
||||
var RadialCanvasGradient = {};
|
||||
RadialCanvasGradient.prototype.addColorStop = function() {};
|
||||
|
||||
// Not sure why this is needed - might be a bug in emsdk that this isn't properly declared.
|
||||
function loadWebAssemblyModule() {};
|
||||
|
||||
|
@ -66,6 +66,156 @@
|
||||
}
|
||||
}
|
||||
|
||||
function LinearCanvasGradient(x1, y1, x2, y2) {
|
||||
this._shader = null;
|
||||
this._colors = [];
|
||||
this._pos = [];
|
||||
|
||||
this.addColorStop = function(offset, color) {
|
||||
if (offset < 0 || offset > 1 || !isFinite(offset)) {
|
||||
throw 'offset must be between 0 and 1 inclusively';
|
||||
}
|
||||
|
||||
color = parseColor(color);
|
||||
// From the spec: If multiple stops are added at the same offset on a
|
||||
// gradient, then they must be placed in the order added, with the first
|
||||
// one closest to the start of the gradient, and each subsequent one
|
||||
// infinitesimally further along towards the end point (in effect
|
||||
// causing all but the first and last stop added at each point to be
|
||||
// ignored).
|
||||
// To implement that, if an offset is already in the list,
|
||||
// we just overwrite its color (since the user can't remove Color stops
|
||||
// after the fact).
|
||||
var idx = this._pos.indexOf(offset);
|
||||
if (idx !== -1) {
|
||||
this._colors[idx] = color;
|
||||
} else {
|
||||
// insert it in sorted order
|
||||
for (idx = 0; idx < this._pos.length; idx++) {
|
||||
if (this._pos[idx] > offset) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
this._pos .splice(idx, 0, offset);
|
||||
this._colors.splice(idx, 0, color);
|
||||
}
|
||||
}
|
||||
|
||||
this._copy = function() {
|
||||
var lcg = new LinearCanvasGradient(x1, y1, x2, y2);
|
||||
lcg._colors = this._colors.slice();
|
||||
lcg._pos = this._pos.slice();
|
||||
return lcg;
|
||||
}
|
||||
|
||||
this._dispose = function() {
|
||||
if (this._shader) {
|
||||
this._shader.delete();
|
||||
this._shader = null;
|
||||
}
|
||||
}
|
||||
|
||||
this._getShader = function(currentTransform, globalAlpha) {
|
||||
// From the spec: "The points in the linear gradient must be transformed
|
||||
// as described by the current transformation matrix when rendering."
|
||||
var pts = [x1, y1, x2, y2];
|
||||
CanvasKit.SkMatrix.mapPoints(currentTransform, pts);
|
||||
var sx1 = pts[0];
|
||||
var sy1 = pts[1];
|
||||
var sx2 = pts[2];
|
||||
var sy2 = pts[3];
|
||||
|
||||
this._dispose();
|
||||
var colors = this._colors.map(function(c) {
|
||||
return CanvasKit.multiplyByAlpha(c, globalAlpha);
|
||||
});
|
||||
this._shader = CanvasKit.MakeLinearGradientShader([sx1, sy1], [sx2, sy2],
|
||||
colors, this._pos, CanvasKit.TileMode.Clamp);
|
||||
return this._shader;
|
||||
}
|
||||
}
|
||||
|
||||
// Note, Skia has a different notion of a "radial" gradient.
|
||||
// Skia has a twoPointConical gradient that is the same as the
|
||||
// canvas's RadialGradient.
|
||||
function RadialCanvasGradient(x1, y1, r1, x2, y2, r2) {
|
||||
this._shader = null;
|
||||
this._colors = [];
|
||||
this._pos = [];
|
||||
|
||||
this.addColorStop = function(offset, color) {
|
||||
if (offset < 0 || offset > 1 || !isFinite(offset)) {
|
||||
throw 'offset must be between 0 and 1 inclusively';
|
||||
}
|
||||
|
||||
color = parseColor(color);
|
||||
// From the spec: If multiple stops are added at the same offset on a
|
||||
// gradient, then they must be placed in the order added, with the first
|
||||
// one closest to the start of the gradient, and each subsequent one
|
||||
// infinitesimally further along towards the end point (in effect
|
||||
// causing all but the first and last stop added at each point to be
|
||||
// ignored).
|
||||
// To implement that, if an offset is already in the list,
|
||||
// we just overwrite its color (since the user can't remove Color stops
|
||||
// after the fact).
|
||||
var idx = this._pos.indexOf(offset);
|
||||
if (idx !== -1) {
|
||||
this._colors[idx] = color;
|
||||
} else {
|
||||
// insert it in sorted order
|
||||
for (idx = 0; idx < this._pos.length; idx++) {
|
||||
if (this._pos[idx] > offset) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
this._pos .splice(idx, 0, offset);
|
||||
this._colors.splice(idx, 0, color);
|
||||
}
|
||||
}
|
||||
|
||||
this._copy = function() {
|
||||
var rcg = new RadialCanvasGradient(x1, y1, r1, x2, y2, r2);
|
||||
rcg._colors = this._colors.slice();
|
||||
rcg._pos = this._pos.slice();
|
||||
return rcg;
|
||||
}
|
||||
|
||||
this._dispose = function() {
|
||||
if (this._shader) {
|
||||
this._shader.delete();
|
||||
this._shader = null;
|
||||
}
|
||||
}
|
||||
|
||||
this._getShader = function(currentTransform, globalAlpha) {
|
||||
// From the spec: "The points in the linear gradient must be transformed
|
||||
// as described by the current transformation matrix when rendering."
|
||||
var pts = [x1, y1, x2, y2];
|
||||
CanvasKit.SkMatrix.mapPoints(currentTransform, pts);
|
||||
var sx1 = pts[0];
|
||||
var sy1 = pts[1];
|
||||
var sx2 = pts[2];
|
||||
var sy2 = pts[3];
|
||||
|
||||
// Maybe refactor _scalefactor() on which this is taken?
|
||||
var sx = currentTransform[0];
|
||||
var sy = currentTransform[4];
|
||||
var scaleFactor = (Math.abs(sx) + Math.abs(sy))/2;
|
||||
|
||||
var sr1 = r1 * scaleFactor;
|
||||
var sr2 = r2 * scaleFactor;
|
||||
|
||||
this._dispose();
|
||||
var colors = this._colors.map(function(c) {
|
||||
return CanvasKit.multiplyByAlpha(c, globalAlpha);
|
||||
});
|
||||
this._shader = CanvasKit.MakeTwoPointConicalGradientShader(
|
||||
[sx1, sy1], sr1, [sx2, sy2], sr2, colors, this._pos,
|
||||
CanvasKit.TileMode.Clamp);
|
||||
return this._shader;
|
||||
}
|
||||
}
|
||||
|
||||
function CanvasRenderingContext2D(skcanvas) {
|
||||
this._canvas = skcanvas;
|
||||
this._paint = new CanvasKit.SkPaint();
|
||||
@ -75,8 +225,8 @@
|
||||
this._paint.setStrokeCap(CanvasKit.StrokeCap.Butt);
|
||||
this._paint.setStrokeJoin(CanvasKit.StrokeJoin.Miter);
|
||||
|
||||
this._strokeColor = CanvasKit.BLACK;
|
||||
this._fillColor = CanvasKit.BLACK;
|
||||
this._strokeStyle = CanvasKit.BLACK;
|
||||
this._fillStyle = CanvasKit.BLACK;
|
||||
this._shadowBlur = 0;
|
||||
this._shadowColor = CanvasKit.TRANSPARENT;
|
||||
this._shadowOffsetX = 0;
|
||||
@ -95,12 +245,19 @@
|
||||
this._currentSubpath = null;
|
||||
this._currentTransform = CanvasKit.SkMatrix.identity();
|
||||
|
||||
// Use this for save/restore
|
||||
this._canvasStateStack = [];
|
||||
// Keep a reference to all the gradients that were allocated
|
||||
// for cleanup in _dispose;
|
||||
this._gradients = [];
|
||||
|
||||
this._dispose = function() {
|
||||
this._currentPath.delete();
|
||||
this._currentSubpath && this._currentSubpath.delete();
|
||||
this._paint.delete();
|
||||
this._gradients.forEach(function(gradient) {
|
||||
gradient._dispose();
|
||||
});
|
||||
// Don't delete this._canvas as it will be disposed
|
||||
// by the surface of which it is based.
|
||||
}
|
||||
@ -146,10 +303,18 @@
|
||||
Object.defineProperty(this, 'fillStyle', {
|
||||
enumerable: true,
|
||||
get: function() {
|
||||
return colorToString(this._fillColor);
|
||||
if (Number.isInteger(this._fillStyle)) {
|
||||
return colorToString(this._fillStyle);
|
||||
}
|
||||
return this._fillStyle;
|
||||
},
|
||||
set: function(newStyle) {
|
||||
this._fillColor = parseColor(newStyle);
|
||||
if (typeof newStyle === 'string') {
|
||||
this._fillStyle = parseColor(newStyle);
|
||||
} else if (newStyle.addColorStop) {
|
||||
// It's probably a gradient.
|
||||
this._fillStyle = newStyle
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@ -494,10 +659,16 @@
|
||||
Object.defineProperty(this, 'strokeStyle', {
|
||||
enumerable: true,
|
||||
get: function() {
|
||||
return colorToString(this._strokeColor);
|
||||
return colorToString(this._strokeStyle);
|
||||
},
|
||||
set: function(newStyle) {
|
||||
this._strokeColor = parseColor(newStyle);
|
||||
if (typeof newStyle === 'string') {
|
||||
this._strokeStyle = parseColor(newStyle);
|
||||
} else if (newStyle.addColorStop) {
|
||||
// It's probably a gradient.
|
||||
this._strokeStyle = newStyle
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
@ -562,6 +733,17 @@
|
||||
this._paint.setBlendMode(this._globalCompositeOperation);
|
||||
}
|
||||
|
||||
this.clip = function(fillRule) {
|
||||
this._commitSubpath();
|
||||
var clip = this._currentPath.copy();
|
||||
if (fillRule && fillRule.toLowerCase() === 'evenodd') {
|
||||
clip.setFillType(CanvasKit.FillType.EvenOdd);
|
||||
} else {
|
||||
clip.setFillType(CanvasKit.FillType.Winding);
|
||||
}
|
||||
this._canvas.clipPath(clip, CanvasKit.ClipOp.Intersect, true);
|
||||
}
|
||||
|
||||
this.closePath = function() {
|
||||
if (this._currentSubpath) {
|
||||
this._currentSubpath.close();
|
||||
@ -570,6 +752,24 @@
|
||||
}
|
||||
}
|
||||
|
||||
this.createLinearGradient = function(x1, y1, x2, y2) {
|
||||
if (!allAreFinite(arguments)) {
|
||||
return;
|
||||
}
|
||||
var lcg = new LinearCanvasGradient(x1, y1, x2, y2);
|
||||
this._gradients.push(lcg);
|
||||
return lcg;
|
||||
}
|
||||
|
||||
this.createRadialGradient = function(x1, y1, r1, x2, y2, r2) {
|
||||
if (!allAreFinite(arguments)) {
|
||||
return;
|
||||
}
|
||||
var rcg = new RadialCanvasGradient(x1, y1, r1, x2, y2, r2);
|
||||
this._gradients.push(rcg);
|
||||
return rcg;
|
||||
}
|
||||
|
||||
this._commitSubpath = function () {
|
||||
if (this._currentSubpath) {
|
||||
this._currentPath.addPath(this._currentSubpath, false);
|
||||
@ -612,8 +812,13 @@
|
||||
this._fillPaint = function() {
|
||||
var paint = this._paint.copy();
|
||||
paint.setStyle(CanvasKit.PaintStyle.Fill);
|
||||
var alphaColor = CanvasKit.multiplyByAlpha(this._fillColor, this._globalAlpha);
|
||||
paint.setColor(alphaColor);
|
||||
if (Number.isInteger(this._fillStyle)) {
|
||||
var alphaColor = CanvasKit.multiplyByAlpha(this._fillStyle, this._globalAlpha);
|
||||
paint.setColor(alphaColor);
|
||||
} else {
|
||||
var gradient = this._fillStyle._getShader(this._currentTransform, this._globalAlpha);
|
||||
paint.setShader(gradient);
|
||||
}
|
||||
|
||||
paint.dispose = function() {
|
||||
// If there are some helper effects in the future, clean them up
|
||||
@ -756,12 +961,11 @@
|
||||
return;
|
||||
}
|
||||
this._currentTransform = newState.ctm;
|
||||
// TODO(kjlubick): clipping region
|
||||
this._lineDashList = newState.ldl;
|
||||
this._strokeWidth = newState.sw;
|
||||
this._paint.setStrokeWidth(this._strokeWidth);
|
||||
this._strokeColor = newState.sc;
|
||||
this._fillColor = newState.fc;
|
||||
this._strokeStyle = newState.ss;
|
||||
this._fillStyle = newState.fs;
|
||||
this._paint.setStrokeCap(newState.cap);
|
||||
this._paint.setStrokeJoin(newState.jn);
|
||||
this._paint.setStrokeMiter(newState.mtr);
|
||||
@ -773,7 +977,10 @@
|
||||
this._globalCompositeOperation = newState.gco;
|
||||
this._paint.setBlendMode(this._globalCompositeOperation);
|
||||
this._lineDashOffset = newState.ldo;
|
||||
//TODO: filter, font, textAlign, textBaseline, direction, imageSmoothingEnabled, imageSmoothingQuality.
|
||||
//TODO: font, textAlign, textBaseline, direction, imageSmoothingEnabled, imageSmoothingQuality.
|
||||
|
||||
// restores the clip
|
||||
this._canvas.restore();
|
||||
}
|
||||
|
||||
this.rotate = function(radians, px, py) {
|
||||
@ -783,13 +990,26 @@
|
||||
}
|
||||
|
||||
this.save = function() {
|
||||
if (this._fillStyle._copy) {
|
||||
var fs = this._fillStyle._copy();
|
||||
this._gradients.push(fs);
|
||||
} else {
|
||||
var fs = this._fillStyle;
|
||||
}
|
||||
|
||||
if (this._strokeStyle._copy) {
|
||||
var ss = this._strokeStyle._copy();
|
||||
this._gradients.push(ss);
|
||||
} else {
|
||||
var ss = this._strokeStyle;
|
||||
}
|
||||
|
||||
this._canvasStateStack.push({
|
||||
ctm: this._currentTransform.slice(),
|
||||
// TODO(kjlubick): clipping region
|
||||
ldl: this._lineDashList.slice(),
|
||||
sw: this._strokeWidth,
|
||||
sc: this._strokeColor,
|
||||
fc: this._fillColor,
|
||||
ss: ss,
|
||||
fs: fs,
|
||||
cap: this._paint.getStrokeCap(),
|
||||
jn: this._paint.getStrokeJoin(),
|
||||
mtr: this._paint.getStrokeMiter(),
|
||||
@ -800,8 +1020,10 @@
|
||||
ga: this._globalAlpha,
|
||||
ldo: this._lineDashOffset,
|
||||
gco: this._globalCompositeOperation,
|
||||
//TODO: filter, font, textAlign, textBaseline, direction, imageSmoothingEnabled, imageSmoothingQuality.
|
||||
//TODO: font, textAlign, textBaseline, direction, imageSmoothingEnabled, imageSmoothingQuality.
|
||||
});
|
||||
// Saves the clip
|
||||
this._canvas.save();
|
||||
}
|
||||
|
||||
this.scale = function(sx, sy) {
|
||||
@ -877,8 +1099,14 @@
|
||||
this._strokePaint = function() {
|
||||
var paint = this._paint.copy();
|
||||
paint.setStyle(CanvasKit.PaintStyle.Stroke);
|
||||
var alphaColor = CanvasKit.multiplyByAlpha(this._strokeColor, this._globalAlpha);
|
||||
paint.setColor(alphaColor);
|
||||
if (Number.isInteger(this._strokeStyle)) {
|
||||
var alphaColor = CanvasKit.multiplyByAlpha(this._strokeStyle, this._globalAlpha);
|
||||
paint.setColor(alphaColor);
|
||||
} else {
|
||||
var gradient = this._strokeStyle._getShader(this._currentTransform, this._globalAlpha);
|
||||
paint.setShader(gradient);
|
||||
}
|
||||
|
||||
// This is not in the spec, but it appears Chrome scales up
|
||||
// the line width by some amount when stroking (and filling?).
|
||||
var scaledWidth = this._strokeWidth * this._scalefactor();
|
||||
|
@ -434,7 +434,6 @@
|
||||
}
|
||||
|
||||
CanvasKit.MakeRadialGradientShader = function(center, radius, colors, pos, mode, localMatrix, flags) {
|
||||
// TODO: matrix and flags
|
||||
var colorPtr = copy1dArray(colors, CanvasKit.HEAP32);
|
||||
var posPtr = copy1dArray(pos, CanvasKit.HEAPF32);
|
||||
flags = flags || 0;
|
||||
@ -456,6 +455,31 @@
|
||||
return rgs;
|
||||
}
|
||||
|
||||
CanvasKit.MakeTwoPointConicalGradientShader = function(start, startRadius, end, endRadius,
|
||||
colors, pos, mode, localMatrix, flags) {
|
||||
var colorPtr = copy1dArray(colors, CanvasKit.HEAP32);
|
||||
var posPtr = copy1dArray(pos, CanvasKit.HEAPF32);
|
||||
flags = flags || 0;
|
||||
|
||||
if (localMatrix) {
|
||||
// Add perspective args if not provided.
|
||||
if (localMatrix.length === 6) {
|
||||
localMatrix.push(0, 0, 1);
|
||||
}
|
||||
var rgs = CanvasKit._MakeTwoPointConicalGradientShader(
|
||||
start, startRadius, end, endRadius,
|
||||
colorPtr, posPtr, colors.length, mode, flags, localMatrix);
|
||||
} else {
|
||||
var rgs = CanvasKit._MakeTwoPointConicalGradientShader(
|
||||
start, startRadius, end, endRadius,
|
||||
colorPtr, posPtr, colors.length, mode, flags);
|
||||
}
|
||||
|
||||
CanvasKit._free(colorPtr);
|
||||
CanvasKit._free(posPtr);
|
||||
return rgs;
|
||||
}
|
||||
|
||||
CanvasKit.MakeSkVertices = function(mode, positions, textureCoordinates, colors,
|
||||
boneIndices, boneWeights, indices) {
|
||||
var positionPtr = copy2dArray(positions, CanvasKit.HEAPF32);
|
||||
|
@ -341,6 +341,47 @@ describe('CanvasKit\'s Canvas 2d Behavior', function() {
|
||||
}));
|
||||
});
|
||||
|
||||
it('supports gradients, which respect clip/save/restore', function(done) {
|
||||
LoadCanvasKit.then(catchException(done, () => {
|
||||
multipleCanvasTest('gradients_clip', done, (canvas) => {
|
||||
let ctx = canvas.getContext('2d');
|
||||
|
||||
var rgradient = ctx.createRadialGradient(200, 300, 10, 100, 100, 300);
|
||||
|
||||
rgradient.addColorStop(0, 'red');
|
||||
rgradient.addColorStop(.7, 'white');
|
||||
rgradient.addColorStop(1, 'blue');
|
||||
|
||||
ctx.fillStyle = rgradient;
|
||||
ctx.globalAlpha = 0.7;
|
||||
ctx.fillRect(0,0,600,600);
|
||||
ctx.globalAlpha = 0.95;
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.arc(300, 100, 90, 0, Math.PI*1.66);
|
||||
ctx.closePath();
|
||||
ctx.strokeStyle = 'yellow';
|
||||
ctx.lineWidth = 5;
|
||||
ctx.stroke();
|
||||
ctx.save();
|
||||
ctx.clip();
|
||||
|
||||
var lgradient = ctx.createLinearGradient(200, 20, 420, 40);
|
||||
|
||||
lgradient.addColorStop(0, 'green');
|
||||
lgradient.addColorStop(.5, 'cyan');
|
||||
lgradient.addColorStop(1, 'orange');
|
||||
|
||||
ctx.fillStyle = lgradient;
|
||||
|
||||
ctx.fillRect(200, 30, 200, 300);
|
||||
|
||||
ctx.restore();
|
||||
ctx.fillRect(550, 550, 40, 40);
|
||||
});
|
||||
}));
|
||||
});
|
||||
|
||||
it('can read default properties', function(done) {
|
||||
LoadCanvasKit.then(catchException(done, () => {
|
||||
const skcanvas = CanvasKit.MakeCanvas(CANVAS_WIDTH, CANVAS_HEIGHT);
|
||||
@ -359,7 +400,7 @@ describe('CanvasKit\'s Canvas 2d Behavior', function() {
|
||||
|
||||
// Compare all the default values of the properties of skcanvas
|
||||
// to the default values on the properties of a real canvas.
|
||||
for( let attr of toTest) {
|
||||
for(let attr of toTest) {
|
||||
expect(skcontext[attr]).toBe(realContext[attr], attr);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user