OLD | NEW |
1 /* | 1 /* |
2 * Copyright 2013 Google Inc. | 2 * Copyright 2013 Google Inc. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
6 */ | 6 */ |
| 7 #include "GrBitmapTextContext.h" |
7 | 8 |
8 #include "GrBitmapTextContext.h" | |
9 #include "GrAtlas.h" | 9 #include "GrAtlas.h" |
| 10 #include "GrBatch.h" |
| 11 #include "GrBatchFontCache.h" |
| 12 #include "GrBatchTarget.h" |
10 #include "GrDefaultGeoProcFactory.h" | 13 #include "GrDefaultGeoProcFactory.h" |
11 #include "GrDrawTarget.h" | 14 #include "GrDrawTarget.h" |
12 #include "GrFontCache.h" | 15 #include "GrFontCache.h" |
13 #include "GrFontScaler.h" | 16 #include "GrFontScaler.h" |
14 #include "GrIndexBuffer.h" | 17 #include "GrIndexBuffer.h" |
15 #include "GrStrokeInfo.h" | 18 #include "GrStrokeInfo.h" |
16 #include "GrTexturePriv.h" | 19 #include "GrTexturePriv.h" |
17 | 20 |
18 #include "SkAutoKern.h" | 21 #include "SkAutoKern.h" |
19 #include "SkColorPriv.h" | 22 #include "SkColorPriv.h" |
20 #include "SkDraw.h" | 23 #include "SkDraw.h" |
| 24 #include "SkDrawFilter.h" |
21 #include "SkDrawProcs.h" | 25 #include "SkDrawProcs.h" |
22 #include "SkGlyphCache.h" | 26 #include "SkGlyphCache.h" |
23 #include "SkGpuDevice.h" | 27 #include "SkGpuDevice.h" |
24 #include "SkGr.h" | 28 #include "SkGr.h" |
25 #include "SkPath.h" | 29 #include "SkPath.h" |
26 #include "SkRTConf.h" | 30 #include "SkRTConf.h" |
27 #include "SkStrokeRec.h" | 31 #include "SkStrokeRec.h" |
| 32 #include "SkTextBlob.h" |
28 #include "SkTextMapStateProc.h" | 33 #include "SkTextMapStateProc.h" |
29 | 34 |
30 #include "effects/GrBitmapTextGeoProc.h" | 35 #include "effects/GrBitmapTextGeoProc.h" |
31 #include "effects/GrSimpleTextureEffect.h" | 36 #include "effects/GrSimpleTextureEffect.h" |
32 | 37 |
33 SK_CONF_DECLARE(bool, c_DumpFontCache, "gpu.dumpFontCache", false, | 38 SK_CONF_DECLARE(bool, c_DumpFontCache, "gpu.dumpFontCache", false, |
34 "Dump the contents of the font cache before every purge."); | 39 "Dump the contents of the font cache before every purge."); |
35 | 40 |
36 namespace { | 41 namespace { |
37 static const size_t kLCDTextVASize = sizeof(SkPoint) + sizeof(SkIPoint16); | 42 static const size_t kLCDTextVASize = sizeof(SkPoint) + sizeof(SkIPoint16); |
38 | 43 |
39 // position + local coord | 44 // position + local coord |
40 static const size_t kColorTextVASize = sizeof(SkPoint) + sizeof(SkIPoint16); | 45 static const size_t kColorTextVASize = sizeof(SkPoint) + sizeof(SkIPoint16); |
41 | 46 |
42 static const size_t kGrayTextVASize = sizeof(SkPoint) + sizeof(GrColor) + sizeof
(SkIPoint16); | 47 static const size_t kGrayTextVASize = sizeof(SkPoint) + sizeof(GrColor) + sizeof
(SkIPoint16); |
43 | 48 |
44 static const int kVerticesPerGlyph = 4; | 49 static const int kVerticesPerGlyph = 4; |
45 static const int kIndicesPerGlyph = 6; | 50 static const int kIndicesPerGlyph = 6; |
46 }; | 51 }; |
47 | 52 |
| 53 // TODO |
| 54 // More tests |
| 55 // move to SkCache |
| 56 // handle textblobs where the whole run is larger than the cache size |
| 57 // TODO implement micro speedy hash map for fast refing of glyphs |
| 58 |
| 59 GrBitmapTextContextB::GrBitmapTextContextB(GrContext* context, |
| 60 SkGpuDevice* gpuDevice, |
| 61 const SkDeviceProperties& properties) |
| 62 : INHERITED(context, gpuDevice, propertie
s) { |
| 63 fCurrStrike = NULL; |
| 64 } |
| 65 |
| 66 void GrBitmapTextContextB::ClearCacheEntry(uint32_t key, BitmapTextBlob** blob)
{ |
| 67 (*blob)->unref(); |
| 68 } |
| 69 |
| 70 GrBitmapTextContextB::~GrBitmapTextContextB() { |
| 71 fCache.foreach(&GrBitmapTextContextB::ClearCacheEntry); |
| 72 } |
| 73 |
| 74 GrBitmapTextContextB* GrBitmapTextContextB::Create(GrContext* context, |
| 75 SkGpuDevice* gpuDevice, |
| 76 const SkDeviceProperties& props
) { |
| 77 return SkNEW_ARGS(GrBitmapTextContextB, (context, gpuDevice, props)); |
| 78 } |
| 79 |
| 80 bool GrBitmapTextContextB::canDraw(const 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 |
48 GrBitmapTextContext::GrBitmapTextContext(GrContext* context, | 981 GrBitmapTextContext::GrBitmapTextContext(GrContext* context, |
49 SkGpuDevice* gpuDevice, | 982 SkGpuDevice* gpuDevice, |
50 const SkDeviceProperties& properties) | 983 const SkDeviceProperties& properties) |
51 : GrTextContext(context, gpuDevice, properties) { | 984 : GrTextContext(context, gpuDevice, properties) { |
52 fStrike = NULL; | 985 fStrike = NULL; |
53 | 986 |
54 fCurrTexture = NULL; | 987 fCurrTexture = NULL; |
55 fEffectTextureUniqueID = SK_InvalidUniqueID; | 988 fEffectTextureUniqueID = SK_InvalidUniqueID; |
56 | 989 |
57 fVertices = NULL; | 990 fVertices = NULL; |
(...skipping 287 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
345 fontScaler); | 1278 fontScaler); |
346 } | 1279 } |
347 pos += scalarsPerPosition; | 1280 pos += scalarsPerPosition; |
348 } | 1281 } |
349 } | 1282 } |
350 } | 1283 } |
351 | 1284 |
352 this->finish(); | 1285 this->finish(); |
353 } | 1286 } |
354 | 1287 |
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 | |
366 static void* alloc_vertices(GrDrawTarget* drawTarget, | 1288 static void* alloc_vertices(GrDrawTarget* drawTarget, |
367 int numVertices, | 1289 int numVertices, |
368 GrMaskFormat maskFormat) { | 1290 GrMaskFormat maskFormat) { |
369 if (numVertices <= 0) { | 1291 if (numVertices <= 0) { |
370 return NULL; | 1292 return NULL; |
371 } | 1293 } |
372 | 1294 |
373 // set up attributes | 1295 // set up attributes |
374 void* vertices = NULL; | 1296 void* vertices = NULL; |
375 bool success = drawTarget->reserveVertexAndIndexSpace(numVertices, | 1297 bool success = drawTarget->reserveVertexAndIndexSpace(numVertices, |
376 get_vertex_stride(mask
Format), | 1298 get_vertex_stride(mask
Format), |
377 0, | 1299 0, |
378 &vertices, | 1300 &vertices, |
379 NULL); | 1301 NULL); |
380 GrAlwaysAssert(success); | 1302 GrAlwaysAssert(success); |
381 return vertices; | 1303 return vertices; |
382 } | 1304 } |
383 | 1305 |
384 inline bool GrBitmapTextContext::uploadGlyph(GrGlyph* glyph, GrFontScaler* scale
r) { | 1306 inline bool GrBitmapTextContext::uploadGlyph(GrGlyph* glyph, GrFontScaler* scale
r) { |
385 if (!fStrike->glyphTooLargeForAtlas(glyph)) { | 1307 if (!fStrike->glyphTooLargeForAtlas(glyph)) { |
386 if (fStrike->addGlyphToAtlas(glyph, scaler)) { | 1308 if (fStrike->addGlyphToAtlas(glyph, scaler)) { |
387 return true; | 1309 return true; |
388 } | 1310 } |
389 | 1311 |
390 // try to clear out an unused plot before we flush | 1312 // try to clear out an unused plot before we flush |
391 if (fContext->getFontCache()->freeUnusedPlot(fStrike, glyph) && | 1313 if (fContext->getFontCache()->freeUnusedPlot(fStrike, glyph) && |
392 fStrike->addGlyphToAtlas(glyph, scaler)) { | 1314 fStrike->addGlyphToAtlas(glyph, scaler)) { |
393 return true; | 1315 return true; |
394 } | 1316 } |
395 | 1317 |
396 if (c_DumpFontCache) { | 1318 if (c_DumpFontCache) { |
397 #ifdef SK_DEVELOPER | 1319 #ifdef SK_DEVELOPER |
398 fContext->getFontCache()->dump(); | 1320 fContext->getFontCache()->dump(); |
399 #endif | 1321 #endif |
400 } | 1322 } |
401 | 1323 |
402 // before we purge the cache, we must flush any accumulated draws | 1324 // before we purge the cache, we must flush any accumulated draws |
403 this->flush(); | 1325 this->flush(); |
404 fContext->flush(); | 1326 fContext->flush(); |
405 | 1327 |
406 // we should have an unused plot now | 1328 // we should have an unused plot now |
407 if (fContext->getFontCache()->freeUnusedPlot(fStrike, glyph) && | 1329 if (fContext->getFontCache()->freeUnusedPlot(fStrike, glyph) && |
408 fStrike->addGlyphToAtlas(glyph, scaler)) { | 1330 fStrike->addGlyphToAtlas(glyph, scaler)) { |
409 return true; | 1331 return true; |
410 } | 1332 } |
411 | 1333 |
412 // we should never get here | 1334 // we should never get here |
413 SkASSERT(false); | 1335 SkASSERT(false); |
414 } | 1336 } |
415 | 1337 |
416 return false; | 1338 return false; |
417 } | 1339 } |
418 | 1340 |
419 void GrBitmapTextContext::appendGlyph(GrGlyph::PackedID packed, | 1341 void GrBitmapTextContext::appendGlyph(GrGlyph::PackedID packed, |
420 int vx, int vy, | 1342 int vx, int vy, |
421 GrFontScaler* scaler) { | 1343 GrFontScaler* scaler) { |
422 if (NULL == fDrawTarget) { | 1344 if (NULL == fDrawTarget) { |
423 return; | 1345 return; |
424 } | 1346 } |
425 | 1347 |
(...skipping 205 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
631 SkSafeSetNull(fCurrTexture); | 1553 SkSafeSetNull(fCurrTexture); |
632 } | 1554 } |
633 } | 1555 } |
634 | 1556 |
635 inline void GrBitmapTextContext::finish() { | 1557 inline void GrBitmapTextContext::finish() { |
636 this->flush(); | 1558 this->flush(); |
637 fTotalVertexCount = 0; | 1559 fTotalVertexCount = 0; |
638 | 1560 |
639 GrTextContext::finish(); | 1561 GrTextContext::finish(); |
640 } | 1562 } |
641 | |
OLD | NEW |