skip to Main Content

Based on the documentation of drawImage(), I’m trying to draw to a specific part of a canvas. However, areas outside of the destination box are being affected, namly having their alpha values zeroed.

I’ve created a JSFiddle demonstrating the issue.

canvas composition results

I copied the canvas on the left (full yellow) to the canvas on the right and the drew the middle canvas to the center of that same canvas using "destination-atop". You can see that it applied properly but at the same time cleared everything outside the draw area. Same happens with "copy". See the Fiddle for details.

Why does drawImage() affect pixels outside its destination?

2

Answers


  1. Chosen as BEST ANSWER

    For reasons unknown to me, the "destination box" (dx, dy, dWidth, dHeight) isn't actually a destination box. It's more like a "center" and "scale" because drawing extends out of this box as though there were transparent source pixels being applied to that outside area.

    The fix is to add a clipping rectangle before doing the drawing, as such:

    ctx.beginPath()
    ctx.rect(dx, dy, dWidth, dHeight)
    ctx.clip()
    ctx.drawImage(img, dx, dy, dWidth, dHeight)
    

    The resulting Fiddle now does what I expected, applying the mask in the middle to the center of the solid-color image on the left to get the image on the right:

    enter image description here


  2. The definition of destination-atop is:

    The existing canvas is only kept where it overlaps the new shape. The new shape is drawn behind the canvas content.

    You want to keep everything, use source-over:

    This is the default setting and draws new shapes on top of the existing canvas content.

    const SIZE = 150
    const G = 63
    
    let c1 = document.getElementById("c1")
    c1.height = c1.width = SIZE
    let ctx1 = c1.getContext("2d")
    ctx1.fillStyle = "yellow"
    ctx1.fillRect(0, 0, SIZE, SIZE)
    
    let c2 = document.getElementById("c2")
    c2.height = c2.width = SIZE
    let ctx2 = c2.getContext("2d")
    let g2= ctx2.createRadialGradient(
        SIZE / 2, SIZE / 2,
        SIZE / 10,
        SIZE / 2, SIZE / 2,
        SIZE / 2 - 1)
    g2.addColorStop(0.00, `rgba(${G},${G},${G},0.000)`)
    g2.addColorStop(0.25, `rgba(${G},${G},${G},0.500)`)
    g2.addColorStop(0.50, `rgba(${G},${G},${G},0.750)`)
    g2.addColorStop(0.75, `rgba(${G},${G},${G},0.875)`)
    g2.addColorStop(1.00, `rgba(${G},${G},${G},1.000)`)
    ctx2.fillStyle = g2
    ctx2.globalCompositeOperation = "copy"
    ctx2.fillRect(0, 0, SIZE, SIZE)
    
    let c3 = document.getElementById("c3")
    c3.height = c3.width = SIZE
    let ctx3 = c3.getContext("2d")
    
    ctx3.globalCompositeOperation = "source-over";
    ctx3.drawImage(c1, 0, 0, SIZE, SIZE)
    ctx3.drawImage(c2, SIZE/4, SIZE/4, SIZE/2, SIZE/2)
    canvas {
      border: 2px solid red;
    }
    <canvas id="c1"></canvas>
    <canvas id="c2"></canvas>
    <canvas id="c3"></canvas>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search