blob: bf60526322dcabe4f12016ea61c1b72407180cec [file] [log] [blame]
Mike Reeddee25cc2018-10-05 11:48:01 -04001/*
2 * Copyright 2018 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#include "SkPaint.h"
9#include "SkColorFilter.h"
10#include "SkDraw.h"
11#include "SkFontDescriptor.h"
12#include "SkGlyphCache.h"
13#include "SkGraphics.h"
14#include "SkPaintDefaults.h"
15#include "SkPaintPriv.h"
16#include "SkPathEffect.h"
17#include "SkSafeRange.h"
18#include "SkScalar.h"
19#include "SkScalerContext.h"
20#include "SkShader.h"
21#include "SkShaderBase.h"
22#include "SkStringUtils.h"
23#include "SkTLazy.h"
24#include "SkTextBlob.h"
25#include "SkTextBlobPriv.h"
26#include "SkTextFormatParams.h"
27#include "SkTextToPathIter.h"
28#include "SkTo.h"
29#include "SkTypeface.h"
30
31///////////////////////////////////////////////////////////////////////////////
32
33static SkScalar mag2(SkScalar x, SkScalar y) {
34 return x * x + y * y;
35}
36
37static bool tooBig(const SkMatrix& m, SkScalar ma2max) {
38 return mag2(m[SkMatrix::kMScaleX], m[SkMatrix::kMSkewY]) > ma2max
39 ||
40 mag2(m[SkMatrix::kMSkewX], m[SkMatrix::kMScaleY]) > ma2max;
41}
42
43bool SkPaint::TooBigToUseCache(const SkMatrix& ctm, const SkMatrix& textM, SkScalar maxLimit) {
44 SkASSERT(!ctm.hasPerspective());
45 SkASSERT(!textM.hasPerspective());
46
47 SkMatrix matrix;
48 matrix.setConcat(ctm, textM);
49 return tooBig(matrix, MaxCacheSize2(maxLimit));
50}
51
52SkScalar SkPaint::MaxCacheSize2(SkScalar maxLimit) {
53 // we have a self-imposed maximum, just for memory-usage sanity
54 const int limit = SkMin32(SkGraphics::GetFontCachePointSizeLimit(), maxLimit);
55 const SkScalar maxSize = SkIntToScalar(limit);
56 return maxSize * maxSize;
57}
58
59///////////////////////////////////////////////////////////////////////////////
60
61#include "SkGlyphCache.h"
62#include "SkUtils.h"
63
64int SkPaint::countText(const void* text, size_t byteLength) const {
65 SkASSERT(text != nullptr);
66 switch (this->getTextEncoding()) {
67 case kUTF8_TextEncoding:
68 return SkUTF::CountUTF8((const char*)text, byteLength);
69 case kUTF16_TextEncoding:
70 return SkUTF::CountUTF16((const uint16_t*)text, byteLength);
71 case kUTF32_TextEncoding:
72 return SkToInt(byteLength >> 2);
73 case kGlyphID_TextEncoding:
74 return SkToInt(byteLength >> 1);
75 default:
76 SkDEBUGFAIL("unknown text encoding");
77 }
78
79 return 0;
80}
81
82static SkTypeface::Encoding to_encoding(SkPaint::TextEncoding e) {
83 static_assert((int)SkTypeface::kUTF8_Encoding == (int)SkPaint::kUTF8_TextEncoding, "");
84 static_assert((int)SkTypeface::kUTF16_Encoding == (int)SkPaint::kUTF16_TextEncoding, "");
85 static_assert((int)SkTypeface::kUTF32_Encoding == (int)SkPaint::kUTF32_TextEncoding, "");
86 return (SkTypeface::Encoding)e;
87}
88
89int SkPaint::textToGlyphs(const void* textData, size_t byteLength, uint16_t glyphs[]) const {
90 SkASSERT(textData != nullptr);
91
92 if (nullptr == glyphs) {
93 return this->countText(textData, byteLength);
94 }
95
96 // if we get here, we have a valid glyphs[] array, so time to fill it in
97
98 // handle this encoding before the setup for the glyphcache
99 if (this->getTextEncoding() == kGlyphID_TextEncoding) {
100 // we want to ignore the low bit of byteLength
101 memcpy(glyphs, textData, byteLength >> 1 << 1);
102 return SkToInt(byteLength >> 1);
103 }
104
105 auto cache = SkStrikeCache::FindOrCreateStrikeExclusive(*this);
106
107 const void* stop = (const char*)textData + byteLength;
108 uint16_t* gptr = glyphs;
109 const SkTypeface::Encoding encoding = to_encoding(this->getTextEncoding());
110
111 while (textData < stop) {
112 SkUnichar unichar = SkUTFN_Next(encoding, &textData, stop);
113 if (unichar < 0) {
114 return 0; // bad UTF-N sequence
115 }
116 *gptr++ = cache->unicharToGlyph(unichar);
117 }
118 return SkToInt(gptr - glyphs);
119}
120
121bool SkPaint::containsText(const void* textData, size_t byteLength) const {
122 if (0 == byteLength) {
123 return true;
124 }
125
126 SkASSERT(textData != nullptr);
127
128 // handle this encoding before the setup for the glyphcache
129 if (this->getTextEncoding() == kGlyphID_TextEncoding) {
130 const uint16_t* glyphID = static_cast<const uint16_t*>(textData);
131 size_t count = byteLength >> 1;
132 for (size_t i = 0; i < count; i++) {
133 if (0 == glyphID[i]) {
134 return false;
135 }
136 }
137 return true;
138 }
139
140 auto cache = SkStrikeCache::FindOrCreateStrikeExclusive(*this);
141 const void* stop = (const char*)textData + byteLength;
142 const SkTypeface::Encoding encoding = to_encoding(this->getTextEncoding());
143 while (textData < stop) {
144 if (0 == cache->unicharToGlyph(SkUTFN_Next(encoding, &textData, stop))) {
145 return false;
146 }
147 }
148 return true;
149}
150
151void SkPaint::glyphsToUnichars(const uint16_t glyphs[], int count, SkUnichar textData[]) const {
152 if (count <= 0) {
153 return;
154 }
155
156 SkASSERT(glyphs != nullptr);
157 SkASSERT(textData != nullptr);
158
159 SkSurfaceProps props(0, kUnknown_SkPixelGeometry);
160 auto cache = SkStrikeCache::FindOrCreateStrikeExclusive(
161 *this, &props, SkScalerContextFlags::kFakeGammaAndBoostContrast, nullptr);
162
163 for (int index = 0; index < count; index++) {
164 textData[index] = cache->glyphToUnichar(glyphs[index]);
165 }
166}
167
168///////////////////////////////////////////////////////////////////////////////
169
170static const SkGlyph& sk_getMetrics_utf8_next(SkGlyphCache* cache,
171 const char** text,
172 const char* stop) {
173 SkASSERT(cache != nullptr);
174 SkASSERT(text != nullptr);
175
176 return cache->getUnicharMetrics(SkUTF::NextUTF8(text, stop));
177}
178
179static const SkGlyph& sk_getMetrics_utf16_next(SkGlyphCache* cache,
180 const char** text,
181 const char* stop) {
182 SkASSERT(cache != nullptr);
183 SkASSERT(text != nullptr);
184
185 return cache->getUnicharMetrics(
186 SkUTF::NextUTF16((const uint16_t**)text, (const uint16_t*)stop));
187}
188
189static const SkGlyph& sk_getMetrics_utf32_next(SkGlyphCache* cache,
190 const char** text,
191 const char* stop) {
192 SkASSERT(cache != nullptr);
193 SkASSERT(text != nullptr);
194
195 return cache->getUnicharMetrics(SkUTF::NextUTF32((const int32_t**)text, (const int32_t*)stop));
196}
197
198static const SkGlyph& sk_getMetrics_glyph_next(SkGlyphCache* cache,
199 const char** text,
200 const char* stop) {
201 SkASSERT(cache != nullptr);
202 SkASSERT(text != nullptr);
203
204 const uint16_t* ptr = *(const uint16_t**)text;
205 unsigned glyphID = *ptr;
206 ptr += 1;
207 *text = (const char*)ptr;
208 return cache->getGlyphIDMetrics(glyphID);
209}
210
211static const SkGlyph& sk_getAdvance_utf8_next(SkGlyphCache* cache,
212 const char** text,
213 const char* stop) {
214 SkASSERT(cache != nullptr);
215 SkASSERT(text != nullptr);
216
217 return cache->getUnicharAdvance(SkUTF::NextUTF8(text, stop));
218}
219
220static const SkGlyph& sk_getAdvance_utf16_next(SkGlyphCache* cache,
221 const char** text,
222 const char* stop) {
223 SkASSERT(cache != nullptr);
224 SkASSERT(text != nullptr);
225
226 return cache->getUnicharAdvance(
227 SkUTF::NextUTF16((const uint16_t**)text, (const uint16_t*)stop));
228}
229
230static const SkGlyph& sk_getAdvance_utf32_next(SkGlyphCache* cache,
231 const char** text,
232 const char* stop) {
233 SkASSERT(cache != nullptr);
234 SkASSERT(text != nullptr);
235
236 return cache->getUnicharAdvance(SkUTF::NextUTF32((const int32_t**)text, (const int32_t*)stop));
237}
238
239static const SkGlyph& sk_getAdvance_glyph_next(SkGlyphCache* cache,
240 const char** text,
241 const char* stop) {
242 SkASSERT(cache != nullptr);
243 SkASSERT(text != nullptr);
244
245 const uint16_t* ptr = *(const uint16_t**)text;
246 unsigned glyphID = *ptr;
247 ptr += 1;
248 *text = (const char*)ptr;
249 return cache->getGlyphIDAdvance(glyphID);
250}
251
252SkPaint::GlyphCacheProc SkPaint::GetGlyphCacheProc(TextEncoding encoding,
253 bool needFullMetrics) {
254 static const GlyphCacheProc gGlyphCacheProcs[] = {
255 sk_getMetrics_utf8_next,
256 sk_getMetrics_utf16_next,
257 sk_getMetrics_utf32_next,
258 sk_getMetrics_glyph_next,
259
260 sk_getAdvance_utf8_next,
261 sk_getAdvance_utf16_next,
262 sk_getAdvance_utf32_next,
263 sk_getAdvance_glyph_next,
264 };
265
266 unsigned index = encoding;
267
268 if (!needFullMetrics) {
269 index += 4;
270 }
271
272 SkASSERT(index < SK_ARRAY_COUNT(gGlyphCacheProcs));
273 return gGlyphCacheProcs[index];
274}
275
276///////////////////////////////////////////////////////////////////////////////
277
278SkScalar SkPaint::setupForAsPaths() {
279
280 constexpr uint32_t flagsToIgnore = SkPaint::kLinearText_Flag |
281 SkPaint::kLCDRenderText_Flag |
282 SkPaint::kEmbeddedBitmapText_Flag |
283 SkPaint::kAutoHinting_Flag;
284
285 uint32_t flags = this->getFlags();
286
287 // clear the flags we don't care about
288 flags &= ~flagsToIgnore;
289
290 // set the flags we do care about
291 flags |= SkPaint::kSubpixelText_Flag;
292
293 this->setFlags(flags);
294 this->setHinting(SkPaint::kNo_Hinting);
295 this->setStyle(SkPaint::kFill_Style);
296 this->setPathEffect(nullptr);
297
298 SkScalar textSize = fTextSize;
299 this->setTextSize(kCanonicalTextSizeForPaths);
300 return textSize / kCanonicalTextSizeForPaths;
301}
302
303class SkCanonicalizePaint {
304public:
305 SkCanonicalizePaint(const SkPaint& paint) : fPaint(&paint), fScale(0) {
306 if (paint.isLinearText() || SkDraw::ShouldDrawTextAsPaths(paint, SkMatrix::I())) {
307 SkPaint* p = fLazy.set(paint);
308 fScale = p->setupForAsPaths();
309 fPaint = p;
310 }
311 }
312
313 const SkPaint& getPaint() const { return *fPaint; }
314
315 /**
316 * Returns 0 if the paint was unmodified, or the scale factor need to
317 * the original textSize
318 */
319 SkScalar getScale() const { return fScale; }
320
321private:
322 const SkPaint* fPaint;
323 SkScalar fScale;
324 SkTLazy<SkPaint> fLazy;
325};
326
327static void set_bounds(const SkGlyph& g, SkRect* bounds) {
328 bounds->set(SkIntToScalar(g.fLeft),
329 SkIntToScalar(g.fTop),
330 SkIntToScalar(g.fLeft + g.fWidth),
331 SkIntToScalar(g.fTop + g.fHeight));
332}
333
334static void join_bounds_x(const SkGlyph& g, SkRect* bounds, SkScalar dx) {
335 bounds->join(SkIntToScalar(g.fLeft) + dx,
336 SkIntToScalar(g.fTop),
337 SkIntToScalar(g.fLeft + g.fWidth) + dx,
338 SkIntToScalar(g.fTop + g.fHeight));
339}
340
341static void join_bounds_y(const SkGlyph& g, SkRect* bounds, SkScalar dy) {
342 bounds->join(SkIntToScalar(g.fLeft),
343 SkIntToScalar(g.fTop) + dy,
344 SkIntToScalar(g.fLeft + g.fWidth),
345 SkIntToScalar(g.fTop + g.fHeight) + dy);
346}
347
348typedef void (*JoinBoundsProc)(const SkGlyph&, SkRect*, SkScalar);
349
350// xyIndex is 0 for fAdvanceX or 1 for fAdvanceY
351static SkScalar advance(const SkGlyph& glyph, int xyIndex) {
352 SkASSERT(0 == xyIndex || 1 == xyIndex);
353 return SkFloatToScalar((&glyph.fAdvanceX)[xyIndex]);
354}
355
356SkScalar SkPaint::measure_text(SkGlyphCache* cache,
357 const char* text, size_t byteLength,
358 int* count, SkRect* bounds) const {
359 SkASSERT(count);
360 if (byteLength == 0) {
361 *count = 0;
362 if (bounds) {
363 bounds->setEmpty();
364 }
365 return 0;
366 }
367
368 GlyphCacheProc glyphCacheProc = SkPaint::GetGlyphCacheProc(this->getTextEncoding(),
369 nullptr != bounds);
370
371 int xyIndex;
372 JoinBoundsProc joinBoundsProc;
373 if (this->isVerticalText()) {
374 xyIndex = 1;
375 joinBoundsProc = join_bounds_y;
376 } else {
377 xyIndex = 0;
378 joinBoundsProc = join_bounds_x;
379 }
380
381 int n = 1;
382 const char* stop = (const char*)text + byteLength;
383 const SkGlyph* g = &glyphCacheProc(cache, &text, stop);
384 SkScalar x = advance(*g, xyIndex);
385
386 if (nullptr == bounds) {
387 for (; text < stop; n++) {
388 x += advance(glyphCacheProc(cache, &text, stop), xyIndex);
389 }
390 } else {
391 set_bounds(*g, bounds);
392
393 for (; text < stop; n++) {
394 g = &glyphCacheProc(cache, &text, stop);
395 joinBoundsProc(*g, bounds, x);
396 x += advance(*g, xyIndex);
397 }
398 }
399 SkASSERT(text == stop);
400
401 *count = n;
402 return x;
403}
404
405SkScalar SkPaint::measureText(const void* textData, size_t length, SkRect* bounds) const {
406 const char* text = (const char*)textData;
407 SkASSERT(text != nullptr || length == 0);
408
409 SkCanonicalizePaint canon(*this);
410 const SkPaint& paint = canon.getPaint();
411 SkScalar scale = canon.getScale();
412
413 auto cache = SkStrikeCache::FindOrCreateStrikeExclusive(paint);
414
415 SkScalar width = 0;
416
417 if (length > 0) {
418 int tempCount;
419
420 width = paint.measure_text(cache.get(), text, length, &tempCount, bounds);
421 if (scale) {
422 width *= scale;
423 if (bounds) {
424 bounds->fLeft *= scale;
425 bounds->fTop *= scale;
426 bounds->fRight *= scale;
427 bounds->fBottom *= scale;
428 }
429 }
430 } else if (bounds) {
431 // ensure that even if we don't measure_text we still update the bounds
432 bounds->setEmpty();
433 }
434 return width;
435}
436
437size_t SkPaint::breakText(const void* textD, size_t length, SkScalar maxWidth,
438 SkScalar* measuredWidth) const {
439 if (0 == length || 0 >= maxWidth) {
440 if (measuredWidth) {
441 *measuredWidth = 0;
442 }
443 return 0;
444 }
445
446 if (0 == fTextSize) {
447 if (measuredWidth) {
448 *measuredWidth = 0;
449 }
450 return length;
451 }
452
453 SkASSERT(textD != nullptr);
454 const char* text = (const char*)textD;
455 const char* stop = text + length;
456
457 SkCanonicalizePaint canon(*this);
458 const SkPaint& paint = canon.getPaint();
459 SkScalar scale = canon.getScale();
460
461 // adjust max in case we changed the textSize in paint
462 if (scale) {
463 maxWidth /= scale;
464 }
465
466 auto cache = SkStrikeCache::FindOrCreateStrikeExclusive(paint);
467
468 GlyphCacheProc glyphCacheProc = SkPaint::GetGlyphCacheProc(paint.getTextEncoding(),
469 false);
470 const int xyIndex = paint.isVerticalText() ? 1 : 0;
471 SkScalar width = 0;
472
473 while (text < stop) {
474 const char* curr = text;
475 SkScalar x = advance(glyphCacheProc(cache.get(), &text, stop), xyIndex);
476 if ((width += x) > maxWidth) {
477 width -= x;
478 text = curr;
479 break;
480 }
481 }
482
483 if (measuredWidth) {
484 if (scale) {
485 width *= scale;
486 }
487 *measuredWidth = width;
488 }
489
490 // return the number of bytes measured
491 return text - stop + length;
492}
493
494///////////////////////////////////////////////////////////////////////////////
495
496SkScalar SkPaint::getFontMetrics(FontMetrics* metrics, SkScalar zoom) const {
497 SkCanonicalizePaint canon(*this);
498 const SkPaint& paint = canon.getPaint();
499 SkScalar scale = canon.getScale();
500
501 SkMatrix zoomMatrix, *zoomPtr = nullptr;
502 if (zoom) {
503 zoomMatrix.setScale(zoom, zoom);
504 zoomPtr = &zoomMatrix;
505 }
506
507 FontMetrics storage;
508 if (nullptr == metrics) {
509 metrics = &storage;
510 }
511
512 SkAutoDescriptor ad;
513 SkScalerContextEffects effects;
514
515 auto desc = SkScalerContext::CreateDescriptorAndEffectsUsingPaint(
516 paint, nullptr, SkScalerContextFlags::kNone, zoomPtr, &ad, &effects);
517
518 {
519 auto typeface = SkPaintPriv::GetTypefaceOrDefault(paint);
520 auto cache = SkStrikeCache::FindOrCreateStrikeExclusive(*desc, effects, *typeface);
521 *metrics = cache->getFontMetrics();
522 }
523
524 if (scale) {
525 SkPaintPriv::ScaleFontMetrics(metrics, scale);
526 }
527 return metrics->fDescent - metrics->fAscent + metrics->fLeading;
528}
529
530///////////////////////////////////////////////////////////////////////////////
531
532static void set_bounds(const SkGlyph& g, SkRect* bounds, SkScalar scale) {
533 bounds->set(g.fLeft * scale,
534 g.fTop * scale,
535 (g.fLeft + g.fWidth) * scale,
536 (g.fTop + g.fHeight) * scale);
537}
538
539int SkPaint::getTextWidths(const void* textData, size_t byteLength,
540 SkScalar widths[], SkRect bounds[]) const {
541 if (0 == byteLength) {
542 return 0;
543 }
544
545 SkASSERT(textData);
546
547 if (nullptr == widths && nullptr == bounds) {
548 return this->countText(textData, byteLength);
549 }
550
551 SkCanonicalizePaint canon(*this);
552 const SkPaint& paint = canon.getPaint();
553 SkScalar scale = canon.getScale();
554
555 auto cache = SkStrikeCache::FindOrCreateStrikeExclusive(paint);
556 GlyphCacheProc glyphCacheProc = SkPaint::GetGlyphCacheProc(paint.getTextEncoding(),
557 nullptr != bounds);
558
559 const char* text = (const char*)textData;
560 const char* stop = text + byteLength;
561 int count = 0;
562 const int xyIndex = paint.isVerticalText() ? 1 : 0;
563
564 if (scale) {
565 while (text < stop) {
566 const SkGlyph& g = glyphCacheProc(cache.get(), &text, stop);
567 if (widths) {
568 *widths++ = advance(g, xyIndex) * scale;
569 }
570 if (bounds) {
571 set_bounds(g, bounds++, scale);
572 }
573 ++count;
574 }
575 } else {
576 while (text < stop) {
577 const SkGlyph& g = glyphCacheProc(cache.get(), &text, stop);
578 if (widths) {
579 *widths++ = advance(g, xyIndex);
580 }
581 if (bounds) {
582 set_bounds(g, bounds++);
583 }
584 ++count;
585 }
586 }
587
588 SkASSERT(text == stop);
589 return count;
590}
591
592///////////////////////////////////////////////////////////////////////////////
593
594#include "SkDraw.h"
595
596void SkPaint::getTextPath(const void* textData, size_t length,
597 SkScalar x, SkScalar y, SkPath* path) const {
598 SkASSERT(length == 0 || textData != nullptr);
599
600 const char* text = (const char*)textData;
601 if (text == nullptr || length == 0 || path == nullptr) {
602 return;
603 }
604
605 SkTextToPathIter iter(text, length, *this, false);
606 SkMatrix matrix;
607 SkScalar prevXPos = 0;
608
609 matrix.setScale(iter.getPathScale(), iter.getPathScale());
610 matrix.postTranslate(x, y);
611 path->reset();
612
613 SkScalar xpos;
614 const SkPath* iterPath;
615 while (iter.next(&iterPath, &xpos)) {
616 matrix.postTranslate(xpos - prevXPos, 0);
617 if (iterPath) {
618 path->addPath(*iterPath, matrix);
619 }
620 prevXPos = xpos;
621 }
622}
623
624void SkPaint::getPosTextPath(const void* textData, size_t length,
625 const SkPoint pos[], SkPath* path) const {
626 SkASSERT(length == 0 || textData != nullptr);
627
628 const char* text = (const char*)textData;
629 if (text == nullptr || length == 0 || path == nullptr) {
630 return;
631 }
632
633 SkTextToPathIter iter(text, length, *this, false);
634 SkMatrix matrix;
635 SkPoint prevPos;
636 prevPos.set(0, 0);
637
638 matrix.setScale(iter.getPathScale(), iter.getPathScale());
639 path->reset();
640
641 unsigned int i = 0;
642 const SkPath* iterPath;
643 while (iter.next(&iterPath, nullptr)) {
644 matrix.postTranslate(pos[i].fX - prevPos.fX, pos[i].fY - prevPos.fY);
645 if (iterPath) {
646 path->addPath(*iterPath, matrix);
647 }
648 prevPos = pos[i];
649 i++;
650 }
651}
652
653template <SkTextInterceptsIter::TextType TextType, typename Func>
654int GetTextIntercepts(const SkPaint& paint, const void* text, size_t length,
655 const SkScalar bounds[2], SkScalar* array, Func posMaker) {
656 SkASSERT(length == 0 || text != nullptr);
657 if (!length) {
658 return 0;
659 }
660
661 const SkPoint pos0 = posMaker(0);
662 SkTextInterceptsIter iter(static_cast<const char*>(text), length, paint, bounds,
663 pos0.x(), pos0.y(), TextType);
664
665 int i = 0;
666 int count = 0;
667 while (iter.next(array, &count)) {
668 if (TextType == SkTextInterceptsIter::TextType::kPosText) {
669 const SkPoint pos = posMaker(++i);
670 iter.setPosition(pos.x(), pos.y());
671 }
672 }
673
674 return count;
675}
676
677int SkPaint::getTextIntercepts(const void* textData, size_t length,
678 SkScalar x, SkScalar y, const SkScalar bounds[2],
679 SkScalar* array) const {
680
681 return GetTextIntercepts<SkTextInterceptsIter::TextType::kText>(
682 *this, textData, length, bounds, array, [&x, &y] (int) -> SkPoint {
683 return SkPoint::Make(x, y);
684 });
685}
686
687int SkPaint::getPosTextIntercepts(const void* textData, size_t length, const SkPoint pos[],
688 const SkScalar bounds[2], SkScalar* array) const {
689
690 return GetTextIntercepts<SkTextInterceptsIter::TextType::kPosText>(
691 *this, textData, length, bounds, array, [&pos] (int i) -> SkPoint {
692 return pos[i];
693 });
694}
695
696int SkPaint::getPosTextHIntercepts(const void* textData, size_t length, const SkScalar xpos[],
697 SkScalar constY, const SkScalar bounds[2],
698 SkScalar* array) const {
699
700 return GetTextIntercepts<SkTextInterceptsIter::TextType::kPosText>(
701 *this, textData, length, bounds, array, [&xpos, &constY] (int i) -> SkPoint {
702 return SkPoint::Make(xpos[i], constY);
703 });
704}
705
706int SkPaint::getTextBlobIntercepts(const SkTextBlob* blob, const SkScalar bounds[2],
707 SkScalar* intervals) const {
708 int count = 0;
709 SkPaint runPaint(*this);
710
711 SkTextBlobRunIterator it(blob);
712 while (!it.done()) {
713 it.applyFontToPaint(&runPaint);
714 const size_t runByteCount = it.glyphCount() * sizeof(SkGlyphID);
715 SkScalar* runIntervals = intervals ? intervals + count : nullptr;
716
717 switch (it.positioning()) {
718 case SkTextBlobRunIterator::kDefault_Positioning:
719 count += runPaint.getTextIntercepts(it.glyphs(), runByteCount, it.offset().x(),
720 it.offset().y(), bounds, runIntervals);
721 break;
722 case SkTextBlobRunIterator::kHorizontal_Positioning:
723 count += runPaint.getPosTextHIntercepts(it.glyphs(), runByteCount, it.pos(),
724 it.offset().y(), bounds, runIntervals);
725 break;
726 case SkTextBlobRunIterator::kFull_Positioning:
727 count += runPaint.getPosTextIntercepts(it.glyphs(), runByteCount,
728 reinterpret_cast<const SkPoint*>(it.pos()),
729 bounds, runIntervals);
730 break;
731 }
732
733 it.next();
734 }
735
736 return count;
737}
738
739SkRect SkPaint::getFontBounds() const {
740 SkMatrix m;
741 m.setScale(fTextSize * fTextScaleX, fTextSize);
742 m.postSkew(fTextSkewX, 0);
743
744 SkTypeface* typeface = SkPaintPriv::GetTypefaceOrDefault(*this);
745
746 SkRect bounds;
747 m.mapRect(&bounds, typeface->getBounds());
748 return bounds;
749}
750
751// return true if the paint is just a single color (i.e. not a shader). If its
752// a shader, then we can't compute a const luminance for it :(
753static bool just_a_color(const SkPaint& paint, SkColor* color) {
754 SkColor c = paint.getColor();
755
756 const auto* shader = as_SB(paint.getShader());
757 if (shader && !shader->asLuminanceColor(&c)) {
758 return false;
759 }
760 if (paint.getColorFilter()) {
761 c = paint.getColorFilter()->filterColor(c);
762 }
763 if (color) {
764 *color = c;
765 }
766 return true;
767}
768
769SkColor SkPaint::computeLuminanceColor() const {
770 SkColor c;
771 if (!just_a_color(*this, &c)) {
772 c = SkColorSetRGB(0x7F, 0x80, 0x7F);
773 }
774 return c;
775}
776
777///////////////////////////////////////////////////////////////////////////////
778
779static bool has_thick_frame(const SkPaint& paint) {
780 return paint.getStrokeWidth() > 0 &&
781 paint.getStyle() != SkPaint::kFill_Style;
782}
783
784SkTextBaseIter::SkTextBaseIter(const char text[], size_t length,
785 const SkPaint& paint,
786 bool applyStrokeAndPathEffects)
787 : fPaint(paint) {
788 fGlyphCacheProc = SkPaint::GetGlyphCacheProc(paint.getTextEncoding(), true);
789
790 fPaint.setLinearText(true);
791 fPaint.setMaskFilter(nullptr); // don't want this affecting our path-cache lookup
792
793 if (fPaint.getPathEffect() == nullptr && !has_thick_frame(fPaint)) {
794 applyStrokeAndPathEffects = false;
795 }
796
797 // can't use our canonical size if we need to apply patheffects
798 if (fPaint.getPathEffect() == nullptr) {
799 fPaint.setTextSize(SkIntToScalar(SkPaint::kCanonicalTextSizeForPaths));
800 fScale = paint.getTextSize() / SkPaint::kCanonicalTextSizeForPaths;
801 // Note: fScale can be zero here (even if it wasn't before the divide). It can also
802 // be very very small. We call sk_ieee_float_divide below to ensure IEEE divide behavior,
803 // since downstream we will check for the resulting coordinates being non-finite anyway.
804 // Thus we don't need to check for zero here.
805 if (has_thick_frame(fPaint)) {
806 fPaint.setStrokeWidth(sk_ieee_float_divide(fPaint.getStrokeWidth(), fScale));
807 }
808 } else {
809 fScale = SK_Scalar1;
810 }
811
812 if (!applyStrokeAndPathEffects) {
813 fPaint.setStyle(SkPaint::kFill_Style);
814 fPaint.setPathEffect(nullptr);
815 }
816
817 // SRGBTODO: Is this correct?
818 fCache = SkStrikeCache::FindOrCreateStrikeExclusive(
819 fPaint, nullptr,
820 SkScalerContextFlags::kFakeGammaAndBoostContrast, nullptr);
821
822 SkPaint::Style style = SkPaint::kFill_Style;
823 sk_sp<SkPathEffect> pe;
824
825 if (!applyStrokeAndPathEffects) {
826 style = paint.getStyle(); // restore
827 pe = paint.refPathEffect(); // restore
828 }
829 fPaint.setStyle(style);
830 fPaint.setPathEffect(pe);
831 fPaint.setMaskFilter(paint.refMaskFilter()); // restore
832
833 // now compute fXOffset if needed
834
835 SkScalar xOffset = 0;
836 if (paint.getTextAlign() != SkPaint::kLeft_Align) { // need to measure first
837 int count;
838 SkScalar width = fPaint.measure_text(fCache.get(), text, length, &count, nullptr) * fScale;
839 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
840 width = SkScalarHalf(width);
841 }
842 xOffset = -width;
843 }
844 fXPos = xOffset;
845 fPrevAdvance = 0;
846
847 fText = text;
848 fStop = text + length;
849
850 fXYIndex = paint.isVerticalText() ? 1 : 0;
851}
852
853bool SkTextToPathIter::next(const SkPath** path, SkScalar* xpos) {
854 if (fText < fStop) {
855 const SkGlyph& glyph = fGlyphCacheProc(fCache.get(), &fText, fStop);
856
857 fXPos += fPrevAdvance * fScale;
858 fPrevAdvance = advance(glyph, fXYIndex); // + fPaint.getTextTracking();
859
860 if (glyph.fWidth) {
861 if (path) {
862 *path = fCache->findPath(glyph);
863 }
864 } else {
865 if (path) {
866 *path = nullptr;
867 }
868 }
869 if (xpos) {
870 *xpos = fXPos;
871 }
872 return true;
873 }
874 return false;
875}
876
877bool SkTextInterceptsIter::next(SkScalar* array, int* count) {
878 const SkGlyph& glyph = fGlyphCacheProc(fCache.get(), &fText, fStop);
879 fXPos += fPrevAdvance * fScale;
880 fPrevAdvance = advance(glyph, fXYIndex); // + fPaint.getTextTracking();
881 if (fCache->findPath(glyph)) {
882 fCache->findIntercepts(fBounds, fScale, fXPos, SkToBool(fXYIndex),
883 const_cast<SkGlyph*>(&glyph), array, count);
884 }
885 return fText < fStop;
886}