diff --git a/Meta/CMake/skia.cmake b/Meta/CMake/skia.cmake new file mode 100644 index 00000000000..0c7763aa7f2 --- /dev/null +++ b/Meta/CMake/skia.cmake @@ -0,0 +1,22 @@ +find_package(unofficial-skia CONFIG) +if(unofficial-skia_FOUND) + set(SKIA_LIBRARIES unofficial::skia::skia) +else() + find_package(PkgConfig) + + # Get skia version from vcpkg.json + file(READ ${LADYBIRD_SOURCE_DIR}/vcpkg.json VCPKG_DOT_JSON) + string(JSON VCPKG_OVERRIDES_LENGTH LENGTH ${VCPKG_DOT_JSON} overrides) + MATH(EXPR VCPKG_OVERRIDES_END_RANGE "${VCPKG_OVERRIDES_LENGTH}-1") + foreach(IDX RANGE ${VCPKG_OVERRIDES_END_RANGE}) + string(JSON VCPKG_OVERRIDE_NAME GET ${VCPKG_DOT_JSON} overrides ${IDX} name) + if(VCPKG_OVERRIDE_NAME STREQUAL "skia") + string(JSON SKIA_REQUIRED_VERSION GET ${VCPKG_DOT_JSON} overrides ${IDX} version) + string(REGEX MATCH "[0-9]+" SKIA_REQUIRED_VERSION ${SKIA_REQUIRED_VERSION}) + endif() + endforeach() + + pkg_check_modules(SKIA skia=${SKIA_REQUIRED_VERSION} REQUIRED) + target_include_directories(LibWeb PRIVATE ${SKIA_INCLUDE_DIRS}) + target_link_directories(LibWeb PRIVATE ${SKIA_LIBRARY_DIRS}) +endif() diff --git a/Tests/LibWeb/Screenshot/images/alt-frame.png b/Tests/LibWeb/Screenshot/images/alt-frame.png index 7d9d934c61f..017c7807b61 100644 Binary files a/Tests/LibWeb/Screenshot/images/alt-frame.png and b/Tests/LibWeb/Screenshot/images/alt-frame.png differ diff --git a/Tests/LibWeb/Screenshot/images/css-background-clip-text.png b/Tests/LibWeb/Screenshot/images/css-background-clip-text.png index a86a410fc0f..2afcdf0c1a6 100644 Binary files a/Tests/LibWeb/Screenshot/images/css-background-clip-text.png and b/Tests/LibWeb/Screenshot/images/css-background-clip-text.png differ diff --git a/Tests/LibWeb/Screenshot/images/css-color-functions-ref.png b/Tests/LibWeb/Screenshot/images/css-color-functions-ref.png index 6f839f452ff..f5e9846098d 100644 Binary files a/Tests/LibWeb/Screenshot/images/css-color-functions-ref.png and b/Tests/LibWeb/Screenshot/images/css-color-functions-ref.png differ diff --git a/Tests/LibWeb/Screenshot/images/css-transform-box-ref.png b/Tests/LibWeb/Screenshot/images/css-transform-box-ref.png index 7f27e188aa9..4fc7c4e4ec2 100644 Binary files a/Tests/LibWeb/Screenshot/images/css-transform-box-ref.png and b/Tests/LibWeb/Screenshot/images/css-transform-box-ref.png differ diff --git a/Tests/LibWeb/Screenshot/images/inline-node-ref.png b/Tests/LibWeb/Screenshot/images/inline-node-ref.png index b1245096a9f..b764e0bacc2 100644 Binary files a/Tests/LibWeb/Screenshot/images/inline-node-ref.png and b/Tests/LibWeb/Screenshot/images/inline-node-ref.png differ diff --git a/Tests/LibWeb/Screenshot/images/input-placeholder-ref.png b/Tests/LibWeb/Screenshot/images/input-placeholder-ref.png index 61f2207a31c..f545337764a 100644 Binary files a/Tests/LibWeb/Screenshot/images/input-placeholder-ref.png and b/Tests/LibWeb/Screenshot/images/input-placeholder-ref.png differ diff --git a/Tests/LibWeb/Screenshot/images/object-fit-position.png b/Tests/LibWeb/Screenshot/images/object-fit-position.png index 5e17de0e33b..75abdd9ad26 100644 Binary files a/Tests/LibWeb/Screenshot/images/object-fit-position.png and b/Tests/LibWeb/Screenshot/images/object-fit-position.png differ diff --git a/Tests/LibWeb/Screenshot/images/text-decorations.png b/Tests/LibWeb/Screenshot/images/text-decorations.png index e1f96c07b28..e74dd4a4a96 100644 Binary files a/Tests/LibWeb/Screenshot/images/text-decorations.png and b/Tests/LibWeb/Screenshot/images/text-decorations.png differ diff --git a/Tests/LibWeb/Screenshot/images/text-shadow-ref.png b/Tests/LibWeb/Screenshot/images/text-shadow-ref.png index 153ee86c8e5..6b2b33ee6d8 100644 Binary files a/Tests/LibWeb/Screenshot/images/text-shadow-ref.png and b/Tests/LibWeb/Screenshot/images/text-shadow-ref.png differ diff --git a/Userland/Libraries/LibGfx/CMakeLists.txt b/Userland/Libraries/LibGfx/CMakeLists.txt index 94018facf22..773b5790b00 100644 --- a/Userland/Libraries/LibGfx/CMakeLists.txt +++ b/Userland/Libraries/LibGfx/CMakeLists.txt @@ -1,3 +1,5 @@ +include(skia) + set(SOURCES AffineTransform.cpp AntiAliasingPainter.cpp @@ -17,7 +19,9 @@ set(SOURCES Font/OpenType/Tables.cpp Font/OpenType/Typeface.cpp Font/ScaledFont.cpp + Font/ScaledFontSkia.cpp Font/Typeface.cpp + Font/TypefaceSkia.cpp Font/WOFF/Loader.cpp Font/WOFF2/Loader.cpp GradientPainting.cpp @@ -65,7 +69,8 @@ set(SOURCES ) serenity_lib(LibGfx gfx) -target_link_libraries(LibGfx PRIVATE LibCompress LibCore LibCrypto LibFileSystem LibRIFF LibTextCodec LibIPC LibUnicode LibURL) + +target_link_libraries(LibGfx PRIVATE LibCompress LibCore LibCrypto LibFileSystem LibRIFF LibTextCodec LibIPC LibUnicode LibURL ${SKIA_LIBRARIES}) set(generated_sources TIFFMetadata.h TIFFTagHandler.cpp) list(TRANSFORM generated_sources PREPEND "ImageFormats/") diff --git a/Userland/Libraries/LibGfx/Font/Font.h b/Userland/Libraries/LibGfx/Font/Font.h index 80969e9eb76..6eb6a211762 100644 --- a/Userland/Libraries/LibGfx/Font/Font.h +++ b/Userland/Libraries/LibGfx/Font/Font.h @@ -98,6 +98,8 @@ enum FontWidth { UltraExpanded = 9 }; +class Typeface; + class Font : public RefCounted { public: virtual ~Font() {}; @@ -143,6 +145,8 @@ public: virtual bool has_color_bitmaps() const = 0; + virtual Typeface const& typeface() const = 0; + private: mutable RefPtr m_bold_variant; }; diff --git a/Userland/Libraries/LibGfx/Font/OpenType/Typeface.cpp b/Userland/Libraries/LibGfx/Font/OpenType/Typeface.cpp index 135a4e6c8ea..d8ca6e16b13 100644 --- a/Userland/Libraries/LibGfx/Font/OpenType/Typeface.cpp +++ b/Userland/Libraries/LibGfx/Font/OpenType/Typeface.cpp @@ -352,7 +352,9 @@ ErrorOr> Typeface::try_load_from_offset(ReadonlyBytes bu move(prep), move(cblc), move(cbdt), - move(gpos))); + move(gpos), + buffer.slice(offset), + options.index)); } Gfx::ScaledFontMetrics Typeface::metrics([[maybe_unused]] float x_scale, float y_scale) const diff --git a/Userland/Libraries/LibGfx/Font/OpenType/Typeface.h b/Userland/Libraries/LibGfx/Font/OpenType/Typeface.h index 83b55200958..0fcdc591c9b 100644 --- a/Userland/Libraries/LibGfx/Font/OpenType/Typeface.h +++ b/Userland/Libraries/LibGfx/Font/OpenType/Typeface.h @@ -88,6 +88,10 @@ public: static constexpr Tag HeaderTag_CFFOutlines = Tag { "OTTO" }; static constexpr Tag HeaderTag_FontCollection = Tag { "ttcf" }; +protected: + virtual ReadonlyBytes buffer() const override { return m_buffer; } + virtual unsigned ttc_index() const override { return m_ttc_index; } + private: struct AscenderAndDescender { i16 ascender; @@ -126,8 +130,12 @@ private: Optional prep, Optional cblc, Optional cbdt, - Optional gpos) - : m_head(move(head)) + Optional gpos, + ReadonlyBytes buffer, + unsigned ttc_index) + : m_buffer(buffer) + , m_ttc_index(ttc_index) + , m_head(move(head)) , m_name(move(name)) , m_hhea(move(hhea)) , m_maxp(move(maxp)) @@ -146,6 +154,8 @@ private: } OwnPtr m_font_data; + ReadonlyBytes m_buffer; + unsigned m_ttc_index { 0 }; // These are stateful wrappers around non-owning slices Head m_head; diff --git a/Userland/Libraries/LibGfx/Font/ScaledFont.h b/Userland/Libraries/LibGfx/Font/ScaledFont.h index 69f2873ed8d..0ef54696557 100644 --- a/Userland/Libraries/LibGfx/Font/ScaledFont.h +++ b/Userland/Libraries/LibGfx/Font/ScaledFont.h @@ -12,6 +12,8 @@ #include #include +class SkFont; + namespace Gfx { struct GlyphIndexWithSubpixelOffset { @@ -57,6 +59,10 @@ public: virtual bool has_color_bitmaps() const override { return m_typeface->has_color_bitmaps(); } + virtual Typeface const& typeface() const override { return m_typeface; } + + SkFont skia_font(float scale) const; + private: NonnullRefPtr m_typeface; float m_x_scale { 0.0f }; diff --git a/Userland/Libraries/LibGfx/Font/ScaledFontSkia.cpp b/Userland/Libraries/LibGfx/Font/ScaledFontSkia.cpp new file mode 100644 index 00000000000..69c8bdccc7c --- /dev/null +++ b/Userland/Libraries/LibGfx/Font/ScaledFontSkia.cpp @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2024, Aliaksandr Kalenik + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#define AK_DONT_REPLACE_STD + +#include + +#include + +namespace Gfx { + +SkFont ScaledFont::skia_font(float scale) const +{ + auto const& typeface = this->typeface().skia_typeface(); + return SkFont { sk_ref_sp(typeface.ptr()), pixel_size() * scale }; +} + +} diff --git a/Userland/Libraries/LibGfx/Font/Typeface.cpp b/Userland/Libraries/LibGfx/Font/Typeface.cpp index 0660248862a..da6852dab27 100644 --- a/Userland/Libraries/LibGfx/Font/Typeface.cpp +++ b/Userland/Libraries/LibGfx/Font/Typeface.cpp @@ -4,6 +4,10 @@ * SPDX-License-Identifier: BSD-2-Clause */ +#define AK_DONT_REPLACE_STD + +#include + #include #include diff --git a/Userland/Libraries/LibGfx/Font/Typeface.h b/Userland/Libraries/LibGfx/Font/Typeface.h index 3854d3ae24d..e7782d31d85 100644 --- a/Userland/Libraries/LibGfx/Font/Typeface.h +++ b/Userland/Libraries/LibGfx/Font/Typeface.h @@ -17,6 +17,8 @@ #define POINTS_PER_INCH 72.0f #define DEFAULT_DPI 96 +class SkTypeface; + namespace Gfx { class ScaledFont; @@ -63,11 +65,17 @@ public: [[nodiscard]] NonnullRefPtr scaled_font(float point_size) const; + RefPtr const& skia_typeface() const; + protected: Typeface(); + virtual ReadonlyBytes buffer() const = 0; + virtual unsigned ttc_index() const = 0; + private: mutable HashMap> m_scaled_fonts; + mutable RefPtr m_skia_typeface; }; } diff --git a/Userland/Libraries/LibGfx/Font/TypefaceSkia.cpp b/Userland/Libraries/LibGfx/Font/TypefaceSkia.cpp new file mode 100644 index 00000000000..f78d25bef2b --- /dev/null +++ b/Userland/Libraries/LibGfx/Font/TypefaceSkia.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2024, Aliaksandr Kalenik + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#define AK_DONT_REPLACE_STD + +#include + +#include +#include +#include + +#ifdef AK_OS_MACOS +# include +#else +# include +#endif + +namespace Gfx { + +static sk_sp s_font_manager; + +RefPtr const& Typeface::skia_typeface() const +{ + if (!s_font_manager) { +#ifdef AK_OS_MACOS + s_font_manager = SkFontMgr_New_CoreText(nullptr); +#else + s_font_manager = SkFontMgr_New_FontConfig(nullptr); +#endif + } + + if (!m_skia_typeface) { + auto data = SkData::MakeWithoutCopy(buffer().data(), buffer().size()); + auto skia_typeface = s_font_manager->makeFromData(data, ttc_index()); + if (!skia_typeface) + VERIFY_NOT_REACHED(); + m_skia_typeface = *skia_typeface; + } + return m_skia_typeface; +} + +} diff --git a/Userland/Libraries/LibWeb/CMakeLists.txt b/Userland/Libraries/LibWeb/CMakeLists.txt index dbf828acf73..049dfb4ac4a 100644 --- a/Userland/Libraries/LibWeb/CMakeLists.txt +++ b/Userland/Libraries/LibWeb/CMakeLists.txt @@ -1,5 +1,6 @@ include(libweb_generators) include(vulkan) +include(skia) set(SOURCES Animations/Animatable.cpp @@ -767,29 +768,6 @@ set(GENERATED_SOURCES serenity_lib(LibWeb web) -find_package(unofficial-skia CONFIG) -if(unofficial-skia_FOUND) - set(SKIA_LIBRARIES unofficial::skia::skia) -else() - find_package(PkgConfig) - - # Get skia version from vcpkg.json - file(READ ${LADYBIRD_SOURCE_DIR}/vcpkg.json VCPKG_DOT_JSON) - string(JSON VCPKG_OVERRIDES_LENGTH LENGTH ${VCPKG_DOT_JSON} overrides) - MATH(EXPR VCPKG_OVERRIDES_END_RANGE "${VCPKG_OVERRIDES_LENGTH}-1") - foreach(IDX RANGE ${VCPKG_OVERRIDES_END_RANGE}) - string(JSON VCPKG_OVERRIDE_NAME GET ${VCPKG_DOT_JSON} overrides ${IDX} name) - if(VCPKG_OVERRIDE_NAME STREQUAL "skia") - string(JSON SKIA_REQUIRED_VERSION GET ${VCPKG_DOT_JSON} overrides ${IDX} version) - string(REGEX MATCH "[0-9]+" SKIA_REQUIRED_VERSION ${SKIA_REQUIRED_VERSION}) - endif() - endforeach() - - pkg_check_modules(SKIA skia=${SKIA_REQUIRED_VERSION} REQUIRED) - target_include_directories(LibWeb PRIVATE ${SKIA_INCLUDE_DIRS}) - target_link_directories(LibWeb PRIVATE ${SKIA_LIBRARY_DIRS}) -endif() - target_link_libraries(LibWeb PRIVATE LibCore LibCrypto LibJS LibHTTP LibGfx LibIPC LibRegex LibSyntax LibTextCodec LibUnicode LibAudio LibMedia LibWasm LibXML LibIDL LibURL LibTLS ${SKIA_LIBRARIES}) generate_js_bindings(LibWeb) diff --git a/Userland/Libraries/LibWeb/Painting/DisplayListPlayerSkia.cpp b/Userland/Libraries/LibWeb/Painting/DisplayListPlayerSkia.cpp index a3a4bc7a164..c6dd275c467 100644 --- a/Userland/Libraries/LibWeb/Painting/DisplayListPlayerSkia.cpp +++ b/Userland/Libraries/LibWeb/Painting/DisplayListPlayerSkia.cpp @@ -10,6 +10,8 @@ #include #include #include +#include +#include #include #include #include @@ -23,6 +25,7 @@ #include #include +#include #include #include #include @@ -387,46 +390,36 @@ DisplayListPlayerSkia::SkiaSurface& DisplayListPlayerSkia::surface() const return static_cast(*m_surface); } -static HashMap> s_glyph_cache; - void DisplayListPlayerSkia::draw_glyph_run(DrawGlyphRun const& command) { - auto& canvas = surface().canvas(); - SkPaint paint; - paint.setColorFilter(SkColorFilters::Blend(to_skia_color(command.color), SkBlendMode::kSrcIn)); - auto const& glyphs = command.glyph_run->glyphs(); - auto const& font = command.glyph_run->font(); - auto scaled_font = font.with_size(font.point_size() * static_cast(command.scale)); - for (auto const& glyph_or_emoji : glyphs) { + auto const& gfx_font = static_cast(command.glyph_run->font()); + auto const& gfx_typeface = gfx_font.typeface(); + auto sk_font = gfx_font.skia_font(command.scale); + + auto glyph_count = command.glyph_run->glyphs().size(); + Vector glyphs; + glyphs.ensure_capacity(glyph_count); + Vector positions; + positions.ensure_capacity(glyph_count); + auto font_ascent = gfx_font.pixel_metrics().ascent; + for (auto const& glyph_or_emoji : command.glyph_run->glyphs()) { auto transformed_glyph = glyph_or_emoji; transformed_glyph.visit([&](auto& glyph) { - glyph.position = glyph.position.scaled(command.scale).translated(command.translation); + glyph.position.set_y(glyph.position.y() + font_ascent); + glyph.position = glyph.position.scaled(command.scale); }); if (transformed_glyph.has()) { auto& glyph = transformed_glyph.get(); auto const& point = glyph.position; auto const& code_point = glyph.code_point; - auto top_left = point + Gfx::FloatPoint(scaled_font->glyph_left_bearing(code_point), 0); - auto glyph_position = Gfx::GlyphRasterPosition::get_nearest_fit_for(top_left); - auto maybe_font_glyph = scaled_font->glyph(code_point, glyph_position.subpixel_offset); - if (!maybe_font_glyph.has_value()) - continue; - if (!maybe_font_glyph->is_color_bitmap()) { - auto const& blit_position = glyph_position.blit_position; - sk_sp image; - if (auto maybe_image = s_glyph_cache.get(maybe_font_glyph->bitmap()); maybe_image.has_value()) { - image = maybe_image.value(); - } else { - auto sk_bitmap = to_skia_bitmap(*maybe_font_glyph->bitmap()); - image = SkImages::RasterFromBitmap(sk_bitmap); - s_glyph_cache.set(maybe_font_glyph->bitmap(), image); - } - canvas.drawImage(image, blit_position.x(), blit_position.y(), SkSamplingOptions(), &paint); - } else { - TODO(); - } + glyphs.append(gfx_typeface.glyph_id_for_code_point(code_point)); + positions.append(to_skia_point(point)); } } + + SkPaint paint; + paint.setColor(to_skia_color(command.color)); + surface().canvas().drawGlyphs(glyphs.size(), glyphs.data(), positions.data(), to_skia_point(command.translation), sk_font, paint); } void DisplayListPlayerSkia::fill_rect(FillRect const& command)