1 /*
2 * Copyright 2022 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
8 #include "include/core/SkCanvas.h"
9 #include "include/core/SkColor.h"
10 #include "include/core/SkFont.h"
11 #include "include/core/SkFontMgr.h"
12 #include "include/core/SkGraphics.h"
13 #include "include/core/SkTypeface.h"
14 #include "include/private/base/SkTemplates.h"
15 #include "src/base/SkTime.h"
16 #include "src/sfnt/SkOTTable_glyf.h"
17 #include "src/sfnt/SkOTTable_head.h"
18 #include "src/sfnt/SkOTTable_hhea.h"
19 #include "src/sfnt/SkOTTable_hmtx.h"
20 #include "src/sfnt/SkOTTable_loca.h"
21 #include "src/sfnt/SkOTTable_maxp.h"
22 #include "src/sfnt/SkOTTable_sbix.h"
23 #include "src/sfnt/SkSFNTHeader.h"
24 #include "tools/Resources.h"
25 #include "tools/fonts/FontToolUtils.h"
26 #include "tools/timer/TimeUtils.h"
27 #include "tools/viewer/ClickHandlerSlide.h"
28
29 #if defined(SK_FONTMGR_FREETYPE_EMPTY_AVAILABLE)
30 #include "include/ports/SkFontMgr_empty.h"
31 #endif
32 #if defined(SK_FONTMGR_FONTATIONS_AVAILABLE)
33 #include "include/ports/SkFontMgr_Fontations.h"
34 #endif
35
36 namespace {
37
38 constexpr SkScalar DX = 100;
39 constexpr SkScalar DY = 300;
40 constexpr int kPointSize = 5;
41 constexpr SkScalar kFontSize = 200;
42
43 constexpr char kFontFile[] = "fonts/sbix_uncompressed_flags.ttf";
44 constexpr SkGlyphID kGlyphID = 2;
45 constexpr uint16_t kStrikeIndex = 2;
46
47 //constexpr char kFontFile[] = "fonts/HangingS.ttf";
48 //constexpr SkGlyphID kGlyphID = 4;
49
50 /**
51 * Return the closest int for the given float. Returns SK_MaxS32FitsInFloat for NaN.
52 */
sk_float_saturate2int16(float x)53 static inline int16_t sk_float_saturate2int16(float x) {
54 x = x < SK_MaxS16 ? x : SK_MaxS16;
55 x = x > SK_MinS16 ? x : SK_MinS16;
56 return (int16_t)x;
57 }
58
59 struct ShortCoordinate { bool negative; uint8_t magnitude; };
sk_float_saturate2sm8(float x)60 static inline ShortCoordinate sk_float_saturate2sm8(float x) {
61 bool negative = x < 0;
62 x = x < 255 ? x : 255;
63 x = x > -255 ? x : -255;
64 return ShortCoordinate{ negative, negative ? (uint8_t)-x : (uint8_t)x };
65 }
66
67 struct SBIXSlide : public ClickHandlerSlide {
68 struct Point {
69 SkPoint location;
70 SkColor color;
71 } fPts[5] = {
72 {{0, 0}, SK_ColorBLACK }, // glyph x/y min
73 {{0, 0}, SK_ColorWHITE }, // glyph x/y max
74 {{0, 20}, SK_ColorGREEN }, // lsb (x only, y ignored)
75 {{0, 0}, SK_ColorBLUE }, // first point of glyph contour
76 {{0, 0}, SK_ColorRED }, // sbix glyph data's origin offset
77 };
78 static constexpr const int kGlyfXYMin = 0;
79 static constexpr const int kGlyfXYMax = 1;
80 static constexpr const int kGlyfLSB = 2;
81 static constexpr const int kGlyfFirstPoint = 3;
82 static constexpr const int kOriginOffset = 4;
83
84 std::vector<sk_sp<SkFontMgr>> fFontMgr;
85 std::vector<SkFont> fFonts;
86 sk_sp<SkData> fSBIXData;
87 bool fInputChanged = false;
88 bool fDirty = true;
89
90 public:
SBIXSlide__anon61ce3e570111::SBIXSlide91 SBIXSlide() { fName = "SBIX"; }
92
load__anon61ce3e570111::SBIXSlide93 void load(SkScalar w, SkScalar h) override {
94 fFontMgr.emplace_back(ToolUtils::TestFontMgr());
95 #if defined(SK_FONTMGR_FREETYPE_EMPTY_AVAILABLE)
96 fFontMgr.emplace_back(SkFontMgr_New_Custom_Empty());
97 #endif
98 #if defined(SK_FONTMGR_FONTATIONS_AVAILABLE)
99 fFontMgr.emplace_back(SkFontMgr_New_Fontations_Empty());
100 #endif
101
102 // GetResourceAsData may be backed by a read only file mapping.
103 // For sanity always make a copy.
104 fSBIXData = GetResourceAsData(kFontFile);
105
106 updateSBIXData(fSBIXData.get(), true);
107 }
108
draw__anon61ce3e570111::SBIXSlide109 void draw(SkCanvas* canvas) override {
110 canvas->clear(SK_ColorGRAY);
111
112 canvas->translate(DX, DY);
113
114 SkPaint paint;
115 SkPoint position{0, 0};
116 SkPoint origin{0, 0};
117
118 if (fDirty) {
119 sk_sp<SkData> data(updateSBIXData(fSBIXData.get(), false));
120 fFonts.clear();
121 for (auto&& fontmgr : fFontMgr) {
122 fFonts.emplace_back(fontmgr->makeFromData(data), kFontSize);
123 }
124 fDirty = false;
125 }
126 for (auto&& font : fFonts) {
127 paint.setStyle(SkPaint::kFill_Style);
128 paint.setColor(SK_ColorBLACK);
129 canvas->drawGlyphs(1, &kGlyphID, &position, origin, font, paint);
130
131 paint.setStrokeWidth(SkIntToScalar(kPointSize / 2));
132 paint.setStyle(SkPaint::kStroke_Style);
133 SkScalar advance;
134 SkRect rect;
135 font.getWidthsBounds(&kGlyphID, 1, &advance, &rect, &paint);
136
137 paint.setColor(SK_ColorRED);
138 canvas->drawRect(rect, paint);
139 paint.setColor(SK_ColorGREEN);
140 canvas->drawLine(0, 0, advance, 0, paint);
141 paint.setColor(SK_ColorRED);
142 canvas->drawPoint(0, 0, paint);
143 canvas->drawPoint(advance, 0, paint);
144
145 paint.setStrokeWidth(SkIntToScalar(kPointSize));
146 for (auto&& pt : fPts) {
147 paint.setColor(pt.color);
148 canvas->drawPoints(SkCanvas::kPoints_PointMode, 1, &pt.location, paint);
149 }
150
151 canvas->translate(kFontSize, 0);
152 }
153 }
154
155 protected:
hittest__anon61ce3e570111::SBIXSlide156 static bool hittest(const SkPoint& pt, SkScalar x, SkScalar y) {
157 return SkPoint::Length(pt.fX - x, pt.fY - y) < SkIntToScalar(kPointSize);
158 }
159
onFindClickHandler__anon61ce3e570111::SBIXSlide160 Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey modi) override {
161 x -= DX;
162 y -= DY;
163 // Look for hit in opposite of paint order
164 for (auto&& pt = std::rbegin(fPts); pt != std::rend(fPts); ++pt) {
165 if (hittest(pt->location, x, y)) {
166 return new PtClick(&*pt);
167 }
168 }
169 return nullptr;
170 }
171
onClick__anon61ce3e570111::SBIXSlide172 bool onClick(Click* click) override {
173 ((PtClick*)click)->fPt->location.set(click->fCurr.fX - DX, click->fCurr.fY - DY);
174 fDirty = true;
175 return true;
176 }
177
178 private:
179 class PtClick : public Click {
180 public:
181 Point* fPt;
PtClick(Point * pt)182 PtClick(Point* pt) : fPt(pt) {}
183 };
184
updateSBIXData__anon61ce3e570111::SBIXSlide185 sk_sp<SkData> updateSBIXData(SkData* originalData, bool setPts) {
186 // Lots of unlikely to be aligned pointers in here, which is UB. Total hack.
187
188 sk_sp<SkData> dataCopy = SkData::MakeWithCopy(originalData->data(), originalData->size());
189
190 SkSFNTHeader* sfntHeader = static_cast<SkSFNTHeader*>(dataCopy->writable_data());
191
192 SkASSERT_RELEASE(memcmp(sfntHeader, originalData->data(), originalData->size()) == 0);
193
194 SkSFNTHeader::TableDirectoryEntry* tableEntry =
195 SkTAfter<SkSFNTHeader::TableDirectoryEntry>(sfntHeader);
196 SkSFNTHeader::TableDirectoryEntry* glyfTableEntry = nullptr;
197 SkSFNTHeader::TableDirectoryEntry* headTableEntry = nullptr;
198 SkSFNTHeader::TableDirectoryEntry* hheaTableEntry = nullptr;
199 SkSFNTHeader::TableDirectoryEntry* hmtxTableEntry = nullptr;
200 SkSFNTHeader::TableDirectoryEntry* locaTableEntry = nullptr;
201 SkSFNTHeader::TableDirectoryEntry* maxpTableEntry = nullptr;
202 SkSFNTHeader::TableDirectoryEntry* sbixTableEntry = nullptr;
203 int numTables = SkEndian_SwapBE16(sfntHeader->numTables);
204 for (int tableEntryIndex = 0; tableEntryIndex < numTables; ++tableEntryIndex) {
205 if (SkOTTableGlyph::TAG == tableEntry[tableEntryIndex].tag) {
206 glyfTableEntry = tableEntry + tableEntryIndex;
207 }
208 if (SkOTTableHead::TAG == tableEntry[tableEntryIndex].tag) {
209 headTableEntry = tableEntry + tableEntryIndex;
210 }
211 if (SkOTTableHorizontalHeader::TAG == tableEntry[tableEntryIndex].tag) {
212 hheaTableEntry = tableEntry + tableEntryIndex;
213 }
214 if (SkOTTableHorizontalMetrics::TAG == tableEntry[tableEntryIndex].tag) {
215 hmtxTableEntry = tableEntry + tableEntryIndex;
216 }
217 if (SkOTTableIndexToLocation::TAG == tableEntry[tableEntryIndex].tag) {
218 locaTableEntry = tableEntry + tableEntryIndex;
219 }
220 if (SkOTTableMaximumProfile::TAG == tableEntry[tableEntryIndex].tag) {
221 maxpTableEntry = tableEntry + tableEntryIndex;
222 }
223 if (SkOTTableStandardBitmapGraphics::TAG == tableEntry[tableEntryIndex].tag) {
224 sbixTableEntry = tableEntry + tableEntryIndex;
225 }
226 }
227 SkASSERT_RELEASE(glyfTableEntry);
228 SkASSERT_RELEASE(headTableEntry);
229 SkASSERT_RELEASE(hheaTableEntry);
230 SkASSERT_RELEASE(hmtxTableEntry);
231 SkASSERT_RELEASE(locaTableEntry);
232 SkASSERT_RELEASE(maxpTableEntry);
233
234 size_t glyfTableOffset = SkEndian_SwapBE32(glyfTableEntry->offset);
235 SkOTTableGlyph* glyfTable =
236 SkTAddOffset<SkOTTableGlyph>(sfntHeader, glyfTableOffset);
237
238 size_t headTableOffset = SkEndian_SwapBE32(headTableEntry->offset);
239 SkOTTableHead* headTable =
240 SkTAddOffset<SkOTTableHead>(sfntHeader, headTableOffset);
241
242 size_t hheaTableOffset = SkEndian_SwapBE32(hheaTableEntry->offset);
243 SkOTTableHorizontalHeader* hheaTable =
244 SkTAddOffset<SkOTTableHorizontalHeader>(sfntHeader, hheaTableOffset);
245
246 size_t hmtxTableOffset = SkEndian_SwapBE32(hmtxTableEntry->offset);
247 SkOTTableHorizontalMetrics* hmtxTable =
248 SkTAddOffset<SkOTTableHorizontalMetrics>(sfntHeader, hmtxTableOffset);
249
250 size_t locaTableOffset = SkEndian_SwapBE32(locaTableEntry->offset);
251 SkOTTableIndexToLocation* locaTable =
252 SkTAddOffset<SkOTTableIndexToLocation>(sfntHeader, locaTableOffset);
253
254 size_t maxpTableOffset = SkEndian_SwapBE32(maxpTableEntry->offset);
255 SkOTTableMaximumProfile* maxpTable =
256 SkTAddOffset<SkOTTableMaximumProfile>(sfntHeader, maxpTableOffset);
257
258 SkASSERT_RELEASE(SkEndian_SwapBE32(maxpTable->version.version) == 0x00010000);
259 int numGlyphs = SkEndian_SwapBE16(maxpTable->version.tt.numGlyphs);
260 SkASSERT_RELEASE(kGlyphID < numGlyphs);
261
262 int emSize = SkEndian_SwapBE16(headTable->unitsPerEm);
263 SkScalar toEm = emSize / kFontSize;
264
265 if (sbixTableEntry) {
266 size_t sbixTableOffset = SkEndian_SwapBE32(sbixTableEntry->offset);
267 SkOTTableStandardBitmapGraphics* sbixTable =
268 SkTAddOffset<SkOTTableStandardBitmapGraphics>(sfntHeader, sbixTableOffset);
269
270 uint32_t numStrikes = SkEndian_SwapBE32(sbixTable->numStrikes);
271 SkASSERT_RELEASE(kStrikeIndex < numStrikes);
272
273 uint32_t strikeOffset = SkEndian_SwapBE32(sbixTable->strikeOffset(kStrikeIndex));
274 SkOTTableStandardBitmapGraphics::Strike* strike =
275 SkTAddOffset<SkOTTableStandardBitmapGraphics::Strike>(sbixTable, strikeOffset);
276 uint16_t strikePpem = SkEndian_SwapBE16(strike->ppem);
277 SkScalar toStrikeEm = strikePpem / kFontSize;
278 uint32_t glyphDataOffset = SkEndian_SwapBE32(strike->glyphDataOffset(kGlyphID));
279 uint32_t glyphDataOffsetNext = SkEndian_SwapBE32(strike->glyphDataOffset(kGlyphID+1));
280 SkASSERT_RELEASE(glyphDataOffset < glyphDataOffsetNext);
281 SkOTTableStandardBitmapGraphics::GlyphData* glyphData =
282 SkTAddOffset<SkOTTableStandardBitmapGraphics::GlyphData>(strike, glyphDataOffset);
283 if (setPts) {
284 fPts[kOriginOffset].location.set((int16_t)SkEndian_SwapBE16(glyphData->originOffsetX) / toStrikeEm,
285 (int16_t)SkEndian_SwapBE16(glyphData->originOffsetY) / -toStrikeEm);
286 } else {
287 glyphData->originOffsetX = SkEndian_SwapBE16(sk_float_saturate2int16( fPts[kOriginOffset].location.x()*toStrikeEm));
288 glyphData->originOffsetY = SkEndian_SwapBE16(sk_float_saturate2int16(-fPts[kOriginOffset].location.y()*toStrikeEm));
289 }
290 }
291
292 SkOTTableGlyph::Iterator glyphIter(*glyfTable, *locaTable, headTable->indexToLocFormat);
293 glyphIter.advance(kGlyphID);
294 SkOTTableGlyphData* glyphData = glyphIter.next();
295 if (glyphData) {
296 if (setPts) {
297 fPts[kGlyfXYMin].location.set((int16_t)SkEndian_SwapBE16(glyphData->xMin) / toEm,
298 (int16_t)SkEndian_SwapBE16(glyphData->yMin) / -toEm);
299 fPts[kGlyfXYMax].location.set((int16_t)SkEndian_SwapBE16(glyphData->xMax) / toEm,
300 (int16_t)SkEndian_SwapBE16(glyphData->yMax) / -toEm);
301 } else {
302 glyphData->xMin = SkEndian_SwapBE16(sk_float_saturate2int16( fPts[kGlyfXYMin].location.x()*toEm));
303 glyphData->yMin = SkEndian_SwapBE16(sk_float_saturate2int16(-fPts[kGlyfXYMin].location.y()*toEm));
304 glyphData->xMax = SkEndian_SwapBE16(sk_float_saturate2int16( fPts[kGlyfXYMax].location.x()*toEm));
305 glyphData->yMax = SkEndian_SwapBE16(sk_float_saturate2int16(-fPts[kGlyfXYMax].location.y()*toEm));
306 }
307
308 int contourCount = SkEndian_SwapBE16(glyphData->numberOfContours);
309 if (contourCount > 0) {
310 SK_OT_USHORT* endPtsOfContours = SkTAfter<SK_OT_USHORT>(glyphData);
311 SK_OT_USHORT* numInstructions = SkTAfter<SK_OT_USHORT>(endPtsOfContours,
312 contourCount);
313 SK_OT_BYTE* instructions = SkTAfter<SK_OT_BYTE>(numInstructions);
314 SkOTTableGlyphData::Simple::Flags* flags =
315 SkTAfter<SkOTTableGlyphData::Simple::Flags>(
316 instructions, SkEndian_SwapBE16(*numInstructions));
317
318 int numResultPoints = SkEndian_SwapBE16(endPtsOfContours[contourCount-1]) + 1;
319 struct Coordinate {
320 SkOTTableGlyphData::Simple::Flags* flags;
321 size_t offsetToXDelta;
322 size_t xDeltaSize;
323 size_t offsetToYDelta;
324 size_t yDeltaSize;
325 };
326 std::vector<Coordinate> coordinates(numResultPoints);
327
328 size_t offsetToXDelta = 0;
329 size_t offsetToYDelta = 0;
330 SkOTTableGlyphData::Simple::Flags* currentFlags = flags;
331 for (int i = 0; i < numResultPoints; ++i) {
332 SkOTTableGlyphData::Simple::Flags* nextFlags;
333 int times = 1;
334 if (currentFlags->field.Repeat) {
335 SK_OT_BYTE* repeat = SkTAfter<SK_OT_BYTE>(currentFlags);
336 times += *repeat;
337 nextFlags = SkTAfter<SkOTTableGlyphData::Simple::Flags>(repeat);
338 } else {
339 nextFlags = SkTAfter<SkOTTableGlyphData::Simple::Flags>(currentFlags);
340 }
341
342 --i;
343 for (int time = 0; time < times; ++time) {
344 ++i;
345 coordinates[i].flags = currentFlags;
346 coordinates[i].offsetToXDelta = offsetToXDelta;
347 coordinates[i].offsetToYDelta = offsetToYDelta;
348
349 if (currentFlags->field.xShortVector) {
350 offsetToXDelta += 1;
351 coordinates[i].xDeltaSize = 1;
352 } else if (currentFlags->field.xIsSame_xShortVectorPositive) {
353 offsetToXDelta += 0;
354 if (i == 0) {
355 coordinates[i].xDeltaSize = 0;
356 } else {
357 coordinates[i].xDeltaSize = coordinates[i-1].xDeltaSize;
358 }
359 } else {
360 offsetToXDelta += 2;
361 coordinates[i].xDeltaSize = 2;
362 }
363
364 if (currentFlags->field.yShortVector) {
365 offsetToYDelta += 1;
366 coordinates[i].yDeltaSize = 1;
367 } else if (currentFlags->field.yIsSame_yShortVectorPositive) {
368 offsetToYDelta += 0;
369 if (i == 0) {
370 coordinates[i].yDeltaSize = 0;
371 } else {
372 coordinates[i].yDeltaSize = coordinates[i-1].yDeltaSize;
373 }
374 } else {
375 offsetToYDelta += 2;
376 coordinates[i].yDeltaSize = 2;
377 }
378 }
379 currentFlags = nextFlags;
380 }
381 SK_OT_BYTE* xCoordinates = reinterpret_cast<SK_OT_BYTE*>(currentFlags);
382 SK_OT_BYTE* yCoordinates = xCoordinates + offsetToXDelta;
383
384 int pointIndex = 0;
385 if (coordinates[pointIndex].xDeltaSize == 0) {
386 // Zero delta relative to the origin. There is no data to modify.
387 SkDebugf("Failed to move point in X at all.\n");
388 } else if (coordinates[pointIndex].xDeltaSize == 1) {
389 ShortCoordinate x = sk_float_saturate2sm8(fPts[kGlyfFirstPoint].location.x()*toEm);
390 xCoordinates[coordinates[pointIndex].offsetToXDelta] = x.magnitude;
391 coordinates[pointIndex].flags->field.xIsSame_xShortVectorPositive = !x.negative;
392 } else {
393 *reinterpret_cast<SK_OT_SHORT*>(xCoordinates + coordinates[pointIndex].offsetToXDelta) =
394 SkEndian_SwapBE16(sk_float_saturate2int16(fPts[kGlyfFirstPoint].location.x()*toEm));
395 }
396
397 if (coordinates[pointIndex].yDeltaSize == 0) {
398 // Zero delta relative to the origin. There is no data to modify.
399 SkDebugf("Failed to move point in Y at all.\n");
400 } else if (coordinates[pointIndex].yDeltaSize == 1) {
401 ShortCoordinate y = sk_float_saturate2sm8(-fPts[kGlyfFirstPoint].location.y()*toEm);
402 yCoordinates[coordinates[pointIndex].offsetToYDelta] = y.magnitude;
403 coordinates[pointIndex].flags->field.yIsSame_yShortVectorPositive = !y.negative;
404 } else {
405 *reinterpret_cast<SK_OT_SHORT*>(yCoordinates + coordinates[pointIndex].offsetToYDelta) =
406 SkEndian_SwapBE16(sk_float_saturate2int16(-fPts[kGlyfFirstPoint].location.y()*toEm));
407 }
408 }
409 }
410
411 int numberOfFullMetrics = SkEndian_SwapBE16(hheaTable->numberOfHMetrics);
412 SkOTTableHorizontalMetrics::FullMetric* fullMetrics = hmtxTable->longHorMetric;
413 SK_OT_SHORT lsb = SkEndian_SwapBE16(sk_float_saturate2int16(fPts[kGlyfLSB].location.x()*toEm));
414 if (kGlyphID < numberOfFullMetrics) {
415 if (setPts) {
416 fPts[kGlyfLSB].location.fX = (int16_t)SkEndian_SwapBE16(fullMetrics[kGlyphID].lsb) / toEm;
417 } else {
418 fullMetrics[kGlyphID].lsb = lsb;
419 }
420 } else {
421 SkOTTableHorizontalMetrics::ShortMetric* shortMetrics =
422 SkTAfter<SkOTTableHorizontalMetrics::ShortMetric>(fullMetrics, numberOfFullMetrics);
423 int shortMetricIndex = kGlyphID - numberOfFullMetrics;
424 if (setPts) {
425 fPts[kGlyfLSB].location.fX = (int16_t)SkEndian_SwapBE16(shortMetrics[shortMetricIndex].lsb) / toEm;
426 } else {
427 shortMetrics[shortMetricIndex].lsb = lsb;
428 }
429 }
430
431 headTable->flags.field.LeftSidebearingAtX0 = false;
432 return dataCopy;
433 }
434 };
435 } // namespace
436 DEF_SLIDE( return new SBIXSlide(); )
437