blob: dee6d133e3806f107272af58751fd7ed1cd05410 [file] [log] [blame]
joshualitt259fbf12015-07-21 11:39:34 -07001/*
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
8#include "GrAtlasTextBlob.h"
9
joshualitt2e2202e2015-12-10 11:22:08 -080010#include "GrBlurUtils.h"
11#include "GrContext.h"
12#include "GrDrawContext.h"
joshualitt0a42e682015-12-10 13:20:58 -080013#include "GrTextUtils.h"
joshualitt2e2202e2015-12-10 11:22:08 -080014#include "SkColorFilter.h"
15#include "SkDrawFilter.h"
16#include "SkTextBlobRunIterator.h"
17#include "batches/GrAtlasTextBatch.h"
18
joshualittf528e0d2015-12-09 06:42:52 -080019void GrAtlasTextBlob::appendGlyph(int runIndex,
20 const SkRect& positions,
21 GrColor color,
22 GrBatchTextStrike* strike,
joshualitta06e6ab2015-12-10 08:54:41 -080023 GrGlyph* glyph,
24 GrFontScaler* scaler, const SkGlyph& skGlyph,
25 SkScalar x, SkScalar y, SkScalar scale, bool applyVM) {
26
27 // If the glyph is too large we fall back to paths
28 if (glyph->fTooLargeForAtlas) {
29 this->appendLargeGlyph(glyph, scaler, skGlyph, x, y, scale, applyVM);
30 return;
31 }
32
joshualittf528e0d2015-12-09 06:42:52 -080033 Run& run = fRuns[runIndex];
34 GrMaskFormat format = glyph->fMaskFormat;
35
36 Run::SubRunInfo* subRun = &run.fSubRunInfo.back();
37 if (run.fInitialized && subRun->maskFormat() != format) {
38 subRun = &run.push_back();
39 subRun->setStrike(strike);
40 } else if (!run.fInitialized) {
41 subRun->setStrike(strike);
42 }
43
44 run.fInitialized = true;
45
46 size_t vertexStride = GetVertexStride(format);
47
48 subRun->setMaskFormat(format);
49
50 run.fVertexBounds.joinNonEmptyArg(positions);
joshualittf9e658b2015-12-09 09:26:44 -080051 subRun->setColor(color);
joshualitt18b072d2015-12-07 12:26:12 -080052
53 intptr_t vertex = reinterpret_cast<intptr_t>(this->fVertices + subRun->vertexEndIndex());
54
joshualittf528e0d2015-12-09 06:42:52 -080055 if (kARGB_GrMaskFormat != glyph->fMaskFormat) {
joshualitt18b072d2015-12-07 12:26:12 -080056 // V0
57 SkPoint* position = reinterpret_cast<SkPoint*>(vertex);
58 position->set(positions.fLeft, positions.fTop);
59 SkColor* colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
60 *colorPtr = color;
61 vertex += vertexStride;
62
63 // V1
64 position = reinterpret_cast<SkPoint*>(vertex);
65 position->set(positions.fLeft, positions.fBottom);
66 colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
67 *colorPtr = color;
68 vertex += vertexStride;
69
70 // V2
71 position = reinterpret_cast<SkPoint*>(vertex);
72 position->set(positions.fRight, positions.fBottom);
73 colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
74 *colorPtr = color;
75 vertex += vertexStride;
76
77 // V3
78 position = reinterpret_cast<SkPoint*>(vertex);
79 position->set(positions.fRight, positions.fTop);
80 colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
81 *colorPtr = color;
82 } else {
83 // V0
84 SkPoint* position = reinterpret_cast<SkPoint*>(vertex);
85 position->set(positions.fLeft, positions.fTop);
86 vertex += vertexStride;
87
88 // V1
89 position = reinterpret_cast<SkPoint*>(vertex);
90 position->set(positions.fLeft, positions.fBottom);
91 vertex += vertexStride;
92
93 // V2
94 position = reinterpret_cast<SkPoint*>(vertex);
95 position->set(positions.fRight, positions.fBottom);
96 vertex += vertexStride;
97
98 // V3
99 position = reinterpret_cast<SkPoint*>(vertex);
100 position->set(positions.fRight, positions.fTop);
101 }
102 subRun->appendVertices(vertexStride);
103 fGlyphs[subRun->glyphEndIndex()] = glyph;
104 subRun->glyphAppended();
105}
106
joshualitta06e6ab2015-12-10 08:54:41 -0800107void GrAtlasTextBlob::appendLargeGlyph(GrGlyph* glyph, GrFontScaler* scaler, const SkGlyph& skGlyph,
108 SkScalar x, SkScalar y, SkScalar scale, bool applyVM) {
109 if (nullptr == glyph->fPath) {
110 const SkPath* glyphPath = scaler->getGlyphPath(skGlyph);
111 if (!glyphPath) {
112 return;
113 }
114
115 glyph->fPath = new SkPath(*glyphPath);
116 }
117 fBigGlyphs.push_back(GrAtlasTextBlob::BigGlyph(*glyph->fPath, x, y, scale, applyVM));
118}
119
joshualittfd5f6c12015-12-10 07:44:50 -0800120bool GrAtlasTextBlob::mustRegenerate(SkScalar* outTransX, SkScalar* outTransY,
121 const SkPaint& paint,
122 GrColor color, const SkMaskFilter::BlurRec& blurRec,
123 const SkMatrix& viewMatrix, SkScalar x, SkScalar y) {
124 // If we have LCD text then our canonical color will be set to transparent, in this case we have
125 // to regenerate the blob on any color change
126 // We use the grPaint to get any color filter effects
127 if (fKey.fCanonicalColor == SK_ColorTRANSPARENT &&
128 fPaintColor != color) {
129 return true;
130 }
131
132 if (fViewMatrix.hasPerspective() != viewMatrix.hasPerspective()) {
133 return true;
134 }
135
136 if (fViewMatrix.hasPerspective() && !fViewMatrix.cheapEqualTo(viewMatrix)) {
137 return true;
138 }
139
140 // We only cache one masked version
141 if (fKey.fHasBlur &&
142 (fBlurRec.fSigma != blurRec.fSigma ||
143 fBlurRec.fStyle != blurRec.fStyle ||
144 fBlurRec.fQuality != blurRec.fQuality)) {
145 return true;
146 }
147
148 // Similarly, we only cache one version for each style
149 if (fKey.fStyle != SkPaint::kFill_Style &&
150 (fStrokeInfo.fFrameWidth != paint.getStrokeWidth() ||
151 fStrokeInfo.fMiterLimit != paint.getStrokeMiter() ||
152 fStrokeInfo.fJoin != paint.getStrokeJoin())) {
153 return true;
154 }
155
156 // Mixed blobs must be regenerated. We could probably figure out a way to do integer scrolls
157 // for mixed blobs if this becomes an issue.
158 if (this->hasBitmap() && this->hasDistanceField()) {
159 // Identical viewmatrices and we can reuse in all cases
160 if (fViewMatrix.cheapEqualTo(viewMatrix) && x == fX && y == fY) {
161 return false;
162 }
163 return true;
164 }
165
166 if (this->hasBitmap()) {
167 if (fViewMatrix.getScaleX() != viewMatrix.getScaleX() ||
168 fViewMatrix.getScaleY() != viewMatrix.getScaleY() ||
169 fViewMatrix.getSkewX() != viewMatrix.getSkewX() ||
170 fViewMatrix.getSkewY() != viewMatrix.getSkewY()) {
171 return true;
172 }
173
174 // We can update the positions in the cachedtextblobs without regenerating the whole blob,
175 // but only for integer translations.
176 // This cool bit of math will determine the necessary translation to apply to the already
177 // generated vertex coordinates to move them to the correct position
178 SkScalar transX = viewMatrix.getTranslateX() +
179 viewMatrix.getScaleX() * (x - fX) +
180 viewMatrix.getSkewX() * (y - fY) -
181 fViewMatrix.getTranslateX();
182 SkScalar transY = viewMatrix.getTranslateY() +
183 viewMatrix.getSkewY() * (x - fX) +
184 viewMatrix.getScaleY() * (y - fY) -
185 fViewMatrix.getTranslateY();
186 if (!SkScalarIsInt(transX) || !SkScalarIsInt(transY) ) {
187 return true;
188 }
189
190 (*outTransX) = transX;
191 (*outTransY) = transY;
192 } else if (this->hasDistanceField()) {
193 // A scale outside of [blob.fMaxMinScale, blob.fMinMaxScale] would result in a different
194 // distance field being generated, so we have to regenerate in those cases
195 SkScalar newMaxScale = viewMatrix.getMaxScale();
196 SkScalar oldMaxScale = fViewMatrix.getMaxScale();
197 SkScalar scaleAdjust = newMaxScale / oldMaxScale;
198 if (scaleAdjust < fMaxMinScale || scaleAdjust > fMinMaxScale) {
199 return true;
200 }
201
202 (*outTransX) = x - fX;
203 (*outTransY) = y - fY;
204 }
205
206
207 // If we can reuse the blob, then make sure we update the blob's viewmatrix, and x/y
208 // offsets. Note, we offset the vertex bounds right before flushing
209 fViewMatrix = viewMatrix;
210 fX = x;
211 fY = y;
212
213 // It is possible that a blob has neither distanceField nor bitmaptext. This is in the case
214 // when all of the runs inside the blob are drawn as paths. In this case, we always regenerate
215 // the blob anyways at flush time, so no need to regenerate explicitly
216 return false;
217}
218
joshualitt2e2202e2015-12-10 11:22:08 -0800219GrDrawBatch* GrAtlasTextBlob::createBatch(const Run::SubRunInfo& info,
220 int glyphCount, int run, int subRun,
221 GrColor color, SkScalar transX, SkScalar transY,
222 const SkPaint& skPaint, const SkSurfaceProps& props,
223 const GrDistanceFieldAdjustTable* distanceAdjustTable,
224 GrBatchFontCache* cache) {
225 GrMaskFormat format = info.maskFormat();
226 GrColor subRunColor;
227 if (kARGB_GrMaskFormat == format) {
228 uint8_t paintAlpha = skPaint.getAlpha();
229 subRunColor = SkColorSetARGB(paintAlpha, paintAlpha, paintAlpha, paintAlpha);
230 } else {
231 subRunColor = color;
232 }
233
234 GrAtlasTextBatch* batch;
235 if (info.drawAsDistanceFields()) {
236 SkColor filteredColor;
237 SkColorFilter* colorFilter = skPaint.getColorFilter();
238 if (colorFilter) {
239 filteredColor = colorFilter->filterColor(skPaint.getColor());
240 } else {
241 filteredColor = skPaint.getColor();
242 }
243 bool useBGR = SkPixelGeometryIsBGR(props.pixelGeometry());
244 batch = GrAtlasTextBatch::CreateDistanceField(glyphCount, cache,
245 distanceAdjustTable, filteredColor,
246 info.hasUseLCDText(), useBGR);
247 } else {
248 batch = GrAtlasTextBatch::CreateBitmap(format, glyphCount, cache);
249 }
250 GrAtlasTextBatch::Geometry& geometry = batch->geometry();
251 geometry.fBlob = SkRef(this);
252 geometry.fRun = run;
253 geometry.fSubRun = subRun;
254 geometry.fColor = subRunColor;
255 geometry.fTransX = transX;
256 geometry.fTransY = transY;
257 batch->init();
258
259 return batch;
260}
261
262inline
263void GrAtlasTextBlob::flushRun(GrDrawContext* dc, GrPipelineBuilder* pipelineBuilder,
264 int run, GrColor color,
265 SkScalar transX, SkScalar transY,
266 const SkPaint& skPaint, const SkSurfaceProps& props,
267 const GrDistanceFieldAdjustTable* distanceAdjustTable,
268 GrBatchFontCache* cache) {
269 for (int subRun = 0; subRun < fRuns[run].fSubRunInfo.count(); subRun++) {
270 const Run::SubRunInfo& info = fRuns[run].fSubRunInfo[subRun];
271 int glyphCount = info.glyphCount();
272 if (0 == glyphCount) {
273 continue;
274 }
275
276 SkAutoTUnref<GrDrawBatch> batch(this->createBatch(info, glyphCount, run,
277 subRun, color, transX, transY,
278 skPaint, props,
279 distanceAdjustTable, cache));
280 dc->drawBatch(pipelineBuilder, batch);
281 }
282}
283
284void GrAtlasTextBlob::flushBigGlyphs(GrContext* context, GrDrawContext* dc,
285 const GrClip& clip, const SkPaint& skPaint,
286 SkScalar transX, SkScalar transY,
287 const SkIRect& clipBounds) {
288 for (int i = 0; i < fBigGlyphs.count(); i++) {
289 GrAtlasTextBlob::BigGlyph& bigGlyph = fBigGlyphs[i];
290 bigGlyph.fVx += transX;
291 bigGlyph.fVy += transY;
292 SkMatrix ctm;
293 ctm.setScale(bigGlyph.fScale, bigGlyph.fScale);
294 ctm.postTranslate(bigGlyph.fVx, bigGlyph.fVy);
295 if (bigGlyph.fApplyVM) {
296 ctm.postConcat(fViewMatrix);
297 }
298
299 GrBlurUtils::drawPathWithMaskFilter(context, dc, clip, bigGlyph.fPath,
300 skPaint, ctm, nullptr, clipBounds, false);
301 }
302}
303
joshualitt0a42e682015-12-10 13:20:58 -0800304void GrAtlasTextBlob::flushRunAsPaths(GrContext* context, GrDrawContext* dc,
joshualitt2e2202e2015-12-10 11:22:08 -0800305 const SkSurfaceProps& props,
306 const SkTextBlobRunIterator& it,
307 const GrClip& clip, const SkPaint& skPaint,
308 SkDrawFilter* drawFilter, const SkMatrix& viewMatrix,
309 const SkIRect& clipBounds, SkScalar x, SkScalar y) {
310 SkPaint runPaint = skPaint;
311
312 size_t textLen = it.glyphCount() * sizeof(uint16_t);
313 const SkPoint& offset = it.offset();
314
315 it.applyFontToPaint(&runPaint);
316
317 if (drawFilter && !drawFilter->filter(&runPaint, SkDrawFilter::kText_Type)) {
318 return;
319 }
320
321 runPaint.setFlags(GrTextContext::FilterTextFlags(props, runPaint));
322
323 switch (it.positioning()) {
324 case SkTextBlob::kDefault_Positioning:
joshualitt0a42e682015-12-10 13:20:58 -0800325 GrTextUtils::DrawTextAsPath(context, dc, clip, runPaint, viewMatrix,
joshualitt2e2202e2015-12-10 11:22:08 -0800326 (const char *)it.glyphs(),
327 textLen, x + offset.x(), y + offset.y(), clipBounds);
328 break;
329 case SkTextBlob::kHorizontal_Positioning:
joshualitt0a42e682015-12-10 13:20:58 -0800330 GrTextUtils::DrawPosTextAsPath(context, dc, props, clip, runPaint, viewMatrix,
joshualitt2e2202e2015-12-10 11:22:08 -0800331 (const char*)it.glyphs(),
332 textLen, it.pos(), 1, SkPoint::Make(x, y + offset.y()),
333 clipBounds);
334 break;
335 case SkTextBlob::kFull_Positioning:
joshualitt0a42e682015-12-10 13:20:58 -0800336 GrTextUtils::DrawPosTextAsPath(context, dc, props, clip, runPaint, viewMatrix,
joshualitt2e2202e2015-12-10 11:22:08 -0800337 (const char*)it.glyphs(),
338 textLen, it.pos(), 2, SkPoint::Make(x, y), clipBounds);
339 break;
340 }
341}
342
joshualitt0a42e682015-12-10 13:20:58 -0800343void GrAtlasTextBlob::flushCached(GrContext* context,
joshualitt2e2202e2015-12-10 11:22:08 -0800344 GrDrawContext* dc,
joshualitt0a42e682015-12-10 13:20:58 -0800345 const SkTextBlob* blob,
joshualitt2e2202e2015-12-10 11:22:08 -0800346 const SkSurfaceProps& props,
347 const GrDistanceFieldAdjustTable* distanceAdjustTable,
348 const SkPaint& skPaint,
349 const GrPaint& grPaint,
350 SkDrawFilter* drawFilter,
351 const GrClip& clip,
352 const SkMatrix& viewMatrix,
353 const SkIRect& clipBounds,
354 SkScalar x, SkScalar y,
355 SkScalar transX, SkScalar transY) {
356 // We loop through the runs of the blob, flushing each. If any run is too large, then we flush
357 // it as paths
358 GrPipelineBuilder pipelineBuilder(grPaint, dc->accessRenderTarget(), clip);
359
360 GrColor color = grPaint.getColor();
361
362 SkTextBlobRunIterator it(blob);
363 for (int run = 0; !it.done(); it.next(), run++) {
364 if (fRuns[run].fDrawAsPaths) {
joshualitt0a42e682015-12-10 13:20:58 -0800365 this->flushRunAsPaths(context, dc, props, it, clip, skPaint,
joshualitt2e2202e2015-12-10 11:22:08 -0800366 drawFilter, viewMatrix, clipBounds, x, y);
367 continue;
368 }
369 fRuns[run].fVertexBounds.offset(transX, transY);
370 this->flushRun(dc, &pipelineBuilder, run, color,
371 transX, transY, skPaint, props,
372 distanceAdjustTable, context->getBatchFontCache());
373 }
374
375 // Now flush big glyphs
376 this->flushBigGlyphs(context, dc, clip, skPaint, transX, transY, clipBounds);
377}
378
379void GrAtlasTextBlob::flushThrowaway(GrContext* context,
380 GrDrawContext* dc,
381 const SkSurfaceProps& props,
382 const GrDistanceFieldAdjustTable* distanceAdjustTable,
383 const SkPaint& skPaint,
384 const GrPaint& grPaint,
385 const GrClip& clip,
386 const SkIRect& clipBounds) {
387 GrPipelineBuilder pipelineBuilder(grPaint, dc->accessRenderTarget(), clip);
388
389 GrColor color = grPaint.getColor();
390 for (int run = 0; run < fRunCount; run++) {
391 this->flushRun(dc, &pipelineBuilder, run, color, 0, 0, skPaint, props,
392 distanceAdjustTable, context->getBatchFontCache());
393 }
394
395 // Now flush big glyphs
396 this->flushBigGlyphs(context, dc, clip, skPaint, 0, 0, clipBounds);
397}
398
399
joshualitt18b072d2015-12-07 12:26:12 -0800400// TODO get this code building again
joshualitt259fbf12015-07-21 11:39:34 -0700401#ifdef CACHE_SANITY_CHECK
402void GrAtlasTextBlob::AssertEqual(const GrAtlasTextBlob& l, const GrAtlasTextBlob& r) {
403 SkASSERT(l.fSize == r.fSize);
404 SkASSERT(l.fPool == r.fPool);
405
406 SkASSERT(l.fBlurRec.fSigma == r.fBlurRec.fSigma);
407 SkASSERT(l.fBlurRec.fStyle == r.fBlurRec.fStyle);
408 SkASSERT(l.fBlurRec.fQuality == r.fBlurRec.fQuality);
409
410 SkASSERT(l.fStrokeInfo.fFrameWidth == r.fStrokeInfo.fFrameWidth);
411 SkASSERT(l.fStrokeInfo.fMiterLimit == r.fStrokeInfo.fMiterLimit);
412 SkASSERT(l.fStrokeInfo.fJoin == r.fStrokeInfo.fJoin);
413
414 SkASSERT(l.fBigGlyphs.count() == r.fBigGlyphs.count());
415 for (int i = 0; i < l.fBigGlyphs.count(); i++) {
416 const BigGlyph& lBigGlyph = l.fBigGlyphs[i];
417 const BigGlyph& rBigGlyph = r.fBigGlyphs[i];
418
419 SkASSERT(lBigGlyph.fPath == rBigGlyph.fPath);
420 // We can't assert that these have the same translations
421 }
422
423 SkASSERT(l.fKey == r.fKey);
424 SkASSERT(l.fViewMatrix.cheapEqualTo(r.fViewMatrix));
425 SkASSERT(l.fPaintColor == r.fPaintColor);
426 SkASSERT(l.fMaxMinScale == r.fMaxMinScale);
427 SkASSERT(l.fMinMaxScale == r.fMinMaxScale);
428 SkASSERT(l.fTextType == r.fTextType);
429
430 SkASSERT(l.fRunCount == r.fRunCount);
431 for (int i = 0; i < l.fRunCount; i++) {
432 const Run& lRun = l.fRuns[i];
433 const Run& rRun = r.fRuns[i];
434
435 if (lRun.fStrike.get()) {
436 SkASSERT(rRun.fStrike.get());
437 SkASSERT(GrBatchTextStrike::GetKey(*lRun.fStrike) ==
438 GrBatchTextStrike::GetKey(*rRun.fStrike));
439
440 } else {
441 SkASSERT(!rRun.fStrike.get());
442 }
443
444 if (lRun.fTypeface.get()) {
445 SkASSERT(rRun.fTypeface.get());
446 SkASSERT(SkTypeface::Equal(lRun.fTypeface, rRun.fTypeface));
447 } else {
448 SkASSERT(!rRun.fTypeface.get());
449 }
450
joshualitt7e7b5c52015-07-21 12:56:56 -0700451 // We offset bounds right before flush time so they will not be correct here
joshualitt259fbf12015-07-21 11:39:34 -0700452 //SkASSERT(lRun.fVertexBounds == rRun.fVertexBounds);
453
454 SkASSERT(lRun.fDescriptor.getDesc());
455 SkASSERT(rRun.fDescriptor.getDesc());
456 SkASSERT(lRun.fDescriptor.getDesc()->equals(*rRun.fDescriptor.getDesc()));
457
458 if (lRun.fOverrideDescriptor.get()) {
459 SkASSERT(lRun.fOverrideDescriptor->getDesc());
460 SkASSERT(rRun.fOverrideDescriptor.get() && rRun.fOverrideDescriptor->getDesc());;
461 SkASSERT(lRun.fOverrideDescriptor->getDesc()->equals(
462 *rRun.fOverrideDescriptor->getDesc()));
463 } else {
464 SkASSERT(!rRun.fOverrideDescriptor.get());
465 }
466
467 // color can be changed
468 //SkASSERT(lRun.fColor == rRun.fColor);
469 SkASSERT(lRun.fInitialized == rRun.fInitialized);
470 SkASSERT(lRun.fDrawAsPaths == rRun.fDrawAsPaths);
471
472 SkASSERT(lRun.fSubRunInfo.count() == rRun.fSubRunInfo.count());
473 for(int j = 0; j < lRun.fSubRunInfo.count(); j++) {
474 const Run::SubRunInfo& lSubRun = lRun.fSubRunInfo[j];
475 const Run::SubRunInfo& rSubRun = rRun.fSubRunInfo[j];
476
477 SkASSERT(lSubRun.fVertexStartIndex == rSubRun.fVertexStartIndex);
478 SkASSERT(lSubRun.fVertexEndIndex == rSubRun.fVertexEndIndex);
479 SkASSERT(lSubRun.fGlyphStartIndex == rSubRun.fGlyphStartIndex);
480 SkASSERT(lSubRun.fGlyphEndIndex == rSubRun.fGlyphEndIndex);
481 SkASSERT(lSubRun.fTextRatio == rSubRun.fTextRatio);
482 SkASSERT(lSubRun.fMaskFormat == rSubRun.fMaskFormat);
483 SkASSERT(lSubRun.fDrawAsDistanceFields == rSubRun.fDrawAsDistanceFields);
484 SkASSERT(lSubRun.fUseLCDText == rSubRun.fUseLCDText);
485
486 //We can't compare the bulk use tokens with this method
487 /*
488 SkASSERT(lSubRun.fBulkUseToken.fPlotsToUpdate.count() ==
489 rSubRun.fBulkUseToken.fPlotsToUpdate.count());
490 SkASSERT(lSubRun.fBulkUseToken.fPlotAlreadyUpdated ==
491 rSubRun.fBulkUseToken.fPlotAlreadyUpdated);
492 for (int k = 0; k < lSubRun.fBulkUseToken.fPlotsToUpdate.count(); k++) {
493 SkASSERT(lSubRun.fBulkUseToken.fPlotsToUpdate[k] ==
494 rSubRun.fBulkUseToken.fPlotsToUpdate[k]);
495 }*/
496 }
497 }
498}
499
500#endif