| OLD | NEW |
| (Empty) |
| 1 /* | |
| 2 * Copyright 2013 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 "GrDistanceFieldTextContext.h" | |
| 9 #include "GrAtlas.h" | |
| 10 #include "GrAtlasTextContext.h" | |
| 11 #include "GrBitmapTextContext.h" | |
| 12 #include "GrDrawTarget.h" | |
| 13 #include "GrDrawTargetCaps.h" | |
| 14 #include "GrFontAtlasSizes.h" | |
| 15 #include "GrFontCache.h" | |
| 16 #include "GrFontScaler.h" | |
| 17 #include "GrGpu.h" | |
| 18 #include "GrIndexBuffer.h" | |
| 19 #include "GrStrokeInfo.h" | |
| 20 #include "GrTexturePriv.h" | |
| 21 | |
| 22 #include "SkAutoKern.h" | |
| 23 #include "SkColorFilter.h" | |
| 24 #include "SkDistanceFieldGen.h" | |
| 25 #include "SkDraw.h" | |
| 26 #include "SkGlyphCache.h" | |
| 27 #include "SkGpuDevice.h" | |
| 28 #include "SkPath.h" | |
| 29 #include "SkRTConf.h" | |
| 30 #include "SkStrokeRec.h" | |
| 31 #include "effects/GrDistanceFieldGeoProc.h" | |
| 32 | |
| 33 SK_CONF_DECLARE(bool, c_DumpFontCache, "gpu.dumpFontCache", false, | |
| 34 "Dump the contents of the font cache before every purge."); | |
| 35 | |
| 36 static const int kMinDFFontSize = 18; | |
| 37 static const int kSmallDFFontSize = 32; | |
| 38 static const int kSmallDFFontLimit = 32; | |
| 39 static const int kMediumDFFontSize = 72; | |
| 40 static const int kMediumDFFontLimit = 72; | |
| 41 static const int kLargeDFFontSize = 162; | |
| 42 | |
| 43 static const int kVerticesPerGlyph = 4; | |
| 44 static const int kIndicesPerGlyph = 6; | |
| 45 | |
| 46 #ifdef SK_DEBUG | |
| 47 static const int kExpectedDistanceAdjustTableSize = 8; | |
| 48 #endif | |
| 49 static const int kDistanceAdjustLumShift = 5; | |
| 50 | |
| 51 GrDistanceFieldTextContext::GrDistanceFieldTextContext(GrContext* context, | |
| 52 SkGpuDevice* gpuDevice, | |
| 53 const SkDeviceProperties&
properties, | |
| 54 bool enable) | |
| 55 : GrTextContext(context, gpuDevice, properties) { | |
| 56 #if SK_FORCE_DISTANCE_FIELD_TEXT | |
| 57 fEnableDFRendering = true; | |
| 58 #else | |
| 59 fEnableDFRendering = enable; | |
| 60 #endif | |
| 61 fStrike = NULL; | |
| 62 fDistanceAdjustTable = NULL; | |
| 63 | |
| 64 fEffectTextureUniqueID = SK_InvalidUniqueID; | |
| 65 fEffectColor = GrColor_ILLEGAL; | |
| 66 fEffectFlags = kInvalid_DistanceFieldEffectFlag; | |
| 67 | |
| 68 fVertices = NULL; | |
| 69 fCurrVertex = 0; | |
| 70 fAllocVertexCount = 0; | |
| 71 fTotalVertexCount = 0; | |
| 72 fCurrTexture = NULL; | |
| 73 | |
| 74 fVertexBounds.setLargestInverted(); | |
| 75 } | |
| 76 | |
| 77 GrDistanceFieldTextContext* GrDistanceFieldTextContext::Create(GrContext* contex
t, | |
| 78 SkGpuDevice* gpuD
evice, | |
| 79 const SkDevicePro
perties& props, | |
| 80 bool enable) { | |
| 81 GrDistanceFieldTextContext* textContext = SkNEW_ARGS(GrDistanceFieldTextCont
ext, | |
| 82 (context, gpuDevice, pr
ops, enable)); | |
| 83 textContext->buildDistanceAdjustTable(); | |
| 84 #ifdef USE_BITMAP_TEXTBLOBS | |
| 85 textContext->fFallbackTextContext = GrAtlasTextContext::Create(context, gpuD
evice, props, | |
| 86 enable); | |
| 87 #else | |
| 88 textContext->fFallbackTextContext = GrBitmapTextContext::Create(context, gpu
Device, props); | |
| 89 #endif | |
| 90 | |
| 91 return textContext; | |
| 92 } | |
| 93 | |
| 94 void GrDistanceFieldTextContext::buildDistanceAdjustTable() { | |
| 95 | |
| 96 // This is used for an approximation of the mask gamma hack, used by raster
and bitmap | |
| 97 // text. The mask gamma hack is based off of guessing what the blend color i
s going to | |
| 98 // be, and adjusting the mask so that when run through the linear blend will | |
| 99 // produce the value closest to the desired result. However, in practice thi
s means | |
| 100 // that the 'adjusted' mask is just increasing or decreasing the coverage of | |
| 101 // the mask depending on what it is thought it will blit against. For black
(on | |
| 102 // assumed white) this means that coverages are decreased (on a curve). For
white (on | |
| 103 // assumed black) this means that coverages are increased (on a a curve). At | |
| 104 // middle (perceptual) gray (which could be blit against anything) the cover
ages | |
| 105 // remain the same. | |
| 106 // | |
| 107 // The idea here is that instead of determining the initial (real) coverage
and | |
| 108 // then adjusting that coverage, we determine an adjusted coverage directly
by | |
| 109 // essentially manipulating the geometry (in this case, the distance to the
glyph | |
| 110 // edge). So for black (on assumed white) this thins a bit; for white (on | |
| 111 // assumed black) this fake bolds the geometry a bit. | |
| 112 // | |
| 113 // The distance adjustment is calculated by determining the actual coverage
value which | |
| 114 // when fed into in the mask gamma table gives us an 'adjusted coverage' val
ue of 0.5. This | |
| 115 // actual coverage value (assuming it's between 0 and 1) corresponds to a di
stance from the | |
| 116 // actual edge. So by subtracting this distance adjustment and computing wit
hout the | |
| 117 // the coverage adjustment we should get 0.5 coverage at the same point. | |
| 118 // | |
| 119 // This has several implications: | |
| 120 // For non-gray lcd smoothed text, each subpixel essentially is using a | |
| 121 // slightly different geometry. | |
| 122 // | |
| 123 // For black (on assumed white) this may not cover some pixels which wer
e | |
| 124 // previously covered; however those pixels would have been only slightl
y | |
| 125 // covered and that slight coverage would have been decreased anyway. Al
so, some pixels | |
| 126 // which were previously fully covered may no longer be fully covered. | |
| 127 // | |
| 128 // For white (on assumed black) this may cover some pixels which weren't | |
| 129 // previously covered at all. | |
| 130 | |
| 131 int width, height; | |
| 132 size_t size; | |
| 133 | |
| 134 #ifdef SK_GAMMA_CONTRAST | |
| 135 SkScalar contrast = SK_GAMMA_CONTRAST; | |
| 136 #else | |
| 137 SkScalar contrast = 0.5f; | |
| 138 #endif | |
| 139 SkScalar paintGamma = fDeviceProperties.gamma(); | |
| 140 SkScalar deviceGamma = fDeviceProperties.gamma(); | |
| 141 | |
| 142 size = SkScalerContext::GetGammaLUTSize(contrast, paintGamma, deviceGamma, | |
| 143 &width, &height); | |
| 144 | |
| 145 SkASSERT(kExpectedDistanceAdjustTableSize == height); | |
| 146 fDistanceAdjustTable = SkNEW_ARRAY(SkScalar, height); | |
| 147 | |
| 148 SkAutoTArray<uint8_t> data((int)size); | |
| 149 SkScalerContext::GetGammaLUTData(contrast, paintGamma, deviceGamma, data.get
()); | |
| 150 | |
| 151 // find the inverse points where we cross 0.5 | |
| 152 // binsearch might be better, but we only need to do this once on creation | |
| 153 for (int row = 0; row < height; ++row) { | |
| 154 uint8_t* rowPtr = data.get() + row*width; | |
| 155 for (int col = 0; col < width - 1; ++col) { | |
| 156 if (rowPtr[col] <= 127 && rowPtr[col + 1] >= 128) { | |
| 157 // compute point where a mask value will give us a result of 0.5 | |
| 158 float interp = (127.5f - rowPtr[col]) / (rowPtr[col + 1] - rowPt
r[col]); | |
| 159 float borderAlpha = (col + interp) / 255.f; | |
| 160 | |
| 161 // compute t value for that alpha | |
| 162 // this is an approximate inverse for smoothstep() | |
| 163 float t = borderAlpha*(borderAlpha*(4.0f*borderAlpha - 6.0f) + 5
.0f) / 3.0f; | |
| 164 | |
| 165 // compute distance which gives us that t value | |
| 166 const float kDistanceFieldAAFactor = 0.65f; // should match SK_D
istanceFieldAAFactor | |
| 167 float d = 2.0f*kDistanceFieldAAFactor*t - kDistanceFieldAAFactor
; | |
| 168 | |
| 169 fDistanceAdjustTable[row] = d; | |
| 170 break; | |
| 171 } | |
| 172 } | |
| 173 } | |
| 174 } | |
| 175 | |
| 176 | |
| 177 GrDistanceFieldTextContext::~GrDistanceFieldTextContext() { | |
| 178 SkDELETE_ARRAY(fDistanceAdjustTable); | |
| 179 fDistanceAdjustTable = NULL; | |
| 180 } | |
| 181 | |
| 182 bool GrDistanceFieldTextContext::canDraw(const GrRenderTarget* rt, | |
| 183 const GrClip& clip, | |
| 184 const GrPaint& paint, | |
| 185 const SkPaint& skPaint, | |
| 186 const SkMatrix& viewMatrix) { | |
| 187 // TODO: support perspective (need getMaxScale replacement) | |
| 188 if (viewMatrix.hasPerspective()) { | |
| 189 return false; | |
| 190 } | |
| 191 | |
| 192 SkScalar maxScale = viewMatrix.getMaxScale(); | |
| 193 SkScalar scaledTextSize = maxScale*skPaint.getTextSize(); | |
| 194 // Hinted text looks far better at small resolutions | |
| 195 // Scaling up beyond 2x yields undesireable artifacts | |
| 196 if (scaledTextSize < kMinDFFontSize || scaledTextSize > 2*kLargeDFFontSize)
{ | |
| 197 return false; | |
| 198 } | |
| 199 | |
| 200 if (!fEnableDFRendering && !skPaint.isDistanceFieldTextTEMP() && | |
| 201 scaledTextSize < kLargeDFFontSize) { | |
| 202 return false; | |
| 203 } | |
| 204 | |
| 205 // rasterizers and mask filters modify alpha, which doesn't | |
| 206 // translate well to distance | |
| 207 if (skPaint.getRasterizer() || skPaint.getMaskFilter() || | |
| 208 !fContext->getTextTarget()->caps()->shaderDerivativeSupport()) { | |
| 209 return false; | |
| 210 } | |
| 211 | |
| 212 // TODO: add some stroking support | |
| 213 if (skPaint.getStyle() != SkPaint::kFill_Style) { | |
| 214 return false; | |
| 215 } | |
| 216 | |
| 217 return true; | |
| 218 } | |
| 219 | |
| 220 inline void GrDistanceFieldTextContext::init(GrRenderTarget* rt, const GrClip& c
lip, | |
| 221 const GrPaint& paint, const SkPaint
& skPaint, | |
| 222 const SkIRect& regionClipBounds) { | |
| 223 GrTextContext::init(rt, clip, paint, skPaint, regionClipBounds); | |
| 224 | |
| 225 fStrike = NULL; | |
| 226 | |
| 227 const SkMatrix& ctm = fViewMatrix; | |
| 228 | |
| 229 // getMaxScale doesn't support perspective, so neither do we at the moment | |
| 230 SkASSERT(!ctm.hasPerspective()); | |
| 231 SkScalar maxScale = ctm.getMaxScale(); | |
| 232 SkScalar textSize = fSkPaint.getTextSize(); | |
| 233 SkScalar scaledTextSize = textSize; | |
| 234 // if we have non-unity scale, we need to choose our base text size | |
| 235 // based on the SkPaint's text size multiplied by the max scale factor | |
| 236 // TODO: do we need to do this if we're scaling down (i.e. maxScale < 1)? | |
| 237 if (maxScale > 0 && !SkScalarNearlyEqual(maxScale, SK_Scalar1)) { | |
| 238 scaledTextSize *= maxScale; | |
| 239 } | |
| 240 | |
| 241 fVertices = NULL; | |
| 242 fCurrVertex = 0; | |
| 243 fAllocVertexCount = 0; | |
| 244 fTotalVertexCount = 0; | |
| 245 | |
| 246 if (scaledTextSize <= kSmallDFFontLimit) { | |
| 247 fTextRatio = textSize / kSmallDFFontSize; | |
| 248 fSkPaint.setTextSize(SkIntToScalar(kSmallDFFontSize)); | |
| 249 #if DEBUG_TEXT_SIZE | |
| 250 fSkPaint.setColor(SkColorSetARGB(0xFF, 0x00, 0x00, 0x7F)); | |
| 251 fPaint.setColor(GrColorPackRGBA(0x00, 0x00, 0x7F, 0xFF)); | |
| 252 #endif | |
| 253 } else if (scaledTextSize <= kMediumDFFontLimit) { | |
| 254 fTextRatio = textSize / kMediumDFFontSize; | |
| 255 fSkPaint.setTextSize(SkIntToScalar(kMediumDFFontSize)); | |
| 256 #if DEBUG_TEXT_SIZE | |
| 257 fSkPaint.setColor(SkColorSetARGB(0xFF, 0x00, 0x3F, 0x00)); | |
| 258 fPaint.setColor(GrColorPackRGBA(0x00, 0x3F, 0x00, 0xFF)); | |
| 259 #endif | |
| 260 } else { | |
| 261 fTextRatio = textSize / kLargeDFFontSize; | |
| 262 fSkPaint.setTextSize(SkIntToScalar(kLargeDFFontSize)); | |
| 263 #if DEBUG_TEXT_SIZE | |
| 264 fSkPaint.setColor(SkColorSetARGB(0xFF, 0x7F, 0x00, 0x00)); | |
| 265 fPaint.setColor(GrColorPackRGBA(0x7F, 0x00, 0x00, 0xFF)); | |
| 266 #endif | |
| 267 } | |
| 268 | |
| 269 fUseLCDText = fSkPaint.isLCDRenderText(); | |
| 270 | |
| 271 fSkPaint.setLCDRenderText(false); | |
| 272 fSkPaint.setAutohinted(false); | |
| 273 fSkPaint.setHinting(SkPaint::kNormal_Hinting); | |
| 274 fSkPaint.setSubpixelText(true); | |
| 275 } | |
| 276 | |
| 277 void GrDistanceFieldTextContext::onDrawText(GrRenderTarget* rt, const GrClip& cl
ip, | |
| 278 const GrPaint& paint, | |
| 279 const SkPaint& skPaint, const SkMatr
ix& viewMatrix, | |
| 280 const char text[], size_t byteLength
, | |
| 281 SkScalar x, SkScalar y, | |
| 282 const SkIRect& regionClipBounds) { | |
| 283 SkASSERT(byteLength == 0 || text != NULL); | |
| 284 | |
| 285 // nothing to draw | |
| 286 if (text == NULL || byteLength == 0) { | |
| 287 return; | |
| 288 } | |
| 289 | |
| 290 fViewMatrix = viewMatrix; | |
| 291 SkDrawCacheProc glyphCacheProc = skPaint.getDrawCacheProc(); | |
| 292 SkAutoGlyphCache autoCache(skPaint, &fDeviceProperties, NULL); | |
| 293 SkGlyphCache* cache = autoCache.getCache(); | |
| 294 | |
| 295 SkTArray<SkScalar> positions; | |
| 296 | |
| 297 const char* textPtr = text; | |
| 298 SkFixed stopX = 0; | |
| 299 SkFixed stopY = 0; | |
| 300 SkFixed origin; | |
| 301 switch (skPaint.getTextAlign()) { | |
| 302 case SkPaint::kRight_Align: origin = SK_Fixed1; break; | |
| 303 case SkPaint::kCenter_Align: origin = SK_FixedHalf; break; | |
| 304 case SkPaint::kLeft_Align: origin = 0; break; | |
| 305 default: SkFAIL("Invalid paint origin"); return; | |
| 306 } | |
| 307 | |
| 308 SkAutoKern autokern; | |
| 309 const char* stop = text + byteLength; | |
| 310 while (textPtr < stop) { | |
| 311 // don't need x, y here, since all subpixel variants will have the | |
| 312 // same advance | |
| 313 const SkGlyph& glyph = glyphCacheProc(cache, &textPtr, 0, 0); | |
| 314 | |
| 315 SkFixed width = glyph.fAdvanceX + autokern.adjust(glyph); | |
| 316 positions.push_back(SkFixedToScalar(stopX + SkFixedMul(origin, width))); | |
| 317 | |
| 318 SkFixed height = glyph.fAdvanceY; | |
| 319 positions.push_back(SkFixedToScalar(stopY + SkFixedMul(origin, height)))
; | |
| 320 | |
| 321 stopX += width; | |
| 322 stopY += height; | |
| 323 } | |
| 324 SkASSERT(textPtr == stop); | |
| 325 | |
| 326 // now adjust starting point depending on alignment | |
| 327 SkScalar alignX = SkFixedToScalar(stopX); | |
| 328 SkScalar alignY = SkFixedToScalar(stopY); | |
| 329 if (skPaint.getTextAlign() == SkPaint::kCenter_Align) { | |
| 330 alignX = SkScalarHalf(alignX); | |
| 331 alignY = SkScalarHalf(alignY); | |
| 332 } else if (skPaint.getTextAlign() == SkPaint::kLeft_Align) { | |
| 333 alignX = 0; | |
| 334 alignY = 0; | |
| 335 } | |
| 336 x -= alignX; | |
| 337 y -= alignY; | |
| 338 SkPoint offset = SkPoint::Make(x, y); | |
| 339 | |
| 340 this->onDrawPosText(rt, clip, paint, skPaint, viewMatrix, text, byteLength,
positions.begin(), | |
| 341 2, offset, regionClipBounds); | |
| 342 } | |
| 343 | |
| 344 void GrDistanceFieldTextContext::onDrawPosText(GrRenderTarget* rt, const GrClip&
clip, | |
| 345 const GrPaint& paint, | |
| 346 const SkPaint& skPaint, const SkM
atrix& viewMatrix, | |
| 347 const char text[], size_t byteLen
gth, | |
| 348 const SkScalar pos[], int scalars
PerPosition, | |
| 349 const SkPoint& offset, | |
| 350 const SkIRect& regionClipBounds)
{ | |
| 351 | |
| 352 SkASSERT(byteLength == 0 || text != NULL); | |
| 353 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition); | |
| 354 | |
| 355 // nothing to draw | |
| 356 if (text == NULL || byteLength == 0 /* no raster clip? || fRC->isEmpty()*/)
{ | |
| 357 return; | |
| 358 } | |
| 359 | |
| 360 fViewMatrix = viewMatrix; | |
| 361 this->init(rt, clip, paint, skPaint, regionClipBounds); | |
| 362 | |
| 363 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc(); | |
| 364 | |
| 365 SkAutoGlyphCacheNoGamma autoCache(fSkPaint, &fDeviceProperties, NULL); | |
| 366 SkGlyphCache* cache = autoCache.getCache(); | |
| 367 GrFontScaler* fontScaler = GetGrFontScaler(cache); | |
| 368 | |
| 369 int numGlyphs = fSkPaint.textToGlyphs(text, byteLength, NULL); | |
| 370 fTotalVertexCount = kVerticesPerGlyph*numGlyphs; | |
| 371 | |
| 372 const char* stop = text + byteLength; | |
| 373 SkTArray<char> fallbackTxt; | |
| 374 SkTArray<SkScalar> fallbackPos; | |
| 375 | |
| 376 if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) { | |
| 377 while (text < stop) { | |
| 378 const char* lastText = text; | |
| 379 // the last 2 parameters are ignored | |
| 380 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0); | |
| 381 | |
| 382 if (glyph.fWidth) { | |
| 383 SkScalar x = offset.x() + pos[0]; | |
| 384 SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0)
; | |
| 385 | |
| 386 if (!this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(), | |
| 387 glyph.getSubXFixed(), | |
| 388 glyph.getSubYFixed(), | |
| 389 GrGlyph::kDistance_MaskStyl
e), | |
| 390 x, y, fontScaler)) { | |
| 391 // couldn't append, send to fallback | |
| 392 fallbackTxt.push_back_n(SkToInt(text-lastText), lastText); | |
| 393 fallbackPos.push_back(pos[0]); | |
| 394 if (2 == scalarsPerPosition) { | |
| 395 fallbackPos.push_back(pos[1]); | |
| 396 } | |
| 397 } | |
| 398 } | |
| 399 pos += scalarsPerPosition; | |
| 400 } | |
| 401 } else { | |
| 402 SkScalar alignMul = SkPaint::kCenter_Align == fSkPaint.getTextAlign() ?
SK_ScalarHalf | |
| 403 :
SK_Scalar1; | |
| 404 while (text < stop) { | |
| 405 const char* lastText = text; | |
| 406 // the last 2 parameters are ignored | |
| 407 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0); | |
| 408 | |
| 409 if (glyph.fWidth) { | |
| 410 SkScalar x = offset.x() + pos[0]; | |
| 411 SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0)
; | |
| 412 | |
| 413 SkScalar advanceX = SkFixedToScalar(glyph.fAdvanceX)*alignMul*fT
extRatio; | |
| 414 SkScalar advanceY = SkFixedToScalar(glyph.fAdvanceY)*alignMul*fT
extRatio; | |
| 415 | |
| 416 if (!this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(), | |
| 417 glyph.getSubXFixed(), | |
| 418 glyph.getSubYFixed(), | |
| 419 GrGlyph::kDistance_MaskStyl
e), | |
| 420 x - advanceX, y - advanceY, fontScaler))
{ | |
| 421 // couldn't append, send to fallback | |
| 422 fallbackTxt.push_back_n(SkToInt(text-lastText), lastText); | |
| 423 fallbackPos.push_back(pos[0]); | |
| 424 if (2 == scalarsPerPosition) { | |
| 425 fallbackPos.push_back(pos[1]); | |
| 426 } | |
| 427 } | |
| 428 } | |
| 429 pos += scalarsPerPosition; | |
| 430 } | |
| 431 } | |
| 432 | |
| 433 this->finish(); | |
| 434 | |
| 435 if (fallbackTxt.count() > 0) { | |
| 436 fFallbackTextContext->drawPosText(rt, clip, paint, skPaint, viewMatrix, | |
| 437 fallbackTxt.begin(), fallbackTxt.count
(), | |
| 438 fallbackPos.begin(), scalarsPerPositio
n, offset, | |
| 439 regionClipBounds); | |
| 440 } | |
| 441 } | |
| 442 | |
| 443 static inline GrColor skcolor_to_grcolor_nopremultiply(SkColor c) { | |
| 444 unsigned r = SkColorGetR(c); | |
| 445 unsigned g = SkColorGetG(c); | |
| 446 unsigned b = SkColorGetB(c); | |
| 447 return GrColorPackRGBA(r, g, b, 0xff); | |
| 448 } | |
| 449 | |
| 450 static size_t get_vertex_stride(bool useColorVerts) { | |
| 451 return useColorVerts ? (sizeof(SkPoint) + sizeof(GrColor) + sizeof(SkIPoint1
6)) : | |
| 452 (sizeof(SkPoint) + sizeof(SkIPoint16)); | |
| 453 } | |
| 454 | |
| 455 static void* alloc_vertices(GrDrawTarget* drawTarget, | |
| 456 int numVertices, | |
| 457 bool useColorVerts) { | |
| 458 if (numVertices <= 0) { | |
| 459 return NULL; | |
| 460 } | |
| 461 | |
| 462 void* vertices = NULL; | |
| 463 bool success = drawTarget->reserveVertexAndIndexSpace(numVertices, | |
| 464 get_vertex_stride(useC
olorVerts), | |
| 465 0, | |
| 466 &vertices, | |
| 467 NULL); | |
| 468 GrAlwaysAssert(success); | |
| 469 return vertices; | |
| 470 } | |
| 471 | |
| 472 void GrDistanceFieldTextContext::setupCoverageEffect(const SkColor& filteredColo
r) { | |
| 473 GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kBilerp_F
ilterMode); | |
| 474 GrTextureParams gammaParams(SkShader::kClamp_TileMode, GrTextureParams::kNon
e_FilterMode); | |
| 475 | |
| 476 uint32_t textureUniqueID = fCurrTexture->getUniqueID(); | |
| 477 const SkMatrix& ctm = fViewMatrix; | |
| 478 | |
| 479 // set up any flags | |
| 480 uint32_t flags = 0; | |
| 481 flags |= ctm.isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0; | |
| 482 flags |= fUseLCDText ? kUseLCD_DistanceFieldEffectFlag : 0; | |
| 483 flags |= fUseLCDText && ctm.rectStaysRect() ? | |
| 484 kRectToRect_DistanceFieldEffectFlag : 0; | |
| 485 bool useBGR = SkPixelGeometryIsBGR(fDeviceProperties.pixelGeometry()); | |
| 486 flags |= fUseLCDText && useBGR ? kBGR_DistanceFieldEffectFlag : 0; | |
| 487 | |
| 488 // see if we need to create a new effect | |
| 489 if (textureUniqueID != fEffectTextureUniqueID || | |
| 490 filteredColor != fEffectColor || | |
| 491 flags != fEffectFlags || | |
| 492 !fCachedGeometryProcessor->viewMatrix().cheapEqualTo(fViewMatrix)) { | |
| 493 GrColor color = fPaint.getColor(); | |
| 494 | |
| 495 if (fUseLCDText) { | |
| 496 GrColor colorNoPreMul = skcolor_to_grcolor_nopremultiply(filteredCol
or); | |
| 497 | |
| 498 float redCorrection = | |
| 499 fDistanceAdjustTable[GrColorUnpackR(colorNoPreMul) >> kDistanceA
djustLumShift]; | |
| 500 float greenCorrection = | |
| 501 fDistanceAdjustTable[GrColorUnpackG(colorNoPreMul) >> kDistanceA
djustLumShift]; | |
| 502 float blueCorrection = | |
| 503 fDistanceAdjustTable[GrColorUnpackB(colorNoPreMul) >> kDistanceA
djustLumShift]; | |
| 504 GrDistanceFieldLCDTextGeoProc::DistanceAdjust widthAdjust = | |
| 505 GrDistanceFieldLCDTextGeoProc::DistanceAdjust::Make(redCorrectio
n, | |
| 506 greenCorrect
ion, | |
| 507 blueCorrecti
on); | |
| 508 fCachedGeometryProcessor.reset(GrDistanceFieldLCDTextGeoProc::Create
(color, | |
| 509
fViewMatrix, | |
| 510
fCurrTexture, | |
| 511
params, | |
| 512
widthAdjust, | |
| 513
flags)); | |
| 514 } else { | |
| 515 flags |= kColorAttr_DistanceFieldEffectFlag; | |
| 516 bool opaque = GrColorIsOpaque(color); | |
| 517 #ifdef SK_GAMMA_APPLY_TO_A8 | |
| 518 U8CPU lum = SkColorSpaceLuminance::computeLuminance(fDevicePropertie
s.gamma(), | |
| 519 filteredColor); | |
| 520 float correction = fDistanceAdjustTable[lum >> kDistanceAdjustLumShi
ft]; | |
| 521 fCachedGeometryProcessor.reset(GrDistanceFieldA8TextGeoProc::Create(
color, | |
| 522
fViewMatrix, | |
| 523
fCurrTexture, | |
| 524
params, | |
| 525
correction, | |
| 526
flags, | |
| 527
opaque)); | |
| 528 #else | |
| 529 fCachedGeometryProcessor.reset(GrDistanceFieldA8TextGeoProc::Create(
color, | |
| 530
fViewMatrix, | |
| 531
fCurrTexture, | |
| 532
params, | |
| 533
flags, | |
| 534
opaque)); | |
| 535 #endif | |
| 536 } | |
| 537 fEffectTextureUniqueID = textureUniqueID; | |
| 538 fEffectColor = filteredColor; | |
| 539 fEffectFlags = flags; | |
| 540 } | |
| 541 | |
| 542 } | |
| 543 | |
| 544 inline bool GrDistanceFieldTextContext::uploadGlyph(GrGlyph* glyph, GrFontScaler
* scaler) { | |
| 545 if (!fStrike->glyphTooLargeForAtlas(glyph)) { | |
| 546 if (fStrike->addGlyphToAtlas(glyph, scaler)) { | |
| 547 return true; | |
| 548 } | |
| 549 | |
| 550 // try to clear out an unused plot before we flush | |
| 551 if (fContext->getFontCache()->freeUnusedPlot(fStrike, glyph) && | |
| 552 fStrike->addGlyphToAtlas(glyph, scaler)) { | |
| 553 return true; | |
| 554 } | |
| 555 | |
| 556 if (c_DumpFontCache) { | |
| 557 #ifdef SK_DEVELOPER | |
| 558 fContext->getFontCache()->dump(); | |
| 559 #endif | |
| 560 } | |
| 561 | |
| 562 // before we purge the cache, we must flush any accumulated draws | |
| 563 this->flush(); | |
| 564 fContext->flush(); | |
| 565 | |
| 566 // we should have an unused plot now | |
| 567 if (fContext->getFontCache()->freeUnusedPlot(fStrike, glyph) && | |
| 568 fStrike->addGlyphToAtlas(glyph, scaler)) { | |
| 569 return true; | |
| 570 } | |
| 571 | |
| 572 // we should never get here | |
| 573 SkASSERT(false); | |
| 574 } | |
| 575 | |
| 576 return false; | |
| 577 } | |
| 578 | |
| 579 | |
| 580 // Returns true if this method handled the glyph, false if needs to be passed to
fallback | |
| 581 // | |
| 582 bool GrDistanceFieldTextContext::appendGlyph(GrGlyph::PackedID packed, | |
| 583 SkScalar sx, SkScalar sy, | |
| 584 GrFontScaler* scaler) { | |
| 585 if (NULL == fDrawTarget) { | |
| 586 return true; | |
| 587 } | |
| 588 | |
| 589 if (NULL == fStrike) { | |
| 590 fStrike = fContext->getFontCache()->getStrike(scaler); | |
| 591 } | |
| 592 | |
| 593 GrGlyph* glyph = fStrike->getGlyph(packed, scaler); | |
| 594 if (NULL == glyph || glyph->fBounds.isEmpty()) { | |
| 595 return true; | |
| 596 } | |
| 597 | |
| 598 // fallback to color glyph support | |
| 599 if (kA8_GrMaskFormat != glyph->fMaskFormat) { | |
| 600 return false; | |
| 601 } | |
| 602 | |
| 603 SkScalar dx = SkIntToScalar(glyph->fBounds.fLeft + SK_DistanceFieldInset); | |
| 604 SkScalar dy = SkIntToScalar(glyph->fBounds.fTop + SK_DistanceFieldInset); | |
| 605 SkScalar width = SkIntToScalar(glyph->fBounds.width() - 2*SK_DistanceFieldIn
set); | |
| 606 SkScalar height = SkIntToScalar(glyph->fBounds.height() - 2*SK_DistanceField
Inset); | |
| 607 | |
| 608 SkScalar scale = fTextRatio; | |
| 609 dx *= scale; | |
| 610 dy *= scale; | |
| 611 width *= scale; | |
| 612 height *= scale; | |
| 613 sx += dx; | |
| 614 sy += dy; | |
| 615 SkRect glyphRect = SkRect::MakeXYWH(sx, sy, width, height); | |
| 616 | |
| 617 // check if we clipped out | |
| 618 SkRect dstRect; | |
| 619 const SkMatrix& ctm = fViewMatrix; | |
| 620 (void) ctm.mapRect(&dstRect, glyphRect); | |
| 621 if (fClipRect.quickReject(SkScalarTruncToInt(dstRect.left()), | |
| 622 SkScalarTruncToInt(dstRect.top()), | |
| 623 SkScalarTruncToInt(dstRect.right()), | |
| 624 SkScalarTruncToInt(dstRect.bottom()))) { | |
| 625 return true; | |
| 626 } | |
| 627 | |
| 628 if (NULL == glyph->fPlot) { | |
| 629 // needs to be a separate conditional to avoid over-optimization | |
| 630 // on Nexus 7 and Nexus 10 | |
| 631 | |
| 632 // If the glyph is too large we fall back to paths | |
| 633 if (!uploadGlyph(glyph, scaler)) { | |
| 634 if (NULL == glyph->fPath) { | |
| 635 SkPath* path = SkNEW(SkPath); | |
| 636 if (!scaler->getGlyphPath(glyph->glyphID(), path)) { | |
| 637 // flag the glyph as being dead? | |
| 638 delete path; | |
| 639 return true; | |
| 640 } | |
| 641 glyph->fPath = path; | |
| 642 } | |
| 643 | |
| 644 // flush any accumulated draws before drawing this glyph as a path. | |
| 645 this->flush(); | |
| 646 | |
| 647 SkMatrix ctm; | |
| 648 ctm.postTranslate(sx - dx, sy - dy); | |
| 649 | |
| 650 SkPath tmpPath(*glyph->fPath); | |
| 651 tmpPath.transform(ctm); | |
| 652 | |
| 653 GrStrokeInfo strokeInfo(SkStrokeRec::kFill_InitStyle); | |
| 654 fContext->drawPath(fRenderTarget, fClip, fPaint, fViewMatrix, tmpPat
h, strokeInfo); | |
| 655 | |
| 656 // remove this glyph from the vertices we need to allocate | |
| 657 fTotalVertexCount -= kVerticesPerGlyph; | |
| 658 return true; | |
| 659 } | |
| 660 } | |
| 661 | |
| 662 SkASSERT(glyph->fPlot); | |
| 663 GrDrawTarget::DrawToken drawToken = fDrawTarget->getCurrentDrawToken(); | |
| 664 glyph->fPlot->setDrawToken(drawToken); | |
| 665 | |
| 666 GrTexture* texture = glyph->fPlot->texture(); | |
| 667 SkASSERT(texture); | |
| 668 | |
| 669 if (fCurrTexture != texture || fCurrVertex + kVerticesPerGlyph > fTotalVerte
xCount) { | |
| 670 this->flush(); | |
| 671 fCurrTexture = texture; | |
| 672 fCurrTexture->ref(); | |
| 673 } | |
| 674 | |
| 675 bool useColorVerts = !fUseLCDText; | |
| 676 | |
| 677 if (NULL == fVertices) { | |
| 678 int maxQuadVertices = kVerticesPerGlyph * fContext->getQuadIndexBuffer()
->maxQuads(); | |
| 679 fAllocVertexCount = SkMin32(fTotalVertexCount, maxQuadVertices); | |
| 680 fVertices = alloc_vertices(fDrawTarget, | |
| 681 fAllocVertexCount, | |
| 682 useColorVerts); | |
| 683 } | |
| 684 | |
| 685 fVertexBounds.joinNonEmptyArg(glyphRect); | |
| 686 | |
| 687 int u0 = glyph->fAtlasLocation.fX + SK_DistanceFieldInset; | |
| 688 int v0 = glyph->fAtlasLocation.fY + SK_DistanceFieldInset; | |
| 689 int u1 = u0 + glyph->fBounds.width() - 2*SK_DistanceFieldInset; | |
| 690 int v1 = v0 + glyph->fBounds.height() - 2*SK_DistanceFieldInset; | |
| 691 | |
| 692 size_t vertSize = get_vertex_stride(useColorVerts); | |
| 693 intptr_t vertex = reinterpret_cast<intptr_t>(fVertices) + vertSize * fCurrVe
rtex; | |
| 694 | |
| 695 // V0 | |
| 696 SkPoint* position = reinterpret_cast<SkPoint*>(vertex); | |
| 697 position->set(glyphRect.fLeft, glyphRect.fTop); | |
| 698 if (useColorVerts) { | |
| 699 SkColor* color = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint)); | |
| 700 *color = fPaint.getColor(); | |
| 701 } | |
| 702 SkIPoint16* textureCoords = reinterpret_cast<SkIPoint16*>(vertex + vertSize
- | |
| 703 sizeof(SkIPoint16)
); | |
| 704 textureCoords->set(u0, v0); | |
| 705 vertex += vertSize; | |
| 706 | |
| 707 // V1 | |
| 708 position = reinterpret_cast<SkPoint*>(vertex); | |
| 709 position->set(glyphRect.fLeft, glyphRect.fBottom); | |
| 710 if (useColorVerts) { | |
| 711 SkColor* color = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint)); | |
| 712 *color = fPaint.getColor(); | |
| 713 } | |
| 714 textureCoords = reinterpret_cast<SkIPoint16*>(vertex + vertSize - sizeof(Sk
IPoint16)); | |
| 715 textureCoords->set(u0, v1); | |
| 716 vertex += vertSize; | |
| 717 | |
| 718 // V2 | |
| 719 position = reinterpret_cast<SkPoint*>(vertex); | |
| 720 position->set(glyphRect.fRight, glyphRect.fBottom); | |
| 721 if (useColorVerts) { | |
| 722 SkColor* color = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint)); | |
| 723 *color = fPaint.getColor(); | |
| 724 } | |
| 725 textureCoords = reinterpret_cast<SkIPoint16*>(vertex + vertSize - sizeof(Sk
IPoint16)); | |
| 726 textureCoords->set(u1, v1); | |
| 727 vertex += vertSize; | |
| 728 | |
| 729 // V3 | |
| 730 position = reinterpret_cast<SkPoint*>(vertex); | |
| 731 position->set(glyphRect.fRight, glyphRect.fTop); | |
| 732 if (useColorVerts) { | |
| 733 SkColor* color = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint)); | |
| 734 *color = fPaint.getColor(); | |
| 735 } | |
| 736 textureCoords = reinterpret_cast<SkIPoint16*>(vertex + vertSize - sizeof(Sk
IPoint16)); | |
| 737 textureCoords->set(u1, v0); | |
| 738 | |
| 739 fCurrVertex += 4; | |
| 740 | |
| 741 return true; | |
| 742 } | |
| 743 | |
| 744 void GrDistanceFieldTextContext::flush() { | |
| 745 if (NULL == fDrawTarget) { | |
| 746 return; | |
| 747 } | |
| 748 | |
| 749 if (fCurrVertex > 0) { | |
| 750 GrPipelineBuilder pipelineBuilder; | |
| 751 pipelineBuilder.setFromPaint(fPaint, fRenderTarget, fClip); | |
| 752 | |
| 753 // setup our sampler state for our text texture/atlas | |
| 754 SkASSERT(SkIsAlign4(fCurrVertex)); | |
| 755 | |
| 756 // get our current color | |
| 757 SkColor filteredColor; | |
| 758 SkColorFilter* colorFilter = fSkPaint.getColorFilter(); | |
| 759 if (colorFilter) { | |
| 760 filteredColor = colorFilter->filterColor(fSkPaint.getColor()); | |
| 761 } else { | |
| 762 filteredColor = fSkPaint.getColor(); | |
| 763 } | |
| 764 this->setupCoverageEffect(filteredColor); | |
| 765 | |
| 766 // Set draw state | |
| 767 if (fUseLCDText) { | |
| 768 // TODO: move supportsRGBCoverage check to setupCoverageEffect and o
nly add LCD | |
| 769 // processor if the xp can support it. For now we will simply assume
that if | |
| 770 // fUseLCDText is true, then we have a known color output. | |
| 771 const GrXPFactory* xpFactory = pipelineBuilder.getXPFactory(); | |
| 772 if (!xpFactory->supportsRGBCoverage(0, kRGBA_GrColorComponentFlags))
{ | |
| 773 SkDebugf("LCD Text will not draw correctly.\n"); | |
| 774 } | |
| 775 SkASSERT(!fCachedGeometryProcessor->hasVertexColor()); | |
| 776 } else { | |
| 777 // We're using per-vertex color. | |
| 778 SkASSERT(fCachedGeometryProcessor->hasVertexColor()); | |
| 779 } | |
| 780 int nGlyphs = fCurrVertex / kVerticesPerGlyph; | |
| 781 fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer()); | |
| 782 fDrawTarget->drawIndexedInstances(&pipelineBuilder, | |
| 783 fCachedGeometryProcessor.get(), | |
| 784 kTriangles_GrPrimitiveType, | |
| 785 nGlyphs, | |
| 786 kVerticesPerGlyph, | |
| 787 kIndicesPerGlyph, | |
| 788 &fVertexBounds); | |
| 789 fDrawTarget->resetVertexSource(); | |
| 790 fVertices = NULL; | |
| 791 fTotalVertexCount -= fCurrVertex; | |
| 792 fCurrVertex = 0; | |
| 793 SkSafeSetNull(fCurrTexture); | |
| 794 fVertexBounds.setLargestInverted(); | |
| 795 } | |
| 796 } | |
| 797 | |
| 798 inline void GrDistanceFieldTextContext::finish() { | |
| 799 this->flush(); | |
| 800 fTotalVertexCount = 0; | |
| 801 | |
| 802 GrTextContext::finish(); | |
| 803 } | |
| 804 | |
| OLD | NEW |