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