Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(45)

Side by Side Diff: src/gpu/GrAtlasTextContext.cpp

Issue 1521453002: Move all text stuff to its own folder (Closed) Base URL: https://skia.googlesource.com/skia.git@cleanuptext11textutils2
Patch Set: Created 5 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « src/gpu/GrAtlasTextContext.h ('k') | src/gpu/GrBatchFontCache.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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
OLDNEW
« no previous file with comments | « src/gpu/GrAtlasTextContext.h ('k') | src/gpu/GrBatchFontCache.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698