Bug 273239

Summary: [Skia] Fix corner cases involving drawing shadows
Product: WebKit Reporter: Pawel Lampe <plampe>
Component: WPE WebKitAssignee: Pawel Lampe <plampe>
Status: RESOLVED FIXED    
Severity: Normal CC: bugs-noreply, webkit-bug-importer
Priority: P2    
Version: WebKit Nightly Build   
Hardware: Unspecified   
OS: Unspecified   
See Also: https://bugs.webkit.org/show_bug.cgi?id=275353
Bug Depends on:    
Bug Blocks: 268972    

Description Pawel Lampe 2024-04-25 02:46:31 PDT
See LayoutTests failures such as:

imported/w3c/web-platform-tests/html/canvas/offscreen/shadows/2d.shadow.composite.1.html
imported/w3c/web-platform-tests/html/canvas/offscreen/shadows/2d.shadow.composite.1.worker.html
imported/w3c/web-platform-tests/html/canvas/offscreen/shadows/2d.shadow.composite.2.html
imported/w3c/web-platform-tests/html/canvas/offscreen/shadows/2d.shadow.composite.2.worker.html
imported/w3c/web-platform-tests/html/canvas/offscreen/shadows/2d.shadow.enable.blur.html
imported/w3c/web-platform-tests/html/canvas/offscreen/shadows/2d.shadow.enable.blur.worker.html
imported/w3c/web-platform-tests/html/canvas/offscreen/shadows/2d.shadow.enable.x.html
imported/w3c/web-platform-tests/html/canvas/offscreen/shadows/2d.shadow.enable.x.worker.html
imported/w3c/web-platform-tests/html/canvas/offscreen/shadows/2d.shadow.enable.y.html
imported/w3c/web-platform-tests/html/canvas/offscreen/shadows/2d.shadow.enable.y.worker.html
imported/w3c/web-platform-tests/html/canvas/offscreen/shadows/2d.shadow.image.alpha.html
imported/w3c/web-platform-tests/html/canvas/offscreen/shadows/2d.shadow.image.basic.html
imported/w3c/web-platform-tests/html/canvas/offscreen/shadows/2d.shadow.image.scale.html
imported/w3c/web-platform-tests/html/canvas/offscreen/shadows/2d.shadow.image.transparent.2.html
Comment 1 Pawel Lampe 2024-04-25 03:10:27 PDT
See more LayoutTests failures such as:

imported/w3c/web-platform-tests/html/canvas/element/shadows/2d.shadow.composite.1.html
imported/w3c/web-platform-tests/html/canvas/element/shadows/2d.shadow.composite.2.html
imported/w3c/web-platform-tests/html/canvas/element/shadows/2d.shadow.enable.blur.html
imported/w3c/web-platform-tests/html/canvas/element/shadows/2d.shadow.enable.x.html
imported/w3c/web-platform-tests/html/canvas/element/shadows/2d.shadow.enable.y.html
css3/color-filters/color-filter-text-decoration-shadow.html
fast/canvas/canvas-scale-shadowBlur.html
fast/canvas/canvas-strokePath-gradient-shadow.html
fast/canvas/canvas-strokeRect-alpha-shadow.html
fast/canvas/canvas-strokeRect-gradient-shadow.html
Comment 2 Pawel Lampe 2024-04-25 03:32:21 PDT
In general the above failures are due to 2 distict problems:

1) Skia bug when drawing an image with a shadow filter:

Currently, when we draw an image with shadow, we do this using a single call:
m_canvas.drawImageRect(image, normalizedSrcRect, normalizedDestRect, toSkSamplingOptions(m_state.imageInterpolationQuality()), &paint, { });
which uses a paint (SkPaint) with SkImageFilters::DropShadow attached using paint.setImageFilter(). While this works in most of the cases, it turns out that sometimes - when SkImageFilters::DropShadow is attached, and the <canvas> size is very close to the size of image being drawn - the above call draws no shadow (or nothing if I recall correctly). So far I've observed that it happens only when the image we're drawing is using accelerated surface, so e.g. if we uncomment the line in below demo (and comment the one above) the issue is gone:

<html>
  <head>
  </head>
  <body>
    <img id="img" src="/images/red.png" style="visibility:hidden">
    <canvas id="canvas" width="166" height="99" style="border: 1px solid #000"/>
    <script>
      var img = document.getElementById("img");
      img.onload = async () => {
          var canvas = document.getElementById("canvas");
          var ctx = canvas.getContext('2d');

          ctx.fillStyle = '#00f';
          ctx.fillRect(0, 0, 100, 50);
          ctx.shadowColor = '#0f0';
          ctx.shadowOffsetY = 50;
          var x = await createImageBitmap(img);
          // var x = img;
          ctx.drawImage(x, 0, -25);
      }
    </script>
  </body>
</html

the red.png comes from LayoutTests/imported/w3c/web-platform-tests/



2) Every time we draw something with a shadow, we do it within a single draw call with shadow filter attached.

While this approach is very clean and efficient, it makes it impossible to handle cases where the certain class of composite operators come into play. More precisely, the operators such as "destination-atop" etc. assume that drawing of e.g. rect with shadow is done in 2 steps: first draw the shadow, then the rect. In such case, having composite operation such as "destination-atop" would make the shadow to appear over the rect since shadow was on canvas before rect, and therefore it serves as a "desitnation" when rect is being drawn.
Comment 3 Pawel Lampe 2024-05-06 05:07:13 PDT
fast/canvas/canvas-scale-shadowBlur.html
fast/canvas/canvas-strokePath-gradient-shadow.html
fast/canvas/canvas-strokeRect-alpha-shadow.html
fast/canvas/canvas-strokeRect-gradient-shadow.html

tests were extracted to https://bugs.webkit.org/show_bug.cgi?id=273766
Comment 4 Pawel Lampe 2024-05-13 01:16:22 PDT
Pull request: https://github.com/WebKit/WebKit/pull/28168
Comment 5 EWS 2024-06-14 03:58:46 PDT
Committed 280008@main (120f5b1d4f90): <https://commits.webkit.org/280008@main>

Reviewed commits have been landed. Closing PR #28168 and removing active labels.