swrenderer: Support multiple rectangles for the dirty regions

This allow to be much faster in the case two small area of the screen
changes.
This commit is contained in:
Olivier Goffart 2024-04-06 13:36:23 +02:00
parent 79a188516a
commit f45bab6a3f
6 changed files with 594 additions and 249 deletions

View file

@ -8,18 +8,20 @@
use super::{PhysicalLength, PhysicalRect};
use crate::graphics::{PixelFormat, Rgb8Pixel};
use crate::lengths::{PointLengths, RectLengths, SizeLengths};
use crate::lengths::{PointLengths, SizeLengths};
use crate::software_renderer::fixed::Fixed;
use crate::Color;
use derive_more::{Add, Mul, Sub};
use integer_sqrt::IntegerSquareRoot;
/// Draw one line of the texture in the line buffer
///
pub(super) fn draw_texture_line(
span: &PhysicalRect,
line: PhysicalLength,
texture: &super::SceneTexture,
line_buffer: &mut [impl TargetPixel],
extra_clip: i16,
) {
let super::SceneTexture {
data,
@ -27,19 +29,16 @@ pub(super) fn draw_texture_line(
pixel_stride,
extra: super::SceneTextureExtra { colorize, alpha, rotation, dx, dy, off_x, off_y },
} = *texture;
let span_size = span.size.cast::<usize>();
let source_size = texture.source_size().cast::<i32>();
let len = line_buffer.len();
let y = line - span.origin.y_length();
let y = if rotation.mirror_width() { span.size.height - y.get() - 1 } else { y.get() } as i32;
let line_buffer = &mut line_buffer[span.origin.x as usize..];
let y = (line - span.origin.y_length()).cast::<usize>();
let y = if rotation.mirror_width() { span_size.height - y.get() - 1 } else { y.get() } as i32;
let off_x = Fixed::<i32, 8>::from_fixed(off_x);
let off_y = Fixed::<i32, 8>::from_fixed(off_y);
let dx = Fixed::<i32, 8>::from_fixed(dx);
let dy = Fixed::<i32, 8>::from_fixed(dy);
let off_x = Fixed::<i32, 8>::from_fixed(off_x) + dx * extra_clip as i32;
if !rotation.is_transpose() {
let mut delta = dx;
@ -58,8 +57,7 @@ pub(super) fn draw_texture_line(
// the accumulated error in image pixels
let mut acc_err;
if rotation.mirror_height() {
let o = (off_x + (delta * (span_size.width as i32 - 1)))
% Fixed::from_integer(source_size.width);
let o = (off_x + (delta * (len as i32 - 1))) % Fixed::from_integer(source_size.width);
pos = init + o;
init += Fixed::from_integer(source_size.width);
end = (o / delta) as usize + 1;
@ -76,9 +74,9 @@ pub(super) fn draw_texture_line(
end += 1;
}
}
end = end.min(span_size.width);
end = end.min(len);
let mut begin = 0;
while begin < span_size.width {
while begin < len {
fetch_blend_pixel(
&mut line_buffer[begin..end],
format,
@ -108,7 +106,7 @@ pub(super) fn draw_texture_line(
end += 1;
}
};
end = end.min(span_size.width);
end = end.min(len);
}
} else {
let bpp = format.bpp();
@ -124,8 +122,8 @@ pub(super) fn draw_texture_line(
let mut acc_err;
if rotation.mirror_height() {
row_init = Fixed::from_integer(source_size.height);
row = (off_y + (row_delta * (span_size.width as i32 - 1)))
% Fixed::from_integer(source_size.height);
row =
(off_y + (row_delta * (len as i32 - 1))) % Fixed::from_integer(source_size.height);
end = (row / row_delta) as usize + 1;
acc_err = -row_delta + row % row_delta;
row_delta = -row_delta;
@ -139,9 +137,9 @@ pub(super) fn draw_texture_line(
end += 1;
}
};
end = end.min(span_size.width);
end = end.min(len);
let mut begin = 0;
while begin < span_size.width {
while begin < len {
fetch_blend_pixel(
&mut line_buffer[begin..end],
format,
@ -171,7 +169,7 @@ pub(super) fn draw_texture_line(
end += 1;
}
};
end = end.min(span_size.width);
end = end.min(len);
}
};
@ -283,6 +281,8 @@ pub(super) fn draw_rounded_rectangle_line(
line: PhysicalLength,
rr: &super::RoundedRectangle,
line_buffer: &mut [impl TargetPixel],
extra_left_clip: i16,
extra_right_clip: i16,
) {
/// This is an integer shifted by 4 bits.
/// Note: this is not a "fixed point" because multiplication and sqrt operation operate to
@ -313,7 +313,7 @@ pub(super) fn draw_rounded_rectangle_line(
Self(self.0 * rhs.0)
}
}
let pos_x = span.origin.x as usize;
let width = line_buffer.len();
let y1 = (line - span.origin.y_length()) + rr.top_clip;
let y2 = (span.origin.y_length() + span.size.height_length() - line) + rr.bottom_clip
- PhysicalLength::new(1);
@ -334,7 +334,8 @@ pub(super) fn draw_rounded_rectangle_line(
}
};
let rev = |x: Shifted| {
(Shifted::new(span.size.width) + Shifted::new(rr.right_clip.get())).saturating_sub(x)
(Shifted::new(width) + Shifted::new(rr.right_clip.get() + extra_right_clip))
.saturating_sub(x)
};
let calculate_xxxx = |r: i16, y: i16| {
let r = Shifted::new(r);
@ -374,10 +375,10 @@ pub(super) fn draw_rounded_rectangle_line(
(x1, x2, x3, x4, rev(x5), rev(x6), rev(x7), rev(x8))
};
anti_alias(
x1.saturating_sub(Shifted::new(rr.left_clip.get())),
x2.saturating_sub(Shifted::new(rr.left_clip.get())),
x1.saturating_sub(Shifted::new(rr.left_clip.get() + extra_left_clip)),
x2.saturating_sub(Shifted::new(rr.left_clip.get() + extra_left_clip)),
&mut |x, cov| {
if x >= span.size.width as usize {
if x >= width {
return;
}
let c = if border == ZERO { rr.inner_color } else { rr.border_color };
@ -387,55 +388,57 @@ pub(super) fn draw_rounded_rectangle_line(
green: (((c.green as u32) * cov as u32) / 255) as u8,
blue: (((c.blue as u32) * cov as u32) / 255) as u8,
};
line_buffer[pos_x + x].blend(col);
line_buffer[x].blend(col);
},
);
if y < rr.width {
// up or down border (x2 .. x7)
let l = x2.ceil().saturating_sub(rr.left_clip.get() as u32).min(span.size.width as u32)
as usize;
let r = x7.floor().min(span.size.width as u32) as usize;
let l = x2
.ceil()
.saturating_sub((rr.left_clip.get() + extra_left_clip) as u32)
.min(width as u32) as usize;
let r = x7.floor().min(width as u32) as usize;
if l < r {
TargetPixel::blend_slice(&mut line_buffer[pos_x + l..pos_x + r], rr.border_color)
TargetPixel::blend_slice(&mut line_buffer[l..r], rr.border_color)
}
} else {
if border > ZERO {
// 3. draw the border (between x2 and x3)
if ONE + x2 <= x3 {
TargetPixel::blend_slice(
&mut line_buffer[pos_x
+ x2.ceil()
.saturating_sub(rr.left_clip.get() as u32)
.min(span.size.width as u32) as usize
..pos_x
+ x3.floor()
.saturating_sub(rr.left_clip.get() as u32)
.min(span.size.width as u32)
as usize],
&mut line_buffer[x2
.ceil()
.saturating_sub((rr.left_clip.get() + extra_left_clip) as u32)
.min(width as u32) as usize
..x3.floor()
.saturating_sub((rr.left_clip.get() + extra_left_clip) as u32)
.min(width as u32) as usize],
rr.border_color,
)
}
// 4. anti-aliasing for the contents (x3 .. x4)
anti_alias(
x3.saturating_sub(Shifted::new(rr.left_clip.get())),
x4.saturating_sub(Shifted::new(rr.left_clip.get())),
x3.saturating_sub(Shifted::new(rr.left_clip.get() + extra_left_clip)),
x4.saturating_sub(Shifted::new(rr.left_clip.get() + extra_left_clip)),
&mut |x, cov| {
if x >= span.size.width as usize {
if x >= width {
return;
}
let col = interpolate_color(cov, rr.border_color, rr.inner_color);
line_buffer[pos_x + x].blend(col);
line_buffer[x].blend(col);
},
);
}
if rr.inner_color.alpha > 0 {
// 5. inside (x4 .. x5)
let begin =
x4.ceil().saturating_sub(rr.left_clip.get() as u32).min(span.size.width as u32);
let end = x5.floor().min(span.size.width as u32);
let begin = x4
.ceil()
.saturating_sub((rr.left_clip.get() + extra_left_clip) as u32)
.min(width as u32);
let end = x5.floor().min(width as u32);
if begin < end {
TargetPixel::blend_slice(
&mut line_buffer[pos_x + begin as usize..pos_x + end as usize],
&mut line_buffer[begin as usize..end as usize],
rr.inner_color,
)
}
@ -443,24 +446,24 @@ pub(super) fn draw_rounded_rectangle_line(
if border > ZERO {
// 6. border anti-aliasing: x5..x6
anti_alias(x5, x6, &mut |x, cov| {
if x >= span.size.width as usize {
if x >= width {
return;
}
let col = interpolate_color(cov, rr.inner_color, rr.border_color);
line_buffer[pos_x + x].blend(col)
line_buffer[x].blend(col)
});
// 7. border x6 .. x7
if ONE + x6 <= x7 {
TargetPixel::blend_slice(
&mut line_buffer[pos_x + x6.ceil().min(span.size.width as u32) as usize
..pos_x + x7.floor().min(span.size.width as u32) as usize],
&mut line_buffer[x6.ceil().min(width as u32) as usize
..x7.floor().min(width as u32) as usize],
rr.border_color,
)
}
}
}
anti_alias(x7, x8, &mut |x, cov| {
if x >= span.size.width as usize {
if x >= width {
return;
}
let c = if border == ZERO { rr.inner_color } else { rr.border_color };
@ -470,7 +473,7 @@ pub(super) fn draw_rounded_rectangle_line(
green: (((c.green as u32) * (255 - cov) as u32) / 255) as u8,
blue: (((c.blue as u32) * (255 - cov) as u32) / 255) as u8,
};
line_buffer[pos_x + x].blend(col);
line_buffer[x].blend(col);
});
}
@ -505,11 +508,9 @@ pub(super) fn draw_gradient_line(
rect: &PhysicalRect,
line: PhysicalLength,
g: &super::GradientCommand,
line_buffer: &mut [impl TargetPixel],
mut buffer: &mut [impl TargetPixel],
extra_left_clip: i16,
) {
let mut buffer = &mut line_buffer
[rect.origin.x as usize..(rect.origin.x_length() + rect.width_length()).get() as usize];
let fill_col1 = g.flags & 0b010 != 0;
let fill_col2 = g.flags & 0b100 != 0;
let invert_slope = g.flags & 0b1 != 0;
@ -539,7 +540,8 @@ pub(super) fn draw_gradient_line(
(y * size_x * (255 - start)) / (size_y * start)
} else {
(size_y - y) * size_x * (255 - start) / (size_y * start)
} + g.left_clip.get() as i32;
} + g.left_clip.get() as i32
+ extra_left_clip as i32;
let len = ((255 * size_x) / start) as usize;