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