diff --git a/Userland/Libraries/LibAccelGfx/CMakeLists.txt b/Userland/Libraries/LibAccelGfx/CMakeLists.txt index 4576622ae0b..fa5e2719085 100644 --- a/Userland/Libraries/LibAccelGfx/CMakeLists.txt +++ b/Userland/Libraries/LibAccelGfx/CMakeLists.txt @@ -3,6 +3,7 @@ include(accelerated_graphics) if (HAS_ACCELERATED_GRAPHICS) set(SOURCES GL.cpp + GlyphAtlas.cpp Context.cpp Painter.cpp Program.cpp diff --git a/Userland/Libraries/LibAccelGfx/GlyphAtlas.cpp b/Userland/Libraries/LibAccelGfx/GlyphAtlas.cpp new file mode 100644 index 00000000000..34a7c07d50a --- /dev/null +++ b/Userland/Libraries/LibAccelGfx/GlyphAtlas.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2023, Aliaksandr Kalenik + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include + +namespace AccelGfx { + +GlyphAtlas& GlyphAtlas::the() +{ + static OwnPtr s_the; + if (!s_the) + s_the = make(); + return *s_the; +} + +void GlyphAtlas::update(HashMap> const& unique_glyphs) +{ + HashMap> glyph_bitmaps; + for (auto const& [font, code_points] : unique_glyphs) { + for (auto const& code_point : code_points) { + auto glyph = font->glyph(code_point); + if (glyph.bitmap()) { + auto atlas_key = GlyphsTextureKey { font, code_point }; + glyph_bitmaps.set(atlas_key, *glyph.bitmap()); + } + } + } + + if (glyph_bitmaps.is_empty()) + return; + + Vector glyphs_sorted_by_height; + glyphs_sorted_by_height.ensure_capacity(glyph_bitmaps.size()); + for (auto const& [atlas_key, bitmap] : glyph_bitmaps) { + glyphs_sorted_by_height.append(atlas_key); + } + quick_sort(glyphs_sorted_by_height, [&](auto const& a, auto const& b) { + auto const& bitmap_a = *glyph_bitmaps.get(a); + auto const& bitmap_b = *glyph_bitmaps.get(b); + return bitmap_a->height() > bitmap_b->height(); + }); + + int current_x = 0; + int current_y = 0; + int row_height = 0; + int const texture_width = 512; + int const padding = 1; + for (auto const& glyphs_texture_key : glyphs_sorted_by_height) { + auto const& bitmap = *glyph_bitmaps.get(glyphs_texture_key); + if (current_x + bitmap->width() > texture_width) { + current_x = 0; + current_y += row_height + padding; + row_height = 0; + } + m_glyphs_texture_map.set(glyphs_texture_key, { current_x, current_y, bitmap->width(), bitmap->height() }); + current_x += bitmap->width() + padding; + row_height = max(row_height, bitmap->height()); + } + + auto glyphs_texture_bitmap = MUST(Gfx::Bitmap::create(Gfx::BitmapFormat::BGRA8888, { texture_width, current_y + row_height })); + auto glyphs_texture_painter = Gfx::Painter(*glyphs_texture_bitmap); + for (auto const& [glyphs_texture_key, glyph_bitmap] : glyph_bitmaps) { + auto rect = m_glyphs_texture_map.get(glyphs_texture_key).value(); + glyphs_texture_painter.blit({ rect.x(), rect.y() }, glyph_bitmap, glyph_bitmap->rect()); + } + + GL::upload_texture_data(m_texture, *glyphs_texture_bitmap); +} + +Optional GlyphAtlas::get_glyph_rect(Gfx::Font const* font, u32 code_point) const +{ + auto atlas_key = GlyphsTextureKey { font, code_point }; + return m_glyphs_texture_map.get(atlas_key); +} + +} diff --git a/Userland/Libraries/LibAccelGfx/GlyphAtlas.h b/Userland/Libraries/LibAccelGfx/GlyphAtlas.h new file mode 100644 index 00000000000..e8887550ea6 --- /dev/null +++ b/Userland/Libraries/LibAccelGfx/GlyphAtlas.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2023, Aliaksandr Kalenik + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include + +namespace AccelGfx { + +class GlyphAtlas { + AK_MAKE_NONCOPYABLE(GlyphAtlas); + +public: + GlyphAtlas() + : m_texture(GL::create_texture()) + { + } + + ~GlyphAtlas() + { + GL::delete_texture(m_texture); + } + + static GlyphAtlas& the(); + + struct GlyphsTextureKey { + Gfx::Font const* font; + u32 code_point; + + bool operator==(GlyphsTextureKey const& other) const + { + return font == other.font && code_point == other.code_point; + } + }; + + void update(HashMap> const& unique_glyphs); + Optional get_glyph_rect(Gfx::Font const*, u32 code_point) const; + + GL::Texture const& texture() const { return m_texture; } + +private: + GL::Texture m_texture; + HashMap m_glyphs_texture_map; +}; + +} + +namespace AK { + +template<> +struct Traits : public DefaultTraits { + static unsigned hash(AccelGfx::GlyphAtlas::GlyphsTextureKey const& key) + { + return pair_int_hash(ptr_hash(key.font), key.code_point); + } +}; + +} diff --git a/Userland/Libraries/LibAccelGfx/Painter.cpp b/Userland/Libraries/LibAccelGfx/Painter.cpp index 864b19e4d39..356d1606c24 100644 --- a/Userland/Libraries/LibAccelGfx/Painter.cpp +++ b/Userland/Libraries/LibAccelGfx/Painter.cpp @@ -5,10 +5,8 @@ * SPDX-License-Identifier: BSD-2-Clause */ -#include #include #include -#include #include #include @@ -145,23 +143,12 @@ OwnPtr Painter::create() return make(context); } -OwnPtr Painter::create_with_glyphs_texture_from_painter(Painter const& painter) -{ - auto& context = Context::the(); - auto glyphs_texture_painter = make(context); - glyphs_texture_painter->m_glyphs_texture = painter.m_glyphs_texture; - glyphs_texture_painter->m_glyphs_texture_size = painter.m_glyphs_texture_size; - glyphs_texture_painter->m_glyphs_texture_map = painter.m_glyphs_texture_map; - return glyphs_texture_painter; -} - Painter::Painter(Context& context) : m_context(context) , m_rectangle_program(Program::create(Program::Name::RectangleProgram, vertex_shader_source, solid_color_fragment_shader_source)) , m_rounded_rectangle_program(Program::create(Program::Name::RoundedRectangleProgram, vertex_shader_source, rect_with_rounded_corners_fragment_shader_source)) , m_blit_program(Program::create(Program::Name::BlitProgram, blit_vertex_shader_source, blit_fragment_shader_source)) , m_linear_gradient_program(Program::create(Program::Name::LinearGradientProgram, linear_gradient_vertex_shader_source, linear_gradient_fragment_shader_source)) - , m_glyphs_texture(GL::create_texture()) { m_state_stack.empend(State()); } @@ -365,62 +352,6 @@ void Painter::draw_scaled_bitmap(Gfx::FloatRect const& dst_rect, Gfx::Bitmap con GL::delete_texture(texture); } -void Painter::prepare_glyph_texture(HashMap> const& unique_glyphs) -{ - HashMap> glyph_bitmaps; - for (auto const& [font, code_points] : unique_glyphs) { - for (auto const& code_point : code_points) { - auto glyph = font->glyph(code_point); - if (glyph.bitmap()) { - auto atlas_key = GlyphsTextureKey { font, code_point }; - glyph_bitmaps.set(atlas_key, *glyph.bitmap()); - } - } - } - - if (glyph_bitmaps.is_empty()) - return; - - Vector glyphs_sorted_by_height; - glyphs_sorted_by_height.ensure_capacity(glyph_bitmaps.size()); - for (auto const& [atlas_key, bitmap] : glyph_bitmaps) { - glyphs_sorted_by_height.append(atlas_key); - } - quick_sort(glyphs_sorted_by_height, [&](auto const& a, auto const& b) { - auto const& bitmap_a = *glyph_bitmaps.get(a); - auto const& bitmap_b = *glyph_bitmaps.get(b); - return bitmap_a->height() > bitmap_b->height(); - }); - - int current_x = 0; - int current_y = 0; - int row_height = 0; - int texture_width = 512; - int padding = 1; - for (auto const& glyphs_texture_key : glyphs_sorted_by_height) { - auto const& bitmap = *glyph_bitmaps.get(glyphs_texture_key); - if (current_x + bitmap->width() > texture_width) { - current_x = 0; - current_y += row_height + padding; - row_height = 0; - } - m_glyphs_texture_map.set(glyphs_texture_key, { current_x, current_y, bitmap->width(), bitmap->height() }); - current_x += bitmap->width() + padding; - row_height = max(row_height, bitmap->height()); - } - - auto glyphs_texture_bitmap = MUST(Gfx::Bitmap::create(Gfx::BitmapFormat::BGRA8888, { texture_width, current_y + row_height })); - auto glyphs_texure_painter = Gfx::Painter(*glyphs_texture_bitmap); - for (auto const& [glyphs_texture_key, glyph_bitmap] : glyph_bitmaps) { - auto rect = m_glyphs_texture_map.get(glyphs_texture_key).value(); - glyphs_texure_painter.blit({ rect.x(), rect.y() }, glyph_bitmap, glyph_bitmap->rect()); - } - - m_glyphs_texture_size = glyphs_texture_bitmap->size(); - - GL::upload_texture_data(m_glyphs_texture, *glyphs_texture_bitmap); -} - void Painter::draw_glyph_run(Vector const& glyph_run, Color const& color) { bind_target_canvas(); @@ -428,20 +359,22 @@ void Painter::draw_glyph_run(Vector const& glyph_run, Col Vector vertices; vertices.ensure_capacity(glyph_run.size() * 24); - for (auto& glyph_or_emoji : glyph_run) { + auto const& glyph_atlas = GlyphAtlas::the(); + + for (auto const& glyph_or_emoji : glyph_run) { if (glyph_or_emoji.has()) { - auto& glyph = glyph_or_emoji.get(); + auto const& glyph = glyph_or_emoji.get(); auto const* font = glyph.font; auto code_point = glyph.code_point; auto point = glyph.position; - auto maybe_texture_rect = m_glyphs_texture_map.get(GlyphsTextureKey { font, code_point }); + auto maybe_texture_rect = glyph_atlas.get_glyph_rect(font, code_point); if (!maybe_texture_rect.has_value()) { continue; } - auto texture_rect = to_texture_space(maybe_texture_rect.value().to_type(), m_glyphs_texture_size); + auto texture_rect = to_texture_space(maybe_texture_rect.value().to_type(), *glyph_atlas.texture().size); auto glyph_position = point + Gfx::FloatPoint(font->glyph_left_bearing(code_point), 0); auto glyph_size = maybe_texture_rect->size().to_type(); @@ -497,7 +430,7 @@ void Painter::draw_glyph_run(Vector const& glyph_run, Col m_blit_program.use(); - GL::bind_texture(m_glyphs_texture); + GL::bind_texture(glyph_atlas.texture()); GL::set_texture_scale_mode(GL::ScalingMode::Nearest); auto position_attribute = m_blit_program.get_attribute_location("aVertexPosition"); diff --git a/Userland/Libraries/LibAccelGfx/Painter.h b/Userland/Libraries/LibAccelGfx/Painter.h index ef20e8b3985..34a768f5a4d 100644 --- a/Userland/Libraries/LibAccelGfx/Painter.h +++ b/Userland/Libraries/LibAccelGfx/Painter.h @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -29,7 +30,6 @@ class Painter { public: static OwnPtr create(); - static OwnPtr create_with_glyphs_texture_from_painter(Painter const& painter); Painter(Context&); ~Painter(); @@ -60,18 +60,6 @@ public: void draw_scaled_immutable_bitmap(Gfx::IntRect const& dst_rect, Gfx::ImmutableBitmap const&, Gfx::IntRect const& src_rect, ScalingMode = ScalingMode::NearestNeighbor); void draw_scaled_immutable_bitmap(Gfx::FloatRect const& dst_rect, Gfx::ImmutableBitmap const&, Gfx::FloatRect const& src_rect, ScalingMode = ScalingMode::NearestNeighbor); - void prepare_glyph_texture(HashMap> const& unique_glyphs); - - struct GlyphsTextureKey { - Gfx::Font const* font; - u32 code_point; - - bool operator==(GlyphsTextureKey const& other) const - { - return font == other.font && code_point == other.code_point; - } - }; - void draw_glyph_run(Vector const& glyph_run, Color const& color); void set_clip_rect(Gfx::IntRect); @@ -118,22 +106,6 @@ private: Program m_rounded_rectangle_program; Program m_blit_program; Program m_linear_gradient_program; - - HashMap m_glyphs_texture_map; - Gfx::IntSize m_glyphs_texture_size; - GL::Texture m_glyphs_texture; -}; - -} - -namespace AK { - -template<> -struct Traits : public DefaultTraits { - static unsigned hash(AccelGfx::Painter::GlyphsTextureKey const& key) - { - return pair_int_hash(ptr_hash(key.font), key.code_point); - } }; } diff --git a/Userland/Libraries/LibWeb/Painting/PaintingCommandExecutorGPU.cpp b/Userland/Libraries/LibWeb/Painting/PaintingCommandExecutorGPU.cpp index f4712d5bf9f..45adb4b0851 100644 --- a/Userland/Libraries/LibWeb/Painting/PaintingCommandExecutorGPU.cpp +++ b/Userland/Libraries/LibWeb/Painting/PaintingCommandExecutorGPU.cpp @@ -4,6 +4,7 @@ * SPDX-License-Identifier: BSD-2-Clause */ +#include #include namespace Web::Painting { @@ -92,7 +93,7 @@ CommandResult PaintingCommandExecutorGPU::set_font(Gfx::Font const&) CommandResult PaintingCommandExecutorGPU::push_stacking_context(float opacity, bool, Gfx::IntRect const& source_paintable_rect, Gfx::IntPoint post_transform_translation, CSS::ImageRendering, StackingContextTransform, Optional) { if (opacity < 1) { - auto painter = AccelGfx::Painter::create_with_glyphs_texture_from_painter(this->painter()); + auto painter = AccelGfx::Painter::create(); auto canvas = AccelGfx::Canvas::create(source_paintable_rect.size()); painter->set_target_canvas(canvas); painter->translate(-source_paintable_rect.location().to_type()); @@ -313,7 +314,7 @@ bool PaintingCommandExecutorGPU::would_be_fully_clipped_by_painter(Gfx::IntRect) void PaintingCommandExecutorGPU::prepare_glyph_texture(HashMap> const& unique_glyphs) { - painter().prepare_glyph_texture(unique_glyphs); + AccelGfx::GlyphAtlas::the().update(unique_glyphs); } void PaintingCommandExecutorGPU::update_immutable_bitmap_texture_cache(HashMap& immutable_bitmaps)