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 |