| 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 |