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 |