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/SkTime.h"
14 #include "include/core/SkTypeface.h"
15 #include "include/ports/SkFontMgr_empty.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/SkSFNTHeader.h"
23 #include "tools/Resources.h"
24 #include "tools/timer/TimeUtils.h"
25 #include "tools/viewer/ClickHandlerSlide.h"
26
27 namespace {
28
29 constexpr SkScalar DX = 100;
30 constexpr SkScalar DY = 300;
31 constexpr int kPointSize = 5;
32 constexpr SkScalar kFontSize = 200;
33
34 constexpr char kFontFile[] = "fonts/sbix_uncompressed_flags.ttf";
35 constexpr SkGlyphID kGlyphID = 2;
36
37 //constexpr char kFontFile[] = "fonts/HangingS.ttf";
38 //constexpr SkGlyphID kGlyphID = 4;
39
40 /**
41 * Return the closest int for the given float. Returns SK_MaxS32FitsInFloat for NaN.
42 */
sk_float_saturate2int16(float x)43 static inline int16_t sk_float_saturate2int16(float x) {
44 x = x < SK_MaxS16 ? x : SK_MaxS16;
45 x = x > SK_MinS16 ? x : SK_MinS16;
46 return (int16_t)x;
47 }
48
49 struct ShortCoordinate { bool negative; uint8_t magnitude; };
sk_float_saturate2sm8(float x)50 static inline ShortCoordinate sk_float_saturate2sm8(float x) {
51 bool negative = x < 0;
52 x = x < 255 ? x : 255;
53 x = x > -255 ? x : -255;
54 return ShortCoordinate{ negative, negative ? (uint8_t)-x : (uint8_t)x };
55 }
56
57 struct SBIXSlide : public ClickHandlerSlide {
58 SkPoint fPts[12] = {
59 {0, 0}, // min
60 {0, 0}, // max
61 {0, 20}, // lsb
62 {0, 0}, // point
63 };
64 std::vector<sk_sp<SkFontMgr>> fFontMgr;
65 std::vector<SkFont> fFonts;
66 sk_sp<SkData> fSBIXData;
67 bool fInputChanged = false;
68 bool fDirty = true;
69
70 public:
SBIXSlide__anon278e10a60111::SBIXSlide71 SBIXSlide() { fName = "SBIX"; }
72
load__anon278e10a60111::SBIXSlide73 void load(SkScalar w, SkScalar h) override {
74 fFontMgr.emplace_back(SkFontMgr::RefDefault());
75 //fFontMgr.emplace_back(SkFontMgr_New_Custom_Empty());
76 // GetResourceAsData may be backed by a read only file mapping.
77 // For sanity always make a copy.
78 fSBIXData = GetResourceAsData(kFontFile);
79
80 updateSBIXData(fSBIXData.get(), true);
81 }
82
draw__anon278e10a60111::SBIXSlide83 void draw(SkCanvas* canvas) override {
84 canvas->clear(SK_ColorGRAY);
85
86 canvas->translate(DX, DY);
87
88 SkPaint paint;
89 SkPoint position{0, 0};
90 SkPoint origin{0, 0};
91
92 if (fDirty) {
93 sk_sp<SkData> data(updateSBIXData(fSBIXData.get(), false));
94 fFonts.clear();
95 for (auto&& fontmgr : fFontMgr) {
96 fFonts.emplace_back(fontmgr->makeFromData(data), kFontSize);
97 }
98 fDirty = false;
99 }
100 for (auto&& font : fFonts) {
101 paint.setStyle(SkPaint::kFill_Style);
102 paint.setColor(SK_ColorBLACK);
103 canvas->drawGlyphs(1, &kGlyphID, &position, origin, font, paint);
104
105 paint.setStrokeWidth(SkIntToScalar(kPointSize / 2));
106 paint.setStyle(SkPaint::kStroke_Style);
107 SkScalar advance;
108 SkRect rect;
109 font.getWidthsBounds(&kGlyphID, 1, &advance, &rect, &paint);
110
111 paint.setColor(SK_ColorRED);
112 canvas->drawRect(rect, paint);
113 paint.setColor(SK_ColorGREEN);
114 canvas->drawLine(0, 0, advance, 0, paint);
115 paint.setColor(SK_ColorRED);
116 canvas->drawPoint(0, 0, paint);
117 canvas->drawPoint(advance, 0, paint);
118
119 paint.setStrokeWidth(SkIntToScalar(kPointSize));
120 canvas->drawPoints(SkCanvas::kPoints_PointMode, std::size(fPts), fPts, paint);
121
122 canvas->translate(kFontSize, 0);
123 }
124 }
125
126 protected:
hittest__anon278e10a60111::SBIXSlide127 static bool hittest(const SkPoint& pt, SkScalar x, SkScalar y) {
128 return SkPoint::Length(pt.fX - x, pt.fY - y) < SkIntToScalar(kPointSize);
129 }
130
onFindClickHandler__anon278e10a60111::SBIXSlide131 Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey modi) override {
132 x -= DX;
133 y -= DY;
134 for (size_t i = 0; i < std::size(fPts); i++) {
135 if (hittest(fPts[i], x, y)) {
136 return new PtClick((int)i);
137 }
138 }
139 return nullptr;
140 }
141
onClick__anon278e10a60111::SBIXSlide142 bool onClick(Click* click) override {
143 fPts[((PtClick*)click)->fIndex].set(click->fCurr.fX - DX, click->fCurr.fY - DY);
144 fDirty = true;
145 return true;
146 }
147
148 private:
149 class PtClick : public Click {
150 public:
151 int fIndex;
PtClick(int index)152 PtClick(int index) : fIndex(index) {}
153 };
154
updateSBIXData__anon278e10a60111::SBIXSlide155 sk_sp<SkData> updateSBIXData(SkData* originalData, bool setPts) {
156 // Lots of unlikely to be aligned pointers in here, which is UB. Total hack.
157
158 sk_sp<SkData> dataCopy = SkData::MakeWithCopy(originalData->data(), originalData->size());
159
160 SkSFNTHeader* sfntHeader = static_cast<SkSFNTHeader*>(dataCopy->writable_data());
161
162 SkASSERT_RELEASE(memcmp(sfntHeader, originalData->data(), originalData->size()) == 0);
163
164 SkSFNTHeader::TableDirectoryEntry* tableEntry =
165 SkTAfter<SkSFNTHeader::TableDirectoryEntry>(sfntHeader);
166 SkSFNTHeader::TableDirectoryEntry* glyfTableEntry = nullptr;
167 SkSFNTHeader::TableDirectoryEntry* headTableEntry = nullptr;
168 SkSFNTHeader::TableDirectoryEntry* hheaTableEntry = nullptr;
169 SkSFNTHeader::TableDirectoryEntry* hmtxTableEntry = nullptr;
170 SkSFNTHeader::TableDirectoryEntry* locaTableEntry = nullptr;
171 SkSFNTHeader::TableDirectoryEntry* maxpTableEntry = nullptr;
172 int numTables = SkEndian_SwapBE16(sfntHeader->numTables);
173 for (int tableEntryIndex = 0; tableEntryIndex < numTables; ++tableEntryIndex) {
174 if (SkOTTableGlyph::TAG == tableEntry[tableEntryIndex].tag) {
175 glyfTableEntry = tableEntry + tableEntryIndex;
176 }
177 if (SkOTTableHead::TAG == tableEntry[tableEntryIndex].tag) {
178 headTableEntry = tableEntry + tableEntryIndex;
179 }
180 if (SkOTTableHorizontalHeader::TAG == tableEntry[tableEntryIndex].tag) {
181 hheaTableEntry = tableEntry + tableEntryIndex;
182 }
183 if (SkOTTableHorizontalMetrics::TAG == tableEntry[tableEntryIndex].tag) {
184 hmtxTableEntry = tableEntry + tableEntryIndex;
185 }
186 if (SkOTTableIndexToLocation::TAG == tableEntry[tableEntryIndex].tag) {
187 locaTableEntry = tableEntry + tableEntryIndex;
188 }
189 if (SkOTTableMaximumProfile::TAG == tableEntry[tableEntryIndex].tag) {
190 maxpTableEntry = tableEntry + tableEntryIndex;
191 }
192 }
193 SkASSERT_RELEASE(glyfTableEntry);
194 SkASSERT_RELEASE(headTableEntry);
195 SkASSERT_RELEASE(hheaTableEntry);
196 SkASSERT_RELEASE(hmtxTableEntry);
197 SkASSERT_RELEASE(locaTableEntry);
198 SkASSERT_RELEASE(maxpTableEntry);
199
200 size_t glyfTableOffset = SkEndian_SwapBE32(glyfTableEntry->offset);
201 SkOTTableGlyph* glyfTable =
202 SkTAddOffset<SkOTTableGlyph>(sfntHeader, glyfTableOffset);
203
204 size_t headTableOffset = SkEndian_SwapBE32(headTableEntry->offset);
205 SkOTTableHead* headTable =
206 SkTAddOffset<SkOTTableHead>(sfntHeader, headTableOffset);
207
208 size_t hheaTableOffset = SkEndian_SwapBE32(hheaTableEntry->offset);
209 SkOTTableHorizontalHeader* hheaTable =
210 SkTAddOffset<SkOTTableHorizontalHeader>(sfntHeader, hheaTableOffset);
211
212 size_t hmtxTableOffset = SkEndian_SwapBE32(hmtxTableEntry->offset);
213 SkOTTableHorizontalMetrics* hmtxTable =
214 SkTAddOffset<SkOTTableHorizontalMetrics>(sfntHeader, hmtxTableOffset);
215
216 size_t locaTableOffset = SkEndian_SwapBE32(locaTableEntry->offset);
217 SkOTTableIndexToLocation* locaTable =
218 SkTAddOffset<SkOTTableIndexToLocation>(sfntHeader, locaTableOffset);
219
220 size_t maxpTableOffset = SkEndian_SwapBE32(maxpTableEntry->offset);
221 SkOTTableMaximumProfile* maxpTable =
222 SkTAddOffset<SkOTTableMaximumProfile>(sfntHeader, maxpTableOffset);
223
224 SkASSERT_RELEASE(SkEndian_SwapBE32(maxpTable->version.version) == 0x00010000);
225 int numGlyphs = SkEndian_SwapBE16(maxpTable->version.tt.numGlyphs);
226 SkASSERT_RELEASE(kGlyphID < numGlyphs);
227
228 int emSize = SkEndian_SwapBE16(headTable->unitsPerEm);
229 SkScalar toEm = emSize / kFontSize;
230
231 SkOTTableGlyph::Iterator glyphIter(*glyfTable, *locaTable, headTable->indexToLocFormat);
232 glyphIter.advance(kGlyphID);
233 SkOTTableGlyphData* glyphData = glyphIter.next();
234 if (glyphData) {
235 if (setPts) {
236 fPts[0].set((int16_t)SkEndian_SwapBE16(glyphData->xMin) / toEm,
237 (int16_t)SkEndian_SwapBE16(glyphData->yMin) / -toEm);
238 fPts[1].set((int16_t)SkEndian_SwapBE16(glyphData->xMax) / toEm,
239 (int16_t)SkEndian_SwapBE16(glyphData->yMax) / -toEm);
240 } else {
241 glyphData->xMin = SkEndian_SwapBE16(sk_float_saturate2int16( fPts[0].x()*toEm));
242 glyphData->yMin = SkEndian_SwapBE16(sk_float_saturate2int16(-fPts[0].y()*toEm));
243 glyphData->xMax = SkEndian_SwapBE16(sk_float_saturate2int16( fPts[1].x()*toEm));
244 glyphData->yMax = SkEndian_SwapBE16(sk_float_saturate2int16(-fPts[1].y()*toEm));
245 }
246
247 int contourCount = SkEndian_SwapBE16(glyphData->numberOfContours);
248 if (contourCount > 0) {
249 SK_OT_USHORT* endPtsOfContours = SkTAfter<SK_OT_USHORT>(glyphData);
250 SK_OT_USHORT* numInstructions = SkTAfter<SK_OT_USHORT>(endPtsOfContours,
251 contourCount);
252 SK_OT_BYTE* instructions = SkTAfter<SK_OT_BYTE>(numInstructions);
253 SkOTTableGlyphData::Simple::Flags* flags =
254 SkTAfter<SkOTTableGlyphData::Simple::Flags>(
255 instructions, SkEndian_SwapBE16(*numInstructions));
256
257 int numResultPoints = SkEndian_SwapBE16(endPtsOfContours[contourCount-1]) + 1;
258 struct Coordinate {
259 SkOTTableGlyphData::Simple::Flags* flags;
260 size_t offsetToXDelta;
261 size_t xDeltaSize;
262 size_t offsetToYDelta;
263 size_t yDeltaSize;
264 };
265 std::vector<Coordinate> coordinates(numResultPoints);
266
267 size_t offsetToXDelta = 0;
268 size_t offsetToYDelta = 0;
269 SkOTTableGlyphData::Simple::Flags* currentFlags = flags;
270 for (int i = 0; i < numResultPoints; ++i) {
271 SkOTTableGlyphData::Simple::Flags* nextFlags;
272 int times = 1;
273 if (currentFlags->field.Repeat) {
274 SK_OT_BYTE* repeat = SkTAfter<SK_OT_BYTE>(currentFlags);
275 times += *repeat;
276 nextFlags = SkTAfter<SkOTTableGlyphData::Simple::Flags>(repeat);
277 } else {
278 nextFlags = SkTAfter<SkOTTableGlyphData::Simple::Flags>(currentFlags);
279 }
280
281 --i;
282 for (int time = 0; time < times; ++time) {
283 ++i;
284 coordinates[i].flags = currentFlags;
285 coordinates[i].offsetToXDelta = offsetToXDelta;
286 coordinates[i].offsetToYDelta = offsetToYDelta;
287
288 if (currentFlags->field.xShortVector) {
289 offsetToXDelta += 1;
290 coordinates[i].xDeltaSize = 1;
291 } else if (currentFlags->field.xIsSame_xShortVectorPositive) {
292 offsetToXDelta += 0;
293 if (i == 0) {
294 coordinates[i].xDeltaSize = 0;
295 } else {
296 coordinates[i].xDeltaSize = coordinates[i-1].xDeltaSize;
297 }
298 } else {
299 offsetToXDelta += 2;
300 coordinates[i].xDeltaSize = 2;
301 }
302
303 if (currentFlags->field.yShortVector) {
304 offsetToYDelta += 1;
305 coordinates[i].yDeltaSize = 1;
306 } else if (currentFlags->field.yIsSame_yShortVectorPositive) {
307 offsetToYDelta += 0;
308 if (i == 0) {
309 coordinates[i].yDeltaSize = 0;
310 } else {
311 coordinates[i].yDeltaSize = coordinates[i-1].yDeltaSize;
312 }
313 } else {
314 offsetToYDelta += 2;
315 coordinates[i].yDeltaSize = 2;
316 }
317 }
318 currentFlags = nextFlags;
319 }
320 SK_OT_BYTE* xCoordinates = reinterpret_cast<SK_OT_BYTE*>(currentFlags);
321 SK_OT_BYTE* yCoordinates = xCoordinates + offsetToXDelta;
322
323 int pointIndex = 0;
324 if (coordinates[pointIndex].xDeltaSize == 0) {
325 // Zero delta relative to the origin. There is no data to modify.
326 SkDebugf("Failed to move point in X at all.\n");
327 } else if (coordinates[pointIndex].xDeltaSize == 1) {
328 ShortCoordinate x = sk_float_saturate2sm8(fPts[3].x()*toEm);
329 xCoordinates[coordinates[pointIndex].offsetToXDelta] = x.magnitude;
330 coordinates[pointIndex].flags->field.xIsSame_xShortVectorPositive = !x.negative;
331 } else {
332 *reinterpret_cast<SK_OT_SHORT*>(xCoordinates + coordinates[pointIndex].offsetToXDelta) =
333 SkEndian_SwapBE16(sk_float_saturate2int16(fPts[3].x()*toEm));
334 }
335
336 if (coordinates[pointIndex].yDeltaSize == 0) {
337 // Zero delta relative to the origin. There is no data to modify.
338 SkDebugf("Failed to move point in Y at all.\n");
339 } else if (coordinates[pointIndex].yDeltaSize == 1) {
340 ShortCoordinate y = sk_float_saturate2sm8(-fPts[3].y()*toEm);
341 yCoordinates[coordinates[pointIndex].offsetToYDelta] = y.magnitude;
342 coordinates[pointIndex].flags->field.yIsSame_yShortVectorPositive = !y.negative;
343 } else {
344 *reinterpret_cast<SK_OT_SHORT*>(yCoordinates + coordinates[pointIndex].offsetToYDelta) =
345 SkEndian_SwapBE16(sk_float_saturate2int16(-fPts[3].y()*toEm));
346 }
347 }
348 }
349
350 int numberOfFullMetrics = SkEndian_SwapBE16(hheaTable->numberOfHMetrics);
351 SkOTTableHorizontalMetrics::FullMetric* fullMetrics = hmtxTable->longHorMetric;
352 SK_OT_SHORT lsb = SkEndian_SwapBE16(sk_float_saturate2int16(fPts[2].x()*toEm));
353 if (kGlyphID < numberOfFullMetrics) {
354 if (setPts) {
355 fPts[2].fX = (int16_t)SkEndian_SwapBE16(fullMetrics[kGlyphID].lsb) / toEm;
356 } else {
357 fullMetrics[kGlyphID].lsb = lsb;
358 }
359 } else {
360 SkOTTableHorizontalMetrics::ShortMetric* shortMetrics =
361 SkTAfter<SkOTTableHorizontalMetrics::ShortMetric>(fullMetrics, numberOfFullMetrics);
362 int shortMetricIndex = kGlyphID - numberOfFullMetrics;
363 if (setPts) {
364 fPts[2].fX = (int16_t)SkEndian_SwapBE16(shortMetrics[shortMetricIndex].lsb) / toEm;
365 } else {
366 shortMetrics[shortMetricIndex].lsb = lsb;
367 }
368 }
369
370 headTable->flags.field.LeftSidebearingAtX0 = false;
371 return dataCopy;
372 }
373 };
374 } // namespace
375 DEF_SLIDE( return new SBIXSlide(); )
376