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