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