blob: bd6338d587448af77766122327e1236e6f531b99 [file] [log] [blame]
herbe5911c92015-11-09 13:15:21 -08001/*
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
herbe59124e2015-11-18 10:54:39 -080011#include "SkAutoKern.h"
herbe5911c92015-11-09 13:15:21 -080012#include "SkGlyph.h"
13#include "SkGlyphCache.h"
14#include "SkPaint.h"
15#include "SkTemplates.h"
herb4c11b3f2015-11-20 13:53:12 -080016#include "SkUtils.h"
herbe5911c92015-11-09 13:15:21 -080017
18// Calculate a type with the same size as the max of all the Ts.
19// This must be top level because the is no specialization of inner classes.
20template<typename... Ts> struct SkMaxSizeOf;
21
22template<>
23struct SkMaxSizeOf<> {
24 static const size_t value = 0;
25};
26
27template<typename H, typename... Ts>
28struct SkMaxSizeOf<H, Ts...> {
29 static const size_t value =
30 sizeof(H) >= SkMaxSizeOf<Ts...>::value ? sizeof(H) : SkMaxSizeOf<Ts...>::value;
31};
32
herbd4c24f62015-12-07 12:12:29 -080033
34// This is a temporary helper function to work around a bug in the code generation
35// for aarch64 (arm) on GCC 4.9. This bug does not show up on other platforms, so it
36// seems to be an aarch64 backend problem.
37//
38// GCC 4.9 on ARM64 does not generate the proper constructor code for PositionReader or
39// GlyphFindAndPlace. The vtable is not set properly without adding the fixme code.
40// The implementation is in SkDraw.cpp.
41extern void FixGCC49Arm64Bug(int v);
42
herbe5911c92015-11-09 13:15:21 -080043class SkFindAndPlaceGlyph {
44public:
herbe59124e2015-11-18 10:54:39 -080045 template<typename ProcessOneGlyph>
herb4c11b3f2015-11-20 13:53:12 -080046 static void ProcessText(
47 SkPaint::TextEncoding, const char text[], size_t byteLength,
48 SkPoint offset, const SkMatrix& matrix, SkPaint::Align textAlignment,
49 SkGlyphCache* cache, ProcessOneGlyph&& processOneGlyph);
herbe5911c92015-11-09 13:15:21 -080050 // ProcessPosText handles all cases for finding and positioning glyphs. It has a very large
51 // multiplicity. It figures out the glyph, position and rounding and pass those parameters to
52 // processOneGlyph.
53 //
54 // The routine processOneGlyph passed in by the client has the following signature:
55 // void f(const SkGlyph& glyph, SkPoint position, SkPoint rounding);
56 //
57 // * Sub-pixel positioning (2) - use sub-pixel positioning.
58 // * Text alignment (3) - text alignment with respect to the glyph's width.
59 // * Matrix type (3) - special cases for translation and X-coordinate scaling.
60 // * Components per position (2) - the positions vector can have a common Y with different
61 // Xs, or XY-pairs.
62 // * Axis Alignment (for sub-pixel positioning) (3) - when using sub-pixel positioning, round
63 // to a whole coordinate instead of using sub-pixel positioning.
64 // The number of variations is 108 for sub-pixel and 36 for full-pixel.
65 // This routine handles all of them using inline polymorphic variable (no heap allocation).
66 template<typename ProcessOneGlyph>
herb4c11b3f2015-11-20 13:53:12 -080067 static void ProcessPosText(
68 SkPaint::TextEncoding, const char text[], size_t byteLength,
69 SkPoint offset, const SkMatrix& matrix, const SkScalar pos[], int scalarsPerPosition,
70 SkPaint::Align textAlignment,
71 SkGlyphCache* cache, ProcessOneGlyph&& processOneGlyph);
herbe5911c92015-11-09 13:15:21 -080072
herbe5911c92015-11-09 13:15:21 -080073private:
74 // UntaggedVariant is a pile of memory that can hold one of the Ts. It provides a way
75 // to initialize that memory in a typesafe way.
76 template<typename... Ts>
77 class UntaggedVariant {
78 public:
79 UntaggedVariant() { }
80
81 ~UntaggedVariant() { }
82 UntaggedVariant(const UntaggedVariant&) = delete;
83 UntaggedVariant& operator=(const UntaggedVariant&) = delete;
84 UntaggedVariant(UntaggedVariant&&) = delete;
85 UntaggedVariant& operator=(UntaggedVariant&&) = delete;
86
87 template<typename Variant, typename... Args>
88 void initialize(Args&&... args) {
89 SkASSERT(sizeof(Variant) <= sizeof(fSpace));
90 #if defined(_MSC_VER) && _MSC_VER < 1900
91 #define alignof __alignof
92 #endif
93 SkASSERT(alignof(Variant) <= alignof(Space));
94 new(&fSpace) Variant(skstd::forward<Args>(args)...);
95 }
96
97 private:
98 typedef SkAlignedSStorage<SkMaxSizeOf<Ts...>::value> Space;
99 Space fSpace;
100 };
101
102 // PolymorphicVariant holds subclasses of Base without slicing. Ts must be subclasses of Base.
103 template<typename Base, typename... Ts>
104 class PolymorphicVariant {
105 public:
106 typedef UntaggedVariant<Ts...> Variants;
107
108 template<typename Initializer>
109 PolymorphicVariant(Initializer&& initializer) {
110 initializer(&fVariants);
111 }
112 ~PolymorphicVariant() { get()->~Base(); }
113 Base* get() const { return reinterpret_cast<Base*>(&fVariants); }
114 Base* operator->() const { return get(); }
115 Base& operator*() const { return *get(); }
116
117 private:
118 mutable Variants fVariants;
119 };
120
herb4c11b3f2015-11-20 13:53:12 -0800121 // GlyphFinderInterface is the polymorphic base for classes that parse a stream of chars into
122 // the right UniChar (or GlyphID) and lookup up the glyph on the cache. The concrete
123 // implementations are: Utf8GlyphFinder, Utf16GlyphFinder, Utf32GlyphFinder,
124 // and GlyphIdGlyphFinder.
125 class GlyphFinderInterface {
126 public:
127 virtual ~GlyphFinderInterface() {}
128 virtual const SkGlyph& lookupGlyph(const char** text) = 0;
129 virtual const SkGlyph& lookupGlyphXY(const char** text, SkFixed x, SkFixed y) = 0;
130 };
131
132 class UtfNGlyphFinder : public GlyphFinderInterface {
133 public:
134 UtfNGlyphFinder(SkGlyphCache* cache) : fCache(cache) { SkASSERT(cache != nullptr); }
135
136 const SkGlyph& lookupGlyph(const char** text) override {
137 SkASSERT(text != nullptr);
138 return fCache->getUnicharMetrics(nextUnichar(text));
139 }
140 const SkGlyph& lookupGlyphXY(const char** text, SkFixed x, SkFixed y) override {
141 SkASSERT(text != nullptr);
142 return fCache->getUnicharMetrics(nextUnichar(text), x, y);
143 }
144
145 private:
146 virtual SkUnichar nextUnichar(const char** text) = 0;
147 SkGlyphCache* fCache;
148 };
149
150 class Utf8GlyphFinder final : public UtfNGlyphFinder {
151 public:
152 Utf8GlyphFinder(SkGlyphCache* cache) : UtfNGlyphFinder(cache) { }
153
154 private:
155 SkUnichar nextUnichar(const char** text) override { return SkUTF8_NextUnichar(text); }
156 };
157
158 class Utf16GlyphFinder final : public UtfNGlyphFinder {
159 public:
160 Utf16GlyphFinder(SkGlyphCache* cache) : UtfNGlyphFinder(cache) { }
161
162 private:
163 SkUnichar nextUnichar(const char** text) override {
164 return SkUTF16_NextUnichar((const uint16_t**)text);
165 }
166 };
167
168 class Utf32GlyphFinder final : public UtfNGlyphFinder {
169 public:
170 Utf32GlyphFinder(SkGlyphCache* cache) : UtfNGlyphFinder(cache) { }
171
172 private:
173 SkUnichar nextUnichar(const char** text) override {
174 const int32_t* ptr = *(const int32_t**)text;
175 SkUnichar uni = *ptr++;
176 *text = (const char*)ptr;
177 return uni;
178 }
179 };
180
181 class GlyphIdGlyphFinder final : public GlyphFinderInterface {
182 public:
183 GlyphIdGlyphFinder(SkGlyphCache* cache) : fCache(cache) { SkASSERT(cache != nullptr); }
184
185 const SkGlyph& lookupGlyph(const char** text) override {
186 return fCache->getGlyphIDMetrics(nextGlyphId(text));
187 }
188 const SkGlyph& lookupGlyphXY(const char** text, SkFixed x, SkFixed y) override {
189 return fCache->getGlyphIDMetrics(nextGlyphId(text), x, y);
190 }
191
192 private:
193 uint16_t nextGlyphId(const char** text) {
194 SkASSERT(text != nullptr);
195
196 const uint16_t* ptr = *(const uint16_t**)text;
197 uint16_t glyphID = *ptr;
198 ptr += 1;
199 *text = (const char*)ptr;
200 return glyphID;
201 }
202 SkGlyphCache* fCache;
203 };
204
205 typedef PolymorphicVariant<
206 GlyphFinderInterface,
207 Utf8GlyphFinder,
208 Utf16GlyphFinder,
209 Utf32GlyphFinder,
210 GlyphIdGlyphFinder> LookupGlyphVariant;
211
212 class LookupGlyph : public LookupGlyphVariant {
213 public:
214 LookupGlyph(SkPaint::TextEncoding encoding, SkGlyphCache* cache)
215 : LookupGlyphVariant(
216 [&](LookupGlyphVariant::Variants* to_init) {
217 switch(encoding) {
218 case SkPaint::kUTF8_TextEncoding:
219 to_init->initialize<Utf8GlyphFinder>(cache);
220 break;
221 case SkPaint::kUTF16_TextEncoding:
222 to_init->initialize<Utf16GlyphFinder>(cache);
223 break;
224 case SkPaint::kUTF32_TextEncoding:
225 to_init->initialize<Utf32GlyphFinder>(cache);
226 break;
227 case SkPaint::kGlyphID_TextEncoding:
228 to_init->initialize<GlyphIdGlyphFinder>(cache);
229 break;
230 }
231 }
232 ) { }
233 };
234
herbe5911c92015-11-09 13:15:21 -0800235 // PositionReaderInterface reads a point from the pos vector.
236 // * HorizontalPositions - assumes a common Y for many X values.
237 // * ArbitraryPositions - a list of (X,Y) pairs.
238 class PositionReaderInterface {
239 public:
240 virtual ~PositionReaderInterface() { }
241 virtual SkPoint nextPoint() = 0;
herbd4c24f62015-12-07 12:12:29 -0800242 // This is only here to fix a GCC 4.9 aarch64 code gen bug.
243 // See comment at the top of the file.
244 virtual int forceUseForBug() = 0;
herbe5911c92015-11-09 13:15:21 -0800245 };
246
247 class HorizontalPositions final : public PositionReaderInterface {
248 public:
249 explicit HorizontalPositions(const SkScalar* positions)
250 : fPositions(positions) { }
251
252 SkPoint nextPoint() override {
253 SkScalar x = *fPositions++;
254 return {x, 0};
255 }
256
herbd4c24f62015-12-07 12:12:29 -0800257 int forceUseForBug() override { return 1; }
258
herbe5911c92015-11-09 13:15:21 -0800259 private:
260 const SkScalar* fPositions;
261 };
262
263 class ArbitraryPositions final : public PositionReaderInterface {
264 public:
265 explicit ArbitraryPositions(const SkScalar* positions)
266 : fPositions(positions) { }
267
268 SkPoint nextPoint() override {
269 SkPoint to_return{fPositions[0], fPositions[1]};
270 fPositions += 2;
271 return to_return;
272 }
273
herbd4c24f62015-12-07 12:12:29 -0800274 int forceUseForBug() override { return 2; }
275
herbe5911c92015-11-09 13:15:21 -0800276 private:
277 const SkScalar* fPositions;
278 };
279
280 typedef PolymorphicVariant<PositionReaderInterface, HorizontalPositions, ArbitraryPositions>
281 PositionReader;
282
herbe59124e2015-11-18 10:54:39 -0800283 // MapperInterface given a point map it through the matrix. There are several shortcut
284 // variants.
herbe5911c92015-11-09 13:15:21 -0800285 // * TranslationMapper - assumes a translation only matrix.
286 // * XScaleMapper - assumes an X scaling and a translation.
287 // * GeneralMapper - Does all other matricies.
288 class MapperInterface {
289 public:
290 virtual ~MapperInterface() { }
291
292 virtual SkPoint map(SkPoint position) const = 0;
293 };
294
295 class TranslationMapper final : public MapperInterface {
296 public:
297 TranslationMapper(const SkMatrix& matrix, const SkPoint origin)
298 : fTranslate(matrix.mapXY(origin.fX, origin.fY)) { }
299
300 SkPoint map(SkPoint position) const override {
301 return position + fTranslate;
302 }
303
304 private:
305 const SkPoint fTranslate;
306 };
307
308 class XScaleMapper final : public MapperInterface {
309 public:
310 XScaleMapper(const SkMatrix& matrix, const SkPoint origin)
311 : fTranslate(matrix.mapXY(origin.fX, origin.fY)), fXScale(matrix.getScaleX()) { }
312
313 SkPoint map(SkPoint position) const override {
314 return {fXScale * position.fX + fTranslate.fX, fTranslate.fY};
315 }
316
317 private:
318 const SkPoint fTranslate;
319 const SkScalar fXScale;
320 };
321
322 // The caller must keep matrix alive while this class is used.
323 class GeneralMapper final : public MapperInterface {
324 public:
325 GeneralMapper(const SkMatrix& matrix, const SkPoint origin)
326 : fOrigin(origin), fMatrix(matrix), fMapProc(matrix.getMapXYProc()) { }
327
328 SkPoint map(SkPoint position) const override {
329 SkPoint result;
330 fMapProc(fMatrix, position.fX + fOrigin.fX, position.fY + fOrigin.fY, &result);
331 return result;
332 }
333
334 private:
335 const SkPoint fOrigin;
336 const SkMatrix& fMatrix;
337 const SkMatrix::MapXYProc fMapProc;
338 };
339
340 typedef PolymorphicVariant<
341 MapperInterface, TranslationMapper, XScaleMapper, GeneralMapper> Mapper;
342
herbe59124e2015-11-18 10:54:39 -0800343 // TextAlignmentAdjustment handles shifting the glyph based on its width.
herbe5911c92015-11-09 13:15:21 -0800344 static SkPoint TextAlignmentAdjustment(SkPaint::Align textAlignment, const SkGlyph& glyph) {
345 switch (textAlignment) {
346 case SkPaint::kLeft_Align:
347 return {0.0f, 0.0f};
348 case SkPaint::kCenter_Align:
349 return {SkFixedToScalar(glyph.fAdvanceX >> 1),
350 SkFixedToScalar(glyph.fAdvanceY >> 1)};
351 case SkPaint::kRight_Align:
352 return {SkFixedToScalar(glyph.fAdvanceX),
353 SkFixedToScalar(glyph.fAdvanceY)};
354 }
355 // Even though the entire enum is covered above, MVSC doesn't think so. Make it happy.
356 SkFAIL("Should never get here.");
357 return {0.0f, 0.0f};
358 }
359
360 // The "call" to SkFixedToScalar is actually a macro. It's macros all the way down.
361 // Needs to be a macro because you can't have a const float unless you make it constexpr.
362 #define kSubpixelRounding (SkFixedToScalar(SkGlyph::kSubpixelRound))
363
herbe59124e2015-11-18 10:54:39 -0800364 // The SubpixelPositionRounding function returns a point suitable for rounding a sub-pixel
herbe5911c92015-11-09 13:15:21 -0800365 // positioned glyph.
366 static SkPoint SubpixelPositionRounding(SkAxisAlignment axisAlignment) {
367 switch (axisAlignment) {
368 case kX_SkAxisAlignment:
369 return {SkFixedToScalar(SkGlyph::kSubpixelRound), SK_ScalarHalf};
370 case kY_SkAxisAlignment:
371 return {SK_ScalarHalf, kSubpixelRounding};
372 case kNone_SkAxisAlignment:
373 return {kSubpixelRounding, kSubpixelRounding};
374 }
375 SkFAIL("Should not get here.");
376 return {0.0f, 0.0f};
377 }
378
herbe59124e2015-11-18 10:54:39 -0800379 // The SubpixelAlignment function produces a suitable position for the glyph cache to
herbe5911c92015-11-09 13:15:21 -0800380 // produce the correct sub-pixel alignment. If a position is aligned with an axis a shortcut
381 // of 0 is used for the sub-pixel position.
382 static SkIPoint SubpixelAlignment(SkAxisAlignment axisAlignment, SkPoint position) {
383 switch (axisAlignment) {
384 case kX_SkAxisAlignment:
385 return {SkScalarToFixed(position.fX + kSubpixelRounding), 0};
386 case kY_SkAxisAlignment:
387 return {0, SkScalarToFixed(position.fY + kSubpixelRounding)};
388 case kNone_SkAxisAlignment:
389 return {SkScalarToFixed(position.fX + kSubpixelRounding),
390 SkScalarToFixed(position.fY + kSubpixelRounding)};
391 }
392 SkFAIL("Should not get here.");
393 return {0, 0};
394 }
395
396 #undef kSubpixelRounding
397
398 // GlyphFindAndPlaceInterface given the text and position finds the correct glyph and does
399 // glyph specific position adjustment. The findAndPositionGlyph method takes text and
400 // position and calls processOneGlyph with the correct glyph, final position and rounding
401 // terms. The final position is not rounded yet and is the responsibility of processOneGlyph.
402 template<typename ProcessOneGlyph>
403 class GlyphFindAndPlaceInterface : SkNoncopyable {
404 public:
405 virtual ~GlyphFindAndPlaceInterface() { };
406
herbe59124e2015-11-18 10:54:39 -0800407 // findAndPositionGlyph calculates the position of the glyph, finds the glyph, and
408 // returns the position of where the next glyph will be using the glyph's advance and
409 // possibly kerning. The returned position is used by drawText, but ignored by drawPosText.
410 // The compiler should prune all this calculation if the return value is not used.
411 //
herbe5911c92015-11-09 13:15:21 -0800412 // This should be a pure virtual, but some versions of GCC <= 4.8 have a bug that causes a
413 // compile error.
414 // See GCC bug: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=60277
herbe59124e2015-11-18 10:54:39 -0800415 virtual SkPoint findAndPositionGlyph(
416 const char** text, SkPoint position, ProcessOneGlyph&& processOneGlyph) {
417 SkFAIL("Should never get here.");
418 return {0.0f, 0.0f};
419 };
herbe5911c92015-11-09 13:15:21 -0800420 };
421
422 // GlyphFindAndPlaceSubpixel handles finding and placing glyphs when sub-pixel positioning is
423 // requested. After it has found and placed the glyph it calls the templated function
424 // ProcessOneGlyph in order to actually perform an action.
herbe59124e2015-11-18 10:54:39 -0800425 template<typename ProcessOneGlyph, SkPaint::Align kTextAlignment,
426 SkAxisAlignment kAxisAlignment>
herbe5911c92015-11-09 13:15:21 -0800427 class GlyphFindAndPlaceSubpixel final : public GlyphFindAndPlaceInterface<ProcessOneGlyph> {
428 public:
herb4c11b3f2015-11-20 13:53:12 -0800429 GlyphFindAndPlaceSubpixel(LookupGlyph& glyphFinder)
herbd4c24f62015-12-07 12:12:29 -0800430 : fGlyphFinder(glyphFinder) {
431 FixGCC49Arm64Bug(1);
432 }
herbe5911c92015-11-09 13:15:21 -0800433
herbe59124e2015-11-18 10:54:39 -0800434 SkPoint findAndPositionGlyph(
435 const char** text, SkPoint position, ProcessOneGlyph&& processOneGlyph) override {
herbe5911c92015-11-09 13:15:21 -0800436 SkPoint finalPosition = position;
437 if (kTextAlignment != SkPaint::kLeft_Align) {
herbe59124e2015-11-18 10:54:39 -0800438 // Get the width of an un-sub-pixel positioned glyph for calculating the
439 // alignment. This is not needed for kLeftAlign because its adjustment is
440 // always {0, 0}.
herbe5911c92015-11-09 13:15:21 -0800441 const char* tempText = *text;
herb4c11b3f2015-11-20 13:53:12 -0800442 const SkGlyph &metricGlyph = fGlyphFinder->lookupGlyph(&tempText);
herbe5911c92015-11-09 13:15:21 -0800443
444 if (metricGlyph.fWidth <= 0) {
herb386127f2015-11-12 08:53:42 -0800445 // Exiting early, be sure to update text pointer.
446 *text = tempText;
herbe59124e2015-11-18 10:54:39 -0800447 return finalPosition + SkPoint{SkFixedToScalar(metricGlyph.fAdvanceX),
448 SkFixedToScalar(metricGlyph.fAdvanceY)};
herbe5911c92015-11-09 13:15:21 -0800449 }
450
451 // Adjust the final position by the alignment adjustment.
452 finalPosition -= TextAlignmentAdjustment(kTextAlignment, metricGlyph);
453 }
454
455 // Find the glyph.
456 SkIPoint lookupPosition = SubpixelAlignment(kAxisAlignment, finalPosition);
herbe59124e2015-11-18 10:54:39 -0800457 const SkGlyph& renderGlyph =
herb4c11b3f2015-11-20 13:53:12 -0800458 fGlyphFinder->lookupGlyphXY(text, lookupPosition.fX, lookupPosition.fY);
herbe5911c92015-11-09 13:15:21 -0800459
460 // If the glyph has no width (no pixels) then don't bother processing it.
461 if (renderGlyph.fWidth > 0) {
462 processOneGlyph(renderGlyph, finalPosition,
463 SubpixelPositionRounding(kAxisAlignment));
464 }
herbe59124e2015-11-18 10:54:39 -0800465 return finalPosition + SkPoint{SkFixedToScalar(renderGlyph.fAdvanceX),
466 SkFixedToScalar(renderGlyph.fAdvanceY)};
herbe5911c92015-11-09 13:15:21 -0800467 }
468
469 private:
herb4c11b3f2015-11-20 13:53:12 -0800470 LookupGlyph& fGlyphFinder;
herbe5911c92015-11-09 13:15:21 -0800471 };
472
herbe59124e2015-11-18 10:54:39 -0800473 enum SelectKerning {
474 kNoKerning = false,
475 kUseKerning = true
476 };
477
herbe5911c92015-11-09 13:15:21 -0800478 // GlyphFindAndPlaceFullPixel handles finding and placing glyphs when no sub-pixel
herbe59124e2015-11-18 10:54:39 -0800479 // positioning is requested. The kUseKerning argument should be true for drawText, and false
480 // for drawPosText.
481 template<typename ProcessOneGlyph, SkPaint::Align kTextAlignment, SelectKerning kUseKerning>
herbe5911c92015-11-09 13:15:21 -0800482 class GlyphFindAndPlaceFullPixel final : public GlyphFindAndPlaceInterface<ProcessOneGlyph> {
483 public:
herb4c11b3f2015-11-20 13:53:12 -0800484 GlyphFindAndPlaceFullPixel(LookupGlyph& glyphFinder)
485 : fGlyphFinder(glyphFinder) {
herbd4c24f62015-12-07 12:12:29 -0800486 FixGCC49Arm64Bug(2);
herbe59124e2015-11-18 10:54:39 -0800487 // Kerning can only be used with SkPaint::kLeft_Align
488 static_assert(!kUseKerning || SkPaint::kLeft_Align == kTextAlignment,
489 "Kerning can only be used with left aligned text.");
490 }
herbe5911c92015-11-09 13:15:21 -0800491
herbe59124e2015-11-18 10:54:39 -0800492 SkPoint findAndPositionGlyph(
493 const char** text, SkPoint position, ProcessOneGlyph&& processOneGlyph) override {
herbe5911c92015-11-09 13:15:21 -0800494 SkPoint finalPosition = position;
herb4c11b3f2015-11-20 13:53:12 -0800495 const SkGlyph& glyph = fGlyphFinder->lookupGlyph(text);
herbe59124e2015-11-18 10:54:39 -0800496 if (kUseKerning) {
497 finalPosition += {SkFixedToScalar(fAutoKern.adjust(glyph)), 0.0f};
herbe5911c92015-11-09 13:15:21 -0800498 }
herbe59124e2015-11-18 10:54:39 -0800499 if (glyph.fWidth > 0) {
500 finalPosition -= TextAlignmentAdjustment(kTextAlignment, glyph);
501 processOneGlyph(glyph, finalPosition, {SK_ScalarHalf, SK_ScalarHalf});
502 }
503 return finalPosition + SkPoint{SkFixedToScalar(glyph.fAdvanceX),
504 SkFixedToScalar(glyph.fAdvanceY)};
herbe5911c92015-11-09 13:15:21 -0800505 }
506
507 private:
herb4c11b3f2015-11-20 13:53:12 -0800508 LookupGlyph& fGlyphFinder;
509
herbe59124e2015-11-18 10:54:39 -0800510 SkAutoKern fAutoKern;
herbe5911c92015-11-09 13:15:21 -0800511 };
512
513 // GlyphFindAndPlace is a large variant that encapsulates the multiple types of finding and
514 // placing a glyph. There are three factors that go into the different factors.
515 // * Is sub-pixel positioned - a boolean that says whether to use sub-pixel positioning.
516 // * Text alignment - indicates if the glyph should be placed to the right, centered or left
517 // of a given position.
518 // * Axis alignment - indicates if the glyphs final sub-pixel position should be rounded to a
519 // whole pixel if the glyph is aligned with an axis. This is only used for sub-pixel
520 // positioning and allows the baseline to look crisp.
521 template<typename ProcessOneGlyph>
522 using GlyphFindAndPlace = PolymorphicVariant<
523 GlyphFindAndPlaceInterface<ProcessOneGlyph>,
524 // Subpixel
525 GlyphFindAndPlaceSubpixel<ProcessOneGlyph, SkPaint::kLeft_Align, kNone_SkAxisAlignment>,
526 GlyphFindAndPlaceSubpixel<ProcessOneGlyph, SkPaint::kLeft_Align, kX_SkAxisAlignment >,
527 GlyphFindAndPlaceSubpixel<ProcessOneGlyph, SkPaint::kLeft_Align, kY_SkAxisAlignment >,
528 GlyphFindAndPlaceSubpixel<ProcessOneGlyph, SkPaint::kCenter_Align, kNone_SkAxisAlignment>,
529 GlyphFindAndPlaceSubpixel<ProcessOneGlyph, SkPaint::kCenter_Align, kX_SkAxisAlignment >,
530 GlyphFindAndPlaceSubpixel<ProcessOneGlyph, SkPaint::kCenter_Align, kY_SkAxisAlignment >,
531 GlyphFindAndPlaceSubpixel<ProcessOneGlyph, SkPaint::kRight_Align, kNone_SkAxisAlignment>,
532 GlyphFindAndPlaceSubpixel<ProcessOneGlyph, SkPaint::kRight_Align, kX_SkAxisAlignment >,
533 GlyphFindAndPlaceSubpixel<ProcessOneGlyph, SkPaint::kRight_Align, kY_SkAxisAlignment >,
534 // Full pixel
herbe59124e2015-11-18 10:54:39 -0800535 GlyphFindAndPlaceFullPixel<ProcessOneGlyph, SkPaint::kLeft_Align, kNoKerning>,
536 GlyphFindAndPlaceFullPixel<ProcessOneGlyph, SkPaint::kCenter_Align, kNoKerning>,
537 GlyphFindAndPlaceFullPixel<ProcessOneGlyph, SkPaint::kRight_Align, kNoKerning>
herbe5911c92015-11-09 13:15:21 -0800538 >;
539
540 // InitSubpixel is a helper function for initializing all the variants of
541 // GlyphFindAndPlaceSubpixel.
542 template<typename ProcessOneGlyph, SkPaint::Align kTextAlignment>
543 static void InitSubpixel(
544 typename GlyphFindAndPlace<ProcessOneGlyph>::Variants* to_init,
545 SkAxisAlignment axisAlignment,
herb4c11b3f2015-11-20 13:53:12 -0800546 LookupGlyph& glyphFinder) {
herbe5911c92015-11-09 13:15:21 -0800547 switch (axisAlignment) {
548 case kX_SkAxisAlignment:
549 to_init->template initialize<GlyphFindAndPlaceSubpixel<
herb4c11b3f2015-11-20 13:53:12 -0800550 ProcessOneGlyph, kTextAlignment, kX_SkAxisAlignment>>(glyphFinder);
herbe5911c92015-11-09 13:15:21 -0800551 break;
552 case kNone_SkAxisAlignment:
553 to_init->template initialize<GlyphFindAndPlaceSubpixel<
herb4c11b3f2015-11-20 13:53:12 -0800554 ProcessOneGlyph, kTextAlignment, kNone_SkAxisAlignment>>(glyphFinder);
herbe5911c92015-11-09 13:15:21 -0800555 break;
556 case kY_SkAxisAlignment:
557 to_init->template initialize<GlyphFindAndPlaceSubpixel<
herb4c11b3f2015-11-20 13:53:12 -0800558 ProcessOneGlyph, kTextAlignment, kY_SkAxisAlignment>>(glyphFinder);
herbe5911c92015-11-09 13:15:21 -0800559 break;
560 }
561 }
herbe59124e2015-11-18 10:54:39 -0800562
herb4c11b3f2015-11-20 13:53:12 -0800563 static SkPoint MeasureText(LookupGlyph& glyphFinder, const char text[], size_t byteLength) {
herbe59124e2015-11-18 10:54:39 -0800564 SkFixed x = 0, y = 0;
565 const char* stop = text + byteLength;
566
567 SkAutoKern autokern;
568
569 while (text < stop) {
570 // don't need x, y here, since all subpixel variants will have the
571 // same advance
herb4c11b3f2015-11-20 13:53:12 -0800572 const SkGlyph& glyph = glyphFinder->lookupGlyph(&text);
herbe59124e2015-11-18 10:54:39 -0800573
574 x += autokern.adjust(glyph) + glyph.fAdvanceX;
575 y += glyph.fAdvanceY;
576 }
577 SkASSERT(text == stop);
578 return {SkFixedToScalar(x), SkFixedToScalar(y)};
579 }
herbe5911c92015-11-09 13:15:21 -0800580};
581
582template<typename ProcessOneGlyph>
583inline void SkFindAndPlaceGlyph::ProcessPosText(
herb4c11b3f2015-11-20 13:53:12 -0800584 SkPaint::TextEncoding textEncoding, const char text[], size_t byteLength,
585 SkPoint offset, const SkMatrix& matrix, const SkScalar pos[], int scalarsPerPosition,
586 SkPaint::Align textAlignment,
587 SkGlyphCache* cache, ProcessOneGlyph&& processOneGlyph) {
herbe5911c92015-11-09 13:15:21 -0800588
herb9be5ff62015-11-11 11:30:11 -0800589 SkAxisAlignment axisAlignment = SkComputeAxisAlignmentForHText(matrix);
590 uint32_t mtype = matrix.getType();
591
herb4c11b3f2015-11-20 13:53:12 -0800592 LookupGlyph glyphFinder(textEncoding, cache);
593
herb9be5ff62015-11-11 11:30:11 -0800594 // Specialized code for handling the most common case for blink. The while loop is totally
595 // de-virtualized.
596 if (scalarsPerPosition == 1
597 && textAlignment == SkPaint::kLeft_Align
598 && axisAlignment == kX_SkAxisAlignment
599 && cache->isSubpixel()
600 && mtype <= SkMatrix::kTranslate_Mask) {
601 typedef GlyphFindAndPlaceSubpixel<
602 ProcessOneGlyph, SkPaint::kLeft_Align, kX_SkAxisAlignment> Positioner;
603 HorizontalPositions positions{pos};
604 TranslationMapper mapper{matrix, offset};
herb4c11b3f2015-11-20 13:53:12 -0800605 Positioner positioner(glyphFinder);
herb9be5ff62015-11-11 11:30:11 -0800606 const char* cursor = text;
607 const char* stop = text + byteLength;
608 while (cursor < stop) {
609 SkPoint mappedPoint = mapper.TranslationMapper::map(
610 positions.HorizontalPositions::nextPoint());
611 positioner.Positioner::findAndPositionGlyph(
612 &cursor, mappedPoint, skstd::forward<ProcessOneGlyph>(processOneGlyph));
613 }
614 return;
615 }
616
herbe5911c92015-11-09 13:15:21 -0800617 PositionReader positionReader{
618 [&](PositionReader::Variants* to_init) {
619 if (2 == scalarsPerPosition) {
620 to_init->initialize<ArbitraryPositions>(pos);
621 } else {
622 to_init->initialize<HorizontalPositions>(pos);
623 }
herbd4c24f62015-12-07 12:12:29 -0800624 positionReader->forceUseForBug();
herbe5911c92015-11-09 13:15:21 -0800625 }
626 };
627
628 Mapper mapper{
629 [&](Mapper::Variants* to_init) {
herbe5911c92015-11-09 13:15:21 -0800630 if (mtype & (SkMatrix::kAffine_Mask | SkMatrix::kPerspective_Mask)
631 || scalarsPerPosition == 2) {
632 to_init->initialize<GeneralMapper>(matrix, offset);
633 } else if (mtype & SkMatrix::kScale_Mask) {
634 to_init->initialize<XScaleMapper>(matrix, offset);
635 } else {
636 to_init->initialize<TranslationMapper>(matrix, offset);
637 }
638 }
639 };
640
herbd4c24f62015-12-07 12:12:29 -0800641 GlyphFindAndPlace<ProcessOneGlyph> findAndPosition {
herbe5911c92015-11-09 13:15:21 -0800642 [&](typename GlyphFindAndPlace<ProcessOneGlyph>::Variants* to_init) {
643 if (cache->isSubpixel()) {
herbe5911c92015-11-09 13:15:21 -0800644 switch (textAlignment) {
645 case SkPaint::kLeft_Align:
646 InitSubpixel<ProcessOneGlyph, SkPaint::kLeft_Align>(
herb4c11b3f2015-11-20 13:53:12 -0800647 to_init, axisAlignment, glyphFinder);
herbe5911c92015-11-09 13:15:21 -0800648 break;
649 case SkPaint::kCenter_Align:
650 InitSubpixel<ProcessOneGlyph, SkPaint::kCenter_Align>(
herb4c11b3f2015-11-20 13:53:12 -0800651 to_init, axisAlignment, glyphFinder);
herbe5911c92015-11-09 13:15:21 -0800652 break;
653 case SkPaint::kRight_Align:
654 InitSubpixel<ProcessOneGlyph, SkPaint::kRight_Align>(
herb4c11b3f2015-11-20 13:53:12 -0800655 to_init, axisAlignment, glyphFinder);
herbe5911c92015-11-09 13:15:21 -0800656 break;
657 }
658 } else {
659 switch (textAlignment) {
660 case SkPaint::kLeft_Align:
661 to_init->template initialize<
662 GlyphFindAndPlaceFullPixel<ProcessOneGlyph,
herb4c11b3f2015-11-20 13:53:12 -0800663 SkPaint::kLeft_Align, kNoKerning>>(glyphFinder);
herbe5911c92015-11-09 13:15:21 -0800664 break;
665 case SkPaint::kCenter_Align:
666 to_init->template initialize<
667 GlyphFindAndPlaceFullPixel<ProcessOneGlyph,
herb4c11b3f2015-11-20 13:53:12 -0800668 SkPaint::kCenter_Align, kNoKerning>>(glyphFinder);
herbe5911c92015-11-09 13:15:21 -0800669 break;
670 case SkPaint::kRight_Align:
671 to_init->template initialize<
672 GlyphFindAndPlaceFullPixel<ProcessOneGlyph,
herb4c11b3f2015-11-20 13:53:12 -0800673 SkPaint::kRight_Align, kNoKerning>>(glyphFinder);
herbe5911c92015-11-09 13:15:21 -0800674 break;
675 }
676 }
677 }
678 };
679
680 const char* stop = text + byteLength;
681 while (text < stop) {
682 SkPoint mappedPoint = mapper->map(positionReader->nextPoint());
683 findAndPosition->findAndPositionGlyph(
684 &text, mappedPoint, skstd::forward<ProcessOneGlyph>(processOneGlyph));
685 }
686}
687
herbe59124e2015-11-18 10:54:39 -0800688template<typename ProcessOneGlyph>
689inline void SkFindAndPlaceGlyph::ProcessText(
herb4c11b3f2015-11-20 13:53:12 -0800690 SkPaint::TextEncoding textEncoding, const char text[], size_t byteLength,
691 SkPoint offset, const SkMatrix& matrix, SkPaint::Align textAlignment,
692 SkGlyphCache* cache, ProcessOneGlyph&& processOneGlyph) {
herbe59124e2015-11-18 10:54:39 -0800693
694 // transform the starting point
695 matrix.mapPoints(&offset, 1);
696
herb4c11b3f2015-11-20 13:53:12 -0800697 LookupGlyph glyphFinder(textEncoding, cache);
698
herbe59124e2015-11-18 10:54:39 -0800699 // need to measure first
700 if (textAlignment != SkPaint::kLeft_Align) {
herb4c11b3f2015-11-20 13:53:12 -0800701 SkVector stop = MeasureText(glyphFinder, text, byteLength);
herbe59124e2015-11-18 10:54:39 -0800702
703 if (textAlignment == SkPaint::kCenter_Align) {
704 stop *= SK_ScalarHalf;
705 }
706 offset -= stop;
707 }
708
709 GlyphFindAndPlace<ProcessOneGlyph> findAndPosition{
710 [&](typename GlyphFindAndPlace<ProcessOneGlyph>::Variants* to_init) {
711 if (cache->isSubpixel()) {
712 SkAxisAlignment axisAlignment = SkComputeAxisAlignmentForHText(matrix);
713 InitSubpixel<ProcessOneGlyph, SkPaint::kLeft_Align>(
herb4c11b3f2015-11-20 13:53:12 -0800714 to_init, axisAlignment, glyphFinder);
herbe59124e2015-11-18 10:54:39 -0800715 } else {
716 to_init->template initialize<
717 GlyphFindAndPlaceFullPixel<
herb4c11b3f2015-11-20 13:53:12 -0800718 ProcessOneGlyph, SkPaint::kLeft_Align, kUseKerning>>(glyphFinder);
herbe59124e2015-11-18 10:54:39 -0800719 }
720 }
721 };
722
723 const char* stop = text + byteLength;
724 SkPoint current = offset;
725 while (text < stop) {
726 current =
727 findAndPosition->findAndPositionGlyph(
728 &text, current, skstd::forward<ProcessOneGlyph>(processOneGlyph));
729
730 }
731}
732
herbe5911c92015-11-09 13:15:21 -0800733#endif // SkFindAndPositionGlyph_DEFINED