Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
TestSVGTypeface.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2014 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
9
10#if defined(SK_ENABLE_SVG)
11
16#include "include/core/SkData.h"
22#include "include/core/SkPath.h"
27#include "include/core/SkSize.h"
37#include "src/base/SkUtils.h"
40#include "src/core/SkFontPriv.h"
41#include "src/core/SkGeometry.h"
42#include "src/core/SkGlyph.h"
43#include "src/core/SkMask.h"
45#include "src/core/SkPathPriv.h"
48#include "src/sfnt/SkOTUtils.h"
49#include "tools/Resources.h"
50
51#include <utility>
52
53using namespace skia_private;
54
55class SkDescriptor;
56
57TestSVGTypeface::TestSVGTypeface(const char* name,
58 int upem,
59 const SkFontMetrics& fontMetrics,
61 const SkFontStyle& style)
62 : SkTypeface(style, false)
63 , fName(name)
64 , fUpem(upem)
65 , fFontMetrics(fontMetrics)
66 , fGlyphs(new Glyph[data.size()])
67 , fGlyphCount(data.size()) {
68 for (size_t i = 0; i < data.size(); ++i) {
69 const SkSVGTestTypefaceGlyphData& datum = data[i];
70 fCMap.set(datum.fUnicode, i);
71 fGlyphs[i].fAdvance = datum.fAdvance;
72 fGlyphs[i].fOrigin = datum.fOrigin;
73 fGlyphs[i].fResourcePath = datum.fSvgResourcePath;
74 }
75}
76
77template <typename Fn>
78void TestSVGTypeface::Glyph::withSVG(Fn&& fn) const {
79 SkAutoMutexExclusive lock(fSvgMutex);
80
81 if (!fParsedSvg) {
82 fParsedSvg = true;
83
84 std::unique_ptr<SkStreamAsset> stream = GetResourceAsStream(fResourcePath);
85 if (!stream) {
86 return;
87 }
88
89 // We expressly *do not want* to set a SkFontMgr when parsing these SVGs.
90 // 1) The SVGs we are processing have no <text> tags in them.
91 // 2) Trying to use ToolUtils::TestFontMgr() is a problem because the portable
92 // SkFontMgr *calls* this function as it creates the typefaces.
94 if (!svg) {
95 return;
96 }
97
98 if (svg->containerSize().isEmpty()) {
99 return;
100 }
101
102 fSvg = std::move(svg);
103 }
104
105 if (fSvg) {
106 fn(*fSvg);
107 }
108}
109
110SkSize TestSVGTypeface::Glyph::size() const {
112 this->withSVG([&](const SkSVGDOM& svg){
113 size = svg.containerSize();
114 });
115 return size;
116}
117
118void TestSVGTypeface::Glyph::render(SkCanvas* canvas) const {
119 this->withSVG([&](const SkSVGDOM& svg){
120 svg.render(canvas);
121 });
122}
123
124TestSVGTypeface::~TestSVGTypeface() {}
125
126TestSVGTypeface::Glyph::Glyph() : fOrigin{0, 0}, fAdvance(0) {}
127TestSVGTypeface::Glyph::~Glyph() {}
128
129SkVector TestSVGTypeface::getAdvance(SkGlyphID glyphID) const {
130 glyphID = glyphID < fGlyphCount ? glyphID : 0;
131 return {fGlyphs[glyphID].fAdvance, 0};
132}
133
134void TestSVGTypeface::getFontMetrics(SkFontMetrics* metrics) const { *metrics = fFontMetrics; }
135
136void TestSVGTypeface::onFilterRec(SkScalerContextRec* rec) const {
138}
139
140void TestSVGTypeface::getGlyphToUnicodeMap(SkUnichar* glyphToUnicode) const {
141 SkDEBUGCODE(unsigned glyphCount = this->countGlyphs());
142 fCMap.foreach ([=](const SkUnichar& c, const SkGlyphID& g) {
143 SkASSERT(g < glyphCount);
144 glyphToUnicode[g] = c;
145 });
146}
147
148std::unique_ptr<SkAdvancedTypefaceMetrics> TestSVGTypeface::onGetAdvancedMetrics() const {
149 std::unique_ptr<SkAdvancedTypefaceMetrics> info(new SkAdvancedTypefaceMetrics);
150 info->fPostScriptName = fName;
151 return info;
152}
153
154void TestSVGTypeface::onGetFontDescriptor(SkFontDescriptor* desc, bool* serialize) const {
155 desc->setFamilyName(fName.c_str());
156 desc->setStyle(this->fontStyle());
157 *serialize = true;
158}
159
160void TestSVGTypeface::onCharsToGlyphs(const SkUnichar uni[], int count, SkGlyphID glyphs[]) const {
161 for (int i = 0; i < count; i++) {
162 SkGlyphID* g = fCMap.find(uni[i]);
163 glyphs[i] = g ? *g : 0;
164 }
165}
166
167void TestSVGTypeface::onGetFamilyName(SkString* familyName) const { *familyName = fName; }
168
169bool TestSVGTypeface::onGetPostScriptName(SkString*) const { return false; }
170
171SkTypeface::LocalizedStrings* TestSVGTypeface::onCreateFamilyNameIterator() const {
172 SkString familyName(fName);
173 SkString language("und"); // undetermined
174 return new SkOTUtils::LocalizedStrings_SingleName(familyName, language);
175}
176
177class SkTestSVGScalerContext : public SkScalerContext {
178public:
179 SkTestSVGScalerContext(sk_sp<TestSVGTypeface> face,
180 const SkScalerContextEffects& effects,
181 const SkDescriptor* desc)
182 : SkScalerContext(std::move(face), effects, desc) {
183 fRec.getSingleMatrix(&fMatrix);
184 SkScalar upem = this->getTestSVGTypeface()->fUpem;
185 fMatrix.preScale(1.f / upem, 1.f / upem);
186 }
187
188protected:
189 TestSVGTypeface* getTestSVGTypeface() const {
190 return static_cast<TestSVGTypeface*>(this->getTypeface());
191 }
192
193 SkVector computeAdvance(SkGlyphID glyphID) {
194 auto advance = this->getTestSVGTypeface()->getAdvance(glyphID);
195 return fMatrix.mapXY(advance.fX, advance.fY);
196 }
197
198 GlyphMetrics generateMetrics(const SkGlyph& glyph, SkArenaAlloc*) override {
199 SkGlyphID glyphID = glyph.getGlyphID();
200 glyphID = glyphID < this->getTestSVGTypeface()->fGlyphCount ? glyphID : 0;
201
202 GlyphMetrics mx(SkMask::kARGB32_Format);
203 mx.neverRequestPath = true;
204 mx.advance = this->computeAdvance(glyph.getGlyphID());
205
206 TestSVGTypeface::Glyph& glyphData = this->getTestSVGTypeface()->fGlyphs[glyphID];
207
208 SkSize containerSize = glyphData.size();
209 SkRect newBounds = SkRect::MakeXYWH(glyphData.fOrigin.fX,
210 -glyphData.fOrigin.fY,
211 containerSize.fWidth,
212 containerSize.fHeight);
213 fMatrix.mapRect(&newBounds);
216 newBounds.offset(dx, dy);
217 newBounds.roundOut(&mx.bounds);
218 return mx;
219 }
220
221 void generateImage(const SkGlyph& glyph, void* imageBuffer) override {
222 SkGlyphID glyphID = glyph.getGlyphID();
223 glyphID = glyphID < this->getTestSVGTypeface()->fGlyphCount ? glyphID : 0;
224
225 SkBitmap bm;
226 // TODO: this should be SkImageInfo::MakeS32 when that passes all the tests.
228 imageBuffer, glyph.rowBytes());
229 bm.eraseColor(0);
230
231 TestSVGTypeface::Glyph& glyphData = this->getTestSVGTypeface()->fGlyphs[glyphID];
232
235
236 SkCanvas canvas(bm);
237 canvas.translate(-glyph.left(), -glyph.top());
238 canvas.translate(dx, dy);
239 canvas.concat(fMatrix);
240 canvas.translate(glyphData.fOrigin.fX, -glyphData.fOrigin.fY);
241
242 glyphData.render(&canvas);
243 }
244
245 bool generatePath(const SkGlyph& glyph, SkPath* path) override {
246 // Should never get here since generateMetrics always sets the path to not exist.
247 SK_ABORT("Path requested, but it should have been indicated that there isn't one.");
248 path->reset();
249 return false;
250 }
251
252 struct SVGGlyphDrawable : public SkDrawable {
253 SkTestSVGScalerContext* fSelf;
254 SkGlyph fGlyph;
255 SVGGlyphDrawable(SkTestSVGScalerContext* self, const SkGlyph& glyph)
256 : fSelf(self), fGlyph(glyph) {}
257 SkRect onGetBounds() override { return fGlyph.rect(); }
258 size_t onApproximateBytesUsed() override { return sizeof(SVGGlyphDrawable); }
259
260 void onDraw(SkCanvas* canvas) override {
261 SkGlyphID glyphID = fGlyph.getGlyphID();
262 glyphID = glyphID < fSelf->getTestSVGTypeface()->fGlyphCount ? glyphID : 0;
263
264 TestSVGTypeface::Glyph& glyphData = fSelf->getTestSVGTypeface()->fGlyphs[glyphID];
265
268
269 canvas->translate(dx, dy);
270 canvas->concat(fSelf->fMatrix);
271 canvas->translate(glyphData.fOrigin.fX, -glyphData.fOrigin.fY);
272
273 glyphData.render(canvas);
274 }
275 };
276 sk_sp<SkDrawable> generateDrawable(const SkGlyph& glyph) override {
277 return sk_sp<SVGGlyphDrawable>(new SVGGlyphDrawable(this, glyph));
278 }
279
280 void generateFontMetrics(SkFontMetrics* metrics) override {
281 this->getTestSVGTypeface()->getFontMetrics(metrics);
282 SkFontPriv::ScaleFontMetrics(metrics, fMatrix.getScaleY());
283 }
284
285private:
286 SkMatrix fMatrix;
287};
288
289std::unique_ptr<SkScalerContext> TestSVGTypeface::onCreateScalerContext(
290 const SkScalerContextEffects& e, const SkDescriptor* desc) const
291{
292 return std::make_unique<SkTestSVGScalerContext>(
293 sk_ref_sp(const_cast<TestSVGTypeface*>(this)), e, desc);
294}
295
296class DefaultTypeface : public TestSVGTypeface {
297 using TestSVGTypeface::TestSVGTypeface;
298
299 bool getPathOp(SkColor color, SkPathOp* op) const override {
300 if ((SkColorGetR(color) + SkColorGetG(color) + SkColorGetB(color)) / 3 > 0x20) {
302 } else {
304 }
305 return true;
306 }
307
308 static constexpr SkTypeface::FactoryId FactoryId = SkSetFourByteTag('d','s','v','g');
309 static constexpr const char gHeaderString[] = "SkTestSVGTypefaceDefault01";
310 static constexpr const size_t kHeaderSize = sizeof(gHeaderString);
311
312 std::unique_ptr<SkStreamAsset> onOpenStream(int* ttcIndex) const override {
315 return wstream.detachAsStream();
316 }
317
318 static sk_sp<SkTypeface> MakeFromStream(std::unique_ptr<SkStreamAsset> stream,
319 const SkFontArguments&) {
320 char header[kHeaderSize];
321 if (stream->read(header, kHeaderSize) != kHeaderSize ||
322 0 != memcmp(header, gHeaderString, kHeaderSize))
323 {
324 return nullptr;
325 }
326 return TestSVGTypeface::Default();
327 }
328
329 void onGetFontDescriptor(SkFontDescriptor* desc, bool* serialize) const override {
330 TestSVGTypeface::onGetFontDescriptor(desc, serialize);
331 desc->setFactoryId(FactoryId);
332 }
333public:
334 struct Register { Register() { SkTypeface::Register(FactoryId, &MakeFromStream); } };
335};
336static DefaultTypeface::Register defaultTypefaceRegister;
337sk_sp<TestSVGTypeface> TestSVGTypeface::Default() {
338 // Recommended that the first four be .notdef, .null, CR, space
339 constexpr const static SkSVGTestTypefaceGlyphData glyphs[] = {
340 {"fonts/svg/notdef.svg", {100, 800}, 800, 0x0}, // .notdef
341 {"fonts/svg/empty.svg", {0, 0}, 800, 0x0020}, // space
342 {"fonts/svg/diamond.svg", {100, 800}, 800, 0x2662}, // ♢
343 {"fonts/svg/smile.svg", {0, 800}, 800, 0x1F600}, // 😀
344 };
345 SkFontMetrics metrics;
350 metrics.fTop = -800;
351 metrics.fAscent = -800;
352 metrics.fDescent = 200;
353 metrics.fBottom = 200;
354 metrics.fLeading = 100;
355 metrics.fAvgCharWidth = 1000;
356 metrics.fMaxCharWidth = 1000;
357 metrics.fXMin = 0;
358 metrics.fXMax = 1000;
359 metrics.fXHeight = 500;
360 metrics.fCapHeight = 700;
361 metrics.fUnderlineThickness = 40;
362 metrics.fUnderlinePosition = 20;
363 metrics.fStrikeoutThickness = 20;
364 metrics.fStrikeoutPosition = -400;
365
367 new DefaultTypeface("Emoji", 1000, metrics, glyphs, SkFontStyle::Normal()));
368}
369
370class PlanetTypeface : public TestSVGTypeface {
371 using TestSVGTypeface::TestSVGTypeface;
372
373 bool getPathOp(SkColor color, SkPathOp* op) const override {
375 return true;
376 }
377
378 static constexpr SkTypeface::FactoryId FactoryId = SkSetFourByteTag('p','s','v','g');
379 static constexpr const char gHeaderString[] = "SkTestSVGTypefacePlanet01";
380 static constexpr const size_t kHeaderSize = sizeof(gHeaderString);
381
382 std::unique_ptr<SkStreamAsset> onOpenStream(int* ttcIndex) const override {
385 return wstream.detachAsStream();
386 }
387
388 static sk_sp<SkTypeface> MakeFromStream(std::unique_ptr<SkStreamAsset> stream,
389 const SkFontArguments&) {
390 char header[kHeaderSize];
391 if (stream->read(header, kHeaderSize) != kHeaderSize ||
392 0 != memcmp(header, gHeaderString, kHeaderSize))
393 {
394 return nullptr;
395 }
396 return TestSVGTypeface::Planets();
397 }
398
399 void onGetFontDescriptor(SkFontDescriptor* desc, bool* isLocal) const override {
400 TestSVGTypeface::onGetFontDescriptor(desc, isLocal);
401 desc->setFactoryId(FactoryId);
402 }
403public:
404 struct Register { Register() { SkTypeface::Register(FactoryId, &MakeFromStream); } };
405};
406static PlanetTypeface::Register planetTypefaceRegister;
407sk_sp<TestSVGTypeface> TestSVGTypeface::Planets() {
408 // Recommended that the first four be .notdef, .null, CR, space
409 constexpr const static SkSVGTestTypefaceGlyphData glyphs[] = {
410 {"fonts/svg/planets/pluto.svg", {0, 20}, 60, 0x0}, // .notdef
411 {"fonts/svg/empty.svg", {0, 0}, 400, 0x0020}, // space
412 {"fonts/svg/planets/mercury.svg", {0, 45}, 120, 0x263F}, // ☿
413 {"fonts/svg/planets/venus.svg", {0, 100}, 240, 0x2640}, // ♀
414 {"fonts/svg/planets/earth.svg", {0, 100}, 240, 0x2641}, // ♁
415 {"fonts/svg/planets/mars.svg", {0, 50}, 130, 0x2642}, // ♂
416 {"fonts/svg/planets/jupiter.svg", {0, 1000}, 2200, 0x2643}, // ♃
417 {"fonts/svg/planets/saturn.svg", {-300, 1500}, 2600, 0x2644}, // ♄
418 {"fonts/svg/planets/uranus.svg", {0, 375}, 790, 0x2645}, // ♅
419 {"fonts/svg/planets/neptune.svg", {0, 350}, 740, 0x2646}, // ♆
420 };
421 SkFontMetrics metrics;
426 metrics.fTop = -1500;
427 metrics.fAscent = -200;
428 metrics.fDescent = 50;
429 metrics.fBottom = 1558;
430 metrics.fLeading = 10;
431 metrics.fAvgCharWidth = 200;
432 metrics.fMaxCharWidth = 200;
433 metrics.fXMin = -300;
434 metrics.fXMax = 2566;
435 metrics.fXHeight = 100;
436 metrics.fCapHeight = 180;
437 metrics.fUnderlineThickness = 8;
438 metrics.fUnderlinePosition = 2;
439 metrics.fStrikeoutThickness = 2;
440 metrics.fStrikeoutPosition = -80;
441
443 new PlanetTypeface("Planets", 200, metrics, glyphs, SkFontStyle::Normal()));
444}
445
446void TestSVGTypeface::exportTtxCommon(SkWStream* out,
447 const char* type,
448 const TArray<GlyfInfo>* glyfInfo) const {
449 int totalGlyphs = fGlyphCount;
450 out->writeText(" <GlyphOrder>\n");
451 for (int i = 0; i < fGlyphCount; ++i) {
452 out->writeText(" <GlyphID name=\"glyf");
453 out->writeHexAsText(i, 4);
454 out->writeText("\"/>\n");
455 }
456 if (glyfInfo) {
457 for (int i = 0; i < fGlyphCount; ++i) {
458 for (int j = 0; j < (*glyfInfo)[i].fLayers.size(); ++j) {
459 out->writeText(" <GlyphID name=\"glyf");
460 out->writeHexAsText(i, 4);
461 out->writeText("l");
462 out->writeHexAsText(j, 4);
463 out->writeText("\"/>\n");
464 ++totalGlyphs;
465 }
466 }
467 }
468 out->writeText(" </GlyphOrder>\n");
469
470 out->writeText(" <head>\n");
471 out->writeText(" <tableVersion value=\"1.0\"/>\n");
472 out->writeText(" <fontRevision value=\"1.0\"/>\n");
473 out->writeText(" <checkSumAdjustment value=\"0xa9c3274\"/>\n");
474 out->writeText(" <magicNumber value=\"0x5f0f3cf5\"/>\n");
475 out->writeText(" <flags value=\"00000000 00011011\"/>\n");
476 out->writeText(" <unitsPerEm value=\"");
477 out->writeDecAsText(fUpem);
478 out->writeText("\"/>\n");
479 out->writeText(" <created value=\"Thu Feb 15 12:55:49 2018\"/>\n");
480 out->writeText(" <modified value=\"Thu Feb 15 12:55:49 2018\"/>\n");
481 // TODO: not recalculated for bitmap fonts?
482 out->writeText(" <xMin value=\"");
483 out->writeScalarAsText(fFontMetrics.fXMin);
484 out->writeText("\"/>\n");
485 out->writeText(" <yMin value=\"");
486 out->writeScalarAsText(-fFontMetrics.fBottom);
487 out->writeText("\"/>\n");
488 out->writeText(" <xMax value=\"");
489 out->writeScalarAsText(fFontMetrics.fXMax);
490 out->writeText("\"/>\n");
491 out->writeText(" <yMax value=\"");
492 out->writeScalarAsText(-fFontMetrics.fTop);
493 out->writeText("\"/>\n");
494
495 char macStyle[16] = {
496 '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0'};
497 if (this->fontStyle().weight() >= SkFontStyle::Bold().weight()) {
498 macStyle[0xF - 0x0] = '1'; // Bold
499 }
500 switch (this->fontStyle().slant()) {
501 case SkFontStyle::kUpright_Slant: break;
503 macStyle[0xF - 0x1] = '1'; // Italic
504 break;
506 macStyle[0xF - 0x1] = '1'; // Italic
507 break;
508 default: SK_ABORT("Unknown slant.");
509 }
510 if (this->fontStyle().width() <= SkFontStyle::kCondensed_Width) {
511 macStyle[0xF - 0x5] = '1'; // Condensed
512 } else if (this->fontStyle().width() >= SkFontStyle::kExpanded_Width) {
513 macStyle[0xF - 0x6] = '1'; // Extended
514 }
515 out->writeText(" <macStyle value=\"");
516 out->write(macStyle, 8);
517 out->writeText(" ");
518 out->write(macStyle + 8, 8);
519 out->writeText("\"/>\n");
520 out->writeText(" <lowestRecPPEM value=\"8\"/>\n");
521 out->writeText(" <fontDirectionHint value=\"2\"/>\n");
522 out->writeText(" <indexToLocFormat value=\"0\"/>\n");
523 out->writeText(" <glyphDataFormat value=\"0\"/>\n");
524 out->writeText(" </head>\n");
525
526 out->writeText(" <hhea>\n");
527 out->writeText(" <tableVersion value=\"0x00010000\"/>\n");
528 out->writeText(" <ascent value=\"");
529 out->writeDecAsText(-fFontMetrics.fAscent);
530 out->writeText("\"/>\n");
531 out->writeText(" <descent value=\"");
532 out->writeDecAsText(-fFontMetrics.fDescent);
533 out->writeText("\"/>\n");
534 out->writeText(" <lineGap value=\"");
535 out->writeDecAsText(fFontMetrics.fLeading);
536 out->writeText("\"/>\n");
537 out->writeText(" <advanceWidthMax value=\"0\"/>\n");
538 out->writeText(" <minLeftSideBearing value=\"0\"/>\n");
539 out->writeText(" <minRightSideBearing value=\"0\"/>\n");
540 out->writeText(" <xMaxExtent value=\"");
541 out->writeScalarAsText(fFontMetrics.fXMax - fFontMetrics.fXMin);
542 out->writeText("\"/>\n");
543 out->writeText(" <caretSlopeRise value=\"1\"/>\n");
544 out->writeText(" <caretSlopeRun value=\"0\"/>\n");
545 out->writeText(" <caretOffset value=\"0\"/>\n");
546 out->writeText(" <reserved0 value=\"0\"/>\n");
547 out->writeText(" <reserved1 value=\"0\"/>\n");
548 out->writeText(" <reserved2 value=\"0\"/>\n");
549 out->writeText(" <reserved3 value=\"0\"/>\n");
550 out->writeText(" <metricDataFormat value=\"0\"/>\n");
551 out->writeText(" <numberOfHMetrics value=\"0\"/>\n");
552 out->writeText(" </hhea>\n");
553
554 // Some of this table is going to be re-calculated, but we have to write it out anyway.
555 out->writeText(" <maxp>\n");
556 out->writeText(" <tableVersion value=\"0x10000\"/>\n");
557 out->writeText(" <numGlyphs value=\"");
558 out->writeDecAsText(totalGlyphs);
559 out->writeText("\"/>\n");
560 out->writeText(" <maxPoints value=\"4\"/>\n");
561 out->writeText(" <maxContours value=\"1\"/>\n");
562 out->writeText(" <maxCompositePoints value=\"0\"/>\n");
563 out->writeText(" <maxCompositeContours value=\"0\"/>\n");
564 out->writeText(" <maxZones value=\"1\"/>\n");
565 out->writeText(" <maxTwilightPoints value=\"0\"/>\n");
566 out->writeText(" <maxStorage value=\"0\"/>\n");
567 out->writeText(" <maxFunctionDefs value=\"10\"/>\n");
568 out->writeText(" <maxInstructionDefs value=\"0\"/>\n");
569 out->writeText(" <maxStackElements value=\"512\"/>\n");
570 out->writeText(" <maxSizeOfInstructions value=\"24\"/>\n");
571 out->writeText(" <maxComponentElements value=\"0\"/>\n");
572 out->writeText(" <maxComponentDepth value=\"0\"/>\n");
573 out->writeText(" </maxp>\n");
574
575 out->writeText(" <OS_2>\n");
576 out->writeText(" <version value=\"4\"/>\n");
577 out->writeText(" <xAvgCharWidth value=\"");
578 out->writeScalarAsText(fFontMetrics.fAvgCharWidth);
579 out->writeText("\"/>\n");
580 out->writeText(" <usWeightClass value=\"");
581 out->writeDecAsText(this->fontStyle().weight());
582 out->writeText("\"/>\n");
583 out->writeText(" <usWidthClass value=\"");
584 out->writeDecAsText(this->fontStyle().width());
585 out->writeText("\"/>\n");
586 out->writeText(" <fsType value=\"00000000 00000000\"/>\n");
587 out->writeText(" <ySubscriptXSize value=\"665\"/>\n");
588 out->writeText(" <ySubscriptYSize value=\"716\"/>\n");
589 out->writeText(" <ySubscriptXOffset value=\"0\"/>\n");
590 out->writeText(" <ySubscriptYOffset value=\"143\"/>\n");
591 out->writeText(" <ySuperscriptXSize value=\"665\"/>\n");
592 out->writeText(" <ySuperscriptYSize value=\"716\"/>\n");
593 out->writeText(" <ySuperscriptXOffset value=\"0\"/>\n");
594 out->writeText(" <ySuperscriptYOffset value=\"491\"/>\n");
595 out->writeText(" <yStrikeoutSize value=\"");
596 out->writeScalarAsText(fFontMetrics.fStrikeoutThickness);
597 out->writeText("\"/>\n");
598 out->writeText(" <yStrikeoutPosition value=\"");
599 out->writeScalarAsText(-fFontMetrics.fStrikeoutPosition);
600 out->writeText("\"/>\n");
601 out->writeText(" <sFamilyClass value=\"0\"/>\n");
602 out->writeText(" <panose>\n");
603 out->writeText(" <bFamilyType value=\"0\"/>\n");
604 out->writeText(" <bSerifStyle value=\"0\"/>\n");
605 out->writeText(" <bWeight value=\"0\"/>\n");
606 out->writeText(" <bProportion value=\"0\"/>\n");
607 out->writeText(" <bContrast value=\"0\"/>\n");
608 out->writeText(" <bStrokeVariation value=\"0\"/>\n");
609 out->writeText(" <bArmStyle value=\"0\"/>\n");
610 out->writeText(" <bLetterForm value=\"0\"/>\n");
611 out->writeText(" <bMidline value=\"0\"/>\n");
612 out->writeText(" <bXHeight value=\"0\"/>\n");
613 out->writeText(" </panose>\n");
614 out->writeText(" <ulUnicodeRange1 value=\"00000000 00000000 00000000 00000001\"/>\n");
615 out->writeText(" <ulUnicodeRange2 value=\"00010000 00000000 00000000 00000000\"/>\n");
616 out->writeText(" <ulUnicodeRange3 value=\"00000000 00000000 00000000 00000000\"/>\n");
617 out->writeText(" <ulUnicodeRange4 value=\"00000000 00000000 00000000 00000000\"/>\n");
618 out->writeText(" <achVendID value=\"Skia\"/>\n");
619 char fsSelection[16] = {
620 '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0'};
621 fsSelection[0xF - 0x7] = '1'; // Use typo metrics
622 if (this->fontStyle().weight() >= SkFontStyle::Bold().weight()) {
623 fsSelection[0xF - 0x5] = '1'; // Bold
624 }
625 switch (this->fontStyle().slant()) {
627 if (this->fontStyle().weight() < SkFontStyle::Bold().weight()) {
628 fsSelection[0xF - 0x6] = '1'; // Not bold or italic, is regular
629 }
630 break;
632 fsSelection[0xF - 0x0] = '1'; // Italic
633 break;
635 fsSelection[0xF - 0x0] = '1'; // Italic
636 fsSelection[0xF - 0x9] = '1'; // Oblique
637 break;
638 default: SK_ABORT("Unknown slant.");
639 }
640 out->writeText(" <fsSelection value=\"");
641 out->write(fsSelection, 8);
642 out->writeText(" ");
643 out->write(fsSelection + 8, 8);
644 out->writeText("\"/>\n");
645 out->writeText(" <usFirstCharIndex value=\"0\"/>\n");
646 out->writeText(" <usLastCharIndex value=\"0\"/>\n");
647 out->writeText(" <sTypoAscender value=\"");
648 out->writeScalarAsText(-fFontMetrics.fAscent);
649 out->writeText("\"/>\n");
650 out->writeText(" <sTypoDescender value=\"");
651 out->writeScalarAsText(-fFontMetrics.fDescent);
652 out->writeText("\"/>\n");
653 out->writeText(" <sTypoLineGap value=\"");
654 out->writeScalarAsText(fFontMetrics.fLeading);
655 out->writeText("\"/>\n");
656 out->writeText(" <usWinAscent value=\"");
657 out->writeScalarAsText(-fFontMetrics.fAscent);
658 out->writeText("\"/>\n");
659 out->writeText(" <usWinDescent value=\"");
660 out->writeScalarAsText(fFontMetrics.fDescent);
661 out->writeText("\"/>\n");
662 out->writeText(" <ulCodePageRange1 value=\"00000000 00000000 00000000 00000000\"/>\n");
663 out->writeText(" <ulCodePageRange2 value=\"00000000 00000000 00000000 00000000\"/>\n");
664 out->writeText(" <sxHeight value=\"");
665 out->writeScalarAsText(fFontMetrics.fXHeight);
666 out->writeText("\"/>\n");
667 out->writeText(" <sCapHeight value=\"");
668 out->writeScalarAsText(fFontMetrics.fCapHeight);
669 out->writeText("\"/>\n");
670 out->writeText(" <usDefaultChar value=\"0\"/>\n");
671 out->writeText(" <usBreakChar value=\"32\"/>\n");
672 out->writeText(" <usMaxContext value=\"0\"/>\n");
673 out->writeText(" </OS_2>\n");
674
675 out->writeText(" <hmtx>\n");
676 for (int i = 0; i < fGlyphCount; ++i) {
677 out->writeText(" <mtx name=\"glyf");
678 out->writeHexAsText(i, 4);
679 out->writeText("\" width=\"");
680 out->writeDecAsText(fGlyphs[i].fAdvance);
681 out->writeText("\" lsb=\"");
682 int lsb = fGlyphs[i].fOrigin.fX;
683 if (glyfInfo) {
684 lsb += (*glyfInfo)[i].fBounds.fLeft;
685 }
686 out->writeDecAsText(lsb);
687 out->writeText("\"/>\n");
688 }
689 if (glyfInfo) {
690 for (int i = 0; i < fGlyphCount; ++i) {
691 for (int j = 0; j < (*glyfInfo)[i].fLayers.size(); ++j) {
692 out->writeText(" <mtx name=\"glyf");
693 out->writeHexAsText(i, 4);
694 out->writeText("l");
695 out->writeHexAsText(j, 4);
696 out->writeText("\" width=\"");
697 out->writeDecAsText(fGlyphs[i].fAdvance);
698 out->writeText("\" lsb=\"");
699 int32_t lsb = fGlyphs[i].fOrigin.fX + (*glyfInfo)[i].fLayers[j].fBounds.fLeft;
700 out->writeDecAsText(lsb);
701 out->writeText("\"/>\n");
702 }
703 }
704 }
705 out->writeText(" </hmtx>\n");
706
707 bool hasNonBMP = false;
708 out->writeText(" <cmap>\n");
709 out->writeText(" <tableVersion version=\"0\"/>\n");
710 out->writeText(" <cmap_format_4 platformID=\"3\" platEncID=\"1\" language=\"0\">\n");
711 fCMap.foreach ([&out, &hasNonBMP](const SkUnichar& c, const SkGlyphID& g) {
712 if (0xFFFF < c) {
713 hasNonBMP = true;
714 return;
715 }
716 out->writeText(" <map code=\"0x");
717 out->writeHexAsText(c, 4);
718 out->writeText("\" name=\"glyf");
719 out->writeHexAsText(g, 4);
720 out->writeText("\"/>\n");
721 });
722 out->writeText(" </cmap_format_4>\n");
723 if (hasNonBMP) {
724 out->writeText(
725 " <cmap_format_12 platformID=\"3\" platEncID=\"10\" format=\"12\" "
726 "reserved=\"0\" length=\"1\" language=\"0\" nGroups=\"0\">\n");
727 fCMap.foreach ([&out](const SkUnichar& c, const SkGlyphID& g) {
728 out->writeText(" <map code=\"0x");
729 out->writeHexAsText(c, 6);
730 out->writeText("\" name=\"glyf");
731 out->writeHexAsText(g, 4);
732 out->writeText("\"/>\n");
733 });
734 out->writeText(" </cmap_format_12>\n");
735 }
736 out->writeText(" </cmap>\n");
737
738 out->writeText(" <name>\n");
739 out->writeText(
740 " <namerecord nameID=\"1\" platformID=\"3\" platEncID=\"1\" langID=\"0x409\">\n");
741 out->writeText(" ");
742 out->writeText(fName.c_str());
743 out->writeText(" ");
744 out->writeText(type);
745 out->writeText("\n");
746 out->writeText(" </namerecord>\n");
747 out->writeText(
748 " <namerecord nameID=\"2\" platformID=\"3\" platEncID=\"1\" langID=\"0x409\">\n");
749 out->writeText(" Regular\n");
750 out->writeText(" </namerecord>\n");
751 out->writeText(" </name>\n");
752
753 out->writeText(" <post>\n");
754 out->writeText(" <formatType value=\"3.0\"/>\n");
755 out->writeText(" <italicAngle value=\"0.0\"/>\n");
756 out->writeText(" <underlinePosition value=\"");
757 out->writeScalarAsText(fFontMetrics.fUnderlinePosition);
758 out->writeText("\"/>\n");
759 out->writeText(" <underlineThickness value=\"");
760 out->writeScalarAsText(fFontMetrics.fUnderlineThickness);
761 out->writeText("\"/>\n");
762 out->writeText(" <isFixedPitch value=\"0\"/>\n");
763 out->writeText(" <minMemType42 value=\"0\"/>\n");
764 out->writeText(" <maxMemType42 value=\"0\"/>\n");
765 out->writeText(" <minMemType1 value=\"0\"/>\n");
766 out->writeText(" <maxMemType1 value=\"0\"/>\n");
767 out->writeText(" </post>\n");
768}
769
770void TestSVGTypeface::exportTtxCbdt(SkWStream* out, SkSpan<unsigned> strikeSizes) const {
772 SkFont font;
773 font.setTypeface(sk_ref_sp(const_cast<TestSVGTypeface*>(this)));
775 this->getFamilyName(&name);
776
777 // The CBDT/CBLC format is quite restrictive. Only write strikes which fully fit.
778 STArray<8, int> goodStrikeSizes;
779 for (size_t strikeIndex = 0; strikeIndex < strikeSizes.size(); ++strikeIndex) {
780 font.setSize(strikeSizes[strikeIndex]);
781
782 // CBLC limits
783 SkFontMetrics fm;
784 font.getMetrics(&fm);
785 if (!SkTFitsIn<int8_t>((int)(-fm.fTop)) || !SkTFitsIn<int8_t>((int)(-fm.fBottom)) ||
786 !SkTFitsIn<uint8_t>((int)(fm.fXMax - fm.fXMin))) {
787 SkDebugf("Metrics too big cbdt font size %f for %s.\n", font.getSize(), name.c_str());
788 continue;
789 }
790
791 // CBDT limits
792 auto exceedsCbdtLimits = [&]() {
793 for (int i = 0; i < fGlyphCount; ++i) {
794 SkGlyphID gid = i;
795 SkScalar advance;
797 font.getWidthsBounds(&gid, 1, &advance, &bounds, nullptr);
798 SkIRect ibounds = bounds.roundOut();
799 if (!SkTFitsIn<int8_t>(ibounds.fLeft) || !SkTFitsIn<int8_t>(ibounds.fTop) ||
800 !SkTFitsIn<uint8_t>(ibounds.width()) || !SkTFitsIn<uint8_t>(ibounds.height()) ||
801 !SkTFitsIn<uint8_t>((int)advance)) {
802 return true;
803 }
804 }
805 return false;
806 };
807 if (exceedsCbdtLimits()) {
808 SkDebugf("Glyphs too big cbdt font size %f for %s.\n", font.getSize(), name.c_str());
809 continue;
810 }
811
812 goodStrikeSizes.emplace_back(strikeSizes[strikeIndex]);
813 }
814
815 if (goodStrikeSizes.empty()) {
816 SkDebugf("No strike size fit for cbdt font for %s.\n", name.c_str());
817 return;
818 }
819
820 out->writeText("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
821 out->writeText("<ttFont sfntVersion=\"\\x00\\x01\\x00\\x00\" ttLibVersion=\"3.19\">\n");
822 this->exportTtxCommon(out, "CBDT");
823
824 out->writeText(" <CBDT>\n");
825 out->writeText(" <header version=\"2.0\"/>\n");
826 for (int strikeIndex = 0; strikeIndex < goodStrikeSizes.size(); ++strikeIndex) {
827 font.setSize(goodStrikeSizes[strikeIndex]);
828
829 out->writeText(" <strikedata index=\"");
830 out->writeDecAsText(strikeIndex);
831 out->writeText("\">\n");
832 for (int i = 0; i < fGlyphCount; ++i) {
833 SkGlyphID gid = i;
834 SkScalar advance;
836 font.getWidthsBounds(&gid, 1, &advance, &bounds, nullptr);
837 SkIRect ibounds = bounds.roundOut();
838 if (ibounds.isEmpty()) {
839 continue;
840 }
841 SkImageInfo image_info = SkImageInfo::MakeN32Premul(ibounds.width(), ibounds.height());
844 SkCanvas* canvas = surface->getCanvas();
845 canvas->clear(0);
846 SkPixmap pix;
847 surface->peekPixels(&pix);
848 canvas->drawSimpleText(&gid,
849 sizeof(gid),
851 -bounds.fLeft,
852 -bounds.fTop,
853 font,
854 paint);
855
856 sk_sp<SkImage> image = surface->makeImageSnapshot();
858
859 out->writeText(" <cbdt_bitmap_format_17 name=\"glyf");
860 out->writeHexAsText(i, 4);
861 out->writeText("\">\n");
862 out->writeText(" <SmallGlyphMetrics>\n");
863 out->writeText(" <height value=\"");
864 out->writeDecAsText(image->height());
865 out->writeText("\"/>\n");
866 out->writeText(" <width value=\"");
867 out->writeDecAsText(image->width());
868 out->writeText("\"/>\n");
869 out->writeText(" <BearingX value=\"");
870 out->writeDecAsText(ibounds.fLeft);
871 out->writeText("\"/>\n");
872 out->writeText(" <BearingY value=\"");
873 out->writeDecAsText(-ibounds.fTop);
874 out->writeText("\"/>\n");
875 out->writeText(" <Advance value=\"");
876 out->writeDecAsText((int)advance);
877 out->writeText("\"/>\n");
878 out->writeText(" </SmallGlyphMetrics>\n");
879 out->writeText(" <rawimagedata>");
880 uint8_t const* bytes = data->bytes();
881 for (size_t j = 0; j < data->size(); ++j) {
882 if ((j % 0x10) == 0x0) {
883 out->writeText("\n ");
884 } else if (((j - 1) % 0x4) == 0x3) {
885 out->writeText(" ");
886 }
887 out->writeHexAsText(bytes[j], 2);
888 }
889 out->writeText("\n");
890 out->writeText(" </rawimagedata>\n");
891 out->writeText(" </cbdt_bitmap_format_17>\n");
892 }
893 out->writeText(" </strikedata>\n");
894 }
895 out->writeText(" </CBDT>\n");
896
897 SkFontMetrics fm;
898 out->writeText(" <CBLC>\n");
899 out->writeText(" <header version=\"2.0\"/>\n");
900 for (int strikeIndex = 0; strikeIndex < goodStrikeSizes.size(); ++strikeIndex) {
901 font.setSize(goodStrikeSizes[strikeIndex]);
902 font.getMetrics(&fm);
903 out->writeText(" <strike index=\"");
904 out->writeDecAsText(strikeIndex);
905 out->writeText("\">\n");
906 out->writeText(" <bitmapSizeTable>\n");
907 out->writeText(" <sbitLineMetrics direction=\"hori\">\n");
908 out->writeText(" <ascender value=\"");
909 out->writeDecAsText((int)(-fm.fTop));
910 out->writeText("\"/>\n");
911 out->writeText(" <descender value=\"");
912 out->writeDecAsText((int)(-fm.fBottom));
913 out->writeText("\"/>\n");
914 out->writeText(" <widthMax value=\"");
915 out->writeDecAsText((int)(fm.fXMax - fm.fXMin));
916 out->writeText("\"/>\n");
917 out->writeText(" <caretSlopeNumerator value=\"0\"/>\n");
918 out->writeText(" <caretSlopeDenominator value=\"0\"/>\n");
919 out->writeText(" <caretOffset value=\"0\"/>\n");
920 out->writeText(" <minOriginSB value=\"0\"/>\n");
921 out->writeText(" <minAdvanceSB value=\"0\"/>\n");
922 out->writeText(" <maxBeforeBL value=\"0\"/>\n");
923 out->writeText(" <minAfterBL value=\"0\"/>\n");
924 out->writeText(" <pad1 value=\"0\"/>\n");
925 out->writeText(" <pad2 value=\"0\"/>\n");
926 out->writeText(" </sbitLineMetrics>\n");
927 out->writeText(" <sbitLineMetrics direction=\"vert\">\n");
928 out->writeText(" <ascender value=\"");
929 out->writeDecAsText((int)(-fm.fTop));
930 out->writeText("\"/>\n");
931 out->writeText(" <descender value=\"");
932 out->writeDecAsText((int)(-fm.fBottom));
933 out->writeText("\"/>\n");
934 out->writeText(" <widthMax value=\"");
935 out->writeDecAsText((int)(fm.fXMax - fm.fXMin));
936 out->writeText("\"/>\n");
937 out->writeText(" <caretSlopeNumerator value=\"0\"/>\n");
938 out->writeText(" <caretSlopeDenominator value=\"0\"/>\n");
939 out->writeText(" <caretOffset value=\"0\"/>\n");
940 out->writeText(" <minOriginSB value=\"0\"/>\n");
941 out->writeText(" <minAdvanceSB value=\"0\"/>\n");
942 out->writeText(" <maxBeforeBL value=\"0\"/>\n");
943 out->writeText(" <minAfterBL value=\"0\"/>\n");
944 out->writeText(" <pad1 value=\"0\"/>\n");
945 out->writeText(" <pad2 value=\"0\"/>\n");
946 out->writeText(" </sbitLineMetrics>\n");
947 out->writeText(" <colorRef value=\"0\"/>\n");
948 out->writeText(" <startGlyphIndex value=\"1\"/>\n");
949 out->writeText(" <endGlyphIndex value=\"1\"/>\n");
950 out->writeText(" <ppemX value=\"");
951 out->writeDecAsText(goodStrikeSizes[strikeIndex]);
952 out->writeText("\"/>\n");
953 out->writeText(" <ppemY value=\"");
954 out->writeDecAsText(goodStrikeSizes[strikeIndex]);
955 out->writeText("\"/>\n");
956 out->writeText(" <bitDepth value=\"32\"/>\n");
957 out->writeText(" <flags value=\"1\"/>\n");
958 out->writeText(" </bitmapSizeTable>\n");
959 out->writeText(
960 " <eblc_index_sub_table_1 imageFormat=\"17\" firstGlyphIndex=\"1\" "
961 "lastGlyphIndex=\"1\">\n");
962 for (int i = 0; i < fGlyphCount; ++i) {
963 SkGlyphID gid = i;
965 font.getBounds(&gid, 1, &bounds, nullptr);
966 if (bounds.isEmpty()) {
967 continue;
968 }
969 out->writeText(" <glyphLoc name=\"glyf");
970 out->writeHexAsText(i, 4);
971 out->writeText("\"/>\n");
972 }
973 out->writeText(" </eblc_index_sub_table_1>\n");
974 out->writeText(" </strike>\n");
975 }
976 out->writeText(" </CBLC>\n");
977
978 out->writeText("</ttFont>\n");
979}
980
981/**
982 * UnitsPerEm is generally 1000 here. Versions of macOS older than 10.13
983 * have problems in CoreText determining the glyph bounds of bitmap glyphs
984 * with unitsPerEm set to 1024 or numbers not divisible by 100 when the
985 * contour is not closed. The bounds of sbix fonts on macOS appear to be those
986 * of the outline in the 'glyf' table. If this countour is closed it will be
987 * drawn, as the 'glyf' outline is to be drawn on top of any bitmap. (There is
988 * a bit which is supposed to control this, but it cannot be relied on.) So
989 * make the glyph contour a degenerate line with points at the edge of the
990 * bounding box of the glyph.
991 */
992void TestSVGTypeface::exportTtxSbix(SkWStream* out, SkSpan<unsigned> strikeSizes) const {
993 out->writeText("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
994 out->writeText("<ttFont sfntVersion=\"\\x00\\x01\\x00\\x00\" ttLibVersion=\"3.19\">\n");
995 this->exportTtxCommon(out, "sbix");
996
998 SkFont font;
999 font.setTypeface(sk_ref_sp(const_cast<TestSVGTypeface*>(this)));
1000
1001 out->writeText(" <glyf>\n");
1002 for (int i = 0; i < fGlyphCount; ++i) {
1003 const TestSVGTypeface::Glyph& glyphData = this->fGlyphs[i];
1004
1005 SkSize containerSize = glyphData.size();
1006 SkRect bounds = SkRect::MakeXYWH(glyphData.fOrigin.fX,
1007 -glyphData.fOrigin.fY,
1008 containerSize.fWidth,
1009 containerSize.fHeight);
1010 SkIRect ibounds = bounds.roundOut();
1011 out->writeText(" <TTGlyph name=\"glyf");
1012 out->writeHexAsText(i, 4);
1013 out->writeText("\" xMin=\"");
1014 out->writeDecAsText(ibounds.fLeft);
1015 out->writeText("\" yMin=\"");
1016 out->writeDecAsText(-ibounds.fBottom);
1017 out->writeText("\" xMax=\"");
1018 out->writeDecAsText(ibounds.fRight);
1019 out->writeText("\" yMax=\"");
1020 out->writeDecAsText(-ibounds.fTop);
1021 out->writeText("\">\n");
1022 out->writeText(" <contour>\n");
1023 out->writeText(" <pt x=\"");
1024 out->writeDecAsText(ibounds.fLeft);
1025 out->writeText("\" y=\"");
1026 out->writeDecAsText(-ibounds.fBottom);
1027 out->writeText("\" on=\"1\"/>\n");
1028 out->writeText(" </contour>\n");
1029 out->writeText(" <contour>\n");
1030 out->writeText(" <pt x=\"");
1031 out->writeDecAsText(ibounds.fRight);
1032 out->writeText("\" y=\"");
1033 out->writeDecAsText(-ibounds.fTop);
1034 out->writeText("\" on=\"1\"/>\n");
1035 out->writeText(" </contour>\n");
1036 out->writeText(" <instructions/>\n");
1037 out->writeText(" </TTGlyph>\n");
1038 }
1039 out->writeText(" </glyf>\n");
1040
1041 // The loca table will be re-calculated, but if we don't write one we don't get one.
1042 out->writeText(" <loca/>\n");
1043
1044 out->writeText(" <sbix>\n");
1045 out->writeText(" <version value=\"1\"/>\n");
1046 out->writeText(" <flags value=\"00000000 00000001\"/>\n");
1047 for (size_t strikeIndex = 0; strikeIndex < strikeSizes.size(); ++strikeIndex) {
1048 font.setSize(strikeSizes[strikeIndex]);
1049 out->writeText(" <strike>\n");
1050 out->writeText(" <ppem value=\"");
1051 out->writeDecAsText(strikeSizes[strikeIndex]);
1052 out->writeText("\"/>\n");
1053 out->writeText(" <resolution value=\"72\"/>\n");
1054 for (int i = 0; i < fGlyphCount; ++i) {
1055 SkGlyphID gid = i;
1056 SkScalar advance;
1057 SkRect bounds;
1058 font.getWidthsBounds(&gid, 1, &advance, &bounds, nullptr);
1059 SkIRect ibounds = bounds.roundOut();
1060 if (ibounds.isEmpty()) {
1061 continue;
1062 }
1063 SkImageInfo image_info = SkImageInfo::MakeN32Premul(ibounds.width(), ibounds.height());
1066 SkCanvas* canvas = surface->getCanvas();
1067 canvas->clear(0);
1068 SkPixmap pix;
1069 surface->peekPixels(&pix);
1070 canvas->drawSimpleText(&gid,
1071 sizeof(gid),
1073 -bounds.fLeft,
1074 -bounds.fTop,
1075 font,
1076 paint);
1077
1078 sk_sp<SkImage> image = surface->makeImageSnapshot();
1080
1081 // The originOffset values are difficult to use as DirectWrite and FreeType interpret
1082 // the origin to be the initial glyph position on the baseline, but CoreGraphics
1083 // interprets the origin to be the lower left of the cbox of the outline in the 'glyf'
1084 // table.
1085 //#define SK_SBIX_LIKE_FT
1086 //#define SK_SBIX_LIKE_DW
1087 out->writeText(" <glyph name=\"glyf");
1088 out->writeHexAsText(i, 4);
1089 out->writeText("\" graphicType=\"png \" originOffsetX=\"");
1090#if defined(SK_SBIX_LIKE_FT) || defined(SK_SBIX_LIKE_DW)
1091 out->writeDecAsText(bounds.fLeft);
1092#else
1093 out->writeDecAsText(0);
1094#endif
1095 // DirectWrite and CoreGraphics use positive values of originOffsetY to push the
1096 // image visually up (but from different origins).
1097 // FreeType uses positive values to push the image down.
1098 out->writeText("\" originOffsetY=\"");
1099#if defined(SK_SBIX_LIKE_FT)
1100 out->writeScalarAsText(bounds.fBottom);
1101#elif defined(SK_SBIX_LIKE_DW)
1102 out->writeScalarAsText(-bounds.fBottom);
1103#else
1104 out->writeDecAsText(0);
1105#endif
1106 out->writeText("\">\n");
1107
1108 out->writeText(" <hexdata>");
1109 uint8_t const* bytes = data->bytes();
1110 for (size_t j = 0; j < data->size(); ++j) {
1111 if ((j % 0x10) == 0x0) {
1112 out->writeText("\n ");
1113 } else if (((j - 1) % 0x4) == 0x3) {
1114 out->writeText(" ");
1115 }
1116 out->writeHexAsText(bytes[j], 2);
1117 }
1118 out->writeText("\n");
1119 out->writeText(" </hexdata>\n");
1120 out->writeText(" </glyph>\n");
1121 }
1122 out->writeText(" </strike>\n");
1123 }
1124 out->writeText(" </sbix>\n");
1125 out->writeText("</ttFont>\n");
1126}
1127
1128namespace {
1129
1130void convert_noninflect_cubic_to_quads(const SkPoint p[4],
1131 SkScalar toleranceSqd,
1132 TArray<SkPoint, true>* quads,
1133 int sublevel = 0) {
1134 // Notation: Point a is always p[0]. Point b is p[1] unless p[1] == p[0], in which case it is
1135 // p[2]. Point d is always p[3]. Point c is p[2] unless p[2] == p[3], in which case it is p[1].
1136
1137 SkVector ab = p[1] - p[0];
1138 SkVector dc = p[2] - p[3];
1139
1142 SkPoint* degQuad = quads->push_back_n(3);
1143 degQuad[0] = p[0];
1144 degQuad[1] = p[0];
1145 degQuad[2] = p[3];
1146 return;
1147 }
1148 ab = p[2] - p[0];
1149 }
1151 dc = p[1] - p[3];
1152 }
1153
1154 static const SkScalar kLengthScale = 3 * SK_Scalar1 / 2;
1155 static const int kMaxSubdivs = 10;
1156
1157 ab.scale(kLengthScale);
1158 dc.scale(kLengthScale);
1159
1160 // e0 and e1 are extrapolations along vectors ab and dc.
1161 SkVector c0 = p[0];
1162 c0 += ab;
1163 SkVector c1 = p[3];
1164 c1 += dc;
1165
1166 SkScalar dSqd = sublevel > kMaxSubdivs ? 0 : SkPointPriv::DistanceToSqd(c0, c1);
1167 if (dSqd < toleranceSqd) {
1168 SkPoint cAvg = c0;
1169 cAvg += c1;
1170 cAvg.scale(SK_ScalarHalf);
1171
1172 SkPoint* pts = quads->push_back_n(3);
1173 pts[0] = p[0];
1174 pts[1] = cAvg;
1175 pts[2] = p[3];
1176 return;
1177 }
1178 SkPoint choppedPts[7];
1179 SkChopCubicAtHalf(p, choppedPts);
1180 convert_noninflect_cubic_to_quads(choppedPts + 0, toleranceSqd, quads, sublevel + 1);
1181 convert_noninflect_cubic_to_quads(choppedPts + 3, toleranceSqd, quads, sublevel + 1);
1182}
1183
1184void convertCubicToQuads(const SkPoint p[4], SkScalar tolScale, TArray<SkPoint, true>* quads) {
1185 if (!p[0].isFinite() || !p[1].isFinite() || !p[2].isFinite() || !p[3].isFinite()) {
1186 return;
1187 }
1188 SkPoint chopped[10];
1189 int count = SkChopCubicAtInflections(p, chopped);
1190
1191 const SkScalar tolSqd = SkScalarSquare(tolScale);
1192
1193 for (int i = 0; i < count; ++i) {
1194 SkPoint* cubic = chopped + 3 * i;
1195 convert_noninflect_cubic_to_quads(cubic, tolSqd, quads);
1196 }
1197}
1198
1199void path_to_quads(const SkPath& path, SkPath* quadPath) {
1200 quadPath->reset();
1203 const SkPoint* quadPts;
1204 for (auto [verb, pts, w] : SkPathPriv::Iterate(path)) {
1205 switch (verb) {
1206 case SkPathVerb::kMove: quadPath->moveTo(pts[0].fX, pts[0].fY); break;
1207 case SkPathVerb::kLine: quadPath->lineTo(pts[1].fX, pts[1].fY); break;
1208 case SkPathVerb::kQuad:
1209 quadPath->quadTo(pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY);
1210 break;
1211 case SkPathVerb::kCubic:
1212 qPts.clear();
1213 convertCubicToQuads(pts, SK_Scalar1, &qPts);
1214 for (int i = 0; i < qPts.size(); i += 3) {
1215 quadPath->quadTo(
1216 qPts[i + 1].fX, qPts[i + 1].fY, qPts[i + 2].fX, qPts[i + 2].fY);
1217 }
1218 break;
1219 case SkPathVerb::kConic:
1220 quadPts = converter.computeQuads(pts, *w, SK_Scalar1);
1221 for (int i = 0; i < converter.countQuads(); ++i) {
1222 quadPath->quadTo(quadPts[i * 2 + 1].fX,
1223 quadPts[i * 2 + 1].fY,
1224 quadPts[i * 2 + 2].fX,
1225 quadPts[i * 2 + 2].fY);
1226 }
1227 break;
1228 case SkPathVerb::kClose: quadPath->close(); break;
1229 }
1230 }
1231}
1232
1233class SkCOLRCanvas : public SkNoDrawCanvas {
1234public:
1235 SkCOLRCanvas(SkRect glyphBounds,
1236 const TestSVGTypeface& typeface,
1237 SkGlyphID glyphId,
1238 TestSVGTypeface::GlyfInfo* glyf,
1239 THashMap<SkColor, int>* colors,
1240 SkWStream* out)
1241 : SkNoDrawCanvas(glyphBounds.roundOut().width(), glyphBounds.roundOut().height())
1242 , fBaselineOffset(glyphBounds.top())
1243 , fTypeface(typeface)
1244 , fGlyphId(glyphId)
1245 , fGlyf(glyf)
1246 , fColors(colors)
1247 , fOut(out)
1248 , fLayerId(0) {}
1249
1250 void writePoint(SkScalar x, SkScalar y, bool on) {
1251 fOut->writeText(" <pt x=\"");
1252 fOut->writeDecAsText(SkScalarRoundToInt(x));
1253 fOut->writeText("\" y=\"");
1254 fOut->writeDecAsText(SkScalarRoundToInt(y));
1255 fOut->writeText("\" on=\"");
1256 fOut->write8(on ? '1' : '0');
1257 fOut->writeText("\"/>\n");
1258 }
1259 SkIRect writePath(const SkPath& path, bool layer) {
1260 // Convert to quads.
1261 SkPath quads;
1262 path_to_quads(path, &quads);
1263
1265 SkIRect ibounds = bounds.roundOut();
1266 // The bounds will be re-calculated anyway.
1267 fOut->writeText(" <TTGlyph name=\"glyf");
1268 fOut->writeHexAsText(fGlyphId, 4);
1269 if (layer) {
1270 fOut->writeText("l");
1271 fOut->writeHexAsText(fLayerId, 4);
1272 }
1273 fOut->writeText("\" xMin=\"");
1274 fOut->writeDecAsText(ibounds.fLeft);
1275 fOut->writeText("\" yMin=\"");
1276 fOut->writeDecAsText(ibounds.fTop);
1277 fOut->writeText("\" xMax=\"");
1278 fOut->writeDecAsText(ibounds.fRight);
1279 fOut->writeText("\" yMax=\"");
1280 fOut->writeDecAsText(ibounds.fBottom);
1281 fOut->writeText("\">\n");
1282
1283 bool contourOpen = false;
1284 for (auto [verb, pts, w] : SkPathPriv::Iterate(quads)) {
1285 switch (verb) {
1286 case SkPathVerb::kMove:
1287 if (contourOpen) {
1288 fOut->writeText(" </contour>\n");
1289 contourOpen = false;
1290 }
1291 break;
1292 case SkPathVerb::kLine:
1293 if (!contourOpen) {
1294 fOut->writeText(" <contour>\n");
1295 this->writePoint(pts[0].fX, pts[0].fY, true);
1296 contourOpen = true;
1297 }
1298 this->writePoint(pts[1].fX, pts[1].fY, true);
1299 break;
1300 case SkPathVerb::kQuad:
1301 if (!contourOpen) {
1302 fOut->writeText(" <contour>\n");
1303 this->writePoint(pts[0].fX, pts[0].fY, true);
1304 contourOpen = true;
1305 }
1306 this->writePoint(pts[1].fX, pts[1].fY, false);
1307 this->writePoint(pts[2].fX, pts[2].fY, true);
1308 break;
1309 case SkPathVerb::kClose:
1310 if (contourOpen) {
1311 fOut->writeText(" </contour>\n");
1312 contourOpen = false;
1313 }
1314 break;
1315 default: SkDEBUGFAIL("bad verb"); return ibounds;
1316 }
1317 }
1318 if (contourOpen) {
1319 fOut->writeText(" </contour>\n");
1320 }
1321
1322 // Required to write out an instructions tag.
1323 fOut->writeText(" <instructions/>\n");
1324 fOut->writeText(" </TTGlyph>\n");
1325 return ibounds;
1326 }
1327
1328 void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
1329 SkPath path;
1330 path.addRect(rect);
1331 this->drawPath(path, paint);
1332 }
1333
1334 void onDrawOval(const SkRect& oval, const SkPaint& paint) override {
1335 SkPath path;
1336 path.addOval(oval);
1337 this->drawPath(path, paint);
1338 }
1339
1340 void onDrawArc(const SkRect& oval,
1341 SkScalar startAngle,
1342 SkScalar sweepAngle,
1343 bool useCenter,
1344 const SkPaint& paint) override {
1345 SkPath path;
1346 bool fillNoPathEffect = SkPaint::kFill_Style == paint.getStyle() && !paint.getPathEffect();
1348 &path, oval, startAngle, sweepAngle, useCenter, fillNoPathEffect);
1349 this->drawPath(path, paint);
1350 }
1351
1352 void onDrawRRect(const SkRRect& rrect, const SkPaint& paint) override {
1353 SkPath path;
1354 path.addRRect(rrect);
1355 this->drawPath(path, paint);
1356 }
1357
1358 void onDrawPath(const SkPath& platonicPath, const SkPaint& originalPaint) override {
1359 SkPaint paint = originalPaint;
1360 SkPath path = platonicPath;
1361
1362 // Apply the path effect.
1363 if (paint.getPathEffect() || paint.getStyle() != SkPaint::kFill_Style) {
1364 bool fill = skpathutils::FillPathWithPaint(path, paint, &path);
1365
1366 paint.setPathEffect(nullptr);
1367 if (fill) {
1368 paint.setStyle(SkPaint::kFill_Style);
1369 } else {
1370 paint.setStyle(SkPaint::kStroke_Style);
1371 paint.setStrokeWidth(0);
1372 }
1373 }
1374
1375 // Apply the matrix.
1376 SkMatrix m = this->getTotalMatrix();
1377 // If done to the canvas then everything would get clipped out.
1378 m.postTranslate(0, fBaselineOffset); // put the baseline at 0
1379 m.postScale(1, -1); // and flip it since OpenType is y-up.
1380 path.transform(m);
1381
1382 // While creating the default glyf, union with dark colors and intersect with bright colors.
1383 SkColor color = paint.getColor();
1384 SkPathOp op;
1385 if (fTypeface.getPathOp(color, &op)) {
1386 fBasePath.add(path, op);
1387 }
1388 SkIRect bounds = this->writePath(path, true);
1389
1390 // The CPAL table has the concept of a 'current color' which is index 0xFFFF.
1391 // Mark any layer drawn in 'currentColor' as having this special index.
1392 // The value of 'currentColor' here should a color which causes this layer to union into the
1393 // default glyf.
1394 constexpr SkColor currentColor = 0xFF2B0000;
1395
1396 int colorIndex;
1397 if (color == currentColor) {
1398 colorIndex = 0xFFFF;
1399 } else {
1400 int* colorIndexPtr = fColors->find(color);
1401 if (colorIndexPtr) {
1402 colorIndex = *colorIndexPtr;
1403 } else {
1404 colorIndex = fColors->count();
1405 fColors->set(color, colorIndex);
1406 }
1407 }
1408 fGlyf->fLayers.emplace_back(colorIndex, bounds);
1409
1410 ++fLayerId;
1411 }
1412
1413 void finishGlyph() {
1414 SkPath baseGlyph;
1415 fBasePath.resolve(&baseGlyph);
1416 fGlyf->fBounds = this->writePath(baseGlyph, false);
1417 }
1418
1419private:
1420 SkScalar fBaselineOffset;
1421 const TestSVGTypeface& fTypeface;
1422 SkGlyphID fGlyphId;
1423 TestSVGTypeface::GlyfInfo* fGlyf;
1424 THashMap<SkColor, int>* fColors;
1425 SkWStream* const fOut;
1426 SkOpBuilder fBasePath;
1427 int fLayerId;
1428};
1429
1430} // namespace
1431
1432void TestSVGTypeface::exportTtxColr(SkWStream* out) const {
1433 out->writeText("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
1434 out->writeText("<ttFont sfntVersion=\"\\x00\\x01\\x00\\x00\" ttLibVersion=\"3.19\">\n");
1435
1437 TArray<GlyfInfo> glyfInfos(fGlyphCount);
1438
1439 // Need to know all the glyphs up front for the common tables.
1440 SkDynamicMemoryWStream glyfOut;
1441 glyfOut.writeText(" <glyf>\n");
1442 for (int i = 0; i < fGlyphCount; ++i) {
1443 const TestSVGTypeface::Glyph& glyphData = this->fGlyphs[i];
1444
1445 SkSize containerSize = glyphData.size();
1446 SkRect bounds = SkRect::MakeXYWH(glyphData.fOrigin.fX,
1447 -glyphData.fOrigin.fY,
1448 containerSize.fWidth,
1449 containerSize.fHeight);
1450 SkCOLRCanvas canvas(bounds, *this, i, &glyfInfos.emplace_back(), &colors, &glyfOut);
1451 glyphData.render(&canvas);
1452 canvas.finishGlyph();
1453 }
1454 glyfOut.writeText(" </glyf>\n");
1455
1456 this->exportTtxCommon(out, "COLR", &glyfInfos);
1457
1458 // The loca table will be re-calculated, but if we don't write one we don't get one.
1459 out->writeText(" <loca/>\n");
1460
1461 std::unique_ptr<SkStreamAsset> glyfStream = glyfOut.detachAsStream();
1462 out->writeStream(glyfStream.get(), glyfStream->getLength());
1463
1464 out->writeText(" <COLR>\n");
1465 out->writeText(" <version value=\"0\"/>\n");
1466 for (int i = 0; i < fGlyphCount; ++i) {
1467 if (glyfInfos[i].fLayers.empty()) {
1468 continue;
1469 }
1470 if (glyfInfos[i].fBounds.isEmpty()) {
1471 SkDebugf("Glyph %d is empty but has layers.\n", i);
1472 }
1473 out->writeText(" <ColorGlyph name=\"glyf");
1474 out->writeHexAsText(i, 4);
1475 out->writeText("\">\n");
1476 for (int j = 0; j < glyfInfos[i].fLayers.size(); ++j) {
1477 const int colorIndex = glyfInfos[i].fLayers[j].fLayerColorIndex;
1478 out->writeText(" <layer colorID=\"");
1479 out->writeDecAsText(colorIndex);
1480 out->writeText("\" name=\"glyf");
1481 out->writeHexAsText(i, 4);
1482 out->writeText("l");
1483 out->writeHexAsText(j, 4);
1484 out->writeText("\"/>\n");
1485 }
1486 out->writeText(" </ColorGlyph>\n");
1487 }
1488 out->writeText(" </COLR>\n");
1489
1490 // The colors must be written in order, the 'index' is ignored by ttx.
1491 AutoTMalloc<SkColor> colorsInOrder(colors.count());
1492 colors.foreach ([&colorsInOrder](const SkColor& c, const int* i) { colorsInOrder[*i] = c; });
1493 out->writeText(" <CPAL>\n");
1494 out->writeText(" <version value=\"0\"/>\n");
1495 out->writeText(" <numPaletteEntries value=\"");
1496 out->writeDecAsText(colors.count());
1497 out->writeText("\"/>\n");
1498 out->writeText(" <palette index=\"0\">\n");
1499 for (int i = 0; i < colors.count(); ++i) {
1500 SkColor c = colorsInOrder[i];
1501 out->writeText(" <color index=\"");
1502 out->writeDecAsText(i);
1503 out->writeText("\" value=\"#");
1504 out->writeHexAsText(SkColorGetR(c), 2);
1505 out->writeHexAsText(SkColorGetG(c), 2);
1506 out->writeHexAsText(SkColorGetB(c), 2);
1507 out->writeHexAsText(SkColorGetA(c), 2);
1508 out->writeText("\"/>\n");
1509 }
1510 out->writeText(" </palette>\n");
1511 out->writeText(" </CPAL>\n");
1512
1513 out->writeText("</ttFont>\n");
1514}
1515#endif // SK_ENABLE_SVG
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition DM.cpp:213
const char * fName
uint16_t glyphs[5]
int count
SkColor4f color
static bool isFinite(const SkRect &r)
const SkRect fBounds
std::unique_ptr< SkStreamAsset > GetResourceAsStream(const char *resource, bool useFileStream)
Definition Resources.cpp:31
@ kPremul_SkAlphaType
pixel components are premultiplied by alpha
Definition SkAlphaType.h:29
#define SkDEBUGFAIL(message)
Definition SkAssert.h:118
#define SK_ABORT(message,...)
Definition SkAssert.h:70
#define SkASSERT(cond)
Definition SkAssert.h:116
#define SkColorGetR(color)
Definition SkColor.h:65
#define SkColorGetG(color)
Definition SkColor.h:69
uint32_t SkColor
Definition SkColor.h:37
#define SkColorGetA(color)
Definition SkColor.h:61
#define SkColorGetB(color)
Definition SkColor.h:73
static const char gHeaderString[]
static constexpr size_t kHeaderSize
void SK_SPI SkDebugf(const char format[],...) SK_PRINTF_LIKE(1
#define SkDEBUGCODE(...)
Definition SkDebug.h:23
#define SkFixedToScalar(x)
Definition SkFixed.h:124
@ kNone
glyph outlines unchanged
@ kGlyphID
uses two byte words to represent glyph indices
void SkChopCubicAtHalf(const SkPoint src[4], SkPoint dst[7])
int SkChopCubicAtInflections(const SkPoint src[4], SkPoint dst[10])
SkPathOp
Definition SkPathOps.h:22
@ kDifference_SkPathOp
subtract the op path from the first path
Definition SkPathOps.h:23
@ kUnion_SkPathOp
union (inclusive-or) the two paths
Definition SkPathOps.h:25
@ kClose
SkPath::RawIter returns 0 points.
@ kCubic
SkPath::RawIter returns 4 points.
@ kConic
SkPath::RawIter returns 3 points + 1 weight.
@ kQuad
SkPath::RawIter returns 3 points.
@ kMove
SkPath::RawIter returns 1 point.
@ kLine
SkPath::RawIter returns 2 points.
sk_sp< T > sk_ref_sp(T *obj)
Definition SkRefCnt.h:381
#define SK_Scalar1
Definition SkScalar.h:18
#define SK_ScalarHalf
Definition SkScalar.h:19
#define SkScalarRoundToInt(x)
Definition SkScalar.h:37
#define SK_ScalarNearlyZero
Definition SkScalar.h:99
static SkScalar SkScalarSquare(SkScalar x)
Definition SkScalar.h:71
int32_t SkUnichar
Definition SkTypes.h:175
uint16_t SkGlyphID
Definition SkTypes.h:179
static constexpr SkFourByteTag SkSetFourByteTag(char a, char b, char c, char d)
Definition SkTypes.h:167
bool installPixels(const SkImageInfo &info, void *pixels, size_t rowBytes, void(*releaseProc)(void *addr, void *context), void *context)
Definition SkBitmap.cpp:323
void eraseColor(SkColor4f) const
Definition SkBitmap.cpp:442
void drawSimpleText(const void *text, size_t byteLength, SkTextEncoding encoding, SkScalar x, SkScalar y, const SkFont &font, const SkPaint &paint)
void translate(SkScalar dx, SkScalar dy)
void clear(SkColor color)
Definition SkCanvas.h:1199
SkMatrix getTotalMatrix() const
void drawPath(const SkPath &path, const SkPaint &paint)
void concat(const SkMatrix &matrix)
std::unique_ptr< SkStreamAsset > detachAsStream()
Definition SkStream.cpp:876
bool write(const void *buffer, size_t size) override
Definition SkStream.cpp:535
static void ScaleFontMetrics(SkFontMetrics *, SkScalar)
Definition SkFont.cpp:336
static constexpr SkFontStyle Bold()
Definition SkFontStyle.h:69
static constexpr SkFontStyle Normal()
Definition SkFontStyle.h:66
int top() const
Definition SkGlyph.h:511
size_t rowBytes() const
Definition SkGlyph.cpp:233
SkGlyphID getGlyphID() const
Definition SkGlyph.h:429
SkRect rect() const
Definition SkGlyph.h:506
int height() const
Definition SkGlyph.h:513
SkFixed getSubYFixed() const
Definition SkGlyph.h:432
SkFixed getSubXFixed() const
Definition SkGlyph.h:431
int width() const
Definition SkGlyph.h:512
int left() const
Definition SkGlyph.h:510
int width() const
Definition SkImage.h:285
int height() const
Definition SkImage.h:291
SkMatrix & postTranslate(SkScalar dx, SkScalar dy)
Definition SkMatrix.cpp:281
void mapXY(SkScalar x, SkScalar y, SkPoint *result) const
Definition SkMatrix.cpp:777
SkScalar getScaleY() const
Definition SkMatrix.h:422
SkMatrix & preScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py)
Definition SkMatrix.cpp:315
bool mapRect(SkRect *dst, const SkRect &src, SkApplyPerspectiveClip pc=SkApplyPerspectiveClip::kYes) const
void onDrawArc(const SkRect &, SkScalar, SkScalar, bool, const SkPaint &) override
void onDrawRRect(const SkRRect &, const SkPaint &) override
void onDrawRect(const SkRect &, const SkPaint &) override
void onDrawOval(const SkRect &, const SkPaint &) override
void onDrawPath(const SkPath &, const SkPaint &) override
@ kStroke_Style
set to stroke geometry
Definition SkPaint.h:194
@ kFill_Style
set to fill geometry
Definition SkPaint.h:193
static void CreateDrawArcPath(SkPath *path, const SkRect &oval, SkScalar startAngle, SkScalar sweepAngle, bool useCenter, bool isFillNoPathEffect)
Definition SkPath.cpp:3290
SkPath & moveTo(SkScalar x, SkScalar y)
Definition SkPath.cpp:678
SkPath & lineTo(SkScalar x, SkScalar y)
Definition SkPath.cpp:718
SkRect computeTightBounds() const
Definition SkPath.cpp:3378
SkPath & reset()
Definition SkPath.cpp:360
SkPath & quadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2)
Definition SkPath.cpp:736
SkPath & close()
Definition SkPath.cpp:813
static SkScalar LengthSqd(const SkPoint &pt)
Definition SkPointPriv.h:63
static SkScalar DistanceToSqd(const SkPoint &pt, const SkPoint &a)
Definition SkPointPriv.h:48
static sk_sp< SkSVGDOM > MakeFromStream(SkStream &str)
Definition SkSVGDOM.h:57
virtual void generateImage(const SkGlyph &glyph, void *imageBuffer)=0
virtual GlyphMetrics generateMetrics(const SkGlyph &, SkArenaAlloc *)=0
virtual bool generatePath(const SkGlyph &, SkPath *)=0
SkTypeface * getTypeface() const
constexpr size_t size() const
Definition SkSpan_impl.h:95
static void Register(FactoryId id, sk_sp< SkTypeface >(*make)(std::unique_ptr< SkStreamAsset >, const SkFontArguments &))
SkFourByteTag FactoryId
Definition SkTypeface.h:335
bool writeText(const char text[])
Definition SkStream.h:247
T * get() const
Definition SkRefCnt.h:303
T * push_back_n(int n)
Definition SkTArray.h:262
bool empty() const
Definition SkTArray.h:194
int size() const
Definition SkTArray.h:416
T & emplace_back(Args &&... args)
Definition SkTArray.h:243
const Paint & paint
VkSurfaceKHR surface
Definition main.cc:49
sk_sp< SkImage > image
Definition examples.cpp:29
float SkScalar
Definition extension.cpp:12
const char * name
Definition fuchsia.cc:50
double y
double x
void convertCubicToQuads(const SkPoint p[4], SkScalar tolScale, skia_private::TArray< SkPoint, true > *quads)
SK_API bool Encode(SkWStream *dst, const SkPixmap &src, const Options &options)
Optional< SkRect > bounds
Definition SkRecords.h:189
PODArray< SkColor > colors
Definition SkRecords.h:276
skia_private::AutoTArray< sk_sp< SkImageFilter > > filters TypedMatrix matrix TypedMatrix matrix SkScalar dx
Definition SkRecords.h:208
SK_API sk_sp< SkSurface > Raster(const SkImageInfo &imageInfo, size_t rowBytes, const SkSurfaceProps *surfaceProps)
sk_sp< SkTypeface > PlanetTypeface()
sk_sp< SkTypeface > DefaultTypeface()
Definition ab.py:1
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
Definition switches.h:57
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot data
Definition switches.h:41
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition switches.h:259
font
Font Metadata and Metrics.
AI float cubic(float precision, const SkPoint pts[], const VectorXform &vectorXform=VectorXform())
SK_API bool FillPathWithPaint(const SkPath &src, const SkPaint &paint, SkPath *dst, const SkRect *cullRect, SkScalar resScale=1)
Definition ref_ptr.h:256
SkScalar w
int32_t height
int32_t width
static const char header[]
Definition skpbench.cpp:88
SkScalar fTop
greatest extent above origin of any glyph bounding box, typically negative; deprecated with variable ...
SkScalar fLeading
distance to add between lines, typically positive or zero
SkScalar fAvgCharWidth
average character width, zero if unknown
SkScalar fStrikeoutPosition
distance from baseline to bottom of stroke, typically negative
SkScalar fStrikeoutThickness
strikeout thickness
SkScalar fMaxCharWidth
maximum character width, zero if unknown
SkScalar fBottom
greatest extent below origin of any glyph bounding box, typically positive; deprecated with variable ...
uint32_t fFlags
FontMetricsFlags indicating which metrics are valid.
SkScalar fAscent
distance to reserve above baseline, typically negative
SkScalar fXHeight
height of lower-case 'x', zero if unknown, typically negative
SkScalar fUnderlineThickness
underline thickness
@ kStrikeoutPositionIsValid_Flag
set if fStrikeoutPosition is valid
@ kStrikeoutThicknessIsValid_Flag
set if fStrikeoutThickness is valid
@ kUnderlinePositionIsValid_Flag
set if fUnderlinePosition is valid
@ kUnderlineThicknessIsValid_Flag
set if fUnderlineThickness is valid
SkScalar fDescent
distance to reserve below baseline, typically positive
SkScalar fCapHeight
height of an upper-case letter, zero if unknown, typically negative
SkScalar fXMin
greatest extent to left of origin of any glyph bounding box, typically negative; deprecated with vari...
SkScalar fUnderlinePosition
distance from baseline to top of stroke, typically positive
SkScalar fXMax
greatest extent to right of origin of any glyph bounding box, typically positive; deprecated with var...
int32_t fBottom
larger y-axis bounds
Definition SkRect.h:36
constexpr int32_t height() const
Definition SkRect.h:165
int32_t fTop
smaller y-axis bounds
Definition SkRect.h:34
constexpr int32_t width() const
Definition SkRect.h:158
bool isEmpty() const
Definition SkRect.h:202
int32_t fLeft
smaller x-axis bounds
Definition SkRect.h:33
int32_t fRight
larger x-axis bounds
Definition SkRect.h:35
static SkImageInfo MakeN32Premul(int width, int height)
static SkImageInfo MakeN32(int width, int height, SkAlphaType at)
@ kARGB32_Format
SkPMColor.
Definition SkMask.h:30
void scale(float scale, SkPoint *dst) const
Definition SkPoint.cpp:17
static constexpr SkRect MakeXYWH(float x, float y, float w, float h)
Definition SkRect.h:659
void roundOut(SkIRect *dst) const
Definition SkRect.h:1241
void offset(float dx, float dy)
Definition SkRect.h:1016
bool isEmpty() const
Definition SkRect.h:693
void setHinting(SkFontHinting)
SkScalar fHeight
Definition SkSize.h:54
static constexpr SkSize MakeEmpty()
Definition SkSize.h:62
SkScalar fWidth
Definition SkSize.h:53
const char * svg