diff options
Diffstat (limited to 'Userland')
-rw-r--r-- | Userland/Demos/LibGfxScaleDemo/main.cpp | 18 | ||||
-rw-r--r-- | Userland/Libraries/LibGUI/Clipboard.cpp | 13 | ||||
-rw-r--r-- | Userland/Libraries/LibGUI/Window.cpp | 3 | ||||
-rw-r--r-- | Userland/Libraries/LibGfx/BMPWriter.cpp | 4 | ||||
-rw-r--r-- | Userland/Libraries/LibGfx/Bitmap.cpp | 119 | ||||
-rw-r--r-- | Userland/Libraries/LibGfx/Bitmap.h | 71 | ||||
-rw-r--r-- | Userland/Libraries/LibGfx/Painter.cpp | 70 | ||||
-rw-r--r-- | Userland/Libraries/LibGfx/Painter.h | 2 | ||||
-rw-r--r-- | Userland/Libraries/LibGfx/ShareableBitmap.cpp | 6 | ||||
-rw-r--r-- | Userland/Libraries/LibGfx/ShareableBitmap.h | 6 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/HTML/ImageData.cpp | 2 | ||||
-rw-r--r-- | Userland/Services/WindowServer/ClientConnection.cpp | 2 | ||||
-rw-r--r-- | Userland/Services/WindowServer/Compositor.cpp | 46 |
13 files changed, 190 insertions, 172 deletions
diff --git a/Userland/Demos/LibGfxScaleDemo/main.cpp b/Userland/Demos/LibGfxScaleDemo/main.cpp index a12a509fbf..a34691a7b6 100644 --- a/Userland/Demos/LibGfxScaleDemo/main.cpp +++ b/Userland/Demos/LibGfxScaleDemo/main.cpp @@ -50,6 +50,7 @@ private: 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; @@ -57,13 +58,20 @@ private: 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(); @@ -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) diff --git a/Userland/Libraries/LibGUI/Clipboard.cpp b/Userland/Libraries/LibGUI/Clipboard.cpp index c7e7ce4b37..c54c69ff3c 100644 --- a/Userland/Libraries/LibGUI/Clipboard.cpp +++ b/Userland/Libraries/LibGUI/Clipboard.cpp @@ -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; @@ -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); } @@ -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); diff --git a/Userland/Libraries/LibGUI/Window.cpp b/Userland/Libraries/LibGUI/Window.cpp index 78fe6a3157..ec162bda85 100644 --- a/Userland/Libraries/LibGUI/Window.cpp +++ b/Userland/Libraries/LibGUI/Window.cpp @@ -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()); diff --git a/Userland/Libraries/LibGfx/BMPWriter.cpp b/Userland/Libraries/LibGfx/BMPWriter.cpp index 8ae2b00a8b..8e29f73ac9 100644 --- a/Userland/Libraries/LibGfx/BMPWriter.cpp +++ b/Userland/Libraries/LibGfx/BMPWriter.cpp @@ -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(); diff --git a/Userland/Libraries/LibGfx/Bitmap.cpp b/Userland/Libraries/LibGfx/Bitmap.cpp index 6296e4512c..291d30a9b3 100644 --- a/Userland/Libraries/LibGfx/Bitmap.cpp +++ b/Userland/Libraries/LibGfx/Bitmap.cpp @@ -56,7 +56,7 @@ struct BackingStore { size_t size_in_bytes { 0 }; }; -size_t Bitmap::minimum_pitch(size_t width, BitmapFormat format) +size_t Bitmap::minimum_pitch(size_t physical_width, BitmapFormat format) { size_t element_size; switch (determine_storage_format(format)) { @@ -71,73 +71,74 @@ size_t Bitmap::minimum_pitch(size_t width, BitmapFormat format) ASSERT_NOT_REACHED(); } - return width * element_size; + return physical_width * element_size; } -static bool size_would_overflow(BitmapFormat format, const IntSize& size) +static bool size_would_overflow(BitmapFormat format, const IntSize& size, int scale_factor) { if (size.width() < 0 || size.height() < 0) return true; // This check is a bit arbitrary, but should protect us from most shenanigans: - if (size.width() >= 32768 || size.height() >= 32768) + if (size.width() >= 32768 || size.height() >= 32768 || scale_factor < 1 || scale_factor > 4) return true; // In contrast, this check is absolutely necessary: - size_t pitch = Bitmap::minimum_pitch(size.width(), format); - return Checked<size_t>::multiplication_would_overflow(pitch, size.height()); + size_t pitch = Bitmap::minimum_pitch(size.width() * scale_factor, format); + return Checked<size_t>::multiplication_would_overflow(pitch, size.height() * scale_factor); } -RefPtr<Bitmap> Bitmap::create(BitmapFormat format, const IntSize& size) +RefPtr<Bitmap> Bitmap::create(BitmapFormat format, const IntSize& size, int scale_factor) { - auto backing_store = Bitmap::allocate_backing_store(format, size, Purgeable::No); + auto backing_store = Bitmap::allocate_backing_store(format, size, scale_factor, Purgeable::No); if (!backing_store.has_value()) return nullptr; - return adopt(*new Bitmap(format, size, Purgeable::No, backing_store.value())); + return adopt(*new Bitmap(format, size, scale_factor, Purgeable::No, backing_store.value())); } -RefPtr<Bitmap> Bitmap::create_purgeable(BitmapFormat format, const IntSize& size) +RefPtr<Bitmap> Bitmap::create_purgeable(BitmapFormat format, const IntSize& size, int scale_factor) { - auto backing_store = Bitmap::allocate_backing_store(format, size, Purgeable::Yes); + auto backing_store = Bitmap::allocate_backing_store(format, size, scale_factor, Purgeable::Yes); if (!backing_store.has_value()) return nullptr; - return adopt(*new Bitmap(format, size, Purgeable::Yes, backing_store.value())); + return adopt(*new Bitmap(format, size, scale_factor, Purgeable::Yes, backing_store.value())); } #ifdef __serenity__ -RefPtr<Bitmap> Bitmap::create_shareable(BitmapFormat format, const IntSize& size) +RefPtr<Bitmap> Bitmap::create_shareable(BitmapFormat format, const IntSize& size, int scale_factor) { - if (size_would_overflow(format, size)) + if (size_would_overflow(format, size, scale_factor)) return nullptr; - const auto pitch = minimum_pitch(size.width(), format); - const auto data_size = size_in_bytes(pitch, size.height()); + const auto pitch = minimum_pitch(size.width() * scale_factor, format); + const auto data_size = size_in_bytes(pitch, size.height() * scale_factor); auto anon_fd = anon_create(round_up_to_power_of_two(data_size, PAGE_SIZE), O_CLOEXEC); if (anon_fd < 0) return nullptr; - return Bitmap::create_with_anon_fd(format, anon_fd, size, {}, ShouldCloseAnonymousFile::No); + return Bitmap::create_with_anon_fd(format, anon_fd, size, scale_factor, {}, ShouldCloseAnonymousFile::No); } #endif -Bitmap::Bitmap(BitmapFormat format, const IntSize& size, Purgeable purgeable, const BackingStore& backing_store) +Bitmap::Bitmap(BitmapFormat format, const IntSize& size, int scale_factor, Purgeable purgeable, const BackingStore& backing_store) : m_size(size) + , m_scale(scale_factor) , m_data(backing_store.data) , m_pitch(backing_store.pitch) , m_format(format) , m_purgeable(purgeable == Purgeable::Yes) { ASSERT(!m_size.is_empty()); - ASSERT(!size_would_overflow(format, size)); + ASSERT(!size_would_overflow(format, size, scale_factor)); ASSERT(m_data); ASSERT(backing_store.size_in_bytes == size_in_bytes()); allocate_palette_from_format(format, {}); m_needs_munmap = true; } -RefPtr<Bitmap> Bitmap::create_wrapper(BitmapFormat format, const IntSize& size, size_t pitch, void* data) +RefPtr<Bitmap> Bitmap::create_wrapper(BitmapFormat format, const IntSize& size, int scale_factor, size_t pitch, void* data) { - if (size_would_overflow(format, size)) + if (size_would_overflow(format, size, scale_factor)) return nullptr; - return adopt(*new Bitmap(format, size, pitch, data)); + return adopt(*new Bitmap(format, size, scale_factor, pitch, data)); } RefPtr<Bitmap> Bitmap::load_from_file(const StringView& path) @@ -151,30 +152,31 @@ RefPtr<Bitmap> Bitmap::load_from_file(const StringView& path) return nullptr; } -Bitmap::Bitmap(BitmapFormat format, const IntSize& size, size_t pitch, void* data) +Bitmap::Bitmap(BitmapFormat format, const IntSize& size, int scale_factor, size_t pitch, void* data) : m_size(size) + , m_scale(scale_factor) , m_data(data) , m_pitch(pitch) , m_format(format) { - ASSERT(pitch >= minimum_pitch(size.width(), format)); - ASSERT(!size_would_overflow(format, size)); + ASSERT(pitch >= minimum_pitch(size.width() * scale_factor, format)); + ASSERT(!size_would_overflow(format, size, scale_factor)); // FIXME: assert that `data` is actually long enough! allocate_palette_from_format(format, {}); } -static bool check_size(const IntSize& size, BitmapFormat format, unsigned actual_size) +static bool check_size(const IntSize& size, int scale_factor, BitmapFormat format, unsigned actual_size) { - // FIXME: Code duplication of size_in_bytes() and m_pitch - unsigned expected_size_min = Bitmap::minimum_pitch(size.width(), format) * size.height(); + unsigned expected_size_min = Bitmap::minimum_pitch(size.width() * scale_factor, format) * size.height() * scale_factor; unsigned expected_size_max = round_up_to_power_of_two(expected_size_min, PAGE_SIZE); if (expected_size_min > actual_size || actual_size > expected_size_max) { // Getting here is most likely an error. - dbgln("Constructing a shared bitmap for format {} and size {}, which demands {} bytes, which rounds up to at most {}.", + dbgln("Constructing a shared bitmap for format {} and size {} @ {}x, which demands {} bytes, which rounds up to at most {}.", static_cast<int>(format), size, + scale_factor, expected_size_min, expected_size_max); @@ -184,7 +186,7 @@ static bool check_size(const IntSize& size, BitmapFormat format, unsigned actual return true; } -RefPtr<Bitmap> Bitmap::create_with_anon_fd(BitmapFormat format, int anon_fd, const IntSize& size, const Vector<RGBA32>& palette, ShouldCloseAnonymousFile should_close_anon_fd) +RefPtr<Bitmap> Bitmap::create_with_anon_fd(BitmapFormat format, int anon_fd, const IntSize& size, int scale_factor, const Vector<RGBA32>& palette, ShouldCloseAnonymousFile should_close_anon_fd) { void* data = nullptr; { @@ -197,11 +199,11 @@ RefPtr<Bitmap> Bitmap::create_with_anon_fd(BitmapFormat format, int anon_fd, con } }; - if (size_would_overflow(format, size)) + if (size_would_overflow(format, size, scale_factor)) return nullptr; - const auto pitch = minimum_pitch(size.width(), format); - const auto data_size_in_bytes = size_in_bytes(pitch, size.height()); + const auto pitch = minimum_pitch(size.width() * scale_factor, format); + const auto data_size_in_bytes = size_in_bytes(pitch, size.height() * scale_factor); data = mmap(nullptr, round_up_to_power_of_two(data_size_in_bytes, PAGE_SIZE), PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED, anon_fd, 0); if (data == MAP_FAILED) { @@ -210,13 +212,14 @@ RefPtr<Bitmap> Bitmap::create_with_anon_fd(BitmapFormat format, int anon_fd, con } } - return adopt(*new Bitmap(format, anon_fd, size, data, palette)); + return adopt(*new Bitmap(format, anon_fd, size, scale_factor, data, palette)); } /// Read a bitmap as described by: /// - actual size /// - width /// - height +/// - scale_factor /// - format /// - palette count /// - palette data (= palette count * RGBA32) @@ -227,6 +230,7 @@ RefPtr<Bitmap> Bitmap::create_from_serialized_byte_buffer(ByteBuffer&& buffer) unsigned actual_size; unsigned width; unsigned height; + unsigned scale_factor; BitmapFormat format; unsigned palette_size; Vector<RGBA32> palette; @@ -237,13 +241,13 @@ RefPtr<Bitmap> Bitmap::create_from_serialized_byte_buffer(ByteBuffer&& buffer) return true; }; - if (!read(actual_size) || !read(width) || !read(height) || !read(format) || !read(palette_size)) + if (!read(actual_size) || !read(width) || !read(height) || !read(scale_factor) || !read(format) || !read(palette_size)) return nullptr; if (format > BitmapFormat::RGBA32 || format < BitmapFormat::Indexed1) return nullptr; - if (!check_size({ width, height }, format, actual_size)) + if (!check_size({ width, height }, scale_factor, format, actual_size)) return {}; palette.ensure_capacity(palette_size); @@ -257,7 +261,7 @@ RefPtr<Bitmap> Bitmap::create_from_serialized_byte_buffer(ByteBuffer&& buffer) auto data = stream.bytes().slice(stream.offset(), actual_size); - auto bitmap = Bitmap::create(format, { width, height }); + auto bitmap = Bitmap::create(format, { width, height }, scale_factor); if (!bitmap) return {}; @@ -282,7 +286,7 @@ ByteBuffer Bitmap::serialize_to_byte_buffer() const auto palette = palette_to_vector(); - if (!write(size_in_bytes()) || !write((unsigned)size().width()) || !write((unsigned)size().height()) || !write(m_format) || !write((unsigned)palette.size())) + if (!write(size_in_bytes()) || !write((unsigned)size().width()) || !write((unsigned)size().height()) || !write((unsigned)scale()) || !write(m_format) || !write((unsigned)palette.size())) return {}; for (auto& p : palette) { @@ -298,17 +302,18 @@ ByteBuffer Bitmap::serialize_to_byte_buffer() const return buffer; } -Bitmap::Bitmap(BitmapFormat format, int anon_fd, const IntSize& size, void* data, const Vector<RGBA32>& palette) +Bitmap::Bitmap(BitmapFormat format, int anon_fd, const IntSize& size, int scale_factor, void* data, const Vector<RGBA32>& palette) : m_size(size) + , m_scale(scale_factor) , m_data(data) - , m_pitch(minimum_pitch(size.width(), format)) + , m_pitch(minimum_pitch(size.width() * scale_factor, format)) , m_format(format) , m_needs_munmap(true) , m_purgeable(true) , m_anon_fd(anon_fd) { ASSERT(!is_indexed() || !palette.is_empty()); - ASSERT(!size_would_overflow(format, size)); + ASSERT(!size_would_overflow(format, size, scale_factor)); if (is_indexed(m_format)) allocate_palette_from_format(m_format, palette); @@ -318,9 +323,9 @@ RefPtr<Gfx::Bitmap> Bitmap::clone() const { RefPtr<Gfx::Bitmap> new_bitmap {}; if (m_purgeable) { - new_bitmap = Bitmap::create_purgeable(format(), size()); + new_bitmap = Bitmap::create_purgeable(format(), size(), scale()); } else { - new_bitmap = Bitmap::create(format(), size()); + new_bitmap = Bitmap::create(format(), size(), scale()); } if (!new_bitmap) { @@ -335,13 +340,12 @@ RefPtr<Gfx::Bitmap> Bitmap::clone() const RefPtr<Gfx::Bitmap> Bitmap::rotated(Gfx::RotationDirection rotation_direction) const { - auto w = this->width(); - auto h = this->height(); - - auto new_bitmap = Gfx::Bitmap::create(this->format(), { h, w }); + auto new_bitmap = Gfx::Bitmap::create(this->format(), { height(), width() }, scale()); if (!new_bitmap) return nullptr; + auto w = this->physical_width(); + auto h = this->physical_height(); for (int i = 0; i < w; i++) { for (int j = 0; j < h; j++) { Color color; @@ -359,13 +363,12 @@ RefPtr<Gfx::Bitmap> Bitmap::rotated(Gfx::RotationDirection rotation_direction) c RefPtr<Gfx::Bitmap> Bitmap::flipped(Gfx::Orientation orientation) const { - auto w = this->width(); - auto h = this->height(); - - auto new_bitmap = Gfx::Bitmap::create(this->format(), { w, h }); + auto new_bitmap = Gfx::Bitmap::create(this->format(), { width(), height() }, scale()); if (!new_bitmap) return nullptr; + auto w = this->physical_width(); + auto h = this->physical_height(); for (int i = 0; i < w; i++) { for (int j = 0; j < h; j++) { Color color = this->get_pixel(i, j); @@ -387,7 +390,7 @@ RefPtr<Bitmap> Bitmap::to_bitmap_backed_by_anon_fd() const auto anon_fd = anon_create(round_up_to_power_of_two(size_in_bytes(), PAGE_SIZE), O_CLOEXEC); if (anon_fd < 0) return nullptr; - auto bitmap = Bitmap::create_with_anon_fd(m_format, anon_fd, m_size, palette_to_vector(), ShouldCloseAnonymousFile::No); + auto bitmap = Bitmap::create_with_anon_fd(m_format, anon_fd, size(), scale(), palette_to_vector(), ShouldCloseAnonymousFile::No); if (!bitmap) return nullptr; memcpy(bitmap->scanline(0), scanline(0), size_in_bytes()); @@ -420,9 +423,9 @@ void Bitmap::set_mmap_name([[maybe_unused]] const StringView& name) void Bitmap::fill(Color color) { ASSERT(!is_indexed(m_format)); - for (int y = 0; y < height(); ++y) { + for (int y = 0; y < physical_height(); ++y) { auto* scanline = this->scanline(y); - fast_u32_fill(scanline, color.value(), width()); + fast_u32_fill(scanline, color.value(), physical_width()); } } @@ -469,13 +472,13 @@ ShareableBitmap Bitmap::to_shareable_bitmap() const } #endif -Optional<BackingStore> Bitmap::allocate_backing_store(BitmapFormat format, const IntSize& size, [[maybe_unused]] Purgeable purgeable) +Optional<BackingStore> Bitmap::allocate_backing_store(BitmapFormat format, const IntSize& size, int scale_factor, [[maybe_unused]] Purgeable purgeable) { - if (size_would_overflow(format, size)) + if (size_would_overflow(format, size, scale_factor)) return {}; - const auto pitch = minimum_pitch(size.width(), format); - const auto data_size_in_bytes = size_in_bytes(pitch, size.height()); + const auto pitch = minimum_pitch(size.width() * scale_factor, format); + const auto data_size_in_bytes = size_in_bytes(pitch, size.height() * scale_factor); int map_flags = MAP_ANONYMOUS | MAP_PRIVATE; if (purgeable == Purgeable::Yes) diff --git a/Userland/Libraries/LibGfx/Bitmap.h b/Userland/Libraries/LibGfx/Bitmap.h index 47098f17ff..92b0f3b144 100644 --- a/Userland/Libraries/LibGfx/Bitmap.h +++ b/Userland/Libraries/LibGfx/Bitmap.h @@ -108,12 +108,12 @@ public: Yes, }; - static RefPtr<Bitmap> create(BitmapFormat, const IntSize&); - static RefPtr<Bitmap> create_shareable(BitmapFormat, const IntSize&); - static RefPtr<Bitmap> create_purgeable(BitmapFormat, const IntSize&); - static RefPtr<Bitmap> create_wrapper(BitmapFormat, const IntSize&, size_t pitch, void*); - static RefPtr<Bitmap> load_from_file(const StringView& path); - static RefPtr<Bitmap> create_with_anon_fd(BitmapFormat, int anon_fd, const IntSize&, const Vector<RGBA32>& palette, ShouldCloseAnonymousFile); + static RefPtr<Bitmap> create(BitmapFormat, const IntSize&, int intrinsic_scale = 1); + static RefPtr<Bitmap> create_shareable(BitmapFormat, const IntSize&, int intrinsic_scale = 1); + static RefPtr<Bitmap> create_purgeable(BitmapFormat, const IntSize&, int intrinsic_scale = 1); + static RefPtr<Bitmap> create_wrapper(BitmapFormat, const IntSize&, int intrinsic_scale, size_t pitch, void*); + static RefPtr<Bitmap> load_from_file(const StringView& path); // FIXME: scale factor + static RefPtr<Bitmap> create_with_anon_fd(BitmapFormat, int anon_fd, const IntSize&, int intrinsic_scale, const Vector<RGBA32>& palette, ShouldCloseAnonymousFile); static RefPtr<Bitmap> create_from_serialized_byte_buffer(ByteBuffer&& buffer); static bool is_path_a_supported_image_format(const StringView& path) { @@ -137,15 +137,21 @@ public: ~Bitmap(); - u8* scanline_u8(int y); - const u8* scanline_u8(int y) const; - RGBA32* scanline(int y); - const RGBA32* scanline(int y) const; + u8* scanline_u8(int physical_y); + const u8* scanline_u8(int physical_y) const; + RGBA32* scanline(int physical_y); + const RGBA32* scanline(int physical_y) const; IntRect rect() const { return { {}, m_size }; } IntSize size() const { return m_size; } int width() const { return m_size.width(); } int height() const { return m_size.height(); } + int scale() const { return m_scale; } + + IntRect physical_rect() const { return rect() * scale(); } + IntSize physical_size() const { return size() * scale(); } + int physical_width() const { return physical_size().width(); } + int physical_height() const { return physical_size().height(); } size_t pitch() const { return m_pitch; } ALWAYS_INLINE bool is_indexed() const @@ -198,7 +204,7 @@ public: } } - static size_t minimum_pitch(size_t width, BitmapFormat); + static size_t minimum_pitch(size_t physical_width, BitmapFormat); unsigned bpp() const { @@ -212,26 +218,26 @@ public: void set_mmap_name(const StringView&); - static constexpr size_t size_in_bytes(size_t pitch, int height) { return pitch * height; } - size_t size_in_bytes() const { return size_in_bytes(m_pitch, height()); } + static constexpr size_t size_in_bytes(size_t pitch, int physical_height) { return pitch * physical_height; } + size_t size_in_bytes() const { return size_in_bytes(m_pitch, physical_height()); } Color palette_color(u8 index) const { return Color::from_rgba(m_palette[index]); } void set_palette_color(u8 index, Color color) { m_palette[index] = color.value(); } template<StorageFormat> - Color get_pixel(int x, int y) const; - Color get_pixel(int x, int y) const; - Color get_pixel(const IntPoint& position) const + Color get_pixel(int physical_x, int physical_y) const; + Color get_pixel(int physical_x, int physical_y) const; + Color get_pixel(const IntPoint& physical_position) const { - return get_pixel(position.x(), position.y()); + return get_pixel(physical_position.x(), physical_position.y()); } template<StorageFormat> - void set_pixel(int x, int y, Color); - void set_pixel(int x, int y, Color); - void set_pixel(const IntPoint& position, Color color) + void set_pixel(int physical_x, int physical_y, Color); + void set_pixel(int physical_x, int physical_y, Color); + void set_pixel(const IntPoint& physical_position, Color color) { - set_pixel(position.x(), position.y(), color); + set_pixel(physical_position.x(), physical_position.y(), color); } bool is_purgeable() const { return m_purgeable; } @@ -246,15 +252,16 @@ private: No, Yes }; - Bitmap(BitmapFormat, const IntSize&, Purgeable, const BackingStore&); - Bitmap(BitmapFormat, const IntSize&, size_t pitch, void*); - Bitmap(BitmapFormat, int anon_fd, const IntSize&, void*, const Vector<RGBA32>& palette); + Bitmap(BitmapFormat, const IntSize&, int, Purgeable, const BackingStore&); + Bitmap(BitmapFormat, const IntSize&, int, size_t pitch, void*); + Bitmap(BitmapFormat, int anon_fd, const IntSize&, int, void*, const Vector<RGBA32>& palette); - static Optional<BackingStore> allocate_backing_store(BitmapFormat, const IntSize&, Purgeable); + static Optional<BackingStore> allocate_backing_store(BitmapFormat, const IntSize&, int, Purgeable); void allocate_palette_from_format(BitmapFormat, const Vector<RGBA32>& source_palette); IntSize m_size; + int m_scale; void* m_data { nullptr }; RGBA32* m_palette { nullptr }; size_t m_pitch { 0 }; @@ -267,13 +274,13 @@ private: inline u8* Bitmap::scanline_u8(int y) { - ASSERT(y >= 0 && y < height()); + ASSERT(y >= 0 && y < physical_height()); return reinterpret_cast<u8*>(m_data) + (y * m_pitch); } inline const u8* Bitmap::scanline_u8(int y) const { - ASSERT(y >= 0 && y < height()); + ASSERT(y >= 0 && y < physical_height()); return reinterpret_cast<const u8*>(m_data) + (y * m_pitch); } @@ -290,21 +297,21 @@ inline const RGBA32* Bitmap::scanline(int y) const template<> inline Color Bitmap::get_pixel<StorageFormat::RGB32>(int x, int y) const { - ASSERT(x >= 0 && x < width()); + ASSERT(x >= 0 && x < physical_width()); return Color::from_rgb(scanline(y)[x]); } template<> inline Color Bitmap::get_pixel<StorageFormat::RGBA32>(int x, int y) const { - ASSERT(x >= 0 && x < width()); + ASSERT(x >= 0 && x < physical_width()); return Color::from_rgba(scanline(y)[x]); } template<> inline Color Bitmap::get_pixel<StorageFormat::Indexed8>(int x, int y) const { - ASSERT(x >= 0 && x < width()); + ASSERT(x >= 0 && x < physical_width()); return Color::from_rgb(m_palette[scanline_u8(y)[x]]); } @@ -325,13 +332,13 @@ inline Color Bitmap::get_pixel(int x, int y) const template<> inline void Bitmap::set_pixel<StorageFormat::RGB32>(int x, int y, Color color) { - ASSERT(x >= 0 && x < width()); + ASSERT(x >= 0 && x < physical_width()); scanline(y)[x] = color.value(); } template<> inline void Bitmap::set_pixel<StorageFormat::RGBA32>(int x, int y, Color color) { - ASSERT(x >= 0 && x < width()); + ASSERT(x >= 0 && x < physical_width()); scanline(y)[x] = color.value(); // drop alpha } inline void Bitmap::set_pixel(int x, int y, Color color) diff --git a/Userland/Libraries/LibGfx/Painter.cpp b/Userland/Libraries/LibGfx/Painter.cpp index 595f04f95f..f8065a9ea8 100644 --- a/Userland/Libraries/LibGfx/Painter.cpp +++ b/Userland/Libraries/LibGfx/Painter.cpp @@ -68,15 +68,16 @@ ALWAYS_INLINE Color get_pixel(const Gfx::Bitmap& bitmap, int x, int y) return bitmap.get_pixel(x, y); } -Painter::Painter(Gfx::Bitmap& bitmap, int scale) +Painter::Painter(Gfx::Bitmap& bitmap) : m_target(bitmap) { + int scale = bitmap.scale(); ASSERT(bitmap.format() == Gfx::BitmapFormat::RGB32 || bitmap.format() == Gfx::BitmapFormat::RGBA32); - ASSERT(bitmap.width() % scale == 0); - ASSERT(bitmap.height() % scale == 0); + ASSERT(bitmap.physical_width() % scale == 0); + ASSERT(bitmap.physical_height() % scale == 0); m_state_stack.append(State()); state().font = &FontDatabase::default_font(); - state().clip_rect = { { 0, 0 }, bitmap.size() }; + state().clip_rect = { { 0, 0 }, bitmap.physical_size() }; state().scale = scale; m_clip_origin = state().clip_rect; } @@ -109,7 +110,7 @@ void Painter::clear_rect(const IntRect& a_rect, Color color) if (rect.is_empty()) return; - ASSERT(m_target->rect().contains(rect)); + ASSERT(m_target->physical_rect().contains(rect)); RGBA32* dst = m_target->scanline(rect.top()) + rect.left(); const size_t dst_skip = m_target->pitch() / sizeof(RGBA32); @@ -120,12 +121,12 @@ void Painter::clear_rect(const IntRect& a_rect, Color color) } } -void Painter::fill_physical_rect(const IntRect& a_rect, Color color) +void Painter::fill_physical_rect(const IntRect& a_physical_rect, Color color) { - auto rect = a_rect.intersected(clip_rect()); + auto rect = a_physical_rect.intersected(clip_rect()); if (rect.is_empty()) return; - ASSERT(m_target->rect().contains(rect)); + ASSERT(m_target->physical_rect().contains(rect)); RGBA32* dst = m_target->scanline(rect.top()) + rect.left(); const size_t dst_skip = m_target->pitch() / sizeof(RGBA32); @@ -693,14 +694,16 @@ void Painter::blit_offset(const IntPoint& position, const Gfx::Bitmap& source, c ASSERT_NOT_REACHED(); } -void Painter::blit_with_alpha(const IntPoint& position, const Gfx::Bitmap& source, const IntRect& src_rect) +void Painter::blit_with_alpha(const IntPoint& position, const Gfx::Bitmap& source, const IntRect& a_src_rect) { - if (scale() != 1) - return draw_scaled_bitmap({ position, src_rect.size() }, source, src_rect); + if (scale() != source.scale()) + return draw_scaled_bitmap({ position, a_src_rect.size() }, source, a_src_rect); ASSERT(source.has_alpha_channel()); - IntRect safe_src_rect = src_rect.intersected(source.rect()); - auto dst_rect = IntRect(position, safe_src_rect.size()).translated(translation()); + 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 clipped_rect = dst_rect.intersected(clip_rect()); if (clipped_rect.is_empty()) return; @@ -728,18 +731,25 @@ void Painter::blit_with_alpha(const IntPoint& position, const Gfx::Bitmap& sourc } } -void Painter::blit(const IntPoint& position, const Gfx::Bitmap& source, const IntRect& src_rect, float opacity) +void Painter::blit(const IntPoint& position, const Gfx::Bitmap& source, const IntRect& a_src_rect, float opacity) { + assert(scale() >= source.scale() && "painter doesn't support downsampling scale factors"); + if (opacity < 1.0f) - return blit_with_opacity(position, source, src_rect, opacity); + return blit_with_opacity(position, source, a_src_rect, opacity); if (source.has_alpha_channel()) - return blit_with_alpha(position, source, src_rect); - if (scale() != 1) - return draw_scaled_bitmap({ position, src_rect.size() }, source, src_rect, opacity); - - auto safe_src_rect = src_rect.intersected(source.rect()); + return blit_with_alpha(position, source, a_src_rect); + if (scale() != source.scale()) + return draw_scaled_bitmap({ position, a_src_rect.size() }, source, a_src_rect, opacity); + + // 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 = IntRect(position, safe_src_rect.size()).translated(translation()); + auto dst_rect = to_physical(IntRect(position, safe_src_rect.size())); + auto src_rect = a_src_rect * source.scale(); + auto clipped_rect = dst_rect.intersected(clip_rect()); if (clipped_rect.is_empty()) return; @@ -832,15 +842,13 @@ ALWAYS_INLINE static void do_draw_scaled_bitmap(Gfx::Bitmap& target, const IntRe } } -void Painter::draw_scaled_bitmap(const IntRect& a_dst_rect, const Gfx::Bitmap& source, const IntRect& src_rect, float opacity) +void Painter::draw_scaled_bitmap(const IntRect& a_dst_rect, const Gfx::Bitmap& source, const IntRect& a_src_rect, float opacity) { - if (scale() == 1 && a_dst_rect.size() == src_rect.size()) - return blit(a_dst_rect.location(), source, src_rect, opacity); + if (scale() == source.scale() && a_dst_rect.size() == a_src_rect.size()) + return blit(a_dst_rect.location(), source, a_src_rect, opacity); auto dst_rect = to_physical(a_dst_rect); - - auto safe_src_rect = src_rect.intersected(source.rect()); - ASSERT(source.rect().contains(safe_src_rect)); + auto src_rect = a_src_rect * source.scale(); auto clipped_rect = dst_rect.intersected(clip_rect()); if (clipped_rect.is_empty()) @@ -1232,7 +1240,7 @@ ALWAYS_INLINE void Painter::fill_physical_scanline_with_draw_op(int y, int x, in } } -void Painter::draw_physical_pixel(const IntPoint& position, Color color, int thickness) +void Painter::draw_physical_pixel(const IntPoint& physical_position, Color color, int thickness) { // This always draws a single physical pixel, independent of scale(). // This should only be called by routines that already handle scale @@ -1240,11 +1248,11 @@ void Painter::draw_physical_pixel(const IntPoint& position, Color color, int thi ASSERT(draw_op() == DrawOp::Copy); if (thickness == 1) { // Implies scale() == 1. - auto& pixel = m_target->scanline(position.y())[position.x()]; + auto& pixel = m_target->scanline(physical_position.y())[physical_position.x()]; return set_physical_pixel_with_draw_op(pixel, Color::from_rgba(pixel).blend(color)); } - IntRect rect { position, { thickness, thickness } }; + IntRect rect { physical_position, { thickness, thickness } }; fill_physical_rect(rect, color); } @@ -1488,7 +1496,7 @@ 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->rect()); // FIXME: This shouldn't be necessary? + state().clip_rect.intersect(m_target->physical_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 a892f18b64..d070d2a830 100644 --- a/Userland/Libraries/LibGfx/Painter.h +++ b/Userland/Libraries/LibGfx/Painter.h @@ -41,7 +41,7 @@ namespace Gfx { class Painter { public: - explicit Painter(Gfx::Bitmap&, int scale = 1); + explicit Painter(Gfx::Bitmap&); ~Painter(); enum class LineStyle { diff --git a/Userland/Libraries/LibGfx/ShareableBitmap.cpp b/Userland/Libraries/LibGfx/ShareableBitmap.cpp index c59a2d0dad..9babcd7ea2 100644 --- a/Userland/Libraries/LibGfx/ShareableBitmap.cpp +++ b/Userland/Libraries/LibGfx/ShareableBitmap.cpp @@ -55,6 +55,7 @@ bool encode(Encoder& encoder, const Gfx::ShareableBitmap& shareable_bitmap) auto& bitmap = *shareable_bitmap.bitmap(); encoder << IPC::File(bitmap.anon_fd()); encoder << bitmap.size(); + encoder << bitmap.scale(); encoder << (u32)bitmap.format(); if (bitmap.is_indexed()) { auto palette = bitmap.palette_to_vector(); @@ -78,6 +79,9 @@ bool decode(Decoder& decoder, Gfx::ShareableBitmap& shareable_bitmap) Gfx::IntSize size; if (!decoder.decode(size)) return false; + u32 scale; + if (!decoder.decode(scale)) + return false; u32 raw_bitmap_format; if (!decoder.decode(raw_bitmap_format)) return false; @@ -89,7 +93,7 @@ bool decode(Decoder& decoder, Gfx::ShareableBitmap& shareable_bitmap) if (!decoder.decode(palette)) return false; } - auto bitmap = Gfx::Bitmap::create_with_anon_fd(bitmap_format, anon_file.take_fd(), size, palette, Gfx::Bitmap::ShouldCloseAnonymousFile::Yes); + auto bitmap = Gfx::Bitmap::create_with_anon_fd(bitmap_format, anon_file.take_fd(), size, scale, palette, Gfx::Bitmap::ShouldCloseAnonymousFile::Yes); if (!bitmap) return false; shareable_bitmap = Gfx::ShareableBitmap { bitmap.release_nonnull(), Gfx::ShareableBitmap::ConstructWithKnownGoodBitmap }; diff --git a/Userland/Libraries/LibGfx/ShareableBitmap.h b/Userland/Libraries/LibGfx/ShareableBitmap.h index d6eecd3cbb..c2f0e9628c 100644 --- a/Userland/Libraries/LibGfx/ShareableBitmap.h +++ b/Userland/Libraries/LibGfx/ShareableBitmap.h @@ -45,12 +45,6 @@ public: const Bitmap* bitmap() const { return m_bitmap; } Bitmap* bitmap() { return m_bitmap; } - IntSize size() const { return m_bitmap ? m_bitmap->size() : IntSize(); } - IntRect rect() const { return m_bitmap ? m_bitmap->rect() : IntRect(); } - - int width() const { return size().width(); } - int height() const { return size().height(); } - private: RefPtr<Bitmap> m_bitmap; }; diff --git a/Userland/Libraries/LibWeb/HTML/ImageData.cpp b/Userland/Libraries/LibWeb/HTML/ImageData.cpp index 085cc88728..81a76594ed 100644 --- a/Userland/Libraries/LibWeb/HTML/ImageData.cpp +++ b/Userland/Libraries/LibWeb/HTML/ImageData.cpp @@ -46,7 +46,7 @@ RefPtr<ImageData> ImageData::create_with_size(JS::GlobalObject& global_object, i auto data_handle = JS::make_handle(data); - auto bitmap = Gfx::Bitmap::create_wrapper(Gfx::BitmapFormat::RGBA32, Gfx::IntSize(width, height), width * sizeof(u32), (u32*)data->data()); + auto bitmap = Gfx::Bitmap::create_wrapper(Gfx::BitmapFormat::RGBA32, Gfx::IntSize(width, height), 1, width * sizeof(u32), (u32*)data->data()); if (!bitmap) return nullptr; return adopt(*new ImageData(bitmap.release_nonnull(), move(data_handle))); diff --git a/Userland/Services/WindowServer/ClientConnection.cpp b/Userland/Services/WindowServer/ClientConnection.cpp index 91b0aacc1c..3c9df70dc2 100644 --- a/Userland/Services/WindowServer/ClientConnection.cpp +++ b/Userland/Services/WindowServer/ClientConnection.cpp @@ -566,10 +566,12 @@ OwnPtr<Messages::WindowServer::SetWindowBackingStoreResponse> ClientConnection:: if (window.last_backing_store() && window.last_backing_store_serial() == message.serial()) { window.swap_backing_stores(); } else { + // FIXME: Plumb scale factor here eventually. auto backing_store = Gfx::Bitmap::create_with_anon_fd( message.has_alpha_channel() ? Gfx::BitmapFormat::RGBA32 : Gfx::BitmapFormat::RGB32, message.anon_file().take_fd(), message.size(), + 1, {}, Gfx::Bitmap::ShouldCloseAnonymousFile::Yes); window.set_backing_store(move(backing_store), message.serial()); diff --git a/Userland/Services/WindowServer/Compositor.cpp b/Userland/Services/WindowServer/Compositor.cpp index 1b91fbc92f..a5d69df513 100644 --- a/Userland/Services/WindowServer/Compositor.cpp +++ b/Userland/Services/WindowServer/Compositor.cpp @@ -92,19 +92,19 @@ Compositor::Compositor() void Compositor::init_bitmaps() { auto& screen = Screen::the(); - auto physical_size = screen.physical_size(); + auto size = screen.size(); - m_front_bitmap = Gfx::Bitmap::create_wrapper(Gfx::BitmapFormat::RGB32, physical_size, screen.pitch(), screen.scanline(0)); - m_front_painter = make<Gfx::Painter>(*m_front_bitmap, screen.scale_factor()); + m_front_bitmap = Gfx::Bitmap::create_wrapper(Gfx::BitmapFormat::RGB32, size, screen.scale_factor(), screen.pitch(), screen.scanline(0)); + m_front_painter = make<Gfx::Painter>(*m_front_bitmap); if (m_screen_can_set_buffer) - m_back_bitmap = Gfx::Bitmap::create_wrapper(Gfx::BitmapFormat::RGB32, physical_size, screen.pitch(), screen.scanline(physical_size.height())); + m_back_bitmap = Gfx::Bitmap::create_wrapper(Gfx::BitmapFormat::RGB32, size, screen.scale_factor(), screen.pitch(), screen.scanline(screen.physical_height())); else - m_back_bitmap = Gfx::Bitmap::create(Gfx::BitmapFormat::RGB32, physical_size); - m_back_painter = make<Gfx::Painter>(*m_back_bitmap, screen.scale_factor()); + m_back_bitmap = Gfx::Bitmap::create(Gfx::BitmapFormat::RGB32, size, screen.scale_factor()); + m_back_painter = make<Gfx::Painter>(*m_back_bitmap); - m_temp_bitmap = Gfx::Bitmap::create(Gfx::BitmapFormat::RGB32, physical_size); - m_temp_painter = make<Gfx::Painter>(*m_temp_bitmap, screen.scale_factor()); + m_temp_bitmap = Gfx::Bitmap::create(Gfx::BitmapFormat::RGB32, size, screen.scale_factor()); + m_temp_painter = make<Gfx::Painter>(*m_temp_bitmap); m_buffers_are_flipped = false; @@ -450,14 +450,8 @@ void Compositor::compose() }()); // Copy anything rendered to the temporary buffer to the back buffer - { - // FIXME: Give Bitmap an intrinsic scale factor and make Painter::blit() do the right thing if both it and the passed bitmap have scale factors: - // If a 2x scaled bitmap is blitted on a 2x scaled painter, it should be blitted without scale. - Gfx::Painter unscaled_back_painter(*m_back_bitmap, 1); - auto scale = Screen::the().scale_factor(); - for (auto& rect : flush_transparent_rects.rects()) - unscaled_back_painter.blit(rect.location() * scale, *m_temp_bitmap, rect * scale); - } + for (auto& rect : flush_transparent_rects.rects()) + back_painter.blit(rect.location(), *m_temp_bitmap, rect); Gfx::IntRect geometry_label_damage_rect; if (draw_geometry_label(geometry_label_damage_rect)) @@ -799,32 +793,24 @@ void Compositor::draw_cursor(const Gfx::IntRect& cursor_rect) { auto& wm = WindowManager::the(); - auto physical_cursor_size = cursor_rect.size() * Screen::the().scale_factor(); - if (!m_cursor_back_bitmap || m_cursor_back_bitmap->size() != physical_cursor_size) { - m_cursor_back_bitmap = Gfx::Bitmap::create(Gfx::BitmapFormat::RGB32, physical_cursor_size); + if (!m_cursor_back_bitmap || m_cursor_back_bitmap->size() != cursor_rect.size() || m_cursor_back_bitmap->scale() != Screen::the().scale_factor()) { + m_cursor_back_bitmap = Gfx::Bitmap::create(Gfx::BitmapFormat::RGB32, cursor_rect.size(), Screen::the().scale_factor()); m_cursor_back_painter = make<Gfx::Painter>(*m_cursor_back_bitmap); } auto& current_cursor = m_current_cursor ? *m_current_cursor : wm.active_cursor(); - // FIXME: Give Bitmap an intrinsic scale factor and make Painter::blit() do the right thing if both it and the passed bitmap have scale factors: - // If a 2x scaled bitmap is blitted on a 2x scaled painter, it should be blitted without scale. - m_cursor_back_painter->blit({ 0, 0 }, *m_back_bitmap, (current_cursor.rect().translated(cursor_rect.location()) * Screen::the().scale_factor()).intersected(Screen::the().physical_rect())); - + m_cursor_back_painter->blit({ 0, 0 }, *m_back_bitmap, current_cursor.rect().translated(cursor_rect.location()).intersected(Screen::the().rect())); m_back_painter->blit(cursor_rect.location(), current_cursor.bitmap(), current_cursor.source_rect(m_current_cursor_frame)); - m_last_cursor_rect = cursor_rect; } void Compositor::restore_cursor_back() { - if (!m_cursor_back_bitmap) + if (!m_cursor_back_bitmap || m_cursor_back_bitmap->scale() != m_back_bitmap->scale()) return; - // FIXME: Give Bitmap an intrinsic scale factor and make Painter::blit() do the right thing if both it and the passed bitmap have scale factors: - // If a 2x scaled bitmap is blitted on a 2x scaled painter, it should be blitted without scale. - Gfx::Painter unscaled_back_painter(*m_back_bitmap, 1); - auto last_physical_cursor_rect = (m_last_cursor_rect * Screen::the().scale_factor()).intersected(Screen::the().physical_rect()); - unscaled_back_painter.blit(last_physical_cursor_rect.location(), *m_cursor_back_bitmap, { { 0, 0 }, last_physical_cursor_rect.size() }); + auto last_cursor_rect = m_last_cursor_rect.intersected(Screen::the().rect()); + m_back_painter->blit(last_cursor_rect.location(), *m_cursor_back_bitmap, { { 0, 0 }, last_cursor_rect.size() }); } void Compositor::notify_display_links() |