| OLD | NEW |
| (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 #include "GrAtlasTextContext.h" | |
| 8 | |
| 9 #include "GrDrawContext.h" | |
| 10 #include "GrDrawTarget.h" | |
| 11 #include "GrFontScaler.h" | |
| 12 #include "GrStrokeInfo.h" | |
| 13 #include "GrTextBlobCache.h" | |
| 14 #include "GrTexturePriv.h" | |
| 15 #include "GrTextUtils.h" | |
| 16 #include "GrVertexBuffer.h" | |
| 17 | |
| 18 #include "SkAutoKern.h" | |
| 19 #include "SkColorPriv.h" | |
| 20 #include "SkColorFilter.h" | |
| 21 #include "SkDistanceFieldGen.h" | |
| 22 #include "SkDraw.h" | |
| 23 #include "SkDrawFilter.h" | |
| 24 #include "SkDrawProcs.h" | |
| 25 #include "SkFindAndPlaceGlyph.h" | |
| 26 #include "SkGlyphCache.h" | |
| 27 #include "SkGpuDevice.h" | |
| 28 #include "SkGrPriv.h" | |
| 29 #include "SkPath.h" | |
| 30 #include "SkRTConf.h" | |
| 31 #include "SkStrokeRec.h" | |
| 32 #include "SkTextBlob.h" | |
| 33 #include "SkTextMapStateProc.h" | |
| 34 | |
| 35 #include "batches/GrAtlasTextBatch.h" | |
| 36 | |
| 37 namespace { | |
| 38 static const int kMinDFFontSize = 18; | |
| 39 static const int kSmallDFFontSize = 32; | |
| 40 static const int kSmallDFFontLimit = 32; | |
| 41 static const int kMediumDFFontSize = 72; | |
| 42 static const int kMediumDFFontLimit = 72; | |
| 43 static const int kLargeDFFontSize = 162; | |
| 44 #ifdef SK_BUILD_FOR_ANDROID | |
| 45 static const int kLargeDFFontLimit = 384; | |
| 46 #else | |
| 47 static const int kLargeDFFontLimit = 2 * kLargeDFFontSize; | |
| 48 #endif | |
| 49 }; | |
| 50 | |
| 51 GrAtlasTextContext::GrAtlasTextContext(GrContext* context, const SkSurfaceProps&
surfaceProps) | |
| 52 : INHERITED(context, surfaceProps) | |
| 53 , fDistanceAdjustTable(new GrDistanceFieldAdjustTable) { | |
| 54 // We overallocate vertices in our textblobs based on the assumption that A8
has the greatest | |
| 55 // vertexStride | |
| 56 static_assert(GrAtlasTextBlob::kGrayTextVASize >= GrAtlasTextBlob::kColorTex
tVASize && | |
| 57 GrAtlasTextBlob::kGrayTextVASize >= GrAtlasTextBlob::kLCDTextV
ASize, | |
| 58 "vertex_attribute_changed"); | |
| 59 fCurrStrike = nullptr; | |
| 60 fCache = context->getTextBlobCache(); | |
| 61 } | |
| 62 | |
| 63 | |
| 64 GrAtlasTextContext* GrAtlasTextContext::Create(GrContext* context, | |
| 65 const SkSurfaceProps& surfaceProp
s) { | |
| 66 return new GrAtlasTextContext(context, surfaceProps); | |
| 67 } | |
| 68 | |
| 69 bool GrAtlasTextContext::canDraw(const SkPaint& skPaint, const SkMatrix& viewMat
rix) { | |
| 70 return this->canDrawAsDistanceFields(skPaint, viewMatrix) || | |
| 71 !SkDraw::ShouldDrawTextAsPaths(skPaint, viewMatrix); | |
| 72 } | |
| 73 | |
| 74 GrColor GrAtlasTextContext::ComputeCanonicalColor(const SkPaint& paint, bool lcd
) { | |
| 75 GrColor canonicalColor = paint.computeLuminanceColor(); | |
| 76 if (lcd) { | |
| 77 // This is the correct computation, but there are tons of cases where LC
D can be overridden. | |
| 78 // For now we just regenerate if any run in a textblob has LCD. | |
| 79 // TODO figure out where all of these overrides are and see if we can in
corporate that logic | |
| 80 // at a higher level *OR* use sRGB | |
| 81 SkASSERT(false); | |
| 82 //canonicalColor = SkMaskGamma::CanonicalColor(canonicalColor); | |
| 83 } else { | |
| 84 // A8, though can have mixed BMP text but it shouldn't matter because BM
P text won't have | |
| 85 // gamma corrected masks anyways, nor color | |
| 86 U8CPU lum = SkComputeLuminance(SkColorGetR(canonicalColor), | |
| 87 SkColorGetG(canonicalColor), | |
| 88 SkColorGetB(canonicalColor)); | |
| 89 // reduce to our finite number of bits | |
| 90 canonicalColor = SkMaskGamma::CanonicalColor(SkColorSetRGB(lum, lum, lum
)); | |
| 91 } | |
| 92 return canonicalColor; | |
| 93 } | |
| 94 | |
| 95 // TODO if this function ever shows up in profiling, then we can compute this va
lue when the | |
| 96 // textblob is being built and cache it. However, for the time being textblobs
mostly only have 1 | |
| 97 // run so this is not a big deal to compute here. | |
| 98 bool GrAtlasTextContext::HasLCD(const SkTextBlob* blob) { | |
| 99 SkTextBlobRunIterator it(blob); | |
| 100 for (; !it.done(); it.next()) { | |
| 101 if (it.isLCD()) { | |
| 102 return true; | |
| 103 } | |
| 104 } | |
| 105 return false; | |
| 106 } | |
| 107 | |
| 108 inline SkGlyphCache* GrAtlasTextContext::setupCache(GrAtlasTextBlob::Run* run, | |
| 109 const SkPaint& skPaint, | |
| 110 const SkMatrix* viewMatrix, | |
| 111 bool noGamma) { | |
| 112 skPaint.getScalerContextDescriptor(&run->fDescriptor, fSurfaceProps, viewMat
rix, noGamma); | |
| 113 run->fTypeface.reset(SkSafeRef(skPaint.getTypeface())); | |
| 114 return SkGlyphCache::DetachCache(run->fTypeface, run->fDescriptor.getDesc())
; | |
| 115 } | |
| 116 | |
| 117 void GrAtlasTextContext::drawTextBlob(GrDrawContext* dc, | |
| 118 const GrClip& clip, const SkPaint& skPaint
, | |
| 119 const SkMatrix& viewMatrix, const SkTextBl
ob* blob, | |
| 120 SkScalar x, SkScalar y, | |
| 121 SkDrawFilter* drawFilter, const SkIRect& c
lipBounds) { | |
| 122 // If we have been abandoned, then don't draw | |
| 123 if (fContext->abandoned()) { | |
| 124 return; | |
| 125 } | |
| 126 | |
| 127 SkAutoTUnref<GrAtlasTextBlob> cacheBlob; | |
| 128 SkMaskFilter::BlurRec blurRec; | |
| 129 GrAtlasTextBlob::Key key; | |
| 130 // It might be worth caching these things, but its not clear at this time | |
| 131 // TODO for animated mask filters, this will fill up our cache. We need a s
afeguard here | |
| 132 const SkMaskFilter* mf = skPaint.getMaskFilter(); | |
| 133 bool canCache = !(skPaint.getPathEffect() || | |
| 134 (mf && !mf->asABlur(&blurRec)) || | |
| 135 drawFilter); | |
| 136 | |
| 137 if (canCache) { | |
| 138 bool hasLCD = HasLCD(blob); | |
| 139 | |
| 140 // We canonicalize all non-lcd draws to use kUnknown_SkPixelGeometry | |
| 141 SkPixelGeometry pixelGeometry = hasLCD ? fSurfaceProps.pixelGeometry() : | |
| 142 kUnknown_SkPixelGeometry; | |
| 143 | |
| 144 // TODO we want to figure out a way to be able to use the canonical colo
r on LCD text, | |
| 145 // see the note on ComputeCanonicalColor above. We pick a dummy value f
or LCD text to | |
| 146 // ensure we always match the same key | |
| 147 GrColor canonicalColor = hasLCD ? SK_ColorTRANSPARENT : | |
| 148 ComputeCanonicalColor(skPaint, hasLCD)
; | |
| 149 | |
| 150 key.fPixelGeometry = pixelGeometry; | |
| 151 key.fUniqueID = blob->uniqueID(); | |
| 152 key.fStyle = skPaint.getStyle(); | |
| 153 key.fHasBlur = SkToBool(mf); | |
| 154 key.fCanonicalColor = canonicalColor; | |
| 155 cacheBlob.reset(SkSafeRef(fCache->find(key))); | |
| 156 } | |
| 157 | |
| 158 SkScalar transX = 0.f; | |
| 159 SkScalar transY = 0.f; | |
| 160 | |
| 161 // Though for the time being runs in the textblob can override the paint, th
ey only touch font | |
| 162 // info. | |
| 163 GrPaint grPaint; | |
| 164 if (!SkPaintToGrPaint(fContext, skPaint, viewMatrix, &grPaint)) { | |
| 165 return; | |
| 166 } | |
| 167 | |
| 168 if (cacheBlob) { | |
| 169 if (cacheBlob->mustRegenerate(&transX, &transY, skPaint, grPaint.getColo
r(), blurRec, | |
| 170 viewMatrix, x, y)) { | |
| 171 // We have to remake the blob because changes may invalidate our mas
ks. | |
| 172 // TODO we could probably get away reuse most of the time if the poi
nter is unique, | |
| 173 // but we'd have to clear the subrun information | |
| 174 fCache->remove(cacheBlob); | |
| 175 cacheBlob.reset(SkRef(fCache->createCachedBlob(blob, key, blurRec, s
kPaint, | |
| 176 GrAtlasTextBlob::kGra
yTextVASize))); | |
| 177 this->regenerateTextBlob(cacheBlob, skPaint, grPaint.getColor(), vie
wMatrix, | |
| 178 blob, x, y, drawFilter, clip); | |
| 179 } else { | |
| 180 fCache->makeMRU(cacheBlob); | |
| 181 #ifdef CACHE_SANITY_CHECK | |
| 182 { | |
| 183 int glyphCount = 0; | |
| 184 int runCount = 0; | |
| 185 GrTextBlobCache::BlobGlyphCount(&glyphCount, &runCount, blob); | |
| 186 SkAutoTUnref<GrAtlasTextBlob> sanityBlob(fCache->createBlob(glyp
hCount, runCount, | |
| 187 kGra
yTextVASize)); | |
| 188 GrTextBlobCache::SetupCacheBlobKey(sanityBlob, key, blurRec, skP
aint); | |
| 189 this->regenerateTextBlob(sanityBlob, skPaint, grPaint.getColor()
, viewMatrix, | |
| 190 blob, x, y, drawFilter, clip); | |
| 191 GrAtlasTextBlob::AssertEqual(*sanityBlob, *cacheBlob); | |
| 192 } | |
| 193 | |
| 194 #endif | |
| 195 } | |
| 196 } else { | |
| 197 if (canCache) { | |
| 198 cacheBlob.reset(SkRef(fCache->createCachedBlob(blob, key, blurRec, s
kPaint, | |
| 199 GrAtlasTextBlob::kGra
yTextVASize))); | |
| 200 } else { | |
| 201 cacheBlob.reset(fCache->createBlob(blob, GrAtlasTextBlob::kGrayTextV
ASize)); | |
| 202 } | |
| 203 this->regenerateTextBlob(cacheBlob, skPaint, grPaint.getColor(), viewMat
rix, | |
| 204 blob, x, y, drawFilter, clip); | |
| 205 } | |
| 206 | |
| 207 cacheBlob->flushCached(fContext, dc, blob, fSurfaceProps, fDistanceAdjustTab
le, skPaint, | |
| 208 grPaint, drawFilter, clip, viewMatrix, clipBounds, x,
y, transX, transY); | |
| 209 } | |
| 210 | |
| 211 inline bool GrAtlasTextContext::canDrawAsDistanceFields(const SkPaint& skPaint, | |
| 212 const SkMatrix& viewMatr
ix) { | |
| 213 // TODO: support perspective (need getMaxScale replacement) | |
| 214 if (viewMatrix.hasPerspective()) { | |
| 215 return false; | |
| 216 } | |
| 217 | |
| 218 SkScalar maxScale = viewMatrix.getMaxScale(); | |
| 219 SkScalar scaledTextSize = maxScale*skPaint.getTextSize(); | |
| 220 // Hinted text looks far better at small resolutions | |
| 221 // Scaling up beyond 2x yields undesireable artifacts | |
| 222 if (scaledTextSize < kMinDFFontSize || scaledTextSize > kLargeDFFontLimit) { | |
| 223 return false; | |
| 224 } | |
| 225 | |
| 226 bool useDFT = fSurfaceProps.isUseDeviceIndependentFonts(); | |
| 227 #if SK_FORCE_DISTANCE_FIELD_TEXT | |
| 228 useDFT = true; | |
| 229 #endif | |
| 230 | |
| 231 if (!useDFT && scaledTextSize < kLargeDFFontSize) { | |
| 232 return false; | |
| 233 } | |
| 234 | |
| 235 // rasterizers and mask filters modify alpha, which doesn't | |
| 236 // translate well to distance | |
| 237 if (skPaint.getRasterizer() || skPaint.getMaskFilter() || | |
| 238 !fContext->caps()->shaderCaps()->shaderDerivativeSupport()) { | |
| 239 return false; | |
| 240 } | |
| 241 | |
| 242 // TODO: add some stroking support | |
| 243 if (skPaint.getStyle() != SkPaint::kFill_Style) { | |
| 244 return false; | |
| 245 } | |
| 246 | |
| 247 return true; | |
| 248 } | |
| 249 | |
| 250 void GrAtlasTextContext::regenerateTextBlob(GrAtlasTextBlob* cacheBlob, | |
| 251 const SkPaint& skPaint, GrColor colo
r, | |
| 252 const SkMatrix& viewMatrix, | |
| 253 const SkTextBlob* blob, SkScalar x,
SkScalar y, | |
| 254 SkDrawFilter* drawFilter, | |
| 255 const GrClip& clip) { | |
| 256 // The color here is the GrPaint color, and it is used to determine whether
we | |
| 257 // have to regenerate LCD text blobs. | |
| 258 // We use this color vs the SkPaint color because it has the colorfilter app
lied. | |
| 259 cacheBlob->fPaintColor = color; | |
| 260 cacheBlob->fViewMatrix = viewMatrix; | |
| 261 cacheBlob->fX = x; | |
| 262 cacheBlob->fY = y; | |
| 263 | |
| 264 // Regenerate textblob | |
| 265 SkPaint runPaint = skPaint; | |
| 266 SkTextBlobRunIterator it(blob); | |
| 267 for (int run = 0; !it.done(); it.next(), run++) { | |
| 268 int glyphCount = it.glyphCount(); | |
| 269 size_t textLen = glyphCount * sizeof(uint16_t); | |
| 270 const SkPoint& offset = it.offset(); | |
| 271 // applyFontToPaint() always overwrites the exact same attributes, | |
| 272 // so it is safe to not re-seed the paint for this reason. | |
| 273 it.applyFontToPaint(&runPaint); | |
| 274 | |
| 275 if (drawFilter && !drawFilter->filter(&runPaint, SkDrawFilter::kText_Typ
e)) { | |
| 276 // A false return from filter() means we should abort the current dr
aw. | |
| 277 runPaint = skPaint; | |
| 278 continue; | |
| 279 } | |
| 280 | |
| 281 runPaint.setFlags(FilterTextFlags(fSurfaceProps, runPaint)); | |
| 282 | |
| 283 cacheBlob->push_back_run(run); | |
| 284 | |
| 285 if (this->canDrawAsDistanceFields(runPaint, viewMatrix)) { | |
| 286 cacheBlob->setHasDistanceField(); | |
| 287 SkPaint dfPaint = runPaint; | |
| 288 SkScalar textRatio; | |
| 289 this->initDistanceFieldPaint(cacheBlob, &dfPaint, &textRatio, viewMa
trix); | |
| 290 Run& runIdx = cacheBlob->fRuns[run]; | |
| 291 PerSubRunInfo& subRun = runIdx.fSubRunInfo.back(); | |
| 292 subRun.setUseLCDText(runPaint.isLCDRenderText()); | |
| 293 subRun.setDrawAsDistanceFields(); | |
| 294 | |
| 295 SkTDArray<char> fallbackTxt; | |
| 296 SkTDArray<SkScalar> fallbackPos; | |
| 297 SkPoint dfOffset; | |
| 298 int scalarsPerPosition = 2; | |
| 299 switch (it.positioning()) { | |
| 300 case SkTextBlob::kDefault_Positioning: { | |
| 301 this->internalDrawDFText(cacheBlob, run, dfPaint, color, vie
wMatrix, | |
| 302 (const char *)it.glyphs(), textLen, | |
| 303 x + offset.x(), y + offset.y(), tex
tRatio, | |
| 304 &fallbackTxt, &fallbackPos, &dfOffs
et, runPaint); | |
| 305 break; | |
| 306 } | |
| 307 case SkTextBlob::kHorizontal_Positioning: { | |
| 308 scalarsPerPosition = 1; | |
| 309 dfOffset = SkPoint::Make(x, y + offset.y()); | |
| 310 this->internalDrawDFPosText(cacheBlob, run, dfPaint, color,
viewMatrix, | |
| 311 (const char*)it.glyphs(), textLe
n, it.pos(), | |
| 312 scalarsPerPosition, dfOffset, te
xtRatio, | |
| 313 &fallbackTxt, &fallbackPos); | |
| 314 break; | |
| 315 } | |
| 316 case SkTextBlob::kFull_Positioning: { | |
| 317 dfOffset = SkPoint::Make(x, y); | |
| 318 this->internalDrawDFPosText(cacheBlob, run, dfPaint, color,
viewMatrix, | |
| 319 (const char*)it.glyphs(), textLe
n, it.pos(), | |
| 320 scalarsPerPosition, dfOffset, te
xtRatio, | |
| 321 &fallbackTxt, &fallbackPos); | |
| 322 break; | |
| 323 } | |
| 324 } | |
| 325 if (fallbackTxt.count()) { | |
| 326 this->fallbackDrawPosText(cacheBlob, run, clip, color, runPaint,
viewMatrix, | |
| 327 fallbackTxt, fallbackPos, scalarsPerPo
sition, dfOffset); | |
| 328 } | |
| 329 } else if (SkDraw::ShouldDrawTextAsPaths(runPaint, viewMatrix)) { | |
| 330 cacheBlob->fRuns[run].fDrawAsPaths = true; | |
| 331 } else { | |
| 332 cacheBlob->setHasBitmap(); | |
| 333 SkGlyphCache* cache = this->setupCache(&cacheBlob->fRuns[run], runPa
int, &viewMatrix, | |
| 334 false); | |
| 335 switch (it.positioning()) { | |
| 336 case SkTextBlob::kDefault_Positioning: | |
| 337 GrTextUtils::DrawBmpText(cacheBlob, run, fContext->getBatchF
ontCache(), | |
| 338 cache, runPaint, color, viewMatrix, | |
| 339 (const char *)it.glyphs(), textLen, | |
| 340 x + offset.x(), y + offset.y()); | |
| 341 break; | |
| 342 case SkTextBlob::kHorizontal_Positioning: | |
| 343 GrTextUtils::DrawBmpPosText(cacheBlob, run, fContext->getBat
chFontCache(), | |
| 344 cache, runPaint, color, viewMatr
ix, | |
| 345 (const char*)it.glyphs(), textLe
n, it.pos(), 1, | |
| 346 SkPoint::Make(x, y + offset.y())
); | |
| 347 break; | |
| 348 case SkTextBlob::kFull_Positioning: | |
| 349 GrTextUtils::DrawBmpPosText(cacheBlob, run, fContext->getBat
chFontCache(), | |
| 350 cache, runPaint, color, viewMatr
ix, | |
| 351 (const char*)it.glyphs(), textLe
n, it.pos(), 2, | |
| 352 SkPoint::Make(x, y)); | |
| 353 break; | |
| 354 } | |
| 355 SkGlyphCache::AttachCache(cache); | |
| 356 } | |
| 357 | |
| 358 if (drawFilter) { | |
| 359 // A draw filter may change the paint arbitrarily, so we must re-see
d in this case. | |
| 360 runPaint = skPaint; | |
| 361 } | |
| 362 } | |
| 363 } | |
| 364 | |
| 365 inline void GrAtlasTextContext::initDistanceFieldPaint(GrAtlasTextBlob* blob, | |
| 366 SkPaint* skPaint, | |
| 367 SkScalar* textRatio, | |
| 368 const SkMatrix& viewMatri
x) { | |
| 369 // getMaxScale doesn't support perspective, so neither do we at the moment | |
| 370 SkASSERT(!viewMatrix.hasPerspective()); | |
| 371 SkScalar maxScale = viewMatrix.getMaxScale(); | |
| 372 SkScalar textSize = skPaint->getTextSize(); | |
| 373 SkScalar scaledTextSize = textSize; | |
| 374 // if we have non-unity scale, we need to choose our base text size | |
| 375 // based on the SkPaint's text size multiplied by the max scale factor | |
| 376 // TODO: do we need to do this if we're scaling down (i.e. maxScale < 1)? | |
| 377 if (maxScale > 0 && !SkScalarNearlyEqual(maxScale, SK_Scalar1)) { | |
| 378 scaledTextSize *= maxScale; | |
| 379 } | |
| 380 | |
| 381 // We have three sizes of distance field text, and within each size 'bucket'
there is a floor | |
| 382 // and ceiling. A scale outside of this range would require regenerating th
e distance fields | |
| 383 SkScalar dfMaskScaleFloor; | |
| 384 SkScalar dfMaskScaleCeil; | |
| 385 if (scaledTextSize <= kSmallDFFontLimit) { | |
| 386 dfMaskScaleFloor = kMinDFFontSize; | |
| 387 dfMaskScaleCeil = kSmallDFFontLimit; | |
| 388 *textRatio = textSize / kSmallDFFontSize; | |
| 389 skPaint->setTextSize(SkIntToScalar(kSmallDFFontSize)); | |
| 390 } else if (scaledTextSize <= kMediumDFFontLimit) { | |
| 391 dfMaskScaleFloor = kSmallDFFontLimit; | |
| 392 dfMaskScaleCeil = kMediumDFFontLimit; | |
| 393 *textRatio = textSize / kMediumDFFontSize; | |
| 394 skPaint->setTextSize(SkIntToScalar(kMediumDFFontSize)); | |
| 395 } else { | |
| 396 dfMaskScaleFloor = kMediumDFFontLimit; | |
| 397 dfMaskScaleCeil = kLargeDFFontLimit; | |
| 398 *textRatio = textSize / kLargeDFFontSize; | |
| 399 skPaint->setTextSize(SkIntToScalar(kLargeDFFontSize)); | |
| 400 } | |
| 401 | |
| 402 // Because there can be multiple runs in the blob, we want the overall maxMi
nScale, and | |
| 403 // minMaxScale to make regeneration decisions. Specifically, we want the ma
ximum minimum scale | |
| 404 // we can tolerate before we'd drop to a lower mip size, and the minimum max
imum scale we can | |
| 405 // tolerate before we'd have to move to a large mip size. When we actually
test these values | |
| 406 // we look at the delta in scale between the new viewmatrix and the old view
matrix, and test | |
| 407 // against these values to decide if we can reuse or not(ie, will a given sc
ale change our mip | |
| 408 // level) | |
| 409 SkASSERT(dfMaskScaleFloor <= scaledTextSize && scaledTextSize <= dfMaskScale
Ceil); | |
| 410 blob->fMaxMinScale = SkMaxScalar(dfMaskScaleFloor / scaledTextSize, blob->fM
axMinScale); | |
| 411 blob->fMinMaxScale = SkMinScalar(dfMaskScaleCeil / scaledTextSize, blob->fMi
nMaxScale); | |
| 412 | |
| 413 skPaint->setLCDRenderText(false); | |
| 414 skPaint->setAutohinted(false); | |
| 415 skPaint->setHinting(SkPaint::kNormal_Hinting); | |
| 416 skPaint->setSubpixelText(true); | |
| 417 } | |
| 418 | |
| 419 inline void GrAtlasTextContext::fallbackDrawPosText(GrAtlasTextBlob* blob, | |
| 420 int runIndex, | |
| 421 const GrClip& clip, | |
| 422 GrColor color, | |
| 423 const SkPaint& skPaint, | |
| 424 const SkMatrix& viewMatrix, | |
| 425 const SkTDArray<char>& fallb
ackTxt, | |
| 426 const SkTDArray<SkScalar>& f
allbackPos, | |
| 427 int scalarsPerPosition, | |
| 428 const SkPoint& offset) { | |
| 429 SkASSERT(fallbackTxt.count()); | |
| 430 blob->setHasBitmap(); | |
| 431 Run& run = blob->fRuns[runIndex]; | |
| 432 // Push back a new subrun to fill and set the override descriptor | |
| 433 run.push_back(); | |
| 434 run.fOverrideDescriptor.reset(new SkAutoDescriptor); | |
| 435 skPaint.getScalerContextDescriptor(run.fOverrideDescriptor, | |
| 436 fSurfaceProps, &viewMatrix, false); | |
| 437 SkGlyphCache* cache = SkGlyphCache::DetachCache(run.fTypeface, | |
| 438 run.fOverrideDescriptor->get
Desc()); | |
| 439 GrTextUtils::DrawBmpPosText(blob, runIndex, fContext->getBatchFontCache(), c
ache, skPaint, | |
| 440 color, viewMatrix, fallbackTxt.begin(), fallback
Txt.count(), | |
| 441 fallbackPos.begin(), scalarsPerPosition, offset)
; | |
| 442 SkGlyphCache::AttachCache(cache); | |
| 443 } | |
| 444 | |
| 445 inline GrAtlasTextBlob* | |
| 446 GrAtlasTextContext::setupDFBlob(int glyphCount, const SkPaint& origPaint, | |
| 447 const SkMatrix& viewMatrix, SkPaint* dfPaint, | |
| 448 SkScalar* textRatio) { | |
| 449 GrAtlasTextBlob* blob = fCache->createBlob(glyphCount, 1, GrAtlasTextBlob::k
GrayTextVASize); | |
| 450 | |
| 451 *dfPaint = origPaint; | |
| 452 this->initDistanceFieldPaint(blob, dfPaint, textRatio, viewMatrix); | |
| 453 blob->fViewMatrix = viewMatrix; | |
| 454 Run& run = blob->fRuns[0]; | |
| 455 PerSubRunInfo& subRun = run.fSubRunInfo.back(); | |
| 456 subRun.setUseLCDText(origPaint.isLCDRenderText()); | |
| 457 subRun.setDrawAsDistanceFields(); | |
| 458 | |
| 459 return blob; | |
| 460 } | |
| 461 | |
| 462 inline GrAtlasTextBlob* | |
| 463 GrAtlasTextContext::createDrawTextBlob(const GrClip& clip, | |
| 464 const GrPaint& paint, const SkPaint& skPa
int, | |
| 465 const SkMatrix& viewMatrix, | |
| 466 const char text[], size_t byteLength, | |
| 467 SkScalar x, SkScalar y, const SkIRect& re
gionClipBounds) { | |
| 468 int glyphCount = skPaint.countText(text, byteLength); | |
| 469 | |
| 470 GrAtlasTextBlob* blob; | |
| 471 if (this->canDrawAsDistanceFields(skPaint, viewMatrix)) { | |
| 472 SkPaint dfPaint; | |
| 473 SkScalar textRatio; | |
| 474 blob = this->setupDFBlob(glyphCount, skPaint, viewMatrix, &dfPaint, &tex
tRatio); | |
| 475 | |
| 476 SkTDArray<char> fallbackTxt; | |
| 477 SkTDArray<SkScalar> fallbackPos; | |
| 478 SkPoint offset; | |
| 479 this->internalDrawDFText(blob, 0, dfPaint, paint.getColor(), viewMatrix,
text, | |
| 480 byteLength, x, y, textRatio, &fallbackTxt, &fal
lbackPos, | |
| 481 &offset, skPaint); | |
| 482 if (fallbackTxt.count()) { | |
| 483 this->fallbackDrawPosText(blob, 0, clip, paint.getColor(), skPaint,
viewMatrix, | |
| 484 fallbackTxt, fallbackPos, 2, offset); | |
| 485 } | |
| 486 } else { | |
| 487 blob = fCache->createBlob(glyphCount, 1, GrAtlasTextBlob::kGrayTextVASiz
e); | |
| 488 blob->fViewMatrix = viewMatrix; | |
| 489 | |
| 490 SkGlyphCache* cache = this->setupCache(&blob->fRuns[0], skPaint, &viewMa
trix, false); | |
| 491 GrTextUtils::DrawBmpText(blob, 0, fContext->getBatchFontCache(), cache,
skPaint, | |
| 492 paint.getColor(), viewMatrix, text, byteLength,
x, y); | |
| 493 SkGlyphCache::AttachCache(cache); | |
| 494 } | |
| 495 return blob; | |
| 496 } | |
| 497 | |
| 498 inline GrAtlasTextBlob* | |
| 499 GrAtlasTextContext::createDrawPosTextBlob(const GrClip& clip, | |
| 500 const GrPaint& paint, const SkPaint& s
kPaint, | |
| 501 const SkMatrix& viewMatrix, | |
| 502 const char text[], size_t byteLength, | |
| 503 const SkScalar pos[], int scalarsPerPo
sition, | |
| 504 const SkPoint& offset, const SkIRect&
regionClipBounds) { | |
| 505 int glyphCount = skPaint.countText(text, byteLength); | |
| 506 | |
| 507 GrAtlasTextBlob* blob; | |
| 508 if (this->canDrawAsDistanceFields(skPaint, viewMatrix)) { | |
| 509 SkPaint dfPaint; | |
| 510 SkScalar textRatio; | |
| 511 blob = this->setupDFBlob(glyphCount, skPaint, viewMatrix, &dfPaint, &tex
tRatio); | |
| 512 | |
| 513 SkTDArray<char> fallbackTxt; | |
| 514 SkTDArray<SkScalar> fallbackPos; | |
| 515 this->internalDrawDFPosText(blob, 0, dfPaint, paint.getColor(), viewMatr
ix, text, | |
| 516 byteLength, pos, scalarsPerPosition, offset, | |
| 517 textRatio, &fallbackTxt, &fallbackPos); | |
| 518 if (fallbackTxt.count()) { | |
| 519 this->fallbackDrawPosText(blob, 0, clip, paint.getColor(), skPaint,
viewMatrix, | |
| 520 fallbackTxt, fallbackPos, scalarsPerPositi
on, offset); | |
| 521 } | |
| 522 } else { | |
| 523 blob = fCache->createBlob(glyphCount, 1, GrAtlasTextBlob::kGrayTextVASiz
e); | |
| 524 blob->fViewMatrix = viewMatrix; | |
| 525 SkGlyphCache* cache = this->setupCache(&blob->fRuns[0], skPaint, &viewMa
trix, false); | |
| 526 GrTextUtils::DrawBmpPosText(blob, 0, fContext->getBatchFontCache(), cach
e, skPaint, | |
| 527 paint.getColor(), viewMatrix, text, | |
| 528 byteLength, pos, scalarsPerPosition, offset)
; | |
| 529 SkGlyphCache::AttachCache(cache); | |
| 530 } | |
| 531 return blob; | |
| 532 } | |
| 533 | |
| 534 void GrAtlasTextContext::onDrawText(GrDrawContext* dc, | |
| 535 const GrClip& clip, | |
| 536 const GrPaint& paint, const SkPaint& skPaint
, | |
| 537 const SkMatrix& viewMatrix, | |
| 538 const char text[], size_t byteLength, | |
| 539 SkScalar x, SkScalar y, const SkIRect& regio
nClipBounds) { | |
| 540 SkAutoTUnref<GrAtlasTextBlob> blob( | |
| 541 this->createDrawTextBlob(clip, paint, skPaint, viewMatrix, | |
| 542 text, byteLength, x, y, regionClipBounds)); | |
| 543 blob->flushThrowaway(fContext, dc, fSurfaceProps, fDistanceAdjustTable, skPa
int, paint, | |
| 544 clip, regionClipBounds); | |
| 545 } | |
| 546 | |
| 547 void GrAtlasTextContext::onDrawPosText(GrDrawContext* dc, | |
| 548 const GrClip& clip, | |
| 549 const GrPaint& paint, const SkPaint& skPa
int, | |
| 550 const SkMatrix& viewMatrix, | |
| 551 const char text[], size_t byteLength, | |
| 552 const SkScalar pos[], int scalarsPerPosit
ion, | |
| 553 const SkPoint& offset, const SkIRect& reg
ionClipBounds) { | |
| 554 SkAutoTUnref<GrAtlasTextBlob> blob( | |
| 555 this->createDrawPosTextBlob(clip, paint, skPaint, viewMatrix, | |
| 556 text, byteLength, | |
| 557 pos, scalarsPerPosition, | |
| 558 offset, regionClipBounds)); | |
| 559 | |
| 560 blob->flushThrowaway(fContext, dc, fSurfaceProps, fDistanceAdjustTable, skPa
int, paint, clip, | |
| 561 regionClipBounds); | |
| 562 } | |
| 563 | |
| 564 void GrAtlasTextContext::internalDrawDFText(GrAtlasTextBlob* blob, int runIndex, | |
| 565 const SkPaint& skPaint, GrColor colo
r, | |
| 566 const SkMatrix& viewMatrix, | |
| 567 const char text[], size_t byteLength
, | |
| 568 SkScalar x, SkScalar y, | |
| 569 SkScalar textRatio, | |
| 570 SkTDArray<char>* fallbackTxt, | |
| 571 SkTDArray<SkScalar>* fallbackPos, | |
| 572 SkPoint* offset, | |
| 573 const SkPaint& origPaint) { | |
| 574 SkASSERT(byteLength == 0 || text != nullptr); | |
| 575 | |
| 576 // nothing to draw | |
| 577 if (text == nullptr || byteLength == 0) { | |
| 578 return; | |
| 579 } | |
| 580 | |
| 581 SkDrawCacheProc glyphCacheProc = origPaint.getDrawCacheProc(); | |
| 582 SkAutoDescriptor desc; | |
| 583 origPaint.getScalerContextDescriptor(&desc, fSurfaceProps, nullptr, true); | |
| 584 SkGlyphCache* origPaintCache = SkGlyphCache::DetachCache(origPaint.getTypefa
ce(), | |
| 585 desc.getDesc()); | |
| 586 | |
| 587 SkTArray<SkScalar> positions; | |
| 588 | |
| 589 const char* textPtr = text; | |
| 590 SkFixed stopX = 0; | |
| 591 SkFixed stopY = 0; | |
| 592 SkFixed origin = 0; | |
| 593 switch (origPaint.getTextAlign()) { | |
| 594 case SkPaint::kRight_Align: origin = SK_Fixed1; break; | |
| 595 case SkPaint::kCenter_Align: origin = SK_FixedHalf; break; | |
| 596 case SkPaint::kLeft_Align: origin = 0; break; | |
| 597 } | |
| 598 | |
| 599 SkAutoKern autokern; | |
| 600 const char* stop = text + byteLength; | |
| 601 while (textPtr < stop) { | |
| 602 // don't need x, y here, since all subpixel variants will have the | |
| 603 // same advance | |
| 604 const SkGlyph& glyph = glyphCacheProc(origPaintCache, &textPtr, 0, 0); | |
| 605 | |
| 606 SkFixed width = glyph.fAdvanceX + autokern.adjust(glyph); | |
| 607 positions.push_back(SkFixedToScalar(stopX + SkFixedMul(origin, width))); | |
| 608 | |
| 609 SkFixed height = glyph.fAdvanceY; | |
| 610 positions.push_back(SkFixedToScalar(stopY + SkFixedMul(origin, height)))
; | |
| 611 | |
| 612 stopX += width; | |
| 613 stopY += height; | |
| 614 } | |
| 615 SkASSERT(textPtr == stop); | |
| 616 | |
| 617 SkGlyphCache::AttachCache(origPaintCache); | |
| 618 | |
| 619 // now adjust starting point depending on alignment | |
| 620 SkScalar alignX = SkFixedToScalar(stopX); | |
| 621 SkScalar alignY = SkFixedToScalar(stopY); | |
| 622 if (origPaint.getTextAlign() == SkPaint::kCenter_Align) { | |
| 623 alignX = SkScalarHalf(alignX); | |
| 624 alignY = SkScalarHalf(alignY); | |
| 625 } else if (origPaint.getTextAlign() == SkPaint::kLeft_Align) { | |
| 626 alignX = 0; | |
| 627 alignY = 0; | |
| 628 } | |
| 629 x -= alignX; | |
| 630 y -= alignY; | |
| 631 *offset = SkPoint::Make(x, y); | |
| 632 | |
| 633 this->internalDrawDFPosText(blob, runIndex, skPaint, color, viewMatrix, text
, byteLength, | |
| 634 positions.begin(), 2, *offset, textRatio, fallba
ckTxt, | |
| 635 fallbackPos); | |
| 636 } | |
| 637 | |
| 638 void GrAtlasTextContext::internalDrawDFPosText(GrAtlasTextBlob* blob, int runInd
ex, | |
| 639 const SkPaint& skPaint, GrColor c
olor, | |
| 640 const SkMatrix& viewMatrix, | |
| 641 const char text[], size_t byteLen
gth, | |
| 642 const SkScalar pos[], int scalars
PerPosition, | |
| 643 const SkPoint& offset, | |
| 644 SkScalar textRatio, | |
| 645 SkTDArray<char>* fallbackTxt, | |
| 646 SkTDArray<SkScalar>* fallbackPos)
{ | |
| 647 | |
| 648 SkASSERT(byteLength == 0 || text != nullptr); | |
| 649 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition); | |
| 650 | |
| 651 // nothing to draw | |
| 652 if (text == nullptr || byteLength == 0) { | |
| 653 return; | |
| 654 } | |
| 655 | |
| 656 fCurrStrike = nullptr; | |
| 657 | |
| 658 SkDrawCacheProc glyphCacheProc = skPaint.getDrawCacheProc(); | |
| 659 SkGlyphCache* cache = this->setupCache(&blob->fRuns[runIndex], skPaint, null
ptr, true); | |
| 660 GrFontScaler* fontScaler = GetGrFontScaler(cache); | |
| 661 | |
| 662 const char* stop = text + byteLength; | |
| 663 | |
| 664 if (SkPaint::kLeft_Align == skPaint.getTextAlign()) { | |
| 665 while (text < stop) { | |
| 666 const char* lastText = text; | |
| 667 // the last 2 parameters are ignored | |
| 668 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0); | |
| 669 | |
| 670 if (glyph.fWidth) { | |
| 671 SkScalar x = offset.x() + pos[0]; | |
| 672 SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0)
; | |
| 673 | |
| 674 if (!this->dfAppendGlyph(blob, | |
| 675 runIndex, | |
| 676 glyph, | |
| 677 x, y, color, fontScaler, | |
| 678 textRatio, viewMatrix)) { | |
| 679 // couldn't append, send to fallback | |
| 680 fallbackTxt->append(SkToInt(text-lastText), lastText); | |
| 681 *fallbackPos->append() = pos[0]; | |
| 682 if (2 == scalarsPerPosition) { | |
| 683 *fallbackPos->append() = pos[1]; | |
| 684 } | |
| 685 } | |
| 686 } | |
| 687 pos += scalarsPerPosition; | |
| 688 } | |
| 689 } else { | |
| 690 SkScalar alignMul = SkPaint::kCenter_Align == skPaint.getTextAlign() ? S
K_ScalarHalf | |
| 691 : S
K_Scalar1; | |
| 692 while (text < stop) { | |
| 693 const char* lastText = text; | |
| 694 // the last 2 parameters are ignored | |
| 695 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0); | |
| 696 | |
| 697 if (glyph.fWidth) { | |
| 698 SkScalar x = offset.x() + pos[0]; | |
| 699 SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0)
; | |
| 700 | |
| 701 SkScalar advanceX = SkFixedToScalar(glyph.fAdvanceX) * alignMul
* textRatio; | |
| 702 SkScalar advanceY = SkFixedToScalar(glyph.fAdvanceY) * alignMul
* textRatio; | |
| 703 | |
| 704 if (!this->dfAppendGlyph(blob, | |
| 705 runIndex, | |
| 706 glyph, | |
| 707 x - advanceX, y - advanceY, color, | |
| 708 fontScaler, | |
| 709 textRatio, | |
| 710 viewMatrix)) { | |
| 711 // couldn't append, send to fallback | |
| 712 fallbackTxt->append(SkToInt(text-lastText), lastText); | |
| 713 *fallbackPos->append() = pos[0]; | |
| 714 if (2 == scalarsPerPosition) { | |
| 715 *fallbackPos->append() = pos[1]; | |
| 716 } | |
| 717 } | |
| 718 } | |
| 719 pos += scalarsPerPosition; | |
| 720 } | |
| 721 } | |
| 722 | |
| 723 SkGlyphCache::AttachCache(cache); | |
| 724 } | |
| 725 | |
| 726 bool GrAtlasTextContext::dfAppendGlyph(GrAtlasTextBlob* blob, int runIndex, | |
| 727 const SkGlyph& skGlyph, | |
| 728 SkScalar sx, SkScalar sy, GrColor color, | |
| 729 GrFontScaler* scaler, | |
| 730 SkScalar textRatio, const SkMatrix& viewM
atrix) { | |
| 731 if (!fCurrStrike) { | |
| 732 fCurrStrike = fContext->getBatchFontCache()->getStrike(scaler); | |
| 733 } | |
| 734 | |
| 735 GrGlyph::PackedID id = GrGlyph::Pack(skGlyph.getGlyphID(), | |
| 736 skGlyph.getSubXFixed(), | |
| 737 skGlyph.getSubYFixed(), | |
| 738 GrGlyph::kDistance_MaskStyle); | |
| 739 GrGlyph* glyph = fCurrStrike->getGlyph(skGlyph, id, scaler); | |
| 740 if (!glyph) { | |
| 741 return true; | |
| 742 } | |
| 743 | |
| 744 // fallback to color glyph support | |
| 745 if (kA8_GrMaskFormat != glyph->fMaskFormat) { | |
| 746 return false; | |
| 747 } | |
| 748 | |
| 749 SkScalar dx = SkIntToScalar(glyph->fBounds.fLeft + SK_DistanceFieldInset); | |
| 750 SkScalar dy = SkIntToScalar(glyph->fBounds.fTop + SK_DistanceFieldInset); | |
| 751 SkScalar width = SkIntToScalar(glyph->fBounds.width() - 2 * SK_DistanceField
Inset); | |
| 752 SkScalar height = SkIntToScalar(glyph->fBounds.height() - 2 * SK_DistanceFie
ldInset); | |
| 753 | |
| 754 SkScalar scale = textRatio; | |
| 755 dx *= scale; | |
| 756 dy *= scale; | |
| 757 width *= scale; | |
| 758 height *= scale; | |
| 759 sx += dx; | |
| 760 sy += dy; | |
| 761 SkRect glyphRect = SkRect::MakeXYWH(sx, sy, width, height); | |
| 762 | |
| 763 blob->appendGlyph(runIndex, glyphRect, color, fCurrStrike, glyph, scaler, sk
Glyph, | |
| 764 sx - dx, sy - dy, scale, true); | |
| 765 return true; | |
| 766 } | |
| 767 | |
| 768 ////////////////////////////////////////////////////////////////////////////////
/////////////////// | |
| 769 | |
| 770 #ifdef GR_TEST_UTILS | |
| 771 | |
| 772 DRAW_BATCH_TEST_DEFINE(TextBlobBatch) { | |
| 773 static uint32_t gContextID = SK_InvalidGenID; | |
| 774 static GrAtlasTextContext* gTextContext = nullptr; | |
| 775 static SkSurfaceProps gSurfaceProps(SkSurfaceProps::kLegacyFontHost_InitType
); | |
| 776 | |
| 777 if (context->uniqueID() != gContextID) { | |
| 778 gContextID = context->uniqueID(); | |
| 779 delete gTextContext; | |
| 780 | |
| 781 // We don't yet test the fall back to paths in the GrTextContext base cl
ass. This is mostly | |
| 782 // because we don't really want to have a gpu device here. | |
| 783 // We enable distance fields by twiddling a knob on the paint | |
| 784 gTextContext = GrAtlasTextContext::Create(context, gSurfaceProps); | |
| 785 } | |
| 786 | |
| 787 // Setup dummy SkPaint / GrPaint | |
| 788 GrColor color = GrRandomColor(random); | |
| 789 SkMatrix viewMatrix = GrTest::TestMatrixInvertible(random); | |
| 790 SkPaint skPaint; | |
| 791 skPaint.setColor(color); | |
| 792 skPaint.setLCDRenderText(random->nextBool()); | |
| 793 skPaint.setAntiAlias(skPaint.isLCDRenderText() ? true : random->nextBool()); | |
| 794 skPaint.setSubpixelText(random->nextBool()); | |
| 795 | |
| 796 GrPaint grPaint; | |
| 797 if (!SkPaintToGrPaint(context, skPaint, viewMatrix, &grPaint)) { | |
| 798 SkFAIL("couldn't convert paint\n"); | |
| 799 } | |
| 800 | |
| 801 const char* text = "The quick brown fox jumps over the lazy dog."; | |
| 802 int textLen = (int)strlen(text); | |
| 803 | |
| 804 // Setup clip | |
| 805 GrClip clip; | |
| 806 SkIRect noClip = SkIRect::MakeLargest(); | |
| 807 | |
| 808 // right now we don't handle textblobs, nor do we handle drawPosText. Since
we only | |
| 809 // intend to test the batch with this unit test, that is okay. | |
| 810 SkAutoTUnref<GrAtlasTextBlob> blob( | |
| 811 gTextContext->createDrawTextBlob(clip, grPaint, skPaint, viewMatrix,
text, | |
| 812 static_cast<size_t>(textLen), 0, 0,
noClip)); | |
| 813 | |
| 814 SkScalar transX = static_cast<SkScalar>(random->nextU()); | |
| 815 SkScalar transY = static_cast<SkScalar>(random->nextU()); | |
| 816 const GrAtlasTextBlob::Run::SubRunInfo& info = blob->fRuns[0].fSubRunInfo[0]
; | |
| 817 return blob->createBatch(info, textLen, 0, 0, color, transX, transY, skPaint
, | |
| 818 gSurfaceProps, gTextContext->dfAdjustTable(), | |
| 819 context->getBatchFontCache()); | |
| 820 } | |
| 821 | |
| 822 #endif | |
| OLD | NEW |