blob: d3d711fa020b0d66fc468f164bcd09837eee7cb3 [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)));
44 context->addResourceToCache(resourceKey, glyphs);
45 }
46
47 return glyphs.detach();
48 }
49
50 const GrPathRange* pathRange() const { return fPathRange.get(); }
51
52 void preloadGlyph(uint16_t glyphID, SkGlyphCache* cache) {
53 const uint16_t groupIndex = glyphID / kGlyphGroupSize;
54 const uint16_t groupByte = groupIndex >> 3;
55 const uint8_t groupBit = 1 << (groupIndex & 7);
56
57 const bool hasGlyph = 0 != (fLoadedGlyphs[groupByte] & groupBit);
58 if (hasGlyph) {
59 return;
60 }
61
62 // We track which glyphs are loaded in groups of kGlyphGroupSize. To
63 // mark a glyph loaded we need to load the entire group.
64 const uint16_t groupFirstID = groupIndex * kGlyphGroupSize;
65 const uint16_t groupLastID = groupFirstID + kGlyphGroupSize - 1;
66 SkPath skPath;
67 for (int id = groupFirstID; id <= groupLastID; ++id) {
68 const SkGlyph& skGlyph = cache->getGlyphIDMetrics(id);
69 if (const SkPath* skPath = cache->findPath(skGlyph)) {
70 fPathRange->initAt(id, *skPath);
71 } // GrGpu::drawPaths will silently ignore undefined paths.
72 }
73
74 fLoadedGlyphs[groupByte] |= groupBit;
75 this->didChangeGpuMemorySize();
76 }
77
bsalomon6d3fe022014-07-25 08:35:45 -070078 // GrGpuResource overrides
cdaltonb85a0aa2014-07-21 15:32:44 -070079 virtual size_t gpuMemorySize() const SK_OVERRIDE { return fPathRange->gpuMemorySize(); }
cdaltonb85a0aa2014-07-21 15:32:44 -070080
81private:
82 GlyphPathRange(GrContext* context, const SkDescriptor& desc, const SkStrokeRec& stroke)
bsalomonc44be0e2014-07-25 07:32:33 -070083 : INHERITED(context->getGpu(), false)
84 , fDesc(desc.copy())
cdaltonb85a0aa2014-07-21 15:32:44 -070085 // We reserve a range of kMaxGlyphCount paths because of fallbacks fonts. We
86 // can't know exactly how many glyphs we might need without preloading every
87 // fallback, which we don't want to do at this point.
88 , fPathRange(context->getGpu()->createPathRange(kMaxGlyphCount, stroke)) {
89 memset(fLoadedGlyphs, 0, sizeof(fLoadedGlyphs));
90 }
91
92 ~GlyphPathRange() {
bsalomonc44be0e2014-07-25 07:32:33 -070093 this->release();
cdaltonb85a0aa2014-07-21 15:32:44 -070094 SkDescriptor::Free(fDesc);
95 }
96
bsalomonc44be0e2014-07-25 07:32:33 -070097 virtual void onRelease() SK_OVERRIDE {
98 INHERITED::onRelease();
99 fPathRange.reset(NULL);
100 }
101
102 virtual void onAbandon() SK_OVERRIDE {
103 INHERITED::onAbandon();
104 fPathRange->abandon();
105 fPathRange.reset(NULL);
106 }
107
108
cdaltonb85a0aa2014-07-21 15:32:44 -0700109 static const int kMaxGroupCount = (kMaxGlyphCount + (kGlyphGroupSize - 1)) / kGlyphGroupSize;
110 SkDescriptor* const fDesc;
111 uint8_t fLoadedGlyphs[(kMaxGroupCount + 7) >> 3]; // One bit per glyph group
112 SkAutoTUnref<GrPathRange> fPathRange;
113
bsalomon6d3fe022014-07-25 08:35:45 -0700114 typedef GrGpuResource INHERITED;
cdaltonb85a0aa2014-07-21 15:32:44 -0700115};
116
kkinnunenc6cb56f2014-06-24 00:12:27 -0700117
118GrStencilAndCoverTextContext::GrStencilAndCoverTextContext(
119 GrContext* context, const SkDeviceProperties& properties)
120 : GrTextContext(context, properties)
cdaltonb85a0aa2014-07-21 15:32:44 -0700121 , fStroke(SkStrokeRec::kFill_InitStyle)
122 , fPendingGlyphCount(0) {
kkinnunenc6cb56f2014-06-24 00:12:27 -0700123}
124
125GrStencilAndCoverTextContext::~GrStencilAndCoverTextContext() {
126}
127
128void GrStencilAndCoverTextContext::drawText(const GrPaint& paint,
129 const SkPaint& skPaint,
130 const char text[],
131 size_t byteLength,
132 SkScalar x, SkScalar y) {
133 SkASSERT(byteLength == 0 || text != NULL);
134
135 if (text == NULL || byteLength == 0 /*|| fRC->isEmpty()*/) {
136 return;
137 }
138
139 // This is the slow path, mainly used by Skia unit tests. The other
140 // backends (8888, gpu, ...) use device-space dependent glyph caches. In
141 // order to match the glyph positions that the other code paths produce, we
142 // must also use device-space dependent glyph cache. This has the
143 // side-effect that the glyph shape outline will be in device-space,
144 // too. This in turn has the side-effect that NVPR can not stroke the paths,
145 // as the stroke in NVPR is defined in object-space.
146 // NOTE: here we have following coincidence that works at the moment:
147 // - When using the device-space glyphs, the transforms we pass to NVPR
148 // instanced drawing are the global transforms, and the view transform is
149 // identity. NVPR can not use non-affine transforms in the instanced
150 // drawing. This is taken care of by SkDraw::ShouldDrawTextAsPaths since it
151 // will turn off the use of device-space glyphs when perspective transforms
152 // are in use.
153
cdaltonb2808cd2014-07-25 14:13:57 -0700154 this->init(paint, skPaint, byteLength, kUseIfNeeded_DeviceSpaceGlyphsBehavior);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700155
156 SkMatrix* glyphCacheTransform = NULL;
157 // Transform our starting point.
158 if (fNeedsDeviceSpaceGlyphs) {
159 SkPoint loc;
cdaltonb2808cd2014-07-25 14:13:57 -0700160 fContextInitialMatrix.mapXY(x, y, &loc);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700161 x = loc.fX;
162 y = loc.fY;
cdaltonb2808cd2014-07-25 14:13:57 -0700163 glyphCacheTransform = &fContextInitialMatrix;
kkinnunenc6cb56f2014-06-24 00:12:27 -0700164 }
165
166 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
167 SkAutoGlyphCache autoCache(fSkPaint, &fDeviceProperties, glyphCacheTransform);
cdaltonb85a0aa2014-07-21 15:32:44 -0700168 fGlyphCache = autoCache.getCache();
169 fGlyphs = GlyphPathRange::Create(fContext, fGlyphCache, fStroke);
cdaltonb2808cd2014-07-25 14:13:57 -0700170 fTransformType = GrDrawTarget::kTranslate_PathTransformType;
kkinnunenc6cb56f2014-06-24 00:12:27 -0700171
172 const char* stop = text + byteLength;
173
174 // Measure first if needed.
175 if (fSkPaint.getTextAlign() != SkPaint::kLeft_Align) {
176 SkFixed stopX = 0;
177 SkFixed stopY = 0;
178
179 const char* textPtr = text;
180 while (textPtr < stop) {
181 // We don't need x, y here, since all subpixel variants will have the
182 // same advance.
cdaltonb85a0aa2014-07-21 15:32:44 -0700183 const SkGlyph& glyph = glyphCacheProc(fGlyphCache, &textPtr, 0, 0);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700184
185 stopX += glyph.fAdvanceX;
186 stopY += glyph.fAdvanceY;
187 }
188 SkASSERT(textPtr == stop);
189
190 SkScalar alignX = SkFixedToScalar(stopX) * fTextRatio;
191 SkScalar alignY = SkFixedToScalar(stopY) * fTextRatio;
192
193 if (fSkPaint.getTextAlign() == SkPaint::kCenter_Align) {
194 alignX = SkScalarHalf(alignX);
195 alignY = SkScalarHalf(alignY);
196 }
197
198 x -= alignX;
199 y -= alignY;
200 }
201
202 SkAutoKern autokern;
203
204 SkFixed fixedSizeRatio = SkScalarToFixed(fTextRatio);
205
206 SkFixed fx = SkScalarToFixed(x);
207 SkFixed fy = SkScalarToFixed(y);
208 while (text < stop) {
cdaltonb85a0aa2014-07-21 15:32:44 -0700209 const SkGlyph& glyph = glyphCacheProc(fGlyphCache, &text, 0, 0);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700210 fx += SkFixedMul_portable(autokern.adjust(glyph), fixedSizeRatio);
211 if (glyph.fWidth) {
cdaltonb85a0aa2014-07-21 15:32:44 -0700212 this->appendGlyph(glyph.getGlyphID(), SkFixedToScalar(fx), SkFixedToScalar(fy));
kkinnunenc6cb56f2014-06-24 00:12:27 -0700213 }
214
215 fx += SkFixedMul_portable(glyph.fAdvanceX, fixedSizeRatio);
216 fy += SkFixedMul_portable(glyph.fAdvanceY, fixedSizeRatio);
217 }
218
219 this->finish();
220}
221
222void GrStencilAndCoverTextContext::drawPosText(const GrPaint& paint,
223 const SkPaint& skPaint,
224 const char text[],
225 size_t byteLength,
226 const SkScalar pos[],
227 SkScalar constY,
228 int scalarsPerPosition) {
229 SkASSERT(byteLength == 0 || text != NULL);
230 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
231
232 // nothing to draw
233 if (text == NULL || byteLength == 0/* || fRC->isEmpty()*/) {
234 return;
235 }
236
237 // This is the fast path. Here we do not bake in the device-transform to
238 // the glyph outline or the advances. This is because we do not need to
239 // position the glyphs at all, since the caller has done the positioning.
240 // The positioning is based on SkPaint::measureText of individual
241 // glyphs. That already uses glyph cache without device transforms. Device
242 // transform is not part of SkPaint::measureText API, and thus we use the
243 // same glyphs as what were measured.
kkinnunenc6cb56f2014-06-24 00:12:27 -0700244
cdaltonb2808cd2014-07-25 14:13:57 -0700245 const float textTranslateY = (1 == scalarsPerPosition ? constY : 0);
246 this->init(paint, skPaint, byteLength, kDoNotUse_DeviceSpaceGlyphsBehavior, textTranslateY);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700247
248 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
249
250 SkAutoGlyphCache autoCache(fSkPaint, &fDeviceProperties, NULL);
cdaltonb85a0aa2014-07-21 15:32:44 -0700251 fGlyphCache = autoCache.getCache();
252 fGlyphs = GlyphPathRange::Create(fContext, fGlyphCache, fStroke);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700253
254 const char* stop = text + byteLength;
kkinnunenc6cb56f2014-06-24 00:12:27 -0700255
256 if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) {
cdaltonb2808cd2014-07-25 14:13:57 -0700257 if (1 == scalarsPerPosition) {
258 fTransformType = GrDrawTarget::kTranslateX_PathTransformType;
259 while (text < stop) {
260 const SkGlyph& glyph = glyphCacheProc(fGlyphCache, &text, 0, 0);
261 if (glyph.fWidth) {
262 this->appendGlyph(glyph.getGlyphID(), *pos);
263 }
264 pos++;
kkinnunenc6cb56f2014-06-24 00:12:27 -0700265 }
cdaltonb2808cd2014-07-25 14:13:57 -0700266 } else {
267 SkASSERT(2 == scalarsPerPosition);
268 fTransformType = GrDrawTarget::kTranslate_PathTransformType;
269 while (text < stop) {
270 const SkGlyph& glyph = glyphCacheProc(fGlyphCache, &text, 0, 0);
271 if (glyph.fWidth) {
272 this->appendGlyph(glyph.getGlyphID(), pos[0], pos[1]);
273 }
274 pos += 2;
275 }
kkinnunenc6cb56f2014-06-24 00:12:27 -0700276 }
277 } else {
cdaltonb2808cd2014-07-25 14:13:57 -0700278 fTransformType = GrDrawTarget::kTranslate_PathTransformType;
279 SkTextMapStateProc tmsProc(SkMatrix::I(), 0, scalarsPerPosition);
280 SkTextAlignProcScalar alignProc(fSkPaint.getTextAlign());
kkinnunenc6cb56f2014-06-24 00:12:27 -0700281 while (text < stop) {
cdaltonb85a0aa2014-07-21 15:32:44 -0700282 const SkGlyph& glyph = glyphCacheProc(fGlyphCache, &text, 0, 0);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700283 if (glyph.fWidth) {
284 SkPoint tmsLoc;
285 tmsProc(pos, &tmsLoc);
286 SkPoint loc;
287 alignProc(tmsLoc, glyph, &loc);
288
cdaltonb85a0aa2014-07-21 15:32:44 -0700289 this->appendGlyph(glyph.getGlyphID(), loc.x(), loc.y());
kkinnunenc6cb56f2014-06-24 00:12:27 -0700290 }
291 pos += scalarsPerPosition;
292 }
293 }
294
295 this->finish();
296}
297
298bool GrStencilAndCoverTextContext::canDraw(const SkPaint& paint) {
299 if (paint.getRasterizer()) {
300 return false;
301 }
302 if (paint.getMaskFilter()) {
303 return false;
304 }
305 if (paint.getPathEffect()) {
306 return false;
307 }
308
309 // No hairlines unless we can map the 1 px width to the object space.
310 if (paint.getStyle() == SkPaint::kStroke_Style
311 && paint.getStrokeWidth() == 0
312 && fContext->getMatrix().hasPerspective()) {
313 return false;
314 }
315
316 // No color bitmap fonts.
317 SkScalerContext::Rec rec;
318 SkScalerContext::MakeRec(paint, &fDeviceProperties, NULL, &rec);
319 return rec.getFormat() != SkMask::kARGB32_Format;
320}
321
322void GrStencilAndCoverTextContext::init(const GrPaint& paint,
323 const SkPaint& skPaint,
cdaltonb2808cd2014-07-25 14:13:57 -0700324 size_t textByteLength,
325 DeviceSpaceGlyphsBehavior deviceSpaceGlyphsBehavior,
326 SkScalar textTranslateY) {
kkinnunenc6cb56f2014-06-24 00:12:27 -0700327 GrTextContext::init(paint, skPaint);
328
cdaltonb2808cd2014-07-25 14:13:57 -0700329 fContextInitialMatrix = fContext->getMatrix();
330
kkinnunenc6cb56f2014-06-24 00:12:27 -0700331 bool otherBackendsWillDrawAsPaths =
cdaltonb2808cd2014-07-25 14:13:57 -0700332 SkDraw::ShouldDrawTextAsPaths(skPaint, fContextInitialMatrix);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700333
334 if (otherBackendsWillDrawAsPaths) {
335 // This is to reproduce SkDraw::drawText_asPaths glyph positions.
336 fSkPaint.setLinearText(true);
337 fTextRatio = fSkPaint.getTextSize() / SkPaint::kCanonicalTextSizeForPaths;
cdaltonb2808cd2014-07-25 14:13:57 -0700338 fTextInverseRatio = SkPaint::kCanonicalTextSizeForPaths / fSkPaint.getTextSize();
kkinnunenc6cb56f2014-06-24 00:12:27 -0700339 fSkPaint.setTextSize(SkIntToScalar(SkPaint::kCanonicalTextSizeForPaths));
340 if (fSkPaint.getStyle() != SkPaint::kFill_Style) {
341 // Compensate the glyphs being scaled up by fTextRatio by scaling the
342 // stroke down.
343 fSkPaint.setStrokeWidth(fSkPaint.getStrokeWidth() / fTextRatio);
344 }
345 fNeedsDeviceSpaceGlyphs = false;
346 } else {
cdaltonb2808cd2014-07-25 14:13:57 -0700347 fTextRatio = fTextInverseRatio = 1.0f;
348 fNeedsDeviceSpaceGlyphs =
349 kUseIfNeeded_DeviceSpaceGlyphsBehavior == deviceSpaceGlyphsBehavior &&
350 (fContextInitialMatrix.getType() &
351 (SkMatrix::kScale_Mask | SkMatrix::kAffine_Mask)) != 0;
kkinnunenc6cb56f2014-06-24 00:12:27 -0700352 // SkDraw::ShouldDrawTextAsPaths takes care of perspective transforms.
cdaltonb2808cd2014-07-25 14:13:57 -0700353 SkASSERT(!fContextInitialMatrix.hasPerspective());
kkinnunenc6cb56f2014-06-24 00:12:27 -0700354 }
355
356 fStroke = SkStrokeRec(fSkPaint);
357
358 if (fNeedsDeviceSpaceGlyphs) {
cdaltonb2808cd2014-07-25 14:13:57 -0700359 SkASSERT(1.0f == fTextRatio);
360 SkASSERT(0.0f == textTranslateY);
361 fPaint.localCoordChangeInverse(fContextInitialMatrix);
362 fContext->setIdentityMatrix();
363
kkinnunenc6cb56f2014-06-24 00:12:27 -0700364 // The whole shape is baked into the glyph. Make NVPR just fill the
365 // baked shape.
366 fStroke.setStrokeStyle(-1, false);
367 } else {
cdaltonb2808cd2014-07-25 14:13:57 -0700368 if (1.0f != fTextRatio || 0.0f != textTranslateY) {
369 SkMatrix textMatrix;
370 textMatrix.setTranslate(0, textTranslateY);
371 textMatrix.preScale(fTextRatio, fTextRatio);
372 fPaint.localCoordChange(textMatrix);
373 fContext->concatMatrix(textMatrix);
374 }
375
kkinnunenc6cb56f2014-06-24 00:12:27 -0700376 if (fSkPaint.getStrokeWidth() == 0.0f) {
377 if (fSkPaint.getStyle() == SkPaint::kStrokeAndFill_Style) {
378 fStroke.setStrokeStyle(-1, false);
379 } else if (fSkPaint.getStyle() == SkPaint::kStroke_Style) {
380 // Approximate hairline stroke.
381 const SkMatrix& ctm = fContext->getMatrix();
382 SkScalar strokeWidth = SK_Scalar1 /
cdaltonb2808cd2014-07-25 14:13:57 -0700383 (SkVector::Make(ctm.getScaleX(), ctm.getSkewY()).length());
kkinnunenc6cb56f2014-06-24 00:12:27 -0700384 fStroke.setStrokeStyle(strokeWidth, false);
385 }
386 }
387
388 // Make glyph cache produce paths geometry for fill. We will stroke them
389 // by passing fStroke to drawPath. This is the fast path.
390 fSkPaint.setStyle(SkPaint::kFill_Style);
391 }
392 fStateRestore.set(fDrawTarget->drawState());
393
394 fDrawTarget->drawState()->setFromPaint(fPaint, fContext->getMatrix(),
395 fContext->getRenderTarget());
396
397 GR_STATIC_CONST_SAME_STENCIL(kStencilPass,
398 kZero_StencilOp,
399 kZero_StencilOp,
400 kNotEqual_StencilFunc,
401 0xffff,
402 0x0000,
403 0xffff);
404
405 *fDrawTarget->drawState()->stencil() = kStencilPass;
406
cdaltonb85a0aa2014-07-21 15:32:44 -0700407 SkASSERT(0 == fPendingGlyphCount);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700408}
409
cdaltonb2808cd2014-07-25 14:13:57 -0700410inline void GrStencilAndCoverTextContext::appendGlyph(uint16_t glyphID, float x) {
411 SkASSERT(GrDrawTarget::kTranslateX_PathTransformType == fTransformType);
412
cdaltonb85a0aa2014-07-21 15:32:44 -0700413 if (fPendingGlyphCount >= kGlyphBufferSize) {
414 this->flush();
415 }
416
417 fGlyphs->preloadGlyph(glyphID, fGlyphCache);
418
419 fIndexBuffer[fPendingGlyphCount] = glyphID;
cdaltonb2808cd2014-07-25 14:13:57 -0700420 fTransformBuffer[fPendingGlyphCount] = fTextInverseRatio * x;
421
422 ++fPendingGlyphCount;
423}
424
425inline void GrStencilAndCoverTextContext::appendGlyph(uint16_t glyphID, float x, float y) {
426 SkASSERT(GrDrawTarget::kTranslate_PathTransformType == fTransformType);
427
428 if (fPendingGlyphCount >= kGlyphBufferSize) {
429 this->flush();
430 }
431
432 fGlyphs->preloadGlyph(glyphID, fGlyphCache);
433
434 fIndexBuffer[fPendingGlyphCount] = glyphID;
435 fTransformBuffer[2 * fPendingGlyphCount] = fTextInverseRatio * x;
436 fTransformBuffer[2 * fPendingGlyphCount + 1] = fTextInverseRatio * y;
cdaltonb85a0aa2014-07-21 15:32:44 -0700437
438 ++fPendingGlyphCount;
439}
440
441void GrStencilAndCoverTextContext::flush() {
442 if (0 == fPendingGlyphCount) {
kkinnunenc6cb56f2014-06-24 00:12:27 -0700443 return;
444 }
445
cdaltonb85a0aa2014-07-21 15:32:44 -0700446 fDrawTarget->drawPaths(fGlyphs->pathRange(), fIndexBuffer, fPendingGlyphCount,
cdaltonb2808cd2014-07-25 14:13:57 -0700447 fTransformBuffer, fTransformType, SkPath::kWinding_FillType);
cdaltonb85a0aa2014-07-21 15:32:44 -0700448
449 fPendingGlyphCount = 0;
kkinnunenc6cb56f2014-06-24 00:12:27 -0700450}
451
452void GrStencilAndCoverTextContext::finish() {
cdaltonb85a0aa2014-07-21 15:32:44 -0700453 this->flush();
kkinnunenc6cb56f2014-06-24 00:12:27 -0700454
cdaltonb85a0aa2014-07-21 15:32:44 -0700455 SkSafeUnref(fGlyphs);
456 fGlyphs = NULL;
457 fGlyphCache = NULL;
kkinnunenc6cb56f2014-06-24 00:12:27 -0700458
459 fDrawTarget->drawState()->stencil()->setDisabled();
460 fStateRestore.set(NULL);
cdaltonb2808cd2014-07-25 14:13:57 -0700461 fContext->setMatrix(fContextInitialMatrix);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700462 GrTextContext::finish();
463}
464