1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-09 23:34:20 +00:00

Font: Cache HarfBuzz fonts and font details

This commit is contained in:
Anthony Nicholls 2025-07-14 12:54:30 +01:00 committed by Anthony Nicholls
parent c0f164ee28
commit 5b0a2b9b80
8 changed files with 287 additions and 186 deletions

View file

@ -219,7 +219,7 @@ static std::vector<ShapedGlyph> lowLevelShape (Span<const juce_wchar> 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<ShapedGlyph> lowLevelShape (Span<const juce_wchar> 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;

View file

@ -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;

View file

@ -35,6 +35,7 @@
namespace juce
{
//==============================================================================
class HbScale
{
static constexpr float factor = 1 << 16;
@ -53,6 +54,92 @@ public:
}
};
//==============================================================================
template <typename HbType, HbType* (*incRef) (HbType*), void (*decRef) (HbType*)>
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<hb_ ## type ## _t, hb_ ## type ## _reference, hb_ ## type ## _destroy>
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 <CTFontOrientation orientation>
@ -130,8 +217,7 @@ static auto getAdvancesFn()
*/
static void overrideCTFontAdvances (hb_font_t* hb, CTFontRef fontRef)
{
using HbFontFuncs = std::unique_ptr<hb_font_funcs_t, FunctionPointerDestructor<hb_font_funcs_destroy>>;
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<hb_font_t, FunctionPointerDestructor<hb_font_destroy>>;
using HbFace = std::unique_ptr<hb_face_t, FunctionPointerDestructor<hb_face_destroy>>;
using HbBuffer = std::unique_ptr<hb_buffer_t, FunctionPointerDestructor<hb_buffer_destroy>>;
using HbBlob = std::unique_ptr<hb_blob_t, FunctionPointerDestructor<hb_blob_destroy>>;
struct TypefaceFallbackColourGlyphSupport
{
virtual ~TypefaceFallbackColourGlyphSupport() = default;
virtual std::vector<GlyphLayer> 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<hb_glyph_extents_t> getGlyphExtents (hb_codepoint_t glyphId) const
{
return glyphExtentsCache.get (glyphId, [this] (auto gid) -> std::optional<hb_glyph_extents_t>
{
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<GlyphLayer> 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<std::tuple<float, float>, HbFont> subFontCache;
mutable LruCache<hb_codepoint_t, std::optional<hb_glyph_extents_t>, 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<hb_draw_funcs_t, FunctionPointerDestructor<hb_draw_funcs_destroy>>;
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<float> 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<GlyphLayer> 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<GlyphLayer> 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<GlyphLayer> 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<GlyphLayer> 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<GlyphLayer> 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<GlyphLayer> 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<uint32_t> 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<FontFeatureTag> 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)
{

View file

@ -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<Typeface>;
@ -315,17 +313,6 @@ public:
*/
std::optional<uint32_t> 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<FontFeatureTag> getSupportedFeatures() const;
/** @internal */
class Native;
/** @internal */
virtual const Native* getNativeDetails() const = 0;
protected:
/** @internal */
Typeface (const String&, const String&);
private:
//==============================================================================
String name;

View file

@ -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<IDWriteFactory2>();
@ -171,6 +166,11 @@ public:
ComSmartPtr<IDWriteFontFace> 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<Native> (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<IDWriteFontCollection> collection;
ComSmartPtr<IDWriteFont> dwFont;
ComSmartPtr<IDWriteFontFace> dwFontFace;
HbFont hbFont;
TypefaceAscentDescent nonPortableMetrics;
std::unique_ptr<Native> native;
};
struct DefaultFontNames

View file

@ -93,9 +93,9 @@ std::unique_ptr<InputStream> makeAndroidInputStreamWrapper (LocalRef<jobject> st
struct AndroidCachedTypeface
{
std::shared_ptr<hb_font_t> 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<const uint16_t*> (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<hb_font_t> 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<Native> (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<MemoryBlock, TypefaceAscentDescent> 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<hb_font_t> hbFont;
DoCache doCache;
TypefaceAscentDescent nonPortableMetrics;
GlobalRef javaFont;
std::unique_ptr<Native> native;
};
//==============================================================================

View file

@ -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<Native> (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> native;
JUCE_DECLARE_NON_COPYABLE (FreeTypeTypeface)
};

View file

@ -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<const std::byte> 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<CFStringRef> cfText { c.toCFString() };
@ -261,7 +259,7 @@ public:
const CFUniquePtr<CFStringRef> 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<CTFontRef> defaultCtFont (CTFontCreateUIFontForLanguage (kCTFontUIFontSystem, 0.0, nullptr));
@ -301,7 +304,7 @@ public:
const CFUniquePtr<CFStringRef> 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<CGFontRef> 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<CTFontRef> 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<Native> (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<CTFontRef> ctFont;
HbFont hb;
MemoryBlock storage;
TypefaceAscentDescent nonPortableMetrics = [&]
{
const CFUniquePtr<CGFontRef> 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> native;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CoreTextTypeface)
};