1 /*
2 * Copyright 2015 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 #ifndef SkFindAndPositionGlyph_DEFINED
9 #define SkFindAndPositionGlyph_DEFINED
10
11 #include "SkArenaAlloc.h"
12 #include "SkAutoKern.h"
13 #include "SkGlyph.h"
14 #include "SkGlyphCache.h"
15 #include "SkMatrixPriv.h"
16 #include "SkPaint.h"
17 #include "SkTemplates.h"
18 #include "SkUtils.h"
19 #include <utility>
20
21 class SkFindAndPlaceGlyph {
22 public:
23 template<typename ProcessOneGlyph>
24 static void ProcessText(
25 SkPaint::TextEncoding, const char text[], size_t byteLength,
26 SkPoint offset, const SkMatrix& matrix, SkPaint::Align textAlignment,
27 SkGlyphCache* cache, ProcessOneGlyph&& processOneGlyph);
28 // ProcessPosText handles all cases for finding and positioning glyphs. It has a very large
29 // multiplicity. It figures out the glyph, position and rounding and pass those parameters to
30 // processOneGlyph.
31 //
32 // The routine processOneGlyph passed in by the client has the following signature:
33 // void f(const SkGlyph& glyph, SkPoint position, SkPoint rounding);
34 //
35 // * Sub-pixel positioning (2) - use sub-pixel positioning.
36 // * Text alignment (3) - text alignment with respect to the glyph's width.
37 // * Matrix type (3) - special cases for translation and X-coordinate scaling.
38 // * Components per position (2) - the positions vector can have a common Y with different
39 // Xs, or XY-pairs.
40 // * Axis Alignment (for sub-pixel positioning) (3) - when using sub-pixel positioning, round
41 // to a whole coordinate instead of using sub-pixel positioning.
42 // The number of variations is 108 for sub-pixel and 36 for full-pixel.
43 // This routine handles all of them using inline polymorphic variable (no heap allocation).
44 template<typename ProcessOneGlyph>
45 static void ProcessPosText(
46 SkPaint::TextEncoding, const char text[], size_t byteLength,
47 SkPoint offset, const SkMatrix& matrix, const SkScalar pos[], int scalarsPerPosition,
48 SkPaint::Align textAlignment,
49 SkGlyphCache* cache, ProcessOneGlyph&& processOneGlyph);
50
51 private:
52 // GlyphFinderInterface is the polymorphic base for classes that parse a stream of chars into
53 // the right UniChar (or GlyphID) and lookup up the glyph on the cache. The concrete
54 // implementations are: Utf8GlyphFinder, Utf16GlyphFinder, Utf32GlyphFinder,
55 // and GlyphIdGlyphFinder.
56 class GlyphFinderInterface {
57 public:
~GlyphFinderInterface()58 virtual ~GlyphFinderInterface() {}
59 virtual const SkGlyph& lookupGlyph(const char** text) = 0;
60 virtual const SkGlyph& lookupGlyphXY(const char** text, SkFixed x, SkFixed y) = 0;
61 };
62
63 class UtfNGlyphFinder : public GlyphFinderInterface {
64 public:
UtfNGlyphFinder(SkGlyphCache * cache)65 explicit UtfNGlyphFinder(SkGlyphCache* cache)
66 : fCache(cache) {
67 SkASSERT(cache != nullptr);
68 }
69
lookupGlyph(const char ** text)70 const SkGlyph& lookupGlyph(const char** text) override {
71 SkASSERT(text != nullptr);
72 return fCache->getUnicharMetrics(nextUnichar(text));
73 }
lookupGlyphXY(const char ** text,SkFixed x,SkFixed y)74 const SkGlyph& lookupGlyphXY(const char** text, SkFixed x, SkFixed y) override {
75 SkASSERT(text != nullptr);
76 return fCache->getUnicharMetrics(nextUnichar(text), x, y);
77 }
78
79 private:
80 virtual SkUnichar nextUnichar(const char** text) = 0;
81 SkGlyphCache* fCache;
82 };
83
84 class Utf8GlyphFinder final : public UtfNGlyphFinder {
85 public:
Utf8GlyphFinder(SkGlyphCache * cache)86 explicit Utf8GlyphFinder(SkGlyphCache* cache) : UtfNGlyphFinder(cache) { }
87
88 private:
nextUnichar(const char ** text)89 SkUnichar nextUnichar(const char** text) override { return SkUTF8_NextUnichar(text); }
90 };
91
92 class Utf16GlyphFinder final : public UtfNGlyphFinder {
93 public:
Utf16GlyphFinder(SkGlyphCache * cache)94 explicit Utf16GlyphFinder(SkGlyphCache* cache) : UtfNGlyphFinder(cache) { }
95
96 private:
nextUnichar(const char ** text)97 SkUnichar nextUnichar(const char** text) override {
98 return SkUTF16_NextUnichar((const uint16_t**)text);
99 }
100 };
101
102 class Utf32GlyphFinder final : public UtfNGlyphFinder {
103 public:
Utf32GlyphFinder(SkGlyphCache * cache)104 explicit Utf32GlyphFinder(SkGlyphCache* cache) : UtfNGlyphFinder(cache) { }
105
106 private:
nextUnichar(const char ** text)107 SkUnichar nextUnichar(const char** text) override {
108 const int32_t* ptr = *(const int32_t**)text;
109 SkUnichar uni = *ptr++;
110 *text = (const char*)ptr;
111 return uni;
112 }
113 };
114
115 class GlyphIdGlyphFinder final : public GlyphFinderInterface {
116 public:
GlyphIdGlyphFinder(SkGlyphCache * cache)117 explicit GlyphIdGlyphFinder(SkGlyphCache* cache)
118 : fCache(cache) {
119 SkASSERT(cache != nullptr);
120 }
121
lookupGlyph(const char ** text)122 const SkGlyph& lookupGlyph(const char** text) override {
123 return fCache->getGlyphIDMetrics(nextGlyphId(text));
124 }
lookupGlyphXY(const char ** text,SkFixed x,SkFixed y)125 const SkGlyph& lookupGlyphXY(const char** text, SkFixed x, SkFixed y) override {
126 return fCache->getGlyphIDMetrics(nextGlyphId(text), x, y);
127 }
128
129 private:
nextGlyphId(const char ** text)130 uint16_t nextGlyphId(const char** text) {
131 SkASSERT(text != nullptr);
132
133 const uint16_t* ptr = *(const uint16_t**)text;
134 uint16_t glyphID = *ptr;
135 ptr += 1;
136 *text = (const char*)ptr;
137 return glyphID;
138 }
139 SkGlyphCache* fCache;
140 };
141
getGlyphFinder(SkArenaAlloc * arena,SkPaint::TextEncoding encoding,SkGlyphCache * cache)142 static GlyphFinderInterface* getGlyphFinder(
143 SkArenaAlloc* arena, SkPaint::TextEncoding encoding, SkGlyphCache* cache) {
144 switch(encoding) {
145 case SkPaint::kUTF8_TextEncoding:
146 return arena->make<Utf8GlyphFinder>(cache);
147 case SkPaint::kUTF16_TextEncoding:
148 return arena->make<Utf16GlyphFinder>(cache);
149 case SkPaint::kUTF32_TextEncoding:
150 return arena->make<Utf32GlyphFinder>(cache);
151 case SkPaint::kGlyphID_TextEncoding:
152 return arena->make<GlyphIdGlyphFinder>(cache);
153 }
154 SK_ABORT("Should not get here.");
155 return nullptr;
156 }
157
158 // PositionReaderInterface reads a point from the pos vector.
159 // * HorizontalPositions - assumes a common Y for many X values.
160 // * ArbitraryPositions - a list of (X,Y) pairs.
161 class PositionReaderInterface {
162 public:
~PositionReaderInterface()163 virtual ~PositionReaderInterface() { }
164 virtual SkPoint nextPoint() = 0;
165 };
166
167 class HorizontalPositions final : public PositionReaderInterface {
168 public:
HorizontalPositions(const SkScalar * positions)169 explicit HorizontalPositions(const SkScalar* positions)
170 : fPositions(positions) { }
171
nextPoint()172 SkPoint nextPoint() override {
173 SkScalar x = *fPositions++;
174 return {x, 0};
175 }
176
177 private:
178 const SkScalar* fPositions;
179 };
180
181 class ArbitraryPositions final : public PositionReaderInterface {
182 public:
ArbitraryPositions(const SkScalar * positions)183 explicit ArbitraryPositions(const SkScalar* positions)
184 : fPositions(positions) { }
185
nextPoint()186 SkPoint nextPoint() override {
187 SkPoint to_return{fPositions[0], fPositions[1]};
188 fPositions += 2;
189 return to_return;
190 }
191
192 private:
193 const SkScalar* fPositions;
194 };
195
196 // MapperInterface given a point map it through the matrix. There are several shortcut
197 // variants.
198 // * TranslationMapper - assumes a translation only matrix.
199 // * XScaleMapper - assumes an X scaling and a translation.
200 // * GeneralMapper - Does all other matricies.
201 class MapperInterface {
202 public:
~MapperInterface()203 virtual ~MapperInterface() { }
204
205 virtual SkPoint map(SkPoint position) const = 0;
206 };
207
208 class TranslationMapper final : public MapperInterface {
209 public:
TranslationMapper(const SkMatrix & matrix,const SkPoint origin)210 TranslationMapper(const SkMatrix& matrix, const SkPoint origin)
211 : fTranslate(matrix.mapXY(origin.fX, origin.fY)) { }
212
map(SkPoint position)213 SkPoint map(SkPoint position) const override {
214 return position + fTranslate;
215 }
216
217 private:
218 const SkPoint fTranslate;
219 };
220
221 class XScaleMapper final : public MapperInterface {
222 public:
XScaleMapper(const SkMatrix & matrix,const SkPoint origin)223 XScaleMapper(const SkMatrix& matrix, const SkPoint origin)
224 : fTranslate(matrix.mapXY(origin.fX, origin.fY)), fXScale(matrix.getScaleX()) { }
225
map(SkPoint position)226 SkPoint map(SkPoint position) const override {
227 return {fXScale * position.fX + fTranslate.fX, fTranslate.fY};
228 }
229
230 private:
231 const SkPoint fTranslate;
232 const SkScalar fXScale;
233 };
234
235 // The caller must keep matrix alive while this class is used.
236 class GeneralMapper final : public MapperInterface {
237 public:
GeneralMapper(const SkMatrix & matrix,const SkPoint origin)238 GeneralMapper(const SkMatrix& matrix, const SkPoint origin)
239 : fOrigin(origin), fMatrix(matrix), fMapProc(SkMatrixPriv::GetMapXYProc(matrix)) { }
240
map(SkPoint position)241 SkPoint map(SkPoint position) const override {
242 SkPoint result;
243 fMapProc(fMatrix, position.fX + fOrigin.fX, position.fY + fOrigin.fY, &result);
244 return result;
245 }
246
247 private:
248 const SkPoint fOrigin;
249 const SkMatrix& fMatrix;
250 const SkMatrixPriv::MapXYProc fMapProc;
251 };
252
253 // TextAlignmentAdjustment handles shifting the glyph based on its width.
TextAlignmentAdjustment(SkPaint::Align textAlignment,const SkGlyph & glyph)254 static SkPoint TextAlignmentAdjustment(SkPaint::Align textAlignment, const SkGlyph& glyph) {
255 switch (textAlignment) {
256 case SkPaint::kLeft_Align:
257 return {0.0f, 0.0f};
258 case SkPaint::kCenter_Align:
259 return {SkFloatToScalar(glyph.fAdvanceX) / 2,
260 SkFloatToScalar(glyph.fAdvanceY) / 2};
261 case SkPaint::kRight_Align:
262 return {SkFloatToScalar(glyph.fAdvanceX),
263 SkFloatToScalar(glyph.fAdvanceY)};
264 }
265 // Even though the entire enum is covered above, MVSC doesn't think so. Make it happy.
266 SK_ABORT("Should never get here.");
267 return {0.0f, 0.0f};
268 }
269
270 // The "call" to SkFixedToScalar is actually a macro. It's macros all the way down.
271 // Needs to be a macro because you can't have a const float unless you make it constexpr.
272 #define kSubpixelRounding (SkFixedToScalar(SkGlyph::kSubpixelRound))
273
274 // The SubpixelPositionRounding function returns a point suitable for rounding a sub-pixel
275 // positioned glyph.
SubpixelPositionRounding(SkAxisAlignment axisAlignment)276 static SkPoint SubpixelPositionRounding(SkAxisAlignment axisAlignment) {
277 switch (axisAlignment) {
278 case kX_SkAxisAlignment:
279 return {kSubpixelRounding, SK_ScalarHalf};
280 case kY_SkAxisAlignment:
281 return {SK_ScalarHalf, kSubpixelRounding};
282 case kNone_SkAxisAlignment:
283 return {kSubpixelRounding, kSubpixelRounding};
284 }
285 SK_ABORT("Should not get here.");
286 return {0.0f, 0.0f};
287 }
288
289 // The SubpixelAlignment function produces a suitable position for the glyph cache to
290 // produce the correct sub-pixel alignment. If a position is aligned with an axis a shortcut
291 // of 0 is used for the sub-pixel position.
SubpixelAlignment(SkAxisAlignment axisAlignment,SkPoint position)292 static SkIPoint SubpixelAlignment(SkAxisAlignment axisAlignment, SkPoint position) {
293 // Only the fractional part of position.fX and position.fY matter, because the result of
294 // this function will just be passed to FixedToSub.
295 switch (axisAlignment) {
296 case kX_SkAxisAlignment:
297 return {SkScalarToFixed(SkScalarFraction(position.fX) + kSubpixelRounding), 0};
298 case kY_SkAxisAlignment:
299 return {0, SkScalarToFixed(SkScalarFraction(position.fY) + kSubpixelRounding)};
300 case kNone_SkAxisAlignment:
301 return {SkScalarToFixed(SkScalarFraction(position.fX) + kSubpixelRounding),
302 SkScalarToFixed(SkScalarFraction(position.fY) + kSubpixelRounding)};
303 }
304 SK_ABORT("Should not get here.");
305 return {0, 0};
306 }
307
308 #undef kSubpixelRounding
309
310 // GlyphFindAndPlaceInterface given the text and position finds the correct glyph and does
311 // glyph specific position adjustment. The findAndPositionGlyph method takes text and
312 // position and calls processOneGlyph with the correct glyph, final position and rounding
313 // terms. The final position is not rounded yet and is the responsibility of processOneGlyph.
314 template<typename ProcessOneGlyph>
315 class GlyphFindAndPlaceInterface : SkNoncopyable {
316 public:
~GlyphFindAndPlaceInterface()317 virtual ~GlyphFindAndPlaceInterface() { }
318
319 // findAndPositionGlyph calculates the position of the glyph, finds the glyph, and
320 // returns the position of where the next glyph will be using the glyph's advance and
321 // possibly kerning. The returned position is used by drawText, but ignored by drawPosText.
322 // The compiler should prune all this calculation if the return value is not used.
323 //
324 // This should be a pure virtual, but some versions of GCC <= 4.8 have a bug that causes a
325 // compile error.
326 // See GCC bug: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=60277
findAndPositionGlyph(const char ** text,SkPoint position,ProcessOneGlyph && processOneGlyph)327 virtual SkPoint findAndPositionGlyph(
328 const char** text, SkPoint position, ProcessOneGlyph&& processOneGlyph) {
329 SK_ABORT("Should never get here.");
330 return {0.0f, 0.0f};
331 }
332 };
333
334 // GlyphFindAndPlaceSubpixel handles finding and placing glyphs when sub-pixel positioning is
335 // requested. After it has found and placed the glyph it calls the templated function
336 // ProcessOneGlyph in order to actually perform an action.
337 template<typename ProcessOneGlyph, SkPaint::Align kTextAlignment,
338 SkAxisAlignment kAxisAlignment>
339 class GlyphFindAndPlaceSubpixel final : public GlyphFindAndPlaceInterface<ProcessOneGlyph> {
340 public:
GlyphFindAndPlaceSubpixel(GlyphFinderInterface * glyphFinder)341 explicit GlyphFindAndPlaceSubpixel(GlyphFinderInterface* glyphFinder)
342 : fGlyphFinder(glyphFinder) { }
343
findAndPositionGlyph(const char ** text,SkPoint position,ProcessOneGlyph && processOneGlyph)344 SkPoint findAndPositionGlyph(
345 const char** text, SkPoint position, ProcessOneGlyph&& processOneGlyph) override {
346
347 if (kTextAlignment != SkPaint::kLeft_Align) {
348 // Get the width of an un-sub-pixel positioned glyph for calculating the
349 // alignment. This is not needed for kLeftAlign because its adjustment is
350 // always {0, 0}.
351 const char* tempText = *text;
352 const SkGlyph &metricGlyph = fGlyphFinder->lookupGlyph(&tempText);
353
354 if (metricGlyph.fWidth <= 0) {
355 // Exiting early, be sure to update text pointer.
356 *text = tempText;
357 return position + SkPoint{SkFloatToScalar(metricGlyph.fAdvanceX),
358 SkFloatToScalar(metricGlyph.fAdvanceY)};
359 }
360
361 // Adjust the final position by the alignment adjustment.
362 position -= TextAlignmentAdjustment(kTextAlignment, metricGlyph);
363 }
364
365 // Find the glyph.
366 SkIPoint lookupPosition = SkScalarsAreFinite(position.fX, position.fY)
367 ? SubpixelAlignment(kAxisAlignment, position)
368 : SkIPoint{0, 0};
369 const SkGlyph& renderGlyph =
370 fGlyphFinder->lookupGlyphXY(text, lookupPosition.fX, lookupPosition.fY);
371
372 // If the glyph has no width (no pixels) then don't bother processing it.
373 if (renderGlyph.fWidth > 0) {
374 processOneGlyph(renderGlyph, position,
375 SubpixelPositionRounding(kAxisAlignment));
376 }
377 return position + SkPoint{SkFloatToScalar(renderGlyph.fAdvanceX),
378 SkFloatToScalar(renderGlyph.fAdvanceY)};
379 }
380
381 private:
382 GlyphFinderInterface* fGlyphFinder;
383 };
384
385 enum SelectKerning {
386 kNoKerning = false,
387 kUseKerning = true
388 };
389
390 // GlyphFindAndPlaceFullPixel handles finding and placing glyphs when no sub-pixel
391 // positioning is requested. The kUseKerning argument should be true for drawText, and false
392 // for drawPosText.
393 template<typename ProcessOneGlyph, SkPaint::Align kTextAlignment, SelectKerning kUseKerning>
394 class GlyphFindAndPlaceFullPixel final : public GlyphFindAndPlaceInterface<ProcessOneGlyph> {
395 public:
GlyphFindAndPlaceFullPixel(GlyphFinderInterface * glyphFinder)396 explicit GlyphFindAndPlaceFullPixel(GlyphFinderInterface* glyphFinder)
397 : fGlyphFinder(glyphFinder) {
398 // Kerning can only be used with SkPaint::kLeft_Align
399 static_assert(!kUseKerning || SkPaint::kLeft_Align == kTextAlignment,
400 "Kerning can only be used with left aligned text.");
401 }
402
findAndPositionGlyph(const char ** text,SkPoint position,ProcessOneGlyph && processOneGlyph)403 SkPoint findAndPositionGlyph(
404 const char** text, SkPoint position, ProcessOneGlyph&& processOneGlyph) override {
405 SkPoint finalPosition = position;
406 const SkGlyph& glyph = fGlyphFinder->lookupGlyph(text);
407 if (kUseKerning) {
408 finalPosition += {fAutoKern.adjust(glyph), 0.0f};
409 }
410 if (glyph.fWidth > 0) {
411 finalPosition -= TextAlignmentAdjustment(kTextAlignment, glyph);
412 processOneGlyph(glyph, finalPosition, {SK_ScalarHalf, SK_ScalarHalf});
413 }
414 return finalPosition + SkPoint{SkFloatToScalar(glyph.fAdvanceX),
415 SkFloatToScalar(glyph.fAdvanceY)};
416 }
417
418 private:
419 GlyphFinderInterface* fGlyphFinder;
420
421 SkAutoKern fAutoKern;
422 };
423
424 template <typename ProcessOneGlyph, SkPaint::Align kTextAlignment>
getSubpixel(SkArenaAlloc * arena,SkAxisAlignment axisAlignment,GlyphFinderInterface * glyphFinder)425 static GlyphFindAndPlaceInterface<ProcessOneGlyph>* getSubpixel(
426 SkArenaAlloc* arena, SkAxisAlignment axisAlignment, GlyphFinderInterface* glyphFinder)
427 {
428 switch (axisAlignment) {
429 case kX_SkAxisAlignment:
430 return arena->make<GlyphFindAndPlaceSubpixel<
431 ProcessOneGlyph, kTextAlignment, kX_SkAxisAlignment>>(glyphFinder);
432 case kNone_SkAxisAlignment:
433 return arena->make<GlyphFindAndPlaceSubpixel<
434 ProcessOneGlyph, kTextAlignment, kNone_SkAxisAlignment>>(glyphFinder);
435 case kY_SkAxisAlignment:
436 return arena->make<GlyphFindAndPlaceSubpixel<
437 ProcessOneGlyph, kTextAlignment, kY_SkAxisAlignment>>(glyphFinder);
438 }
439 SK_ABORT("Should never get here.");
440 return nullptr;
441 }
442
MeasureText(GlyphFinderInterface * glyphFinder,const char text[],size_t byteLength)443 static SkPoint MeasureText(
444 GlyphFinderInterface* glyphFinder, const char text[], size_t byteLength) {
445 SkScalar x = 0, y = 0;
446 const char* stop = text + byteLength;
447
448 SkAutoKern autokern;
449
450 while (text < stop) {
451 // don't need x, y here, since all subpixel variants will have the
452 // same advance
453 const SkGlyph& glyph = glyphFinder->lookupGlyph(&text);
454
455 x += autokern.adjust(glyph) + SkFloatToScalar(glyph.fAdvanceX);
456 y += SkFloatToScalar(glyph.fAdvanceY);
457 }
458 SkASSERT(text == stop);
459 return {x, y};
460 }
461 };
462
463 template<typename ProcessOneGlyph>
ProcessPosText(SkPaint::TextEncoding textEncoding,const char text[],size_t byteLength,SkPoint offset,const SkMatrix & matrix,const SkScalar pos[],int scalarsPerPosition,SkPaint::Align textAlignment,SkGlyphCache * cache,ProcessOneGlyph && processOneGlyph)464 inline void SkFindAndPlaceGlyph::ProcessPosText(
465 SkPaint::TextEncoding textEncoding, const char text[], size_t byteLength,
466 SkPoint offset, const SkMatrix& matrix, const SkScalar pos[], int scalarsPerPosition,
467 SkPaint::Align textAlignment,
468 SkGlyphCache* cache, ProcessOneGlyph&& processOneGlyph) {
469
470 SkAxisAlignment axisAlignment = cache->getScalerContext()->computeAxisAlignmentForHText();
471 uint32_t mtype = matrix.getType();
472
473 // Specialized code for handling the most common case for blink.
474 if (textEncoding == SkPaint::kGlyphID_TextEncoding
475 && textAlignment == SkPaint::kLeft_Align
476 && axisAlignment == kX_SkAxisAlignment
477 && cache->isSubpixel()
478 && mtype <= SkMatrix::kTranslate_Mask)
479 {
480 GlyphIdGlyphFinder glyphFinder(cache);
481 using Positioner =
482 GlyphFindAndPlaceSubpixel <
483 ProcessOneGlyph, SkPaint::kLeft_Align, kX_SkAxisAlignment>;
484 HorizontalPositions hPositions{pos};
485 ArbitraryPositions aPositions{pos};
486 PositionReaderInterface* positions = nullptr;
487 if (scalarsPerPosition == 2) {
488 positions = &aPositions;
489 } else {
490 positions = &hPositions;
491 }
492 TranslationMapper mapper{matrix, offset};
493 Positioner positioner(&glyphFinder);
494 const char* cursor = text;
495 const char* stop = text + byteLength;
496 while (cursor < stop) {
497 SkPoint mappedPoint = mapper.TranslationMapper::map(positions->nextPoint());
498 positioner.Positioner::findAndPositionGlyph(
499 &cursor, mappedPoint, std::forward<ProcessOneGlyph>(processOneGlyph));
500 }
501 return;
502 }
503
504 SkSTArenaAlloc<120> arena;
505
506 GlyphFinderInterface* glyphFinder = getGlyphFinder(&arena, textEncoding, cache);
507
508 PositionReaderInterface* positionReader = nullptr;
509 if (2 == scalarsPerPosition) {
510 positionReader = arena.make<ArbitraryPositions>(pos);
511 } else {
512 positionReader = arena.make<HorizontalPositions>(pos);
513 }
514
515 MapperInterface* mapper = nullptr;
516 if (mtype & (SkMatrix::kAffine_Mask | SkMatrix::kPerspective_Mask)
517 || scalarsPerPosition == 2) {
518 mapper = arena.make<GeneralMapper>(matrix, offset);
519 } else if (mtype & SkMatrix::kScale_Mask) {
520 mapper = arena.make<XScaleMapper>(matrix, offset);
521 } else {
522 mapper = arena.make<TranslationMapper>(matrix, offset);
523 }
524
525 GlyphFindAndPlaceInterface<ProcessOneGlyph>* findAndPosition = nullptr;
526 if (cache->isSubpixel()) {
527 switch (textAlignment) {
528 case SkPaint::kLeft_Align:
529 findAndPosition = getSubpixel<ProcessOneGlyph, SkPaint::kLeft_Align>(
530 &arena, axisAlignment, glyphFinder);
531 break;
532 case SkPaint::kCenter_Align:
533 findAndPosition = getSubpixel<ProcessOneGlyph, SkPaint::kCenter_Align>(
534 &arena, axisAlignment, glyphFinder);
535 break;
536 case SkPaint::kRight_Align:
537 findAndPosition = getSubpixel<ProcessOneGlyph, SkPaint::kRight_Align>(
538 &arena, axisAlignment, glyphFinder);
539 break;
540 }
541 } else {
542 switch (textAlignment) {
543 case SkPaint::kLeft_Align:
544 findAndPosition = arena.make<
545 GlyphFindAndPlaceFullPixel<ProcessOneGlyph,
546 SkPaint::kLeft_Align, kNoKerning>>(glyphFinder);
547 break;
548 case SkPaint::kCenter_Align:
549 findAndPosition = arena.make<
550 GlyphFindAndPlaceFullPixel<ProcessOneGlyph,
551 SkPaint::kCenter_Align, kNoKerning>>(glyphFinder);
552 break;
553 case SkPaint::kRight_Align:
554 findAndPosition = arena.make<
555 GlyphFindAndPlaceFullPixel<ProcessOneGlyph,
556 SkPaint::kRight_Align, kNoKerning>>(glyphFinder);
557 break;
558 }
559 }
560
561 const char* stop = text + byteLength;
562 while (text < stop) {
563 SkPoint mappedPoint = mapper->map(positionReader->nextPoint());
564 findAndPosition->findAndPositionGlyph(
565 &text, mappedPoint, std::forward<ProcessOneGlyph>(processOneGlyph));
566 }
567 }
568
569 template<typename ProcessOneGlyph>
ProcessText(SkPaint::TextEncoding textEncoding,const char text[],size_t byteLength,SkPoint offset,const SkMatrix & matrix,SkPaint::Align textAlignment,SkGlyphCache * cache,ProcessOneGlyph && processOneGlyph)570 inline void SkFindAndPlaceGlyph::ProcessText(
571 SkPaint::TextEncoding textEncoding, const char text[], size_t byteLength,
572 SkPoint offset, const SkMatrix& matrix, SkPaint::Align textAlignment,
573 SkGlyphCache* cache, ProcessOneGlyph&& processOneGlyph) {
574 SkSTArenaAlloc<64> arena;
575
576 // transform the starting point
577 matrix.mapPoints(&offset, 1);
578
579 GlyphFinderInterface* glyphFinder = getGlyphFinder(&arena, textEncoding, cache);
580
581 // need to measure first
582 if (textAlignment != SkPaint::kLeft_Align) {
583 SkVector stop = MeasureText(glyphFinder, text, byteLength);
584
585 if (textAlignment == SkPaint::kCenter_Align) {
586 stop *= SK_ScalarHalf;
587 }
588 offset -= stop;
589 }
590
591 GlyphFindAndPlaceInterface<ProcessOneGlyph>* findAndPosition = nullptr;
592 if (cache->isSubpixel()) {
593 SkAxisAlignment axisAlignment = cache->getScalerContext()->computeAxisAlignmentForHText();
594 findAndPosition = getSubpixel<ProcessOneGlyph, SkPaint::kLeft_Align>(
595 &arena, axisAlignment, glyphFinder);
596 } else {
597 using FullPixel =
598 GlyphFindAndPlaceFullPixel<ProcessOneGlyph, SkPaint::kLeft_Align, kUseKerning>;
599 findAndPosition = arena.make<FullPixel>(glyphFinder);
600 }
601
602 const char* stop = text + byteLength;
603 SkPoint current = offset;
604 while (text < stop) {
605 current =
606 findAndPosition->findAndPositionGlyph(
607 &text, current, std::forward<ProcessOneGlyph>(processOneGlyph));
608
609 }
610 }
611
612 #endif // SkFindAndPositionGlyph_DEFINED
613