OLD | NEW |
---|---|
1 /* | 1 /* |
2 * Copyright 2013 Google Inc. | 2 * Copyright 2013 Google Inc. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
6 */ | 6 */ |
7 #include "GrBitmapTextContext.h" | |
7 | 8 |
8 #include "GrBitmapTextContext.h" | |
9 #include "GrAtlas.h" | 9 #include "GrAtlas.h" |
10 #include "GrBatch.h" | |
11 #include "GrBatchFontCache.h" | |
12 #include "GrBatchTarget.h" | |
10 #include "GrDefaultGeoProcFactory.h" | 13 #include "GrDefaultGeoProcFactory.h" |
11 #include "GrDrawTarget.h" | 14 #include "GrDrawTarget.h" |
12 #include "GrFontCache.h" | 15 #include "GrFontCache.h" |
13 #include "GrFontScaler.h" | 16 #include "GrFontScaler.h" |
14 #include "GrIndexBuffer.h" | 17 #include "GrIndexBuffer.h" |
15 #include "GrStrokeInfo.h" | 18 #include "GrStrokeInfo.h" |
16 #include "GrTexturePriv.h" | 19 #include "GrTexturePriv.h" |
17 | 20 |
18 #include "SkAutoKern.h" | 21 #include "SkAutoKern.h" |
19 #include "SkColorPriv.h" | 22 #include "SkColorPriv.h" |
20 #include "SkDraw.h" | 23 #include "SkDraw.h" |
24 #include "SkDrawFilter.h" | |
21 #include "SkDrawProcs.h" | 25 #include "SkDrawProcs.h" |
22 #include "SkGlyphCache.h" | 26 #include "SkGlyphCache.h" |
23 #include "SkGpuDevice.h" | 27 #include "SkGpuDevice.h" |
24 #include "SkGr.h" | 28 #include "SkGr.h" |
25 #include "SkPath.h" | 29 #include "SkPath.h" |
26 #include "SkRTConf.h" | 30 #include "SkRTConf.h" |
27 #include "SkStrokeRec.h" | 31 #include "SkStrokeRec.h" |
32 #include "SkTextBlob.h" | |
28 #include "SkTextMapStateProc.h" | 33 #include "SkTextMapStateProc.h" |
29 | 34 |
30 #include "effects/GrBitmapTextGeoProc.h" | 35 #include "effects/GrBitmapTextGeoProc.h" |
31 #include "effects/GrSimpleTextureEffect.h" | 36 #include "effects/GrSimpleTextureEffect.h" |
32 | 37 |
33 SK_CONF_DECLARE(bool, c_DumpFontCache, "gpu.dumpFontCache", false, | 38 SK_CONF_DECLARE(bool, c_DumpFontCache, "gpu.dumpFontCache", false, |
34 "Dump the contents of the font cache before every purge."); | 39 "Dump the contents of the font cache before every purge."); |
35 | 40 |
36 namespace { | 41 namespace { |
37 static const size_t kLCDTextVASize = sizeof(SkPoint) + sizeof(SkIPoint16); | 42 static const size_t kLCDTextVASize = sizeof(SkPoint) + sizeof(SkIPoint16); |
38 | 43 |
39 // position + local coord | 44 // position + local coord |
40 static const size_t kColorTextVASize = sizeof(SkPoint) + sizeof(SkIPoint16); | 45 static const size_t kColorTextVASize = sizeof(SkPoint) + sizeof(SkIPoint16); |
41 | 46 |
42 static const size_t kGrayTextVASize = sizeof(SkPoint) + sizeof(GrColor) + sizeof (SkIPoint16); | 47 static const size_t kGrayTextVASize = sizeof(SkPoint) + sizeof(GrColor) + sizeof (SkIPoint16); |
43 | 48 |
44 static const int kVerticesPerGlyph = 4; | 49 static const int kVerticesPerGlyph = 4; |
45 static const int kIndicesPerGlyph = 6; | 50 static const int kIndicesPerGlyph = 6; |
46 }; | 51 }; |
47 | 52 |
53 // TODO | |
54 // More tests | |
55 // move to SkCache | |
56 // handle textblobs where the whole run is larger than the cache size | |
57 // TODO implement micro speedy hash map for fast refing of glyphs | |
58 | |
59 GrBitmapTextContextB::GrBitmapTextContextB(GrContext* context, | |
60 SkGpuDevice* gpuDevice, | |
61 const SkDeviceProperties& properties) | |
62 : INHERITED(context, gpuDevice, propertie s) { | |
63 fCurrStrike = NULL; | |
64 } | |
65 | |
66 void GrBitmapTextContextB::ClearCacheEntry(uint32_t key, BitmapTextBlob** blob) { | |
67 (*blob)->unref(); | |
68 } | |
69 | |
70 GrBitmapTextContextB::~GrBitmapTextContextB() { | |
71 fCache.foreach(&GrBitmapTextContextB::ClearCacheEntry); | |
72 } | |
73 | |
74 GrBitmapTextContextB* GrBitmapTextContextB::Create(GrContext* context, | |
75 SkGpuDevice* gpuDevice, | |
76 const SkDeviceProperties& props ) { | |
77 return SkNEW_ARGS(GrBitmapTextContextB, (context, gpuDevice, props)); | |
78 } | |
79 | |
80 bool GrBitmapTextContextB::canDraw(const SkPaint& paint, const SkMatrix& viewMat rix) { | |
81 return !SkDraw::ShouldDrawTextAsPaths(paint, viewMatrix); | |
82 } | |
83 | |
84 inline void GrBitmapTextContextB::init(GrRenderTarget* rt, const GrClip& clip, | |
85 const GrPaint& paint, const SkPaint& skPai nt, | |
86 const SkIRect& regionClipBounds) { | |
87 INHERITED::init(rt, clip, paint, skPaint, regionClipBounds); | |
88 | |
89 fCurrStrike = NULL; | |
90 } | |
91 | |
92 bool GrBitmapTextContextB::MustRegenerateBlob(const BitmapTextBlob& blob, const SkPaint& paint, | |
93 const SkMatrix& viewMatrix, SkScala r x, SkScalar y) { | |
94 // We always regenerate blobs with patheffects or mask filters we could cach e these | |
95 // TODO find some way to cache the maskfilter / patheffects on the textblob | |
96 return !blob.fViewMatrix.cheapEqualTo(viewMatrix) || blob.fX != x || blob.fY != y || | |
97 paint.getMaskFilter() || paint.getPathEffect() || paint.getStyle() ! = blob.fStyle; | |
98 } | |
99 | |
100 void GrBitmapTextContextB::onDrawTextBlob(GrRenderTarget* rt, const GrClip& clip , | |
101 const SkPaint& skPaint, const SkMatrix& viewMatrix, | |
102 const SkTextBlob* blob, SkScalar x, SkS calar y, | |
103 SkDrawFilter* drawFilter, const SkIRect & clipBounds) { | |
104 BitmapTextBlob* cacheBlob; | |
105 BitmapTextBlob** foundBlob = fCache.find(blob->uniqueID()); | |
106 | |
107 SkIRect clipRect; | |
108 clip.getConservativeBounds(rt->width(), rt->height(), &clipRect); | |
109 | |
110 if (foundBlob) { | |
111 cacheBlob = *foundBlob; | |
112 if (MustRegenerateBlob(*cacheBlob, skPaint, viewMatrix, x, y)) { | |
113 // We can get away with reusing the blob if there are no outstanding refs on it. | |
114 // However, we still have to reset all of the runs. | |
115 if (!cacheBlob->unique()) { | |
116 cacheBlob->unref(); | |
117 cacheBlob = SkNEW(BitmapTextBlob); | |
118 fCache.set(blob->uniqueID(), cacheBlob); | |
119 } | |
120 this->regenerateTextBlob(cacheBlob, skPaint, viewMatrix, blob, x, y, drawFilter, | |
121 clipRect); | |
122 } | |
123 } else { | |
124 cacheBlob = SkNEW(BitmapTextBlob); | |
125 fCache.set(blob->uniqueID(), cacheBlob); | |
126 this->regenerateTextBlob(cacheBlob, skPaint, viewMatrix, blob, x, y, dra wFilter, clipRect); | |
127 } | |
128 | |
129 // Though for the time being runs in the textblob can override the paint, th ey only touch font | |
130 // info. | |
131 GrPaint grPaint; | |
132 SkPaint2GrPaintShader(fContext, rt, skPaint, viewMatrix, true, &grPaint); | |
133 | |
134 this->flush(fContext->getTextTarget(), cacheBlob, rt, grPaint, clip, viewMat rix, | |
135 fSkPaint.getAlpha()); | |
136 } | |
137 | |
138 void GrBitmapTextContextB::regenerateTextBlob(BitmapTextBlob* cacheBlob, | |
139 const SkPaint& skPaint, const SkMa trix& viewMatrix, | |
140 const SkTextBlob* blob, SkScalar x , SkScalar y, | |
141 SkDrawFilter* drawFilter, const Sk IRect& clipRect) { | |
142 cacheBlob->fViewMatrix = viewMatrix; | |
143 cacheBlob->fX = x; | |
144 cacheBlob->fY = y; | |
145 cacheBlob->fStyle = skPaint.getStyle(); | |
146 cacheBlob->fRuns.reset(blob->fRunCount); | |
147 | |
148 // Regenerate textblob | |
149 SkPaint runPaint = skPaint; | |
150 SkTextBlob::RunIterator it(blob); | |
151 for (int run = 0; !it.done(); it.next(), run++) { | |
152 size_t textLen = it.glyphCount() * sizeof(uint16_t); | |
153 const SkPoint& offset = it.offset(); | |
154 // applyFontToPaint() always overwrites the exact same attributes, | |
155 // so it is safe to not re-seed the paint for this reason. | |
156 it.applyFontToPaint(&runPaint); | |
157 | |
158 if (drawFilter && !drawFilter->filter(&runPaint, SkDrawFilter::kText_Typ e)) { | |
159 // A false return from filter() means we should abort the current dr aw. | |
160 runPaint = skPaint; | |
161 continue; | |
162 } | |
163 | |
164 runPaint.setFlags(fGpuDevice->filterTextFlags(runPaint)); | |
165 | |
166 switch (it.positioning()) { | |
167 case SkTextBlob::kDefault_Positioning: | |
168 this->internalDrawText(cacheBlob, run, runPaint, viewMatrix, | |
169 (const char *)it.glyphs(), textLen, | |
170 x + offset.x(), y + offset.y(), clipRect) ; | |
171 break; | |
172 case SkTextBlob::kHorizontal_Positioning: | |
173 this->internalDrawPosText(cacheBlob, run, runPaint, viewMatrix, | |
174 (const char*)it.glyphs(), textLen, it. pos(), 1, | |
175 SkPoint::Make(x, y + offset.y()), clip Rect); | |
176 break; | |
177 case SkTextBlob::kFull_Positioning: | |
178 this->internalDrawPosText(cacheBlob, run, runPaint, viewMatrix, | |
179 (const char*)it.glyphs(), textLen, it. pos(), 2, | |
180 SkPoint::Make(x, y), clipRect); | |
181 break; | |
182 } | |
183 | |
184 if (drawFilter) { | |
185 // A draw filter may change the paint arbitrarily, so we must re-see d in this case. | |
186 runPaint = skPaint; | |
187 } | |
188 } | |
189 } | |
190 | |
191 void GrBitmapTextContextB::onDrawText(GrRenderTarget* rt, const GrClip& clip, | |
192 const GrPaint& paint, const SkPaint& skPain t, | |
193 const SkMatrix& viewMatrix, | |
194 const char text[], size_t byteLength, | |
195 SkScalar x, SkScalar y, const SkIRect& regi onClipBounds) { | |
196 SkAutoTUnref<BitmapTextBlob> blob(SkNEW(BitmapTextBlob)); | |
197 blob->fViewMatrix = viewMatrix; | |
198 blob->fX = x; | |
199 blob->fY = y; | |
200 blob->fStyle = skPaint.getStyle(); | |
201 blob->fRuns.push_back(); | |
202 | |
203 SkIRect clipRect; | |
204 clip.getConservativeBounds(rt->width(), rt->height(), &clipRect); | |
205 this->internalDrawText(blob, 0, skPaint, viewMatrix, text, byteLength, x, y, clipRect); | |
206 this->flush(fContext->getTextTarget(), blob, rt, paint, clip, viewMatrix, sk Paint.getAlpha()); | |
207 } | |
208 | |
209 void GrBitmapTextContextB::internalDrawText(BitmapTextBlob* blob, int runIndex, | |
210 const SkPaint& skPaint, | |
211 const SkMatrix& viewMatrix, | |
212 const char text[], size_t byteLength, | |
213 SkScalar x, SkScalar y, const SkIRect & clipRect) { | |
214 SkASSERT(byteLength == 0 || text != NULL); | |
215 | |
216 // nothing to draw | |
217 if (text == NULL || byteLength == 0) { | |
218 return; | |
219 } | |
220 | |
221 fCurrStrike = NULL; | |
222 SkDrawCacheProc glyphCacheProc = skPaint.getDrawCacheProc(); | |
223 | |
224 // Get GrFontScaler from cache | |
225 BitmapTextBlob::Run& run = blob->fRuns[runIndex]; | |
226 run.fDescriptor.reset(skPaint.getScalerContextDescriptor(&fDeviceProperties, &viewMatrix, | |
227 false)); | |
228 run.fTypeface.reset(SkSafeRef(skPaint.getTypeface())); | |
229 const SkDescriptor* desc = reinterpret_cast<const SkDescriptor*>(run.fDescri ptor->data()); | |
230 SkGlyphCache* cache = SkGlyphCache::DetachCache(run.fTypeface, desc); | |
231 GrFontScaler* fontScaler = GetGrFontScaler(cache); | |
232 | |
233 // transform our starting point | |
234 { | |
235 SkPoint loc; | |
236 viewMatrix.mapXY(x, y, &loc); | |
237 x = loc.fX; | |
238 y = loc.fY; | |
239 } | |
240 | |
241 // need to measure first | |
242 if (skPaint.getTextAlign() != SkPaint::kLeft_Align) { | |
243 SkVector stopVector; | |
244 MeasureText(cache, glyphCacheProc, text, byteLength, &stopVector); | |
245 | |
246 SkScalar stopX = stopVector.fX; | |
247 SkScalar stopY = stopVector.fY; | |
248 | |
249 if (skPaint.getTextAlign() == SkPaint::kCenter_Align) { | |
250 stopX = SkScalarHalf(stopX); | |
251 stopY = SkScalarHalf(stopY); | |
252 } | |
253 x -= stopX; | |
254 y -= stopY; | |
255 } | |
256 | |
257 const char* stop = text + byteLength; | |
258 | |
259 SkAutoKern autokern; | |
260 | |
261 SkFixed fxMask = ~0; | |
262 SkFixed fyMask = ~0; | |
263 SkScalar halfSampleX, halfSampleY; | |
264 if (cache->isSubpixel()) { | |
265 halfSampleX = halfSampleY = SkFixedToScalar(SkGlyph::kSubpixelRound); | |
266 SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(viewMatrix); | |
267 if (kX_SkAxisAlignment == baseline) { | |
268 fyMask = 0; | |
269 halfSampleY = SK_ScalarHalf; | |
270 } else if (kY_SkAxisAlignment == baseline) { | |
271 fxMask = 0; | |
272 halfSampleX = SK_ScalarHalf; | |
273 } | |
274 } else { | |
275 halfSampleX = halfSampleY = SK_ScalarHalf; | |
276 } | |
277 | |
278 Sk48Dot16 fx = SkScalarTo48Dot16(x + halfSampleX); | |
279 Sk48Dot16 fy = SkScalarTo48Dot16(y + halfSampleY); | |
280 | |
281 while (text < stop) { | |
282 const SkGlyph& glyph = glyphCacheProc(cache, &text, fx & fxMask, fy & fy Mask); | |
283 | |
284 fx += autokern.adjust(glyph); | |
285 | |
286 if (glyph.fWidth) { | |
287 this->appendGlyph(blob, | |
288 runIndex, | |
289 GrGlyph::Pack(glyph.getGlyphID(), | |
290 glyph.getSubXFixed(), | |
291 glyph.getSubYFixed(), | |
292 GrGlyph::kCoverage_MaskStyle), | |
293 Sk48Dot16FloorToInt(fx), | |
294 Sk48Dot16FloorToInt(fy), | |
295 fontScaler, | |
296 clipRect); | |
297 } | |
298 | |
299 fx += glyph.fAdvanceX; | |
300 fy += glyph.fAdvanceY; | |
301 } | |
302 | |
303 SkGlyphCache::AttachCache(cache); | |
304 } | |
305 | |
306 void GrBitmapTextContextB::onDrawPosText(GrRenderTarget* rt, const GrClip& clip, | |
307 const GrPaint& paint, const SkPaint& skP aint, | |
308 const SkMatrix& viewMatrix, | |
309 const char text[], size_t byteLength, | |
310 const SkScalar pos[], int scalarsPerPosi tion, | |
311 const SkPoint& offset, const SkIRect& re gionClipBounds) { | |
312 SkAutoTUnref<BitmapTextBlob> blob(SkNEW(BitmapTextBlob)); | |
313 blob->fStyle = skPaint.getStyle(); | |
314 blob->fRuns.push_back(); | |
315 blob->fViewMatrix = viewMatrix; | |
316 | |
317 SkIRect clipRect; | |
318 clip.getConservativeBounds(rt->width(), rt->height(), &clipRect); | |
319 this->internalDrawPosText(blob, 0, skPaint, viewMatrix, text, byteLength, po s, | |
320 scalarsPerPosition, offset, clipRect); | |
321 this->flush(fContext->getTextTarget(), blob, rt, paint, clip, viewMatrix, fS kPaint.getAlpha()); | |
322 } | |
323 | |
324 void GrBitmapTextContextB::internalDrawPosText(BitmapTextBlob* blob, int runInde x, | |
325 const SkPaint& skPaint, | |
326 const SkMatrix& viewMatrix, | |
327 const char text[], size_t byteLeng th, | |
328 const SkScalar pos[], int scalarsP erPosition, | |
329 const SkPoint& offset, const SkIRe ct& clipRect) { | |
330 SkASSERT(byteLength == 0 || text != NULL); | |
331 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition); | |
332 | |
333 // nothing to draw | |
334 if (text == NULL || byteLength == 0) { | |
335 return; | |
336 } | |
337 | |
338 fCurrStrike = NULL; | |
339 SkDrawCacheProc glyphCacheProc = skPaint.getDrawCacheProc(); | |
340 | |
341 // Get GrFontScaler from cache | |
342 BitmapTextBlob::Run& run = blob->fRuns[runIndex]; | |
343 run.fDescriptor.reset(skPaint.getScalerContextDescriptor(&fDeviceProperties, &viewMatrix, | |
344 false)); | |
345 run.fTypeface.reset(SkSafeRef(skPaint.getTypeface())); | |
346 const SkDescriptor* desc = reinterpret_cast<const SkDescriptor*>(run.fDescri ptor->data()); | |
347 SkGlyphCache* cache = SkGlyphCache::DetachCache(run.fTypeface, desc); | |
348 GrFontScaler* fontScaler = GetGrFontScaler(cache); | |
349 | |
350 const char* stop = text + byteLength; | |
351 SkTextAlignProc alignProc(skPaint.getTextAlign()); | |
352 SkTextMapStateProc tmsProc(viewMatrix, offset, scalarsPerPosition); | |
353 SkScalar halfSampleX = 0, halfSampleY = 0; | |
354 | |
355 if (cache->isSubpixel()) { | |
356 // maybe we should skip the rounding if linearText is set | |
357 SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(viewMatrix); | |
358 | |
359 SkFixed fxMask = ~0; | |
360 SkFixed fyMask = ~0; | |
361 if (kX_SkAxisAlignment == baseline) { | |
362 fyMask = 0; | |
363 halfSampleY = SK_ScalarHalf; | |
364 } else if (kY_SkAxisAlignment == baseline) { | |
365 fxMask = 0; | |
366 halfSampleX = SK_ScalarHalf; | |
367 } | |
368 | |
369 if (SkPaint::kLeft_Align == skPaint.getTextAlign()) { | |
370 while (text < stop) { | |
371 SkPoint tmsLoc; | |
372 tmsProc(pos, &tmsLoc); | |
373 Sk48Dot16 fx = SkScalarTo48Dot16(tmsLoc.fX + halfSampleX); | |
374 Sk48Dot16 fy = SkScalarTo48Dot16(tmsLoc.fY + halfSampleY); | |
375 | |
376 const SkGlyph& glyph = glyphCacheProc(cache, &text, | |
377 fx & fxMask, fy & fyMask); | |
378 | |
379 if (glyph.fWidth) { | |
380 this->appendGlyph(blob, | |
381 runIndex, | |
382 GrGlyph::Pack(glyph.getGlyphID(), | |
383 glyph.getSubXFixed(), | |
384 glyph.getSubYFixed(), | |
385 GrGlyph::kCoverage_MaskStyle ), | |
386 Sk48Dot16FloorToInt(fx), | |
387 Sk48Dot16FloorToInt(fy), | |
388 fontScaler, | |
389 clipRect); | |
390 } | |
391 pos += scalarsPerPosition; | |
392 } | |
393 } else { | |
394 while (text < stop) { | |
395 const char* currentText = text; | |
396 const SkGlyph& metricGlyph = glyphCacheProc(cache, &text, 0, 0); | |
397 | |
398 if (metricGlyph.fWidth) { | |
399 SkDEBUGCODE(SkFixed prevAdvX = metricGlyph.fAdvanceX;) | |
400 SkDEBUGCODE(SkFixed prevAdvY = metricGlyph.fAdvanceY;) | |
401 SkPoint tmsLoc; | |
402 tmsProc(pos, &tmsLoc); | |
403 SkPoint alignLoc; | |
404 alignProc(tmsLoc, metricGlyph, &alignLoc); | |
405 | |
406 Sk48Dot16 fx = SkScalarTo48Dot16(alignLoc.fX + halfSampleX); | |
407 Sk48Dot16 fy = SkScalarTo48Dot16(alignLoc.fY + halfSampleY); | |
408 | |
409 // have to call again, now that we've been "aligned" | |
410 const SkGlyph& glyph = glyphCacheProc(cache, ¤tText, | |
411 fx & fxMask, fy & fyMa sk); | |
412 // the assumption is that the metrics haven't changed | |
413 SkASSERT(prevAdvX == glyph.fAdvanceX); | |
414 SkASSERT(prevAdvY == glyph.fAdvanceY); | |
415 SkASSERT(glyph.fWidth); | |
416 | |
417 this->appendGlyph(blob, | |
418 runIndex, | |
419 GrGlyph::Pack(glyph.getGlyphID(), | |
420 glyph.getSubXFixed(), | |
421 glyph.getSubYFixed(), | |
422 GrGlyph::kCoverage_MaskStyle ), | |
423 Sk48Dot16FloorToInt(fx), | |
424 Sk48Dot16FloorToInt(fy), | |
425 fontScaler, | |
426 clipRect); | |
427 } | |
428 pos += scalarsPerPosition; | |
429 } | |
430 } | |
431 } else { // not subpixel | |
432 | |
433 if (SkPaint::kLeft_Align == skPaint.getTextAlign()) { | |
434 while (text < stop) { | |
435 // the last 2 parameters are ignored | |
436 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0); | |
437 | |
438 if (glyph.fWidth) { | |
439 SkPoint tmsLoc; | |
440 tmsProc(pos, &tmsLoc); | |
441 | |
442 Sk48Dot16 fx = SkScalarTo48Dot16(tmsLoc.fX + SK_ScalarHalf); //halfSampleX; | |
443 Sk48Dot16 fy = SkScalarTo48Dot16(tmsLoc.fY + SK_ScalarHalf); //halfSampleY; | |
444 this->appendGlyph(blob, | |
445 runIndex, | |
446 GrGlyph::Pack(glyph.getGlyphID(), | |
447 glyph.getSubXFixed(), | |
448 glyph.getSubYFixed(), | |
449 GrGlyph::kCoverage_MaskStyle ), | |
450 Sk48Dot16FloorToInt(fx), | |
451 Sk48Dot16FloorToInt(fy), | |
452 fontScaler, | |
453 clipRect); | |
454 } | |
455 pos += scalarsPerPosition; | |
456 } | |
457 } else { | |
458 while (text < stop) { | |
459 // the last 2 parameters are ignored | |
460 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0); | |
461 | |
462 if (glyph.fWidth) { | |
463 SkPoint tmsLoc; | |
464 tmsProc(pos, &tmsLoc); | |
465 | |
466 SkPoint alignLoc; | |
467 alignProc(tmsLoc, glyph, &alignLoc); | |
468 | |
469 Sk48Dot16 fx = SkScalarTo48Dot16(alignLoc.fX + SK_ScalarHalf ); //halfSampleX; | |
470 Sk48Dot16 fy = SkScalarTo48Dot16(alignLoc.fY + SK_ScalarHalf ); //halfSampleY; | |
471 this->appendGlyph(blob, | |
472 runIndex, | |
473 GrGlyph::Pack(glyph.getGlyphID(), | |
474 glyph.getSubXFixed(), | |
475 glyph.getSubYFixed(), | |
476 GrGlyph::kCoverage_MaskStyle ), | |
477 Sk48Dot16FloorToInt(fx), | |
478 Sk48Dot16FloorToInt(fy), | |
479 fontScaler, | |
480 clipRect); | |
481 } | |
482 pos += scalarsPerPosition; | |
483 } | |
484 } | |
485 } | |
486 SkGlyphCache::AttachCache(cache); | |
487 } | |
488 | |
489 static size_t get_vertex_stride(GrMaskFormat maskFormat) { | |
490 switch (maskFormat) { | |
491 case kA8_GrMaskFormat: | |
492 return kGrayTextVASize; | |
493 case kARGB_GrMaskFormat: | |
494 return kColorTextVASize; | |
495 default: | |
496 return kLCDTextVASize; | |
497 } | |
498 } | |
499 | |
500 void GrBitmapTextContextB::appendGlyph(BitmapTextBlob* blob, int runIndex, GrGly ph::PackedID packed, | |
501 int vx, int vy, GrFontScaler* scaler, | |
502 const SkIRect& clipRect) { | |
503 if (NULL == fCurrStrike) { | |
504 fCurrStrike = fContext->getBatchFontCache()->getStrike(scaler); | |
505 } | |
506 | |
507 GrGlyph* glyph = fCurrStrike->getGlyph(packed, scaler); | |
508 if (NULL == glyph || glyph->fBounds.isEmpty()) { | |
509 return; | |
510 } | |
511 | |
512 int x = vx + glyph->fBounds.fLeft; | |
513 int y = vy + glyph->fBounds.fTop; | |
514 | |
515 // keep them as ints until we've done the clip-test | |
516 int width = glyph->fBounds.width(); | |
517 int height = glyph->fBounds.height(); | |
518 | |
519 // check if we clipped out | |
520 if (clipRect.quickReject(x, y, x + width, y + height)) { | |
521 return; | |
522 } | |
523 | |
524 // If the glyph is too large we fall back to paths | |
525 if (fCurrStrike->glyphTooLargeForAtlas(glyph)) { | |
526 if (NULL == glyph->fPath) { | |
527 SkPath* path = SkNEW(SkPath); | |
528 if (!scaler->getGlyphPath(glyph->glyphID(), path)) { | |
529 // flag the glyph as being dead? | |
530 SkDELETE(path); | |
531 return; | |
532 } | |
533 glyph->fPath = path; | |
534 } | |
535 SkASSERT(glyph->fPath); | |
536 blob->fBigGlyphs.push_back(BitmapTextBlob::BigGlyph(*glyph->fPath, vx, v y)); | |
537 return; | |
Jvsquare
2015/03/27 15:07:29
Suggestion for the future: you know how many glyph
joshualitt
2015/03/30 14:56:43
Agreed, I have a future CL which does exactly this
| |
538 } | |
539 GrMaskFormat format = glyph->fMaskFormat; | |
540 size_t vertexStride = get_vertex_stride(format); | |
541 | |
542 BitmapTextBlob::Run& run = blob->fRuns[runIndex]; | |
543 int glyphIdx = run.fInfos[format].fGlyphIDs.count(); | |
544 *run.fInfos[format].fGlyphIDs.append() = packed; | |
545 run.fInfos[format].fVertices.append(vertexStride * kVerticesPerGlyph); | |
546 | |
547 SkRect r; | |
548 r.fLeft = SkIntToScalar(x); | |
549 r.fTop = SkIntToScalar(y); | |
550 r.fRight = r.fLeft + SkIntToScalar(width); | |
551 r.fBottom = r.fTop + SkIntToScalar(height); | |
552 | |
553 run.fVertexBounds.joinNonEmptyArg(r); | |
554 GrColor color = fPaint.getColor(); | |
555 run.fColor = color; | |
556 | |
557 intptr_t vertex = reinterpret_cast<intptr_t>(run.fInfos[format].fVertices.be gin()); | |
558 vertex += vertexStride * glyphIdx * kVerticesPerGlyph; | |
559 | |
560 // V0 | |
561 SkPoint* position = reinterpret_cast<SkPoint*>(vertex); | |
562 position->set(r.fLeft, r.fTop); | |
563 if (kA8_GrMaskFormat == format) { | |
564 SkColor* colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint)) ; | |
565 *colorPtr = color; | |
566 } | |
567 vertex += vertexStride; | |
568 | |
569 // V1 | |
570 position = reinterpret_cast<SkPoint*>(vertex); | |
571 position->set(r.fLeft, r.fBottom); | |
572 if (kA8_GrMaskFormat == format) { | |
573 SkColor* colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint)) ; | |
574 *colorPtr = color; | |
575 } | |
576 vertex += vertexStride; | |
577 | |
578 // V2 | |
579 position = reinterpret_cast<SkPoint*>(vertex); | |
580 position->set(r.fRight, r.fBottom); | |
581 if (kA8_GrMaskFormat == format) { | |
582 SkColor* colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint)) ; | |
583 *colorPtr = color; | |
584 } | |
585 vertex += vertexStride; | |
586 | |
587 // V3 | |
588 position = reinterpret_cast<SkPoint*>(vertex); | |
589 position->set(r.fRight, r.fTop); | |
590 if (kA8_GrMaskFormat == format) { | |
591 SkColor* colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint)) ; | |
592 *colorPtr = color; | |
593 } | |
594 } | |
595 | |
596 class BitmapTextBatch : public GrBatch { | |
597 public: | |
598 typedef GrBitmapTextContextB::BitmapTextBlob Blob; | |
599 typedef Blob::Run Run; | |
600 typedef Run::PerFormatInfo TextInfo; | |
601 struct Geometry { | |
602 Geometry() {} | |
603 Geometry(const Geometry& geometry) | |
604 : fBlob(SkRef(geometry.fBlob.get())) | |
605 , fRun(geometry.fRun) | |
606 , fColor(geometry.fColor) {} | |
607 SkAutoTUnref<Blob> fBlob; | |
608 int fRun; | |
609 GrColor fColor; | |
610 }; | |
611 | |
612 static GrBatch* Create(const Geometry& geometry, GrColor color, GrMaskFormat maskFormat, | |
613 GrBatchFontCache* fontCache) { | |
614 return SkNEW_ARGS(BitmapTextBatch, (geometry, color, maskFormat, fontCac he)); | |
615 } | |
616 | |
617 const char* name() const SK_OVERRIDE { return "BitmapTextBatch"; } | |
618 | |
619 void getInvariantOutputColor(GrInitInvariantOutput* out) const SK_OVERRIDE { | |
620 if (kARGB_GrMaskFormat == fMaskFormat) { | |
621 out->setUnknownFourComponents(); | |
622 } else { | |
623 out->setKnownFourComponents(fBatch.fColor); | |
624 } | |
625 } | |
626 | |
627 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const SK_OVERRID E { | |
628 if (kARGB_GrMaskFormat != fMaskFormat) { | |
629 if (GrPixelConfigIsAlphaOnly(fPixelConfig)) { | |
630 out->setUnknownSingleComponent(); | |
631 } else if (GrPixelConfigIsOpaque(fPixelConfig)) { | |
632 out->setUnknownOpaqueFourComponents(); | |
633 out->setUsingLCDCoverage(); | |
634 } else { | |
635 out->setUnknownFourComponents(); | |
636 out->setUsingLCDCoverage(); | |
637 } | |
638 } else { | |
639 out->setKnownSingleComponent(0xff); | |
640 } | |
641 } | |
642 | |
643 void initBatchTracker(const GrPipelineInfo& init) SK_OVERRIDE { | |
644 // Handle any color overrides | |
645 if (init.fColorIgnored) { | |
646 fBatch.fColor = GrColor_ILLEGAL; | |
647 } else if (GrColor_ILLEGAL != init.fOverrideColor) { | |
648 fBatch.fColor = init.fOverrideColor; | |
649 } | |
650 | |
651 // setup batch properties | |
652 fBatch.fColorIgnored = init.fColorIgnored; | |
653 fBatch.fUsesLocalCoords = init.fUsesLocalCoords; | |
654 fBatch.fCoverageIgnored = init.fCoverageIgnored; | |
655 } | |
656 | |
657 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline ) SK_OVERRIDE { | |
658 // if we have RGB, then we won't have any SkShaders so no need to use a localmatrix. | |
659 // TODO actually only invert if we don't have RGBA | |
660 SkMatrix localMatrix; | |
661 if (this->usesLocalCoords() && !this->viewMatrix().invert(&localMatrix)) { | |
662 SkDebugf("Cannot invert viewmatrix\n"); | |
663 return; | |
664 } | |
665 | |
666 GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kNone _FilterMode); | |
667 // This will be ignored in the non A8 case | |
668 bool opaqueVertexColors = GrColorIsOpaque(this->color()); | |
669 SkAutoTUnref<const GrGeometryProcessor> gp( | |
670 GrBitmapTextGeoProc::Create(this->color(), | |
671 fFontCache->getTexture(fMaskFormat), | |
672 params, | |
673 fMaskFormat, | |
674 opaqueVertexColors, | |
675 localMatrix)); | |
676 | |
677 size_t vertexStride = gp->getVertexStride(); | |
678 SkASSERT(vertexStride == get_vertex_stride(fMaskFormat)); | |
679 | |
680 this->initDraw(batchTarget, gp, pipeline); | |
681 | |
682 int glyphCount = this->numGlyphs(); | |
683 int instanceCount = fGeoData.count(); | |
684 const GrVertexBuffer* vertexBuffer; | |
685 int firstVertex; | |
686 | |
687 void* vertices = batchTarget->vertexPool()->makeSpace(vertexStride, | |
688 glyphCount * kVert icesPerGlyph, | |
689 &vertexBuffer, | |
690 &firstVertex); | |
691 if (!vertices) { | |
692 SkDebugf("Could not allocate vertices\n"); | |
693 return; | |
694 } | |
695 | |
696 unsigned char* currVertex = reinterpret_cast<unsigned char*>(vertices); | |
697 | |
698 // setup drawinfo | |
699 const GrIndexBuffer* quadIndexBuffer = batchTarget->quadIndexBuffer(); | |
700 int maxInstancesPerDraw = quadIndexBuffer->maxQuads(); | |
701 | |
702 GrDrawTarget::DrawInfo drawInfo; | |
703 drawInfo.setPrimitiveType(kTriangles_GrPrimitiveType); | |
704 drawInfo.setStartVertex(0); | |
705 drawInfo.setStartIndex(0); | |
706 drawInfo.setVerticesPerInstance(kVerticesPerGlyph); | |
707 drawInfo.setIndicesPerInstance(kIndicesPerGlyph); | |
708 drawInfo.adjustStartVertex(firstVertex); | |
709 drawInfo.setVertexBuffer(vertexBuffer); | |
710 drawInfo.setIndexBuffer(quadIndexBuffer); | |
711 | |
712 int instancesToFlush = 0; | |
713 for (int i = 0; i < instanceCount; i++) { | |
714 Geometry& args = fGeoData[i]; | |
715 Blob* blob = args.fBlob; | |
716 Run& run = blob->fRuns[args.fRun]; | |
717 TextInfo& info = run.fInfos[fMaskFormat]; | |
718 | |
719 uint32_t currentAtlasGen = fFontCache->atlasGeneration(fMaskFormat); | |
720 bool regenerateTextureCoords = info.fAtlasGeneration != currentAtlas Gen; | |
721 bool regenerateColors = kA8_GrMaskFormat == fMaskFormat && run.fColo r != args.fColor; | |
722 int glyphCount = info.fGlyphIDs.count(); | |
723 | |
724 // We regenerate both texture coords and colors in the blob itself, and update the | |
725 // atlas generation. If we don't end up purging any unused plots, w e can avoid | |
726 // regenerating the coords. We could take a finer grained approach to updating texture | |
727 // coords but its not clear if the extra bookkeeping would offset an y gains. | |
728 // To avoid looping over the glyphs twice, we do one loop and condit ionally update color | |
729 // or coords as needed. One final note, if we have to break a run f or an atlas eviction | |
730 // then we can't really trust the atlas has all of the correct data. Atlas evictions | |
731 // should be pretty rare, so we just always regenerate in those case s | |
732 if (regenerateTextureCoords || regenerateColors) { | |
733 // first regenerate texture coordinates / colors if need be | |
734 const SkDescriptor* desc = NULL; | |
735 SkGlyphCache* cache = NULL; | |
736 GrFontScaler* scaler = NULL; | |
737 GrBatchTextStrike* strike = NULL; | |
738 bool brokenRun = false; | |
739 if (regenerateTextureCoords) { | |
740 desc = reinterpret_cast<const SkDescriptor*>(run.fDescriptor ->data()); | |
741 cache = SkGlyphCache::DetachCache(run.fTypeface, desc); | |
742 scaler = GrTextContext::GetGrFontScaler(cache); | |
743 strike = fFontCache->getStrike(scaler); | |
744 } | |
745 for (int glyphIdx = 0; glyphIdx < glyphCount; glyphIdx++) { | |
746 GrGlyph::PackedID glyphID = info.fGlyphIDs[glyphIdx]; | |
747 | |
748 if (regenerateTextureCoords) { | |
749 // Upload the glyph only if needed | |
750 GrGlyph* glyph = strike->getGlyph(glyphID, scaler); | |
751 SkASSERT(glyph); | |
752 | |
753 if (!fFontCache->hasGlyph(glyph) && | |
754 !strike->addGlyphToAtlas(batchTarget, glyph, scaler) ) { | |
755 this->flush(batchTarget, &drawInfo, instancesToFlush , | |
756 maxInstancesPerDraw); | |
757 this->initDraw(batchTarget, gp, pipeline); | |
758 instancesToFlush = 0; | |
759 brokenRun = glyphIdx > 0; | |
760 | |
761 SkDEBUGCODE(bool success =) strike->addGlyphToAtlas( batchTarget, glyph, | |
762 scaler); | |
763 SkASSERT(success); | |
764 } | |
765 | |
766 fFontCache->setGlyphRefToken(glyph, batchTarget->current Token()); | |
767 | |
768 // Texture coords are the last vertex attribute so we ge t a pointer to the | |
769 // first one and then map with stride in regenerateTextu reCoords | |
770 intptr_t vertex = reinterpret_cast<intptr_t>(info.fVerti ces.begin()); | |
771 vertex += vertexStride * glyphIdx * kVerticesPerGlyph; | |
772 vertex += vertexStride - sizeof(SkIPoint16); | |
773 | |
774 this->regenerateTextureCoords(glyph, vertex, vertexStrid e); | |
775 } | |
776 | |
777 if (regenerateColors) { | |
778 intptr_t vertex = reinterpret_cast<intptr_t>(info.fVerti ces.begin()); | |
779 vertex += vertexStride * glyphIdx * kVerticesPerGlyph + sizeof(SkPoint); | |
780 this->regenerateColors(vertex, vertexStride, args.fColor ); | |
781 } | |
782 | |
783 instancesToFlush++; | |
784 } | |
785 | |
786 if (regenerateTextureCoords) { | |
787 SkGlyphCache::AttachCache(cache); | |
788 info.fAtlasGeneration = brokenRun ? GrBatchAtlas::kInvalidAt lasGeneration : | |
789 fFontCache->atlasGenerat ion(fMaskFormat); | |
790 } | |
791 } else { | |
792 instancesToFlush += glyphCount; | |
793 } | |
794 | |
795 // now copy all vertices | |
796 int byteCount = info.fVertices.count(); | |
797 memcpy(currVertex, info.fVertices.begin(), byteCount); | |
798 | |
799 currVertex += byteCount; | |
800 } | |
801 | |
802 this->flush(batchTarget, &drawInfo, instancesToFlush, maxInstancesPerDra w); | |
803 } | |
804 | |
805 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; } | |
806 | |
807 private: | |
808 BitmapTextBatch(const Geometry& geometry, GrColor color, GrMaskFormat maskFo rmat, | |
809 GrBatchFontCache* fontCache) | |
810 : fMaskFormat(maskFormat) | |
811 , fPixelConfig(fontCache->getPixelConfig(maskFormat)) | |
812 , fFontCache(fontCache) { | |
813 this->initClassID<BitmapTextBatch>(); | |
814 fGeoData.push_back(geometry); | |
815 fBatch.fColor = color; | |
816 fBatch.fViewMatrix = geometry.fBlob->fViewMatrix; | |
817 int numGlyphs = geometry.fBlob->fRuns[geometry.fRun].fInfos[maskFormat]. fGlyphIDs.count(); | |
818 fBatch.fNumGlyphs = numGlyphs; | |
819 } | |
820 | |
821 void regenerateTextureCoords(GrGlyph* glyph, intptr_t vertex, size_t vertexS tride) { | |
822 int width = glyph->fBounds.width(); | |
823 int height = glyph->fBounds.height(); | |
824 int u0 = glyph->fAtlasLocation.fX; | |
825 int v0 = glyph->fAtlasLocation.fY; | |
826 int u1 = u0 + width; | |
827 int v1 = v0 + height; | |
828 | |
829 // we assume texture coords are the last vertex attribute, this is a bit fragile. | |
830 // TODO pass in this offset or something | |
831 SkIPoint16* textureCoords; | |
832 // V0 | |
833 textureCoords = reinterpret_cast<SkIPoint16*>(vertex); | |
834 textureCoords->set(u0, v0); | |
835 vertex += vertexStride; | |
836 | |
837 // V1 | |
838 textureCoords = reinterpret_cast<SkIPoint16*>(vertex); | |
839 textureCoords->set(u0, v1); | |
840 vertex += vertexStride; | |
841 | |
842 // V2 | |
843 textureCoords = reinterpret_cast<SkIPoint16*>(vertex); | |
844 textureCoords->set(u1, v1); | |
845 vertex += vertexStride; | |
846 | |
847 // V3 | |
848 textureCoords = reinterpret_cast<SkIPoint16*>(vertex); | |
849 textureCoords->set(u1, v0); | |
850 } | |
851 | |
852 void regenerateColors(intptr_t vertex, size_t vertexStride, GrColor color) { | |
853 for (int i = 0; i < kVerticesPerGlyph; i++) { | |
854 SkColor* vcolor = reinterpret_cast<SkColor*>(vertex); | |
855 *vcolor = color; | |
856 vertex += vertexStride; | |
857 } | |
858 } | |
859 | |
860 void initDraw(GrBatchTarget* batchTarget, | |
861 const GrGeometryProcessor* gp, | |
862 const GrPipeline* pipeline) { | |
863 batchTarget->initDraw(gp, pipeline); | |
864 | |
865 // TODO remove this when batch is everywhere | |
866 GrPipelineInfo init; | |
867 init.fColorIgnored = fBatch.fColorIgnored; | |
868 init.fOverrideColor = GrColor_ILLEGAL; | |
869 init.fCoverageIgnored = fBatch.fCoverageIgnored; | |
870 init.fUsesLocalCoords = this->usesLocalCoords(); | |
871 gp->initBatchTracker(batchTarget->currentBatchTracker(), init); | |
872 } | |
873 | |
874 void flush(GrBatchTarget* batchTarget, | |
875 GrDrawTarget::DrawInfo* drawInfo, | |
876 int instanceCount, | |
877 int maxInstancesPerDraw) { | |
878 while (instanceCount) { | |
879 drawInfo->setInstanceCount(SkTMin(instanceCount, maxInstancesPerDraw )); | |
880 drawInfo->setVertexCount(drawInfo->instanceCount() * drawInfo->verti cesPerInstance()); | |
881 drawInfo->setIndexCount(drawInfo->instanceCount() * drawInfo->indice sPerInstance()); | |
882 | |
883 batchTarget->draw(*drawInfo); | |
884 | |
885 drawInfo->setStartVertex(drawInfo->startVertex() + drawInfo->vertexC ount()); | |
886 instanceCount -= drawInfo->instanceCount(); | |
887 } | |
888 } | |
889 | |
890 GrColor color() const { return fBatch.fColor; } | |
891 const SkMatrix& viewMatrix() const { return fBatch.fViewMatrix; } | |
892 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; } | |
893 int numGlyphs() const { return fBatch.fNumGlyphs; } | |
894 | |
895 bool onCombineIfPossible(GrBatch* t) SK_OVERRIDE { | |
896 BitmapTextBatch* that = t->cast<BitmapTextBatch>(); | |
897 | |
898 if (this->fMaskFormat != that->fMaskFormat) { | |
899 return false; | |
900 } | |
901 | |
902 if (this->fMaskFormat != kA8_GrMaskFormat && this->color() != that->colo r()) { | |
903 return false; | |
904 } | |
905 | |
906 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->vi ewMatrix())) { | |
907 return false; | |
908 } | |
909 | |
910 fBatch.fNumGlyphs += that->numGlyphs(); | |
911 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin()) ; | |
912 return true; | |
913 } | |
914 | |
915 struct BatchTracker { | |
916 GrColor fColor; | |
917 SkMatrix fViewMatrix; | |
918 bool fUsesLocalCoords; | |
919 bool fColorIgnored; | |
920 bool fCoverageIgnored; | |
921 int fNumGlyphs; | |
922 }; | |
923 | |
924 BatchTracker fBatch; | |
925 SkSTArray<1, Geometry, true> fGeoData; | |
926 GrMaskFormat fMaskFormat; | |
927 GrPixelConfig fPixelConfig; | |
928 GrBatchFontCache* fFontCache; | |
929 }; | |
930 | |
931 void GrBitmapTextContextB::flush(GrDrawTarget* target, BitmapTextBlob* blob, int i, | |
932 GrPipelineBuilder* pipelineBuilder, GrMaskForma t format, | |
933 GrColor color, int paintAlpha) { | |
934 if (0 == blob->fRuns[i].fInfos[format].fGlyphIDs.count()) { | |
935 return; | |
936 } | |
937 | |
938 if (kARGB_GrMaskFormat == format) { | |
939 color = SkColorSetARGB(paintAlpha, paintAlpha, paintAlpha, paintAlpha); | |
940 } | |
941 | |
942 BitmapTextBatch::Geometry geometry; | |
943 geometry.fBlob.reset(SkRef(blob)); | |
944 geometry.fRun = i; | |
945 geometry.fColor = color; | |
946 SkAutoTUnref<GrBatch> batch(BitmapTextBatch::Create(geometry, color, format, | |
947 fContext->getBatchFontCa che())); | |
948 | |
949 target->drawBatch(pipelineBuilder, batch, &blob->fRuns[i].fVertexBounds); | |
950 } | |
951 | |
952 void GrBitmapTextContextB::flush(GrDrawTarget* target, BitmapTextBlob* blob, GrR enderTarget* rt, | |
953 const GrPaint& paint, const GrClip& clip, | |
954 const SkMatrix& viewMatrix, int paintAlpha) { | |
955 GrPipelineBuilder pipelineBuilder; | |
956 pipelineBuilder.setFromPaint(paint, rt, clip); | |
957 | |
958 GrColor color = paint.getColor(); | |
959 for (int i = 0; i < blob->fRuns.count(); i++) { | |
960 this->flush(target, blob, i, &pipelineBuilder, kA8_GrMaskFormat, color, paintAlpha); | |
961 this->flush(target, blob, i, &pipelineBuilder, kA565_GrMaskFormat, color , paintAlpha); | |
962 this->flush(target, blob, i, &pipelineBuilder, kARGB_GrMaskFormat, color , paintAlpha); | |
963 } | |
964 | |
965 // Now flush big glyphs | |
966 for (int i = 0; i < blob->fBigGlyphs.count(); i++) { | |
967 BitmapTextBlob::BigGlyph& bigGlyph = blob->fBigGlyphs[i]; | |
968 SkMatrix translate; | |
969 translate.setTranslate(SkIntToScalar(bigGlyph.fVx), SkIntToScalar(bigGly ph.fVy)); | |
970 SkPath tmpPath(bigGlyph.fPath); | |
971 tmpPath.transform(translate); | |
972 GrStrokeInfo strokeInfo(SkStrokeRec::kFill_InitStyle); | |
973 fContext->drawPath(rt, clip, paint, SkMatrix::I(), tmpPath, strokeInfo); | |
974 } | |
975 } | |
976 | |
48 GrBitmapTextContext::GrBitmapTextContext(GrContext* context, | 977 GrBitmapTextContext::GrBitmapTextContext(GrContext* context, |
49 SkGpuDevice* gpuDevice, | 978 SkGpuDevice* gpuDevice, |
50 const SkDeviceProperties& properties) | 979 const SkDeviceProperties& properties) |
51 : GrTextContext(context, gpuDevice, properties) { | 980 : GrTextContext(context, gpuDevice, properties) { |
52 fStrike = NULL; | 981 fStrike = NULL; |
53 | 982 |
54 fCurrTexture = NULL; | 983 fCurrTexture = NULL; |
55 fEffectTextureUniqueID = SK_InvalidUniqueID; | 984 fEffectTextureUniqueID = SK_InvalidUniqueID; |
56 | 985 |
57 fVertices = NULL; | 986 fVertices = NULL; |
(...skipping 283 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
341 fontScaler); | 1270 fontScaler); |
342 } | 1271 } |
343 pos += scalarsPerPosition; | 1272 pos += scalarsPerPosition; |
344 } | 1273 } |
345 } | 1274 } |
346 } | 1275 } |
347 | 1276 |
348 this->finish(); | 1277 this->finish(); |
349 } | 1278 } |
350 | 1279 |
351 static size_t get_vertex_stride(GrMaskFormat maskFormat) { | |
352 switch (maskFormat) { | |
353 case kA8_GrMaskFormat: | |
354 return kGrayTextVASize; | |
355 case kARGB_GrMaskFormat: | |
356 return kColorTextVASize; | |
357 default: | |
358 return kLCDTextVASize; | |
359 } | |
360 } | |
361 | |
362 static void* alloc_vertices(GrDrawTarget* drawTarget, | 1280 static void* alloc_vertices(GrDrawTarget* drawTarget, |
363 int numVertices, | 1281 int numVertices, |
364 GrMaskFormat maskFormat) { | 1282 GrMaskFormat maskFormat) { |
365 if (numVertices <= 0) { | 1283 if (numVertices <= 0) { |
366 return NULL; | 1284 return NULL; |
367 } | 1285 } |
368 | 1286 |
369 // set up attributes | 1287 // set up attributes |
370 void* vertices = NULL; | 1288 void* vertices = NULL; |
371 bool success = drawTarget->reserveVertexAndIndexSpace(numVertices, | 1289 bool success = drawTarget->reserveVertexAndIndexSpace(numVertices, |
372 get_vertex_stride(mask Format), | 1290 get_vertex_stride(mask Format), |
373 0, | 1291 0, |
374 &vertices, | 1292 &vertices, |
375 NULL); | 1293 NULL); |
376 GrAlwaysAssert(success); | 1294 GrAlwaysAssert(success); |
377 return vertices; | 1295 return vertices; |
378 } | 1296 } |
379 | 1297 |
380 inline bool GrBitmapTextContext::uploadGlyph(GrGlyph* glyph, GrFontScaler* scale r) { | 1298 inline bool GrBitmapTextContext::uploadGlyph(GrGlyph* glyph, GrFontScaler* scale r) { |
381 if (!fStrike->glyphTooLargeForAtlas(glyph)) { | 1299 if (!fStrike->glyphTooLargeForAtlas(glyph)) { |
382 if (fStrike->addGlyphToAtlas(glyph, scaler)) { | 1300 if (fStrike->addGlyphToAtlas(glyph, scaler)) { |
383 return true; | 1301 return true; |
384 } | 1302 } |
385 | 1303 |
386 // try to clear out an unused plot before we flush | 1304 // try to clear out an unused plot before we flush |
387 if (fContext->getFontCache()->freeUnusedPlot(fStrike, glyph) && | 1305 if (fContext->getFontCache()->freeUnusedPlot(fStrike, glyph) && |
388 fStrike->addGlyphToAtlas(glyph, scaler)) { | 1306 fStrike->addGlyphToAtlas(glyph, scaler)) { |
389 return true; | 1307 return true; |
390 } | 1308 } |
391 | 1309 |
392 if (c_DumpFontCache) { | 1310 if (c_DumpFontCache) { |
393 #ifdef SK_DEVELOPER | 1311 #ifdef SK_DEVELOPER |
394 fContext->getFontCache()->dump(); | 1312 fContext->getFontCache()->dump(); |
395 #endif | 1313 #endif |
396 } | 1314 } |
397 | 1315 |
398 // before we purge the cache, we must flush any accumulated draws | 1316 // before we purge the cache, we must flush any accumulated draws |
399 this->flush(); | 1317 this->flush(); |
400 fContext->flush(); | 1318 fContext->flush(); |
401 | 1319 |
402 // we should have an unused plot now | 1320 // we should have an unused plot now |
403 if (fContext->getFontCache()->freeUnusedPlot(fStrike, glyph) && | 1321 if (fContext->getFontCache()->freeUnusedPlot(fStrike, glyph) && |
404 fStrike->addGlyphToAtlas(glyph, scaler)) { | 1322 fStrike->addGlyphToAtlas(glyph, scaler)) { |
405 return true; | 1323 return true; |
406 } | 1324 } |
407 | 1325 |
408 // we should never get here | 1326 // we should never get here |
409 SkASSERT(false); | 1327 SkASSERT(false); |
410 } | 1328 } |
411 | 1329 |
412 return false; | 1330 return false; |
413 } | 1331 } |
414 | 1332 |
415 void GrBitmapTextContext::appendGlyph(GrGlyph::PackedID packed, | 1333 void GrBitmapTextContext::appendGlyph(GrGlyph::PackedID packed, |
416 int vx, int vy, | 1334 int vx, int vy, |
417 GrFontScaler* scaler) { | 1335 GrFontScaler* scaler) { |
418 if (NULL == fDrawTarget) { | 1336 if (NULL == fDrawTarget) { |
419 return; | 1337 return; |
420 } | 1338 } |
421 | 1339 |
(...skipping 205 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
627 SkSafeSetNull(fCurrTexture); | 1545 SkSafeSetNull(fCurrTexture); |
628 } | 1546 } |
629 } | 1547 } |
630 | 1548 |
631 inline void GrBitmapTextContext::finish() { | 1549 inline void GrBitmapTextContext::finish() { |
632 this->flush(); | 1550 this->flush(); |
633 fTotalVertexCount = 0; | 1551 fTotalVertexCount = 0; |
634 | 1552 |
635 GrTextContext::finish(); | 1553 GrTextContext::finish(); |
636 } | 1554 } |
637 | |
OLD | NEW |