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 #include "GrBitmapTextContext.h" |
8 | 8 |
9 #include "GrAtlas.h" | 9 #include "GrAtlas.h" |
10 #include "GrBatch.h" | 10 #include "GrBatch.h" |
(...skipping 30 matching lines...) Expand all Loading... |
41 namespace { | 41 namespace { |
42 static const size_t kLCDTextVASize = sizeof(SkPoint) + sizeof(SkIPoint16); | 42 static const size_t kLCDTextVASize = sizeof(SkPoint) + sizeof(SkIPoint16); |
43 | 43 |
44 // position + local coord | 44 // position + local coord |
45 static const size_t kColorTextVASize = sizeof(SkPoint) + sizeof(SkIPoint16); | 45 static const size_t kColorTextVASize = sizeof(SkPoint) + sizeof(SkIPoint16); |
46 | 46 |
47 static const size_t kGrayTextVASize = sizeof(SkPoint) + sizeof(GrColor) + sizeof
(SkIPoint16); | 47 static const size_t kGrayTextVASize = sizeof(SkPoint) + sizeof(GrColor) + sizeof
(SkIPoint16); |
48 | 48 |
49 static const int kVerticesPerGlyph = 4; | 49 static const int kVerticesPerGlyph = 4; |
50 static const int kIndicesPerGlyph = 6; | 50 static const int kIndicesPerGlyph = 6; |
51 }; | |
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 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 | |
105 inline SkGlyphCache* GrBitmapTextContextB::setupCache(BitmapTextBlob::Run* run, | |
106 const SkPaint& skPaint, | |
107 const SkMatrix& viewMatrix
) { | |
108 skPaint.getScalerContextDescriptor(&run->fDescriptor, &fDeviceProperties, &v
iewMatrix, false); | |
109 run->fTypeface.reset(SkSafeRef(skPaint.getTypeface())); | |
110 return SkGlyphCache::DetachCache(run->fTypeface, run->fDescriptor.getDesc())
; | |
111 } | |
112 | |
113 inline void GrBitmapTextContextB::BlobGlyphCount(int* glyphCount, int* runCount, | |
114 const SkTextBlob* blob) { | |
115 SkTextBlob::RunIterator itCounter(blob); | |
116 for (; !itCounter.done(); itCounter.next(), (*runCount)++) { | |
117 *glyphCount += itCounter.glyphCount(); | |
118 } | |
119 } | |
120 | |
121 GrBitmapTextContextB::BitmapTextBlob* GrBitmapTextContextB::CreateBlob(int glyph
Count, | |
122 int runCo
unt) { | |
123 // We allocate size for the BitmapTextBlob itself, plus size for the vertice
s array, | |
124 // and size for the glyphIds array. | |
125 SK_COMPILE_ASSERT(kGrayTextVASize >= kColorTextVASize && kGrayTextVASize >=
kLCDTextVASize, | |
126 vertex_attribute_changed); | |
127 size_t verticesCount = glyphCount * kVerticesPerGlyph * kGrayTextVASize; | |
128 size_t length = sizeof(BitmapTextBlob) + | |
129 verticesCount + | |
130 glyphCount * sizeof(GrGlyph::PackedID) + | |
131 sizeof(BitmapTextBlob::Run) * runCount; | |
132 | |
133 BitmapTextBlob* cacheBlob = SkNEW_PLACEMENT(sk_malloc_throw(length), BitmapT
extBlob); | |
134 | |
135 // setup offsets for vertices / glyphs | |
136 cacheBlob->fVertices = sizeof(BitmapTextBlob) + reinterpret_cast<unsigned ch
ar*>(cacheBlob); | |
137 cacheBlob->fGlyphIDs = | |
138 reinterpret_cast<GrGlyph::PackedID*>(cacheBlob->fVertices + vertices
Count); | |
139 cacheBlob->fRuns = reinterpret_cast<BitmapTextBlob::Run*>(cacheBlob->fGlyphI
Ds + glyphCount); | |
140 | |
141 // Initialize runs | |
142 for (int i = 0; i < runCount; i++) { | |
143 SkNEW_PLACEMENT(&cacheBlob->fRuns[i], BitmapTextBlob::Run); | |
144 } | |
145 cacheBlob->fRunCount = runCount; | |
146 return cacheBlob; | |
147 } | |
148 | |
149 void GrBitmapTextContextB::drawTextBlob(GrRenderTarget* rt, const GrClip& clip, | |
150 const SkPaint& skPaint, const SkMatrix&
viewMatrix, | |
151 const SkTextBlob* blob, SkScalar x, SkSc
alar y, | |
152 SkDrawFilter* drawFilter, const SkIRect&
clipBounds) { | |
153 BitmapTextBlob* cacheBlob; | |
154 BitmapTextBlob** foundBlob = fCache.find(blob->uniqueID()); | |
155 SkIRect clipRect; | |
156 clip.getConservativeBounds(rt->width(), rt->height(), &clipRect); | |
157 | |
158 if (foundBlob) { | |
159 cacheBlob = *foundBlob; | |
160 if (MustRegenerateBlob(*cacheBlob, skPaint, viewMatrix, x, y)) { | |
161 // We have to remake the blob because changes may invalidate our mas
ks. | |
162 // TODO we could probably get away reuse most of the time if the poi
nter is unique, | |
163 // but we'd have to clear the subrun information | |
164 cacheBlob->unref(); | |
165 int glyphCount = 0; | |
166 int runCount = 0; | |
167 BlobGlyphCount(&glyphCount, &runCount, blob); | |
168 cacheBlob = CreateBlob(glyphCount, runCount); | |
169 fCache.set(blob->uniqueID(), cacheBlob); | |
170 this->regenerateTextBlob(cacheBlob, skPaint, viewMatrix, blob, x, y,
drawFilter, | |
171 clipRect); | |
172 } | |
173 } else { | |
174 int glyphCount = 0; | |
175 int runCount = 0; | |
176 BlobGlyphCount(&glyphCount, &runCount, blob); | |
177 cacheBlob = CreateBlob(glyphCount, runCount); | |
178 fCache.set(blob->uniqueID(), cacheBlob); | |
179 this->regenerateTextBlob(cacheBlob, skPaint, viewMatrix, blob, x, y, dra
wFilter, clipRect); | |
180 } | |
181 | |
182 // Though for the time being runs in the textblob can override the paint, th
ey only touch font | |
183 // info. | |
184 GrPaint grPaint; | |
185 SkPaint2GrPaintShader(fContext, rt, skPaint, viewMatrix, true, &grPaint); | |
186 | |
187 this->flush(fContext->getTextTarget(), cacheBlob, rt, grPaint, clip, viewMat
rix, | |
188 fSkPaint.getAlpha()); | |
189 } | |
190 | |
191 void GrBitmapTextContextB::regenerateTextBlob(BitmapTextBlob* cacheBlob, | |
192 const SkPaint& skPaint, const SkMa
trix& viewMatrix, | |
193 const SkTextBlob* blob, SkScalar x
, SkScalar y, | |
194 SkDrawFilter* drawFilter, const Sk
IRect& clipRect) { | |
195 cacheBlob->fViewMatrix = viewMatrix; | |
196 cacheBlob->fX = x; | |
197 cacheBlob->fY = y; | |
198 cacheBlob->fStyle = skPaint.getStyle(); | |
199 | |
200 // Regenerate textblob | |
201 SkPaint runPaint = skPaint; | |
202 SkTextBlob::RunIterator it(blob); | |
203 for (int run = 0; !it.done(); it.next(), run++) { | |
204 int glyphCount = it.glyphCount(); | |
205 size_t textLen = glyphCount * sizeof(uint16_t); | |
206 const SkPoint& offset = it.offset(); | |
207 // applyFontToPaint() always overwrites the exact same attributes, | |
208 // so it is safe to not re-seed the paint for this reason. | |
209 it.applyFontToPaint(&runPaint); | |
210 | |
211 if (drawFilter && !drawFilter->filter(&runPaint, SkDrawFilter::kText_Typ
e)) { | |
212 // A false return from filter() means we should abort the current dr
aw. | |
213 runPaint = skPaint; | |
214 continue; | |
215 } | |
216 | |
217 runPaint.setFlags(fGpuDevice->filterTextFlags(runPaint)); | |
218 | |
219 SkGlyphCache* cache = this->setupCache(&cacheBlob->fRuns[run], runPaint,
viewMatrix); | |
220 | |
221 // setup vertex / glyphIndex for the new run | |
222 if (run > 0) { | |
223 PerSubRunInfo& newRun = cacheBlob->fRuns[run].fSubRunInfo.back(); | |
224 PerSubRunInfo& lastRun = cacheBlob->fRuns[run - 1].fSubRunInfo.back(
); | |
225 | |
226 newRun.fVertexStartIndex = lastRun.fVertexEndIndex; | |
227 newRun.fVertexEndIndex = lastRun.fVertexEndIndex; | |
228 | |
229 newRun.fGlyphStartIndex = lastRun.fGlyphEndIndex; | |
230 newRun.fGlyphEndIndex = lastRun.fGlyphEndIndex; | |
231 } | |
232 | |
233 switch (it.positioning()) { | |
234 case SkTextBlob::kDefault_Positioning: | |
235 this->internalDrawText(cacheBlob, run, cache, runPaint, viewMatr
ix, | |
236 (const char *)it.glyphs(), textLen, | |
237 x + offset.x(), y + offset.y(), clipRect)
; | |
238 break; | |
239 case SkTextBlob::kHorizontal_Positioning: | |
240 this->internalDrawPosText(cacheBlob, run, cache, runPaint, viewM
atrix, | |
241 (const char*)it.glyphs(), textLen, it.
pos(), 1, | |
242 SkPoint::Make(x, y + offset.y()), clip
Rect); | |
243 break; | |
244 case SkTextBlob::kFull_Positioning: | |
245 this->internalDrawPosText(cacheBlob, run, cache, runPaint, viewM
atrix, | |
246 (const char*)it.glyphs(), textLen, it.
pos(), 2, | |
247 SkPoint::Make(x, y), clipRect); | |
248 break; | |
249 } | |
250 | |
251 if (drawFilter) { | |
252 // A draw filter may change the paint arbitrarily, so we must re-see
d in this case. | |
253 runPaint = skPaint; | |
254 } | |
255 | |
256 SkGlyphCache::AttachCache(cache); | |
257 } | |
258 } | |
259 | |
260 void GrBitmapTextContextB::onDrawText(GrRenderTarget* rt, const GrClip& clip, | |
261 const GrPaint& paint, const SkPaint& skPain
t, | |
262 const SkMatrix& viewMatrix, | |
263 const char text[], size_t byteLength, | |
264 SkScalar x, SkScalar y, const SkIRect& regi
onClipBounds) { | |
265 int glyphCount = skPaint.countText(text, byteLength); | |
266 SkAutoTUnref<BitmapTextBlob> blob(CreateBlob(glyphCount, 1)); | |
267 blob->fViewMatrix = viewMatrix; | |
268 blob->fX = x; | |
269 blob->fY = y; | |
270 blob->fStyle = skPaint.getStyle(); | |
271 | |
272 SkIRect clipRect; | |
273 clip.getConservativeBounds(rt->width(), rt->height(), &clipRect); | |
274 | |
275 // setup cache | |
276 SkGlyphCache* cache = this->setupCache(&blob->fRuns[0], skPaint, viewMatrix)
; | |
277 this->internalDrawText(blob, 0, cache, skPaint, viewMatrix, text, byteLength
, x, y, clipRect); | |
278 SkGlyphCache::AttachCache(cache); | |
279 | |
280 this->flush(fContext->getTextTarget(), blob, rt, paint, clip, viewMatrix, sk
Paint.getAlpha()); | |
281 } | |
282 | |
283 void GrBitmapTextContextB::internalDrawText(BitmapTextBlob* blob, int runIndex, | |
284 SkGlyphCache* cache, const SkPaint&
skPaint, | |
285 const SkMatrix& viewMatrix, | |
286 const char text[], size_t byteLength, | |
287 SkScalar x, SkScalar y, const SkIRect
& clipRect) { | |
288 SkASSERT(byteLength == 0 || text != NULL); | |
289 | |
290 // nothing to draw | |
291 if (text == NULL || byteLength == 0) { | |
292 return; | |
293 } | |
294 | |
295 fCurrStrike = NULL; | |
296 SkDrawCacheProc glyphCacheProc = skPaint.getDrawCacheProc(); | |
297 | |
298 // Get GrFontScaler from cache | |
299 GrFontScaler* fontScaler = GetGrFontScaler(cache); | |
300 | |
301 // transform our starting point | |
302 { | |
303 SkPoint loc; | |
304 viewMatrix.mapXY(x, y, &loc); | |
305 x = loc.fX; | |
306 y = loc.fY; | |
307 } | |
308 | |
309 // need to measure first | |
310 if (skPaint.getTextAlign() != SkPaint::kLeft_Align) { | |
311 SkVector stopVector; | |
312 MeasureText(cache, glyphCacheProc, text, byteLength, &stopVector); | |
313 | |
314 SkScalar stopX = stopVector.fX; | |
315 SkScalar stopY = stopVector.fY; | |
316 | |
317 if (skPaint.getTextAlign() == SkPaint::kCenter_Align) { | |
318 stopX = SkScalarHalf(stopX); | |
319 stopY = SkScalarHalf(stopY); | |
320 } | |
321 x -= stopX; | |
322 y -= stopY; | |
323 } | |
324 | |
325 const char* stop = text + byteLength; | |
326 | |
327 SkAutoKern autokern; | |
328 | |
329 SkFixed fxMask = ~0; | |
330 SkFixed fyMask = ~0; | |
331 SkScalar halfSampleX, halfSampleY; | |
332 if (cache->isSubpixel()) { | |
333 halfSampleX = halfSampleY = SkFixedToScalar(SkGlyph::kSubpixelRound); | |
334 SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(viewMatrix); | |
335 if (kX_SkAxisAlignment == baseline) { | |
336 fyMask = 0; | |
337 halfSampleY = SK_ScalarHalf; | |
338 } else if (kY_SkAxisAlignment == baseline) { | |
339 fxMask = 0; | |
340 halfSampleX = SK_ScalarHalf; | |
341 } | |
342 } else { | |
343 halfSampleX = halfSampleY = SK_ScalarHalf; | |
344 } | |
345 | |
346 Sk48Dot16 fx = SkScalarTo48Dot16(x + halfSampleX); | |
347 Sk48Dot16 fy = SkScalarTo48Dot16(y + halfSampleY); | |
348 | |
349 while (text < stop) { | |
350 const SkGlyph& glyph = glyphCacheProc(cache, &text, fx & fxMask, fy & fy
Mask); | |
351 | |
352 fx += autokern.adjust(glyph); | |
353 | |
354 if (glyph.fWidth) { | |
355 this->appendGlyph(blob, | |
356 runIndex, | |
357 GrGlyph::Pack(glyph.getGlyphID(), | |
358 glyph.getSubXFixed(), | |
359 glyph.getSubYFixed(), | |
360 GrGlyph::kCoverage_MaskStyle), | |
361 Sk48Dot16FloorToInt(fx), | |
362 Sk48Dot16FloorToInt(fy), | |
363 fontScaler, | |
364 clipRect); | |
365 } | |
366 | |
367 fx += glyph.fAdvanceX; | |
368 fy += glyph.fAdvanceY; | |
369 } | |
370 } | |
371 | |
372 void GrBitmapTextContextB::onDrawPosText(GrRenderTarget* rt, const GrClip& clip, | |
373 const GrPaint& paint, const SkPaint& skP
aint, | |
374 const SkMatrix& viewMatrix, | |
375 const char text[], size_t byteLength, | |
376 const SkScalar pos[], int scalarsPerPosi
tion, | |
377 const SkPoint& offset, const SkIRect& re
gionClipBounds) { | |
378 int glyphCount = skPaint.countText(text, byteLength); | |
379 SkAutoTUnref<BitmapTextBlob> blob(CreateBlob(glyphCount, 1)); | |
380 blob->fStyle = skPaint.getStyle(); | |
381 blob->fViewMatrix = viewMatrix; | |
382 | |
383 SkIRect clipRect; | |
384 clip.getConservativeBounds(rt->width(), rt->height(), &clipRect); | |
385 | |
386 // setup cache | |
387 SkGlyphCache* cache = this->setupCache(&blob->fRuns[0], skPaint, viewMatrix)
; | |
388 this->internalDrawPosText(blob, 0, cache, skPaint, viewMatrix, text, byteLen
gth, pos, | |
389 scalarsPerPosition, offset, clipRect); | |
390 SkGlyphCache::AttachCache(cache); | |
391 | |
392 this->flush(fContext->getTextTarget(), blob, rt, paint, clip, viewMatrix, fS
kPaint.getAlpha()); | |
393 } | |
394 | |
395 void GrBitmapTextContextB::internalDrawPosText(BitmapTextBlob* blob, int runInde
x, | |
396 SkGlyphCache* cache, const SkPain
t& skPaint, | |
397 const SkMatrix& viewMatrix, | |
398 const char text[], size_t byteLeng
th, | |
399 const SkScalar pos[], int scalarsP
erPosition, | |
400 const SkPoint& offset, const SkIRe
ct& clipRect) { | |
401 SkASSERT(byteLength == 0 || text != NULL); | |
402 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition); | |
403 | |
404 // nothing to draw | |
405 if (text == NULL || byteLength == 0) { | |
406 return; | |
407 } | |
408 | |
409 fCurrStrike = NULL; | |
410 SkDrawCacheProc glyphCacheProc = skPaint.getDrawCacheProc(); | |
411 | |
412 // Get GrFontScaler from cache | |
413 GrFontScaler* fontScaler = GetGrFontScaler(cache); | |
414 | |
415 const char* stop = text + byteLength; | |
416 SkTextAlignProc alignProc(skPaint.getTextAlign()); | |
417 SkTextMapStateProc tmsProc(viewMatrix, offset, scalarsPerPosition); | |
418 SkScalar halfSampleX = 0, halfSampleY = 0; | |
419 | |
420 if (cache->isSubpixel()) { | |
421 // maybe we should skip the rounding if linearText is set | |
422 SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(viewMatrix); | |
423 | |
424 SkFixed fxMask = ~0; | |
425 SkFixed fyMask = ~0; | |
426 if (kX_SkAxisAlignment == baseline) { | |
427 fyMask = 0; | |
428 halfSampleY = SK_ScalarHalf; | |
429 } else if (kY_SkAxisAlignment == baseline) { | |
430 fxMask = 0; | |
431 halfSampleX = SK_ScalarHalf; | |
432 } | |
433 | |
434 if (SkPaint::kLeft_Align == skPaint.getTextAlign()) { | |
435 while (text < stop) { | |
436 SkPoint tmsLoc; | |
437 tmsProc(pos, &tmsLoc); | |
438 Sk48Dot16 fx = SkScalarTo48Dot16(tmsLoc.fX + halfSampleX); | |
439 Sk48Dot16 fy = SkScalarTo48Dot16(tmsLoc.fY + halfSampleY); | |
440 | |
441 const SkGlyph& glyph = glyphCacheProc(cache, &text, | |
442 fx & fxMask, fy & fyMask); | |
443 | |
444 if (glyph.fWidth) { | |
445 this->appendGlyph(blob, | |
446 runIndex, | |
447 GrGlyph::Pack(glyph.getGlyphID(), | |
448 glyph.getSubXFixed(), | |
449 glyph.getSubYFixed(), | |
450 GrGlyph::kCoverage_MaskStyle
), | |
451 Sk48Dot16FloorToInt(fx), | |
452 Sk48Dot16FloorToInt(fy), | |
453 fontScaler, | |
454 clipRect); | |
455 } | |
456 pos += scalarsPerPosition; | |
457 } | |
458 } else { | |
459 while (text < stop) { | |
460 const char* currentText = text; | |
461 const SkGlyph& metricGlyph = glyphCacheProc(cache, &text, 0, 0); | |
462 | |
463 if (metricGlyph.fWidth) { | |
464 SkDEBUGCODE(SkFixed prevAdvX = metricGlyph.fAdvanceX;) | |
465 SkDEBUGCODE(SkFixed prevAdvY = metricGlyph.fAdvanceY;) | |
466 SkPoint tmsLoc; | |
467 tmsProc(pos, &tmsLoc); | |
468 SkPoint alignLoc; | |
469 alignProc(tmsLoc, metricGlyph, &alignLoc); | |
470 | |
471 Sk48Dot16 fx = SkScalarTo48Dot16(alignLoc.fX + halfSampleX); | |
472 Sk48Dot16 fy = SkScalarTo48Dot16(alignLoc.fY + halfSampleY); | |
473 | |
474 // have to call again, now that we've been "aligned" | |
475 const SkGlyph& glyph = glyphCacheProc(cache, ¤tText, | |
476 fx & fxMask, fy & fyMa
sk); | |
477 // the assumption is that the metrics haven't changed | |
478 SkASSERT(prevAdvX == glyph.fAdvanceX); | |
479 SkASSERT(prevAdvY == glyph.fAdvanceY); | |
480 SkASSERT(glyph.fWidth); | |
481 | |
482 this->appendGlyph(blob, | |
483 runIndex, | |
484 GrGlyph::Pack(glyph.getGlyphID(), | |
485 glyph.getSubXFixed(), | |
486 glyph.getSubYFixed(), | |
487 GrGlyph::kCoverage_MaskStyle
), | |
488 Sk48Dot16FloorToInt(fx), | |
489 Sk48Dot16FloorToInt(fy), | |
490 fontScaler, | |
491 clipRect); | |
492 } | |
493 pos += scalarsPerPosition; | |
494 } | |
495 } | |
496 } else { // not subpixel | |
497 | |
498 if (SkPaint::kLeft_Align == skPaint.getTextAlign()) { | |
499 while (text < stop) { | |
500 // the last 2 parameters are ignored | |
501 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0); | |
502 | |
503 if (glyph.fWidth) { | |
504 SkPoint tmsLoc; | |
505 tmsProc(pos, &tmsLoc); | |
506 | |
507 Sk48Dot16 fx = SkScalarTo48Dot16(tmsLoc.fX + SK_ScalarHalf);
//halfSampleX; | |
508 Sk48Dot16 fy = SkScalarTo48Dot16(tmsLoc.fY + SK_ScalarHalf);
//halfSampleY; | |
509 this->appendGlyph(blob, | |
510 runIndex, | |
511 GrGlyph::Pack(glyph.getGlyphID(), | |
512 glyph.getSubXFixed(), | |
513 glyph.getSubYFixed(), | |
514 GrGlyph::kCoverage_MaskStyle
), | |
515 Sk48Dot16FloorToInt(fx), | |
516 Sk48Dot16FloorToInt(fy), | |
517 fontScaler, | |
518 clipRect); | |
519 } | |
520 pos += scalarsPerPosition; | |
521 } | |
522 } else { | |
523 while (text < stop) { | |
524 // the last 2 parameters are ignored | |
525 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0); | |
526 | |
527 if (glyph.fWidth) { | |
528 SkPoint tmsLoc; | |
529 tmsProc(pos, &tmsLoc); | |
530 | |
531 SkPoint alignLoc; | |
532 alignProc(tmsLoc, glyph, &alignLoc); | |
533 | |
534 Sk48Dot16 fx = SkScalarTo48Dot16(alignLoc.fX + SK_ScalarHalf
); //halfSampleX; | |
535 Sk48Dot16 fy = SkScalarTo48Dot16(alignLoc.fY + SK_ScalarHalf
); //halfSampleY; | |
536 this->appendGlyph(blob, | |
537 runIndex, | |
538 GrGlyph::Pack(glyph.getGlyphID(), | |
539 glyph.getSubXFixed(), | |
540 glyph.getSubYFixed(), | |
541 GrGlyph::kCoverage_MaskStyle
), | |
542 Sk48Dot16FloorToInt(fx), | |
543 Sk48Dot16FloorToInt(fy), | |
544 fontScaler, | |
545 clipRect); | |
546 } | |
547 pos += scalarsPerPosition; | |
548 } | |
549 } | |
550 } | |
551 } | |
552 | 51 |
553 static size_t get_vertex_stride(GrMaskFormat maskFormat) { | 52 static size_t get_vertex_stride(GrMaskFormat maskFormat) { |
554 switch (maskFormat) { | 53 switch (maskFormat) { |
555 case kA8_GrMaskFormat: | 54 case kA8_GrMaskFormat: |
556 return kGrayTextVASize; | 55 return kGrayTextVASize; |
557 case kARGB_GrMaskFormat: | 56 case kARGB_GrMaskFormat: |
558 return kColorTextVASize; | 57 return kColorTextVASize; |
559 default: | 58 default: |
560 return kLCDTextVASize; | 59 return kLCDTextVASize; |
561 } | 60 } |
562 } | 61 } |
563 | 62 |
564 void GrBitmapTextContextB::appendGlyph(BitmapTextBlob* blob, int runIndex, GrGly
ph::PackedID packed, | |
565 int vx, int vy, GrFontScaler* scaler, | |
566 const SkIRect& clipRect) { | |
567 if (NULL == fCurrStrike) { | |
568 fCurrStrike = fContext->getBatchFontCache()->getStrike(scaler); | |
569 } | |
570 | |
571 GrGlyph* glyph = fCurrStrike->getGlyph(packed, scaler); | |
572 if (NULL == glyph || glyph->fBounds.isEmpty()) { | |
573 return; | |
574 } | |
575 | |
576 int x = vx + glyph->fBounds.fLeft; | |
577 int y = vy + glyph->fBounds.fTop; | |
578 | |
579 // keep them as ints until we've done the clip-test | |
580 int width = glyph->fBounds.width(); | |
581 int height = glyph->fBounds.height(); | |
582 | |
583 // check if we clipped out | |
584 if (clipRect.quickReject(x, y, x + width, y + height)) { | |
585 return; | |
586 } | |
587 | |
588 // If the glyph is too large we fall back to paths | |
589 if (fCurrStrike->glyphTooLargeForAtlas(glyph)) { | |
590 if (NULL == glyph->fPath) { | |
591 SkPath* path = SkNEW(SkPath); | |
592 if (!scaler->getGlyphPath(glyph->glyphID(), path)) { | |
593 // flag the glyph as being dead? | |
594 SkDELETE(path); | |
595 return; | |
596 } | |
597 glyph->fPath = path; | |
598 } | |
599 SkASSERT(glyph->fPath); | |
600 blob->fBigGlyphs.push_back(BitmapTextBlob::BigGlyph(*glyph->fPath, vx, v
y)); | |
601 return; | |
602 } | |
603 | |
604 Run& run = blob->fRuns[runIndex]; | |
605 | |
606 GrMaskFormat format = glyph->fMaskFormat; | |
607 | |
608 PerSubRunInfo* subRun = &run.fSubRunInfo.back(); | |
609 if (run.fInitialized && subRun->fMaskFormat != format) { | |
610 PerSubRunInfo* newSubRun = &run.fSubRunInfo.push_back(); | |
611 newSubRun->fGlyphStartIndex = subRun->fGlyphEndIndex; | |
612 newSubRun->fGlyphEndIndex = subRun->fGlyphEndIndex; | |
613 | |
614 newSubRun->fVertexStartIndex = subRun->fVertexEndIndex; | |
615 newSubRun->fVertexEndIndex = subRun->fVertexEndIndex; | |
616 | |
617 subRun = newSubRun; | |
618 } | |
619 | |
620 run.fInitialized = true; | |
621 subRun->fMaskFormat = format; | |
622 blob->fGlyphIDs[subRun->fGlyphEndIndex] = packed; | |
623 | |
624 size_t vertexStride = get_vertex_stride(format); | |
625 | |
626 SkRect r; | |
627 r.fLeft = SkIntToScalar(x); | |
628 r.fTop = SkIntToScalar(y); | |
629 r.fRight = r.fLeft + SkIntToScalar(width); | |
630 r.fBottom = r.fTop + SkIntToScalar(height); | |
631 | |
632 run.fVertexBounds.joinNonEmptyArg(r); | |
633 GrColor color = fPaint.getColor(); | |
634 run.fColor = color; | |
635 | |
636 intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVertices + subRun->fVert
exEndIndex); | |
637 | |
638 // V0 | |
639 SkPoint* position = reinterpret_cast<SkPoint*>(vertex); | |
640 position->set(r.fLeft, r.fTop); | |
641 if (kA8_GrMaskFormat == format) { | |
642 SkColor* colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint))
; | |
643 *colorPtr = color; | |
644 } | |
645 vertex += vertexStride; | |
646 | |
647 // V1 | |
648 position = reinterpret_cast<SkPoint*>(vertex); | |
649 position->set(r.fLeft, r.fBottom); | |
650 if (kA8_GrMaskFormat == format) { | |
651 SkColor* colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint))
; | |
652 *colorPtr = color; | |
653 } | |
654 vertex += vertexStride; | |
655 | |
656 // V2 | |
657 position = reinterpret_cast<SkPoint*>(vertex); | |
658 position->set(r.fRight, r.fBottom); | |
659 if (kA8_GrMaskFormat == format) { | |
660 SkColor* colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint))
; | |
661 *colorPtr = color; | |
662 } | |
663 vertex += vertexStride; | |
664 | |
665 // V3 | |
666 position = reinterpret_cast<SkPoint*>(vertex); | |
667 position->set(r.fRight, r.fTop); | |
668 if (kA8_GrMaskFormat == format) { | |
669 SkColor* colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint))
; | |
670 *colorPtr = color; | |
671 } | |
672 | |
673 subRun->fGlyphEndIndex++; | |
674 subRun->fVertexEndIndex += vertexStride * kVerticesPerGlyph; | |
675 } | |
676 | |
677 class BitmapTextBatch : public GrBatch { | |
678 public: | |
679 typedef GrBitmapTextContextB::BitmapTextBlob Blob; | |
680 typedef Blob::Run Run; | |
681 typedef Run::SubRunInfo TextInfo; | |
682 struct Geometry { | |
683 Geometry() {} | |
684 Geometry(const Geometry& geometry) | |
685 : fBlob(SkRef(geometry.fBlob.get())) | |
686 , fRun(geometry.fRun) | |
687 , fSubRun(geometry.fSubRun) | |
688 , fColor(geometry.fColor) {} | |
689 SkAutoTUnref<Blob> fBlob; | |
690 int fRun; | |
691 int fSubRun; | |
692 GrColor fColor; | |
693 }; | |
694 | |
695 static GrBatch* Create(const Geometry& geometry, GrColor color, GrMaskFormat
maskFormat, | |
696 int glyphCount, GrBatchFontCache* fontCache) { | |
697 return SkNEW_ARGS(BitmapTextBatch, (geometry, color, maskFormat, glyphCo
unt, fontCache)); | |
698 } | |
699 | |
700 const char* name() const override { return "BitmapTextBatch"; } | |
701 | |
702 void getInvariantOutputColor(GrInitInvariantOutput* out) const override { | |
703 if (kARGB_GrMaskFormat == fMaskFormat) { | |
704 out->setUnknownFourComponents(); | |
705 } else { | |
706 out->setKnownFourComponents(fBatch.fColor); | |
707 } | |
708 } | |
709 | |
710 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override { | |
711 if (kARGB_GrMaskFormat != fMaskFormat) { | |
712 if (GrPixelConfigIsAlphaOnly(fPixelConfig)) { | |
713 out->setUnknownSingleComponent(); | |
714 } else if (GrPixelConfigIsOpaque(fPixelConfig)) { | |
715 out->setUnknownOpaqueFourComponents(); | |
716 out->setUsingLCDCoverage(); | |
717 } else { | |
718 out->setUnknownFourComponents(); | |
719 out->setUsingLCDCoverage(); | |
720 } | |
721 } else { | |
722 out->setKnownSingleComponent(0xff); | |
723 } | |
724 } | |
725 | |
726 void initBatchTracker(const GrPipelineInfo& init) override { | |
727 // Handle any color overrides | |
728 if (init.fColorIgnored) { | |
729 fBatch.fColor = GrColor_ILLEGAL; | |
730 } else if (GrColor_ILLEGAL != init.fOverrideColor) { | |
731 fBatch.fColor = init.fOverrideColor; | |
732 } | |
733 | |
734 // setup batch properties | |
735 fBatch.fColorIgnored = init.fColorIgnored; | |
736 fBatch.fUsesLocalCoords = init.fUsesLocalCoords; | |
737 fBatch.fCoverageIgnored = init.fCoverageIgnored; | |
738 } | |
739 | |
740 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline
) override { | |
741 // if we have RGB, then we won't have any SkShaders so no need to use a
localmatrix. | |
742 // TODO actually only invert if we don't have RGBA | |
743 SkMatrix localMatrix; | |
744 if (this->usesLocalCoords() && !this->viewMatrix().invert(&localMatrix))
{ | |
745 SkDebugf("Cannot invert viewmatrix\n"); | |
746 return; | |
747 } | |
748 | |
749 GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kNone
_FilterMode); | |
750 // This will be ignored in the non A8 case | |
751 bool opaqueVertexColors = GrColorIsOpaque(this->color()); | |
752 SkAutoTUnref<const GrGeometryProcessor> gp( | |
753 GrBitmapTextGeoProc::Create(this->color(), | |
754 fFontCache->getTexture(fMaskFormat), | |
755 params, | |
756 fMaskFormat, | |
757 opaqueVertexColors, | |
758 localMatrix)); | |
759 | |
760 size_t vertexStride = gp->getVertexStride(); | |
761 SkASSERT(vertexStride == get_vertex_stride(fMaskFormat)); | |
762 | |
763 this->initDraw(batchTarget, gp, pipeline); | |
764 | |
765 int glyphCount = this->numGlyphs(); | |
766 int instanceCount = fGeoData.count(); | |
767 const GrVertexBuffer* vertexBuffer; | |
768 int firstVertex; | |
769 | |
770 void* vertices = batchTarget->vertexPool()->makeSpace(vertexStride, | |
771 glyphCount * kVert
icesPerGlyph, | |
772 &vertexBuffer, | |
773 &firstVertex); | |
774 if (!vertices) { | |
775 SkDebugf("Could not allocate vertices\n"); | |
776 return; | |
777 } | |
778 | |
779 unsigned char* currVertex = reinterpret_cast<unsigned char*>(vertices); | |
780 | |
781 // setup drawinfo | |
782 const GrIndexBuffer* quadIndexBuffer = batchTarget->quadIndexBuffer(); | |
783 int maxInstancesPerDraw = quadIndexBuffer->maxQuads(); | |
784 | |
785 GrDrawTarget::DrawInfo drawInfo; | |
786 drawInfo.setPrimitiveType(kTriangles_GrPrimitiveType); | |
787 drawInfo.setStartVertex(0); | |
788 drawInfo.setStartIndex(0); | |
789 drawInfo.setVerticesPerInstance(kVerticesPerGlyph); | |
790 drawInfo.setIndicesPerInstance(kIndicesPerGlyph); | |
791 drawInfo.adjustStartVertex(firstVertex); | |
792 drawInfo.setVertexBuffer(vertexBuffer); | |
793 drawInfo.setIndexBuffer(quadIndexBuffer); | |
794 | |
795 int instancesToFlush = 0; | |
796 for (int i = 0; i < instanceCount; i++) { | |
797 Geometry& args = fGeoData[i]; | |
798 Blob* blob = args.fBlob; | |
799 Run& run = blob->fRuns[args.fRun]; | |
800 TextInfo& info = run.fSubRunInfo[args.fSubRun]; | |
801 | |
802 uint64_t currentAtlasGen = fFontCache->atlasGeneration(fMaskFormat); | |
803 bool regenerateTextureCoords = info.fAtlasGeneration != currentAtlas
Gen; | |
804 bool regenerateColors = kA8_GrMaskFormat == fMaskFormat && run.fColo
r != args.fColor; | |
805 int glyphCount = info.fGlyphEndIndex - info.fGlyphStartIndex; | |
806 | |
807 // We regenerate both texture coords and colors in the blob itself,
and update the | |
808 // atlas generation. If we don't end up purging any unused plots, w
e can avoid | |
809 // regenerating the coords. We could take a finer grained approach
to updating texture | |
810 // coords but its not clear if the extra bookkeeping would offset an
y gains. | |
811 // To avoid looping over the glyphs twice, we do one loop and condit
ionally update color | |
812 // or coords as needed. One final note, if we have to break a run f
or an atlas eviction | |
813 // then we can't really trust the atlas has all of the correct data.
Atlas evictions | |
814 // should be pretty rare, so we just always regenerate in those case
s | |
815 if (regenerateTextureCoords || regenerateColors) { | |
816 // first regenerate texture coordinates / colors if need be | |
817 const SkDescriptor* desc = NULL; | |
818 SkGlyphCache* cache = NULL; | |
819 GrFontScaler* scaler = NULL; | |
820 GrBatchTextStrike* strike = NULL; | |
821 bool brokenRun = false; | |
822 if (regenerateTextureCoords) { | |
823 desc = run.fDescriptor.getDesc(); | |
824 cache = SkGlyphCache::DetachCache(run.fTypeface, desc); | |
825 scaler = GrTextContext::GetGrFontScaler(cache); | |
826 strike = fFontCache->getStrike(scaler); | |
827 } | |
828 for (int glyphIdx = 0; glyphIdx < glyphCount; glyphIdx++) { | |
829 GrGlyph::PackedID glyphID = blob->fGlyphIDs[glyphIdx + info.
fGlyphStartIndex]; | |
830 | |
831 if (regenerateTextureCoords) { | |
832 // Upload the glyph only if needed | |
833 GrGlyph* glyph = strike->getGlyph(glyphID, scaler); | |
834 SkASSERT(glyph); | |
835 | |
836 if (!fFontCache->hasGlyph(glyph) && | |
837 !strike->addGlyphToAtlas(batchTarget, glyph, scaler)
) { | |
838 this->flush(batchTarget, &drawInfo, instancesToFlush
, | |
839 maxInstancesPerDraw); | |
840 this->initDraw(batchTarget, gp, pipeline); | |
841 instancesToFlush = 0; | |
842 brokenRun = glyphIdx > 0; | |
843 | |
844 SkDEBUGCODE(bool success =) strike->addGlyphToAtlas(
batchTarget, glyph, | |
845
scaler); | |
846 SkASSERT(success); | |
847 } | |
848 | |
849 fFontCache->setGlyphRefToken(glyph, batchTarget->current
Token()); | |
850 | |
851 // Texture coords are the last vertex attribute so we ge
t a pointer to the | |
852 // first one and then map with stride in regenerateTextu
reCoords | |
853 intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVert
ices); | |
854 vertex += info.fVertexStartIndex; | |
855 vertex += vertexStride * glyphIdx * kVerticesPerGlyph; | |
856 vertex += vertexStride - sizeof(SkIPoint16); | |
857 | |
858 this->regenerateTextureCoords(glyph, vertex, vertexStrid
e); | |
859 } | |
860 | |
861 if (regenerateColors) { | |
862 intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVert
ices); | |
863 vertex += info.fVertexStartIndex; | |
864 vertex += vertexStride * glyphIdx * kVerticesPerGlyph +
sizeof(SkPoint); | |
865 this->regenerateColors(vertex, vertexStride, args.fColor
); | |
866 } | |
867 | |
868 instancesToFlush++; | |
869 } | |
870 | |
871 if (regenerateTextureCoords) { | |
872 SkGlyphCache::AttachCache(cache); | |
873 info.fAtlasGeneration = brokenRun ? GrBatchAtlas::kInvalidAt
lasGeneration : | |
874 fFontCache->atlasGenerat
ion(fMaskFormat); | |
875 } | |
876 } else { | |
877 instancesToFlush += glyphCount; | |
878 } | |
879 | |
880 // now copy all vertices | |
881 size_t byteCount = info.fVertexEndIndex - info.fVertexStartIndex; | |
882 memcpy(currVertex, blob->fVertices + info.fVertexStartIndex, byteCou
nt); | |
883 | |
884 currVertex += byteCount; | |
885 } | |
886 | |
887 this->flush(batchTarget, &drawInfo, instancesToFlush, maxInstancesPerDra
w); | |
888 } | |
889 | |
890 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; } | |
891 | |
892 private: | |
893 BitmapTextBatch(const Geometry& geometry, GrColor color, GrMaskFormat maskFo
rmat, | |
894 int glyphCount, GrBatchFontCache* fontCache) | |
895 : fMaskFormat(maskFormat) | |
896 , fPixelConfig(fontCache->getPixelConfig(maskFormat)) | |
897 , fFontCache(fontCache) { | |
898 this->initClassID<BitmapTextBatch>(); | |
899 fGeoData.push_back(geometry); | |
900 fBatch.fColor = color; | |
901 fBatch.fViewMatrix = geometry.fBlob->fViewMatrix; | |
902 fBatch.fNumGlyphs = glyphCount; | |
903 } | |
904 | |
905 void regenerateTextureCoords(GrGlyph* glyph, intptr_t vertex, size_t vertexS
tride) { | |
906 int width = glyph->fBounds.width(); | |
907 int height = glyph->fBounds.height(); | |
908 int u0 = glyph->fAtlasLocation.fX; | |
909 int v0 = glyph->fAtlasLocation.fY; | |
910 int u1 = u0 + width; | |
911 int v1 = v0 + height; | |
912 | |
913 // we assume texture coords are the last vertex attribute, this is a bit
fragile. | |
914 // TODO pass in this offset or something | |
915 SkIPoint16* textureCoords; | |
916 // V0 | |
917 textureCoords = reinterpret_cast<SkIPoint16*>(vertex); | |
918 textureCoords->set(u0, v0); | |
919 vertex += vertexStride; | |
920 | |
921 // V1 | |
922 textureCoords = reinterpret_cast<SkIPoint16*>(vertex); | |
923 textureCoords->set(u0, v1); | |
924 vertex += vertexStride; | |
925 | |
926 // V2 | |
927 textureCoords = reinterpret_cast<SkIPoint16*>(vertex); | |
928 textureCoords->set(u1, v1); | |
929 vertex += vertexStride; | |
930 | |
931 // V3 | |
932 textureCoords = reinterpret_cast<SkIPoint16*>(vertex); | |
933 textureCoords->set(u1, v0); | |
934 } | |
935 | |
936 void regenerateColors(intptr_t vertex, size_t vertexStride, GrColor color) { | |
937 for (int i = 0; i < kVerticesPerGlyph; i++) { | |
938 SkColor* vcolor = reinterpret_cast<SkColor*>(vertex); | |
939 *vcolor = color; | |
940 vertex += vertexStride; | |
941 } | |
942 } | |
943 | |
944 void initDraw(GrBatchTarget* batchTarget, | |
945 const GrGeometryProcessor* gp, | |
946 const GrPipeline* pipeline) { | |
947 batchTarget->initDraw(gp, pipeline); | |
948 | |
949 // TODO remove this when batch is everywhere | |
950 GrPipelineInfo init; | |
951 init.fColorIgnored = fBatch.fColorIgnored; | |
952 init.fOverrideColor = GrColor_ILLEGAL; | |
953 init.fCoverageIgnored = fBatch.fCoverageIgnored; | |
954 init.fUsesLocalCoords = this->usesLocalCoords(); | |
955 gp->initBatchTracker(batchTarget->currentBatchTracker(), init); | |
956 } | |
957 | |
958 void flush(GrBatchTarget* batchTarget, | |
959 GrDrawTarget::DrawInfo* drawInfo, | |
960 int instanceCount, | |
961 int maxInstancesPerDraw) { | |
962 while (instanceCount) { | |
963 drawInfo->setInstanceCount(SkTMin(instanceCount, maxInstancesPerDraw
)); | |
964 drawInfo->setVertexCount(drawInfo->instanceCount() * drawInfo->verti
cesPerInstance()); | |
965 drawInfo->setIndexCount(drawInfo->instanceCount() * drawInfo->indice
sPerInstance()); | |
966 | |
967 batchTarget->draw(*drawInfo); | |
968 | |
969 drawInfo->setStartVertex(drawInfo->startVertex() + drawInfo->vertexC
ount()); | |
970 instanceCount -= drawInfo->instanceCount(); | |
971 } | |
972 } | |
973 | |
974 GrColor color() const { return fBatch.fColor; } | |
975 const SkMatrix& viewMatrix() const { return fBatch.fViewMatrix; } | |
976 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; } | |
977 int numGlyphs() const { return fBatch.fNumGlyphs; } | |
978 | |
979 bool onCombineIfPossible(GrBatch* t) override { | |
980 BitmapTextBatch* that = t->cast<BitmapTextBatch>(); | |
981 | |
982 if (this->fMaskFormat != that->fMaskFormat) { | |
983 return false; | |
984 } | |
985 | |
986 if (this->fMaskFormat != kA8_GrMaskFormat && this->color() != that->colo
r()) { | |
987 return false; | |
988 } | |
989 | |
990 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->vi
ewMatrix())) { | |
991 return false; | |
992 } | |
993 | |
994 fBatch.fNumGlyphs += that->numGlyphs(); | |
995 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin())
; | |
996 return true; | |
997 } | |
998 | |
999 struct BatchTracker { | |
1000 GrColor fColor; | |
1001 SkMatrix fViewMatrix; | |
1002 bool fUsesLocalCoords; | |
1003 bool fColorIgnored; | |
1004 bool fCoverageIgnored; | |
1005 int fNumGlyphs; | |
1006 }; | |
1007 | |
1008 BatchTracker fBatch; | |
1009 SkSTArray<1, Geometry, true> fGeoData; | |
1010 GrMaskFormat fMaskFormat; | |
1011 GrPixelConfig fPixelConfig; | |
1012 GrBatchFontCache* fFontCache; | |
1013 }; | 63 }; |
1014 | 64 |
1015 void GrBitmapTextContextB::flush(GrDrawTarget* target, BitmapTextBlob* blob, GrR
enderTarget* rt, | |
1016 const GrPaint& paint, const GrClip& clip, | |
1017 const SkMatrix& viewMatrix, int paintAlpha) { | |
1018 GrPipelineBuilder pipelineBuilder; | |
1019 pipelineBuilder.setFromPaint(paint, rt, clip); | |
1020 | |
1021 GrColor color = paint.getColor(); | |
1022 for (uint32_t run = 0; run < blob->fRunCount; run++) { | |
1023 for (int subRun = 0; subRun < blob->fRuns[run].fSubRunInfo.count(); subR
un++) { | |
1024 PerSubRunInfo& info = blob->fRuns[run].fSubRunInfo[subRun]; | |
1025 int glyphCount = info.fGlyphEndIndex - info.fGlyphStartIndex; | |
1026 if (0 == glyphCount) { | |
1027 continue; | |
1028 } | |
1029 | |
1030 GrMaskFormat format = info.fMaskFormat; | |
1031 if (kARGB_GrMaskFormat == format) { | |
1032 color = SkColorSetARGB(paintAlpha, paintAlpha, paintAlpha, paint
Alpha); | |
1033 } | |
1034 | |
1035 BitmapTextBatch::Geometry geometry; | |
1036 geometry.fBlob.reset(SkRef(blob)); | |
1037 geometry.fRun = run; | |
1038 geometry.fSubRun = subRun; | |
1039 geometry.fColor = color; | |
1040 SkAutoTUnref<GrBatch> batch(BitmapTextBatch::Create(geometry, color,
format, glyphCount, | |
1041 fContext->getBat
chFontCache())); | |
1042 | |
1043 target->drawBatch(&pipelineBuilder, batch, &blob->fRuns[run].fVertex
Bounds); | |
1044 } | |
1045 } | |
1046 | |
1047 // Now flush big glyphs | |
1048 for (int i = 0; i < blob->fBigGlyphs.count(); i++) { | |
1049 BitmapTextBlob::BigGlyph& bigGlyph = blob->fBigGlyphs[i]; | |
1050 SkMatrix translate; | |
1051 translate.setTranslate(SkIntToScalar(bigGlyph.fVx), SkIntToScalar(bigGly
ph.fVy)); | |
1052 SkPath tmpPath(bigGlyph.fPath); | |
1053 tmpPath.transform(translate); | |
1054 GrStrokeInfo strokeInfo(SkStrokeRec::kFill_InitStyle); | |
1055 fContext->drawPath(rt, clip, paint, SkMatrix::I(), tmpPath, strokeInfo); | |
1056 } | |
1057 } | |
1058 | |
1059 GrBitmapTextContext::GrBitmapTextContext(GrContext* context, | 65 GrBitmapTextContext::GrBitmapTextContext(GrContext* context, |
1060 SkGpuDevice* gpuDevice, | 66 SkGpuDevice* gpuDevice, |
1061 const SkDeviceProperties& properties) | 67 const SkDeviceProperties& properties) |
1062 : GrTextContext(context, gpuDevice, properties) { | 68 : GrTextContext(context, gpuDevice, properties) { |
1063 fStrike = NULL; | 69 fStrike = NULL; |
1064 | 70 |
1065 fCurrTexture = NULL; | 71 fCurrTexture = NULL; |
1066 fEffectTextureUniqueID = SK_InvalidUniqueID; | 72 fEffectTextureUniqueID = SK_InvalidUniqueID; |
1067 | 73 |
1068 fVertices = NULL; | 74 fVertices = NULL; |
(...skipping 563 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1632 SkSafeSetNull(fCurrTexture); | 638 SkSafeSetNull(fCurrTexture); |
1633 } | 639 } |
1634 } | 640 } |
1635 | 641 |
1636 inline void GrBitmapTextContext::finish() { | 642 inline void GrBitmapTextContext::finish() { |
1637 this->flush(); | 643 this->flush(); |
1638 fTotalVertexCount = 0; | 644 fTotalVertexCount = 0; |
1639 | 645 |
1640 GrTextContext::finish(); | 646 GrTextContext::finish(); |
1641 } | 647 } |
OLD | NEW |