| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright 2014 Google Inc. | 2 * Copyright 2014 Google Inc. |
| 3 * | 3 * |
| 4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
| 5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
| 6 */ | 6 */ |
| 7 | 7 |
| 8 #include "GrStencilAndCoverTextContext.h" | 8 #include "GrStencilAndCoverTextContext.h" |
| 9 #include "GrDrawTarget.h" | 9 #include "GrDrawTarget.h" |
| 10 #include "GrGpu.h" | 10 #include "GrGpu.h" |
| 11 #include "GrPath.h" | 11 #include "GrPath.h" |
| 12 #include "GrPathRange.h" | 12 #include "GrPathRange.h" |
| 13 #include "SkAutoKern.h" | 13 #include "SkAutoKern.h" |
| 14 #include "SkDraw.h" | 14 #include "SkDraw.h" |
| 15 #include "SkDrawProcs.h" | 15 #include "SkDrawProcs.h" |
| 16 #include "SkGlyphCache.h" | 16 #include "SkGlyphCache.h" |
| 17 #include "SkGpuDevice.h" | 17 #include "SkGpuDevice.h" |
| 18 #include "SkPath.h" | 18 #include "SkPath.h" |
| 19 #include "SkTextMapStateProc.h" | 19 #include "SkTextMapStateProc.h" |
| 20 | 20 #include "SkTextFormatParams.h" |
| 21 class GrStencilAndCoverTextContext::GlyphPathRange : public GrGpuResource { | |
| 22 static const int kMaxGlyphCount = 1 << 16; // Glyph IDs are uint16_t's | |
| 23 static const int kGlyphGroupSize = 16; // Glyphs get tracked in groups of 16 | |
| 24 | |
| 25 public: | |
| 26 static GlyphPathRange* Create(GrContext* context, | |
| 27 SkGlyphCache* cache, | |
| 28 const SkStrokeRec& stroke) { | |
| 29 static const GrCacheID::Domain gGlyphPathRangeDomain = GrCacheID::Genera
teDomain(); | |
| 30 | |
| 31 GrCacheID::Key key; | |
| 32 key.fData32[0] = cache->getDescriptor().getChecksum(); | |
| 33 key.fData32[1] = cache->getScalerContext()->getTypeface()->uniqueID(); | |
| 34 key.fData64[1] = GrPath::ComputeStrokeKey(stroke); | |
| 35 | |
| 36 GrResourceKey resourceKey(GrCacheID(gGlyphPathRangeDomain, key), | |
| 37 GrPathRange::resourceType(), 0); | |
| 38 SkAutoTUnref<GlyphPathRange> glyphs( | |
| 39 static_cast<GlyphPathRange*>(context->findAndRefCachedResource(resou
rceKey))); | |
| 40 | |
| 41 if (NULL == glyphs || | |
| 42 !glyphs->fDesc->equals(cache->getDescriptor() /*checksum collision*/
)) { | |
| 43 glyphs.reset(SkNEW_ARGS(GlyphPathRange, (context, cache->getDescript
or(), stroke))); | |
| 44 glyphs->registerWithCache(); | |
| 45 context->addResourceToCache(resourceKey, glyphs); | |
| 46 } | |
| 47 | |
| 48 return glyphs.detach(); | |
| 49 } | |
| 50 | |
| 51 const GrPathRange* pathRange() const { return fPathRange.get(); } | |
| 52 | |
| 53 void preloadGlyph(uint16_t glyphID, SkGlyphCache* cache) { | |
| 54 const uint16_t groupIndex = glyphID / kGlyphGroupSize; | |
| 55 const uint16_t groupByte = groupIndex >> 3; | |
| 56 const uint8_t groupBit = 1 << (groupIndex & 7); | |
| 57 | |
| 58 const bool hasGlyph = 0 != (fLoadedGlyphs[groupByte] & groupBit); | |
| 59 if (hasGlyph) { | |
| 60 return; | |
| 61 } | |
| 62 | |
| 63 // We track which glyphs are loaded in groups of kGlyphGroupSize. To | |
| 64 // mark a glyph loaded we need to load the entire group. | |
| 65 const uint16_t groupFirstID = groupIndex * kGlyphGroupSize; | |
| 66 const uint16_t groupLastID = groupFirstID + kGlyphGroupSize - 1; | |
| 67 SkPath skPath; | |
| 68 for (int id = groupFirstID; id <= groupLastID; ++id) { | |
| 69 const SkGlyph& skGlyph = cache->getGlyphIDMetrics(id); | |
| 70 if (const SkPath* skPath = cache->findPath(skGlyph)) { | |
| 71 fPathRange->initAt(id, *skPath); | |
| 72 } // GrGpu::drawPaths will silently ignore undefined paths. | |
| 73 } | |
| 74 | |
| 75 fLoadedGlyphs[groupByte] |= groupBit; | |
| 76 this->didChangeGpuMemorySize(); | |
| 77 } | |
| 78 | |
| 79 // GrGpuResource overrides | |
| 80 virtual size_t gpuMemorySize() const SK_OVERRIDE { return fPathRange->gpuMem
orySize(); } | |
| 81 | |
| 82 private: | |
| 83 GlyphPathRange(GrContext* context, const SkDescriptor& desc, const SkStrokeR
ec& stroke) | |
| 84 : INHERITED(context->getGpu(), false) | |
| 85 , fDesc(desc.copy()) | |
| 86 // We reserve a range of kMaxGlyphCount paths because of fallbacks fonts
. We | |
| 87 // can't know exactly how many glyphs we might need without preloading e
very | |
| 88 // fallback, which we don't want to do at this point. | |
| 89 , fPathRange(context->getGpu()->pathRendering()->createPathRange(kMaxGly
phCount, stroke)) { | |
| 90 memset(fLoadedGlyphs, 0, sizeof(fLoadedGlyphs)); | |
| 91 } | |
| 92 | |
| 93 ~GlyphPathRange() { | |
| 94 this->release(); | |
| 95 SkDescriptor::Free(fDesc); | |
| 96 } | |
| 97 | |
| 98 static const int kMaxGroupCount = (kMaxGlyphCount + (kGlyphGroupSize - 1)) /
kGlyphGroupSize; | |
| 99 SkDescriptor* const fDesc; | |
| 100 uint8_t fLoadedGlyphs[(kMaxGroupCount + 7) >> 3]; // One bit per glyph group | |
| 101 SkAutoTUnref<GrPathRange> fPathRange; | |
| 102 | |
| 103 typedef GrGpuResource INHERITED; | |
| 104 }; | |
| 105 | |
| 106 | 21 |
| 107 GrStencilAndCoverTextContext::GrStencilAndCoverTextContext( | 22 GrStencilAndCoverTextContext::GrStencilAndCoverTextContext( |
| 108 GrContext* context, const SkDeviceProperties& properties) | 23 GrContext* context, const SkDeviceProperties& properties) |
| 109 : GrTextContext(context, properties) | 24 : GrTextContext(context, properties) |
| 110 , fStroke(SkStrokeRec::kFill_InitStyle) | |
| 111 , fPendingGlyphCount(0) { | 25 , fPendingGlyphCount(0) { |
| 112 } | 26 } |
| 113 | 27 |
| 114 GrStencilAndCoverTextContext::~GrStencilAndCoverTextContext() { | 28 GrStencilAndCoverTextContext::~GrStencilAndCoverTextContext() { |
| 115 } | 29 } |
| 116 | 30 |
| 117 void GrStencilAndCoverTextContext::drawText(const GrPaint& paint, | 31 void GrStencilAndCoverTextContext::drawText(const GrPaint& paint, |
| 118 const SkPaint& skPaint, | 32 const SkPaint& skPaint, |
| 119 const char text[], | 33 const char text[], |
| 120 size_t byteLength, | 34 size_t byteLength, |
| (...skipping 12 matching lines...) Expand all Loading... |
| 133 // too. This in turn has the side-effect that NVPR can not stroke the paths, | 47 // too. This in turn has the side-effect that NVPR can not stroke the paths, |
| 134 // as the stroke in NVPR is defined in object-space. | 48 // as the stroke in NVPR is defined in object-space. |
| 135 // NOTE: here we have following coincidence that works at the moment: | 49 // NOTE: here we have following coincidence that works at the moment: |
| 136 // - When using the device-space glyphs, the transforms we pass to NVPR | 50 // - When using the device-space glyphs, the transforms we pass to NVPR |
| 137 // instanced drawing are the global transforms, and the view transform is | 51 // instanced drawing are the global transforms, and the view transform is |
| 138 // identity. NVPR can not use non-affine transforms in the instanced | 52 // identity. NVPR can not use non-affine transforms in the instanced |
| 139 // drawing. This is taken care of by SkDraw::ShouldDrawTextAsPaths since it | 53 // drawing. This is taken care of by SkDraw::ShouldDrawTextAsPaths since it |
| 140 // will turn off the use of device-space glyphs when perspective transforms | 54 // will turn off the use of device-space glyphs when perspective transforms |
| 141 // are in use. | 55 // are in use. |
| 142 | 56 |
| 143 this->init(paint, skPaint, byteLength, kUseIfNeeded_DeviceSpaceGlyphsBehavio
r); | 57 this->init(paint, skPaint, byteLength, kMaxAccuracy_RenderMode); |
| 144 | 58 |
| 145 SkMatrix* glyphCacheTransform = NULL; | |
| 146 // Transform our starting point. | 59 // Transform our starting point. |
| 147 if (fNeedsDeviceSpaceGlyphs) { | 60 if (fNeedsDeviceSpaceGlyphs) { |
| 148 SkPoint loc; | 61 SkPoint loc; |
| 149 fContextInitialMatrix.mapXY(x, y, &loc); | 62 fContextInitialMatrix.mapXY(x, y, &loc); |
| 150 x = loc.fX; | 63 x = loc.fX; |
| 151 y = loc.fY; | 64 y = loc.fY; |
| 152 glyphCacheTransform = &fContextInitialMatrix; | |
| 153 } | 65 } |
| 154 | 66 |
| 155 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc(); | 67 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc(); |
| 156 SkAutoGlyphCache autoCache(fSkPaint, &fDeviceProperties, glyphCacheTransform
); | 68 |
| 157 fGlyphCache = autoCache.getCache(); | |
| 158 fGlyphs = GlyphPathRange::Create(fContext, fGlyphCache, fStroke); | |
| 159 fTransformType = GrPathRendering::kTranslate_PathTransformType; | 69 fTransformType = GrPathRendering::kTranslate_PathTransformType; |
| 160 | 70 |
| 161 const char* stop = text + byteLength; | 71 const char* stop = text + byteLength; |
| 162 | 72 |
| 163 // Measure first if needed. | 73 // Measure first if needed. |
| 164 if (fSkPaint.getTextAlign() != SkPaint::kLeft_Align) { | 74 if (fSkPaint.getTextAlign() != SkPaint::kLeft_Align) { |
| 165 SkFixed stopX = 0; | 75 SkFixed stopX = 0; |
| 166 SkFixed stopY = 0; | 76 SkFixed stopY = 0; |
| 167 | 77 |
| 168 const char* textPtr = text; | 78 const char* textPtr = text; |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 225 | 135 |
| 226 // This is the fast path. Here we do not bake in the device-transform to | 136 // This is the fast path. Here we do not bake in the device-transform to |
| 227 // the glyph outline or the advances. This is because we do not need to | 137 // the glyph outline or the advances. This is because we do not need to |
| 228 // position the glyphs at all, since the caller has done the positioning. | 138 // position the glyphs at all, since the caller has done the positioning. |
| 229 // The positioning is based on SkPaint::measureText of individual | 139 // The positioning is based on SkPaint::measureText of individual |
| 230 // glyphs. That already uses glyph cache without device transforms. Device | 140 // glyphs. That already uses glyph cache without device transforms. Device |
| 231 // transform is not part of SkPaint::measureText API, and thus we use the | 141 // transform is not part of SkPaint::measureText API, and thus we use the |
| 232 // same glyphs as what were measured. | 142 // same glyphs as what were measured. |
| 233 | 143 |
| 234 const float textTranslateY = (1 == scalarsPerPosition ? constY : 0); | 144 const float textTranslateY = (1 == scalarsPerPosition ? constY : 0); |
| 235 this->init(paint, skPaint, byteLength, kDoNotUse_DeviceSpaceGlyphsBehavior,
textTranslateY); | 145 this->init(paint, skPaint, byteLength, kMaxPerformance_RenderMode, textTrans
lateY); |
| 236 | 146 |
| 237 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc(); | 147 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc(); |
| 238 | 148 |
| 239 SkAutoGlyphCache autoCache(fSkPaint, &fDeviceProperties, NULL); | |
| 240 fGlyphCache = autoCache.getCache(); | |
| 241 fGlyphs = GlyphPathRange::Create(fContext, fGlyphCache, fStroke); | |
| 242 | |
| 243 const char* stop = text + byteLength; | 149 const char* stop = text + byteLength; |
| 244 | 150 |
| 245 if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) { | 151 if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) { |
| 246 if (1 == scalarsPerPosition) { | 152 if (1 == scalarsPerPosition) { |
| 247 fTransformType = GrPathRendering::kTranslateX_PathTransformType; | 153 fTransformType = GrPathRendering::kTranslateX_PathTransformType; |
| 248 while (text < stop) { | 154 while (text < stop) { |
| 249 const SkGlyph& glyph = glyphCacheProc(fGlyphCache, &text, 0, 0); | 155 const SkGlyph& glyph = glyphCacheProc(fGlyphCache, &text, 0, 0); |
| 250 if (glyph.fWidth) { | 156 if (glyph.fWidth) { |
| 251 this->appendGlyph(glyph.getGlyphID(), *pos); | 157 this->appendGlyph(glyph.getGlyphID(), *pos); |
| 252 } | 158 } |
| (...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 301 && fContext->getMatrix().hasPerspective()) { | 207 && fContext->getMatrix().hasPerspective()) { |
| 302 return false; | 208 return false; |
| 303 } | 209 } |
| 304 | 210 |
| 305 // No color bitmap fonts. | 211 // No color bitmap fonts. |
| 306 SkScalerContext::Rec rec; | 212 SkScalerContext::Rec rec; |
| 307 SkScalerContext::MakeRec(paint, &fDeviceProperties, NULL, &rec); | 213 SkScalerContext::MakeRec(paint, &fDeviceProperties, NULL, &rec); |
| 308 return rec.getFormat() != SkMask::kARGB32_Format; | 214 return rec.getFormat() != SkMask::kARGB32_Format; |
| 309 } | 215 } |
| 310 | 216 |
| 217 static GrPathRange* get_gr_glyphs(GrContext* ctx, |
| 218 const SkTypeface* typeface, |
| 219 const SkDescriptor* desc, |
| 220 const SkStrokeRec& stroke) { |
| 221 static const GrCacheID::Domain gGlyphsDomain = GrCacheID::GenerateDomain(); |
| 222 |
| 223 GrCacheID::Key key; |
| 224 uint64_t* keyData = key.fData64; |
| 225 keyData[0] = desc ? desc->getChecksum() : 0; |
| 226 keyData[0] = (keyData[0] << 32) | (typeface ? typeface->uniqueID() : 0); |
| 227 keyData[1] = GrPath::ComputeStrokeKey(stroke); |
| 228 GrResourceKey resourceKey = GrResourceKey(GrCacheID(gGlyphsDomain, key), |
| 229 GrPathRange::resourceType(), 0); |
| 230 |
| 231 SkAutoTUnref<GrPathRange> glyphs( |
| 232 static_cast<GrPathRange*>(ctx->findAndRefCachedResource(resourceKey))); |
| 233 if (NULL == glyphs || (NULL != desc && !glyphs->isEqualTo(*desc))) { |
| 234 glyphs.reset(ctx->getGpu()->pathRendering()->createGlyphs(typeface, desc
, stroke)); |
| 235 ctx->addResourceToCache(resourceKey, glyphs); |
| 236 } |
| 237 |
| 238 return glyphs.detach(); |
| 239 } |
| 240 |
| 311 void GrStencilAndCoverTextContext::init(const GrPaint& paint, | 241 void GrStencilAndCoverTextContext::init(const GrPaint& paint, |
| 312 const SkPaint& skPaint, | 242 const SkPaint& skPaint, |
| 313 size_t textByteLength, | 243 size_t textByteLength, |
| 314 DeviceSpaceGlyphsBehavior deviceSpaceGly
phsBehavior, | 244 RenderMode renderMode, |
| 315 SkScalar textTranslateY) { | 245 SkScalar textTranslateY) { |
| 316 GrTextContext::init(paint, skPaint); | 246 GrTextContext::init(paint, skPaint); |
| 317 | 247 |
| 318 fContextInitialMatrix = fContext->getMatrix(); | 248 fContextInitialMatrix = fContext->getMatrix(); |
| 319 | 249 |
| 320 bool otherBackendsWillDrawAsPaths = | 250 const bool otherBackendsWillDrawAsPaths = |
| 321 SkDraw::ShouldDrawTextAsPaths(skPaint, fContextInitialMatrix); | 251 SkDraw::ShouldDrawTextAsPaths(skPaint, fContextInitialMatrix); |
| 322 | 252 |
| 323 if (otherBackendsWillDrawAsPaths) { | 253 fNeedsDeviceSpaceGlyphs = !otherBackendsWillDrawAsPaths && |
| 324 // This is to reproduce SkDraw::drawText_asPaths glyph positions. | 254 kMaxAccuracy_RenderMode == renderMode && |
| 325 fSkPaint.setLinearText(true); | 255 SkToBool(fContextInitialMatrix.getType() & |
| 326 fTextRatio = fSkPaint.getTextSize() / SkPaint::kCanonicalTextSizeForPath
s; | 256 (SkMatrix::kScale_Mask | SkMatrix::kAffin
e_Mask)); |
| 327 fTextInverseRatio = SkPaint::kCanonicalTextSizeForPaths / fSkPaint.getTe
xtSize(); | 257 |
| 328 fSkPaint.setTextSize(SkIntToScalar(SkPaint::kCanonicalTextSizeForPaths))
; | 258 if (fNeedsDeviceSpaceGlyphs) { |
| 329 if (fSkPaint.getStyle() != SkPaint::kFill_Style) { | |
| 330 // Compensate the glyphs being scaled up by fTextRatio by scaling th
e | |
| 331 // stroke down. | |
| 332 fSkPaint.setStrokeWidth(fSkPaint.getStrokeWidth() / fTextRatio); | |
| 333 } | |
| 334 fNeedsDeviceSpaceGlyphs = false; | |
| 335 } else { | |
| 336 fTextRatio = fTextInverseRatio = 1.0f; | |
| 337 fNeedsDeviceSpaceGlyphs = | |
| 338 kUseIfNeeded_DeviceSpaceGlyphsBehavior == deviceSpaceGlyphsBehavior
&& | |
| 339 (fContextInitialMatrix.getType() & | |
| 340 (SkMatrix::kScale_Mask | SkMatrix::kAffine_Mask)) != 0; | |
| 341 // SkDraw::ShouldDrawTextAsPaths takes care of perspective transforms. | 259 // SkDraw::ShouldDrawTextAsPaths takes care of perspective transforms. |
| 342 SkASSERT(!fContextInitialMatrix.hasPerspective()); | 260 SkASSERT(!fContextInitialMatrix.hasPerspective()); |
| 261 SkASSERT(0 == textTranslateY); // TODO: Handle textTranslateY in device-
space usecase. |
| 262 |
| 263 fTextRatio = fTextInverseRatio = 1.0f; |
| 264 |
| 265 // Glyphs loaded by GPU path rendering have an inverted y-direction. |
| 266 SkMatrix m; |
| 267 m.setScale(1, -1); |
| 268 fContext->setMatrix(m); |
| 269 |
| 270 // Post-flip the initial matrix so we're left with just the flip after |
| 271 // the paint preConcats the inverse. |
| 272 m = fContextInitialMatrix; |
| 273 m.postScale(1, -1); |
| 274 fPaint.localCoordChangeInverse(m); |
| 275 |
| 276 // The whole shape (including stroke) will be baked into the glyph outli
nes. Make |
| 277 // NVPR just fill the baked shapes. |
| 278 fGlyphCache = fSkPaint.detachCache(&fDeviceProperties, &fContextInitialM
atrix, false); |
| 279 fGlyphs = get_gr_glyphs(fContext, fGlyphCache->getScalerContext()->getTy
peface(), |
| 280 &fGlyphCache->getDescriptor(), |
| 281 SkStrokeRec(SkStrokeRec::kFill_InitStyle)); |
| 282 } else { |
| 283 // Don't bake strokes into the glyph outlines. We will stroke the glyphs |
| 284 // using the GPU instead. This is the fast path. |
| 285 SkStrokeRec gpuStroke = SkStrokeRec(fSkPaint); |
| 286 fSkPaint.setStyle(SkPaint::kFill_Style); |
| 287 |
| 288 if (gpuStroke.isHairlineStyle()) { |
| 289 // Approximate hairline stroke. |
| 290 SkScalar strokeWidth = SK_Scalar1 / |
| 291 (SkVector::Make(fContextInitialMatrix.getScaleX(), |
| 292 fContextInitialMatrix.getSkewY()).length()); |
| 293 gpuStroke.setStrokeStyle(strokeWidth, false /*strokeAndFill*/); |
| 294 |
| 295 } else if (fSkPaint.isFakeBoldText() && |
| 296 #ifdef SK_USE_FREETYPE_EMBOLDEN |
| 297 kMaxPerformance_RenderMode == renderMode && |
| 298 #endif |
| 299 SkStrokeRec::kStroke_Style != gpuStroke.getStyle()) { |
| 300 |
| 301 // Instead of baking fake bold into the glyph outlines, do it with t
he GPU stroke. |
| 302 SkScalar fakeBoldScale = SkScalarInterpFunc(fSkPaint.getTextSize(), |
| 303 kStdFakeBoldInterpKeys, |
| 304 kStdFakeBoldInterpValues
, |
| 305 kStdFakeBoldInterpLength
); |
| 306 SkScalar extra = SkScalarMul(fSkPaint.getTextSize(), fakeBoldScale); |
| 307 gpuStroke.setStrokeStyle(gpuStroke.needToApply() ? gpuStroke.getWidt
h() + extra : extra, |
| 308 true /*strokeAndFill*/); |
| 309 |
| 310 fSkPaint.setFakeBoldText(false); |
| 311 } |
| 312 |
| 313 bool canUseRawPaths; |
| 314 |
| 315 if (otherBackendsWillDrawAsPaths || kMaxPerformance_RenderMode == render
Mode) { |
| 316 // We can draw the glyphs from canonically sized paths. |
| 317 fTextRatio = fSkPaint.getTextSize() / SkPaint::kCanonicalTextSizeFor
Paths; |
| 318 fTextInverseRatio = SkPaint::kCanonicalTextSizeForPaths / fSkPaint.g
etTextSize(); |
| 319 |
| 320 // Compensate for the glyphs being scaled by fTextRatio. |
| 321 if (!gpuStroke.isFillStyle()) { |
| 322 gpuStroke.setStrokeStyle(gpuStroke.getWidth() / fTextRatio, |
| 323 SkStrokeRec::kStrokeAndFill_Style == gp
uStroke.getStyle()); |
| 324 } |
| 325 |
| 326 fSkPaint.setLinearText(true); |
| 327 fSkPaint.setLCDRenderText(false); |
| 328 fSkPaint.setAutohinted(false); |
| 329 fSkPaint.setHinting(SkPaint::kNo_Hinting); |
| 330 fSkPaint.setSubpixelText(true); |
| 331 fSkPaint.setTextSize(SkIntToScalar(SkPaint::kCanonicalTextSizeForPat
hs)); |
| 332 |
| 333 canUseRawPaths = SK_Scalar1 == fSkPaint.getTextScaleX() && |
| 334 0 == fSkPaint.getTextSkewX() && |
| 335 !fSkPaint.isFakeBoldText() && |
| 336 !fSkPaint.isVerticalText(); |
| 337 } else { |
| 338 fTextRatio = fTextInverseRatio = 1.0f; |
| 339 canUseRawPaths = false; |
| 340 } |
| 341 |
| 342 SkMatrix textMatrix; |
| 343 textMatrix.setTranslate(0, textTranslateY); |
| 344 // Glyphs loaded by GPU path rendering have an inverted y-direction. |
| 345 textMatrix.preScale(fTextRatio, -fTextRatio); |
| 346 fPaint.localCoordChange(textMatrix); |
| 347 fContext->concatMatrix(textMatrix); |
| 348 |
| 349 fGlyphCache = fSkPaint.detachCache(&fDeviceProperties, NULL, false); |
| 350 fGlyphs = canUseRawPaths ? |
| 351 get_gr_glyphs(fContext, fSkPaint.getTypeface(), NULL, gpuS
troke) : |
| 352 get_gr_glyphs(fContext, fGlyphCache->getScalerContext()->g
etTypeface(), |
| 353 &fGlyphCache->getDescriptor(), gpuStroke); |
| 343 } | 354 } |
| 344 | 355 |
| 345 fStroke = SkStrokeRec(fSkPaint); | |
| 346 | |
| 347 if (fNeedsDeviceSpaceGlyphs) { | |
| 348 SkASSERT(1.0f == fTextRatio); | |
| 349 SkASSERT(0.0f == textTranslateY); | |
| 350 fPaint.localCoordChangeInverse(fContextInitialMatrix); | |
| 351 fContext->setIdentityMatrix(); | |
| 352 | |
| 353 // The whole shape is baked into the glyph. Make NVPR just fill the | |
| 354 // baked shape. | |
| 355 fStroke.setStrokeStyle(-1, false); | |
| 356 } else { | |
| 357 if (1.0f != fTextRatio || 0.0f != textTranslateY) { | |
| 358 SkMatrix textMatrix; | |
| 359 textMatrix.setTranslate(0, textTranslateY); | |
| 360 textMatrix.preScale(fTextRatio, fTextRatio); | |
| 361 fPaint.localCoordChange(textMatrix); | |
| 362 fContext->concatMatrix(textMatrix); | |
| 363 } | |
| 364 | |
| 365 if (fSkPaint.getStrokeWidth() == 0.0f) { | |
| 366 if (fSkPaint.getStyle() == SkPaint::kStrokeAndFill_Style) { | |
| 367 fStroke.setStrokeStyle(-1, false); | |
| 368 } else if (fSkPaint.getStyle() == SkPaint::kStroke_Style) { | |
| 369 // Approximate hairline stroke. | |
| 370 const SkMatrix& ctm = fContext->getMatrix(); | |
| 371 SkScalar strokeWidth = SK_Scalar1 / | |
| 372 (SkVector::Make(ctm.getScaleX(), ctm.getSkewY()).length()); | |
| 373 fStroke.setStrokeStyle(strokeWidth, false); | |
| 374 } | |
| 375 } | |
| 376 | |
| 377 // Make glyph cache produce paths geometry for fill. We will stroke them | |
| 378 // by passing fStroke to drawPath. This is the fast path. | |
| 379 fSkPaint.setStyle(SkPaint::kFill_Style); | |
| 380 } | |
| 381 fStateRestore.set(fDrawTarget->drawState()); | 356 fStateRestore.set(fDrawTarget->drawState()); |
| 382 | 357 |
| 383 fDrawTarget->drawState()->setFromPaint(fPaint, fContext->getMatrix(), | 358 fDrawTarget->drawState()->setFromPaint(fPaint, fContext->getMatrix(), |
| 384 fContext->getRenderTarget()); | 359 fContext->getRenderTarget()); |
| 385 | 360 |
| 386 GR_STATIC_CONST_SAME_STENCIL(kStencilPass, | 361 GR_STATIC_CONST_SAME_STENCIL(kStencilPass, |
| 387 kZero_StencilOp, | 362 kZero_StencilOp, |
| 388 kZero_StencilOp, | 363 kZero_StencilOp, |
| 389 kNotEqual_StencilFunc, | 364 kNotEqual_StencilFunc, |
| 390 0xffff, | 365 0xffff, |
| 391 0x0000, | 366 0x0000, |
| 392 0xffff); | 367 0xffff); |
| 393 | 368 |
| 394 *fDrawTarget->drawState()->stencil() = kStencilPass; | 369 *fDrawTarget->drawState()->stencil() = kStencilPass; |
| 395 | 370 |
| 396 SkASSERT(0 == fPendingGlyphCount); | 371 SkASSERT(0 == fPendingGlyphCount); |
| 397 } | 372 } |
| 398 | 373 |
| 399 inline void GrStencilAndCoverTextContext::appendGlyph(uint16_t glyphID, float x)
{ | 374 inline void GrStencilAndCoverTextContext::appendGlyph(uint16_t glyphID, float x)
{ |
| 400 SkASSERT(GrPathRendering::kTranslateX_PathTransformType == fTransformType); | 375 SkASSERT(GrPathRendering::kTranslateX_PathTransformType == fTransformType); |
| 401 | 376 |
| 402 if (fPendingGlyphCount >= kGlyphBufferSize) { | 377 if (fPendingGlyphCount >= kGlyphBufferSize) { |
| 403 this->flush(); | 378 this->flush(); |
| 404 } | 379 } |
| 405 | 380 |
| 406 fGlyphs->preloadGlyph(glyphID, fGlyphCache); | |
| 407 | |
| 408 fIndexBuffer[fPendingGlyphCount] = glyphID; | 381 fIndexBuffer[fPendingGlyphCount] = glyphID; |
| 409 fTransformBuffer[fPendingGlyphCount] = fTextInverseRatio * x; | 382 fTransformBuffer[fPendingGlyphCount] = fTextInverseRatio * x; |
| 410 | 383 |
| 411 ++fPendingGlyphCount; | 384 ++fPendingGlyphCount; |
| 412 } | 385 } |
| 413 | 386 |
| 414 inline void GrStencilAndCoverTextContext::appendGlyph(uint16_t glyphID, float x,
float y) { | 387 inline void GrStencilAndCoverTextContext::appendGlyph(uint16_t glyphID, float x,
float y) { |
| 415 SkASSERT(GrPathRendering::kTranslate_PathTransformType == fTransformType); | 388 SkASSERT(GrPathRendering::kTranslate_PathTransformType == fTransformType); |
| 416 | 389 |
| 417 if (fPendingGlyphCount >= kGlyphBufferSize) { | 390 if (fPendingGlyphCount >= kGlyphBufferSize) { |
| 418 this->flush(); | 391 this->flush(); |
| 419 } | 392 } |
| 420 | 393 |
| 421 fGlyphs->preloadGlyph(glyphID, fGlyphCache); | |
| 422 | |
| 423 fIndexBuffer[fPendingGlyphCount] = glyphID; | 394 fIndexBuffer[fPendingGlyphCount] = glyphID; |
| 424 fTransformBuffer[2 * fPendingGlyphCount] = fTextInverseRatio * x; | 395 fTransformBuffer[2 * fPendingGlyphCount] = fTextInverseRatio * x; |
| 425 fTransformBuffer[2 * fPendingGlyphCount + 1] = fTextInverseRatio * y; | 396 fTransformBuffer[2 * fPendingGlyphCount + 1] = -fTextInverseRatio * y; |
| 426 | 397 |
| 427 ++fPendingGlyphCount; | 398 ++fPendingGlyphCount; |
| 428 } | 399 } |
| 429 | 400 |
| 430 void GrStencilAndCoverTextContext::flush() { | 401 void GrStencilAndCoverTextContext::flush() { |
| 431 if (0 == fPendingGlyphCount) { | 402 if (0 == fPendingGlyphCount) { |
| 432 return; | 403 return; |
| 433 } | 404 } |
| 434 | 405 |
| 435 fDrawTarget->drawPaths(fGlyphs->pathRange(), fIndexBuffer, fPendingGlyphCoun
t, | 406 fDrawTarget->drawPaths(fGlyphs, fIndexBuffer, fPendingGlyphCount, |
| 436 fTransformBuffer, fTransformType, SkPath::kWinding_Fi
llType); | 407 fTransformBuffer, fTransformType, SkPath::kWinding_Fi
llType); |
| 437 | 408 |
| 438 fPendingGlyphCount = 0; | 409 fPendingGlyphCount = 0; |
| 439 } | 410 } |
| 440 | 411 |
| 441 void GrStencilAndCoverTextContext::finish() { | 412 void GrStencilAndCoverTextContext::finish() { |
| 442 this->flush(); | 413 this->flush(); |
| 443 | 414 |
| 444 SkSafeUnref(fGlyphs); | 415 fGlyphs->unref(); |
| 445 fGlyphs = NULL; | 416 fGlyphs = NULL; |
| 417 |
| 418 SkGlyphCache::AttachCache(fGlyphCache); |
| 446 fGlyphCache = NULL; | 419 fGlyphCache = NULL; |
| 447 | 420 |
| 448 fDrawTarget->drawState()->stencil()->setDisabled(); | 421 fDrawTarget->drawState()->stencil()->setDisabled(); |
| 449 fStateRestore.set(NULL); | 422 fStateRestore.set(NULL); |
| 450 fContext->setMatrix(fContextInitialMatrix); | 423 fContext->setMatrix(fContextInitialMatrix); |
| 451 GrTextContext::finish(); | 424 GrTextContext::finish(); |
| 452 } | 425 } |
| 453 | 426 |
| OLD | NEW |