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()->createPathRange(kMaxGlyphCount, 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 14 matching lines...) Expand all Loading... |
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, kUseIfNeeded_DeviceSpaceGlyphsBehavio
r); |
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 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
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, kDoNotUse_DeviceSpaceGlyphsBehavior,
textTranslateY); |
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 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
310 | 216 |
311 void GrStencilAndCoverTextContext::init(const GrPaint& paint, | 217 void GrStencilAndCoverTextContext::init(const GrPaint& paint, |
312 const SkPaint& skPaint, | 218 const SkPaint& skPaint, |
313 size_t textByteLength, | 219 size_t textByteLength, |
314 DeviceSpaceGlyphsBehavior deviceSpaceGly
phsBehavior, | 220 DeviceSpaceGlyphsBehavior deviceSpaceGly
phsBehavior, |
315 SkScalar textTranslateY) { | 221 SkScalar textTranslateY) { |
316 GrTextContext::init(paint, skPaint); | 222 GrTextContext::init(paint, skPaint); |
317 | 223 |
318 fContextInitialMatrix = fContext->getMatrix(); | 224 fContextInitialMatrix = fContext->getMatrix(); |
319 | 225 |
320 bool otherBackendsWillDrawAsPaths = | 226 const bool otherBackendsWillDrawAsPaths = |
321 SkDraw::ShouldDrawTextAsPaths(skPaint, fContextInitialMatrix); | 227 SkDraw::ShouldDrawTextAsPaths(skPaint, fContextInitialMatrix); |
322 | 228 |
323 if (otherBackendsWillDrawAsPaths) { | 229 fNeedsDeviceSpaceGlyphs = !otherBackendsWillDrawAsPaths && |
324 // This is to reproduce SkDraw::drawText_asPaths glyph positions. | 230 kUseIfNeeded_DeviceSpaceGlyphsBehavior == deviceSp
aceGlyphsBehavior; |
325 fSkPaint.setLinearText(true); | 231 |
326 fTextRatio = fSkPaint.getTextSize() / SkPaint::kCanonicalTextSizeForPath
s; | 232 if (fNeedsDeviceSpaceGlyphs) { |
327 fTextInverseRatio = SkPaint::kCanonicalTextSizeForPaths / fSkPaint.getTe
xtSize(); | |
328 fSkPaint.setTextSize(SkIntToScalar(SkPaint::kCanonicalTextSizeForPaths))
; | |
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. | 233 // SkDraw::ShouldDrawTextAsPaths takes care of perspective transforms. |
342 SkASSERT(!fContextInitialMatrix.hasPerspective()); | 234 SkASSERT(!fContextInitialMatrix.hasPerspective()); |
| 235 SkASSERT(0 == textTranslateY); |
| 236 |
| 237 fTextRatio = fTextInverseRatio = 1.0f; |
| 238 |
| 239 // The whole shape (including stroke) will be baked into the glyph outli
nes. Make |
| 240 // NVPR just fill the baked shapes. |
| 241 fGlyphCache = fSkPaint.detachCache(&fDeviceProperties, &fContextInitialM
atrix, false); |
| 242 fGlyphs = fContext->createGlyphs(fGlyphCache->getScalerContext()->getTyp
eface(), |
| 243 &fGlyphCache->getDescriptor(), |
| 244 SkStrokeRec(SkStrokeRec::kFill_InitStyl
e)); |
| 245 |
| 246 // Glyphs loaded by GPU path rendering have an inverted y-direction. |
| 247 SkMatrix m; |
| 248 m.setScale(1, -1); |
| 249 fContext->setMatrix(m); |
| 250 |
| 251 // Post-flip the initial matrix so we're left with just the flip after |
| 252 // the paint preConcats the inverse. |
| 253 m = fContextInitialMatrix; |
| 254 m.postScale(1, -1); |
| 255 fPaint.localCoordChangeInverse(m); |
| 256 } else { |
| 257 // Don't bake strokes into the glyph outlines. We will stroke the glyphs |
| 258 // using the GPU instead. This is the fast path. |
| 259 SkStrokeRec gpuStroke = SkStrokeRec(fSkPaint); |
| 260 fSkPaint.setStyle(SkPaint::kFill_Style); |
| 261 |
| 262 #ifndef SK_USE_FREETYPE_EMBOLDEN |
| 263 if (fSkPaint.isFakeBoldText()) { |
| 264 SkScalar fakeBoldScale = SkScalarInterpFunc(fSkPaint.getTextSize(), |
| 265 kStdFakeBoldInterpKeys, |
| 266 kStdFakeBoldInterpValues
, |
| 267 kStdFakeBoldInterpLength
); |
| 268 SkScalar extra = SkScalarMul(fSkPaint.getTextSize(), fakeBoldScale); |
| 269 |
| 270 if (gpuStroke.isFillStyle()) { |
| 271 gpuStroke.setStrokeStyle(extra, true /*strokeAndFill*/); |
| 272 } else { |
| 273 gpuStroke.setStrokeStyle(gpuStroke.getWidth() + extra, |
| 274 SkStrokeRec::kStrokeAndFill_Style == gp
uStroke.getStyle()); |
| 275 } |
| 276 |
| 277 fSkPaint.setFakeBoldText(false); |
| 278 } |
| 279 #endif |
| 280 |
| 281 if (gpuStroke.isHairlineStyle()) { |
| 282 // Approximate hairline stroke. |
| 283 SkScalar strokeWidth = SK_Scalar1 / |
| 284 (SkVector::Make(fContextInitialMatrix.getScaleX(), |
| 285 fContextInitialMatrix.getSkewY()).length()); |
| 286 gpuStroke.setStrokeStyle(strokeWidth, false); |
| 287 } |
| 288 |
| 289 if (otherBackendsWillDrawAsPaths || (SK_Scalar1 == fSkPaint.getTextScale
X() && |
| 290 0 == fSkPaint.getTextSkewX() && |
| 291 #ifdef SK_USE_FREETYPE_EMBOLDEN |
| 292 !fSkPaint.isFakeBoldText() && |
| 293 #endif |
| 294 !fSkPaint.isVerticalText())) { |
| 295 // We can draw the glyphs from their generic raw paths. |
| 296 fTextRatio = fSkPaint.getTextSize() / SkPaint::kCanonicalTextSizeFor
Paths; |
| 297 fTextInverseRatio = SkPaint::kCanonicalTextSizeForPaths / fSkPaint.g
etTextSize(); |
| 298 |
| 299 if (!gpuStroke.isFillStyle()) { |
| 300 // Compensate for the glyphs being scaled by fTextRatio. |
| 301 gpuStroke.setStrokeStyle(gpuStroke.getWidth() / fTextRatio, |
| 302 SkStrokeRec::kStrokeAndFill_Style == gp
uStroke.getStyle()); |
| 303 } |
| 304 |
| 305 fSkPaint.setLinearText(true); |
| 306 fSkPaint.setLCDRenderText(false); |
| 307 fSkPaint.setAutohinted(false); |
| 308 fSkPaint.setHinting(SkPaint::kNo_Hinting); |
| 309 fSkPaint.setSubpixelText(true); |
| 310 fSkPaint.setTextSize(SkIntToScalar(SkPaint::kCanonicalTextSizeForPat
hs)); |
| 311 |
| 312 fGlyphCache = fSkPaint.detachCache(&fDeviceProperties, NULL, false); |
| 313 fGlyphs = fContext->createGlyphs(fSkPaint.getTypeface(), NULL, gpuSt
roke); |
| 314 } else { |
| 315 fTextRatio = fTextInverseRatio = 1.0f; |
| 316 |
| 317 fGlyphCache = fSkPaint.detachCache(&fDeviceProperties, NULL, false); |
| 318 fGlyphs = fContext->createGlyphs(fGlyphCache->getScalerContext()->ge
tTypeface(), |
| 319 &fGlyphCache->getDescriptor(), |
| 320 gpuStroke); |
| 321 } |
| 322 |
| 323 SkMatrix textMatrix; |
| 324 textMatrix.setTranslate(0, textTranslateY); |
| 325 // Glyphs loaded by GPU path rendering have an inverted y-direction. |
| 326 textMatrix.preScale(fTextRatio, -fTextRatio); |
| 327 fPaint.localCoordChange(textMatrix); |
| 328 fContext->concatMatrix(textMatrix); |
343 } | 329 } |
344 | 330 |
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()); | 331 fStateRestore.set(fDrawTarget->drawState()); |
382 | 332 |
383 fDrawTarget->drawState()->setFromPaint(fPaint, fContext->getMatrix(), | 333 fDrawTarget->drawState()->setFromPaint(fPaint, fContext->getMatrix(), |
384 fContext->getRenderTarget()); | 334 fContext->getRenderTarget()); |
385 | 335 |
386 GR_STATIC_CONST_SAME_STENCIL(kStencilPass, | 336 GR_STATIC_CONST_SAME_STENCIL(kStencilPass, |
387 kZero_StencilOp, | 337 kZero_StencilOp, |
388 kZero_StencilOp, | 338 kZero_StencilOp, |
389 kNotEqual_StencilFunc, | 339 kNotEqual_StencilFunc, |
390 0xffff, | 340 0xffff, |
391 0x0000, | 341 0x0000, |
392 0xffff); | 342 0xffff); |
393 | 343 |
394 *fDrawTarget->drawState()->stencil() = kStencilPass; | 344 *fDrawTarget->drawState()->stencil() = kStencilPass; |
395 | 345 |
396 SkASSERT(0 == fPendingGlyphCount); | 346 SkASSERT(0 == fPendingGlyphCount); |
397 } | 347 } |
398 | 348 |
399 inline void GrStencilAndCoverTextContext::appendGlyph(uint16_t glyphID, float x)
{ | 349 inline void GrStencilAndCoverTextContext::appendGlyph(uint16_t glyphID, float x)
{ |
400 SkASSERT(GrPathRendering::kTranslateX_PathTransformType == fTransformType); | 350 SkASSERT(GrPathRendering::kTranslateX_PathTransformType == fTransformType); |
401 | 351 |
402 if (fPendingGlyphCount >= kGlyphBufferSize) { | 352 if (fPendingGlyphCount >= kGlyphBufferSize) { |
403 this->flush(); | 353 this->flush(); |
404 } | 354 } |
405 | 355 |
406 fGlyphs->preloadGlyph(glyphID, fGlyphCache); | |
407 | |
408 fIndexBuffer[fPendingGlyphCount] = glyphID; | 356 fIndexBuffer[fPendingGlyphCount] = glyphID; |
409 fTransformBuffer[fPendingGlyphCount] = fTextInverseRatio * x; | 357 fTransformBuffer[fPendingGlyphCount] = fTextInverseRatio * x; |
410 | 358 |
411 ++fPendingGlyphCount; | 359 ++fPendingGlyphCount; |
412 } | 360 } |
413 | 361 |
414 inline void GrStencilAndCoverTextContext::appendGlyph(uint16_t glyphID, float x,
float y) { | 362 inline void GrStencilAndCoverTextContext::appendGlyph(uint16_t glyphID, float x,
float y) { |
415 SkASSERT(GrPathRendering::kTranslate_PathTransformType == fTransformType); | 363 SkASSERT(GrPathRendering::kTranslate_PathTransformType == fTransformType); |
416 | 364 |
417 if (fPendingGlyphCount >= kGlyphBufferSize) { | 365 if (fPendingGlyphCount >= kGlyphBufferSize) { |
418 this->flush(); | 366 this->flush(); |
419 } | 367 } |
420 | 368 |
421 fGlyphs->preloadGlyph(glyphID, fGlyphCache); | |
422 | |
423 fIndexBuffer[fPendingGlyphCount] = glyphID; | 369 fIndexBuffer[fPendingGlyphCount] = glyphID; |
424 fTransformBuffer[2 * fPendingGlyphCount] = fTextInverseRatio * x; | 370 fTransformBuffer[2 * fPendingGlyphCount] = fTextInverseRatio * x; |
425 fTransformBuffer[2 * fPendingGlyphCount + 1] = fTextInverseRatio * y; | 371 fTransformBuffer[2 * fPendingGlyphCount + 1] = -fTextInverseRatio * y; |
426 | 372 |
427 ++fPendingGlyphCount; | 373 ++fPendingGlyphCount; |
428 } | 374 } |
429 | 375 |
430 void GrStencilAndCoverTextContext::flush() { | 376 void GrStencilAndCoverTextContext::flush() { |
431 if (0 == fPendingGlyphCount) { | 377 if (0 == fPendingGlyphCount) { |
432 return; | 378 return; |
433 } | 379 } |
434 | 380 |
435 fDrawTarget->drawPaths(fGlyphs->pathRange(), fIndexBuffer, fPendingGlyphCoun
t, | 381 fDrawTarget->drawPaths(fGlyphs, fIndexBuffer, fPendingGlyphCount, |
436 fTransformBuffer, fTransformType, SkPath::kWinding_Fi
llType); | 382 fTransformBuffer, fTransformType, SkPath::kWinding_Fi
llType); |
437 | 383 |
438 fPendingGlyphCount = 0; | 384 fPendingGlyphCount = 0; |
439 } | 385 } |
440 | 386 |
441 void GrStencilAndCoverTextContext::finish() { | 387 void GrStencilAndCoverTextContext::finish() { |
442 this->flush(); | 388 this->flush(); |
443 | 389 |
444 SkSafeUnref(fGlyphs); | 390 fGlyphs->unref(); |
445 fGlyphs = NULL; | 391 fGlyphs = NULL; |
| 392 |
| 393 SkGlyphCache::AttachCache(fGlyphCache); |
446 fGlyphCache = NULL; | 394 fGlyphCache = NULL; |
447 | 395 |
448 fDrawTarget->drawState()->stencil()->setDisabled(); | 396 fDrawTarget->drawState()->stencil()->setDisabled(); |
449 fStateRestore.set(NULL); | 397 fStateRestore.set(NULL); |
450 fContext->setMatrix(fContextInitialMatrix); | 398 fContext->setMatrix(fContextInitialMatrix); |
451 GrTextContext::finish(); | 399 GrTextContext::finish(); |
452 } | 400 } |
453 | 401 |
OLD | NEW |