You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I have a program that uses raqote to render an image in "chunks" on
different threads, and then composite them all into a final image. By
"chunks" I mean disjoint rectangular regions that cover the canvas:
e.g., a 2×2 grid of quadrants.
I thought that blending with Src would produce the same result because
no two pixels in a chunk overlap. But in fact it seems that when drawing
an image into a DrawTarget with Src, all other pixels in the row
are cleared, set to transparency. Here is an example:
use raqote::{BlendMode,DrawOptions,DrawTarget,Image,PathBuilder,SolidSource,Source};fnmain() -> Result<(),Box<dyn std::error::Error>>{let rgb_source = |r, g, b| Source::Solid(SolidSource{ r, g, b,a:255});let blue = rgb_source(34,127,190);let white = rgb_source(255,255,255);// Create a white circle on a blue field.letmut circle = DrawTarget::new(50,50);
circle.fill_rect(0.0,0.0,50.0,50.0,&blue,&DrawOptions::new());letmut pb = PathBuilder::new();
pb.arc(25.0,25.0,20.0,0.0, std::f32::consts::TAU);
circle.fill(&pb.finish(),&white,&DrawOptions::new());let circle_image = Image{width: circle.width(),height: circle.height(),data: circle.get_data(),};// Now, try to draw a 2x2 grid of those.letmut canvas = DrawTarget::new(100,100);// Top row: two blue circles next to each other, default (SrcOver) compositing.
canvas.draw_image_at(0.0,0.0,&circle_image,&DrawOptions::new());
canvas.draw_image_at(50.0,0.0,&circle_image,&DrawOptions::new());// Bottom row: using Src compositing, since the pixels are disjoint and should cover the row// exactly. Should be the same, right?letmut opts_src = DrawOptions::new();
opts_src.blend_mode = BlendMode::Src;
canvas.draw_image_at(0.0,50.0,&circle_image,&opts_src);
canvas.draw_image_at(50.0,50.0,&circle_image,&opts_src);
canvas.write_png("canvas.png")?;Ok(())}
At raqote v0.8.2, this produces the following image:
As you can see, drawing the bottom-right quadrant has caused the
bottom-left quadrant to be clobbered with transparency, even though it
shouldn't have been affected at all because it is not within the bounds
of the image that I asked to draw.
In addition to being a correctness issue, this suggests that raqote is
doing a lot more work compositing than it needs to! e.g., we would
expect drawing an M×N grid of fixed-size images onto a canvas to take
O(M·N) time, but it seems like it would actually take O(M·N²) time,
because each image that's drawn needs to rewrite the whole row.
I tried to track down what's happening, and this is my understanding:
blend_row::<Src>(src, dst) effectively picks how much to blend
based on the length of src, since dst is set to a whole suffix
of the image data buffer;
the instantiated value of src is &self.tmp[..], which is created
in DrawTarget::choose_blitter as a vector with length width;
and choose_blitter's unique call site, in DrawTarget::composite,
unconditionally sets width to self.width, which does not at all
incorporate the rect to which we're drawing.
Correspondingly, this patch seems to fix the problem for me:
The raqote test suite continues to pass with this patch. I'm not
familiar enough to understand whether this is the right patch and good
in all cases, but I did try it out on the sweep-gradient example and
on images like this that my program generates; each one was
bitwise-identical before and after this patch.
What do you think—is this patch appropriate and sufficient, or is
something trickier needed?
The text was updated successfully, but these errors were encountered:
I have a program that uses
raqote
to render an image in "chunks" ondifferent threads, and then composite them all into a final image. By
"chunks" I mean disjoint rectangular regions that cover the canvas:
e.g., a 2×2 grid of quadrants.
This works fine with the default
SrcOver
blend mode, but I figuredthat it would be faster to use
Src
since it's is a no-opinstead of some bit operations that compile down to 15 or so
instructions and probably prevent autovectorizing of the
copy loop.
I thought that blending with
Src
would produce the same result becauseno two pixels in a chunk overlap. But in fact it seems that when drawing
an image into a
DrawTarget
withSrc
, all other pixels in the roware cleared, set to transparency. Here is an example:
At
raqote
v0.8.2, this produces the following image:As you can see, drawing the bottom-right quadrant has caused the
bottom-left quadrant to be clobbered with transparency, even though it
shouldn't have been affected at all because it is not within the bounds
of the image that I asked to draw.
In addition to being a correctness issue, this suggests that
raqote
isdoing a lot more work compositing than it needs to! e.g., we would
expect drawing an M×N grid of fixed-size images onto a canvas to take
O(M·N) time, but it seems like it would actually take O(M·N²) time,
because each image that's drawn needs to rewrite the whole row.
I tried to track down what's happening, and this is my understanding:
ShaderBlendBlitter::blit_span
, whereblend_fn
points toblend_row::<Src>
;blend_row::<Src>(src, dst)
effectively picks how much to blendbased on the length of
src
, sincedst
is set to a whole suffixof the image data buffer;
src
is&self.tmp[..]
, which is createdin
DrawTarget::choose_blitter
as a vector with lengthwidth
;choose_blitter
's unique call site, inDrawTarget::composite
,unconditionally sets
width
toself.width
, which does not at allincorporate the rect to which we're drawing.
Correspondingly, this patch seems to fix the problem for me:
The
raqote
test suite continues to pass with this patch. I'm notfamiliar enough to understand whether this is the right patch and good
in all cases, but I did try it out on the
sweep-gradient
example andon images like this that my program generates; each one was
bitwise-identical before and after this patch.
What do you think—is this patch appropriate and sufficient, or is
something trickier needed?
The text was updated successfully, but these errors were encountered: