canvaskit: linear gradient doesn’t render correctly with MakeCanvas when there's transform

135 views
Skip to first unread message

Alan Song

unread,
May 6, 2024, 5:10:04 PM5/6/24
to skia-discuss

Reproduction:  https://jsfiddle.net/459cepyq/

Running the same linear gradient rendering code simple side by side with native ctx2d and MakeCanvas's ctx produces different results:

function renderLinearGradient(ctx) {
const gradient = ctx.createLinearGradient(0, 0, 100, 100);
gradient.addColorStop(0, "#FF0000");
gradient.addColorStop(1, "#00FF00");

ctx.translate(50, 50);
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, 100, 100);
}

linear-gradient.png


In the source code, LinearCanvasGradient’s _getShader is applying the current transform to the points before calling CanvasKit.Shader.MakeLinearGradient. 

 https://skia.googlesource.com/skia/+/81ccf3ab27ee093cfe8c6896e7b75fc263b221e2/modules/canvaskit/htmlcanvas/lineargradient.js#52

this._getShader = function(currentTransform) {

// From the spec: "The points in the linear gradient must be transform
// as described by the current transformation matrix when rendering."

var pts = [x1, y1, x2, y2];


CanvasKit.Matrix.mapPoints(currentTransform, pts);


var sx1 = pts[0];


var sy1 = pts[1];


var sx2 = pts[2];


var sy2 = pts[3];




this._dispose();


this._shader = CanvasKit.Shader.MakeLinearGradient([sx1, sy1], [sx2, sy2],


this._colors, this._pos, CanvasKit.TileMode.Clamp);


return this._shader;


}

Removing this step seems to produce the correct result:

linear-gradient-change.png

linear-gradient-fixed.png

I think by the time _getShader is called, surface’s `canvas` is already set up to the correct transform for this._canvas.drawPath() that uses this._fillPaint() (that calls _getShader()), so we don’t need to transform it again for MakeLinearGradient.



Brian Osman

unread,
May 13, 2024, 3:16:48 PM5/13/24
to skia-d...@googlegroups.com
Yes, I think you're right about the problem (and the underlying cause). I don't think the canvas-compatible canvas is used much, so I'm not surprised that there are some bugs. Just from reading that file - I see one more problem: The handling of duplicate color stops overwrites the previous value, but that produces different results from the specified behavior (where the API should actually create a hard-stop): https://jsfiddle.net/oa7rbwsL/

--
You received this message because you are subscribed to the Google Groups "skia-discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to skia-discuss...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/skia-discuss/a97bf7f9-803c-4570-bb10-130487204c76n%40googlegroups.com.

Alan Song

unread,
May 15, 2024, 6:12:15 PM5/15/24
to skia-discuss

Thank you for the investigation. We didn’t have hard stop use cases so we didn’t discover it. Other than the gradient issue it is performing extremely well.

I think the compatible ctx2d is a hidden gem of canvaskit. It basically allows you to call ctx2d api and render with webgl (maybe webgpu in the future). This means you can migrate your existing ctx2d app to canvaskit, and progressively refactor it with native skia apis for better performance (drawVertices, drawAtlas etc), whereas for native ctx2d you would have hit a wall after pulling every trick in the book. The only downside is fonts management because system fonts won't be available (exclusive to native ctx2d's fillText), and Safari/Firefox support for Local Font Access api doesn't look promising.

Reply all
Reply to author
Forward
0 new messages