From 7d8bb168d25e1db90015eba2ba31fcac6cf75bbf Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Tue, 25 Mar 2025 13:10:50 +0900 Subject: [PATCH] ttf: ++thread safety Properly allow the ttf loader sharable among threads. issue: https://github.com/thorvg/thorvg/issues/3165 --- src/loaders/ttf/tvgTtfLoader.cpp | 23 ++++++----------------- src/loaders/ttf/tvgTtfLoader.h | 9 +++++---- src/loaders/ttf/tvgTtfReader.cpp | 24 ++++++++++++++---------- src/loaders/ttf/tvgTtfReader.h | 14 +++++++------- src/renderer/tvgLoadModule.h | 21 +++++++++++++++------ src/renderer/tvgLoader.cpp | 4 +++- src/renderer/tvgText.h | 18 ++++++++++-------- 7 files changed, 60 insertions(+), 53 deletions(-) diff --git a/src/loaders/ttf/tvgTtfLoader.cpp b/src/loaders/ttf/tvgTtfLoader.cpp index 6d66907f..bd676068 100644 --- a/src/loaders/ttf/tvgTtfLoader.cpp +++ b/src/loaders/ttf/tvgTtfLoader.cpp @@ -217,17 +217,16 @@ void TtfLoader::clear() /************************************************************************/ -bool TtfLoader::transform(Paint* paint, float fontSize, bool italic) +float TtfLoader::transform(Paint* paint, FontMetrics& metrics, float fontSize, bool italic) { - if (!paint) return false; auto shift = 0.0f; auto dpi = 96.0f / 72.0f; //dpi base? - scale = fontSize * dpi / reader.metrics.unitsPerEm; + auto scale = fontSize * dpi / reader.metrics.unitsPerEm; if (italic) shift = -scale * 0.18f; //experimental decision. - Matrix m = {scale, shift, -(shift * reader.metrics.minw), 0, scale, 0, 0, 0, 1}; + Matrix m = {scale, shift, -(shift * metrics.minw), 0, scale, 0, 0, 0, 1}; paint->transform(m); - return true; + return scale; } @@ -270,21 +269,11 @@ bool TtfLoader::open(const char* data, uint32_t size, bool copy) } -bool TtfLoader::request(Shape* shape, char* text) -{ - this->shape = shape; - this->text = text; - - return true; -} - - -bool TtfLoader::read() +bool TtfLoader::read(Shape* shape, char* text, FontMetrics& out) { if (!text) return false; shape->reset(); - shape->fill(FillRule::EvenOdd); auto n = strlen(text); auto code = _codepoints(text, n); @@ -306,7 +295,7 @@ bool TtfLoader::read() lglyph = rglyph; //store the first glyph with outline min size for italic transform. if (loadMinw && gmetrics.outline) { - reader.metrics.minw = gmetrics.minw; + out.minw = gmetrics.minw; loadMinw = false; } } diff --git a/src/loaders/ttf/tvgTtfLoader.h b/src/loaders/ttf/tvgTtfLoader.h index da55c673..f129bd29 100644 --- a/src/loaders/ttf/tvgTtfLoader.h +++ b/src/loaders/ttf/tvgTtfLoader.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023 - 2024 the ThorVG project. All rights reserved. + * Copyright (c) 2023 - 2025 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -43,11 +43,12 @@ struct TtfLoader : public FontLoader ~TtfLoader(); using FontLoader::open; + using FontLoader::read; + bool open(const string& path) override; bool open(const char *data, uint32_t size, bool copy) override; - bool transform(Paint* paint, float fontSize, bool italic) override; - bool request(Shape* shape, char* text) override; - bool read() override; + float transform(Paint* paint, FontMetrics& metrices, float fontSize, bool italic) override; + bool read(Shape* shape, char* text, FontMetrics& out) override; void clear(); }; diff --git a/src/loaders/ttf/tvgTtfReader.cpp b/src/loaders/ttf/tvgTtfReader.cpp index 8abc4899..4c43287a 100644 --- a/src/loaders/ttf/tvgTtfReader.cpp +++ b/src/loaders/ttf/tvgTtfReader.cpp @@ -72,12 +72,10 @@ static int _cmpu32(const void *a, const void *b) bool TtfReader::validate(uint32_t offset, uint32_t margin) const { -#if 1 if ((offset > size) || (size - offset < margin)) { TVGERR("TTF", "Invalidate data"); return false; } -#endif return true; } @@ -204,8 +202,11 @@ uint32_t TtfReader::outlineOffset(uint32_t glyph) { uint32_t cur, next; - if (!loca) loca = table("loca"); - if (!glyf) glyf = table("glyf"); + auto loca = this->loca.load(); + if (loca == 0) this->loca = loca = table("loca"); + + auto glyf = this->glyf.load(); + if (glyf == 0) this->glyf = glyf = table("glyf"); if (metrics.locaFormat == 0) { auto base = loca + 2 * glyph; @@ -327,8 +328,8 @@ bool TtfReader::header() metrics.numHmtx = _u16(data, hhea + 34); //kerning - this->kern = table("kern"); - if (this->kern) { + auto kern = this->kern = table("kern"); + if (kern) { if (!validate(kern, 4)) return false; if (_u16(data, kern) != 0) return false; } @@ -339,8 +340,9 @@ bool TtfReader::header() uint32_t TtfReader::glyph(uint32_t codepoint) { + auto cmap = this->cmap.load(); if (cmap == 0) { - cmap = table("cmap"); + this->cmap = cmap = table("cmap"); if (!validate(cmap, 4)) return -1; } @@ -397,7 +399,8 @@ uint32_t TtfReader::glyph(uint32_t codepoint, TtfGlyphMetrics& gmetrics) bool TtfReader::glyphMetrics(uint32_t glyphIndex, TtfGlyphMetrics& gmetrics) { //horizontal metrics - if (!hmtx) hmtx = table("hmtx"); + auto hmtx = this->hmtx.load(); + if (hmtx == 0) this->hmtx = hmtx = table("hmtx"); //glyph is inside long metrics segment. if (glyphIndex < metrics.numHmtx) { @@ -451,7 +454,8 @@ bool TtfReader::convert(Shape* shape, TtfGlyphMetrics& gmetrics, const Point& of if (outlineCnt == 0) return false; if (outlineCnt < 0) { uint16_t maxComponentDepth = 1U; - if (!maxp) maxp = table("maxp"); + auto maxp = this->maxp.load(); + if (maxp == 0) this->maxp = maxp = table("maxp"); if (validate(maxp, 32) && _u32(data, maxp) >= 0x00010000U) { // >= version 1.0 maxComponentDepth = _u16(data, maxp + 30); } @@ -607,7 +611,7 @@ void TtfReader::kerning(uint32_t lglyph, uint32_t rglyph, Point& out) if (!kern) return; - auto kern = this->kern; + auto kern = this->kern.load(); out.x = out.y = 0.0f; diff --git a/src/loaders/ttf/tvgTtfReader.h b/src/loaders/ttf/tvgTtfReader.h index 7ee1aab3..ae559233 100644 --- a/src/loaders/ttf/tvgTtfReader.h +++ b/src/loaders/ttf/tvgTtfReader.h @@ -23,6 +23,7 @@ #ifndef _TVG_TTF_READER_H #define _TVG_TTF_READER_H +#include #include "tvgCommon.h" #include "tvgArray.h" @@ -55,7 +56,6 @@ public: float lineGap; } hhea; - float minw; //first glyph width (used for italic) uint16_t unitsPerEm; uint16_t numHmtx; //the number of Horizontal metrics table uint8_t locaFormat; //0 for short offsets, 1 for long @@ -68,12 +68,12 @@ public: private: //table offsets - uint32_t cmap = 0; - uint32_t hmtx = 0; - uint32_t loca = 0; - uint32_t glyf = 0; - uint32_t kern = 0; - uint32_t maxp = 0; + atomic cmap{}; + atomic hmtx{}; + atomic loca{}; + atomic glyf{}; + atomic kern{}; + atomic maxp{}; uint32_t cmap_12_13(uint32_t table, uint32_t codepoint, int which) const; uint32_t cmap_4(uint32_t table, uint32_t codepoint) const; diff --git a/src/renderer/tvgLoadModule.h b/src/renderer/tvgLoadModule.h index a9c1a685..4d2fa1aa 100644 --- a/src/renderer/tvgLoadModule.h +++ b/src/renderer/tvgLoadModule.h @@ -23,6 +23,8 @@ #ifndef _TVG_LOAD_MODULE_H_ #define _TVG_LOAD_MODULE_H_ +#include +#include "tvgCommon.h" #include "tvgRender.h" #include "tvgInlist.h" @@ -38,7 +40,7 @@ struct LoadModule }; FileType type; //current loader file type - uint16_t sharing = 0; //reference count + atomic sharing{}; //reference count bool readied = false; //read done already. bool pathcache = false; //cached by path @@ -77,7 +79,7 @@ struct LoadModule struct ImageLoader : LoadModule { - static ColorSpace cs; //desired value + static atomic cs; //desired value float w = 0, h = 0; //default image size RenderSurface surface; @@ -95,14 +97,21 @@ struct ImageLoader : LoadModule }; +struct FontMetrics +{ + //TODO: add necessary metrics + float minw; +}; + + struct FontLoader : LoadModule { - float scale = 1.0f; - FontLoader(FileType type) : LoadModule(type) {} - virtual bool request(Shape* shape, char* text) = 0; - virtual bool transform(Paint* paint, float fontSize, bool italic) = 0; + using LoadModule::read; + + virtual bool read(Shape* shape, char* text, FontMetrics& out) = 0; + virtual float transform(Paint* paint, FontMetrics& mertrics, float fontSize, bool italic) = 0; }; #endif //_TVG_LOAD_MODULE_H_ diff --git a/src/renderer/tvgLoader.cpp b/src/renderer/tvgLoader.cpp index 7b91a2bb..89b6fdc5 100644 --- a/src/renderer/tvgLoader.cpp +++ b/src/renderer/tvgLoader.cpp @@ -22,6 +22,7 @@ #include +#include #include "tvgInlist.h" #include "tvgLoader.h" #include "tvgLock.h" @@ -66,7 +67,8 @@ uintptr_t HASH_KEY(const char* data) /* Internal Class Implementation */ /************************************************************************/ -ColorSpace ImageLoader::cs = ColorSpace::ARGB8888; +//TODO: remove it. +atomic ImageLoader::cs{ColorSpace::ARGB8888}; static Key _key; static Inlist _activeLoaders; diff --git a/src/renderer/tvgText.h b/src/renderer/tvgText.h index d92495ef..9d4529ab 100644 --- a/src/renderer/tvgText.h +++ b/src/renderer/tvgText.h @@ -24,6 +24,7 @@ #define _TVG_TEXT_H #include +#include "tvgMath.h" #include "tvgShape.h" #include "tvgFill.h" #include "tvgLoader.h" @@ -33,6 +34,7 @@ struct Text::Impl FontLoader* loader = nullptr; Text* paint; Shape* shape; + FontMetrics metrics; char* utf8 = nullptr; float fontSize; bool italic = false; @@ -40,6 +42,7 @@ struct Text::Impl Impl(Text* p) : paint(p), shape(Shape::gen().release()) { + shape->fill(FillRule::EvenOdd); } ~Impl() @@ -94,27 +97,26 @@ struct Text::Impl return PP(shape)->render(renderer); } - bool load() + float load() { - if (!loader) return false; + if (!loader) return 0.0f; - loader->request(shape, utf8); //reload if (changed) { - loader->read(); + loader->read(shape, utf8, metrics); changed = false; } - return loader->transform(shape, fontSize, italic); + return loader->transform(shape, metrics, fontSize, italic); } RenderData update(RenderMethod* renderer, const Matrix& transform, Array& clips, uint8_t opacity, RenderUpdateFlag pFlag, TVG_UNUSED bool clipper) { - if (!load()) return nullptr; + auto scale = 1.0f / load(); + if (tvg::zero(scale)) return nullptr; //transform the gradient coordinates based on the final scaled font. auto fill = P(shape)->rs.fill; if (fill && P(shape)->rFlag & RenderUpdateFlag::Gradient) { - auto scale = 1.0f / loader->scale; if (fill->type() == Type::LinearGradient) { P(static_cast(fill))->x1 *= scale; P(static_cast(fill))->y1 *= scale; @@ -134,7 +136,7 @@ struct Text::Impl bool bounds(float* x, float* y, float* w, float* h, TVG_UNUSED bool stroking) { - if (!load()) return false; + if (load() == 0.0f) return false; PP(shape)->bounds(x, y, w, h, true, true, false); return true; }