From 5b0a2b9b809eeee059a9bd3bcbedd787a1480926 Mon Sep 17 00:00:00 2001 From: Anthony Nicholls Date: Mon, 14 Jul 2025 12:54:30 +0100 Subject: [PATCH] Font: Cache HarfBuzz fonts and font details --- .../detail/juce_SimpleShapedText.cpp | 22 +- modules/juce_graphics/fonts/juce_Font.cpp | 10 +- modules/juce_graphics/fonts/juce_Typeface.cpp | 253 ++++++++++++------ modules/juce_graphics/fonts/juce_Typeface.h | 23 +- .../juce_DirectWriteTypeface_windows.cpp | 21 +- .../native/juce_Fonts_android.cpp | 52 ++-- .../native/juce_Fonts_freetype.cpp | 45 ++-- .../juce_graphics/native/juce_Fonts_mac.mm | 47 ++-- 8 files changed, 287 insertions(+), 186 deletions(-) diff --git a/modules/juce_graphics/detail/juce_SimpleShapedText.cpp b/modules/juce_graphics/detail/juce_SimpleShapedText.cpp index 72e05b545b..a03aecf2fb 100644 --- a/modules/juce_graphics/detail/juce_SimpleShapedText.cpp +++ b/modules/juce_graphics/detail/juce_SimpleShapedText.cpp @@ -219,7 +219,7 @@ static std::vector lowLevelShape (Span string, { static const auto language = SystemStats::getDisplayLanguage(); - HbBuffer buffer { hb_buffer_create() }; + HbBuffer buffer { hb_buffer_create(), IncrementRef::no }; hb_buffer_clear_contents (buffer.get()); hb_buffer_set_cluster_level (buffer.get(), HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES); @@ -316,20 +316,14 @@ static std::vector lowLevelShape (Span string, const auto xAdvanceBase = HbScale::hbToJuce (positions[visualIndex].x_advance); const auto yAdvanceBase = -HbScale::hbToJuce (positions[visualIndex].y_advance); - // For certain OS, Font and glyph ID combinations harfbuzz will not find extents data and - // hb_font_get_glyph_extents will return false. In such cases Typeface::getGlyphBounds - // will return an empty rectangle. Here we need to distinguish this situation from the one - // where extents information is available and is an empty rectangle, which indicates a - // whitespace. - const auto extentsDataAvailable = std::invoke ([&] - { - hb_glyph_extents_t extents{}; - return hb_font_get_glyph_extents (font.getTypefacePtr()->getNativeDetails().getFont(), - (hb_codepoint_t) glyphId, - &extents); - }); + // For certain OS, Font and glyph ID combinations harfbuzz will not find extents data. + // In such cases Typeface::getGlyphBounds will return an empty rectangle. Here we need + // to distinguish this situation from the one where extents information is available + // and is an empty rectangle, which indicates a whitespace. + const auto* native = font.getTypefacePtr()->getNativeDetails(); + const auto extents = native->getGlyphExtents (glyphId); - const auto whitespace = extentsDataAvailable + const auto whitespace = extents.has_value() && font.getTypefacePtr()->getGlyphBounds (font.getMetricsKind(), (int) glyphId).isEmpty() && xAdvanceBase > 0; diff --git a/modules/juce_graphics/fonts/juce_Font.cpp b/modules/juce_graphics/fonts/juce_Font.cpp index 28f811f3a8..edca7bfeb2 100644 --- a/modules/juce_graphics/fonts/juce_Font.cpp +++ b/modules/juce_graphics/fonts/juce_Font.cpp @@ -220,7 +220,7 @@ public: const ScopedLock lock (mutex); if (auto ptr = getTypefacePtr (f)) - return ptr->getNativeDetails().getFontAtPointSizeAndScale (f.getHeightInPoints(), f.getHorizontalScale()); + return ptr->getNativeDetails()->getFontAtPointSizeAndScale (f.getHeightInPoints(), f.getHorizontalScale()); return {}; } @@ -231,7 +231,7 @@ public: if (auto ptr = getTypefacePtr (f)) { - const auto ascentDescent = ptr->getNativeDetails().getAscentDescent (f.getMetricsKind()); + const auto ascentDescent = ptr->getNativeDetails()->getAscentDescent (f.getMetricsKind()); auto adjusted = ascentDescent; adjusted.ascent = getAscentOverride().value_or (adjusted.ascent); @@ -886,7 +886,7 @@ static bool characterNotRendered (uint32_t c) static bool isFontSuitableForCodepoint (const Font& font, juce_wchar c) { - const auto& hbFont = font.getNativeDetails().font; + const auto hbFont = font.getNativeDetails().font; if (hbFont == nullptr) return false; @@ -994,7 +994,7 @@ Font::Native Font::getNativeDetails() const Typeface::Ptr Font::getDefaultTypefaceForFont (const Font& font) { - const auto resolvedTypeface = [&]() -> Typeface::Ptr + const auto resolvedTypeface = std::invoke ([&]() -> Typeface::Ptr { if (font.getTypefaceName() != getSystemUIFontName()) return {}; @@ -1010,7 +1010,7 @@ Typeface::Ptr Font::getDefaultTypefaceForFont (const Font& font) auto copy = font; copy.setTypefaceName (systemTypeface->getName()); return getDefaultTypefaceForFont (copy); - }(); + }); if (resolvedTypeface != nullptr) return resolvedTypeface; diff --git a/modules/juce_graphics/fonts/juce_Typeface.cpp b/modules/juce_graphics/fonts/juce_Typeface.cpp index ede83f4e36..a620a921f3 100644 --- a/modules/juce_graphics/fonts/juce_Typeface.cpp +++ b/modules/juce_graphics/fonts/juce_Typeface.cpp @@ -35,6 +35,7 @@ namespace juce { +//============================================================================== class HbScale { static constexpr float factor = 1 << 16; @@ -53,6 +54,92 @@ public: } }; +//============================================================================== +template +class HbPtr +{ +public: + HbPtr (HbType* i, IncrementRef incrementRefCount) + : instance (i) + { + if (incrementRefCount == IncrementRef::yes) + incRefIfNotNull(); + } + + HbPtr() = default; + + ~HbPtr() + { + decRefIfNotNull(); + } + + HbPtr (const HbPtr& other) + : HbPtr (other.instance, IncrementRef::yes) + {} + + HbPtr (HbPtr&& other) + { + swap (other); + } + + HbPtr& operator= (const HbPtr& other) + { + HbPtr { other }.swap (*this); + return *this; + } + + HbPtr& operator= (HbPtr&& other) + { + swap (other); + return *this; + } + + HbPtr& operator= (std::nullptr_t) + { + return operator= (HbPtr { nullptr }); + } + + HbType* get() const { return instance; } + + bool operator== (std::nullptr_t) const + { + return instance == nullptr; + } + +private: + void incRefIfNotNull() + { + if (instance != nullptr) + incRef (instance); + } + + void decRefIfNotNull() + { + if (instance != nullptr) + decRef (instance); + } + + void swap (HbPtr& other) noexcept + { + std::swap (instance, other.instance); + } + + HbType* instance = nullptr; +}; + +//============================================================================== +#define JUCE_HB_PTR_TYPE(type) \ + HbPtr + +using HbFont = JUCE_HB_PTR_TYPE (font); +using HbFontFuncs = JUCE_HB_PTR_TYPE (font_funcs); +using HbFace = JUCE_HB_PTR_TYPE (face); +using HbBuffer = JUCE_HB_PTR_TYPE (buffer); +using HbBlob = JUCE_HB_PTR_TYPE (blob); +using HbDrawFuncs = JUCE_HB_PTR_TYPE (draw_funcs); + +#undef JUCE_HB_PTR_TYPE + //============================================================================== #if JUCE_MAC || JUCE_IOS template @@ -130,8 +217,7 @@ static auto getAdvancesFn() */ static void overrideCTFontAdvances (hb_font_t* hb, CTFontRef fontRef) { - using HbFontFuncs = std::unique_ptr>; - const HbFontFuncs funcs { hb_font_funcs_create() }; + HbFontFuncs funcs { hb_font_funcs_create(), IncrementRef::no }; // We pass the CTFontRef as user data to each of these functions. // We don't pass a custom destructor for the user data, as that will be handled by the custom @@ -170,30 +256,31 @@ struct TypefaceAscentDescent } }; -using HbFont = std::unique_ptr>; -using HbFace = std::unique_ptr>; -using HbBuffer = std::unique_ptr>; -using HbBlob = std::unique_ptr>; - struct TypefaceFallbackColourGlyphSupport { virtual ~TypefaceFallbackColourGlyphSupport() = default; virtual std::vector getFallbackColourGlyphLayers (int, const AffineTransform&) const = 0; }; +struct TypefaceNativeOptions +{ + HbFont font; + TypefaceAscentDescent metrics; + TypefaceFallbackColourGlyphSupport* colourGlyphSupport{}; +}; + class Typeface::Native { public: - Native (hb_font_t* fontRef, - TypefaceAscentDescent nonPortableMetricsIn, - const TypefaceFallbackColourGlyphSupport* colourGlyphSupportIn = {}) - : font (fontRef), - nonPortable (nonPortableMetricsIn), - colourGlyphSupport (colourGlyphSupportIn) + explicit Native (TypefaceNativeOptions options) + : font (std::move (options.font)), + nonPortable (options.metrics), + colourGlyphSupport (options.colourGlyphSupport) { } - auto* getFont() const { return font; } + // Returns the backing HarfBuzz font with a size of 1 pt (i.e. 1 pt per em). + hb_font_t* getFont() const { return font.get(); } TypefaceAscentDescent getAscentDescent (TypefaceMetricsKind kind) const { @@ -209,52 +296,68 @@ public: return {}; } + std::optional getGlyphExtents (hb_codepoint_t glyphId) const + { + return glyphExtentsCache.get (glyphId, [this] (auto gid) -> std::optional + { + hb_glyph_extents_t extents{}; + + if (hb_font_get_glyph_extents (getFont(), gid, &extents) == 0) + return {}; + + return extents; + }); + } + HbFont getFontAtPointSizeAndScale (float points, float horizontalScale) const { - HbFont subFont { hb_font_create_sub_font (font) }; + return subFontCache.get ({ points, horizontalScale }, [this] (auto args) + { + const auto [p, h] = args; + HbFont subFont { hb_font_create_sub_font (getFont()), IncrementRef::no }; - hb_font_set_ptem (subFont.get(), points); - hb_font_set_scale (subFont.get(), HbScale::juceToHb (points * horizontalScale), HbScale::juceToHb (points)); + hb_font_set_ptem (subFont.get(), p); + hb_font_set_scale (subFont.get(), HbScale::juceToHb (p * h), HbScale::juceToHb (p)); - #if JUCE_MAC || JUCE_IOS - overrideCTFontAdvances (subFont.get(), hb_coretext_font_get_ct_font (subFont.get())); - #endif + #if JUCE_MAC || JUCE_IOS + overrideCTFontAdvances (subFont.get(), hb_coretext_font_get_ct_font (subFont.get())); + #endif - return subFont; + return subFont; + }); } std::vector getFallbackColourGlyphLayers (int glyph, const AffineTransform& transform) const { - if (colourGlyphSupport != nullptr) - return colourGlyphSupport->getFallbackColourGlyphLayers (glyph, transform); + if (colourGlyphSupport == nullptr) + return {}; - return {}; + return colourGlyphSupport->getFallbackColourGlyphLayers (glyph, transform); } private: - static TypefaceAscentDescent findPortableMetrics (hb_font_t* f, TypefaceAscentDescent fallback) + HbFont font; + TypefaceAscentDescent nonPortable; + TypefaceAscentDescent portable = std::invoke ([&] { hb_font_extents_t extents{}; - if (! hb_font_get_h_extents (f, &extents)) - return fallback; + if (! hb_font_get_h_extents (font.get(), &extents)) + return nonPortable; const auto ascent = std::abs ((float) extents.ascender); const auto descent = std::abs ((float) extents.descender); - const auto upem = (float) hb_face_get_upem (hb_font_get_face (f)); + const auto upem = (float) hb_face_get_upem (hb_font_get_face (font.get())); TypefaceAscentDescent result; result.ascent = ascent / upem; result.descent = descent / upem; return result; - } - - hb_font_t* font = nullptr; - - TypefaceAscentDescent nonPortable; - TypefaceAscentDescent portable = findPortableMetrics (font, nonPortable); - const TypefaceFallbackColourGlyphSupport* colourGlyphSupport = nullptr; + }); + TypefaceFallbackColourGlyphSupport* colourGlyphSupport; + mutable LruCache, HbFont> subFontCache; + mutable LruCache, 512> glyphExtentsCache; }; struct FontStyleHelpers @@ -354,12 +457,13 @@ struct FontStyleHelpers (unsigned int) bytes.size(), HB_MEMORY_MODE_DUPLICATE, nullptr, - nullptr) }; + nullptr), + IncrementRef::no }; const auto count = hb_face_count (blob.get()); if (index < count) - return HbFace { hb_face_create (blob.get(), index) }; + return { hb_face_create (blob.get(), index), IncrementRef::no }; // Attempted to create a font from invalid data. Perhaps the font format was unrecognised. jassertfalse; @@ -367,20 +471,16 @@ struct FontStyleHelpers } }; -//============================================================================== -Typeface::Typeface (const String& faceName, const String& faceStyle) noexcept - : name (faceName), - style (faceStyle) +Typeface::Typeface (const String& nameIn, const String& styleIn) + : name (nameIn), style (styleIn) { } Typeface::~Typeface() = default; -using HbDrawFuncs = std::unique_ptr>; - static HbDrawFuncs getPathDrawFuncs() { - HbDrawFuncs funcs { hb_draw_funcs_create() }; + HbDrawFuncs funcs { hb_draw_funcs_create(), IncrementRef::no }; hb_draw_funcs_set_move_to_func (funcs.get(), [] (auto*, void* data, auto*, float x, float y, auto*) { @@ -422,34 +522,32 @@ static HbDrawFuncs getPathDrawFuncs() void Typeface::getOutlineForGlyph (TypefaceMetricsKind kind, int glyphNumber, Path& path) const { - const auto native = getNativeDetails(); - auto* font = native.getFont(); - const auto metrics = native.getAscentDescent (kind); + auto* font = getNativeDetails()->getFont(); + const auto metrics = getNativeDetails()->getAscentDescent (kind); const auto factor = metrics.getHeightToPointsFactor(); jassert (! std::isinf (factor)); const auto scale = factor / (float) hb_face_get_upem (hb_font_get_face (font)); // getTypefaceGlyph returns glyphs in em space, getOutlineForGlyph returns glyphs in "special JUCE units" space - path = getGlyphPathInGlyphUnits ((hb_codepoint_t) glyphNumber, getNativeDetails().getFont()); + path = getGlyphPathInGlyphUnits ((hb_codepoint_t) glyphNumber, font); path.applyTransform (AffineTransform::scale (scale, -scale)); } Rectangle Typeface::getGlyphBounds (TypefaceMetricsKind kind, int glyphNumber) const { - auto* font = getNativeDetails().getFont(); + const auto extents = getNativeDetails()->getGlyphExtents ((hb_codepoint_t) glyphNumber); - hb_glyph_extents_t extents{}; - if (! hb_font_get_glyph_extents (font, (hb_codepoint_t) glyphNumber, &extents)) + if (! extents.has_value()) return {}; - const auto native = getNativeDetails(); - const auto metrics = native.getAscentDescent (kind); + auto* font = getNativeDetails()->getFont(); + const auto metrics = getNativeDetails()->getAscentDescent (kind); const auto factor = metrics.getHeightToPointsFactor(); jassert (! std::isinf (factor)); const auto scale = factor / (float) hb_face_get_upem (hb_font_get_face (font)); - return Rectangle { (float) extents.width, (float) extents.height } - .withPosition ((float) extents.x_bearing, (float) extents.y_bearing) + return Rectangle { (float) extents->width, (float) extents->height } + .withPosition ((float) extents->x_bearing, (float) extents->y_bearing) .transformedBy (AffineTransform::scale (scale).scaled (1.0f, -1.0f)); } @@ -477,7 +575,7 @@ static Colour makeColour (hb_color_t c) static std::vector getCOLRv0Layers (const Typeface& typeface, int glyphNumber, const AffineTransform& transform) { - auto* font = typeface.getNativeDetails().getFont(); + auto* font = typeface.getNativeDetails()->getFont(); auto* face = hb_font_get_face (font); constexpr auto palette = 0; @@ -519,9 +617,16 @@ static std::vector getBitmapLayer (const Typeface& typeface, int gly if ((typeface.getColourGlyphFormats() & Typeface::colourGlyphFormatBitmap) == 0) return {}; - auto* font = typeface.getNativeDetails().getFont(); + const auto* native = typeface.getNativeDetails(); + const auto extents = native->getGlyphExtents ((hb_codepoint_t) glyphNumber); - const HbBlob blob { hb_ot_color_glyph_reference_png (font, (hb_codepoint_t) glyphNumber) }; + if (! extents.has_value()) + return {}; + + auto* font = native->getFont(); + + HbBlob blob { hb_ot_color_glyph_reference_png (font, (hb_codepoint_t) glyphNumber), + IncrementRef::no }; unsigned int imageDataSize{}; const char* imageData = hb_blob_get_data (blob.get(), &imageDataSize); @@ -530,16 +635,13 @@ static std::vector getBitmapLayer (const Typeface& typeface, int gly if (juceImage.isNull()) return {}; - hb_glyph_extents_t extents{}; - hb_font_get_glyph_extents (font, (hb_codepoint_t) glyphNumber, &extents); - const auto wDenom = std::max (1, juceImage.getWidth()); const auto hDenom = std::max (1, juceImage.getHeight()); - const auto transform = AffineTransform::scale ((float) extents.width / (float) wDenom, - (float) extents.height / (float) hDenom) - .translated ((float) extents.x_bearing, - (float) extents.y_bearing) + const auto transform = AffineTransform::scale ((float) extents->width / (float) wDenom, + (float) extents->height / (float) hDenom) + .translated ((float) extents->x_bearing, + (float) extents->y_bearing) .followedBy (t); return { GlyphLayer { ImageLayer { juceImage, transform } } }; } @@ -548,9 +650,8 @@ std::vector Typeface::getLayersForGlyph (TypefaceMetricsKind kind, int glyphNumber, const AffineTransform& transform) const { - auto native = getNativeDetails(); - auto* font = native.getFont(); - const auto metrics = native.getAscentDescent (kind); + auto* font = getNativeDetails()->getFont(); + const auto metrics = getNativeDetails()->getAscentDescent (kind); const auto factor = metrics.getHeightToPointsFactor(); jassert (! std::isinf (factor)); const auto scale = factor / (float) hb_face_get_upem (hb_font_get_face (font)); @@ -569,7 +670,7 @@ std::vector Typeface::getLayersForGlyph (TypefaceMetricsKind kind, // easily display. In such cases, we can use system facilities to render the glyph into a // bitmap. If the face has colour info that wasn't already handled, try rendering to a bitmap. if (getColourGlyphFormats() != 0) - if (auto layer = native.getFallbackColourGlyphLayers (glyphNumber, combinedTransform); ! layer.empty()) + if (auto layer = getNativeDetails()->getFallbackColourGlyphLayers (glyphNumber, combinedTransform); ! layer.empty()) return layer; // No colour info available for this glyph, so just get a simple monochromatic outline @@ -584,7 +685,7 @@ std::vector Typeface::getLayersForGlyph (TypefaceMetricsKind kind, int Typeface::getColourGlyphFormats() const { - auto* face = hb_font_get_face (getNativeDetails().getFont()); + auto* face = hb_font_get_face (getNativeDetails()->getFont()); return (hb_ot_color_has_png (face) ? colourGlyphFormatBitmap : 0) | (hb_ot_color_has_svg (face) ? colourGlyphFormatSvg : 0) | (hb_ot_color_has_layers (face) ? colourGlyphFormatCOLRv0 : 0) @@ -593,7 +694,7 @@ int Typeface::getColourGlyphFormats() const TypefaceMetrics Typeface::getMetrics (TypefaceMetricsKind kind) const { - return getNativeDetails().getAscentDescent (kind).getTypefaceMetrics(); + return getNativeDetails()->getAscentDescent (kind).getTypefaceMetrics(); } Typeface::Ptr Typeface::createSystemTypefaceFor (const void* fontFileData, size_t fontFileDataSize) @@ -604,7 +705,7 @@ Typeface::Ptr Typeface::createSystemTypefaceFor (const void* fontFileData, size_ //============================================================================== std::optional Typeface::getNominalGlyphForCodepoint (juce_wchar cp) const { - auto* font = getNativeDetails().getFont(); + auto* font = getNativeDetails()->getFont(); if (font == nullptr) return {}; @@ -630,14 +731,14 @@ static float doSimpleShapeWithNoBreaks (const Typeface& typeface, float horizontalScale, Consumer&& consumer) { - HbBuffer buffer { hb_buffer_create() }; + HbBuffer buffer { hb_buffer_create(), IncrementRef::no }; hb_buffer_add_utf8 (buffer.get(), text.toRawUTF8(), -1, 0, -1); hb_buffer_set_cluster_level (buffer.get(), HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS); hb_buffer_guess_segment_properties (buffer.get()); - const auto& native = typeface.getNativeDetails(); + const auto* native = typeface.getNativeDetails(); const auto points = typeface.getMetrics (kind).heightToPoints * height; - const auto sized = native.getFontAtPointSizeAndScale (points, horizontalScale); + const auto sized = native->getFontAtPointSizeAndScale (points, horizontalScale); auto* font = sized.get(); // Disable ligatures, because TextEditor requires a 1:1 codepoint/glyph mapping for caret @@ -737,7 +838,7 @@ std::vector Typeface::getSupportedFeatures() const HB_OT_TAG_GSUB }; - auto* face = hb_font_get_face (getNativeDetails().getFont()); + auto* face = hb_font_get_face (getNativeDetails()->getFont()); for (auto table : tagTables) { diff --git a/modules/juce_graphics/fonts/juce_Typeface.h b/modules/juce_graphics/fonts/juce_Typeface.h index afba6281a4..2c14fc7975 100644 --- a/modules/juce_graphics/fonts/juce_Typeface.h +++ b/modules/juce_graphics/fonts/juce_Typeface.h @@ -130,8 +130,6 @@ struct TypefaceMetrics class JUCE_API Typeface : public ReferenceCountedObject { public: - Typeface (const String& name, const String& newStyle) noexcept; - //============================================================================== /** A handy typedef for a pointer to a typeface. */ using Ptr = ReferenceCountedObjectPtr; @@ -315,17 +313,6 @@ public: */ std::optional getNominalGlyphForCodepoint (juce_wchar) const; - /** @internal */ - class Native; - - /** @internal - - At the moment, this is a way to get at the hb_font_t that backs this typeface. - The typeface's hb_font_t has a size of 1 pt (i.e. 1 pt per em). - This is only for internal use! - */ - virtual Native getNativeDetails() const = 0; - /** Attempts to locate a font with a similar style that is capable of displaying the requested string. @@ -394,6 +381,16 @@ public: */ std::vector getSupportedFeatures() const; + /** @internal */ + class Native; + + /** @internal */ + virtual const Native* getNativeDetails() const = 0; + +protected: + /** @internal */ + Typeface (const String&, const String&); + private: //============================================================================== String name; diff --git a/modules/juce_graphics/native/juce_DirectWriteTypeface_windows.cpp b/modules/juce_graphics/native/juce_DirectWriteTypeface_windows.cpp index 5f3f60b50f..641bc60344 100644 --- a/modules/juce_graphics/native/juce_DirectWriteTypeface_windows.cpp +++ b/modules/juce_graphics/native/juce_DirectWriteTypeface_windows.cpp @@ -130,11 +130,6 @@ public: return fromFont (dwFont, customFontCollection, nullptr, MetricsMechanism::gdiWithDwriteFallback); } - Native getNativeDetails() const override - { - return Native { hbFont.get(), nonPortableMetrics }; - } - Typeface::Ptr createSystemFallback (const String& c, const String& language) const override { auto factory = factories->getDWriteFactory().getInterface(); @@ -171,6 +166,11 @@ public: ComSmartPtr getIDWriteFontFace() const { return dwFontFace; } + const Native* getNativeDetails() const override + { + return native.get(); + } + static Typeface::Ptr findSystemTypeface() { NONCLIENTMETRICS nonClientMetrics{}; @@ -295,8 +295,7 @@ private: collection (std::move (collectionIn)), dwFont (font), dwFontFace (face), - hbFont (std::move (hbFontIn)), - nonPortableMetrics (metrics) + native (std::make_unique (TypefaceNativeOptions { std::move (hbFontIn), metrics })) { if (collection != nullptr) factories->getFonts().addCollection (collection); @@ -341,8 +340,9 @@ private: const auto name = getLocalisedFamilyName (*dwFont); const auto style = getLocalisedStyle (*dwFont); - const HbFace hbFace { hb_directwrite_face_create (dwFace) }; - HbFont font { hb_font_create (hbFace.get()) }; + HbFace hbFace { hb_directwrite_face_create (dwFace), IncrementRef::no }; + HbFont font { hb_font_create (hbFace.get()), IncrementRef::no }; + const auto dwMetrics = getDwriteMetrics (*dwFace); const auto metrics = mm == MetricsMechanism::gdiWithDwriteFallback @@ -411,8 +411,7 @@ private: ComSmartPtr collection; ComSmartPtr dwFont; ComSmartPtr dwFontFace; - HbFont hbFont; - TypefaceAscentDescent nonPortableMetrics; + std::unique_ptr native; }; struct DefaultFontNames diff --git a/modules/juce_graphics/native/juce_Fonts_android.cpp b/modules/juce_graphics/native/juce_Fonts_android.cpp index 6ef01ec3bc..756f490ceb 100644 --- a/modules/juce_graphics/native/juce_Fonts_android.cpp +++ b/modules/juce_graphics/native/juce_Fonts_android.cpp @@ -93,9 +93,9 @@ std::unique_ptr makeAndroidInputStreamWrapper (LocalRef st struct AndroidCachedTypeface { - std::shared_ptr font; + HbFont font; GlobalRef javaFont; - TypefaceAscentDescent nonPortableMetrics; + TypefaceAscentDescent metrics; }; //============================================================================== @@ -235,7 +235,7 @@ public: { return new AndroidTypeface (DoCache::no, result->font, - result->nonPortableMetrics, + result->metrics, font.getTypefaceName(), font.getTypefaceStyle(), result->javaFont); @@ -251,7 +251,7 @@ public: return {}; } - HbFont hbFont { hb_font_create (face.get()) }; + HbFont hbFont { hb_font_create (face.get()), IncrementRef::no }; FontStyleHelpers::initSynthetics (hbFont.get(), font); const auto androidFont = shouldStoreAndroidFont (face.get()) @@ -271,11 +271,6 @@ public: return fromMemory (DoCache::yes, blob, index); } - Native getNativeDetails() const override - { - return Native { hbFont.get(), nonPortableMetrics, this }; - } - Typeface::Ptr createSystemFallback (const String& text, const String& language) const override { if (__builtin_available (android 29, *)) @@ -293,6 +288,11 @@ public: c->remove ({ getName(), getStyle() }); } + const Native* getNativeDetails() const override + { + return native.get(); + } + static Typeface::Ptr findSystemTypeface() { if (__builtin_available (android 29, *)) @@ -358,8 +358,8 @@ private: const AFontMatcherPtr matcher { AFontMatcher_create() }; - const auto weight = hb_style_get_value (hbFont.get(), HB_STYLE_TAG_WEIGHT); - const auto italic = hb_style_get_value (hbFont.get(), HB_STYLE_TAG_ITALIC) != 0.0f; + const auto weight = hb_style_get_value (native->getFont(), HB_STYLE_TAG_WEIGHT); + const auto italic = hb_style_get_value (native->getFont(), HB_STYLE_TAG_ITALIC) != 0.0f; AFontMatcher_setStyle (matcher.get(), (uint16_t) weight, italic); AFontMatcher_setLocales (matcher.get(), language.toRawUTF8()); @@ -367,7 +367,7 @@ private: const auto utf16 = text.toUTF16(); const AFontPtr matched { AFontMatcher_match (matcher.get(), - readFontName (hb_font_get_face (hbFont.get()), + readFontName (hb_font_get_face (native->getFont()), HB_OT_NAME_ID_FONT_FAMILY, nullptr).toRawUTF8(), unalignedPointerCast (utf16.getAddress()), @@ -442,7 +442,7 @@ private: const auto metrics = findNonPortableMetricsForData (blob); return new AndroidTypeface (cache, - HbFont { hb_font_create (face.get()) }, + HbFont (hb_font_create (face.get()), IncrementRef::no), metrics, readFontName (face.get(), HB_OT_NAME_ID_FONT_FAMILY, nullptr), readFontName (face.get(), HB_OT_NAME_ID_FONT_SUBFAMILY, nullptr), @@ -461,20 +461,19 @@ private: } AndroidTypeface (DoCache cache, - std::shared_ptr fontIn, + HbFont fontIn, TypefaceAscentDescent nonPortableMetricsIn, const String& name, const String& style, GlobalRef javaFontIn) : Typeface (name, style), - hbFont (std::move (fontIn)), doCache (cache), - nonPortableMetrics (nonPortableMetricsIn), - javaFont (std::move (javaFontIn)) + javaFont (std::move (javaFontIn)), + native (std::make_unique (TypefaceNativeOptions { std::move (fontIn), nonPortableMetricsIn, this })) { if (doCache == DoCache::yes) if (auto* c = MemoryFontCache::getInstance()) - c->add ({ name, style }, { hbFont, javaFont, nonPortableMetrics }); + c->add ({ name, style }, { fontIn, javaFont, nonPortableMetricsIn }); } static std::tuple getBlobForFont (const Font& font) @@ -664,22 +663,22 @@ private: auto* env = getEnv(); - hb_glyph_extents_t extents{}; + const auto extents = native->getGlyphExtents ((hb_codepoint_t) glyph); - if (! hb_font_get_glyph_extents (hbFont.get(), (hb_codepoint_t) glyph, &extents)) + if (! extents.has_value()) { // Trying to retrieve an image for a glyph that's not present in the font? jassertfalse; return {}; } - const auto upem = (jint) hb_face_get_upem (hb_font_get_face (hbFont.get())); + const auto upem = (jint) hb_face_get_upem (hb_font_get_face (native->getFont())); constexpr jint referenceSize = 128; - const jint pixelW = (referenceSize * abs (extents.width)) / upem; - const jint pixelH = (referenceSize * abs (extents.height)) / upem; - const jint pixelBearingX = (referenceSize * extents.x_bearing) / upem; - const jint pixelBearingY = (referenceSize * extents.y_bearing) / upem; + const jint pixelW = (referenceSize * abs (extents->width)) / upem; + const jint pixelH = (referenceSize * abs (extents->height)) / upem; + const jint pixelBearingX = (referenceSize * extents->x_bearing) / upem; + const jint pixelBearingY = (referenceSize * extents->y_bearing) / upem; const jint pixelPadding = 2; @@ -760,10 +759,9 @@ private: .followedBy (transform) } } }; } - std::shared_ptr hbFont; DoCache doCache; - TypefaceAscentDescent nonPortableMetrics; GlobalRef javaFont; + std::unique_ptr native; }; //============================================================================== diff --git a/modules/juce_graphics/native/juce_Fonts_freetype.cpp b/modules/juce_graphics/native/juce_Fonts_freetype.cpp index ffa4d8d014..21e36d14cf 100644 --- a/modules/juce_graphics/native/juce_Fonts_freetype.cpp +++ b/modules/juce_graphics/native/juce_Fonts_freetype.cpp @@ -394,10 +394,8 @@ public: if (face == nullptr) return {}; - auto* hbFace = hb_ft_face_create_referenced (face->face); - const ScopeGuard scope { [&] { hb_face_destroy (hbFace); } }; - - HbFont hb { hb_font_create (hbFace) }; + HbFace hbFace { hb_ft_face_create_referenced (face->face), IncrementRef::no }; + HbFont hb { hb_font_create (hbFace.get()), IncrementRef::no }; if (hb == nullptr) return {}; @@ -413,10 +411,8 @@ public: if (face == nullptr) return {}; - auto* hbFace = hb_ft_face_create_referenced (face->face); - const ScopeGuard scope { [&] { hb_face_destroy (hbFace); } }; - - HbFont hb { hb_font_create (hbFace) }; + HbFace hbFace { hb_ft_face_create_referenced (face->face), IncrementRef::no }; + HbFont hb { hb_font_create (hbFace.get()), IncrementRef::no }; if (hb == nullptr) return {}; @@ -424,11 +420,6 @@ public: return new FreeTypeTypeface (DoCache::yes, face, std::move (hb), face->face->family_name, face->face->style_name); } - Native getNativeDetails() const override - { - return Native { hb.get(), nonPortableMetrics }; - } - Typeface::Ptr createSystemFallback ([[maybe_unused]] const String& text, [[maybe_unused]] const String& language) const override { @@ -478,11 +469,18 @@ public: ~FreeTypeTypeface() override { + native.reset(); + if (doCache == DoCache::yes) if (auto* list = FTTypefaceList::getInstanceWithoutCreating()) list->removeMemoryFace (ftFace); } + const Native* getNativeDetails() const override + { + return native.get(); + } + static Typeface::Ptr findSystemTypeface() { #if JUCE_USE_FONTCONFIG @@ -530,8 +528,8 @@ private: if (face == nullptr) return {}; - const HbFace hbFace { hb_ft_face_create_referenced (face->face) }; - HbFont cachedFont { hb_font_create (hbFace.get()) }; + HbFace hbFace { hb_ft_face_create_referenced (face->face), IncrementRef::no }; + HbFont cachedFont { hb_font_create (hbFace.get()), IncrementRef::no }; if (cachedFont == nullptr) return {}; @@ -541,6 +539,15 @@ private: } #endif + static TypefaceAscentDescent getNativeMetrics (FTFaceWrapper::Ptr ftFace) + { + const auto upem = (float) ftFace->face->units_per_EM; + const auto ascent = (float) std::abs (ftFace->face->ascender) / upem; + const auto descent = (float) std::abs (ftFace->face->descender) / upem; + + return { ascent, descent }; + } + FreeTypeTypeface (DoCache cache, FTFaceWrapper::Ptr ftFaceIn, HbFont hbIn, @@ -548,8 +555,8 @@ private: const String& styleIn) : Typeface (nameIn, styleIn), ftFace (ftFaceIn), - hb (std::move (hbIn)), - doCache (cache) + doCache (cache), + native (std::make_unique (TypefaceNativeOptions { std::move (hbIn), getNativeMetrics (ftFaceIn) })) { if (doCache == DoCache::yes) if (auto* list = FTTypefaceList::getInstance()) @@ -557,10 +564,8 @@ private: } FTFaceWrapper::Ptr ftFace; - HbFont hb; DoCache doCache; - TypefaceAscentDescent nonPortableMetrics { (float) std::abs (ftFace->face->ascender) / (float) ftFace->face->units_per_EM, - (float) std::abs (ftFace->face->descender) / (float) ftFace->face->units_per_EM }; + std::unique_ptr native; JUCE_DECLARE_NON_COPYABLE (FreeTypeTypeface) }; diff --git a/modules/juce_graphics/native/juce_Fonts_mac.mm b/modules/juce_graphics/native/juce_Fonts_mac.mm index 83cdda94d7..3fbe6b3d4b 100644 --- a/modules/juce_graphics/native/juce_Fonts_mac.mm +++ b/modules/juce_graphics/native/juce_Fonts_mac.mm @@ -174,14 +174,17 @@ public: if (ctFont == nullptr) return {}; - HbFont result { hb_coretext_font_create (ctFont.get()) }; + HbFont result { hb_coretext_font_create (ctFont.get()), IncrementRef::no }; if (result == nullptr) return {}; FontStyleHelpers::initSynthetics (result.get(), font); - return new CoreTextTypeface (std::move (ctFont), std::move (result), font.getTypefaceName(), font.getTypefaceStyle()); + return new CoreTextTypeface (std::move (ctFont), + std::move (result), + font.getTypefaceName(), + font.getTypefaceStyle()); } static Typeface::Ptr from (Span data) @@ -220,7 +223,7 @@ public: if (ctFont == nullptr) return {}; - HbFont result { hb_coretext_font_create (ctFont.get()) }; + HbFont result { hb_coretext_font_create (ctFont.get()), IncrementRef::no }; if (result == nullptr) return {}; @@ -235,11 +238,6 @@ public: std::move (copy)); } - Native getNativeDetails() const override - { - return Native { hb.get(), nonPortableMetrics }; - } - Typeface::Ptr createSystemFallback (const String& c, const String& language) const override { const CFUniquePtr cfText { c.toCFString() }; @@ -261,7 +259,7 @@ public: const CFUniquePtr newStyle { (CFStringRef) CTFontDescriptorCopyAttribute (descriptor.get(), kCTFontStyleNameAttribute) }; - HbFont result { hb_coretext_font_create (newFont.get()) }; + HbFont result { hb_coretext_font_create (newFont.get()), IncrementRef::no }; if (result == nullptr) return {}; @@ -293,6 +291,11 @@ public: return ctFont.get(); } + const Native* getNativeDetails() const override + { + return native.get(); + } + static Typeface::Ptr findSystemTypeface() { CFUniquePtr defaultCtFont (CTFontCreateUIFontForLanguage (kCTFontUIFontSystem, 0.0, nullptr)); @@ -301,7 +304,7 @@ public: const CFUniquePtr newStyle { (CFStringRef) CTFontDescriptorCopyAttribute (descriptor.get(), kCTFontStyleNameAttribute) }; - HbFont result { hb_coretext_font_create (defaultCtFont.get()) }; + HbFont result { hb_coretext_font_create (defaultCtFont.get()), IncrementRef::no }; if (result == nullptr) return {}; @@ -314,6 +317,17 @@ public: } private: + static TypefaceAscentDescent getNativeMetrics (CTFontRef ctFont) + { + const CFUniquePtr cgFont { CTFontCopyGraphicsFont (ctFont, nullptr) }; + + const auto upem = (float) CGFontGetUnitsPerEm (cgFont.get()); + const auto ascent = std::abs ((float) CGFontGetAscent (cgFont.get()) / upem); + const auto descent = std::abs ((float) CGFontGetDescent (cgFont.get()) / upem); + + return { ascent, descent }; + } + CoreTextTypeface (CFUniquePtr nativeFont, HbFont fontIn, const String& name, @@ -321,8 +335,8 @@ private: MemoryBlock data = {}) : Typeface (name, style), ctFont (std::move (nativeFont)), - hb (std::move (fontIn)), - storage (std::move (data)) + storage (std::move (data)), + native (std::make_unique (TypefaceNativeOptions { std::move (fontIn), getNativeMetrics (ctFont.get()) })) { if (! storage.isEmpty()) getRegistered().add (ctFont.get()); @@ -342,15 +356,8 @@ private: // We store this, rather than calling hb_coretext_font_get_ct_font, because harfbuzz may // override the font cascade list in the returned font. CFUniquePtr ctFont; - HbFont hb; MemoryBlock storage; - TypefaceAscentDescent nonPortableMetrics = [&] - { - const CFUniquePtr cgFont { CTFontCopyGraphicsFont (ctFont.get(), nullptr) }; - const auto upem = (float) CGFontGetUnitsPerEm (cgFont.get()); - return TypefaceAscentDescent { (float) std::abs (CGFontGetAscent (cgFont.get()) / upem), - (float) std::abs (CGFontGetDescent (cgFont.get()) / upem) }; - }(); + std::unique_ptr native; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CoreTextTypeface) };