mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-12-28 21:54:40 +03:00
LibAccelGfx+LibWeb: Move glyph atlas into a singleton class
This is going to allow us reuse glyph texture across painters and page repaints.
This commit is contained in:
parent
4b23046a22
commit
28723d8be1
Notes:
sideshowbarker
2024-07-17 01:12:07 +09:00
Author: https://github.com/kalenikaliaksandr Commit: https://github.com/SerenityOS/serenity/commit/28723d8be1 Pull-request: https://github.com/SerenityOS/serenity/pull/22060
@ -3,6 +3,7 @@ include(accelerated_graphics)
|
||||
if (HAS_ACCELERATED_GRAPHICS)
|
||||
set(SOURCES
|
||||
GL.cpp
|
||||
GlyphAtlas.cpp
|
||||
Context.cpp
|
||||
Painter.cpp
|
||||
Program.cpp
|
||||
|
81
Userland/Libraries/LibAccelGfx/GlyphAtlas.cpp
Normal file
81
Userland/Libraries/LibAccelGfx/GlyphAtlas.cpp
Normal file
@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright (c) 2023, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/QuickSort.h>
|
||||
#include <LibAccelGfx/GlyphAtlas.h>
|
||||
#include <LibGfx/Painter.h>
|
||||
|
||||
namespace AccelGfx {
|
||||
|
||||
GlyphAtlas& GlyphAtlas::the()
|
||||
{
|
||||
static OwnPtr<GlyphAtlas> s_the;
|
||||
if (!s_the)
|
||||
s_the = make<GlyphAtlas>();
|
||||
return *s_the;
|
||||
}
|
||||
|
||||
void GlyphAtlas::update(HashMap<Gfx::Font const*, HashTable<u32>> const& unique_glyphs)
|
||||
{
|
||||
HashMap<GlyphsTextureKey, NonnullRefPtr<Gfx::Bitmap>> 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<GlyphsTextureKey> 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<Gfx::IntRect> 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);
|
||||
}
|
||||
|
||||
}
|
64
Userland/Libraries/LibAccelGfx/GlyphAtlas.h
Normal file
64
Userland/Libraries/LibAccelGfx/GlyphAtlas.h
Normal file
@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright (c) 2023, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/HashMap.h>
|
||||
#include <AK/Noncopyable.h>
|
||||
#include <LibAccelGfx/GL.h>
|
||||
#include <LibGfx/Font/Font.h>
|
||||
|
||||
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<Gfx::Font const*, HashTable<u32>> const& unique_glyphs);
|
||||
Optional<Gfx::IntRect> get_glyph_rect(Gfx::Font const*, u32 code_point) const;
|
||||
|
||||
GL::Texture const& texture() const { return m_texture; }
|
||||
|
||||
private:
|
||||
GL::Texture m_texture;
|
||||
HashMap<GlyphsTextureKey, Gfx::IntRect> m_glyphs_texture_map;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace AK {
|
||||
|
||||
template<>
|
||||
struct Traits<AccelGfx::GlyphAtlas::GlyphsTextureKey> : public DefaultTraits<AccelGfx::GlyphAtlas::GlyphsTextureKey> {
|
||||
static unsigned hash(AccelGfx::GlyphAtlas::GlyphsTextureKey const& key)
|
||||
{
|
||||
return pair_int_hash(ptr_hash(key.font), key.code_point);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
@ -5,10 +5,8 @@
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/QuickSort.h>
|
||||
#include <LibAccelGfx/GL.h>
|
||||
#include <LibAccelGfx/Painter.h>
|
||||
#include <LibGfx/Color.h>
|
||||
#include <LibGfx/ImmutableBitmap.h>
|
||||
#include <LibGfx/Painter.h>
|
||||
|
||||
@ -145,23 +143,12 @@ OwnPtr<Painter> Painter::create()
|
||||
return make<Painter>(context);
|
||||
}
|
||||
|
||||
OwnPtr<Painter> Painter::create_with_glyphs_texture_from_painter(Painter const& painter)
|
||||
{
|
||||
auto& context = Context::the();
|
||||
auto glyphs_texture_painter = make<Painter>(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<Gfx::Font const*, HashTable<u32>> const& unique_glyphs)
|
||||
{
|
||||
HashMap<GlyphsTextureKey, NonnullRefPtr<Gfx::Bitmap>> 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<GlyphsTextureKey> 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<Gfx::DrawGlyphOrEmoji> const& glyph_run, Color const& color)
|
||||
{
|
||||
bind_target_canvas();
|
||||
@ -428,20 +359,22 @@ void Painter::draw_glyph_run(Vector<Gfx::DrawGlyphOrEmoji> const& glyph_run, Col
|
||||
Vector<GLfloat> 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<Gfx::DrawGlyph>()) {
|
||||
auto& glyph = glyph_or_emoji.get<Gfx::DrawGlyph>();
|
||||
auto const& glyph = glyph_or_emoji.get<Gfx::DrawGlyph>();
|
||||
|
||||
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<float>(), m_glyphs_texture_size);
|
||||
auto texture_rect = to_texture_space(maybe_texture_rect.value().to_type<float>(), *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<float>();
|
||||
@ -497,7 +430,7 @@ void Painter::draw_glyph_run(Vector<Gfx::DrawGlyphOrEmoji> 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");
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include <LibAccelGfx/Context.h>
|
||||
#include <LibAccelGfx/Forward.h>
|
||||
#include <LibAccelGfx/GL.h>
|
||||
#include <LibAccelGfx/GlyphAtlas.h>
|
||||
#include <LibAccelGfx/Program.h>
|
||||
#include <LibGfx/AffineTransform.h>
|
||||
#include <LibGfx/Font/Font.h>
|
||||
@ -29,7 +30,6 @@ class Painter {
|
||||
|
||||
public:
|
||||
static OwnPtr<Painter> create();
|
||||
static OwnPtr<Painter> 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<Gfx::Font const*, HashTable<u32>> 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<Gfx::DrawGlyphOrEmoji> 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<GlyphsTextureKey, Gfx::IntRect> m_glyphs_texture_map;
|
||||
Gfx::IntSize m_glyphs_texture_size;
|
||||
GL::Texture m_glyphs_texture;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace AK {
|
||||
|
||||
template<>
|
||||
struct Traits<AccelGfx::Painter::GlyphsTextureKey> : public DefaultTraits<AccelGfx::Painter::GlyphsTextureKey> {
|
||||
static unsigned hash(AccelGfx::Painter::GlyphsTextureKey const& key)
|
||||
{
|
||||
return pair_int_hash(ptr_hash(key.font), key.code_point);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibAccelGfx/GlyphAtlas.h>
|
||||
#include <LibWeb/Painting/PaintingCommandExecutorGPU.h>
|
||||
|
||||
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<StackingContextMask>)
|
||||
{
|
||||
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<float>());
|
||||
@ -313,7 +314,7 @@ bool PaintingCommandExecutorGPU::would_be_fully_clipped_by_painter(Gfx::IntRect)
|
||||
|
||||
void PaintingCommandExecutorGPU::prepare_glyph_texture(HashMap<Gfx::Font const*, HashTable<u32>> const& unique_glyphs)
|
||||
{
|
||||
painter().prepare_glyph_texture(unique_glyphs);
|
||||
AccelGfx::GlyphAtlas::the().update(unique_glyphs);
|
||||
}
|
||||
|
||||
void PaintingCommandExecutorGPU::update_immutable_bitmap_texture_cache(HashMap<u32, Gfx::ImmutableBitmap const*>& immutable_bitmaps)
|
||||
|
Loading…
Reference in New Issue
Block a user