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