Skip to content

Commit

Permalink
Fix and improve unclipped image blits
Browse files Browse the repository at this point in the history
* Remove the limitation of fill path having to be equal to the source image:
  Simplify and correct the u/v texture coordinate calculation by applying
  the transformation that happens in the fragment shader ahead of time in Rust.
* Support intersecting the rect fill with the scissor rect (note: anti-aliasing is
  explicitly disabled in the paint for this code to trigger)

This brings my scene from 47 FPS to 51 FPS
  • Loading branch information
tronical committed Apr 7, 2022
1 parent 5fed26b commit 108038d
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 71 deletions.
20 changes: 19 additions & 1 deletion src/geometry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -234,12 +234,30 @@ impl Rect {
}

pub fn contains_rect(&self, other: &Rect) -> bool {
(other.w <= 0. || other.h <= 0.)
other.is_empty()
|| (self.x <= other.x
&& other.x + other.w <= self.x + self.w
&& self.y <= other.y
&& other.y + other.h <= self.y + self.h)
}

pub fn intersection(&self, other: &Rect) -> Option<Self> {
let x = self.x.max(other.x);
let y = self.y.max(other.y);
let w = (self.x + self.w).min(other.x + other.w) - x;
let h = (self.y + self.h).min(other.y + other.h) - y;

let result = Self { x, y, w, h };
if !result.is_empty() {
Some(result)
} else {
None
}
}

pub fn is_empty(&self) -> bool {
self.w <= 0. || self.h <= 0.
}
}

#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
Expand Down
90 changes: 37 additions & 53 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -876,24 +876,18 @@ where
match (
path_cache.path_fill_is_rect(),
scissor.as_rect(canvas_width, canvas_height),
paint.as_straight_tinted_image(),
paint.is_straight_tinted_image(),
) {
(Some(path_rect), Some(scissor_rect), Some((image_id, image_width, image_height, image_tint)))
if scissor_rect.contains_rect(&path_rect)
&& image_width == path_rect.w
&& image_height == path_rect.h =>
{
self.render_unclipped_image_blit(
path_rect.x,
path_rect.y,
path_rect.w,
path_rect.h,
image_id,
image_width,
image_height,
image_tint,
);
return;
(Some(path_rect), Some(scissor_rect), true) => {
if scissor_rect.contains_rect(&path_rect) {
self.render_unclipped_image_blit(&path_rect, paint);
return;
} else if let Some(intersection) = path_rect.intersection(&scissor_rect) {
self.render_unclipped_image_blit(&intersection, paint);
return;
} else {
return;
}
}
_ => {}
}
Expand Down Expand Up @@ -1119,55 +1113,42 @@ where
self.append_cmd(cmd);
}

fn render_unclipped_image_blit(
&mut self,
post_transform_x: f32,
post_transform_y: f32,
width: f32,
height: f32,
image: ImageId,
image_width: f32,
image_height: f32,
tint: Color,
) {
let image_info = match self.images.info(image) {
Some(info) => info,
None => return,
};

fn render_unclipped_image_blit(&mut self, target_rect: &Rect, paint: Paint) {
let scissor = self.state().scissor;

let mut paint = Paint::image_tint(image, 0., 0., width, height, 0., tint).with_anti_alias(false);
// Apply global alpha
paint.mul_alpha(self.state().alpha);

paint.transform = self.state().transform;

let mut params = Params::new(&self.images, &paint, &scissor, 1.0, 1.0, -1.0);
let mut params = Params::new(&self.images, &paint, &scissor, 0., 0., -1.0);
params.shader_type = ShaderType::TextureCopyUnclipped.to_f32();

let mut cmd = Command::new(CommandType::Triangles { params });
cmd.composite_operation = self.state().composite_operation;
cmd.glyph_texture = paint.glyph_texture();

let x0 = post_transform_x;
let y0 = post_transform_y;
let x1 = post_transform_x + width;
let y1 = post_transform_y + height;
let x0 = target_rect.x;
let y0 = target_rect.y;
let x1 = x0 + target_rect.w;
let y1 = y0 + target_rect.h;

let (p0, p1) = (x0, y0);
let (p2, p3) = (x1, y0);
let (p4, p5) = (x1, y1);
let (p6, p7) = (x0, y1);

let s0 = 0.;
let mut t0 = 0.;
let s1 = image_width / image_info.width() as f32;
let mut t1 = image_height / image_info.height() as f32;

if image_info.flags().contains(ImageFlags::FLIP_Y) {
std::mem::swap(&mut t0, &mut t1);
}
// Apply the same mapping from vertex coordinates to texture coordinates as in the fragment shader,
// but now ahead of time.
let mut to_texture_space_transform = Transform2D::identity();
to_texture_space_transform.scale(1. / params.extent[0], 1. / params.extent[1]);
to_texture_space_transform.premultiply(&Transform2D([
params.paint_mat[0],
params.paint_mat[1],
params.paint_mat[4],
params.paint_mat[5],
params.paint_mat[8],
params.paint_mat[9],
]));

let (s0, t0) = to_texture_space_transform.transform_point(target_rect.x, target_rect.y);
let (s1, t1) =
to_texture_space_transform.transform_point(target_rect.x + target_rect.w, target_rect.y + target_rect.h);

let verts = [
Vertex::new(p0, p1, s0, t0),
Expand All @@ -1178,7 +1159,10 @@ where
Vertex::new(p4, p5, s1, t1),
];

cmd.image = Some(image);
if let PaintFlavor::Image { id, .. } = paint.flavor {
cmd.image = Some(id);
}

cmd.triangles_verts = Some((self.verts.len(), verts.len()));
self.append_cmd(cmd);

Expand Down
20 changes: 3 additions & 17 deletions src/paint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -785,22 +785,8 @@ impl Paint {
}
}

/// Returns the image id, width/height, and associated tint if this paint is an untransformed
/// image paint without anti-aliasing at the edges in case of a fill
pub(crate) fn as_straight_tinted_image(&self) -> Option<(ImageId, f32, f32, Color)> {
match self.flavor {
PaintFlavor::Image {
id: image_id,
cx,
cy,
width,
height,
angle,
tint,
} if cx == 0.0 && cy == 0.0 && angle == 0.0 && !self.shape_anti_alias => {
Some((image_id, width, height, tint))
}
_ => None,
}
/// Returns true if this paint is an untransformed image paint without anti-aliasing at the edges in case of a fill
pub(crate) fn is_straight_tinted_image(&self) -> bool {
matches!(self.flavor, PaintFlavor::Image { angle, .. } if angle == 0.0 && !self.shape_anti_alias)
}
}

0 comments on commit 108038d

Please sign in to comment.