mirror of
https://github.com/RGBCube/serenity
synced 2025-07-26 06:47:34 +00:00
LibGfx: Correctly shift pixels during bilinear blend
Our bilinear scaling logic worked well for upscaling, but during downscaling the bitmap was often shifted one pixel to the bottom right. This is a common problem, described here in more detail: https://bartwronski.com/2021/02/15/bilinear-down-upsampling-pixel-grids-and-that-half-pixel-offset/ Fix it by calculating coordinate shift values that align the pixel's boundaries between the source and target pixels before selecting the source pixels to interpolate.
This commit is contained in:
parent
178164808c
commit
15a9fca461
1 changed files with 11 additions and 7 deletions
|
@ -1154,7 +1154,8 @@ ALWAYS_INLINE static void do_draw_scaled_bitmap(Gfx::Bitmap& target, IntRect con
|
|||
bool has_opacity = opacity != 1.0f;
|
||||
i64 shift = (i64)1 << 32;
|
||||
i64 fractional_mask = (shift - (u64)1);
|
||||
i64 half_pixel = (i64)1 << 31;
|
||||
i64 bilinear_offset_x = (1ll << 31) * (src_rect.width() / dst_rect.width() - 1);
|
||||
i64 bilinear_offset_y = (1ll << 31) * (src_rect.height() / dst_rect.height() - 1);
|
||||
i64 hscale = (src_rect.width() * shift) / dst_rect.width();
|
||||
i64 vscale = (src_rect.height() * shift) / dst_rect.height();
|
||||
i64 src_left = src_rect.left() * shift;
|
||||
|
@ -1175,13 +1176,16 @@ ALWAYS_INLINE static void do_draw_scaled_bitmap(Gfx::Bitmap& target, IntRect con
|
|||
|
||||
Color src_pixel;
|
||||
if constexpr (scaling_mode == Painter::ScalingMode::BilinearBlend) {
|
||||
auto scaled_x0 = clamp((desired_x - half_pixel) >> 32, clipped_src_rect.left(), clipped_src_rect.right());
|
||||
auto scaled_x1 = clamp((desired_x + half_pixel) >> 32, clipped_src_rect.left(), clipped_src_rect.right());
|
||||
auto scaled_y0 = clamp((desired_y - half_pixel) >> 32, clipped_src_rect.top(), clipped_src_rect.bottom());
|
||||
auto scaled_y1 = clamp((desired_y + half_pixel) >> 32, clipped_src_rect.top(), clipped_src_rect.bottom());
|
||||
auto shifted_x = desired_x + bilinear_offset_x;
|
||||
auto shifted_y = desired_y + bilinear_offset_y;
|
||||
|
||||
float x_ratio = (((desired_x + half_pixel) & fractional_mask) / (float)shift);
|
||||
float y_ratio = (((desired_y + half_pixel) & fractional_mask) / (float)shift);
|
||||
auto scaled_x0 = clamp(shifted_x >> 32, clipped_src_rect.left(), clipped_src_rect.right());
|
||||
auto scaled_x1 = clamp((shifted_x >> 32) + 1, clipped_src_rect.left(), clipped_src_rect.right());
|
||||
auto scaled_y0 = clamp(shifted_y >> 32, clipped_src_rect.top(), clipped_src_rect.bottom());
|
||||
auto scaled_y1 = clamp((shifted_y >> 32) + 1, clipped_src_rect.top(), clipped_src_rect.bottom());
|
||||
|
||||
float x_ratio = (shifted_x & fractional_mask) / static_cast<float>(shift);
|
||||
float y_ratio = (shifted_y & fractional_mask) / static_cast<float>(shift);
|
||||
|
||||
auto top_left = get_pixel(source, scaled_x0, scaled_y0);
|
||||
auto top_right = get_pixel(source, scaled_x1, scaled_y0);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue