diff --git a/Ladybird/Android/src/main/cpp/WebViewImplementationNative.cpp b/Ladybird/Android/src/main/cpp/WebViewImplementationNative.cpp index e001104a8d7..c4cb12e7595 100644 --- a/Ladybird/Android/src/main/cpp/WebViewImplementationNative.cpp +++ b/Ladybird/Android/src/main/cpp/WebViewImplementationNative.cpp @@ -69,7 +69,7 @@ void WebViewImplementationNative::paint_into_bitmap(void* android_bitmap_raw, An // Software bitmaps only for now! VERIFY((info.flags & ANDROID_BITMAP_FLAGS_IS_HARDWARE) == 0); - auto android_bitmap = MUST(Gfx::Bitmap::create_wrapper(to_gfx_bitmap_format(info.format), { info.width, info.height }, info.stride, android_bitmap_raw)); + auto android_bitmap = MUST(Gfx::Bitmap::create_wrapper(to_gfx_bitmap_format(info.format), Gfx::AlphaType::Premultiplied, { info.width, info.height }, info.stride, android_bitmap_raw)); Gfx::Painter painter(android_bitmap); if (auto* bitmap = m_client_state.has_usable_bitmap ? m_client_state.front_bitmap.bitmap.ptr() : m_backup_bitmap.ptr()) painter.blit({ 0, 0 }, *bitmap, bitmap->rect()); diff --git a/Userland/Libraries/LibGfx/Bitmap.cpp b/Userland/Libraries/LibGfx/Bitmap.cpp index 2c745240bec..0a3e3926d66 100644 --- a/Userland/Libraries/LibGfx/Bitmap.cpp +++ b/Userland/Libraries/LibGfx/Bitmap.cpp @@ -1,6 +1,7 @@ /* * Copyright (c) 2018-2024, Andreas Kling * Copyright (c) 2022, Timothy Slater + * Copyright (c) 2024, Jelle Raaijmakers * * SPDX-License-Identifier: BSD-2-Clause */ @@ -60,11 +61,17 @@ static bool size_would_overflow(BitmapFormat format, IntSize size) ErrorOr> Bitmap::create(BitmapFormat format, IntSize size) { - auto backing_store = TRY(Bitmap::allocate_backing_store(format, size)); - return AK::adopt_nonnull_ref_or_enomem(new (nothrow) Bitmap(format, size, backing_store)); + // For backwards compatibility, premultiplied alpha is assumed + return create(format, AlphaType::Premultiplied, size); } -ErrorOr> Bitmap::create_shareable(BitmapFormat format, IntSize size) +ErrorOr> Bitmap::create(BitmapFormat format, AlphaType alpha_type, IntSize size) +{ + auto backing_store = TRY(Bitmap::allocate_backing_store(format, size)); + return AK::adopt_nonnull_ref_or_enomem(new (nothrow) Bitmap(format, alpha_type, size, backing_store)); +} + +ErrorOr> Bitmap::create_shareable(BitmapFormat format, AlphaType alpha_type, IntSize size) { if (size_would_overflow(format, size)) return Error::from_string_literal("Gfx::Bitmap::create_shareable size overflow"); @@ -73,15 +80,16 @@ ErrorOr> Bitmap::create_shareable(BitmapFormat format, Int auto const data_size = size_in_bytes(pitch, size.height()); auto buffer = TRY(Core::AnonymousBuffer::create_with_size(round_up_to_power_of_two(data_size, PAGE_SIZE))); - auto bitmap = TRY(Bitmap::create_with_anonymous_buffer(format, buffer, size)); + auto bitmap = TRY(Bitmap::create_with_anonymous_buffer(format, alpha_type, buffer, size)); return bitmap; } -Bitmap::Bitmap(BitmapFormat format, IntSize size, BackingStore const& backing_store) +Bitmap::Bitmap(BitmapFormat format, AlphaType alpha_type, IntSize size, BackingStore const& backing_store) : m_size(size) , m_data(backing_store.data) , m_pitch(backing_store.pitch) , m_format(format) + , m_alpha_type(alpha_type) { VERIFY(!m_size.is_empty()); VERIFY(!size_would_overflow(format, size)); @@ -92,11 +100,11 @@ Bitmap::Bitmap(BitmapFormat format, IntSize size, BackingStore const& backing_st }; } -ErrorOr> Bitmap::create_wrapper(BitmapFormat format, IntSize size, size_t pitch, void* data, Function&& destruction_callback) +ErrorOr> Bitmap::create_wrapper(BitmapFormat format, AlphaType alpha_type, IntSize size, size_t pitch, void* data, Function&& destruction_callback) { if (size_would_overflow(format, size)) return Error::from_string_literal("Gfx::Bitmap::create_wrapper size overflow"); - return adopt_ref(*new Bitmap(format, size, pitch, data, move(destruction_callback))); + return adopt_ref(*new Bitmap(format, alpha_type, size, pitch, data, move(destruction_callback))); } ErrorOr> Bitmap::load_from_file(StringView path, Optional ideal_size) @@ -123,11 +131,12 @@ ErrorOr> Bitmap::load_from_bytes(ReadonlyBytes bytes, Opti return Error::from_string_literal("Gfx::Bitmap unable to load from file"); } -Bitmap::Bitmap(BitmapFormat format, IntSize size, size_t pitch, void* data, Function&& destruction_callback) +Bitmap::Bitmap(BitmapFormat format, AlphaType alpha_type, IntSize size, size_t pitch, void* data, Function&& destruction_callback) : m_size(size) , m_data(data) , m_pitch(pitch) , m_format(format) + , m_alpha_type(alpha_type) , m_destruction_callback(move(destruction_callback)) { VERIFY(pitch >= minimum_pitch(size.width(), format)); @@ -135,19 +144,20 @@ Bitmap::Bitmap(BitmapFormat format, IntSize size, size_t pitch, void* data, Func // FIXME: assert that `data` is actually long enough! } -ErrorOr> Bitmap::create_with_anonymous_buffer(BitmapFormat format, Core::AnonymousBuffer buffer, IntSize size) +ErrorOr> Bitmap::create_with_anonymous_buffer(BitmapFormat format, AlphaType alpha_type, Core::AnonymousBuffer buffer, IntSize size) { if (size_would_overflow(format, size)) return Error::from_string_literal("Gfx::Bitmap::create_with_anonymous_buffer size overflow"); - return adopt_nonnull_ref_or_enomem(new (nothrow) Bitmap(format, move(buffer), size)); + return adopt_nonnull_ref_or_enomem(new (nothrow) Bitmap(format, alpha_type, move(buffer), size)); } -Bitmap::Bitmap(BitmapFormat format, Core::AnonymousBuffer buffer, IntSize size) +Bitmap::Bitmap(BitmapFormat format, AlphaType alpha_type, Core::AnonymousBuffer buffer, IntSize size) : m_size(size) , m_data(buffer.data()) , m_pitch(minimum_pitch(size.width(), format)) , m_format(format) + , m_alpha_type(alpha_type) , m_buffer(move(buffer)) { VERIFY(!size_would_overflow(format, size)); @@ -155,7 +165,7 @@ Bitmap::Bitmap(BitmapFormat format, Core::AnonymousBuffer buffer, IntSize size) ErrorOr> Bitmap::clone() const { - auto new_bitmap = TRY(Bitmap::create(format(), size())); + auto new_bitmap = TRY(Bitmap::create(format(), alpha_type(), size())); VERIFY(size_in_bytes() == new_bitmap->size_in_bytes()); memcpy(new_bitmap->scanline(0), scanline(0), size_in_bytes()); @@ -188,7 +198,7 @@ ErrorOr> Bitmap::scaled(int sx, int sy) const if (sx == 1 && sy == 1) return clone(); - auto new_bitmap = TRY(Gfx::Bitmap::create(format(), { width() * sx, height() * sy })); + auto new_bitmap = TRY(Gfx::Bitmap::create(format(), alpha_type(), { width() * sx, height() * sy })); auto old_width = width(); auto old_height = height(); @@ -224,7 +234,7 @@ ErrorOr> Bitmap::scaled(float sx, float sy) const // http://fourier.eng.hmc.edu/e161/lectures/resize/node3.html ErrorOr> Bitmap::scaled_to_size(Gfx::IntSize size) const { - auto new_bitmap = TRY(Gfx::Bitmap::create(format(), size)); + auto new_bitmap = TRY(Gfx::Bitmap::create(format(), alpha_type(), size)); auto old_width = width(); auto old_height = height(); @@ -344,7 +354,7 @@ ErrorOr> Bitmap::scaled_to_size(Gfx::IntSize size) co ErrorOr> Bitmap::cropped(Gfx::IntRect crop, Optional new_bitmap_format) const { - auto new_bitmap = TRY(Gfx::Bitmap::create(new_bitmap_format.value_or(format()), { crop.width(), crop.height() })); + auto new_bitmap = TRY(Gfx::Bitmap::create(new_bitmap_format.value_or(format()), alpha_type(), { crop.width(), crop.height() })); for (int y = 0; y < crop.height(); ++y) { for (int x = 0; x < crop.width(); ++x) { @@ -367,7 +377,7 @@ ErrorOr> Bitmap::to_bitmap_backed_by_anonymous_buffer() co return NonnullRefPtr { const_cast(*this) }; } auto buffer = TRY(Core::AnonymousBuffer::create_with_size(round_up_to_power_of_two(size_in_bytes(), PAGE_SIZE))); - auto bitmap = TRY(Bitmap::create_with_anonymous_buffer(m_format, move(buffer), size())); + auto bitmap = TRY(Bitmap::create_with_anonymous_buffer(format(), alpha_type(), move(buffer), size())); memcpy(bitmap->scanline(0), scanline(0), size_in_bytes()); return bitmap; } @@ -453,6 +463,7 @@ ErrorOr encode(Encoder& encoder, AK::NonnullRefPtr const& bit } TRY(encoder.encode(TRY(IPC::File::clone_fd(buffer.fd())))); TRY(encoder.encode(static_cast(bitmap->format()))); + TRY(encoder.encode(static_cast(bitmap->alpha_type()))); TRY(encoder.encode(bitmap->size_in_bytes())); TRY(encoder.encode(bitmap->pitch())); TRY(encoder.encode(bitmap->size())); @@ -463,15 +474,22 @@ template<> ErrorOr> decode(Decoder& decoder) { auto anon_file = TRY(decoder.decode()); + auto raw_bitmap_format = TRY(decoder.decode()); if (!Gfx::is_valid_bitmap_format(raw_bitmap_format)) return Error::from_string_literal("IPC: Invalid Gfx::ShareableBitmap format"); auto bitmap_format = static_cast(raw_bitmap_format); + + auto raw_alpha_type = TRY(decoder.decode()); + if (!Gfx::is_valid_alpha_type(raw_alpha_type)) + return Error::from_string_literal("IPC: Invalid Gfx::ShareableBitmap alpha type"); + auto alpha_type = static_cast(raw_alpha_type); + auto size_in_bytes = TRY(decoder.decode()); auto pitch = TRY(decoder.decode()); auto size = TRY(decoder.decode()); auto* data = TRY(Core::System::mmap(nullptr, round_up_to_power_of_two(size_in_bytes, PAGE_SIZE), PROT_READ | PROT_WRITE, MAP_SHARED, anon_file.fd(), 0)); - return Gfx::Bitmap::create_wrapper(bitmap_format, size, pitch, data, [data, size_in_bytes] { + return Gfx::Bitmap::create_wrapper(bitmap_format, alpha_type, size, pitch, data, [data, size_in_bytes] { MUST(Core::System::munmap(data, size_in_bytes)); }); } diff --git a/Userland/Libraries/LibGfx/Bitmap.h b/Userland/Libraries/LibGfx/Bitmap.h index 9b8be274c4c..7481a0058e5 100644 --- a/Userland/Libraries/LibGfx/Bitmap.h +++ b/Userland/Libraries/LibGfx/Bitmap.h @@ -63,12 +63,13 @@ struct BackingStore; class Bitmap : public RefCounted { public: [[nodiscard]] static ErrorOr> create(BitmapFormat, IntSize); - [[nodiscard]] static ErrorOr> create_shareable(BitmapFormat, IntSize); - [[nodiscard]] static ErrorOr> create_wrapper(BitmapFormat, IntSize, size_t pitch, void*, Function&& destruction_callback = {}); + [[nodiscard]] static ErrorOr> create(BitmapFormat, AlphaType, IntSize); + [[nodiscard]] static ErrorOr> create_shareable(BitmapFormat, AlphaType, IntSize); + [[nodiscard]] static ErrorOr> create_wrapper(BitmapFormat, AlphaType, IntSize, size_t pitch, void*, Function&& destruction_callback = {}); [[nodiscard]] static ErrorOr> load_from_file(StringView path, Optional ideal_size = {}); [[nodiscard]] static ErrorOr> load_from_file(NonnullOwnPtr, StringView path, Optional ideal_size = {}); [[nodiscard]] static ErrorOr> load_from_bytes(ReadonlyBytes, Optional ideal_size = {}, Optional mine_type = {}); - [[nodiscard]] static ErrorOr> create_with_anonymous_buffer(BitmapFormat, Core::AnonymousBuffer, IntSize); + [[nodiscard]] static ErrorOr> create_with_anonymous_buffer(BitmapFormat, AlphaType, Core::AnonymousBuffer, IntSize); ErrorOr> clone() const; @@ -159,10 +160,12 @@ public: [[nodiscard]] bool visually_equals(Bitmap const&) const; + [[nodiscard]] AlphaType alpha_type() const { return m_alpha_type; } + private: - Bitmap(BitmapFormat, IntSize, BackingStore const&); - Bitmap(BitmapFormat, IntSize, size_t pitch, void*, Function&& destruction_callback); - Bitmap(BitmapFormat, Core::AnonymousBuffer, IntSize); + Bitmap(BitmapFormat, AlphaType, IntSize, BackingStore const&); + Bitmap(BitmapFormat, AlphaType, IntSize, size_t pitch, void*, Function&& destruction_callback); + Bitmap(BitmapFormat, AlphaType, Core::AnonymousBuffer, IntSize); static ErrorOr allocate_backing_store(BitmapFormat format, IntSize size); @@ -170,6 +173,7 @@ private: void* m_data { nullptr }; size_t m_pitch { 0 }; BitmapFormat m_format { BitmapFormat::Invalid }; + AlphaType m_alpha_type { AlphaType::Premultiplied }; Core::AnonymousBuffer m_buffer; Function m_destruction_callback; }; diff --git a/Userland/Libraries/LibGfx/Color.h b/Userland/Libraries/LibGfx/Color.h index 2bb53994949..2b4817fa6c3 100644 --- a/Userland/Libraries/LibGfx/Color.h +++ b/Userland/Libraries/LibGfx/Color.h @@ -25,6 +25,16 @@ enum class AlphaType { Unpremultiplied, }; +inline bool is_valid_alpha_type(u32 alpha_type) +{ + switch (alpha_type) { + case (u32)AlphaType::Premultiplied: + case (u32)AlphaType::Unpremultiplied: + return true; + } + return false; +} + struct HSV { double hue { 0 }; double saturation { 0 }; diff --git a/Userland/Libraries/LibGfx/ShareableBitmap.cpp b/Userland/Libraries/LibGfx/ShareableBitmap.cpp index 372fb2285d0..ce9f020b74a 100644 --- a/Userland/Libraries/LibGfx/ShareableBitmap.cpp +++ b/Userland/Libraries/LibGfx/ShareableBitmap.cpp @@ -33,6 +33,7 @@ ErrorOr encode(Encoder& encoder, Gfx::ShareableBitmap const& shareable_bit TRY(encoder.encode(TRY(IPC::File::clone_fd(bitmap.anonymous_buffer().fd())))); TRY(encoder.encode(bitmap.size())); TRY(encoder.encode(static_cast(bitmap.format()))); + TRY(encoder.encode(static_cast(bitmap.alpha_type()))); return {}; } @@ -44,14 +45,19 @@ ErrorOr decode(Decoder& decoder) auto anon_file = TRY(decoder.decode()); auto size = TRY(decoder.decode()); + auto raw_bitmap_format = TRY(decoder.decode()); if (!Gfx::is_valid_bitmap_format(raw_bitmap_format)) return Error::from_string_literal("IPC: Invalid Gfx::ShareableBitmap format"); - auto bitmap_format = static_cast(raw_bitmap_format); + auto raw_alpha_type = TRY(decoder.decode()); + if (!Gfx::is_valid_alpha_type(raw_alpha_type)) + return Error::from_string_literal("IPC: Invalid Gfx::ShareableBitmap alpha type"); + auto alpha_type = static_cast(raw_alpha_type); + auto buffer = TRY(Core::AnonymousBuffer::create_from_anon_fd(anon_file.take_fd(), Gfx::Bitmap::size_in_bytes(Gfx::Bitmap::minimum_pitch(size.width(), bitmap_format), size.height()))); - auto bitmap = TRY(Gfx::Bitmap::create_with_anonymous_buffer(bitmap_format, move(buffer), size)); + auto bitmap = TRY(Gfx::Bitmap::create_with_anonymous_buffer(bitmap_format, alpha_type, move(buffer), size)); return Gfx::ShareableBitmap { move(bitmap), Gfx::ShareableBitmap::ConstructWithKnownGoodBitmap }; } diff --git a/Userland/Libraries/LibWeb/HTML/ImageData.cpp b/Userland/Libraries/LibWeb/HTML/ImageData.cpp index ff49112f815..dd9af815d5e 100644 --- a/Userland/Libraries/LibWeb/HTML/ImageData.cpp +++ b/Userland/Libraries/LibWeb/HTML/ImageData.cpp @@ -30,7 +30,7 @@ WebIDL::ExceptionOr> ImageData::create(JS::Realm& re // 2. Initialize this given sw, sh, and settings set to settings. // 3. Initialize the image data of this to transparent black. auto data = TRY(JS::Uint8ClampedArray::create(realm, sw * sh * 4)); - auto bitmap = TRY_OR_THROW_OOM(vm, Gfx::Bitmap::create_wrapper(Gfx::BitmapFormat::RGBA8888, Gfx::IntSize(sw, sh), sw * sizeof(u32), data->data().data())); + auto bitmap = TRY_OR_THROW_OOM(vm, Gfx::Bitmap::create_wrapper(Gfx::BitmapFormat::RGBA8888, Gfx::AlphaType::Premultiplied, Gfx::IntSize(sw, sh), sw * sizeof(u32), data->data().data())); return realm.heap().allocate(realm, realm, bitmap, data); } @@ -74,7 +74,7 @@ WebIDL::ExceptionOr> ImageData::create(JS::Realm& re return WebIDL::IndexSizeError::create(realm, "Source height must be equal to the calculated height of the data."_fly_string); // 7. Initialize this given sw, sh, settings set to settings, and source set to data. - auto bitmap = TRY_OR_THROW_OOM(vm, Gfx::Bitmap::create_wrapper(Gfx::BitmapFormat::RGBA8888, Gfx::IntSize(sw, height), sw * sizeof(u32), uint8_clamped_array_data.data().data())); + auto bitmap = TRY_OR_THROW_OOM(vm, Gfx::Bitmap::create_wrapper(Gfx::BitmapFormat::RGBA8888, Gfx::AlphaType::Premultiplied, Gfx::IntSize(sw, height), sw * sizeof(u32), uint8_clamped_array_data.data().data())); return realm.heap().allocate(realm, realm, bitmap, uint8_clamped_array_data); } diff --git a/Userland/Libraries/LibWeb/Painting/BackingStore.cpp b/Userland/Libraries/LibWeb/Painting/BackingStore.cpp index df012934297..ccea6c21a60 100644 --- a/Userland/Libraries/LibWeb/Painting/BackingStore.cpp +++ b/Userland/Libraries/LibWeb/Painting/BackingStore.cpp @@ -19,7 +19,7 @@ IOSurfaceBackingStore::IOSurfaceBackingStore(Core::IOSurfaceHandle&& iosurface_h : m_iosurface_handle(move(iosurface_handle)) { auto bytes_per_row = m_iosurface_handle.bytes_per_row(); - auto bitmap = Gfx::Bitmap::create_wrapper(Gfx::BitmapFormat::BGRA8888, size(), bytes_per_row, m_iosurface_handle.data()); + auto bitmap = Gfx::Bitmap::create_wrapper(Gfx::BitmapFormat::BGRA8888, Gfx::AlphaType::Premultiplied, size(), bytes_per_row, m_iosurface_handle.data()); m_bitmap_wrapper = bitmap.release_value(); } diff --git a/Userland/Libraries/LibWebView/ViewImplementation.cpp b/Userland/Libraries/LibWebView/ViewImplementation.cpp index 6b4366aa332..bfdc7c1af2d 100644 --- a/Userland/Libraries/LibWebView/ViewImplementation.cpp +++ b/Userland/Libraries/LibWebView/ViewImplementation.cpp @@ -431,8 +431,8 @@ void ViewImplementation::did_allocate_iosurface_backing_stores(i32 front_id, Cor auto bytes_per_row = front_iosurface.bytes_per_row(); - auto front_bitmap = Gfx::Bitmap::create_wrapper(Gfx::BitmapFormat::BGRA8888, front_size, bytes_per_row, front_iosurface.data(), [handle = move(front_iosurface)] {}); - auto back_bitmap = Gfx::Bitmap::create_wrapper(Gfx::BitmapFormat::BGRA8888, back_size, bytes_per_row, back_iosurface.data(), [handle = move(back_iosurface)] {}); + auto front_bitmap = Gfx::Bitmap::create_wrapper(Gfx::BitmapFormat::BGRA8888, Gfx::AlphaType::Premultiplied, front_size, bytes_per_row, front_iosurface.data(), [handle = move(front_iosurface)] {}); + auto back_bitmap = Gfx::Bitmap::create_wrapper(Gfx::BitmapFormat::BGRA8888, Gfx::AlphaType::Premultiplied, back_size, bytes_per_row, back_iosurface.data(), [handle = move(back_iosurface)] {}); m_client_state.front_bitmap.bitmap = front_bitmap.release_value_but_fixme_should_propagate_errors(); m_client_state.front_bitmap.id = front_id; diff --git a/Userland/Services/WebContent/BackingStoreManager.cpp b/Userland/Services/WebContent/BackingStoreManager.cpp index 23c67e09551..74ea6e152c5 100644 --- a/Userland/Services/WebContent/BackingStoreManager.cpp +++ b/Userland/Services/WebContent/BackingStoreManager.cpp @@ -93,8 +93,8 @@ void BackingStoreManager::reallocate_backing_stores(Gfx::IntSize size) m_front_bitmap_id = m_next_bitmap_id++; m_back_bitmap_id = m_next_bitmap_id++; - auto front_bitmap = Gfx::Bitmap::create_shareable(Gfx::BitmapFormat::BGRA8888, size).release_value(); - auto back_bitmap = Gfx::Bitmap::create_shareable(Gfx::BitmapFormat::BGRA8888, size).release_value(); + auto front_bitmap = Gfx::Bitmap::create_shareable(Gfx::BitmapFormat::BGRA8888, Gfx::AlphaType::Premultiplied, size).release_value(); + auto back_bitmap = Gfx::Bitmap::create_shareable(Gfx::BitmapFormat::BGRA8888, Gfx::AlphaType::Premultiplied, size).release_value(); m_front_store = make(front_bitmap); m_back_store = make(back_bitmap);