Skip to content

Commit

Permalink
LibGfx: Give Bitmap a scale factor
Browse files Browse the repository at this point in the history
Gfx::Bitmap can now store its scale factor. Normally it's 1, but
in high dpi mode it can be 2.

If a Bitmap with a scale factor of 2 is blitted to a Painter with
scale factor of 2, the pixels can be copied over without any resampling.
(When blitting a Bitmap with a scale factor of 1 to a Painter with scale
factor of 2, the Bitmap is painted at twice its width and height at
paint time. Blitting a Bitmap with a scale factor of 2 to a Painter with
scale factor 1 is not supported.)

A Bitmap with scale factor of 2 reports the same width() and height() as
one with scale factor 1. That's important because many places in the
codebase use a bitmap's width() and height() to layout Widgets, and all
widget coordinates are in logical coordinates as well, per
Documentation/HighDPI.md.

Bitmap grows physical_width() / physical_height() to access the actual
pixel size. Update a few callers that work with pixels to call this
instead.

Make Painter's constructor take its scale factor from the target bitmap
that's passed in, and update its various blit() methods to handle
blitting a 2x bitmap to a 2x painter. This allows removing some gnarly
code in Compositor. (In return, put some new gnarly code in
LibGfxScaleDemo to preserve behavior there.)

No intended behavior change.
  • Loading branch information
nico authored and awesomekling committed Jan 20, 2021
1 parent c6726f3 commit 5f9c42c
Show file tree
Hide file tree
Showing 14 changed files with 191 additions and 173 deletions.
2 changes: 1 addition & 1 deletion Documentation/HighDPI.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Integer scale factors are needed in any case so let's get that working first. Ac
Desired end state
-----------------

- All rects (Window and Widget rects, mouse cursor) are in "logical" coordinates, which is the same as pixels at 1x scale, as much as possible.
- All rects (Window and Widget rects, mouse cursor, even bitmap sizes) are in "logical" coordinates, which is the same as pixels at 1x scale, as much as possible.
- If something needs to be in pixels, its name starts with `physical_`. Physical coordinates should as much as possible not cross API boundaries.
- Jury's still out if logical coordinates should stay ints. Probably, but it means mouse cursor etc only have point resolution, not pixel resolution
- We should have something that can store a collection of (lazily-loaded?) bitmaps and fonts that each represent a single image / font at different scale levels, and at paint time the right representation is picked for the current scale
Expand Down
18 changes: 13 additions & 5 deletions Userland/Demos/LibGfxScaleDemo/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,20 +50,28 @@ class Canvas final : public GUI::Widget {
Canvas();
RefPtr<Gfx::Bitmap> m_bitmap_1x;
RefPtr<Gfx::Bitmap> m_bitmap_2x;
RefPtr<Gfx::Bitmap> m_bitmap_2x_as_1x;

void draw(Gfx::Painter& painter);
virtual void paint_event(GUI::PaintEvent&) override;
};

Canvas::Canvas()
{
m_bitmap_1x = Gfx::Bitmap::create(Gfx::BitmapFormat::RGB32, { WIDTH, HEIGHT });
m_bitmap_2x = Gfx::Bitmap::create(Gfx::BitmapFormat::RGB32, { WIDTH * 2, HEIGHT * 2 });
m_bitmap_1x = Gfx::Bitmap::create(Gfx::BitmapFormat::RGB32, { WIDTH, HEIGHT }, 1);
m_bitmap_2x = Gfx::Bitmap::create(Gfx::BitmapFormat::RGB32, { WIDTH, HEIGHT }, 2);

Gfx::Painter painter_1x(*m_bitmap_1x, 1);
// m_bitmap_1x and m_bitmap_2x have the same logical size, so LibGfx will try to draw them at the same physical size:
// When drawing on a 2x backing store it'd scale m_bitmap_1x up 2x and paint m_bitmap_2x at its physical size.
// When drawing on a 1x backing store it'd draw m_bitmap_1x at its physical size, and it would have to scale down m_bitmap_2x to 0.5x its size.
// But the system can't current scale down, and we want to draw the 2x bitmap at twice the size of the 1x bitmap in this particular application,
// so make a 1x alias of the 2x bitmap to make LibGfx paint it without any scaling at paint time, mapping once pixel to one pixel.
m_bitmap_2x_as_1x = Gfx::Bitmap::create_wrapper(Gfx::BitmapFormat::RGB32, m_bitmap_2x->physical_size(), 1, m_bitmap_2x->pitch(), m_bitmap_2x->scanline(0));

Gfx::Painter painter_1x(*m_bitmap_1x);
draw(painter_1x);

Gfx::Painter painter_2x(*m_bitmap_2x, 2);
Gfx::Painter painter_2x(*m_bitmap_2x);
draw(painter_2x);

update();
Expand All @@ -79,7 +87,7 @@ void Canvas::paint_event(GUI::PaintEvent& event)
painter.add_clip_rect(event.rect());
painter.fill_rect(event.rect(), Color::Magenta);
painter.blit({ 0, 0 }, *m_bitmap_1x, m_bitmap_1x->rect());
painter.blit({ 0, HEIGHT }, *m_bitmap_2x, m_bitmap_2x->rect());
painter.blit({ 0, HEIGHT }, *m_bitmap_2x_as_1x, m_bitmap_2x_as_1x->rect());
}

void Canvas::draw(Gfx::Painter& painter)
Expand Down
13 changes: 9 additions & 4 deletions Userland/Libraries/LibGUI/Clipboard.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,10 @@ RefPtr<Gfx::Bitmap> Clipboard::bitmap() const
if (!height.has_value() || height.value() == 0)
return nullptr;

auto scale = clipping.metadata.get("scale").value_or("0").to_uint();
if (!scale.has_value() || scale.value() == 0)
return nullptr;

auto pitch = clipping.metadata.get("pitch").value_or("0").to_uint();
if (!pitch.has_value() || pitch.value() == 0)
return nullptr;
Expand All @@ -129,11 +133,11 @@ RefPtr<Gfx::Bitmap> Clipboard::bitmap() const
if (!format.has_value() || format.value() == 0)
return nullptr;

auto clipping_bitmap = Gfx::Bitmap::create_wrapper((Gfx::BitmapFormat)format.value(), { (int)width.value(), (int)height.value() }, pitch.value(), clipping.data.data());
auto bitmap = Gfx::Bitmap::create(Gfx::BitmapFormat::RGBA32, { (int)width.value(), (int)height.value() });
auto clipping_bitmap = Gfx::Bitmap::create_wrapper((Gfx::BitmapFormat)format.value(), { (int)width.value(), (int)height.value() }, scale.value(), pitch.value(), clipping.data.data());
auto bitmap = Gfx::Bitmap::create(Gfx::BitmapFormat::RGBA32, { (int)width.value(), (int)height.value() }, scale.value());

for (int y = 0; y < clipping_bitmap->height(); ++y) {
for (int x = 0; x < clipping_bitmap->width(); ++x) {
for (int y = 0; y < clipping_bitmap->physical_height(); ++y) {
for (int x = 0; x < clipping_bitmap->physical_width(); ++x) {
auto pixel = clipping_bitmap->get_pixel(x, y);
bitmap->set_pixel(x, y, pixel);
}
Expand All @@ -147,6 +151,7 @@ void Clipboard::set_bitmap(const Gfx::Bitmap& bitmap)
HashMap<String, String> metadata;
metadata.set("width", String::number(bitmap.width()));
metadata.set("height", String::number(bitmap.height()));
metadata.set("scale", String::number(bitmap.scale()));
metadata.set("format", String::number((int)bitmap.format()));
metadata.set("pitch", String::number(bitmap.pitch()));
set_data({ bitmap.scanline(0), bitmap.size_in_bytes() }, "image/x-serenityos", metadata);
Expand Down
3 changes: 2 additions & 1 deletion Userland/Libraries/LibGUI/Window.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -728,7 +728,8 @@ OwnPtr<WindowBackingStore> Window::create_backing_store(const Gfx::IntSize& size
return {};
}

auto bitmap = Gfx::Bitmap::create_with_anon_fd(format, anon_fd, size, {}, Gfx::Bitmap::ShouldCloseAnonymousFile::No);
// FIXME: Plumb scale factor here eventually.
auto bitmap = Gfx::Bitmap::create_with_anon_fd(format, anon_fd, size, 1, {}, Gfx::Bitmap::ShouldCloseAnonymousFile::No);
if (!bitmap)
return {};
return make<WindowBackingStore>(bitmap.release_nonnull());
Expand Down
4 changes: 2 additions & 2 deletions Userland/Libraries/LibGfx/BMPWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,9 @@ static ByteBuffer write_pixel_data(const RefPtr<Bitmap> bitmap, int pixel_row_da
auto buffer = ByteBuffer::create_uninitialized(image_size);

int current_row = 0;
for (int y = bitmap->height() - 1; y >= 0; --y) {
for (int y = bitmap->physical_height() - 1; y >= 0; --y) {
auto* row = buffer.data() + (pixel_row_data_size * current_row++);
for (int x = 0; x < bitmap->width(); x++) {
for (int x = 0; x < bitmap->physical_width(); x++) {
auto pixel = bitmap->get_pixel(x, y);
row[x * bytes_per_pixel + 0] = pixel.blue();
row[x * bytes_per_pixel + 1] = pixel.green();
Expand Down
Loading

0 comments on commit 5f9c42c

Please sign in to comment.