Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(138)

Side by Side Diff: src/core/SkFindAndPlaceGlyph.h

Issue 1409123010: Extract glyph find and position code. (Closed) Base URL: https://skia.googlesource.com/skia.git@extract-glyph-position-code
Patch Set: cleaned up Created 5 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« src/core/SkDraw.cpp ('K') | « src/core/SkDraw.cpp ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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 "SkGlyph.h"
12 #include "SkGlyphCache.h"
13 #include "SkPaint.h"
14 #include "SkTemplates.h"
15
16 // Calculate a type with the same size as the max of all the Ts.
17 // This must be top level because the is no specialization of inner classes.
18 template<typename... Ts> struct SkMaxSizeOf;
19
20 template<>
21 struct SkMaxSizeOf<> {
22 static const size_t value = 0;
23 };
24
25 template<typename H, typename... Ts>
26 struct SkMaxSizeOf<H, Ts...> {
27 static const size_t value =
28 sizeof(H) >= SkMaxSizeOf<Ts...>::value ? sizeof(H) : SkMaxSizeOf<Ts...>: :value;
29 };
30
31 class SkFindAndPlaceGlyph {
32 public:
33 // ProcessPosText handles all cases for finding and positioning glyphs. It h as a very large
34 // multiplicity. It figures out the glyph, position and rounding and pass th ose parameters to
35 // processOneGlyph.
36 //
37 // The routine processOneGlyph passed in by the client has the following sig nature:
38 // void f(const SkGlyph& glyph, SkPoint position, SkPoint rounding);
39 //
40 // * Sub-pixel positioning (2) - use sub-pixel positioning.
41 // * Text alignment (3) - text alignment with respect to the glyph's width.
42 // * Matrix type (3) - special cases for translation and X-coordinate scalin g.
43 // * Components per position (2) - the positions vector can have a common Y with different
44 // Xs, or XY-pairs.
45 // * Axis Alignment (for sub-pixel positioning) (3) - when using sub-pixel p ositioning, round
46 // to a whole coordinate instead of using sub-pixel positioning.
47 // The number of variations is 108 for sub-pixel and 36 for full-pixel.
48 // This routine handles all of them using inline polymorphic variable (no he ap allocation).
49 template<typename ProcessOneGlyph>
50 static void ProcessPosText(const char text[], size_t byteLength,
51 const SkPoint& offset, const SkMatrix& matrix,
52 const SkScalar pos[], int scalarsPerPosition,
53 SkPaint::Align textAlignment, SkDrawCacheProc& gl yphCacheProc,
54 SkGlyphCache* cache, ProcessOneGlyph&& processOne Glyph);
55
56 // SpecializedProcessPosText is a version of ProcessPosText that de-virtuali zes the
57 // different components used. It returns true if it can handle the situation , otherwise it
58 // returns false. This allows greater inlining freedom to the compiler. Curr ently, there is
59 // only one specialized variant: sub-pixel position, left-aligned, x-axis-al igned,
60 // translation, and one scalar per position entry.
61 // * This is by far the most common type of text Blink draws.
62 template<typename ProcessOneGlyph>
63 static bool SpecializedProcessPosText(const char* const text, size_t byteLen gth,
64 const SkPoint& offset, const SkMatrix& matrix,
65 const SkScalar pos[], int scalarsPerPo sition,
66 SkPaint::Align textAlignment,
67 SkDrawCacheProc& glyphCacheProc,
68 SkGlyphCache* cache,
69 ProcessOneGlyph&& processOneGlyph);
70
71 private:
72 // UntaggedVariant is a pile of memory that can hold one of the Ts. It provi des a way
73 // to initialize that memory in a typesafe way.
74 template<typename... Ts>
75 class UntaggedVariant {
76 public:
77 UntaggedVariant() { }
78
79 ~UntaggedVariant() { }
80 UntaggedVariant(const UntaggedVariant&) = delete;
81 UntaggedVariant& operator=(const UntaggedVariant&) = delete;
82 UntaggedVariant(UntaggedVariant&&) = delete;
83 UntaggedVariant& operator=(UntaggedVariant&&) = delete;
84
85 template<typename Variant, typename... Args>
86 void initialize(Args&&... args) {
87 SkASSERT(sizeof(Variant) <= sizeof(fSpace));
88 #if defined(_MSC_VER) && _MSC_VER < 1900
89 #define alignof __alignof
90 #endif
91 SkASSERT(alignof(Variant) <= alignof(Space));
92 new(&fSpace) Variant(skstd::forward<Args>(args)...);
93 }
94
95 private:
96 typedef SkAlignedSStorage<SkMaxSizeOf<Ts...>::value> Space;
97 Space fSpace;
98 };
99
100 // PolymorphicVariant holds subclasses of Base without slicing. Ts must be s ubclasses of Base.
101 template<typename Base, typename... Ts>
102 class PolymorphicVariant {
103 public:
104 typedef UntaggedVariant<Ts...> Variants;
105
106 template<typename Initializer>
107 PolymorphicVariant(Initializer&& initializer) {
108 initializer(&fVariants);
109 }
110 ~PolymorphicVariant() { get()->~Base(); }
111 Base* get() const { return reinterpret_cast<Base*>(&fVariants); }
112 Base* operator->() const { return get(); }
113 Base& operator*() const { return *get(); }
114
115 private:
116 mutable Variants fVariants;
117 };
118
119 // PositionReaderInterface reads a point from the pos vector.
120 // * HorizontalPositions - assumes a common Y for many X values.
121 // * ArbitraryPositions - a list of (X,Y) pairs.
122 class PositionReaderInterface {
123 public:
124 virtual ~PositionReaderInterface() { }
125 virtual SkPoint nextPoint() = 0;
126 };
127
128 class HorizontalPositions final : public PositionReaderInterface {
129 public:
130 explicit HorizontalPositions(const SkScalar* positions)
131 : fPositions(positions) { }
132
133 SkPoint nextPoint() override {
134 SkScalar x = *fPositions++;
135 return {x, 0};
136 }
137
138 private:
139 const SkScalar* fPositions;
140 };
141
142 class ArbitraryPositions final : public PositionReaderInterface {
143 public:
144 explicit ArbitraryPositions(const SkScalar* positions)
145 : fPositions(positions) { }
146
147 SkPoint nextPoint() override {
148 SkPoint to_return{fPositions[0], fPositions[1]};
149 fPositions += 2;
150 return to_return;
151 }
152
153 private:
154 const SkScalar* fPositions;
155 };
156
157 typedef PolymorphicVariant<PositionReaderInterface, HorizontalPositions, Arb itraryPositions>
158 PositionReader;
159
160 // MapperInterface given a point map it through the matrix. There are severa l shortcut variants.
161 // * TranslationMapper - assumes a translation only matrix.
162 // * XScaleMapper - assumes an X scaling and a translation.
163 // * GeneralMapper - Does all other matricies.
164 class MapperInterface {
165 public:
166 virtual ~MapperInterface() { }
167
168 virtual SkPoint map(SkPoint position) const = 0;
169 };
170
171 class TranslationMapper final : public MapperInterface {
172 public:
173 TranslationMapper(const SkMatrix& matrix, const SkPoint origin)
174 : fTranslate(matrix.mapXY(origin.fX, origin.fY)) { }
175
176 SkPoint map(SkPoint position) const override {
177 return position + fTranslate;
178 }
179
180 private:
181 const SkPoint fTranslate;
182 };
183
184 class XScaleMapper final : public MapperInterface {
185 public:
186 XScaleMapper(const SkMatrix& matrix, const SkPoint origin)
187 : fTranslate(matrix.mapXY(origin.fX, origin.fY)), fXScale(matrix.get ScaleX()) { }
188
189 SkPoint map(SkPoint position) const override {
190 return {fXScale * position.fX + fTranslate.fX, fTranslate.fY};
191 }
192
193 private:
194 const SkPoint fTranslate;
195 const SkScalar fXScale;
196 };
197
198 // The caller must keep matrix alive while this class is used.
199 class GeneralMapper final : public MapperInterface {
200 public:
201 GeneralMapper(const SkMatrix& matrix, const SkPoint origin)
202 : fOrigin(origin), fMatrix(matrix), fMapProc(matrix.getMapXYProc()) { }
203
204 SkPoint map(SkPoint position) const override {
205 SkPoint result;
206 fMapProc(fMatrix, position.fX + fOrigin.fX, position.fY + fOrigin.fY , &result);
207 return result;
208 }
209
210 private:
211 const SkPoint fOrigin;
212 const SkMatrix& fMatrix;
213 const SkMatrix::MapXYProc fMapProc;
214 };
215
216 typedef PolymorphicVariant<
217 MapperInterface, TranslationMapper, XScaleMapper, GeneralMapper> Mapper;
218
219 // Text alignment handles shifting the glyph based on its width.
220 static SkPoint TextAlignmentAdjustment(SkPaint::Align textAlignment, const S kGlyph& glyph) {
221 switch (textAlignment) {
222 case SkPaint::kLeft_Align:
223 return {0.0f, 0.0f};
224 case SkPaint::kCenter_Align:
225 return {SkFixedToScalar(glyph.fAdvanceX >> 1),
226 SkFixedToScalar(glyph.fAdvanceY >> 1)};
227 case SkPaint::kRight_Align:
228 return {SkFixedToScalar(glyph.fAdvanceX),
229 SkFixedToScalar(glyph.fAdvanceY)};
230 }
231 // Even though the entire enum is covered above, MVSC doesn't think so. Make it happy.
232 SkFAIL("Should never get here.");
233 return {0.0f, 0.0f};
234 }
235
236 // The "call" to SkFixedToScalar is actually a macro. It's macros all the wa y down.
237 static constexpr SkScalar kSubpixelRounding = SkFixedToScalar(SkGlyph::kSubp ixelRound);
238
239 // Functions for handling sub-pixel aligned positions.
240 // The subpixel_position_rounding function returns a point suitable for roun ding a sub-pixel
241 // positioned glyph.
242 static SkPoint SubpixelPositionRounding(SkAxisAlignment axisAlignment) {
243 switch (axisAlignment) {
244 case kX_SkAxisAlignment:
245 return {SkFixedToScalar(SkGlyph::kSubpixelRound), SK_ScalarHalf} ;
246 case kY_SkAxisAlignment:
247 return {SK_ScalarHalf, kSubpixelRounding};
248 case kNone_SkAxisAlignment:
249 return {kSubpixelRounding, kSubpixelRounding};
250 }
251 SkFAIL("Should not get here.");
252 return {0.0f, 0.0f};
253 }
254
255 // The subpixel_position_alignment function produces a suitable position for the glyph cache to
256 // produce the correct sub-pixel alignment. If a position is aligned with an axis a shortcut
257 // of 0 is used for the sub-pixel position.
258 static SkIPoint SubpixelAlignment(SkAxisAlignment axisAlignment, SkPoint pos ition) {
259 switch (axisAlignment) {
260 case kX_SkAxisAlignment:
261 return {SkScalarToFixed(position.fX + kSubpixelRounding), 0};
262 case kY_SkAxisAlignment:
263 return {0, SkScalarToFixed(position.fY + kSubpixelRounding)};
264 case kNone_SkAxisAlignment:
265 return {SkScalarToFixed(position.fX + kSubpixelRounding),
266 SkScalarToFixed(position.fY + kSubpixelRounding)};
267 }
268 SkFAIL("Should not get here.");
269 return {0, 0};
270 }
271
272 // GlyphFindAndPlaceInterface given the text and position finds the correct glyph and does
273 // glyph specific position adjustment. The findAndPositionGlyph method takes text and
274 // position and calls processOneGlyph with the correct glyph, final position and rounding
275 // terms. The final position is not rounded yet and is the responsibility of processOneGlyph.
276 template<typename ProcessOneGlyph>
277 class GlyphFindAndPlaceInterface : SkNoncopyable {
278 public:
279 virtual ~GlyphFindAndPlaceInterface() { };
280
281 // This should be a pure virtual, but some versions of GCC <= 4.8 have a bug that causes a
282 // compile error.
283 // See GCC bug: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=60277
284 virtual void findAndPositionGlyph(const char** text, SkPoint position,
285 ProcessOneGlyph&& processOneGlyph) { } ;
286 };
287
288 // GlyphFindAndPlaceSubpixel handles finding and placing glyphs when sub-pix el positioning is
289 // requested. After it has found and placed the glyph it calls the templated function
290 // ProcessOneGlyph in order to actually perform an action.
291 template<typename ProcessOneGlyph, SkPaint::Align kTextAlignment, SkAxisAlig nment kAxisAlignment>
292 class GlyphFindAndPlaceSubpixel final : public GlyphFindAndPlaceInterface<Pr ocessOneGlyph> {
293 public:
294 GlyphFindAndPlaceSubpixel(SkGlyphCache* cache, SkDrawCacheProc glyphCach eProc)
295 : fCache(cache), fGlyphCacheProc(glyphCacheProc) {
296 }
297
298 void findAndPositionGlyph(const char** text, SkPoint position,
299 ProcessOneGlyph&& processOneGlyph) override {
300 SkPoint finalPosition = position;
301 if (kTextAlignment != SkPaint::kLeft_Align) {
302 // Get the width of an un-sub-pixel positioned glyph for calcula ting the alignment.
303 // This is not needed for kLeftAlign because its adjustment is a lways {0, 0}.
304 const char* tempText = *text;
305 const SkGlyph &metricGlyph = fGlyphCacheProc(fCache, &tempText, 0, 0);
306
307 if (metricGlyph.fWidth <= 0) {
308 return;
309 }
310
311 // Adjust the final position by the alignment adjustment.
312 finalPosition -= TextAlignmentAdjustment(kTextAlignment, metricG lyph);
313 }
314
315 // Find the glyph.
316 SkIPoint lookupPosition = SubpixelAlignment(kAxisAlignment, finalPos ition);
317 const SkGlyph& renderGlyph = fGlyphCacheProc(
318 fCache, text, lookupPosition.fX, lookupPosition.fY);
319
320 // If the glyph has no width (no pixels) then don't bother processin g it.
321 if (renderGlyph.fWidth > 0) {
322 processOneGlyph(renderGlyph, finalPosition,
323 SubpixelPositionRounding(kAxisAlignment));
324 }
325 }
326
327 private:
328 SkGlyphCache* const fCache;
329 SkDrawCacheProc fGlyphCacheProc;
330 };
331
332 // GlyphFindAndPlaceFullPixel handles finding and placing glyphs when no sub -pixel
333 // positioning is requested.
334 template<typename ProcessOneGlyph, SkPaint::Align kTextAlignment>
335 class GlyphFindAndPlaceFullPixel final : public GlyphFindAndPlaceInterface<P rocessOneGlyph> {
336 public:
337 GlyphFindAndPlaceFullPixel(SkGlyphCache* cache, SkDrawCacheProc glyphCac heProc)
338 : fCache(cache), fGlyphCacheProc(glyphCacheProc) { }
339
340 void findAndPositionGlyph(const char** text, SkPoint position,
341 ProcessOneGlyph&& processOneGlyph) override {
342 SkPoint finalPosition = position;
343 const SkGlyph& glyph = fGlyphCacheProc(fCache, text, 0, 0);
344 if (glyph.fWidth <= 0) {
345 return;
346 }
347 finalPosition -= TextAlignmentAdjustment(kTextAlignment, glyph);
348 processOneGlyph(glyph, finalPosition, {SK_ScalarHalf, SK_ScalarHalf} );
349 }
350
351 private:
352 SkGlyphCache* const fCache;
353 SkDrawCacheProc fGlyphCacheProc;
354 };
355
356 // GlyphFindAndPlace is a large variant that encapsulates the multiple types of finding and
357 // placing a glyph. There are three factors that go into the different facto rs.
358 // * Is sub-pixel positioned - a boolean that says whether to use sub-pixel positioning.
359 // * Text alignment - indicates if the glyph should be placed to the right, centered or left
360 // of a given position.
361 // * Axis alignment - indicates if the glyphs final sub-pixel position shoul d be rounded to a
362 // whole pixel if the glyph is aligned with an axis. This is only used for sub-pixel
363 // positioning and allows the baseline to look crisp.
364 template<typename ProcessOneGlyph>
365 using GlyphFindAndPlace = PolymorphicVariant<
366 GlyphFindAndPlaceInterface<ProcessOneGlyph>,
367 // Subpixel
368 GlyphFindAndPlaceSubpixel<ProcessOneGlyph, SkPaint::kLeft_Align, kNon e_SkAxisAlignment>,
369 GlyphFindAndPlaceSubpixel<ProcessOneGlyph, SkPaint::kLeft_Align, kX_S kAxisAlignment >,
370 GlyphFindAndPlaceSubpixel<ProcessOneGlyph, SkPaint::kLeft_Align, kY_S kAxisAlignment >,
371 GlyphFindAndPlaceSubpixel<ProcessOneGlyph, SkPaint::kCenter_Align, kNon e_SkAxisAlignment>,
372 GlyphFindAndPlaceSubpixel<ProcessOneGlyph, SkPaint::kCenter_Align, kX_S kAxisAlignment >,
373 GlyphFindAndPlaceSubpixel<ProcessOneGlyph, SkPaint::kCenter_Align, kY_S kAxisAlignment >,
374 GlyphFindAndPlaceSubpixel<ProcessOneGlyph, SkPaint::kRight_Align, kNon e_SkAxisAlignment>,
375 GlyphFindAndPlaceSubpixel<ProcessOneGlyph, SkPaint::kRight_Align, kX_S kAxisAlignment >,
376 GlyphFindAndPlaceSubpixel<ProcessOneGlyph, SkPaint::kRight_Align, kY_S kAxisAlignment >,
377 // Full pixel
378 GlyphFindAndPlaceFullPixel<ProcessOneGlyph, SkPaint::kLeft_Align >,
379 GlyphFindAndPlaceFullPixel<ProcessOneGlyph, SkPaint::kCenter_Align>,
380 GlyphFindAndPlaceFullPixel<ProcessOneGlyph, SkPaint::kRight_Align >
381 >;
382
383 // InitSubpixel is a helper function for initializing all the variants of
384 // GlyphFindAndPlaceSubpixel.
385 template<typename ProcessOneGlyph, SkPaint::Align kTextAlignment>
386 static void InitSubpixel(
387 typename GlyphFindAndPlace<ProcessOneGlyph>::Variants* to_init,
388 SkAxisAlignment axisAlignment,
389 SkGlyphCache* cache,
390 SkDrawCacheProc glyphCacheProc) {
391 switch (axisAlignment) {
392 case kX_SkAxisAlignment:
393 to_init->template initialize<GlyphFindAndPlaceSubpixel<
394 ProcessOneGlyph, kTextAlignment, kX_SkAxisAlignment>>(
395 cache, glyphCacheProc);
396 break;
397 case kNone_SkAxisAlignment:
398 to_init->template initialize<GlyphFindAndPlaceSubpixel<
399 ProcessOneGlyph, kTextAlignment, kNone_SkAxisAlignment>>(
400 cache, glyphCacheProc);
401 break;
402 case kY_SkAxisAlignment:
403 to_init->template initialize<GlyphFindAndPlaceSubpixel<
404 ProcessOneGlyph, kTextAlignment, kY_SkAxisAlignment>>(
405 cache, glyphCacheProc);
406 break;
407 }
408 }
409 };
410
411 template<typename ProcessOneGlyph>
412 inline void SkFindAndPlaceGlyph::ProcessPosText(
413 const char text[], size_t byteLength, const SkPoint& offset, const SkMatrix& matrix,
414 const SkScalar pos[], int scalarsPerPosition, SkPaint::Align textAlignment,
415 SkDrawCacheProc& glyphCacheProc, SkGlyphCache* cache, ProcessOneGlyph&& proc essOneGlyph) {
416
417 PositionReader positionReader{
418 [&](PositionReader::Variants* to_init) {
419 if (2 == scalarsPerPosition) {
420 to_init->initialize<ArbitraryPositions>(pos);
421 } else {
422 to_init->initialize<HorizontalPositions>(pos);
423 }
424 }
425 };
426
427 Mapper mapper{
428 [&](Mapper::Variants* to_init) {
429 uint32_t mtype = matrix.getType();
430 if (mtype & (SkMatrix::kAffine_Mask | SkMatrix::kPerspective_Mask)
431 || scalarsPerPosition == 2) {
432 to_init->initialize<GeneralMapper>(matrix, offset);
433 } else if (mtype & SkMatrix::kScale_Mask) {
434 to_init->initialize<XScaleMapper>(matrix, offset);
435 } else {
436 to_init->initialize<TranslationMapper>(matrix, offset);
437 }
438 }
439 };
440
441 GlyphFindAndPlace<ProcessOneGlyph> findAndPosition{
442 [&](typename GlyphFindAndPlace<ProcessOneGlyph>::Variants* to_init) {
443 if (cache->isSubpixel()) {
444 SkAxisAlignment axisAlignment = SkComputeAxisAlignmentForHText(m atrix);
445 switch (textAlignment) {
446 case SkPaint::kLeft_Align:
447 InitSubpixel<ProcessOneGlyph, SkPaint::kLeft_Align>(
448 to_init, axisAlignment, cache, glyphCacheProc);
449 break;
450 case SkPaint::kCenter_Align:
451 InitSubpixel<ProcessOneGlyph, SkPaint::kCenter_Align>(
452 to_init, axisAlignment, cache, glyphCacheProc);
453 break;
454 case SkPaint::kRight_Align:
455 InitSubpixel<ProcessOneGlyph, SkPaint::kRight_Align>(
456 to_init, axisAlignment, cache, glyphCacheProc);
457 break;
458 }
459 } else {
460 switch (textAlignment) {
461 case SkPaint::kLeft_Align:
462 to_init->template initialize<
463 GlyphFindAndPlaceFullPixel<ProcessOneGlyph,
464 SkPaint::kLeft_Align>>(cache, glyphCacheProc);
465 break;
466 case SkPaint::kCenter_Align:
467 to_init->template initialize<
468 GlyphFindAndPlaceFullPixel<ProcessOneGlyph,
469 SkPaint::kCenter_Align>>(cache, glyphCacheProc);
470 break;
471 case SkPaint::kRight_Align:
472 to_init->template initialize<
473 GlyphFindAndPlaceFullPixel<ProcessOneGlyph,
474 SkPaint::kRight_Align>>(cache, glyphCacheProc);
475 break;
476 }
477 }
478 }
479 };
480
481 const char* stop = text + byteLength;
482 while (text < stop) {
483 SkPoint mappedPoint = mapper->map(positionReader->nextPoint());
484 findAndPosition->findAndPositionGlyph(
485 &text, mappedPoint, skstd::forward<ProcessOneGlyph>(processOneGlyph) );
486 }
487 }
488
489 template<typename ProcessOneGlyph>
490 inline bool SkFindAndPlaceGlyph::SpecializedProcessPosText(
491 const char* const text, size_t byteLength, const SkPoint& offset, const SkMa trix& matrix,
492 const SkScalar pos[], int scalarsPerPosition, SkPaint::Align textAlignment,
493 SkDrawCacheProc& glyphCacheProc, SkGlyphCache* cache, ProcessOneGlyph&& proc essOneGlyph) {
494 SkAxisAlignment axisAlignment = SkComputeAxisAlignmentForHText(matrix);
495 uint32_t mtype = matrix.getType();
496 if (scalarsPerPosition == 1
497 && textAlignment == SkPaint::kLeft_Align
498 && axisAlignment == kX_SkAxisAlignment
499 && cache->isSubpixel()
500 && mtype <= SkMatrix::kTranslate_Mask) {
501 typedef GlyphFindAndPlaceSubpixel<
502 ProcessOneGlyph, SkPaint::kLeft_Align, kX_SkAxisAlignment> Positioner;
503 HorizontalPositions positions{pos};
504 TranslationMapper mapper{matrix, offset};
505 Positioner positioner(cache, glyphCacheProc);
506 const char* cursor = text;
507 const char* stop = text + byteLength;
508 while (cursor < stop) {
509 SkPoint mappedPoint = mapper.TranslationMapper::map(
510 positions.HorizontalPositions::nextPoint());
511 positioner.Positioner::findAndPositionGlyph(
512 &cursor, mappedPoint, skstd::forward<ProcessOneGlyph>(processOne Glyph));
513 }
514 return true;
515 }
516 return false;
517 }
518
519
520 #endif // SkFindAndPositionGlyph_DEFINED
OLDNEW
« src/core/SkDraw.cpp ('K') | « src/core/SkDraw.cpp ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698