Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
SBIXSlide.cpp
Go to the documentation of this file.
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
10#include "include/core/SkFont.h"
16#include "src/base/SkTime.h"
24#include "tools/Resources.h"
28
29namespace {
30
31constexpr SkScalar DX = 100;
32constexpr SkScalar DY = 300;
33constexpr int kPointSize = 5;
34constexpr SkScalar kFontSize = 200;
35
36constexpr char kFontFile[] = "fonts/sbix_uncompressed_flags.ttf";
37constexpr SkGlyphID kGlyphID = 2;
38
39//constexpr char kFontFile[] = "fonts/HangingS.ttf";
40//constexpr SkGlyphID kGlyphID = 4;
41
42/**
43 * Return the closest int for the given float. Returns SK_MaxS32FitsInFloat for NaN.
44 */
45static inline int16_t sk_float_saturate2int16(float x) {
46 x = x < SK_MaxS16 ? x : SK_MaxS16;
47 x = x > SK_MinS16 ? x : SK_MinS16;
48 return (int16_t)x;
49}
50
51struct ShortCoordinate { bool negative; uint8_t magnitude; };
52static inline ShortCoordinate sk_float_saturate2sm8(float x) {
53 bool negative = x < 0;
54 x = x < 255 ? x : 255;
55 x = x > -255 ? x : -255;
56 return ShortCoordinate{ negative, negative ? (uint8_t)-x : (uint8_t)x };
57}
58
59struct SBIXSlide : public ClickHandlerSlide {
60 struct Point {
61 SkPoint location;
63 } fPts[4] = {
64 {{0, 0}, SK_ColorBLACK }, // glyph x/y min
65 {{0, 0}, SK_ColorWHITE }, // glyph x/y max
66 {{0, 20}, SK_ColorGREEN }, // lsb (x only, y ignored)
67 {{0, 0}, SK_ColorBLUE }, // first point of glyph contour
68 };
69 static constexpr const int kGlyfXYMin = 0;
70 static constexpr const int kGlyfXYMax = 1;
71 static constexpr const int kGlyfLSB = 2;
72 static constexpr const int kGlyfFirstPoint = 3;
73
74 std::vector<sk_sp<SkFontMgr>> fFontMgr;
75 std::vector<SkFont> fFonts;
76 sk_sp<SkData> fSBIXData;
77 bool fInputChanged = false;
78 bool fDirty = true;
79
80public:
81 SBIXSlide() { fName = "SBIX"; }
82
83 void load(SkScalar w, SkScalar h) override {
84 fFontMgr.emplace_back(ToolUtils::TestFontMgr());
85 //fFontMgr.emplace_back(SkFontMgr_New_Custom_Empty());
86 // GetResourceAsData may be backed by a read only file mapping.
87 // For sanity always make a copy.
88 fSBIXData = GetResourceAsData(kFontFile);
89
90 updateSBIXData(fSBIXData.get(), true);
91 }
92
93 void draw(SkCanvas* canvas) override {
94 canvas->clear(SK_ColorGRAY);
95
96 canvas->translate(DX, DY);
97
99 SkPoint position{0, 0};
100 SkPoint origin{0, 0};
101
102 if (fDirty) {
103 sk_sp<SkData> data(updateSBIXData(fSBIXData.get(), false));
104 fFonts.clear();
105 for (auto&& fontmgr : fFontMgr) {
106 fFonts.emplace_back(fontmgr->makeFromData(data), kFontSize);
107 }
108 fDirty = false;
109 }
110 for (auto&& font : fFonts) {
111 paint.setStyle(SkPaint::kFill_Style);
112 paint.setColor(SK_ColorBLACK);
113 canvas->drawGlyphs(1, &kGlyphID, &position, origin, font, paint);
114
115 paint.setStrokeWidth(SkIntToScalar(kPointSize / 2));
117 SkScalar advance;
118 SkRect rect;
119 font.getWidthsBounds(&kGlyphID, 1, &advance, &rect, &paint);
120
121 paint.setColor(SK_ColorRED);
122 canvas->drawRect(rect, paint);
123 paint.setColor(SK_ColorGREEN);
124 canvas->drawLine(0, 0, advance, 0, paint);
125 paint.setColor(SK_ColorRED);
126 canvas->drawPoint(0, 0, paint);
127 canvas->drawPoint(advance, 0, paint);
128
129 paint.setStrokeWidth(SkIntToScalar(kPointSize));
130 for (auto&& pt : fPts) {
131 paint.setColor(pt.color);
132 canvas->drawPoints(SkCanvas::kPoints_PointMode, 1, &pt.location, paint);
133 }
134
135 canvas->translate(kFontSize, 0);
136 }
137 }
138
139protected:
140 static bool hittest(const SkPoint& pt, SkScalar x, SkScalar y) {
141 return SkPoint::Length(pt.fX - x, pt.fY - y) < SkIntToScalar(kPointSize);
142 }
143
144 Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey modi) override {
145 x -= DX;
146 y -= DY;
147 for (size_t i = 0; i < std::size(fPts); ++i) {
148 if (hittest(fPts[i].location, x, y)) {
149 return new PtClick((int)i);
150 }
151 }
152 return nullptr;
153 }
154
155 bool onClick(Click* click) override {
156 fPts[((PtClick*)click)->fIndex].location.set(click->fCurr.fX - DX, click->fCurr.fY - DY);
157 fDirty = true;
158 return true;
159 }
160
161private:
162 class PtClick : public Click {
163 public:
164 int fIndex;
165 PtClick(int index) : fIndex(index) {}
166 };
167
168 sk_sp<SkData> updateSBIXData(SkData* originalData, bool setPts) {
169 // Lots of unlikely to be aligned pointers in here, which is UB. Total hack.
170
171 sk_sp<SkData> dataCopy = SkData::MakeWithCopy(originalData->data(), originalData->size());
172
173 SkSFNTHeader* sfntHeader = static_cast<SkSFNTHeader*>(dataCopy->writable_data());
174
175 SkASSERT_RELEASE(memcmp(sfntHeader, originalData->data(), originalData->size()) == 0);
176
178 SkTAfter<SkSFNTHeader::TableDirectoryEntry>(sfntHeader);
179 SkSFNTHeader::TableDirectoryEntry* glyfTableEntry = nullptr;
180 SkSFNTHeader::TableDirectoryEntry* headTableEntry = nullptr;
181 SkSFNTHeader::TableDirectoryEntry* hheaTableEntry = nullptr;
182 SkSFNTHeader::TableDirectoryEntry* hmtxTableEntry = nullptr;
183 SkSFNTHeader::TableDirectoryEntry* locaTableEntry = nullptr;
184 SkSFNTHeader::TableDirectoryEntry* maxpTableEntry = nullptr;
185 int numTables = SkEndian_SwapBE16(sfntHeader->numTables);
186 for (int tableEntryIndex = 0; tableEntryIndex < numTables; ++tableEntryIndex) {
187 if (SkOTTableGlyph::TAG == tableEntry[tableEntryIndex].tag) {
188 glyfTableEntry = tableEntry + tableEntryIndex;
189 }
190 if (SkOTTableHead::TAG == tableEntry[tableEntryIndex].tag) {
191 headTableEntry = tableEntry + tableEntryIndex;
192 }
193 if (SkOTTableHorizontalHeader::TAG == tableEntry[tableEntryIndex].tag) {
194 hheaTableEntry = tableEntry + tableEntryIndex;
195 }
196 if (SkOTTableHorizontalMetrics::TAG == tableEntry[tableEntryIndex].tag) {
197 hmtxTableEntry = tableEntry + tableEntryIndex;
198 }
199 if (SkOTTableIndexToLocation::TAG == tableEntry[tableEntryIndex].tag) {
200 locaTableEntry = tableEntry + tableEntryIndex;
201 }
202 if (SkOTTableMaximumProfile::TAG == tableEntry[tableEntryIndex].tag) {
203 maxpTableEntry = tableEntry + tableEntryIndex;
204 }
205 }
206 SkASSERT_RELEASE(glyfTableEntry);
207 SkASSERT_RELEASE(headTableEntry);
208 SkASSERT_RELEASE(hheaTableEntry);
209 SkASSERT_RELEASE(hmtxTableEntry);
210 SkASSERT_RELEASE(locaTableEntry);
211 SkASSERT_RELEASE(maxpTableEntry);
212
213 size_t glyfTableOffset = SkEndian_SwapBE32(glyfTableEntry->offset);
214 SkOTTableGlyph* glyfTable =
215 SkTAddOffset<SkOTTableGlyph>(sfntHeader, glyfTableOffset);
216
217 size_t headTableOffset = SkEndian_SwapBE32(headTableEntry->offset);
218 SkOTTableHead* headTable =
219 SkTAddOffset<SkOTTableHead>(sfntHeader, headTableOffset);
220
221 size_t hheaTableOffset = SkEndian_SwapBE32(hheaTableEntry->offset);
222 SkOTTableHorizontalHeader* hheaTable =
223 SkTAddOffset<SkOTTableHorizontalHeader>(sfntHeader, hheaTableOffset);
224
225 size_t hmtxTableOffset = SkEndian_SwapBE32(hmtxTableEntry->offset);
226 SkOTTableHorizontalMetrics* hmtxTable =
227 SkTAddOffset<SkOTTableHorizontalMetrics>(sfntHeader, hmtxTableOffset);
228
229 size_t locaTableOffset = SkEndian_SwapBE32(locaTableEntry->offset);
230 SkOTTableIndexToLocation* locaTable =
231 SkTAddOffset<SkOTTableIndexToLocation>(sfntHeader, locaTableOffset);
232
233 size_t maxpTableOffset = SkEndian_SwapBE32(maxpTableEntry->offset);
234 SkOTTableMaximumProfile* maxpTable =
235 SkTAddOffset<SkOTTableMaximumProfile>(sfntHeader, maxpTableOffset);
236
237 SkASSERT_RELEASE(SkEndian_SwapBE32(maxpTable->version.version) == 0x00010000);
238 int numGlyphs = SkEndian_SwapBE16(maxpTable->version.tt.numGlyphs);
239 SkASSERT_RELEASE(kGlyphID < numGlyphs);
240
241 int emSize = SkEndian_SwapBE16(headTable->unitsPerEm);
242 SkScalar toEm = emSize / kFontSize;
243
244 SkOTTableGlyph::Iterator glyphIter(*glyfTable, *locaTable, headTable->indexToLocFormat);
245 glyphIter.advance(kGlyphID);
246 SkOTTableGlyphData* glyphData = glyphIter.next();
247 if (glyphData) {
248 if (setPts) {
249 fPts[kGlyfXYMin].location.set((int16_t)SkEndian_SwapBE16(glyphData->xMin) / toEm,
250 (int16_t)SkEndian_SwapBE16(glyphData->yMin) / -toEm);
251 fPts[kGlyfXYMax].location.set((int16_t)SkEndian_SwapBE16(glyphData->xMax) / toEm,
252 (int16_t)SkEndian_SwapBE16(glyphData->yMax) / -toEm);
253 } else {
254 glyphData->xMin = SkEndian_SwapBE16(sk_float_saturate2int16( fPts[kGlyfXYMin].location.x()*toEm));
255 glyphData->yMin = SkEndian_SwapBE16(sk_float_saturate2int16(-fPts[kGlyfXYMin].location.y()*toEm));
256 glyphData->xMax = SkEndian_SwapBE16(sk_float_saturate2int16( fPts[kGlyfXYMax].location.x()*toEm));
257 glyphData->yMax = SkEndian_SwapBE16(sk_float_saturate2int16(-fPts[kGlyfXYMax].location.y()*toEm));
258 }
259
260 int contourCount = SkEndian_SwapBE16(glyphData->numberOfContours);
261 if (contourCount > 0) {
262 SK_OT_USHORT* endPtsOfContours = SkTAfter<SK_OT_USHORT>(glyphData);
263 SK_OT_USHORT* numInstructions = SkTAfter<SK_OT_USHORT>(endPtsOfContours,
264 contourCount);
265 SK_OT_BYTE* instructions = SkTAfter<SK_OT_BYTE>(numInstructions);
267 SkTAfter<SkOTTableGlyphData::Simple::Flags>(
268 instructions, SkEndian_SwapBE16(*numInstructions));
269
270 int numResultPoints = SkEndian_SwapBE16(endPtsOfContours[contourCount-1]) + 1;
271 struct Coordinate {
273 size_t offsetToXDelta;
274 size_t xDeltaSize;
275 size_t offsetToYDelta;
276 size_t yDeltaSize;
277 };
278 std::vector<Coordinate> coordinates(numResultPoints);
279
280 size_t offsetToXDelta = 0;
281 size_t offsetToYDelta = 0;
283 for (int i = 0; i < numResultPoints; ++i) {
285 int times = 1;
286 if (currentFlags->field.Repeat) {
287 SK_OT_BYTE* repeat = SkTAfter<SK_OT_BYTE>(currentFlags);
288 times += *repeat;
289 nextFlags = SkTAfter<SkOTTableGlyphData::Simple::Flags>(repeat);
290 } else {
291 nextFlags = SkTAfter<SkOTTableGlyphData::Simple::Flags>(currentFlags);
292 }
293
294 --i;
295 for (int time = 0; time < times; ++time) {
296 ++i;
297 coordinates[i].flags = currentFlags;
298 coordinates[i].offsetToXDelta = offsetToXDelta;
299 coordinates[i].offsetToYDelta = offsetToYDelta;
300
301 if (currentFlags->field.xShortVector) {
302 offsetToXDelta += 1;
303 coordinates[i].xDeltaSize = 1;
304 } else if (currentFlags->field.xIsSame_xShortVectorPositive) {
305 offsetToXDelta += 0;
306 if (i == 0) {
307 coordinates[i].xDeltaSize = 0;
308 } else {
309 coordinates[i].xDeltaSize = coordinates[i-1].xDeltaSize;
310 }
311 } else {
312 offsetToXDelta += 2;
313 coordinates[i].xDeltaSize = 2;
314 }
315
316 if (currentFlags->field.yShortVector) {
317 offsetToYDelta += 1;
318 coordinates[i].yDeltaSize = 1;
319 } else if (currentFlags->field.yIsSame_yShortVectorPositive) {
320 offsetToYDelta += 0;
321 if (i == 0) {
322 coordinates[i].yDeltaSize = 0;
323 } else {
324 coordinates[i].yDeltaSize = coordinates[i-1].yDeltaSize;
325 }
326 } else {
327 offsetToYDelta += 2;
328 coordinates[i].yDeltaSize = 2;
329 }
330 }
331 currentFlags = nextFlags;
332 }
333 SK_OT_BYTE* xCoordinates = reinterpret_cast<SK_OT_BYTE*>(currentFlags);
334 SK_OT_BYTE* yCoordinates = xCoordinates + offsetToXDelta;
335
336 int pointIndex = 0;
337 if (coordinates[pointIndex].xDeltaSize == 0) {
338 // Zero delta relative to the origin. There is no data to modify.
339 SkDebugf("Failed to move point in X at all.\n");
340 } else if (coordinates[pointIndex].xDeltaSize == 1) {
341 ShortCoordinate x = sk_float_saturate2sm8(fPts[kGlyfFirstPoint].location.x()*toEm);
342 xCoordinates[coordinates[pointIndex].offsetToXDelta] = x.magnitude;
343 coordinates[pointIndex].flags->field.xIsSame_xShortVectorPositive = !x.negative;
344 } else {
345 *reinterpret_cast<SK_OT_SHORT*>(xCoordinates + coordinates[pointIndex].offsetToXDelta) =
346 SkEndian_SwapBE16(sk_float_saturate2int16(fPts[kGlyfFirstPoint].location.x()*toEm));
347 }
348
349 if (coordinates[pointIndex].yDeltaSize == 0) {
350 // Zero delta relative to the origin. There is no data to modify.
351 SkDebugf("Failed to move point in Y at all.\n");
352 } else if (coordinates[pointIndex].yDeltaSize == 1) {
353 ShortCoordinate y = sk_float_saturate2sm8(-fPts[kGlyfFirstPoint].location.y()*toEm);
354 yCoordinates[coordinates[pointIndex].offsetToYDelta] = y.magnitude;
355 coordinates[pointIndex].flags->field.yIsSame_yShortVectorPositive = !y.negative;
356 } else {
357 *reinterpret_cast<SK_OT_SHORT*>(yCoordinates + coordinates[pointIndex].offsetToYDelta) =
358 SkEndian_SwapBE16(sk_float_saturate2int16(-fPts[kGlyfFirstPoint].location.y()*toEm));
359 }
360 }
361 }
362
363 int numberOfFullMetrics = SkEndian_SwapBE16(hheaTable->numberOfHMetrics);
365 SK_OT_SHORT lsb = SkEndian_SwapBE16(sk_float_saturate2int16(fPts[kGlyfLSB].location.x()*toEm));
366 if (kGlyphID < numberOfFullMetrics) {
367 if (setPts) {
368 fPts[kGlyfLSB].location.fX = (int16_t)SkEndian_SwapBE16(fullMetrics[kGlyphID].lsb) / toEm;
369 } else {
370 fullMetrics[kGlyphID].lsb = lsb;
371 }
372 } else {
374 SkTAfter<SkOTTableHorizontalMetrics::ShortMetric>(fullMetrics, numberOfFullMetrics);
375 int shortMetricIndex = kGlyphID - numberOfFullMetrics;
376 if (setPts) {
377 fPts[kGlyfLSB].location.fX = (int16_t)SkEndian_SwapBE16(shortMetrics[shortMetricIndex].lsb) / toEm;
378 } else {
379 shortMetrics[shortMetricIndex].lsb = lsb;
380 }
381 }
382
383 headTable->flags.field.LeftSidebearingAtX0 = false;
384 return dataCopy;
385 }
386};
387} // namespace
388DEF_SLIDE( return new SBIXSlide(); )
SkPoint fPts[2]
static SkISize times(const SkISize &size, float factor)
const char * fName
SkColor4f color
static bool hittest(const SkPoint &target, SkScalar x, SkScalar y)
sk_sp< SkData > GetResourceAsData(const char *resource)
Definition Resources.cpp:42
#define SkASSERT_RELEASE(cond)
Definition SkAssert.h:100
static unsigned repeat(SkFixed fx, int max)
uint32_t SkColor
Definition SkColor.h:37
constexpr SkColor SK_ColorGRAY
Definition SkColor.h:113
constexpr SkColor SK_ColorBLUE
Definition SkColor.h:135
constexpr SkColor SK_ColorRED
Definition SkColor.h:126
constexpr SkColor SK_ColorBLACK
Definition SkColor.h:103
constexpr SkColor SK_ColorGREEN
Definition SkColor.h:131
constexpr SkColor SK_ColorWHITE
Definition SkColor.h:122
void SK_SPI SkDebugf(const char format[],...) SK_PRINTF_LIKE(1
#define SkEndian_SwapBE32(n)
Definition SkEndian.h:136
#define SkEndian_SwapBE16(n)
Definition SkEndian.h:135
@ kGlyphID
uses two byte words to represent glyph indices
static constexpr int16_t SK_MaxS16
Definition SkMath.h:18
static constexpr int16_t SK_MinS16
Definition SkMath.h:19
uint8_t SK_OT_BYTE
uint16_t SK_OT_USHORT
uint16_t SK_OT_SHORT
#define SkIntToScalar(x)
Definition SkScalar.h:57
uint16_t SkGlyphID
Definition SkTypes.h:179
#define DEF_SLIDE(code)
Definition Slide.h:25
virtual bool onClick(Click *)=0
virtual Click * onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey modi)=0
void drawRect(const SkRect &rect, const SkPaint &paint)
void drawPoints(PointMode mode, size_t count, const SkPoint pts[], const SkPaint &paint)
void drawPoint(SkScalar x, SkScalar y, const SkPaint &paint)
void translate(SkScalar dx, SkScalar dy)
void drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1, const SkPaint &paint)
void drawGlyphs(int count, const SkGlyphID glyphs[], const SkPoint positions[], const uint32_t clusters[], int textByteCount, const char utf8text[], SkPoint origin, const SkFont &font, const SkPaint &paint)
void clear(SkColor color)
Definition SkCanvas.h:1199
@ kPoints_PointMode
draw each point separately
Definition SkCanvas.h:1241
const void * data() const
Definition SkData.h:37
static sk_sp< SkData > MakeWithCopy(const void *data, size_t length)
Definition SkData.cpp:111
size_t size() const
Definition SkData.h:30
@ kStroke_Style
set to stroke geometry
Definition SkPaint.h:194
@ kFill_Style
set to fill geometry
Definition SkPaint.h:193
virtual void load(SkScalar winWidth, SkScalar winHeight)
Definition Slide.h:40
virtual void draw(SkCanvas *canvas)=0
T * get() const
Definition SkRefCnt.h:303
const Paint & paint
float SkScalar
Definition extension.cpp:12
FlutterSemanticsFlag flags
double y
double x
sk_sp< SkBlender > blender SkRect rect
Definition SkRecords.h:350
sk_sp< SkFontMgr > TestFontMgr()
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot data
Definition switches.h:41
font
Font Metadata and Metrics.
TPoint< Scalar > Point
Definition point.h:316
constexpr int kPointSize
ModifierKey
Definition ModifierKey.h:9
SkScalar w
SkScalar h
SK_OT_SHORT numberOfContours
static const SK_OT_ULONG TAG
struct SkOTTableHead::IndexToLocFormat indexToLocFormat
static const SK_OT_ULONG TAG
union SkOTTableHead::Flags flags
SK_OT_USHORT unitsPerEm
static const SK_OT_ULONG TAG
static const SK_OT_ULONG TAG
struct SkOTTableHorizontalMetrics::FullMetric longHorMetric[1/*hhea::numberOfHMetrics */]
static const SK_OT_ULONG TAG
union SkOTTableMaximumProfile::Version version
static const SK_OT_ULONG TAG
float fX
x-axis value
void set(float x, float y)
static float Length(float x, float y)
Definition SkPoint.cpp:79
float fY
y-axis value
SK_SFNT_USHORT numTables
struct SkOTTableGlyphData::Simple::Flags::Field field
struct SkOTTableHead::Flags::Field field
SkOTTableMaximumProfile::Version::TT tt