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