blob: 1cd25e74de33e1c8d2904aca466c933f55950204 [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
joshualittf528e0d2015-12-09 06:42:52 -080010void GrAtlasTextBlob::appendGlyph(int runIndex,
11 const SkRect& positions,
12 GrColor color,
13 GrBatchTextStrike* strike,
joshualitt18b072d2015-12-07 12:26:12 -080014 GrGlyph* glyph) {
joshualittf528e0d2015-12-09 06:42:52 -080015 Run& run = fRuns[runIndex];
16 GrMaskFormat format = glyph->fMaskFormat;
17
18 Run::SubRunInfo* subRun = &run.fSubRunInfo.back();
19 if (run.fInitialized && subRun->maskFormat() != format) {
20 subRun = &run.push_back();
21 subRun->setStrike(strike);
22 } else if (!run.fInitialized) {
23 subRun->setStrike(strike);
24 }
25
26 run.fInitialized = true;
27
28 size_t vertexStride = GetVertexStride(format);
29
30 subRun->setMaskFormat(format);
31
32 run.fVertexBounds.joinNonEmptyArg(positions);
joshualittf9e658b2015-12-09 09:26:44 -080033 subRun->setColor(color);
joshualitt18b072d2015-12-07 12:26:12 -080034
35 intptr_t vertex = reinterpret_cast<intptr_t>(this->fVertices + subRun->vertexEndIndex());
36
joshualittf528e0d2015-12-09 06:42:52 -080037 if (kARGB_GrMaskFormat != glyph->fMaskFormat) {
joshualitt18b072d2015-12-07 12:26:12 -080038 // V0
39 SkPoint* position = reinterpret_cast<SkPoint*>(vertex);
40 position->set(positions.fLeft, positions.fTop);
41 SkColor* colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
42 *colorPtr = color;
43 vertex += vertexStride;
44
45 // V1
46 position = reinterpret_cast<SkPoint*>(vertex);
47 position->set(positions.fLeft, positions.fBottom);
48 colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
49 *colorPtr = color;
50 vertex += vertexStride;
51
52 // V2
53 position = reinterpret_cast<SkPoint*>(vertex);
54 position->set(positions.fRight, positions.fBottom);
55 colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
56 *colorPtr = color;
57 vertex += vertexStride;
58
59 // V3
60 position = reinterpret_cast<SkPoint*>(vertex);
61 position->set(positions.fRight, positions.fTop);
62 colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
63 *colorPtr = color;
64 } else {
65 // V0
66 SkPoint* position = reinterpret_cast<SkPoint*>(vertex);
67 position->set(positions.fLeft, positions.fTop);
68 vertex += vertexStride;
69
70 // V1
71 position = reinterpret_cast<SkPoint*>(vertex);
72 position->set(positions.fLeft, positions.fBottom);
73 vertex += vertexStride;
74
75 // V2
76 position = reinterpret_cast<SkPoint*>(vertex);
77 position->set(positions.fRight, positions.fBottom);
78 vertex += vertexStride;
79
80 // V3
81 position = reinterpret_cast<SkPoint*>(vertex);
82 position->set(positions.fRight, positions.fTop);
83 }
84 subRun->appendVertices(vertexStride);
85 fGlyphs[subRun->glyphEndIndex()] = glyph;
86 subRun->glyphAppended();
87}
88
joshualittfd5f6c12015-12-10 07:44:50 -080089bool GrAtlasTextBlob::mustRegenerate(SkScalar* outTransX, SkScalar* outTransY,
90 const SkPaint& paint,
91 GrColor color, const SkMaskFilter::BlurRec& blurRec,
92 const SkMatrix& viewMatrix, SkScalar x, SkScalar y) {
93 // If we have LCD text then our canonical color will be set to transparent, in this case we have
94 // to regenerate the blob on any color change
95 // We use the grPaint to get any color filter effects
96 if (fKey.fCanonicalColor == SK_ColorTRANSPARENT &&
97 fPaintColor != color) {
98 return true;
99 }
100
101 if (fViewMatrix.hasPerspective() != viewMatrix.hasPerspective()) {
102 return true;
103 }
104
105 if (fViewMatrix.hasPerspective() && !fViewMatrix.cheapEqualTo(viewMatrix)) {
106 return true;
107 }
108
109 // We only cache one masked version
110 if (fKey.fHasBlur &&
111 (fBlurRec.fSigma != blurRec.fSigma ||
112 fBlurRec.fStyle != blurRec.fStyle ||
113 fBlurRec.fQuality != blurRec.fQuality)) {
114 return true;
115 }
116
117 // Similarly, we only cache one version for each style
118 if (fKey.fStyle != SkPaint::kFill_Style &&
119 (fStrokeInfo.fFrameWidth != paint.getStrokeWidth() ||
120 fStrokeInfo.fMiterLimit != paint.getStrokeMiter() ||
121 fStrokeInfo.fJoin != paint.getStrokeJoin())) {
122 return true;
123 }
124
125 // Mixed blobs must be regenerated. We could probably figure out a way to do integer scrolls
126 // for mixed blobs if this becomes an issue.
127 if (this->hasBitmap() && this->hasDistanceField()) {
128 // Identical viewmatrices and we can reuse in all cases
129 if (fViewMatrix.cheapEqualTo(viewMatrix) && x == fX && y == fY) {
130 return false;
131 }
132 return true;
133 }
134
135 if (this->hasBitmap()) {
136 if (fViewMatrix.getScaleX() != viewMatrix.getScaleX() ||
137 fViewMatrix.getScaleY() != viewMatrix.getScaleY() ||
138 fViewMatrix.getSkewX() != viewMatrix.getSkewX() ||
139 fViewMatrix.getSkewY() != viewMatrix.getSkewY()) {
140 return true;
141 }
142
143 // We can update the positions in the cachedtextblobs without regenerating the whole blob,
144 // but only for integer translations.
145 // This cool bit of math will determine the necessary translation to apply to the already
146 // generated vertex coordinates to move them to the correct position
147 SkScalar transX = viewMatrix.getTranslateX() +
148 viewMatrix.getScaleX() * (x - fX) +
149 viewMatrix.getSkewX() * (y - fY) -
150 fViewMatrix.getTranslateX();
151 SkScalar transY = viewMatrix.getTranslateY() +
152 viewMatrix.getSkewY() * (x - fX) +
153 viewMatrix.getScaleY() * (y - fY) -
154 fViewMatrix.getTranslateY();
155 if (!SkScalarIsInt(transX) || !SkScalarIsInt(transY) ) {
156 return true;
157 }
158
159 (*outTransX) = transX;
160 (*outTransY) = transY;
161 } else if (this->hasDistanceField()) {
162 // A scale outside of [blob.fMaxMinScale, blob.fMinMaxScale] would result in a different
163 // distance field being generated, so we have to regenerate in those cases
164 SkScalar newMaxScale = viewMatrix.getMaxScale();
165 SkScalar oldMaxScale = fViewMatrix.getMaxScale();
166 SkScalar scaleAdjust = newMaxScale / oldMaxScale;
167 if (scaleAdjust < fMaxMinScale || scaleAdjust > fMinMaxScale) {
168 return true;
169 }
170
171 (*outTransX) = x - fX;
172 (*outTransY) = y - fY;
173 }
174
175
176 // If we can reuse the blob, then make sure we update the blob's viewmatrix, and x/y
177 // offsets. Note, we offset the vertex bounds right before flushing
178 fViewMatrix = viewMatrix;
179 fX = x;
180 fY = y;
181
182 // It is possible that a blob has neither distanceField nor bitmaptext. This is in the case
183 // when all of the runs inside the blob are drawn as paths. In this case, we always regenerate
184 // the blob anyways at flush time, so no need to regenerate explicitly
185 return false;
186}
187
joshualitt18b072d2015-12-07 12:26:12 -0800188// TODO get this code building again
joshualitt259fbf12015-07-21 11:39:34 -0700189#ifdef CACHE_SANITY_CHECK
190void GrAtlasTextBlob::AssertEqual(const GrAtlasTextBlob& l, const GrAtlasTextBlob& r) {
191 SkASSERT(l.fSize == r.fSize);
192 SkASSERT(l.fPool == r.fPool);
193
194 SkASSERT(l.fBlurRec.fSigma == r.fBlurRec.fSigma);
195 SkASSERT(l.fBlurRec.fStyle == r.fBlurRec.fStyle);
196 SkASSERT(l.fBlurRec.fQuality == r.fBlurRec.fQuality);
197
198 SkASSERT(l.fStrokeInfo.fFrameWidth == r.fStrokeInfo.fFrameWidth);
199 SkASSERT(l.fStrokeInfo.fMiterLimit == r.fStrokeInfo.fMiterLimit);
200 SkASSERT(l.fStrokeInfo.fJoin == r.fStrokeInfo.fJoin);
201
202 SkASSERT(l.fBigGlyphs.count() == r.fBigGlyphs.count());
203 for (int i = 0; i < l.fBigGlyphs.count(); i++) {
204 const BigGlyph& lBigGlyph = l.fBigGlyphs[i];
205 const BigGlyph& rBigGlyph = r.fBigGlyphs[i];
206
207 SkASSERT(lBigGlyph.fPath == rBigGlyph.fPath);
208 // We can't assert that these have the same translations
209 }
210
211 SkASSERT(l.fKey == r.fKey);
212 SkASSERT(l.fViewMatrix.cheapEqualTo(r.fViewMatrix));
213 SkASSERT(l.fPaintColor == r.fPaintColor);
214 SkASSERT(l.fMaxMinScale == r.fMaxMinScale);
215 SkASSERT(l.fMinMaxScale == r.fMinMaxScale);
216 SkASSERT(l.fTextType == r.fTextType);
217
218 SkASSERT(l.fRunCount == r.fRunCount);
219 for (int i = 0; i < l.fRunCount; i++) {
220 const Run& lRun = l.fRuns[i];
221 const Run& rRun = r.fRuns[i];
222
223 if (lRun.fStrike.get()) {
224 SkASSERT(rRun.fStrike.get());
225 SkASSERT(GrBatchTextStrike::GetKey(*lRun.fStrike) ==
226 GrBatchTextStrike::GetKey(*rRun.fStrike));
227
228 } else {
229 SkASSERT(!rRun.fStrike.get());
230 }
231
232 if (lRun.fTypeface.get()) {
233 SkASSERT(rRun.fTypeface.get());
234 SkASSERT(SkTypeface::Equal(lRun.fTypeface, rRun.fTypeface));
235 } else {
236 SkASSERT(!rRun.fTypeface.get());
237 }
238
joshualitt7e7b5c52015-07-21 12:56:56 -0700239 // We offset bounds right before flush time so they will not be correct here
joshualitt259fbf12015-07-21 11:39:34 -0700240 //SkASSERT(lRun.fVertexBounds == rRun.fVertexBounds);
241
242 SkASSERT(lRun.fDescriptor.getDesc());
243 SkASSERT(rRun.fDescriptor.getDesc());
244 SkASSERT(lRun.fDescriptor.getDesc()->equals(*rRun.fDescriptor.getDesc()));
245
246 if (lRun.fOverrideDescriptor.get()) {
247 SkASSERT(lRun.fOverrideDescriptor->getDesc());
248 SkASSERT(rRun.fOverrideDescriptor.get() && rRun.fOverrideDescriptor->getDesc());;
249 SkASSERT(lRun.fOverrideDescriptor->getDesc()->equals(
250 *rRun.fOverrideDescriptor->getDesc()));
251 } else {
252 SkASSERT(!rRun.fOverrideDescriptor.get());
253 }
254
255 // color can be changed
256 //SkASSERT(lRun.fColor == rRun.fColor);
257 SkASSERT(lRun.fInitialized == rRun.fInitialized);
258 SkASSERT(lRun.fDrawAsPaths == rRun.fDrawAsPaths);
259
260 SkASSERT(lRun.fSubRunInfo.count() == rRun.fSubRunInfo.count());
261 for(int j = 0; j < lRun.fSubRunInfo.count(); j++) {
262 const Run::SubRunInfo& lSubRun = lRun.fSubRunInfo[j];
263 const Run::SubRunInfo& rSubRun = rRun.fSubRunInfo[j];
264
265 SkASSERT(lSubRun.fVertexStartIndex == rSubRun.fVertexStartIndex);
266 SkASSERT(lSubRun.fVertexEndIndex == rSubRun.fVertexEndIndex);
267 SkASSERT(lSubRun.fGlyphStartIndex == rSubRun.fGlyphStartIndex);
268 SkASSERT(lSubRun.fGlyphEndIndex == rSubRun.fGlyphEndIndex);
269 SkASSERT(lSubRun.fTextRatio == rSubRun.fTextRatio);
270 SkASSERT(lSubRun.fMaskFormat == rSubRun.fMaskFormat);
271 SkASSERT(lSubRun.fDrawAsDistanceFields == rSubRun.fDrawAsDistanceFields);
272 SkASSERT(lSubRun.fUseLCDText == rSubRun.fUseLCDText);
273
274 //We can't compare the bulk use tokens with this method
275 /*
276 SkASSERT(lSubRun.fBulkUseToken.fPlotsToUpdate.count() ==
277 rSubRun.fBulkUseToken.fPlotsToUpdate.count());
278 SkASSERT(lSubRun.fBulkUseToken.fPlotAlreadyUpdated ==
279 rSubRun.fBulkUseToken.fPlotAlreadyUpdated);
280 for (int k = 0; k < lSubRun.fBulkUseToken.fPlotsToUpdate.count(); k++) {
281 SkASSERT(lSubRun.fBulkUseToken.fPlotsToUpdate[k] ==
282 rSubRun.fBulkUseToken.fPlotsToUpdate[k]);
283 }*/
284 }
285 }
286}
287
288#endif