diff options
author | Nico Weber <thakis@chromium.org> | 2021-01-19 12:10:47 -0500 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2021-01-20 10:28:27 +0100 |
commit | 5f9c42c404a5acfa66461a3c6568592f5326e9de (patch) | |
tree | 9d29f3caeea87721d8ec9a3d754e526bb5d253b9 /Userland/Libraries/LibGfx/Bitmap.cpp | |
parent | c6726f331ea53bc7ee5679a84c6c10af65428a4b (diff) | |
download | serenity-5f9c42c404a5acfa66461a3c6568592f5326e9de.zip |
LibGfx: Give Bitmap a scale factor
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.
Diffstat (limited to 'Userland/Libraries/LibGfx/Bitmap.cpp')
-rw-r--r-- | Userland/Libraries/LibGfx/Bitmap.cpp | 119 |
1 files changed, 61 insertions, 58 deletions
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) |