Mike Reed | dee25cc | 2018-10-05 11:48:01 -0400 | [diff] [blame] | 1 | /* |
| 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 | |
| 33 | static SkScalar mag2(SkScalar x, SkScalar y) { |
| 34 | return x * x + y * y; |
| 35 | } |
| 36 | |
| 37 | static 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 | |
| 43 | bool 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 | |
| 52 | SkScalar 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 | |
Mike Reed | 4ff4085 | 2018-11-23 16:20:28 -0500 | [diff] [blame] | 64 | int SkPaint::countText(const void* text, size_t length) const { |
Mike Reed | 0f9d33e | 2018-12-05 10:54:05 -0500 | [diff] [blame] | 65 | return SkFont::LEGACY_ExtractFromPaint(*this).countText(text, length, this->getTextEncoding()); |
Mike Reed | dee25cc | 2018-10-05 11:48:01 -0400 | [diff] [blame] | 66 | } |
| 67 | |
Mike Reed | 4ff4085 | 2018-11-23 16:20:28 -0500 | [diff] [blame] | 68 | int SkPaint::textToGlyphs(const void* text, size_t length, uint16_t glyphs[]) const { |
| 69 | return SkFont::LEGACY_ExtractFromPaint(*this).textToGlyphs(text, length, |
Mike Reed | 0f9d33e | 2018-12-05 10:54:05 -0500 | [diff] [blame] | 70 | this->getTextEncoding(), |
Mike Reed | 4ff4085 | 2018-11-23 16:20:28 -0500 | [diff] [blame] | 71 | glyphs, length); |
Mike Reed | dee25cc | 2018-10-05 11:48:01 -0400 | [diff] [blame] | 72 | } |
| 73 | |
Mike Reed | 4ff4085 | 2018-11-23 16:20:28 -0500 | [diff] [blame] | 74 | bool SkPaint::containsText(const void* text, size_t length) const { |
| 75 | return SkFont::LEGACY_ExtractFromPaint(*this).containsText(text, length, |
Mike Reed | 0f9d33e | 2018-12-05 10:54:05 -0500 | [diff] [blame] | 76 | this->getTextEncoding()); |
Mike Reed | dee25cc | 2018-10-05 11:48:01 -0400 | [diff] [blame] | 77 | } |
| 78 | |
| 79 | void SkPaint::glyphsToUnichars(const uint16_t glyphs[], int count, SkUnichar textData[]) const { |
Mike Reed | 4e336e3 | 2018-12-06 17:08:11 -0500 | [diff] [blame] | 80 | SkFont::LEGACY_ExtractFromPaint(*this).glyphsToUnichars(glyphs, count, textData); |
Mike Reed | dee25cc | 2018-10-05 11:48:01 -0400 | [diff] [blame] | 81 | } |
| 82 | |
| 83 | /////////////////////////////////////////////////////////////////////////////// |
| 84 | |
| 85 | static const SkGlyph& sk_getMetrics_utf8_next(SkGlyphCache* cache, |
| 86 | const char** text, |
| 87 | const char* stop) { |
| 88 | SkASSERT(cache != nullptr); |
| 89 | SkASSERT(text != nullptr); |
| 90 | |
| 91 | return cache->getUnicharMetrics(SkUTF::NextUTF8(text, stop)); |
| 92 | } |
| 93 | |
| 94 | static const SkGlyph& sk_getMetrics_utf16_next(SkGlyphCache* cache, |
| 95 | const char** text, |
| 96 | const char* stop) { |
| 97 | SkASSERT(cache != nullptr); |
| 98 | SkASSERT(text != nullptr); |
| 99 | |
| 100 | return cache->getUnicharMetrics( |
| 101 | SkUTF::NextUTF16((const uint16_t**)text, (const uint16_t*)stop)); |
| 102 | } |
| 103 | |
| 104 | static const SkGlyph& sk_getMetrics_utf32_next(SkGlyphCache* cache, |
| 105 | const char** text, |
| 106 | const char* stop) { |
| 107 | SkASSERT(cache != nullptr); |
| 108 | SkASSERT(text != nullptr); |
| 109 | |
| 110 | return cache->getUnicharMetrics(SkUTF::NextUTF32((const int32_t**)text, (const int32_t*)stop)); |
| 111 | } |
| 112 | |
| 113 | static const SkGlyph& sk_getMetrics_glyph_next(SkGlyphCache* cache, |
| 114 | const char** text, |
| 115 | const char* stop) { |
| 116 | SkASSERT(cache != nullptr); |
| 117 | SkASSERT(text != nullptr); |
| 118 | |
| 119 | const uint16_t* ptr = *(const uint16_t**)text; |
| 120 | unsigned glyphID = *ptr; |
| 121 | ptr += 1; |
| 122 | *text = (const char*)ptr; |
| 123 | return cache->getGlyphIDMetrics(glyphID); |
| 124 | } |
| 125 | |
| 126 | static const SkGlyph& sk_getAdvance_utf8_next(SkGlyphCache* cache, |
| 127 | const char** text, |
| 128 | const char* stop) { |
| 129 | SkASSERT(cache != nullptr); |
| 130 | SkASSERT(text != nullptr); |
| 131 | |
| 132 | return cache->getUnicharAdvance(SkUTF::NextUTF8(text, stop)); |
| 133 | } |
| 134 | |
| 135 | static const SkGlyph& sk_getAdvance_utf16_next(SkGlyphCache* cache, |
| 136 | const char** text, |
| 137 | const char* stop) { |
| 138 | SkASSERT(cache != nullptr); |
| 139 | SkASSERT(text != nullptr); |
| 140 | |
| 141 | return cache->getUnicharAdvance( |
| 142 | SkUTF::NextUTF16((const uint16_t**)text, (const uint16_t*)stop)); |
| 143 | } |
| 144 | |
| 145 | static const SkGlyph& sk_getAdvance_utf32_next(SkGlyphCache* cache, |
| 146 | const char** text, |
| 147 | const char* stop) { |
| 148 | SkASSERT(cache != nullptr); |
| 149 | SkASSERT(text != nullptr); |
| 150 | |
| 151 | return cache->getUnicharAdvance(SkUTF::NextUTF32((const int32_t**)text, (const int32_t*)stop)); |
| 152 | } |
| 153 | |
| 154 | static const SkGlyph& sk_getAdvance_glyph_next(SkGlyphCache* cache, |
| 155 | const char** text, |
| 156 | const char* stop) { |
| 157 | SkASSERT(cache != nullptr); |
| 158 | SkASSERT(text != nullptr); |
| 159 | |
| 160 | const uint16_t* ptr = *(const uint16_t**)text; |
| 161 | unsigned glyphID = *ptr; |
| 162 | ptr += 1; |
| 163 | *text = (const char*)ptr; |
| 164 | return cache->getGlyphIDAdvance(glyphID); |
| 165 | } |
| 166 | |
Mike Reed | 064ca01 | 2018-11-10 09:13:13 -0500 | [diff] [blame] | 167 | SkFontPriv::GlyphCacheProc SkFontPriv::GetGlyphCacheProc(SkTextEncoding encoding, |
| 168 | bool needFullMetrics) { |
Mike Reed | dee25cc | 2018-10-05 11:48:01 -0400 | [diff] [blame] | 169 | static const GlyphCacheProc gGlyphCacheProcs[] = { |
| 170 | sk_getMetrics_utf8_next, |
| 171 | sk_getMetrics_utf16_next, |
| 172 | sk_getMetrics_utf32_next, |
| 173 | sk_getMetrics_glyph_next, |
| 174 | |
| 175 | sk_getAdvance_utf8_next, |
| 176 | sk_getAdvance_utf16_next, |
| 177 | sk_getAdvance_utf32_next, |
| 178 | sk_getAdvance_glyph_next, |
| 179 | }; |
| 180 | |
Mike Reed | d723ee1 | 2018-11-13 14:21:14 -0800 | [diff] [blame] | 181 | unsigned index = static_cast<unsigned>(encoding); |
Mike Reed | dee25cc | 2018-10-05 11:48:01 -0400 | [diff] [blame] | 182 | |
| 183 | if (!needFullMetrics) { |
| 184 | index += 4; |
| 185 | } |
| 186 | |
| 187 | SkASSERT(index < SK_ARRAY_COUNT(gGlyphCacheProcs)); |
| 188 | return gGlyphCacheProcs[index]; |
| 189 | } |
| 190 | |
| 191 | /////////////////////////////////////////////////////////////////////////////// |
| 192 | |
| 193 | SkScalar SkPaint::setupForAsPaths() { |
| 194 | |
| 195 | constexpr uint32_t flagsToIgnore = SkPaint::kLinearText_Flag | |
| 196 | SkPaint::kLCDRenderText_Flag | |
| 197 | SkPaint::kEmbeddedBitmapText_Flag | |
| 198 | SkPaint::kAutoHinting_Flag; |
| 199 | |
| 200 | uint32_t flags = this->getFlags(); |
| 201 | |
| 202 | // clear the flags we don't care about |
| 203 | flags &= ~flagsToIgnore; |
| 204 | |
| 205 | // set the flags we do care about |
| 206 | flags |= SkPaint::kSubpixelText_Flag; |
| 207 | |
| 208 | this->setFlags(flags); |
Mike Reed | 9edbf42 | 2018-11-07 19:54:33 -0500 | [diff] [blame] | 209 | this->setHinting(kNo_SkFontHinting); |
Mike Reed | dee25cc | 2018-10-05 11:48:01 -0400 | [diff] [blame] | 210 | this->setStyle(SkPaint::kFill_Style); |
| 211 | this->setPathEffect(nullptr); |
| 212 | |
| 213 | SkScalar textSize = fTextSize; |
| 214 | this->setTextSize(kCanonicalTextSizeForPaths); |
| 215 | return textSize / kCanonicalTextSizeForPaths; |
| 216 | } |
| 217 | |
| 218 | class SkCanonicalizePaint { |
| 219 | public: |
| 220 | SkCanonicalizePaint(const SkPaint& paint) : fPaint(&paint), fScale(0) { |
| 221 | if (paint.isLinearText() || SkDraw::ShouldDrawTextAsPaths(paint, SkMatrix::I())) { |
| 222 | SkPaint* p = fLazy.set(paint); |
| 223 | fScale = p->setupForAsPaths(); |
| 224 | fPaint = p; |
| 225 | } |
| 226 | } |
| 227 | |
| 228 | const SkPaint& getPaint() const { return *fPaint; } |
| 229 | |
| 230 | /** |
| 231 | * Returns 0 if the paint was unmodified, or the scale factor need to |
| 232 | * the original textSize |
| 233 | */ |
| 234 | SkScalar getScale() const { return fScale; } |
| 235 | |
| 236 | private: |
| 237 | const SkPaint* fPaint; |
| 238 | SkScalar fScale; |
| 239 | SkTLazy<SkPaint> fLazy; |
| 240 | }; |
| 241 | |
| 242 | static void set_bounds(const SkGlyph& g, SkRect* bounds) { |
| 243 | bounds->set(SkIntToScalar(g.fLeft), |
| 244 | SkIntToScalar(g.fTop), |
| 245 | SkIntToScalar(g.fLeft + g.fWidth), |
| 246 | SkIntToScalar(g.fTop + g.fHeight)); |
| 247 | } |
| 248 | |
| 249 | static void join_bounds_x(const SkGlyph& g, SkRect* bounds, SkScalar dx) { |
| 250 | bounds->join(SkIntToScalar(g.fLeft) + dx, |
| 251 | SkIntToScalar(g.fTop), |
| 252 | SkIntToScalar(g.fLeft + g.fWidth) + dx, |
| 253 | SkIntToScalar(g.fTop + g.fHeight)); |
| 254 | } |
| 255 | |
Mike Reed | dee25cc | 2018-10-05 11:48:01 -0400 | [diff] [blame] | 256 | // xyIndex is 0 for fAdvanceX or 1 for fAdvanceY |
Mike Reed | c88cc77 | 2018-10-23 12:05:47 -0400 | [diff] [blame] | 257 | static SkScalar advance(const SkGlyph& glyph) { |
| 258 | return SkFloatToScalar(glyph.fAdvanceX); |
Mike Reed | dee25cc | 2018-10-05 11:48:01 -0400 | [diff] [blame] | 259 | } |
| 260 | |
| 261 | SkScalar SkPaint::measure_text(SkGlyphCache* cache, |
| 262 | const char* text, size_t byteLength, |
| 263 | int* count, SkRect* bounds) const { |
| 264 | SkASSERT(count); |
| 265 | if (byteLength == 0) { |
| 266 | *count = 0; |
| 267 | if (bounds) { |
| 268 | bounds->setEmpty(); |
| 269 | } |
| 270 | return 0; |
| 271 | } |
| 272 | |
Mike Reed | 064ca01 | 2018-11-10 09:13:13 -0500 | [diff] [blame] | 273 | SkFontPriv::GlyphCacheProc glyphCacheProc = SkFontPriv::GetGlyphCacheProc( |
Mike Reed | 0f9d33e | 2018-12-05 10:54:05 -0500 | [diff] [blame] | 274 | this->getTextEncoding(), nullptr != bounds); |
Mike Reed | dee25cc | 2018-10-05 11:48:01 -0400 | [diff] [blame] | 275 | |
Mike Reed | dee25cc | 2018-10-05 11:48:01 -0400 | [diff] [blame] | 276 | int n = 1; |
| 277 | const char* stop = (const char*)text + byteLength; |
| 278 | const SkGlyph* g = &glyphCacheProc(cache, &text, stop); |
Mike Reed | c88cc77 | 2018-10-23 12:05:47 -0400 | [diff] [blame] | 279 | SkScalar x = advance(*g); |
Mike Reed | dee25cc | 2018-10-05 11:48:01 -0400 | [diff] [blame] | 280 | |
| 281 | if (nullptr == bounds) { |
| 282 | for (; text < stop; n++) { |
Mike Reed | c88cc77 | 2018-10-23 12:05:47 -0400 | [diff] [blame] | 283 | x += advance(glyphCacheProc(cache, &text, stop)); |
Mike Reed | dee25cc | 2018-10-05 11:48:01 -0400 | [diff] [blame] | 284 | } |
| 285 | } else { |
| 286 | set_bounds(*g, bounds); |
| 287 | |
| 288 | for (; text < stop; n++) { |
| 289 | g = &glyphCacheProc(cache, &text, stop); |
Mike Reed | c88cc77 | 2018-10-23 12:05:47 -0400 | [diff] [blame] | 290 | join_bounds_x(*g, bounds, x); |
| 291 | x += advance(*g); |
Mike Reed | dee25cc | 2018-10-05 11:48:01 -0400 | [diff] [blame] | 292 | } |
| 293 | } |
| 294 | SkASSERT(text == stop); |
| 295 | |
| 296 | *count = n; |
| 297 | return x; |
| 298 | } |
| 299 | |
| 300 | SkScalar SkPaint::measureText(const void* textData, size_t length, SkRect* bounds) const { |
| 301 | const char* text = (const char*)textData; |
| 302 | SkASSERT(text != nullptr || length == 0); |
| 303 | |
| 304 | SkCanonicalizePaint canon(*this); |
| 305 | const SkPaint& paint = canon.getPaint(); |
| 306 | SkScalar scale = canon.getScale(); |
| 307 | |
Mike Reed | 32c6066 | 2018-11-28 10:28:07 -0500 | [diff] [blame] | 308 | const SkFont font = SkFont::LEGACY_ExtractFromPaint(paint); |
| 309 | auto cache = SkStrikeCache::FindOrCreateStrikeWithNoDeviceExclusive(font, paint); |
Mike Reed | dee25cc | 2018-10-05 11:48:01 -0400 | [diff] [blame] | 310 | |
| 311 | SkScalar width = 0; |
| 312 | |
| 313 | if (length > 0) { |
| 314 | int tempCount; |
| 315 | |
| 316 | width = paint.measure_text(cache.get(), text, length, &tempCount, bounds); |
| 317 | if (scale) { |
| 318 | width *= scale; |
| 319 | if (bounds) { |
| 320 | bounds->fLeft *= scale; |
| 321 | bounds->fTop *= scale; |
| 322 | bounds->fRight *= scale; |
| 323 | bounds->fBottom *= scale; |
| 324 | } |
| 325 | } |
| 326 | } else if (bounds) { |
| 327 | // ensure that even if we don't measure_text we still update the bounds |
| 328 | bounds->setEmpty(); |
| 329 | } |
| 330 | return width; |
| 331 | } |
| 332 | |
Mike Reed | bb786b4 | 2018-12-06 22:10:29 -0500 | [diff] [blame] | 333 | #ifdef SK_SUPPORT_LEGACY_PAINT_BREAKTEXT |
Mike Reed | 2bf4339 | 2018-12-06 23:05:51 +0000 | [diff] [blame] | 334 | size_t SkPaint::breakText(const void* textD, size_t length, SkScalar maxWidth, |
| 335 | SkScalar* measuredWidth) const { |
| 336 | return SkFont::LEGACY_ExtractFromPaint(*this).breakText(textD, length, |
| 337 | this->getTextEncoding(), maxWidth, measuredWidth); |
| 338 | } |
Mike Reed | bb786b4 | 2018-12-06 22:10:29 -0500 | [diff] [blame] | 339 | #endif |
Mike Reed | 2bf4339 | 2018-12-06 23:05:51 +0000 | [diff] [blame] | 340 | |
Mike Reed | cb6f53e | 2018-11-06 12:44:54 -0500 | [diff] [blame] | 341 | SkScalar SkPaint::getFontMetrics(SkFontMetrics* metrics) const { |
Mike Reed | 4ff4085 | 2018-11-23 16:20:28 -0500 | [diff] [blame] | 342 | return SkFont::LEGACY_ExtractFromPaint(*this).getMetrics(metrics); |
Mike Reed | dee25cc | 2018-10-05 11:48:01 -0400 | [diff] [blame] | 343 | } |
| 344 | |
| 345 | /////////////////////////////////////////////////////////////////////////////// |
| 346 | |
Mike Reed | 54bf684 | 2018-11-27 12:27:15 -0500 | [diff] [blame] | 347 | int SkPaint::getTextWidths(const void* text, size_t len, SkScalar widths[], SkRect bounds[]) const { |
| 348 | const SkFont font = SkFont::LEGACY_ExtractFromPaint(*this); |
Mike Reed | 0f9d33e | 2018-12-05 10:54:05 -0500 | [diff] [blame] | 349 | SkAutoToGlyphs gly(font, text, len, this->getTextEncoding()); |
Mike Reed | 54bf684 | 2018-11-27 12:27:15 -0500 | [diff] [blame] | 350 | font.getWidthsBounds(gly.glyphs(), gly.count(), widths, bounds, this); |
| 351 | return gly.count(); |
Mike Reed | dee25cc | 2018-10-05 11:48:01 -0400 | [diff] [blame] | 352 | } |
| 353 | |
| 354 | /////////////////////////////////////////////////////////////////////////////// |
| 355 | |
| 356 | #include "SkDraw.h" |
| 357 | |
Mike Reed | c16abee | 2018-11-24 13:27:27 -0500 | [diff] [blame] | 358 | struct PathPosRec { |
| 359 | SkPath* fDst; |
| 360 | const SkPoint* fPos; |
| 361 | }; |
Mike Reed | 2871509 | 2018-11-24 22:55:54 -0500 | [diff] [blame] | 362 | static void PathPosProc(const SkPath* src, const SkMatrix& mx, void* ctx) { |
Mike Reed | c16abee | 2018-11-24 13:27:27 -0500 | [diff] [blame] | 363 | PathPosRec* rec = static_cast<PathPosRec*>(ctx); |
| 364 | if (src) { |
Mike Reed | 2871509 | 2018-11-24 22:55:54 -0500 | [diff] [blame] | 365 | SkMatrix m(mx); |
| 366 | m.postTranslate(rec->fPos->fX, rec->fPos->fY); |
| 367 | rec->fDst->addPath(*src, m); |
Mike Reed | dee25cc | 2018-10-05 11:48:01 -0400 | [diff] [blame] | 368 | } |
Mike Reed | c16abee | 2018-11-24 13:27:27 -0500 | [diff] [blame] | 369 | rec->fPos += 1; |
Mike Reed | dee25cc | 2018-10-05 11:48:01 -0400 | [diff] [blame] | 370 | } |
| 371 | |
Mike Reed | c16abee | 2018-11-24 13:27:27 -0500 | [diff] [blame] | 372 | void SkPaint::getTextPath(const void* text, size_t length, |
| 373 | SkScalar x, SkScalar y, SkPath* path) const { |
| 374 | SkFont font = SkFont::LEGACY_ExtractFromPaint(*this); |
Mike Reed | 0f9d33e | 2018-12-05 10:54:05 -0500 | [diff] [blame] | 375 | SkAutoToGlyphs gly(font, text, length, this->getTextEncoding()); |
Mike Reed | c16abee | 2018-11-24 13:27:27 -0500 | [diff] [blame] | 376 | SkAutoSTArray<32, SkPoint> fPos(gly.count()); |
| 377 | font.getPos(gly.glyphs(), gly.count(), fPos.get(), {x, y}); |
Mike Reed | dee25cc | 2018-10-05 11:48:01 -0400 | [diff] [blame] | 378 | |
Mike Reed | dee25cc | 2018-10-05 11:48:01 -0400 | [diff] [blame] | 379 | path->reset(); |
Mike Reed | c16abee | 2018-11-24 13:27:27 -0500 | [diff] [blame] | 380 | PathPosRec rec = { path, fPos.get() }; |
| 381 | font.getPaths(gly.glyphs(), gly.count(), PathPosProc, &rec); |
| 382 | } |
Mike Reed | dee25cc | 2018-10-05 11:48:01 -0400 | [diff] [blame] | 383 | |
Mike Reed | c16abee | 2018-11-24 13:27:27 -0500 | [diff] [blame] | 384 | void SkPaint::getPosTextPath(const void* text, size_t length, |
| 385 | const SkPoint pos[], SkPath* path) const { |
| 386 | SkFont font = SkFont::LEGACY_ExtractFromPaint(*this); |
Mike Reed | 0f9d33e | 2018-12-05 10:54:05 -0500 | [diff] [blame] | 387 | SkAutoToGlyphs gly(font, text, length, this->getTextEncoding()); |
Mike Reed | c16abee | 2018-11-24 13:27:27 -0500 | [diff] [blame] | 388 | |
| 389 | path->reset(); |
| 390 | PathPosRec rec = { path, pos }; |
| 391 | font.getPaths(gly.glyphs(), gly.count(), PathPosProc, &rec); |
Mike Reed | dee25cc | 2018-10-05 11:48:01 -0400 | [diff] [blame] | 392 | } |
| 393 | |
| 394 | template <SkTextInterceptsIter::TextType TextType, typename Func> |
| 395 | int GetTextIntercepts(const SkPaint& paint, const void* text, size_t length, |
| 396 | const SkScalar bounds[2], SkScalar* array, Func posMaker) { |
| 397 | SkASSERT(length == 0 || text != nullptr); |
| 398 | if (!length) { |
| 399 | return 0; |
| 400 | } |
| 401 | |
| 402 | const SkPoint pos0 = posMaker(0); |
| 403 | SkTextInterceptsIter iter(static_cast<const char*>(text), length, paint, bounds, |
| 404 | pos0.x(), pos0.y(), TextType); |
| 405 | |
| 406 | int i = 0; |
| 407 | int count = 0; |
| 408 | while (iter.next(array, &count)) { |
| 409 | if (TextType == SkTextInterceptsIter::TextType::kPosText) { |
| 410 | const SkPoint pos = posMaker(++i); |
| 411 | iter.setPosition(pos.x(), pos.y()); |
| 412 | } |
| 413 | } |
| 414 | |
| 415 | return count; |
| 416 | } |
| 417 | |
| 418 | int SkPaint::getTextIntercepts(const void* textData, size_t length, |
| 419 | SkScalar x, SkScalar y, const SkScalar bounds[2], |
| 420 | SkScalar* array) const { |
| 421 | |
| 422 | return GetTextIntercepts<SkTextInterceptsIter::TextType::kText>( |
| 423 | *this, textData, length, bounds, array, [&x, &y] (int) -> SkPoint { |
| 424 | return SkPoint::Make(x, y); |
| 425 | }); |
| 426 | } |
| 427 | |
| 428 | int SkPaint::getPosTextIntercepts(const void* textData, size_t length, const SkPoint pos[], |
| 429 | const SkScalar bounds[2], SkScalar* array) const { |
| 430 | |
| 431 | return GetTextIntercepts<SkTextInterceptsIter::TextType::kPosText>( |
| 432 | *this, textData, length, bounds, array, [&pos] (int i) -> SkPoint { |
| 433 | return pos[i]; |
| 434 | }); |
| 435 | } |
| 436 | |
| 437 | int SkPaint::getPosTextHIntercepts(const void* textData, size_t length, const SkScalar xpos[], |
| 438 | SkScalar constY, const SkScalar bounds[2], |
| 439 | SkScalar* array) const { |
| 440 | |
| 441 | return GetTextIntercepts<SkTextInterceptsIter::TextType::kPosText>( |
| 442 | *this, textData, length, bounds, array, [&xpos, &constY] (int i) -> SkPoint { |
| 443 | return SkPoint::Make(xpos[i], constY); |
| 444 | }); |
| 445 | } |
| 446 | |
| 447 | int SkPaint::getTextBlobIntercepts(const SkTextBlob* blob, const SkScalar bounds[2], |
| 448 | SkScalar* intervals) const { |
| 449 | int count = 0; |
| 450 | SkPaint runPaint(*this); |
| 451 | |
| 452 | SkTextBlobRunIterator it(blob); |
| 453 | while (!it.done()) { |
| 454 | it.applyFontToPaint(&runPaint); |
| 455 | const size_t runByteCount = it.glyphCount() * sizeof(SkGlyphID); |
| 456 | SkScalar* runIntervals = intervals ? intervals + count : nullptr; |
| 457 | |
| 458 | switch (it.positioning()) { |
| 459 | case SkTextBlobRunIterator::kDefault_Positioning: |
| 460 | count += runPaint.getTextIntercepts(it.glyphs(), runByteCount, it.offset().x(), |
| 461 | it.offset().y(), bounds, runIntervals); |
| 462 | break; |
| 463 | case SkTextBlobRunIterator::kHorizontal_Positioning: |
| 464 | count += runPaint.getPosTextHIntercepts(it.glyphs(), runByteCount, it.pos(), |
| 465 | it.offset().y(), bounds, runIntervals); |
| 466 | break; |
| 467 | case SkTextBlobRunIterator::kFull_Positioning: |
| 468 | count += runPaint.getPosTextIntercepts(it.glyphs(), runByteCount, |
| 469 | reinterpret_cast<const SkPoint*>(it.pos()), |
| 470 | bounds, runIntervals); |
| 471 | break; |
| 472 | } |
| 473 | |
| 474 | it.next(); |
| 475 | } |
| 476 | |
| 477 | return count; |
| 478 | } |
| 479 | |
Mike Reed | dee25cc | 2018-10-05 11:48:01 -0400 | [diff] [blame] | 480 | // return true if the paint is just a single color (i.e. not a shader). If its |
| 481 | // a shader, then we can't compute a const luminance for it :( |
| 482 | static bool just_a_color(const SkPaint& paint, SkColor* color) { |
| 483 | SkColor c = paint.getColor(); |
| 484 | |
| 485 | const auto* shader = as_SB(paint.getShader()); |
| 486 | if (shader && !shader->asLuminanceColor(&c)) { |
| 487 | return false; |
| 488 | } |
| 489 | if (paint.getColorFilter()) { |
| 490 | c = paint.getColorFilter()->filterColor(c); |
| 491 | } |
| 492 | if (color) { |
| 493 | *color = c; |
| 494 | } |
| 495 | return true; |
| 496 | } |
| 497 | |
| 498 | SkColor SkPaint::computeLuminanceColor() const { |
| 499 | SkColor c; |
| 500 | if (!just_a_color(*this, &c)) { |
| 501 | c = SkColorSetRGB(0x7F, 0x80, 0x7F); |
| 502 | } |
| 503 | return c; |
| 504 | } |
| 505 | |
| 506 | /////////////////////////////////////////////////////////////////////////////// |
| 507 | |
| 508 | static bool has_thick_frame(const SkPaint& paint) { |
| 509 | return paint.getStrokeWidth() > 0 && |
| 510 | paint.getStyle() != SkPaint::kFill_Style; |
| 511 | } |
| 512 | |
| 513 | SkTextBaseIter::SkTextBaseIter(const char text[], size_t length, |
| 514 | const SkPaint& paint, |
| 515 | bool applyStrokeAndPathEffects) |
| 516 | : fPaint(paint) { |
Mike Reed | 0f9d33e | 2018-12-05 10:54:05 -0500 | [diff] [blame] | 517 | fGlyphCacheProc = SkFontPriv::GetGlyphCacheProc(paint.getTextEncoding(), true); |
Mike Reed | dee25cc | 2018-10-05 11:48:01 -0400 | [diff] [blame] | 518 | |
| 519 | fPaint.setLinearText(true); |
| 520 | fPaint.setMaskFilter(nullptr); // don't want this affecting our path-cache lookup |
| 521 | |
| 522 | if (fPaint.getPathEffect() == nullptr && !has_thick_frame(fPaint)) { |
| 523 | applyStrokeAndPathEffects = false; |
| 524 | } |
| 525 | |
| 526 | // can't use our canonical size if we need to apply patheffects |
| 527 | if (fPaint.getPathEffect() == nullptr) { |
| 528 | fPaint.setTextSize(SkIntToScalar(SkPaint::kCanonicalTextSizeForPaths)); |
| 529 | fScale = paint.getTextSize() / SkPaint::kCanonicalTextSizeForPaths; |
| 530 | // Note: fScale can be zero here (even if it wasn't before the divide). It can also |
| 531 | // be very very small. We call sk_ieee_float_divide below to ensure IEEE divide behavior, |
| 532 | // since downstream we will check for the resulting coordinates being non-finite anyway. |
| 533 | // Thus we don't need to check for zero here. |
| 534 | if (has_thick_frame(fPaint)) { |
| 535 | fPaint.setStrokeWidth(sk_ieee_float_divide(fPaint.getStrokeWidth(), fScale)); |
| 536 | } |
| 537 | } else { |
| 538 | fScale = SK_Scalar1; |
| 539 | } |
| 540 | |
| 541 | if (!applyStrokeAndPathEffects) { |
| 542 | fPaint.setStyle(SkPaint::kFill_Style); |
| 543 | fPaint.setPathEffect(nullptr); |
| 544 | } |
| 545 | |
| 546 | // SRGBTODO: Is this correct? |
Mike Reed | 32c6066 | 2018-11-28 10:28:07 -0500 | [diff] [blame] | 547 | const SkFont font = SkFont::LEGACY_ExtractFromPaint(fPaint); |
| 548 | fCache = SkStrikeCache::FindOrCreateStrikeWithNoDeviceExclusive(font, fPaint); |
Mike Reed | dee25cc | 2018-10-05 11:48:01 -0400 | [diff] [blame] | 549 | |
| 550 | SkPaint::Style style = SkPaint::kFill_Style; |
| 551 | sk_sp<SkPathEffect> pe; |
| 552 | |
| 553 | if (!applyStrokeAndPathEffects) { |
| 554 | style = paint.getStyle(); // restore |
| 555 | pe = paint.refPathEffect(); // restore |
| 556 | } |
| 557 | fPaint.setStyle(style); |
| 558 | fPaint.setPathEffect(pe); |
| 559 | fPaint.setMaskFilter(paint.refMaskFilter()); // restore |
| 560 | |
| 561 | // now compute fXOffset if needed |
| 562 | |
| 563 | SkScalar xOffset = 0; |
Mike Reed | dee25cc | 2018-10-05 11:48:01 -0400 | [diff] [blame] | 564 | fXPos = xOffset; |
| 565 | fPrevAdvance = 0; |
| 566 | |
| 567 | fText = text; |
| 568 | fStop = text + length; |
Mike Reed | dee25cc | 2018-10-05 11:48:01 -0400 | [diff] [blame] | 569 | } |
| 570 | |
| 571 | bool SkTextToPathIter::next(const SkPath** path, SkScalar* xpos) { |
| 572 | if (fText < fStop) { |
| 573 | const SkGlyph& glyph = fGlyphCacheProc(fCache.get(), &fText, fStop); |
| 574 | |
| 575 | fXPos += fPrevAdvance * fScale; |
Mike Reed | c88cc77 | 2018-10-23 12:05:47 -0400 | [diff] [blame] | 576 | fPrevAdvance = advance(glyph); // + fPaint.getTextTracking(); |
Mike Reed | dee25cc | 2018-10-05 11:48:01 -0400 | [diff] [blame] | 577 | |
| 578 | if (glyph.fWidth) { |
| 579 | if (path) { |
| 580 | *path = fCache->findPath(glyph); |
| 581 | } |
| 582 | } else { |
| 583 | if (path) { |
| 584 | *path = nullptr; |
| 585 | } |
| 586 | } |
| 587 | if (xpos) { |
| 588 | *xpos = fXPos; |
| 589 | } |
| 590 | return true; |
| 591 | } |
| 592 | return false; |
| 593 | } |
| 594 | |
| 595 | bool SkTextInterceptsIter::next(SkScalar* array, int* count) { |
| 596 | const SkGlyph& glyph = fGlyphCacheProc(fCache.get(), &fText, fStop); |
| 597 | fXPos += fPrevAdvance * fScale; |
Mike Reed | c88cc77 | 2018-10-23 12:05:47 -0400 | [diff] [blame] | 598 | fPrevAdvance = advance(glyph); // + fPaint.getTextTracking(); |
Mike Reed | dee25cc | 2018-10-05 11:48:01 -0400 | [diff] [blame] | 599 | if (fCache->findPath(glyph)) { |
Mike Reed | c88cc77 | 2018-10-23 12:05:47 -0400 | [diff] [blame] | 600 | fCache->findIntercepts(fBounds, fScale, fXPos, false, |
Mike Reed | dee25cc | 2018-10-05 11:48:01 -0400 | [diff] [blame] | 601 | const_cast<SkGlyph*>(&glyph), array, count); |
| 602 | } |
| 603 | return fText < fStop; |
| 604 | } |