Bug 197689

Summary: Text with a gradient and drop shadow does not display properly if the canvas context is scaled
Product: WebKit Reporter: themoonrat
Component: CanvasAssignee: Nobody <webkit-unassigned>
Status: NEW ---    
Severity: Normal CC: ahmad.saleem792, dino, mmaxfield, sabouhallawa, sam.shiels, simon.fraser, webkit-bug-importer
Priority: P2 Keywords: BrowserCompat, InRadar
Version: Safari 11   
Hardware: All   
OS: All   
See Also: https://bugs.webkit.org/show_bug.cgi?id=227954
Attachments:
Description Flags
Comparing visuals to another browser
none
Testcase
none
Simple and system drop-shadow none

Description themoonrat 2019-05-08 02:36:40 PDT
Created attachment 369367 [details]
Comparing visuals to another browser

Seen on:
Safari 11.1.2on MacOS High Sierra 10.13.6
iPad Pro on 12.1.3
iPad Air on 12.x (can't remember exact version)
iPhone S8+ on 11.4.1

On an empty html page, as this script in the body

<script>
	var canvas = document.getElementById("myCanvas");
	var ctx = canvas.getContext('2d');
	ctx.shadowColor = "#000000";
	ctx.shadowOffsetX = 5;
	ctx.shadowOffsetY = 5
	ctx.shadowBlur = 10;
	ctx.font = "70px arial";
	var gradient = ctx.createLinearGradient(300, 0, 300, 100);
	gradient.addColorStop(0, '#FF0000');
	gradient.addColorStop(0.25, '#FF0000');
	gradient.addColorStop(0.5, '#00FF00');
	gradient.addColorStop(0.75, '#0000FF');
	gradient.addColorStop(1, '#0000FF');
	ctx.fillStyle = gradient;
	ctx.scale(0.75, 0.75);
	ctx.fillText("1234567890", 10, 70);
</script>

On every other browser / os combination I've tried, you get the correct visual you see at the top of my attachment. But on Safari for MacOS and iOS, the drop shadow appears incorrectly way above the text.

If you remove the gradient and use a simple fill style, the drop shadow will appear in the correct place.
If you keep the gradient but remove the ctx.scale line, the drop shadow will appear in the correct place.

It's the combination of both gradient and scale where things go wrong.
Comment 1 Radar WebKit Bug Importer 2019-05-08 09:36:26 PDT
<rdar://problem/50583846>
Comment 2 Simon Fraser (smfr) 2019-05-14 15:20:12 PDT
Created attachment 369899 [details]
Testcase
Comment 3 Simon Fraser (smfr) 2021-07-15 19:47:45 PDT
*** Bug 227954 has been marked as a duplicate of this bug. ***
Comment 4 Said Abou-Hallawa 2021-07-16 16:47:47 PDT
The drop-shadow is drawn separate from drawing the text. But we have two different code paths for drawing the drop-shadow: simple and system. In the "simple" case, we draw the drop-shadow ourselves and in the "system" case we let the system draw the drop-shadow for dummy clipped-out text.

In FontCascade::drawGlyphs() we make a distinction between these two cases by the boolean hasSimpleShadow. When it is true we draw normal text and we clear the shadow setting temporarily by calling context.clearShadow()/context.setShadow().

It is important to note that,

1. In CanvasRenderingContext2DBase::drawTextUnchecked() and if (shouldDrawShadows()) is true, we call fontProxy.drawBidiText() but we clip the drawing such that text is clipped but the drop-shadow appears. I think the calculation under this if-statement is the cause of this bug.

2. In FontCascade::drawGlyphs(), hasSimpleShadow is set to true if the drop-shadow is not blurred and the GraphicsContext is not scaled, rotated or sheared.
Comment 5 Said Abou-Hallawa 2021-07-16 17:36:43 PDT
Created attachment 433722 [details]
Simple and system drop-shadow
Comment 6 Alexey Proskuryakov 2021-10-05 08:48:54 PDT
*** Bug 227954 has been marked as a duplicate of this bug. ***
Comment 7 Ahmad Saleem 2023-02-12 13:55:42 PST
It is still broken in WebKit ToT 260168@main.

Chrome Canary 112 and Firefox Nightly 111 match each other except the border around the digits while using "Simple and system drop-shadow" test case.

Just wanted to share updated testing result. Thanks!