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