diff options
author | Nico Weber <thakis@chromium.org> | 2021-01-20 09:59:22 -0500 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2021-01-20 21:01:48 +0100 |
commit | 91aa0d99973163a8b7c37dc5c7535b794546d3e0 (patch) | |
tree | c363e5627f9c2f7274771ee5ac3de3d3b7737b20 | |
parent | 362bde4a863a8505dabeb332343e06fcc2ae8ad6 (diff) | |
download | serenity-91aa0d99973163a8b7c37dc5c7535b794546d3e0.zip |
LibGfx/Painter: Keep translation and clip_rect in logical coordinates
Moves Painter away from general affine transport support a bit, but
this scale factor business does feel like it's a bit different.
This is conceptually cleaner (everything should use logical coordinates
as much as possible), and it means the code in GUI::Painter() will work
without changes due to that, but the draw function implementations
overall get a bit murkier (draw_rect() becomes nicer though). Still,
feels like the right direction.
No behavior change.
-rw-r--r-- | Userland/Libraries/LibGfx/Painter.cpp | 129 | ||||
-rw-r--r-- | Userland/Libraries/LibGfx/Painter.h | 8 |
2 files changed, 73 insertions, 64 deletions
diff --git a/Userland/Libraries/LibGfx/Painter.cpp b/Userland/Libraries/LibGfx/Painter.cpp index f8065a9ea8..8626d8c700 100644 --- a/Userland/Libraries/LibGfx/Painter.cpp +++ b/Userland/Libraries/LibGfx/Painter.cpp @@ -77,7 +77,7 @@ Painter::Painter(Gfx::Bitmap& bitmap) ASSERT(bitmap.physical_height() % scale == 0); m_state_stack.append(State()); state().font = &FontDatabase::default_font(); - state().clip_rect = { { 0, 0 }, bitmap.physical_size() }; + state().clip_rect = { { 0, 0 }, bitmap.size() }; state().scale = scale; m_clip_origin = state().clip_rect; } @@ -106,11 +106,12 @@ void Painter::fill_rect_with_draw_op(const IntRect& a_rect, Color color) void Painter::clear_rect(const IntRect& a_rect, Color color) { - auto rect = to_physical(a_rect).intersected(clip_rect()); + auto rect = a_rect.translated(translation()).intersected(clip_rect()); if (rect.is_empty()) return; - ASSERT(m_target->physical_rect().contains(rect)); + ASSERT(m_target->rect().contains(rect)); + rect *= scale(); RGBA32* dst = m_target->scanline(rect.top()) + rect.left(); const size_t dst_skip = m_target->pitch() / sizeof(RGBA32); @@ -121,18 +122,14 @@ void Painter::clear_rect(const IntRect& a_rect, Color color) } } -void Painter::fill_physical_rect(const IntRect& a_physical_rect, Color color) +void Painter::fill_physical_rect(const IntRect& physical_rect, Color color) { - auto rect = a_physical_rect.intersected(clip_rect()); - if (rect.is_empty()) - return; - ASSERT(m_target->physical_rect().contains(rect)); - - RGBA32* dst = m_target->scanline(rect.top()) + rect.left(); + // Callers must do clipping. + RGBA32* dst = m_target->scanline(physical_rect.top()) + physical_rect.left(); const size_t dst_skip = m_target->pitch() / sizeof(RGBA32); - for (int i = rect.height() - 1; i >= 0; --i) { - for (int j = 0; j < rect.width(); ++j) + for (int i = physical_rect.height() - 1; i >= 0; --i) { + for (int j = 0; j < physical_rect.width(); ++j) dst[j] = Color::from_rgba(dst[j]).blend(color).value(); dst += dst_skip; } @@ -153,7 +150,12 @@ void Painter::fill_rect(const IntRect& a_rect, Color color) return; } - fill_physical_rect(to_physical(a_rect), color); + auto rect = a_rect.translated(translation()).intersected(clip_rect()); + if (rect.is_empty()) + return; + ASSERT(m_target->rect().contains(rect)); + + fill_physical_rect(rect * scale(), color); } void Painter::fill_rect_with_dither_pattern(const IntRect& a_rect, Color color_a, Color color_b) @@ -209,7 +211,7 @@ void Painter::fill_rect_with_gradient(Orientation orientation, const IntRect& a_ #endif auto rect = to_physical(a_rect); - auto clipped_rect = IntRect::intersection(rect, clip_rect()); + auto clipped_rect = IntRect::intersection(rect, clip_rect() * scale()); if (clipped_rect.is_empty()) return; @@ -323,35 +325,28 @@ void Painter::draw_focus_rect(const IntRect& rect, Color color) void Painter::draw_rect(const IntRect& a_rect, Color color, bool rough) { - IntRect rect = to_physical(a_rect); + IntRect rect = a_rect.translated(translation()); auto clipped_rect = rect.intersected(clip_rect()); if (clipped_rect.is_empty()) return; int min_y = clipped_rect.top(); int max_y = clipped_rect.bottom(); - - // Don't use rect.bottom() / right() when dealing with physical rects: They will be off by scale()-1 physical pixels. - // (It's fine to use them when comparing bottom() / right() to other physical rects, since then both rects are off by the same amount. - // But don't use them for pixel access.) - int max_y_rounded_to_logical_increment = clipped_rect.top() + clipped_rect.height() - scale(); - int max_x_rounded_to_logical_increment = clipped_rect.left() + clipped_rect.width() - scale(); + int scale = this->scale(); if (rect.top() >= clipped_rect.top() && rect.top() <= clipped_rect.bottom()) { - int start_x = rough ? max(rect.x() + scale(), clipped_rect.x()) : clipped_rect.x(); - int width = rough ? min(rect.width() - 2 * scale(), clipped_rect.width()) : clipped_rect.width(); - for (int i = 0; i < scale(); ++i) { - fill_physical_scanline_with_draw_op(rect.top() + i, start_x, width, color); - ++min_y; - } + int start_x = rough ? max(rect.x() + 1, clipped_rect.x()) : clipped_rect.x(); + int width = rough ? min(rect.width() - 2, clipped_rect.width()) : clipped_rect.width(); + for (int i = 0; i < scale; ++i) + fill_physical_scanline_with_draw_op(rect.top() * scale + i, start_x * scale, width * scale, color); + ++min_y; } if (rect.bottom() >= clipped_rect.top() && rect.bottom() <= clipped_rect.bottom()) { - int start_x = rough ? max(rect.x() + scale(), clipped_rect.x()) : clipped_rect.x(); - int width = rough ? min(rect.width() - 2 * scale(), clipped_rect.width()) : clipped_rect.width(); - for (int i = 0; i < scale(); ++i) { - fill_physical_scanline_with_draw_op(max_y_rounded_to_logical_increment + i, start_x, width, color); - --max_y; - } + int start_x = rough ? max(rect.x() + 1, clipped_rect.x()) : clipped_rect.x(); + int width = rough ? min(rect.width() - 2, clipped_rect.width()) : clipped_rect.width(); + for (int i = 0; i < scale; ++i) + fill_physical_scanline_with_draw_op(max_y * scale + i, start_x * scale, width * scale, color); + --max_y; } bool draw_left_side = rect.left() >= clipped_rect.left(); @@ -359,22 +354,22 @@ void Painter::draw_rect(const IntRect& a_rect, Color color, bool rough) if (draw_left_side && draw_right_side) { // Specialized loop when drawing both sides. - for (int y = min_y; y <= max_y; ++y) { + for (int y = min_y * scale; y <= max_y * scale; ++y) { auto* bits = m_target->scanline(y); - for (int i = 0; i < scale(); ++i) - set_physical_pixel_with_draw_op(bits[rect.left() + i], color); - for (int i = 0; i < scale(); ++i) - set_physical_pixel_with_draw_op(bits[max_x_rounded_to_logical_increment + i], color); + for (int i = 0; i < scale; ++i) + set_physical_pixel_with_draw_op(bits[rect.left() * scale + i], color); + for (int i = 0; i < scale; ++i) + set_physical_pixel_with_draw_op(bits[rect.right() * scale + i], color); } } else { - for (int y = min_y; y <= max_y; ++y) { + for (int y = min_y * scale; y <= max_y * scale; ++y) { auto* bits = m_target->scanline(y); if (draw_left_side) - for (int i = 0; i < scale(); ++i) - set_physical_pixel_with_draw_op(bits[rect.left() + i], color); + for (int i = 0; i < scale; ++i) + set_physical_pixel_with_draw_op(bits[rect.left() * scale + i], color); if (draw_right_side) - for (int i = 0; i < scale(); ++i) - set_physical_pixel_with_draw_op(bits[max_x_rounded_to_logical_increment + i], color); + for (int i = 0; i < scale; ++i) + set_physical_pixel_with_draw_op(bits[rect.right() * scale + i], color); } } } @@ -409,7 +404,7 @@ void Painter::draw_bitmap(const IntPoint& p, const CharacterBitmap& bitmap, Colo void Painter::draw_bitmap(const IntPoint& p, const GlyphBitmap& bitmap, Color color) { - auto dst_rect = to_physical(IntRect(p, bitmap.size())); + auto dst_rect = IntRect(p, bitmap.size()).translated(translation()); auto clipped_rect = dst_rect.intersected(clip_rect()); if (clipped_rect.is_empty()) return; @@ -417,10 +412,12 @@ void Painter::draw_bitmap(const IntPoint& p, const GlyphBitmap& bitmap, Color co const int last_row = clipped_rect.bottom() - dst_rect.top(); const int first_column = clipped_rect.left() - dst_rect.left(); const int last_column = clipped_rect.right() - dst_rect.left(); - RGBA32* dst = m_target->scanline(clipped_rect.y()) + clipped_rect.x(); + + int scale = this->scale(); + RGBA32* dst = m_target->scanline(clipped_rect.y() * scale) + clipped_rect.x() * scale; const size_t dst_skip = m_target->pitch() / sizeof(RGBA32); - if (scale() == 1) { + if (scale == 1) { for (int row = first_row; row <= last_row; ++row) { for (int j = 0; j <= (last_column - first_column); ++j) { if (bitmap.bit_at(j + first_column, row)) @@ -431,10 +428,13 @@ void Painter::draw_bitmap(const IntPoint& p, const GlyphBitmap& bitmap, Color co } else { for (int row = first_row; row <= last_row; ++row) { for (int j = 0; j <= (last_column - first_column); ++j) { - if (bitmap.bit_at((j + first_column) / scale(), row / scale())) - dst[j] = color.value(); + if (bitmap.bit_at((j + first_column), row)) { + for (int iy = 0; iy < scale; ++iy) + for (int ix = 0; ix < scale; ++ix) + dst[j * scale + ix + iy * dst_skip] = color.value(); + } } - dst += dst_skip; + dst += dst_skip * scale; } } } @@ -701,16 +701,22 @@ void Painter::blit_with_alpha(const IntPoint& position, const Gfx::Bitmap& sourc ASSERT(source.has_alpha_channel()); IntRect safe_src_rect = a_src_rect.intersected(source.rect()); - auto dst_rect = to_physical(IntRect(position, safe_src_rect.size())); - auto src_rect = a_src_rect * source.scale(); + auto dst_rect = IntRect(position, safe_src_rect.size()).translated(translation()); auto clipped_rect = dst_rect.intersected(clip_rect()); if (clipped_rect.is_empty()) return; + + int scale = this->scale(); + auto src_rect = a_src_rect * scale; + clipped_rect *= scale; + dst_rect *= scale; + const int first_row = clipped_rect.top() - dst_rect.top(); const int last_row = clipped_rect.bottom() - dst_rect.top(); const int first_column = clipped_rect.left() - dst_rect.left(); const int last_column = clipped_rect.right() - dst_rect.left(); + RGBA32* dst = m_target->scanline(clipped_rect.y()) + clipped_rect.x(); const RGBA32* src = source.scanline(src_rect.top() + first_row) + src_rect.left() + first_column; const size_t dst_skip = m_target->pitch() / sizeof(RGBA32); @@ -744,15 +750,19 @@ void Painter::blit(const IntPoint& position, const Gfx::Bitmap& source, const In // If we get here, the Painter might have a scale factor, but the source bitmap has the same scale factor. // We need to transform from logical to physical coordinates, but we can just copy pixels without resampling. - // All computations below are in physical coordinates (except for safe_src_rect). auto safe_src_rect = a_src_rect.intersected(source.rect()); ASSERT(source.rect().contains(safe_src_rect)); - auto dst_rect = to_physical(IntRect(position, safe_src_rect.size())); - auto src_rect = a_src_rect * source.scale(); - + auto dst_rect = IntRect(position, safe_src_rect.size()).translated(translation()); auto clipped_rect = dst_rect.intersected(clip_rect()); if (clipped_rect.is_empty()) return; + + // All computations below are in physical coordinates. + int scale = this->scale(); + auto src_rect = a_src_rect * scale; + clipped_rect *= scale; + dst_rect *= scale; + const int first_row = clipped_rect.top() - dst_rect.top(); const int last_row = clipped_rect.bottom() - dst_rect.top(); const int first_column = clipped_rect.left() - dst_rect.left(); @@ -849,8 +859,7 @@ void Painter::draw_scaled_bitmap(const IntRect& a_dst_rect, const Gfx::Bitmap& s auto dst_rect = to_physical(a_dst_rect); auto src_rect = a_src_rect * source.scale(); - - auto clipped_rect = dst_rect.intersected(clip_rect()); + auto clipped_rect = dst_rect.intersected(clip_rect() * scale()); if (clipped_rect.is_empty()) return; @@ -1261,7 +1270,7 @@ void Painter::draw_line(const IntPoint& p1, const IntPoint& p2, Color color, int if (color.alpha() == 0) return; - auto clip_rect = this->clip_rect(); + auto clip_rect = this->clip_rect() * scale(); auto point1 = to_physical(p1); auto point2 = to_physical(p2); @@ -1495,8 +1504,8 @@ void Painter::draw_elliptical_arc(const IntPoint& p1, const IntPoint& p2, const void Painter::add_clip_rect(const IntRect& rect) { - state().clip_rect.intersect(to_physical(rect)); - state().clip_rect.intersect(m_target->physical_rect()); // FIXME: This shouldn't be necessary? + state().clip_rect.intersect(rect.translated(translation())); + state().clip_rect.intersect(m_target->rect()); // FIXME: This shouldn't be necessary? } void Painter::clear_clip_rect() diff --git a/Userland/Libraries/LibGfx/Painter.h b/Userland/Libraries/LibGfx/Painter.h index d070d2a830..d6b0eedd99 100644 --- a/Userland/Libraries/LibGfx/Painter.h +++ b/Userland/Libraries/LibGfx/Painter.h @@ -118,7 +118,7 @@ public: void clear_clip_rect(); void translate(int dx, int dy) { translate({ dx, dy }); } - void translate(const IntPoint& delta) { state().translation.move_by(delta * state().scale); } + void translate(const IntPoint& delta) { state().translation.move_by(delta); } Gfx::Bitmap* target() { return m_target.ptr(); } @@ -131,8 +131,8 @@ public: protected: IntPoint translation() const { return state().translation; } - IntRect to_physical(const IntRect& r) const { return (r * scale()).translated(translation()); } - IntPoint to_physical(const IntPoint& p) const { return (p * scale()).translated(translation()); } + IntRect to_physical(const IntRect& r) const { return r.translated(translation()) * scale(); } + IntPoint to_physical(const IntPoint& p) const { return p.translated(translation()) * scale(); } int scale() const { return state().scale; } void set_physical_pixel_with_draw_op(u32& pixel, const Color&); void fill_physical_scanline_with_draw_op(int y, int x, int width, const Color& color); @@ -146,7 +146,7 @@ protected: const Font* font; IntPoint translation; int scale = 1; - IntRect clip_rect; // In physical coordinates. + IntRect clip_rect; DrawOp draw_op; }; |