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