Repository navigation
LibGfx: Implement scale() support in Gfx::Painter - #27096
jmalcolm137 wants to merge 4 commits into
Conversation
fill_physical_rect()'s comment says that callers do clipping, but draw_line() passes rects that can extend past the bottom or right edge of the bitmap. A thick horizontal or vertical line ending on the clip boundary, for example, fills from its start position for `thickness` pixels, which writes out of bounds. Intersect the requested rect with the bitmap's physical size before touching pixels.
draw_rect()'s side loops iterate from min_y * scale to max_y * scale inclusive. At scale > 1 that covers only the first physical row of the final logical row, so the bottom physical row of each side is left undrawn, leaving a one pixel gap just above the bottom edge. Iterate up to (max_y + 1) * scale instead.
Several Painter methods still VERIFY() that the target bitmap's scale is 1. Some drawing primitives already handle a non-unit scale (draw_rect(), draw_line(), draw_bitmap(GlyphBitmap), ...), but the rest do not. Implement the missing scaled paths: - fill_rect_with_draw_op(), fill_rect_with_dither_pattern() and draw_bitmap(CharacterBitmap) expand every logical pixel into a scale x scale block. - fill_rect_with_checkerboard() scales the rects and the cell size before running the existing run-based algorithm, leaving the unscaled path unchanged. - draw_focus_rect() fills a 1x1 rect instead of setting a single physical pixel. - fill_ellipse(), draw_ellipse_intersecting(), draw_quadratic_bezier_curve() and draw_elliptical_arc() already delegate to the scale-aware draw_line(), so their VERIFY() can go. - draw_text() places glyphs through scale-aware glyph drawing paths, so the text overloads no longer have to reject scaling. - get_region_bitmap() returns a bitmap at the painter's scale. Add a test asserting that drawing into a 2x bitmap produces the pixel-doubled image of drawing the same scene into a 1x bitmap.
|
Hello! One or more of the commit messages in this PR do not match the SerenityOS code submission policy, please check the |
Filling a path with a painter whose scale is > 1 hit the VERIFY(painter.scale() == 1) FIXME, which aborted clients that draw paths at 2x -- e.g. a graph's filled area, which crashed on repaint. Rasterise in device space: map logical coordinates to device as (logical + translation) * scale, scale the clip rect, grow the scanline buffer to the device extent, and scale the path edges by scale (subpixel samples by samples_per_pixel * scale). scale == 1 is unchanged.
83a63fe to
073b01e
Compare
|
What's the motivation for this? Usually we only add code that has in-tree callers. |
I have been using LibGUI to build applications on Linux. My MacBook has a Retina display. |
|
So this is for out-of-tree clients? |
Well, I am primarily using this to run all the in-tree clients. But, I am running them on Linux. And yes, I am writing out-of-tree clients as well. This will of course be useful / necessary for running SerenityOS native at HiDPI. But that is not my current use case. |
|
These were labelled as FIXME. There is also an existing bug that mixes logical and physical which gets hidden when those are the same. |
FYI: Some of these FIXMEs were/are "I guess this could be handled, but it's not needed, so I'll leave a FIXME". |
Several
Gfx::Paintermethods stillVERIFY()that the target bitmap'sscale()is 1. Some primitives already support a non-unit scale(
draw_rect(),draw_line(),draw_bitmap(GlyphBitmap), ...), but therest do not. This series implements the missing scaled paths so a
Paintercan render into a bitmap withscale() > 1(for example, into aclient-side HiDPI backing store).
Commits
LibGfx: Keep Gfx::Painter::fill_physical_rect() within the bitmapdraw_line()can pass a rect that overruns the bitmap's edge (a thickhorizontal or vertical line ending on the clip boundary), which
previously wrote out of bounds. Clip against the bitmap's physical size.
LibGfx: Draw all rows of Gfx::Painter::draw_rect() sides at scale > 1The side loops iterated up to
max_y * scaleinclusive, which coversonly the first physical row of the final logical row, leaving a one pixel
gap just above the bottom edge at scale > 1.
LibGfx: Implement scale() support in Gfx::PainterAdds the scaled paths for the rect fills (including dither and
checkerboard patterns),
draw_bitmap(CharacterBitmap),draw_focus_rect(), thedraw_text()overloads, ellipse/bezier/arcby lifting their guards (they delegate to the scale-aware
draw_line()), andget_region_bitmap(). Also adds the tests.The scaled paths keep the existing unscaled fast paths intact, so 1x
output is unchanged by construction.
Testing
Tests/LibGfx/TestPaintergains:painter_scale_matches_pixel_doubled_unscaled: renders the same scene(fills, checkerboard, dither pattern,
draw_rect(),draw_focus_rect(),horizontal and vertical lines, and a
CharacterBitmap) into a 1x and a2x bitmap, and asserts the 2x result is exactly the pixel-doubled image of
the 1x result.
painter_scale_get_region_bitmap: checksget_region_bitmap()returns abitmap at the painter's scale.
ninja TestPainterbuilds and all tests pass.Notes
clang-format(version 20) on the changed files before pushing. Thechanges were written to match the surrounding style.
draw_text()overloads no longer rejectscale() > 1; glyphs areplaced through the already scale-aware glyph drawing paths. Rasterizing
glyphs at the scaled size for higher quality HiDPI text is left as a
follow-up.