Skip to content

Commit 91aa0d9

Browse files
nicoawesomekling
authored andcommitted
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.
1 parent 362bde4 commit 91aa0d9

File tree

2 files changed

+73
-64
lines changed

2 files changed

+73
-64
lines changed

Userland/Libraries/LibGfx/Painter.cpp

Lines changed: 69 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ Painter::Painter(Gfx::Bitmap& bitmap)
7777
ASSERT(bitmap.physical_height() % scale == 0);
7878
m_state_stack.append(State());
7979
state().font = &FontDatabase::default_font();
80-
state().clip_rect = { { 0, 0 }, bitmap.physical_size() };
80+
state().clip_rect = { { 0, 0 }, bitmap.size() };
8181
state().scale = scale;
8282
m_clip_origin = state().clip_rect;
8383
}
@@ -106,11 +106,12 @@ void Painter::fill_rect_with_draw_op(const IntRect& a_rect, Color color)
106106

107107
void Painter::clear_rect(const IntRect& a_rect, Color color)
108108
{
109-
auto rect = to_physical(a_rect).intersected(clip_rect());
109+
auto rect = a_rect.translated(translation()).intersected(clip_rect());
110110
if (rect.is_empty())
111111
return;
112112

113-
ASSERT(m_target->physical_rect().contains(rect));
113+
ASSERT(m_target->rect().contains(rect));
114+
rect *= scale();
114115

115116
RGBA32* dst = m_target->scanline(rect.top()) + rect.left();
116117
const size_t dst_skip = m_target->pitch() / sizeof(RGBA32);
@@ -121,18 +122,14 @@ void Painter::clear_rect(const IntRect& a_rect, Color color)
121122
}
122123
}
123124

124-
void Painter::fill_physical_rect(const IntRect& a_physical_rect, Color color)
125+
void Painter::fill_physical_rect(const IntRect& physical_rect, Color color)
125126
{
126-
auto rect = a_physical_rect.intersected(clip_rect());
127-
if (rect.is_empty())
128-
return;
129-
ASSERT(m_target->physical_rect().contains(rect));
130-
131-
RGBA32* dst = m_target->scanline(rect.top()) + rect.left();
127+
// Callers must do clipping.
128+
RGBA32* dst = m_target->scanline(physical_rect.top()) + physical_rect.left();
132129
const size_t dst_skip = m_target->pitch() / sizeof(RGBA32);
133130

134-
for (int i = rect.height() - 1; i >= 0; --i) {
135-
for (int j = 0; j < rect.width(); ++j)
131+
for (int i = physical_rect.height() - 1; i >= 0; --i) {
132+
for (int j = 0; j < physical_rect.width(); ++j)
136133
dst[j] = Color::from_rgba(dst[j]).blend(color).value();
137134
dst += dst_skip;
138135
}
@@ -153,7 +150,12 @@ void Painter::fill_rect(const IntRect& a_rect, Color color)
153150
return;
154151
}
155152

156-
fill_physical_rect(to_physical(a_rect), color);
153+
auto rect = a_rect.translated(translation()).intersected(clip_rect());
154+
if (rect.is_empty())
155+
return;
156+
ASSERT(m_target->rect().contains(rect));
157+
158+
fill_physical_rect(rect * scale(), color);
157159
}
158160

159161
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_
209211
#endif
210212

211213
auto rect = to_physical(a_rect);
212-
auto clipped_rect = IntRect::intersection(rect, clip_rect());
214+
auto clipped_rect = IntRect::intersection(rect, clip_rect() * scale());
213215
if (clipped_rect.is_empty())
214216
return;
215217

@@ -323,58 +325,51 @@ void Painter::draw_focus_rect(const IntRect& rect, Color color)
323325

324326
void Painter::draw_rect(const IntRect& a_rect, Color color, bool rough)
325327
{
326-
IntRect rect = to_physical(a_rect);
328+
IntRect rect = a_rect.translated(translation());
327329
auto clipped_rect = rect.intersected(clip_rect());
328330
if (clipped_rect.is_empty())
329331
return;
330332

331333
int min_y = clipped_rect.top();
332334
int max_y = clipped_rect.bottom();
333-
334-
// Don't use rect.bottom() / right() when dealing with physical rects: They will be off by scale()-1 physical pixels.
335-
// (It's fine to use them when comparing bottom() / right() to other physical rects, since then both rects are off by the same amount.
336-
// But don't use them for pixel access.)
337-
int max_y_rounded_to_logical_increment = clipped_rect.top() + clipped_rect.height() - scale();
338-
int max_x_rounded_to_logical_increment = clipped_rect.left() + clipped_rect.width() - scale();
335+
int scale = this->scale();
339336

340337
if (rect.top() >= clipped_rect.top() && rect.top() <= clipped_rect.bottom()) {
341-
int start_x = rough ? max(rect.x() + scale(), clipped_rect.x()) : clipped_rect.x();
342-
int width = rough ? min(rect.width() - 2 * scale(), clipped_rect.width()) : clipped_rect.width();
343-
for (int i = 0; i < scale(); ++i) {
344-
fill_physical_scanline_with_draw_op(rect.top() + i, start_x, width, color);
345-
++min_y;
346-
}
338+
int start_x = rough ? max(rect.x() + 1, clipped_rect.x()) : clipped_rect.x();
339+
int width = rough ? min(rect.width() - 2, clipped_rect.width()) : clipped_rect.width();
340+
for (int i = 0; i < scale; ++i)
341+
fill_physical_scanline_with_draw_op(rect.top() * scale + i, start_x * scale, width * scale, color);
342+
++min_y;
347343
}
348344
if (rect.bottom() >= clipped_rect.top() && rect.bottom() <= clipped_rect.bottom()) {
349-
int start_x = rough ? max(rect.x() + scale(), clipped_rect.x()) : clipped_rect.x();
350-
int width = rough ? min(rect.width() - 2 * scale(), clipped_rect.width()) : clipped_rect.width();
351-
for (int i = 0; i < scale(); ++i) {
352-
fill_physical_scanline_with_draw_op(max_y_rounded_to_logical_increment + i, start_x, width, color);
353-
--max_y;
354-
}
345+
int start_x = rough ? max(rect.x() + 1, clipped_rect.x()) : clipped_rect.x();
346+
int width = rough ? min(rect.width() - 2, clipped_rect.width()) : clipped_rect.width();
347+
for (int i = 0; i < scale; ++i)
348+
fill_physical_scanline_with_draw_op(max_y * scale + i, start_x * scale, width * scale, color);
349+
--max_y;
355350
}
356351

357352
bool draw_left_side = rect.left() >= clipped_rect.left();
358353
bool draw_right_side = rect.right() == clipped_rect.right();
359354

360355
if (draw_left_side && draw_right_side) {
361356
// Specialized loop when drawing both sides.
362-
for (int y = min_y; y <= max_y; ++y) {
357+
for (int y = min_y * scale; y <= max_y * scale; ++y) {
363358
auto* bits = m_target->scanline(y);
364-
for (int i = 0; i < scale(); ++i)
365-
set_physical_pixel_with_draw_op(bits[rect.left() + i], color);
366-
for (int i = 0; i < scale(); ++i)
367-
set_physical_pixel_with_draw_op(bits[max_x_rounded_to_logical_increment + i], color);
359+
for (int i = 0; i < scale; ++i)
360+
set_physical_pixel_with_draw_op(bits[rect.left() * scale + i], color);
361+
for (int i = 0; i < scale; ++i)
362+
set_physical_pixel_with_draw_op(bits[rect.right() * scale + i], color);
368363
}
369364
} else {
370-
for (int y = min_y; y <= max_y; ++y) {
365+
for (int y = min_y * scale; y <= max_y * scale; ++y) {
371366
auto* bits = m_target->scanline(y);
372367
if (draw_left_side)
373-
for (int i = 0; i < scale(); ++i)
374-
set_physical_pixel_with_draw_op(bits[rect.left() + i], color);
368+
for (int i = 0; i < scale; ++i)
369+
set_physical_pixel_with_draw_op(bits[rect.left() * scale + i], color);
375370
if (draw_right_side)
376-
for (int i = 0; i < scale(); ++i)
377-
set_physical_pixel_with_draw_op(bits[max_x_rounded_to_logical_increment + i], color);
371+
for (int i = 0; i < scale; ++i)
372+
set_physical_pixel_with_draw_op(bits[rect.right() * scale + i], color);
378373
}
379374
}
380375
}
@@ -409,18 +404,20 @@ void Painter::draw_bitmap(const IntPoint& p, const CharacterBitmap& bitmap, Colo
409404

410405
void Painter::draw_bitmap(const IntPoint& p, const GlyphBitmap& bitmap, Color color)
411406
{
412-
auto dst_rect = to_physical(IntRect(p, bitmap.size()));
407+
auto dst_rect = IntRect(p, bitmap.size()).translated(translation());
413408
auto clipped_rect = dst_rect.intersected(clip_rect());
414409
if (clipped_rect.is_empty())
415410
return;
416411
const int first_row = clipped_rect.top() - dst_rect.top();
417412
const int last_row = clipped_rect.bottom() - dst_rect.top();
418413
const int first_column = clipped_rect.left() - dst_rect.left();
419414
const int last_column = clipped_rect.right() - dst_rect.left();
420-
RGBA32* dst = m_target->scanline(clipped_rect.y()) + clipped_rect.x();
415+
416+
int scale = this->scale();
417+
RGBA32* dst = m_target->scanline(clipped_rect.y() * scale) + clipped_rect.x() * scale;
421418
const size_t dst_skip = m_target->pitch() / sizeof(RGBA32);
422419

423-
if (scale() == 1) {
420+
if (scale == 1) {
424421
for (int row = first_row; row <= last_row; ++row) {
425422
for (int j = 0; j <= (last_column - first_column); ++j) {
426423
if (bitmap.bit_at(j + first_column, row))
@@ -431,10 +428,13 @@ void Painter::draw_bitmap(const IntPoint& p, const GlyphBitmap& bitmap, Color co
431428
} else {
432429
for (int row = first_row; row <= last_row; ++row) {
433430
for (int j = 0; j <= (last_column - first_column); ++j) {
434-
if (bitmap.bit_at((j + first_column) / scale(), row / scale()))
435-
dst[j] = color.value();
431+
if (bitmap.bit_at((j + first_column), row)) {
432+
for (int iy = 0; iy < scale; ++iy)
433+
for (int ix = 0; ix < scale; ++ix)
434+
dst[j * scale + ix + iy * dst_skip] = color.value();
435+
}
436436
}
437-
dst += dst_skip;
437+
dst += dst_skip * scale;
438438
}
439439
}
440440
}
@@ -701,16 +701,22 @@ void Painter::blit_with_alpha(const IntPoint& position, const Gfx::Bitmap& sourc
701701

702702
ASSERT(source.has_alpha_channel());
703703
IntRect safe_src_rect = a_src_rect.intersected(source.rect());
704-
auto dst_rect = to_physical(IntRect(position, safe_src_rect.size()));
705-
auto src_rect = a_src_rect * source.scale();
704+
auto dst_rect = IntRect(position, safe_src_rect.size()).translated(translation());
706705

707706
auto clipped_rect = dst_rect.intersected(clip_rect());
708707
if (clipped_rect.is_empty())
709708
return;
709+
710+
int scale = this->scale();
711+
auto src_rect = a_src_rect * scale;
712+
clipped_rect *= scale;
713+
dst_rect *= scale;
714+
710715
const int first_row = clipped_rect.top() - dst_rect.top();
711716
const int last_row = clipped_rect.bottom() - dst_rect.top();
712717
const int first_column = clipped_rect.left() - dst_rect.left();
713718
const int last_column = clipped_rect.right() - dst_rect.left();
719+
714720
RGBA32* dst = m_target->scanline(clipped_rect.y()) + clipped_rect.x();
715721
const RGBA32* src = source.scanline(src_rect.top() + first_row) + src_rect.left() + first_column;
716722
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
744750

745751
// If we get here, the Painter might have a scale factor, but the source bitmap has the same scale factor.
746752
// We need to transform from logical to physical coordinates, but we can just copy pixels without resampling.
747-
// All computations below are in physical coordinates (except for safe_src_rect).
748753
auto safe_src_rect = a_src_rect.intersected(source.rect());
749754
ASSERT(source.rect().contains(safe_src_rect));
750-
auto dst_rect = to_physical(IntRect(position, safe_src_rect.size()));
751-
auto src_rect = a_src_rect * source.scale();
752-
755+
auto dst_rect = IntRect(position, safe_src_rect.size()).translated(translation());
753756
auto clipped_rect = dst_rect.intersected(clip_rect());
754757
if (clipped_rect.is_empty())
755758
return;
759+
760+
// All computations below are in physical coordinates.
761+
int scale = this->scale();
762+
auto src_rect = a_src_rect * scale;
763+
clipped_rect *= scale;
764+
dst_rect *= scale;
765+
756766
const int first_row = clipped_rect.top() - dst_rect.top();
757767
const int last_row = clipped_rect.bottom() - dst_rect.top();
758768
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
849859

850860
auto dst_rect = to_physical(a_dst_rect);
851861
auto src_rect = a_src_rect * source.scale();
852-
853-
auto clipped_rect = dst_rect.intersected(clip_rect());
862+
auto clipped_rect = dst_rect.intersected(clip_rect() * scale());
854863
if (clipped_rect.is_empty())
855864
return;
856865

@@ -1261,7 +1270,7 @@ void Painter::draw_line(const IntPoint& p1, const IntPoint& p2, Color color, int
12611270
if (color.alpha() == 0)
12621271
return;
12631272

1264-
auto clip_rect = this->clip_rect();
1273+
auto clip_rect = this->clip_rect() * scale();
12651274

12661275
auto point1 = to_physical(p1);
12671276
auto point2 = to_physical(p2);
@@ -1495,8 +1504,8 @@ void Painter::draw_elliptical_arc(const IntPoint& p1, const IntPoint& p2, const
14951504

14961505
void Painter::add_clip_rect(const IntRect& rect)
14971506
{
1498-
state().clip_rect.intersect(to_physical(rect));
1499-
state().clip_rect.intersect(m_target->physical_rect()); // FIXME: This shouldn't be necessary?
1507+
state().clip_rect.intersect(rect.translated(translation()));
1508+
state().clip_rect.intersect(m_target->rect()); // FIXME: This shouldn't be necessary?
15001509
}
15011510

15021511
void Painter::clear_clip_rect()

Userland/Libraries/LibGfx/Painter.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ class Painter {
118118
void clear_clip_rect();
119119

120120
void translate(int dx, int dy) { translate({ dx, dy }); }
121-
void translate(const IntPoint& delta) { state().translation.move_by(delta * state().scale); }
121+
void translate(const IntPoint& delta) { state().translation.move_by(delta); }
122122

123123
Gfx::Bitmap* target() { return m_target.ptr(); }
124124

@@ -131,8 +131,8 @@ class Painter {
131131

132132
protected:
133133
IntPoint translation() const { return state().translation; }
134-
IntRect to_physical(const IntRect& r) const { return (r * scale()).translated(translation()); }
135-
IntPoint to_physical(const IntPoint& p) const { return (p * scale()).translated(translation()); }
134+
IntRect to_physical(const IntRect& r) const { return r.translated(translation()) * scale(); }
135+
IntPoint to_physical(const IntPoint& p) const { return p.translated(translation()) * scale(); }
136136
int scale() const { return state().scale; }
137137
void set_physical_pixel_with_draw_op(u32& pixel, const Color&);
138138
void fill_physical_scanline_with_draw_op(int y, int x, int width, const Color& color);
@@ -146,7 +146,7 @@ class Painter {
146146
const Font* font;
147147
IntPoint translation;
148148
int scale = 1;
149-
IntRect clip_rect; // In physical coordinates.
149+
IntRect clip_rect;
150150
DrawOp draw_op;
151151
};
152152

0 commit comments

Comments
 (0)