Line data Source code
1 : /**
2 : Copyright (c) 2023 Stappler LLC <admin@stappler.dev>
3 :
4 : Permission is hereby granted, free of charge, to any person obtaining a copy
5 : of this software and associated documentation files (the "Software"), to deal
6 : in the Software without restriction, including without limitation the rights
7 : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 : copies of the Software, and to permit persons to whom the Software is
9 : furnished to do so, subject to the following conditions:
10 :
11 : The above copyright notice and this permission notice shall be included in
12 : all copies or substantial portions of the Software.
13 :
14 : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 : IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 : FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 : AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 : LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 : OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 : THE SOFTWARE.
21 : **/
22 :
23 : #include "XLFontExtension.h"
24 : #include "XLCoreLoop.h"
25 : #include "XLCoreQueue.h"
26 : #include "XLCoreImageStorage.h"
27 : #include "XLCoreFrameRequest.h"
28 : #include "XLCoreFrameQueue.h"
29 : #include "XLApplication.h"
30 : #include "ft2build.h"
31 : #include FT_FREETYPE_H
32 : #include FT_GLYPH_H
33 : #include FT_MULTIPLE_MASTERS_H
34 : #include FT_SFNT_NAMES_H
35 : #include FT_ADVANCES_H
36 :
37 : #include "SPFontFace.h"
38 :
39 : namespace STAPPLER_VERSIONIZED stappler::xenolith::font {
40 :
41 48 : FontExtension::~FontExtension() {
42 24 : _queue = nullptr;
43 48 : }
44 :
45 24 : bool FontExtension::init(Rc<Application> &&loop, Rc<core::Queue> &&q) {
46 24 : _mainLoop = move(loop);
47 24 : _glLoop = _mainLoop->getGlLoop();
48 24 : _queue = move(q);
49 24 : _library = Rc<FontLibrary>::alloc();
50 24 : if (_queue->isCompiled()) {
51 0 : onActivated();
52 : } else {
53 24 : auto linkId = retain();
54 24 : _glLoop->compileQueue(_queue, [this, linkId] (bool success) {
55 24 : if (success) {
56 24 : _mainLoop->performOnMainThread([this] {
57 24 : onActivated();
58 24 : }, this);
59 : }
60 24 : release(linkId);
61 24 : });
62 : }
63 24 : return true;
64 : }
65 :
66 0 : void FontExtension::initialize(Application *) {
67 :
68 0 : }
69 :
70 24 : void FontExtension::invalidate(Application *) {
71 24 : _library->invalidate();
72 :
73 24 : _queue = nullptr;
74 24 : _mainLoop = nullptr;
75 24 : _glLoop = nullptr;
76 24 : }
77 :
78 2333 : void FontExtension::update(Application *, const UpdateTime &clock) {
79 2333 : _library->update();
80 2333 : }
81 :
82 71 : static Bytes openResourceFont(FontLibrary::DefaultFontName name) {
83 71 : auto d = FontLibrary::getFont(name);
84 144 : return data::decompress<memory::StandartInterface>(d.data(), d.size());
85 : }
86 :
87 96 : static String getResourceFontName(FontLibrary::DefaultFontName name) {
88 192 : return toString("resource:", FontLibrary::getFontName(name));
89 : }
90 :
91 72 : static const FontController::FontSource * makeResourceFontQuery(FontController::Builder &builder, FontLibrary::DefaultFontName name,
92 : FontLayoutParameters params = FontLayoutParameters()) {
93 142 : return builder.addFontSource( getResourceFontName(name), [name] { return openResourceFont(name); }, params);
94 : }
95 :
96 24 : FontController::Builder FontExtension::makeDefaultControllerBuilder(StringView key) {
97 24 : FontController::Builder ret(key);
98 :
99 24 : auto res_RobotoFlex = makeResourceFontQuery(ret, DefaultFontName::RobotoFlex_VariableFont);
100 24 : auto res_RobotoMono_Variable = makeResourceFontQuery(ret, DefaultFontName::RobotoMono_VariableFont);
101 24 : auto res_RobotoMono_Italic_Variable = makeResourceFontQuery(ret, DefaultFontName::RobotoMono_Italic_VariableFont, FontLayoutParameters{
102 : FontStyle::Italic, FontWeight::Normal, FontStretch::Normal});
103 24 : auto res_DejaVuSans = ret.addFontSource( getResourceFontName(DefaultFontName::DejaVuSans), FontLibrary::getFont(DefaultFontName::DejaVuSans));
104 :
105 24 : ret.addFontFaceQuery("sans", res_RobotoFlex);
106 24 : ret.addFontFaceQuery("sans", res_DejaVuSans);
107 24 : ret.addFontFaceQuery("monospace", res_RobotoMono_Variable);
108 24 : ret.addFontFaceQuery("monospace", res_RobotoMono_Italic_Variable);
109 :
110 24 : ret.addAlias("default", "sans");
111 24 : ret.addAlias("system", "sans");
112 :
113 24 : return ret;
114 0 : }
115 :
116 24 : Rc<FontController> FontExtension::acquireController(FontController::Builder &&b) {
117 : struct ControllerBuilder : Ref {
118 : FontController::Builder builder;
119 : Rc<FontController> controller;
120 : Rc<core::DynamicImage> dynamicImage;
121 :
122 : bool invalid = false;
123 : std::atomic<size_t> pendingData = 0;
124 : FontExtension *ext = nullptr;
125 :
126 24 : ControllerBuilder(FontController::Builder &&b)
127 24 : : builder(move(b)) {
128 24 : if (auto c = builder.getTarget()) {
129 0 : controller = c;
130 : }
131 24 : }
132 :
133 0 : void invalidate() {
134 0 : controller = nullptr;
135 0 : }
136 :
137 24 : void loadData() {
138 24 : if (invalid) {
139 0 : invalidate();
140 0 : return;
141 : }
142 :
143 24 : ext->getMainLoop()->performOnMainThread([this] () {
144 72 : for (const Pair<const String, FontController::FamilyQuery> &it : builder.getFamilyQueries()) {
145 48 : Vector<Rc<FontFaceData>> d; d.reserve(it.second.sources.size());
146 144 : for (auto &iit : it.second.sources) {
147 96 : d.emplace_back(move(iit->data));
148 : }
149 48 : controller->addFont(it.second.family, move(d), it.second.addInFront);
150 48 : }
151 :
152 24 : if (builder.getTarget()) {
153 0 : for (auto &it : builder.getAliases()) {
154 0 : controller->addAlias(it.first, it.second);
155 : }
156 0 : controller->sendFontUpdatedEvent();
157 : } else {
158 24 : controller->setAliases(Map<String, String>(builder.getAliases()));
159 24 : controller->setLoaded(true);
160 : }
161 24 : controller = nullptr;
162 24 : }, this);
163 : }
164 :
165 96 : void onDataLoaded(bool success) {
166 96 : auto v = pendingData.fetch_sub(1);
167 96 : if (!success) {
168 0 : invalid = true;
169 0 : if (v == 1) {
170 0 : invalidate();
171 : }
172 96 : } else if (v == 1) {
173 2 : loadData();
174 : }
175 96 : }
176 :
177 24 : void onImageLoaded(Rc<core::DynamicImage> &&image) {
178 24 : auto v = pendingData.fetch_sub(1);
179 24 : if (image) {
180 24 : controller->setImage(move(image));
181 24 : if (v == 1) {
182 22 : loadData();
183 : }
184 : } else {
185 0 : invalid = true;
186 0 : if (v == 1) {
187 0 : invalidate();
188 : }
189 : }
190 24 : }
191 : };
192 :
193 24 : bool hasController = (b.getTarget() != nullptr);
194 24 : auto builder = Rc<ControllerBuilder>::alloc(move(b));
195 24 : builder->ext = this;
196 24 : if (!hasController) {
197 24 : builder->controller = Rc<FontController>::create(this);
198 24 : builder->pendingData = builder->builder.getDataQueries().size() + 1;
199 : } else {
200 0 : builder->pendingData = builder->builder.getDataQueries().size();
201 : }
202 :
203 120 : for (auto &it : builder->builder.getDataQueries()) {
204 96 : _mainLoop->perform([this, name = it.first, sourcePtr = &it.second, builder] (const thread::Task &) mutable -> bool {
205 187 : sourcePtr->data = _library->openFontData(name, sourcePtr->params, sourcePtr->preconfiguredParams, [&] () -> FontLibrary::FontData {
206 96 : if (sourcePtr->fontCallback) {
207 72 : return FontLibrary::FontData(move(sourcePtr->fontCallback));
208 24 : } else if (!sourcePtr->fontExternalData.empty()) {
209 24 : return FontLibrary::FontData(sourcePtr->fontExternalData, true);
210 0 : } else if (!sourcePtr->fontMemoryData.empty()) {
211 0 : return FontLibrary::FontData(move(sourcePtr->fontMemoryData));
212 0 : } else if (!sourcePtr->fontFilePath.empty()) {
213 0 : auto d = filesystem::readIntoMemory<Interface>(sourcePtr->fontFilePath);
214 0 : if (!d.empty()) {
215 0 : return FontLibrary::FontData(move(d));
216 : }
217 0 : }
218 0 : return FontLibrary::FontData(BytesView(), false);
219 96 : });
220 96 : if (sourcePtr->data) {
221 96 : builder->onDataLoaded(true);
222 : } else {
223 0 : builder->onDataLoaded(false);
224 : }
225 96 : return true;
226 : });
227 : }
228 :
229 24 : if (!hasController) {
230 48 : builder->dynamicImage = Rc<core::DynamicImage>::create([name = builder->builder.getName()] (core::DynamicImage::Builder &builder) {
231 72 : builder.setImage(name,
232 48 : core::ImageInfo(
233 0 : Extent2(2, 2),
234 0 : core::ImageUsage::Sampled | core::ImageUsage::TransferSrc,
235 0 : core::PassType::Graphics,
236 48 : core::ImageFormat::R8_UNORM
237 24 : ), [] (uint8_t *ptr, uint64_t, const core::ImageData::DataCallback &cb) {
238 24 : if (ptr) {
239 0 : ptr[0] = 0;
240 0 : ptr[1] = 255;
241 0 : ptr[2] = 255;
242 0 : ptr[3] = 0;
243 : } else {
244 24 : Bytes bytes; bytes.reserve(4);
245 24 : bytes.emplace_back(0);
246 24 : bytes.emplace_back(255);
247 24 : bytes.emplace_back(255);
248 24 : bytes.emplace_back(0);
249 24 : cb(bytes);
250 24 : }
251 48 : }, nullptr);
252 24 : return true;
253 24 : });
254 :
255 24 : _glLoop->compileImage(builder->dynamicImage, [this, builder] (bool success) {
256 24 : _mainLoop->performOnMainThread([success, builder] {
257 24 : if (success) {
258 24 : builder->onImageLoaded(move(builder->dynamicImage));
259 : } else {
260 0 : builder->onImageLoaded(nullptr);
261 : }
262 24 : }, this);
263 24 : });
264 : }
265 :
266 48 : return builder->controller;
267 24 : }
268 :
269 612 : void FontExtension::updateImage(const Rc<core::DynamicImage> &image, Vector<font::FontUpdateRequest> &&data,
270 : Rc<core::DependencyEvent> &&dep) {
271 612 : if (!_active) {
272 0 : _pendingImageQueries.emplace_back(ImageQuery{image, move(data), move(dep)});
273 0 : return;
274 : }
275 :
276 612 : auto input = Rc<RenderFontInput>::alloc();
277 612 : input->queue = _mainLoop->getQueue();
278 612 : input->image = image;
279 612 : input->ext = this;
280 612 : input->requests = move(data);
281 : /*input->output = [] (const gl::ImageInfo &info, BytesView data) {
282 : Bitmap bmp(data, info.extent.width, info.extent.height, bitmap::PixelFormat::A8);
283 : bmp.save(bitmap::FileFormat::Png, toString(Time::now().toMicros(), ".png"));
284 : };*/
285 :
286 612 : auto req = Rc<core::FrameRequest>::create(_queue);
287 612 : if (dep) {
288 600 : req->addSignalDependency(move(dep));
289 : }
290 :
291 612 : for (auto &it : _queue->getInputAttachments()) {
292 612 : req->addInput(it, move(input));
293 612 : break;
294 : }
295 :
296 612 : _glLoop->runRenderQueue(move(req), 0, [app = Rc<Application>(_mainLoop)] (bool success) {
297 612 : app->wakeup();
298 612 : });
299 612 : }
300 :
301 24 : void FontExtension::onActivated() {
302 24 : _active = true;
303 :
304 24 : for (auto &it : _pendingImageQueries) {
305 0 : updateImage(it.image, move(it.chars), move(it.dependency));
306 : }
307 :
308 24 : _pendingImageQueries.clear();
309 24 : }
310 :
311 : }
|