blob: 796183feba5e742c2ae37531b3d6083c219761b9 [file] [log] [blame]
kkinnunenc6cb56f2014-06-24 00:12:27 -07001/*
2 * Copyright 2014 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "GrStencilAndCoverTextContext.h"
9#include "GrDrawTarget.h"
kkinnunenc6cb56f2014-06-24 00:12:27 -070010#include "GrGpu.h"
11#include "GrPath.h"
cdaltonb85a0aa2014-07-21 15:32:44 -070012#include "GrPathRange.h"
kkinnunenc6cb56f2014-06-24 00:12:27 -070013#include "SkAutoKern.h"
14#include "SkDraw.h"
15#include "SkDrawProcs.h"
16#include "SkGlyphCache.h"
17#include "SkGpuDevice.h"
18#include "SkPath.h"
19#include "SkTextMapStateProc.h"
20
bsalomon6d3fe022014-07-25 08:35:45 -070021class GrStencilAndCoverTextContext::GlyphPathRange : public GrGpuResource {
cdaltonb85a0aa2014-07-21 15:32:44 -070022 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
25public:
26 static GlyphPathRange* Create(GrContext* context,
27 SkGlyphCache* cache,
28 const SkStrokeRec& stroke) {
29 static const GrCacheID::Domain gGlyphPathRangeDomain = GrCacheID::GenerateDomain();
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(resourceKey)));
40
41 if (NULL == glyphs ||
42 !glyphs->fDesc->equals(cache->getDescriptor() /*checksum collision*/)) {
43 glyphs.reset(SkNEW_ARGS(GlyphPathRange, (context, cache->getDescriptor(), stroke)));
bsalomon16961262014-08-26 14:01:07 -070044 glyphs->registerWithCache();
cdaltonb85a0aa2014-07-21 15:32:44 -070045 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
bsalomon6d3fe022014-07-25 08:35:45 -070079 // GrGpuResource overrides
cdaltonb85a0aa2014-07-21 15:32:44 -070080 virtual size_t gpuMemorySize() const SK_OVERRIDE { return fPathRange->gpuMemorySize(); }
cdaltonb85a0aa2014-07-21 15:32:44 -070081
82private:
83 GlyphPathRange(GrContext* context, const SkDescriptor& desc, const SkStrokeRec& stroke)
bsalomonc44be0e2014-07-25 07:32:33 -070084 : INHERITED(context->getGpu(), false)
85 , fDesc(desc.copy())
cdaltonb85a0aa2014-07-21 15:32:44 -070086 // 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 every
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() {
bsalomonc44be0e2014-07-25 07:32:33 -070094 this->release();
cdaltonb85a0aa2014-07-21 15:32:44 -070095 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
bsalomon6d3fe022014-07-25 08:35:45 -0700103 typedef GrGpuResource INHERITED;
cdaltonb85a0aa2014-07-21 15:32:44 -0700104};
105
kkinnunenc6cb56f2014-06-24 00:12:27 -0700106
107GrStencilAndCoverTextContext::GrStencilAndCoverTextContext(
108 GrContext* context, const SkDeviceProperties& properties)
109 : GrTextContext(context, properties)
cdaltonb85a0aa2014-07-21 15:32:44 -0700110 , fStroke(SkStrokeRec::kFill_InitStyle)
111 , fPendingGlyphCount(0) {
kkinnunenc6cb56f2014-06-24 00:12:27 -0700112}
113
114GrStencilAndCoverTextContext::~GrStencilAndCoverTextContext() {
115}
116
117void GrStencilAndCoverTextContext::drawText(const GrPaint& paint,
118 const SkPaint& skPaint,
119 const char text[],
120 size_t byteLength,
121 SkScalar x, SkScalar y) {
122 SkASSERT(byteLength == 0 || text != NULL);
123
124 if (text == NULL || byteLength == 0 /*|| fRC->isEmpty()*/) {
125 return;
126 }
127
128 // This is the slow path, mainly used by Skia unit tests. The other
129 // backends (8888, gpu, ...) use device-space dependent glyph caches. In
130 // order to match the glyph positions that the other code paths produce, we
131 // must also use device-space dependent glyph cache. This has the
132 // side-effect that the glyph shape outline will be in device-space,
133 // 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.
135 // NOTE: here we have following coincidence that works at the moment:
136 // - When using the device-space glyphs, the transforms we pass to NVPR
137 // instanced drawing are the global transforms, and the view transform is
138 // identity. NVPR can not use non-affine transforms in the instanced
139 // drawing. This is taken care of by SkDraw::ShouldDrawTextAsPaths since it
140 // will turn off the use of device-space glyphs when perspective transforms
141 // are in use.
142
cdaltonb2808cd2014-07-25 14:13:57 -0700143 this->init(paint, skPaint, byteLength, kUseIfNeeded_DeviceSpaceGlyphsBehavior);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700144
145 SkMatrix* glyphCacheTransform = NULL;
146 // Transform our starting point.
147 if (fNeedsDeviceSpaceGlyphs) {
148 SkPoint loc;
cdaltonb2808cd2014-07-25 14:13:57 -0700149 fContextInitialMatrix.mapXY(x, y, &loc);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700150 x = loc.fX;
151 y = loc.fY;
cdaltonb2808cd2014-07-25 14:13:57 -0700152 glyphCacheTransform = &fContextInitialMatrix;
kkinnunenc6cb56f2014-06-24 00:12:27 -0700153 }
154
155 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
156 SkAutoGlyphCache autoCache(fSkPaint, &fDeviceProperties, glyphCacheTransform);
cdaltonb85a0aa2014-07-21 15:32:44 -0700157 fGlyphCache = autoCache.getCache();
158 fGlyphs = GlyphPathRange::Create(fContext, fGlyphCache, fStroke);
kkinnunenccdaa042014-08-20 01:36:23 -0700159 fTransformType = GrPathRendering::kTranslate_PathTransformType;
kkinnunenc6cb56f2014-06-24 00:12:27 -0700160
161 const char* stop = text + byteLength;
162
163 // Measure first if needed.
164 if (fSkPaint.getTextAlign() != SkPaint::kLeft_Align) {
165 SkFixed stopX = 0;
166 SkFixed stopY = 0;
167
168 const char* textPtr = text;
169 while (textPtr < stop) {
170 // We don't need x, y here, since all subpixel variants will have the
171 // same advance.
cdaltonb85a0aa2014-07-21 15:32:44 -0700172 const SkGlyph& glyph = glyphCacheProc(fGlyphCache, &textPtr, 0, 0);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700173
174 stopX += glyph.fAdvanceX;
175 stopY += glyph.fAdvanceY;
176 }
177 SkASSERT(textPtr == stop);
178
179 SkScalar alignX = SkFixedToScalar(stopX) * fTextRatio;
180 SkScalar alignY = SkFixedToScalar(stopY) * fTextRatio;
181
182 if (fSkPaint.getTextAlign() == SkPaint::kCenter_Align) {
183 alignX = SkScalarHalf(alignX);
184 alignY = SkScalarHalf(alignY);
185 }
186
187 x -= alignX;
188 y -= alignY;
189 }
190
191 SkAutoKern autokern;
192
193 SkFixed fixedSizeRatio = SkScalarToFixed(fTextRatio);
194
195 SkFixed fx = SkScalarToFixed(x);
196 SkFixed fy = SkScalarToFixed(y);
197 while (text < stop) {
cdaltonb85a0aa2014-07-21 15:32:44 -0700198 const SkGlyph& glyph = glyphCacheProc(fGlyphCache, &text, 0, 0);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700199 fx += SkFixedMul_portable(autokern.adjust(glyph), fixedSizeRatio);
200 if (glyph.fWidth) {
cdaltonb85a0aa2014-07-21 15:32:44 -0700201 this->appendGlyph(glyph.getGlyphID(), SkFixedToScalar(fx), SkFixedToScalar(fy));
kkinnunenc6cb56f2014-06-24 00:12:27 -0700202 }
203
204 fx += SkFixedMul_portable(glyph.fAdvanceX, fixedSizeRatio);
205 fy += SkFixedMul_portable(glyph.fAdvanceY, fixedSizeRatio);
206 }
207
208 this->finish();
209}
210
211void GrStencilAndCoverTextContext::drawPosText(const GrPaint& paint,
212 const SkPaint& skPaint,
213 const char text[],
214 size_t byteLength,
215 const SkScalar pos[],
216 SkScalar constY,
217 int scalarsPerPosition) {
218 SkASSERT(byteLength == 0 || text != NULL);
219 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
220
221 // nothing to draw
222 if (text == NULL || byteLength == 0/* || fRC->isEmpty()*/) {
223 return;
224 }
225
226 // 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
228 // position the glyphs at all, since the caller has done the positioning.
229 // The positioning is based on SkPaint::measureText of individual
230 // glyphs. That already uses glyph cache without device transforms. Device
231 // transform is not part of SkPaint::measureText API, and thus we use the
232 // same glyphs as what were measured.
kkinnunenc6cb56f2014-06-24 00:12:27 -0700233
cdaltonb2808cd2014-07-25 14:13:57 -0700234 const float textTranslateY = (1 == scalarsPerPosition ? constY : 0);
235 this->init(paint, skPaint, byteLength, kDoNotUse_DeviceSpaceGlyphsBehavior, textTranslateY);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700236
237 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
238
239 SkAutoGlyphCache autoCache(fSkPaint, &fDeviceProperties, NULL);
cdaltonb85a0aa2014-07-21 15:32:44 -0700240 fGlyphCache = autoCache.getCache();
241 fGlyphs = GlyphPathRange::Create(fContext, fGlyphCache, fStroke);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700242
243 const char* stop = text + byteLength;
kkinnunenc6cb56f2014-06-24 00:12:27 -0700244
245 if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) {
cdaltonb2808cd2014-07-25 14:13:57 -0700246 if (1 == scalarsPerPosition) {
kkinnunenccdaa042014-08-20 01:36:23 -0700247 fTransformType = GrPathRendering::kTranslateX_PathTransformType;
cdaltonb2808cd2014-07-25 14:13:57 -0700248 while (text < stop) {
249 const SkGlyph& glyph = glyphCacheProc(fGlyphCache, &text, 0, 0);
250 if (glyph.fWidth) {
251 this->appendGlyph(glyph.getGlyphID(), *pos);
252 }
253 pos++;
kkinnunenc6cb56f2014-06-24 00:12:27 -0700254 }
cdaltonb2808cd2014-07-25 14:13:57 -0700255 } else {
256 SkASSERT(2 == scalarsPerPosition);
kkinnunenccdaa042014-08-20 01:36:23 -0700257 fTransformType = GrPathRendering::kTranslate_PathTransformType;
cdaltonb2808cd2014-07-25 14:13:57 -0700258 while (text < stop) {
259 const SkGlyph& glyph = glyphCacheProc(fGlyphCache, &text, 0, 0);
260 if (glyph.fWidth) {
261 this->appendGlyph(glyph.getGlyphID(), pos[0], pos[1]);
262 }
263 pos += 2;
264 }
kkinnunenc6cb56f2014-06-24 00:12:27 -0700265 }
266 } else {
kkinnunenccdaa042014-08-20 01:36:23 -0700267 fTransformType = GrPathRendering::kTranslate_PathTransformType;
cdaltonb2808cd2014-07-25 14:13:57 -0700268 SkTextMapStateProc tmsProc(SkMatrix::I(), 0, scalarsPerPosition);
269 SkTextAlignProcScalar alignProc(fSkPaint.getTextAlign());
kkinnunenc6cb56f2014-06-24 00:12:27 -0700270 while (text < stop) {
cdaltonb85a0aa2014-07-21 15:32:44 -0700271 const SkGlyph& glyph = glyphCacheProc(fGlyphCache, &text, 0, 0);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700272 if (glyph.fWidth) {
273 SkPoint tmsLoc;
274 tmsProc(pos, &tmsLoc);
275 SkPoint loc;
276 alignProc(tmsLoc, glyph, &loc);
277
cdaltonb85a0aa2014-07-21 15:32:44 -0700278 this->appendGlyph(glyph.getGlyphID(), loc.x(), loc.y());
kkinnunenc6cb56f2014-06-24 00:12:27 -0700279 }
280 pos += scalarsPerPosition;
281 }
282 }
283
284 this->finish();
285}
286
287bool GrStencilAndCoverTextContext::canDraw(const SkPaint& paint) {
288 if (paint.getRasterizer()) {
289 return false;
290 }
291 if (paint.getMaskFilter()) {
292 return false;
293 }
294 if (paint.getPathEffect()) {
295 return false;
296 }
297
298 // No hairlines unless we can map the 1 px width to the object space.
299 if (paint.getStyle() == SkPaint::kStroke_Style
300 && paint.getStrokeWidth() == 0
301 && fContext->getMatrix().hasPerspective()) {
302 return false;
303 }
304
305 // No color bitmap fonts.
306 SkScalerContext::Rec rec;
307 SkScalerContext::MakeRec(paint, &fDeviceProperties, NULL, &rec);
308 return rec.getFormat() != SkMask::kARGB32_Format;
309}
310
311void GrStencilAndCoverTextContext::init(const GrPaint& paint,
312 const SkPaint& skPaint,
cdaltonb2808cd2014-07-25 14:13:57 -0700313 size_t textByteLength,
314 DeviceSpaceGlyphsBehavior deviceSpaceGlyphsBehavior,
315 SkScalar textTranslateY) {
kkinnunenc6cb56f2014-06-24 00:12:27 -0700316 GrTextContext::init(paint, skPaint);
317
cdaltonb2808cd2014-07-25 14:13:57 -0700318 fContextInitialMatrix = fContext->getMatrix();
319
kkinnunenc6cb56f2014-06-24 00:12:27 -0700320 bool otherBackendsWillDrawAsPaths =
cdaltonb2808cd2014-07-25 14:13:57 -0700321 SkDraw::ShouldDrawTextAsPaths(skPaint, fContextInitialMatrix);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700322
323 if (otherBackendsWillDrawAsPaths) {
324 // This is to reproduce SkDraw::drawText_asPaths glyph positions.
325 fSkPaint.setLinearText(true);
326 fTextRatio = fSkPaint.getTextSize() / SkPaint::kCanonicalTextSizeForPaths;
cdaltonb2808cd2014-07-25 14:13:57 -0700327 fTextInverseRatio = SkPaint::kCanonicalTextSizeForPaths / fSkPaint.getTextSize();
kkinnunenc6cb56f2014-06-24 00:12:27 -0700328 fSkPaint.setTextSize(SkIntToScalar(SkPaint::kCanonicalTextSizeForPaths));
329 if (fSkPaint.getStyle() != SkPaint::kFill_Style) {
330 // Compensate the glyphs being scaled up by fTextRatio by scaling the
331 // stroke down.
332 fSkPaint.setStrokeWidth(fSkPaint.getStrokeWidth() / fTextRatio);
333 }
334 fNeedsDeviceSpaceGlyphs = false;
335 } else {
cdaltonb2808cd2014-07-25 14:13:57 -0700336 fTextRatio = fTextInverseRatio = 1.0f;
337 fNeedsDeviceSpaceGlyphs =
338 kUseIfNeeded_DeviceSpaceGlyphsBehavior == deviceSpaceGlyphsBehavior &&
339 (fContextInitialMatrix.getType() &
340 (SkMatrix::kScale_Mask | SkMatrix::kAffine_Mask)) != 0;
kkinnunenc6cb56f2014-06-24 00:12:27 -0700341 // SkDraw::ShouldDrawTextAsPaths takes care of perspective transforms.
cdaltonb2808cd2014-07-25 14:13:57 -0700342 SkASSERT(!fContextInitialMatrix.hasPerspective());
kkinnunenc6cb56f2014-06-24 00:12:27 -0700343 }
344
345 fStroke = SkStrokeRec(fSkPaint);
346
347 if (fNeedsDeviceSpaceGlyphs) {
cdaltonb2808cd2014-07-25 14:13:57 -0700348 SkASSERT(1.0f == fTextRatio);
349 SkASSERT(0.0f == textTranslateY);
350 fPaint.localCoordChangeInverse(fContextInitialMatrix);
351 fContext->setIdentityMatrix();
352
kkinnunenc6cb56f2014-06-24 00:12:27 -0700353 // The whole shape is baked into the glyph. Make NVPR just fill the
354 // baked shape.
355 fStroke.setStrokeStyle(-1, false);
356 } else {
cdaltonb2808cd2014-07-25 14:13:57 -0700357 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
kkinnunenc6cb56f2014-06-24 00:12:27 -0700365 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 /
cdaltonb2808cd2014-07-25 14:13:57 -0700372 (SkVector::Make(ctm.getScaleX(), ctm.getSkewY()).length());
kkinnunenc6cb56f2014-06-24 00:12:27 -0700373 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());
382
383 fDrawTarget->drawState()->setFromPaint(fPaint, fContext->getMatrix(),
384 fContext->getRenderTarget());
385
386 GR_STATIC_CONST_SAME_STENCIL(kStencilPass,
387 kZero_StencilOp,
388 kZero_StencilOp,
389 kNotEqual_StencilFunc,
390 0xffff,
391 0x0000,
392 0xffff);
393
394 *fDrawTarget->drawState()->stencil() = kStencilPass;
395
cdaltonb85a0aa2014-07-21 15:32:44 -0700396 SkASSERT(0 == fPendingGlyphCount);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700397}
398
cdaltonb2808cd2014-07-25 14:13:57 -0700399inline void GrStencilAndCoverTextContext::appendGlyph(uint16_t glyphID, float x) {
kkinnunenccdaa042014-08-20 01:36:23 -0700400 SkASSERT(GrPathRendering::kTranslateX_PathTransformType == fTransformType);
cdaltonb2808cd2014-07-25 14:13:57 -0700401
cdaltonb85a0aa2014-07-21 15:32:44 -0700402 if (fPendingGlyphCount >= kGlyphBufferSize) {
403 this->flush();
404 }
405
406 fGlyphs->preloadGlyph(glyphID, fGlyphCache);
407
408 fIndexBuffer[fPendingGlyphCount] = glyphID;
cdaltonb2808cd2014-07-25 14:13:57 -0700409 fTransformBuffer[fPendingGlyphCount] = fTextInverseRatio * x;
410
411 ++fPendingGlyphCount;
412}
413
414inline void GrStencilAndCoverTextContext::appendGlyph(uint16_t glyphID, float x, float y) {
kkinnunenccdaa042014-08-20 01:36:23 -0700415 SkASSERT(GrPathRendering::kTranslate_PathTransformType == fTransformType);
cdaltonb2808cd2014-07-25 14:13:57 -0700416
417 if (fPendingGlyphCount >= kGlyphBufferSize) {
418 this->flush();
419 }
420
421 fGlyphs->preloadGlyph(glyphID, fGlyphCache);
422
423 fIndexBuffer[fPendingGlyphCount] = glyphID;
424 fTransformBuffer[2 * fPendingGlyphCount] = fTextInverseRatio * x;
425 fTransformBuffer[2 * fPendingGlyphCount + 1] = fTextInverseRatio * y;
cdaltonb85a0aa2014-07-21 15:32:44 -0700426
427 ++fPendingGlyphCount;
428}
429
430void GrStencilAndCoverTextContext::flush() {
431 if (0 == fPendingGlyphCount) {
kkinnunenc6cb56f2014-06-24 00:12:27 -0700432 return;
433 }
434
cdaltonb85a0aa2014-07-21 15:32:44 -0700435 fDrawTarget->drawPaths(fGlyphs->pathRange(), fIndexBuffer, fPendingGlyphCount,
cdaltonb2808cd2014-07-25 14:13:57 -0700436 fTransformBuffer, fTransformType, SkPath::kWinding_FillType);
cdaltonb85a0aa2014-07-21 15:32:44 -0700437
438 fPendingGlyphCount = 0;
kkinnunenc6cb56f2014-06-24 00:12:27 -0700439}
440
441void GrStencilAndCoverTextContext::finish() {
cdaltonb85a0aa2014-07-21 15:32:44 -0700442 this->flush();
kkinnunenc6cb56f2014-06-24 00:12:27 -0700443
cdaltonb85a0aa2014-07-21 15:32:44 -0700444 SkSafeUnref(fGlyphs);
445 fGlyphs = NULL;
446 fGlyphCache = NULL;
kkinnunenc6cb56f2014-06-24 00:12:27 -0700447
448 fDrawTarget->drawState()->stencil()->setDisabled();
449 fStateRestore.set(NULL);
cdaltonb2808cd2014-07-25 14:13:57 -0700450 fContext->setMatrix(fContextInitialMatrix);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700451 GrTextContext::finish();
452}
453