LibWeb+LibGfx: Optimize generation of alpha mask from bitmap
Some checks are pending
CI / Lagom (false, FUZZ, ubuntu-22.04, Linux, Clang) (push) Waiting to run
CI / Lagom (false, NO_FUZZ, macos-14, macOS, Clang) (push) Waiting to run
CI / Lagom (false, NO_FUZZ, ubuntu-22.04, Linux, GNU) (push) Waiting to run
CI / Lagom (true, NO_FUZZ, ubuntu-22.04, Linux, Clang) (push) Waiting to run
Package the js repl as a binary artifact / build-and-package (macos-14, macOS, macOS-universal2) (push) Waiting to run
Package the js repl as a binary artifact / build-and-package (ubuntu-22.04, Linux, Linux-x86_64) (push) Waiting to run
Run test262 and test-wasm / run_and_update_results (push) Waiting to run
Lint Code / lint (push) Waiting to run
Push notes / build (push) Waiting to run

It would be nice if we could somehow move this work to the GPU, but even
with some basic local optimization (mostly coalescing bounds checks and
inlining pixel data access), this knocks a 13% item down to 9% in a
profile of loading https://vercel.com/
This commit is contained in:
Andreas Kling 2024-09-07 13:21:51 +02:00 committed by Andreas Kling
parent f647d190a3
commit b10016d914
Notes: github-actions[bot] 2024-09-07 17:45:20 +00:00
2 changed files with 63 additions and 18 deletions

View file

@ -95,6 +95,11 @@ public:
[[nodiscard]] ARGB32* scanline(int physical_y);
[[nodiscard]] ARGB32 const* scanline(int physical_y) const;
[[nodiscard]] u8* unchecked_scanline_u8(int physical_y);
[[nodiscard]] u8 const* unchecked_scanline_u8(int physical_y) const;
[[nodiscard]] ARGB32* unchecked_scanline(int physical_y);
[[nodiscard]] ARGB32 const* unchecked_scanline(int physical_y) const;
[[nodiscard]] ARGB32* begin();
[[nodiscard]] ARGB32 const* begin() const;
[[nodiscard]] ARGB32* end();
@ -139,6 +144,9 @@ public:
[[nodiscard]] static constexpr size_t size_in_bytes(size_t pitch, int height) { return pitch * height; }
[[nodiscard]] size_t size_in_bytes() const { return size_in_bytes(m_pitch, height()); }
template<StorageFormat>
[[nodiscard]] Color unchecked_get_pixel(int physical_x, int physical_y) const;
template<StorageFormat>
[[nodiscard]] Color get_pixel(int physical_x, int physical_y) const;
[[nodiscard]] Color get_pixel(int physical_x, int physical_y) const;
@ -178,18 +186,38 @@ private:
Function<void()> m_destruction_callback;
};
ALWAYS_INLINE u8* Bitmap::unchecked_scanline_u8(int y)
{
return reinterpret_cast<u8*>(m_data) + (y * m_pitch);
}
ALWAYS_INLINE u8 const* Bitmap::unchecked_scanline_u8(int y) const
{
return reinterpret_cast<u8 const*>(m_data) + (y * m_pitch);
}
ALWAYS_INLINE ARGB32* Bitmap::unchecked_scanline(int y)
{
return reinterpret_cast<ARGB32*>(unchecked_scanline_u8(y));
}
ALWAYS_INLINE ARGB32 const* Bitmap::unchecked_scanline(int y) const
{
return reinterpret_cast<ARGB32 const*>(unchecked_scanline_u8(y));
}
ALWAYS_INLINE u8* Bitmap::scanline_u8(int y)
{
VERIFY(y >= 0);
VERIFY(y < height());
return reinterpret_cast<u8*>(m_data) + (y * m_pitch);
return unchecked_scanline_u8(y);
}
ALWAYS_INLINE u8 const* Bitmap::scanline_u8(int y) const
{
VERIFY(y >= 0);
VERIFY(y < height());
return reinterpret_cast<u8 const*>(m_data) + (y * m_pitch);
return unchecked_scanline_u8(y);
}
ALWAYS_INLINE ARGB32* Bitmap::scanline(int y)
@ -228,19 +256,23 @@ ALWAYS_INLINE size_t Bitmap::data_size() const
}
template<>
ALWAYS_INLINE Color Bitmap::get_pixel<StorageFormat::BGRx8888>(int x, int y) const
ALWAYS_INLINE Color Bitmap::unchecked_get_pixel<StorageFormat::BGRx8888>(int x, int y) const
{
VERIFY(x >= 0);
VERIFY(x < width());
return Color::from_rgb(scanline(y)[x]);
return Color::from_rgb(unchecked_scanline(y)[x]);
}
template<>
ALWAYS_INLINE Color Bitmap::get_pixel<StorageFormat::BGRA8888>(int x, int y) const
ALWAYS_INLINE Color Bitmap::unchecked_get_pixel<StorageFormat::BGRA8888>(int x, int y) const
{
return Color::from_argb(unchecked_scanline(y)[x]);
}
template<StorageFormat storage_format>
ALWAYS_INLINE Color Bitmap::get_pixel(int x, int y) const
{
VERIFY(x >= 0);
VERIFY(x < width());
return Color::from_argb(scanline(y)[x]);
return unchecked_get_pixel<storage_format>(x, y);
}
ALWAYS_INLINE Color Bitmap::get_pixel(int x, int y) const

View file

@ -430,25 +430,38 @@ void DisplayListPlayerSkia::restore(Restore const&)
canvas.restore();
}
static SkBitmap alpha_mask_from_bitmap(Gfx::Bitmap const& bitmap, Gfx::Bitmap::MaskKind kind)
template<Gfx::Bitmap::MaskKind mask_kind, Gfx::StorageFormat storage_format>
[[maybe_unused]] static SkBitmap alpha_mask_from_bitmap_impl(Gfx::Bitmap const& bitmap)
{
SkBitmap alpha_mask;
alpha_mask.allocPixels(SkImageInfo::MakeA8(bitmap.width(), bitmap.height()));
for (int y = 0; y < bitmap.height(); y++) {
for (int x = 0; x < bitmap.width(); x++) {
if (kind == Gfx::Bitmap::MaskKind::Luminance) {
auto color = bitmap.get_pixel(x, y);
*alpha_mask.getAddr8(x, y) = color.alpha() * color.luminosity() / 255;
} else {
VERIFY(kind == Gfx::Bitmap::MaskKind::Alpha);
auto color = bitmap.get_pixel(x, y);
*alpha_mask.getAddr8(x, y) = color.alpha();
int width = bitmap.width();
int height = bitmap.height();
for (int y = 0; y < height; y++) {
auto* dst = alpha_mask.getAddr8(0, y);
for (int x = 0; x < width; x++, ++dst) {
auto color = bitmap.unchecked_get_pixel<storage_format>(x, y);
if constexpr (mask_kind == Gfx::Bitmap::MaskKind::Luminance) {
*dst = color.alpha() * color.luminosity() / 255;
} else if constexpr (mask_kind == Gfx::Bitmap::MaskKind::Alpha) {
*dst = color.alpha();
}
}
}
return alpha_mask;
}
[[maybe_unused]] static SkBitmap alpha_mask_from_bitmap(Gfx::Bitmap const& bitmap, Gfx::Bitmap::MaskKind kind)
{
if (bitmap.format() == Gfx::BitmapFormat::BGRA8888) {
if (kind == Gfx::Bitmap::MaskKind::Luminance)
return alpha_mask_from_bitmap_impl<Gfx::Bitmap::MaskKind::Luminance, Gfx::StorageFormat::BGRA8888>(bitmap);
VERIFY(kind == Gfx::Bitmap::MaskKind::Alpha);
return alpha_mask_from_bitmap_impl<Gfx::Bitmap::MaskKind::Alpha, Gfx::StorageFormat::BGRA8888>(bitmap);
}
VERIFY_NOT_REACHED();
}
void DisplayListPlayerSkia::push_stacking_context(PushStackingContext const& command)
{
auto& canvas = surface().canvas();