xref: /aosp_15_r20/external/skia/tools/viewer/SBIXSlide.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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