-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
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
composite with blend dest-in produces white halo #1676
Comments
I played around with the code some more in search for a workaround to the issue. Although I did not find one, I did find another interesting visual manifestation of the same issue. The following code draws a solid purple image, and applies a linear gradient fade mask on it with composite blend const sharp = require("sharp");
const square = sharp(
Buffer.from(
'<svg viewBox="0 0 100 100"><rect width="100" height="100" fill="#80F" /></svg>',
"utf-8"
)
);
const fade = sharp(
Buffer.from(
`<svg viewBox="0 0 100 100">
<defs>
<linearGradient id="fade">
<stop offset="0" stop-opacity="0" />
<stop offset="1" stop-opacity="1" />
</linearGradient>
</defs>
<rect width="100" height="100" fill="url('#fade')" />
</svg>`,
"utf-8"
)
);
fade
.toBuffer()
.then(fadeBuffer =>
square
.composite([{ input: fadeBuffer, blend: "dest-in" }])
.toFile("./test.png")
); But with this issue, the color is somehow skewed by the So the issue is not directly related to any shapes or their borders, but seems to be more related to alpha channels in general. Since the behaviour seemed so consistent, I had to check the documentation for the specification of the blend modes. https://www.cairographics.org/operators/#dest_in does specify that the result colour (xR) should always match the destination image colour (xB). Unfortunately I don't have the expertise required to verify that the libvips implementation works as specified. |
Hello, I think this is working as expected. Composite will premultiply images, blend, then unpremultiply. Premultiplication scales the image by the alpha (so RGB values get much darker in transparent areas), unpremultiplication does the opposite: it'll brighten pixels in very transparent areas.
|
Workaround: rather than removing the alpha, try flattening it out, it should remove the sparkles. I made a libvips issue: libvips/libvips#1301 |
Hi, thanks for taking the time to investigate the issue. I'm not really sure what premultiplication means, and I think the composite API is more user-friendly if the user of the API doesn't need to be concerned about implementation details. I'm afraid flattening the image doesn't help me, as my intention is to produce a transparent image. In the bug report I just used |
Ah, I think I see what you mean @jcupitt . The use of So while const sharp = require("sharp");
const square = sharp(
Buffer.from(
'<svg viewBox="0 0 100 100"><rect width="100" height="100" fill="#222" /></svg>',
"utf-8"
)
);
const circle = sharp(
Buffer.from(
'<svg viewBox="0 0 100 100"><circle cx="50" cy="50" r="30" fill="#000" /></svg>',
"utf-8"
)
);
circle
.toBuffer()
.then(circleBuffer =>
square.composite([{ input: circleBuffer, blend: "dest-in" }]).toBuffer()
)
.then(compositeBuffer =>
sharp(compositeBuffer)
.flatten({ background: { r: 40, g: 40, b: 40 } })
.toFile("./test.png")
); (P.S. for some reason flatten didn't seem to work without writing to a buffer in between. Edit: created issue #1677 ) I get the result Similarly, the issue is visible when opening the unflattened image in an image viewer like macOS Preview in dark mode as here So the while |
This has been fixed and merged to master. It'll be in 8.8. Thanks for reporting this! |
This compositing issue is the only thing blocking my use of sharp. Is there a way to already use libvips 8.8.0 with sharp 0.22? Version 0.23 seems to still take some time looking at the milestones. |
sharp v0.23.0 is now available. |
Using the new powerful
composite
api to cut out a non-rectangular mask of a gray image with the blend modedest-in
seems to produce a strange light edge on the masked result. The effect only seems to apply to antialiased shapes, such as circles. For some reason, this bug is also not present when the image to be cut is either completely white or completely black.I would expect the following code to produce a gray circle on a black background, but what I get is a gray circle with a thin white halo on a black background, as seen in the image below.
The text was updated successfully, but these errors were encountered: