Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Antialias when using overlayWith #1401

Closed
agaskell opened this issue Oct 5, 2018 · 2 comments
Closed

Antialias when using overlayWith #1401

agaskell opened this issue Oct 5, 2018 · 2 comments
Labels

Comments

@agaskell
Copy link

agaskell commented Oct 5, 2018

Is there a way to add anti-aliasing on the transparency border when using overlayWith? When I overlay a circle everything comes out okay, but the edges are sharp. Here's an example image:

Cropped image

Here's my code:

  const rect = Buffer.from(
    `<svg>
      <rect
        height="${size}"
        rx="${Math.floor(size / 2)}"
        ry="${Math.floor(size / 2)}"
        shape-rendering="auto" <- I have tried the other shape-rendering values
        width="${size}"
        x="0"
        y="0"
      />
    </svg>`
  );  

  const buffer = await image
    .extract({
      height: radius * 2,
      left: Math.max(0, x - radius),
      top: Math.max(0, y - radius),
      width: radius * 2 
    })  
    .resize(size, size)
    .overlayWith(rect, { cutout: true })

Here's an image that has smoother edges (not sure how it was created):

Smooth edges

@lovell
Copy link
Owner

lovell commented Oct 5, 2018

Hello, the summary of the cutout logic in sharp is that it takes the alpha channel from the librsvg/cairo-rendered SVG and sticks it on the input. My best guess would be that there's a missing premultiplication step somewhere in the middle of the librsvg/cairo rendering, but I'm not sure there's much sharp can do here.

If you haven't already seen it, the existing overlayWith logic will be changing to take advantage of the relatively-new libvips composite logic as part of #728

As a workaround, perhaps try rendering at double the desired dimensions then perform a second pass to resize to half the dimensions as the Lanczos kernel will apply anti-aliasing.

@lovell lovell added the question label Oct 5, 2018
@agaskell
Copy link
Author

agaskell commented Oct 6, 2018

@lovell thank you so much! The workaround is hugely helpful and rendering twice did the trick. Here's my code if it helps anyone else:

  // SVG now matches extract dimensions
  const rect = Buffer.from(
    `<svg>
      <rect
        height="${radius * 2}"
        rx="${radius}"
        ry="${radius}"
        shape-rendering="geometricPrecision"
        width="${radius * 2}"
        x="0"
        y="0"
      />
    </svg>`
  );
  
  // Extract and overlay in the first pass
  const buffer = await image
    .extract({
      height: radius * 2,
      left: Math.max(0, x - radius),
      top: Math.max(0, y - radius),
      width: radius * 2 
    })  
    .overlayWith(rect, { cutout: true })
    .toBuffer();

  // Resize after initial render
  await sharp(buffer)
    .resize(size, size)
    .toFile(`o_${fileName}.webp`, function(err, info) {
      console.log('err', err);
      console.log('info', info);
    }); 

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants