blob: b53ff797fa4718616fbab42936faf96eb4ba965b [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"
jvanverth8c27a182014-10-14 08:45:50 -07009#include "GrBitmapTextContext.h"
kkinnunenc6cb56f2014-06-24 00:12:27 -070010#include "GrDrawTarget.h"
kkinnunenc6cb56f2014-06-24 00:12:27 -070011#include "GrGpu.h"
12#include "GrPath.h"
cdaltonb85a0aa2014-07-21 15:32:44 -070013#include "GrPathRange.h"
kkinnunenc6cb56f2014-06-24 00:12:27 -070014#include "SkAutoKern.h"
15#include "SkDraw.h"
16#include "SkDrawProcs.h"
17#include "SkGlyphCache.h"
18#include "SkGpuDevice.h"
19#include "SkPath.h"
20#include "SkTextMapStateProc.h"
cdalton855d83f2014-09-18 13:51:53 -070021#include "SkTextFormatParams.h"
kkinnunenc6cb56f2014-06-24 00:12:27 -070022
23GrStencilAndCoverTextContext::GrStencilAndCoverTextContext(
24 GrContext* context, const SkDeviceProperties& properties)
25 : GrTextContext(context, properties)
cdaltonb85a0aa2014-07-21 15:32:44 -070026 , fPendingGlyphCount(0) {
kkinnunenc6cb56f2014-06-24 00:12:27 -070027}
28
jvanverth8c27a182014-10-14 08:45:50 -070029GrStencilAndCoverTextContext* GrStencilAndCoverTextContext::Create(GrContext* context,
30 const SkDeviceProperties& props) {
31 GrStencilAndCoverTextContext* textContext = SkNEW_ARGS(GrStencilAndCoverTextContext,
32 (context, props));
33 textContext->fFallbackTextContext = GrBitmapTextContext::Create(context, props);
34
35 return textContext;
36}
37
kkinnunenc6cb56f2014-06-24 00:12:27 -070038GrStencilAndCoverTextContext::~GrStencilAndCoverTextContext() {
39}
40
jvanverth0fedb192014-10-08 09:07:27 -070041bool GrStencilAndCoverTextContext::canDraw(const SkPaint& paint) {
42 if (paint.getRasterizer()) {
43 return false;
44 }
45 if (paint.getMaskFilter()) {
46 return false;
47 }
48 if (paint.getPathEffect()) {
49 return false;
50 }
51
52 // No hairlines unless we can map the 1 px width to the object space.
53 if (paint.getStyle() == SkPaint::kStroke_Style
54 && paint.getStrokeWidth() == 0
55 && fContext->getMatrix().hasPerspective()) {
56 return false;
57 }
58
59 // No color bitmap fonts.
60 SkScalerContext::Rec rec;
61 SkScalerContext::MakeRec(paint, &fDeviceProperties, NULL, &rec);
62 return rec.getFormat() != SkMask::kARGB32_Format;
63}
64
jvanverth8c27a182014-10-14 08:45:50 -070065void GrStencilAndCoverTextContext::onDrawText(const GrPaint& paint,
kkinnunenc6cb56f2014-06-24 00:12:27 -070066 const SkPaint& skPaint,
67 const char text[],
68 size_t byteLength,
69 SkScalar x, SkScalar y) {
70 SkASSERT(byteLength == 0 || text != NULL);
71
72 if (text == NULL || byteLength == 0 /*|| fRC->isEmpty()*/) {
73 return;
74 }
75
76 // This is the slow path, mainly used by Skia unit tests. The other
77 // backends (8888, gpu, ...) use device-space dependent glyph caches. In
78 // order to match the glyph positions that the other code paths produce, we
79 // must also use device-space dependent glyph cache. This has the
80 // side-effect that the glyph shape outline will be in device-space,
81 // too. This in turn has the side-effect that NVPR can not stroke the paths,
82 // as the stroke in NVPR is defined in object-space.
83 // NOTE: here we have following coincidence that works at the moment:
84 // - When using the device-space glyphs, the transforms we pass to NVPR
85 // instanced drawing are the global transforms, and the view transform is
86 // identity. NVPR can not use non-affine transforms in the instanced
87 // drawing. This is taken care of by SkDraw::ShouldDrawTextAsPaths since it
88 // will turn off the use of device-space glyphs when perspective transforms
89 // are in use.
90
fmalita05c4a432014-09-29 06:29:53 -070091 this->init(paint, skPaint, byteLength, kMaxAccuracy_RenderMode, SkPoint::Make(0, 0));
kkinnunenc6cb56f2014-06-24 00:12:27 -070092
kkinnunenc6cb56f2014-06-24 00:12:27 -070093 // Transform our starting point.
94 if (fNeedsDeviceSpaceGlyphs) {
95 SkPoint loc;
cdaltonb2808cd2014-07-25 14:13:57 -070096 fContextInitialMatrix.mapXY(x, y, &loc);
kkinnunenc6cb56f2014-06-24 00:12:27 -070097 x = loc.fX;
98 y = loc.fY;
kkinnunenc6cb56f2014-06-24 00:12:27 -070099 }
100
101 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
cdalton855d83f2014-09-18 13:51:53 -0700102
kkinnunenccdaa042014-08-20 01:36:23 -0700103 fTransformType = GrPathRendering::kTranslate_PathTransformType;
kkinnunenc6cb56f2014-06-24 00:12:27 -0700104
105 const char* stop = text + byteLength;
106
107 // Measure first if needed.
108 if (fSkPaint.getTextAlign() != SkPaint::kLeft_Align) {
109 SkFixed stopX = 0;
110 SkFixed stopY = 0;
111
112 const char* textPtr = text;
113 while (textPtr < stop) {
114 // We don't need x, y here, since all subpixel variants will have the
115 // same advance.
cdaltonb85a0aa2014-07-21 15:32:44 -0700116 const SkGlyph& glyph = glyphCacheProc(fGlyphCache, &textPtr, 0, 0);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700117
118 stopX += glyph.fAdvanceX;
119 stopY += glyph.fAdvanceY;
120 }
121 SkASSERT(textPtr == stop);
122
123 SkScalar alignX = SkFixedToScalar(stopX) * fTextRatio;
124 SkScalar alignY = SkFixedToScalar(stopY) * fTextRatio;
125
126 if (fSkPaint.getTextAlign() == SkPaint::kCenter_Align) {
127 alignX = SkScalarHalf(alignX);
128 alignY = SkScalarHalf(alignY);
129 }
130
131 x -= alignX;
132 y -= alignY;
133 }
134
135 SkAutoKern autokern;
136
137 SkFixed fixedSizeRatio = SkScalarToFixed(fTextRatio);
138
139 SkFixed fx = SkScalarToFixed(x);
140 SkFixed fy = SkScalarToFixed(y);
141 while (text < stop) {
cdaltonb85a0aa2014-07-21 15:32:44 -0700142 const SkGlyph& glyph = glyphCacheProc(fGlyphCache, &text, 0, 0);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700143 fx += SkFixedMul_portable(autokern.adjust(glyph), fixedSizeRatio);
144 if (glyph.fWidth) {
cdaltonb85a0aa2014-07-21 15:32:44 -0700145 this->appendGlyph(glyph.getGlyphID(), SkFixedToScalar(fx), SkFixedToScalar(fy));
kkinnunenc6cb56f2014-06-24 00:12:27 -0700146 }
147
148 fx += SkFixedMul_portable(glyph.fAdvanceX, fixedSizeRatio);
149 fy += SkFixedMul_portable(glyph.fAdvanceY, fixedSizeRatio);
150 }
151
152 this->finish();
153}
154
jvanverth8c27a182014-10-14 08:45:50 -0700155void GrStencilAndCoverTextContext::onDrawPosText(const GrPaint& paint,
kkinnunenc6cb56f2014-06-24 00:12:27 -0700156 const SkPaint& skPaint,
157 const char text[],
158 size_t byteLength,
159 const SkScalar pos[],
fmalita05c4a432014-09-29 06:29:53 -0700160 int scalarsPerPosition,
161 const SkPoint& offset) {
kkinnunenc6cb56f2014-06-24 00:12:27 -0700162 SkASSERT(byteLength == 0 || text != NULL);
163 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
164
165 // nothing to draw
166 if (text == NULL || byteLength == 0/* || fRC->isEmpty()*/) {
167 return;
168 }
169
170 // This is the fast path. Here we do not bake in the device-transform to
171 // the glyph outline or the advances. This is because we do not need to
172 // position the glyphs at all, since the caller has done the positioning.
173 // The positioning is based on SkPaint::measureText of individual
174 // glyphs. That already uses glyph cache without device transforms. Device
175 // transform is not part of SkPaint::measureText API, and thus we use the
176 // same glyphs as what were measured.
kkinnunenc6cb56f2014-06-24 00:12:27 -0700177
fmalita05c4a432014-09-29 06:29:53 -0700178 this->init(paint, skPaint, byteLength, kMaxPerformance_RenderMode, offset);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700179
180 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
181
kkinnunenc6cb56f2014-06-24 00:12:27 -0700182 const char* stop = text + byteLength;
kkinnunenc6cb56f2014-06-24 00:12:27 -0700183
184 if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) {
cdaltonb2808cd2014-07-25 14:13:57 -0700185 if (1 == scalarsPerPosition) {
kkinnunenccdaa042014-08-20 01:36:23 -0700186 fTransformType = GrPathRendering::kTranslateX_PathTransformType;
cdaltonb2808cd2014-07-25 14:13:57 -0700187 while (text < stop) {
188 const SkGlyph& glyph = glyphCacheProc(fGlyphCache, &text, 0, 0);
189 if (glyph.fWidth) {
190 this->appendGlyph(glyph.getGlyphID(), *pos);
191 }
192 pos++;
kkinnunenc6cb56f2014-06-24 00:12:27 -0700193 }
cdaltonb2808cd2014-07-25 14:13:57 -0700194 } else {
195 SkASSERT(2 == scalarsPerPosition);
kkinnunenccdaa042014-08-20 01:36:23 -0700196 fTransformType = GrPathRendering::kTranslate_PathTransformType;
cdaltonb2808cd2014-07-25 14:13:57 -0700197 while (text < stop) {
198 const SkGlyph& glyph = glyphCacheProc(fGlyphCache, &text, 0, 0);
199 if (glyph.fWidth) {
200 this->appendGlyph(glyph.getGlyphID(), pos[0], pos[1]);
201 }
202 pos += 2;
203 }
kkinnunenc6cb56f2014-06-24 00:12:27 -0700204 }
205 } else {
kkinnunenccdaa042014-08-20 01:36:23 -0700206 fTransformType = GrPathRendering::kTranslate_PathTransformType;
fmalita05c4a432014-09-29 06:29:53 -0700207 SkTextMapStateProc tmsProc(SkMatrix::I(), SkPoint::Make(0, 0), scalarsPerPosition);
cdaltonb2808cd2014-07-25 14:13:57 -0700208 SkTextAlignProcScalar alignProc(fSkPaint.getTextAlign());
kkinnunenc6cb56f2014-06-24 00:12:27 -0700209 while (text < stop) {
cdaltonb85a0aa2014-07-21 15:32:44 -0700210 const SkGlyph& glyph = glyphCacheProc(fGlyphCache, &text, 0, 0);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700211 if (glyph.fWidth) {
212 SkPoint tmsLoc;
213 tmsProc(pos, &tmsLoc);
214 SkPoint loc;
215 alignProc(tmsLoc, glyph, &loc);
216
cdaltonb85a0aa2014-07-21 15:32:44 -0700217 this->appendGlyph(glyph.getGlyphID(), loc.x(), loc.y());
kkinnunenc6cb56f2014-06-24 00:12:27 -0700218 }
219 pos += scalarsPerPosition;
220 }
221 }
222
223 this->finish();
224}
225
cdalton855d83f2014-09-18 13:51:53 -0700226static GrPathRange* get_gr_glyphs(GrContext* ctx,
227 const SkTypeface* typeface,
228 const SkDescriptor* desc,
229 const SkStrokeRec& stroke) {
230 static const GrCacheID::Domain gGlyphsDomain = GrCacheID::GenerateDomain();
231
232 GrCacheID::Key key;
233 uint64_t* keyData = key.fData64;
234 keyData[0] = desc ? desc->getChecksum() : 0;
235 keyData[0] = (keyData[0] << 32) | (typeface ? typeface->uniqueID() : 0);
236 keyData[1] = GrPath::ComputeStrokeKey(stroke);
237 GrResourceKey resourceKey = GrResourceKey(GrCacheID(gGlyphsDomain, key),
238 GrPathRange::resourceType(), 0);
239
240 SkAutoTUnref<GrPathRange> glyphs(
241 static_cast<GrPathRange*>(ctx->findAndRefCachedResource(resourceKey)));
242 if (NULL == glyphs || (NULL != desc && !glyphs->isEqualTo(*desc))) {
243 glyphs.reset(ctx->getGpu()->pathRendering()->createGlyphs(typeface, desc, stroke));
244 ctx->addResourceToCache(resourceKey, glyphs);
245 }
246
247 return glyphs.detach();
248}
249
kkinnunenc6cb56f2014-06-24 00:12:27 -0700250void GrStencilAndCoverTextContext::init(const GrPaint& paint,
251 const SkPaint& skPaint,
cdaltonb2808cd2014-07-25 14:13:57 -0700252 size_t textByteLength,
cdalton855d83f2014-09-18 13:51:53 -0700253 RenderMode renderMode,
fmalita05c4a432014-09-29 06:29:53 -0700254 const SkPoint& textTranslate) {
kkinnunenc6cb56f2014-06-24 00:12:27 -0700255 GrTextContext::init(paint, skPaint);
256
cdaltonb2808cd2014-07-25 14:13:57 -0700257 fContextInitialMatrix = fContext->getMatrix();
258
cdalton855d83f2014-09-18 13:51:53 -0700259 const bool otherBackendsWillDrawAsPaths =
cdaltonb2808cd2014-07-25 14:13:57 -0700260 SkDraw::ShouldDrawTextAsPaths(skPaint, fContextInitialMatrix);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700261
cdalton855d83f2014-09-18 13:51:53 -0700262 fNeedsDeviceSpaceGlyphs = !otherBackendsWillDrawAsPaths &&
263 kMaxAccuracy_RenderMode == renderMode &&
264 SkToBool(fContextInitialMatrix.getType() &
265 (SkMatrix::kScale_Mask | SkMatrix::kAffine_Mask));
kkinnunenc6cb56f2014-06-24 00:12:27 -0700266
267 if (fNeedsDeviceSpaceGlyphs) {
cdalton855d83f2014-09-18 13:51:53 -0700268 // SkDraw::ShouldDrawTextAsPaths takes care of perspective transforms.
269 SkASSERT(!fContextInitialMatrix.hasPerspective());
fmalita05c4a432014-09-29 06:29:53 -0700270 SkASSERT(textTranslate.isZero()); // TODO: Handle textTranslate in device-space usecase.
cdaltonb2808cd2014-07-25 14:13:57 -0700271
cdalton855d83f2014-09-18 13:51:53 -0700272 fTextRatio = fTextInverseRatio = 1.0f;
273
274 // Glyphs loaded by GPU path rendering have an inverted y-direction.
275 SkMatrix m;
276 m.setScale(1, -1);
277 fContext->setMatrix(m);
278
279 // Post-flip the initial matrix so we're left with just the flip after
280 // the paint preConcats the inverse.
281 m = fContextInitialMatrix;
282 m.postScale(1, -1);
283 fPaint.localCoordChangeInverse(m);
284
285 // The whole shape (including stroke) will be baked into the glyph outlines. Make
286 // NVPR just fill the baked shapes.
287 fGlyphCache = fSkPaint.detachCache(&fDeviceProperties, &fContextInitialMatrix, false);
288 fGlyphs = get_gr_glyphs(fContext, fGlyphCache->getScalerContext()->getTypeface(),
289 &fGlyphCache->getDescriptor(),
290 SkStrokeRec(SkStrokeRec::kFill_InitStyle));
kkinnunenc6cb56f2014-06-24 00:12:27 -0700291 } else {
cdalton855d83f2014-09-18 13:51:53 -0700292 // Don't bake strokes into the glyph outlines. We will stroke the glyphs
293 // using the GPU instead. This is the fast path.
294 SkStrokeRec gpuStroke = SkStrokeRec(fSkPaint);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700295 fSkPaint.setStyle(SkPaint::kFill_Style);
cdalton855d83f2014-09-18 13:51:53 -0700296
297 if (gpuStroke.isHairlineStyle()) {
298 // Approximate hairline stroke.
299 SkScalar strokeWidth = SK_Scalar1 /
300 (SkVector::Make(fContextInitialMatrix.getScaleX(),
301 fContextInitialMatrix.getSkewY()).length());
302 gpuStroke.setStrokeStyle(strokeWidth, false /*strokeAndFill*/);
303
304 } else if (fSkPaint.isFakeBoldText() &&
305#ifdef SK_USE_FREETYPE_EMBOLDEN
306 kMaxPerformance_RenderMode == renderMode &&
307#endif
308 SkStrokeRec::kStroke_Style != gpuStroke.getStyle()) {
309
310 // Instead of baking fake bold into the glyph outlines, do it with the GPU stroke.
311 SkScalar fakeBoldScale = SkScalarInterpFunc(fSkPaint.getTextSize(),
312 kStdFakeBoldInterpKeys,
313 kStdFakeBoldInterpValues,
314 kStdFakeBoldInterpLength);
315 SkScalar extra = SkScalarMul(fSkPaint.getTextSize(), fakeBoldScale);
316 gpuStroke.setStrokeStyle(gpuStroke.needToApply() ? gpuStroke.getWidth() + extra : extra,
317 true /*strokeAndFill*/);
318
319 fSkPaint.setFakeBoldText(false);
320 }
321
322 bool canUseRawPaths;
323
324 if (otherBackendsWillDrawAsPaths || kMaxPerformance_RenderMode == renderMode) {
325 // We can draw the glyphs from canonically sized paths.
326 fTextRatio = fSkPaint.getTextSize() / SkPaint::kCanonicalTextSizeForPaths;
327 fTextInverseRatio = SkPaint::kCanonicalTextSizeForPaths / fSkPaint.getTextSize();
328
329 // Compensate for the glyphs being scaled by fTextRatio.
330 if (!gpuStroke.isFillStyle()) {
331 gpuStroke.setStrokeStyle(gpuStroke.getWidth() / fTextRatio,
332 SkStrokeRec::kStrokeAndFill_Style == gpuStroke.getStyle());
333 }
334
335 fSkPaint.setLinearText(true);
336 fSkPaint.setLCDRenderText(false);
337 fSkPaint.setAutohinted(false);
338 fSkPaint.setHinting(SkPaint::kNo_Hinting);
339 fSkPaint.setSubpixelText(true);
340 fSkPaint.setTextSize(SkIntToScalar(SkPaint::kCanonicalTextSizeForPaths));
341
342 canUseRawPaths = SK_Scalar1 == fSkPaint.getTextScaleX() &&
343 0 == fSkPaint.getTextSkewX() &&
344 !fSkPaint.isFakeBoldText() &&
345 !fSkPaint.isVerticalText();
346 } else {
347 fTextRatio = fTextInverseRatio = 1.0f;
348 canUseRawPaths = false;
349 }
350
351 SkMatrix textMatrix;
fmalita05c4a432014-09-29 06:29:53 -0700352 textMatrix.setTranslate(textTranslate.x(), textTranslate.y());
cdalton855d83f2014-09-18 13:51:53 -0700353 // Glyphs loaded by GPU path rendering have an inverted y-direction.
354 textMatrix.preScale(fTextRatio, -fTextRatio);
355 fPaint.localCoordChange(textMatrix);
356 fContext->concatMatrix(textMatrix);
357
358 fGlyphCache = fSkPaint.detachCache(&fDeviceProperties, NULL, false);
359 fGlyphs = canUseRawPaths ?
360 get_gr_glyphs(fContext, fSkPaint.getTypeface(), NULL, gpuStroke) :
361 get_gr_glyphs(fContext, fGlyphCache->getScalerContext()->getTypeface(),
362 &fGlyphCache->getDescriptor(), gpuStroke);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700363 }
cdalton855d83f2014-09-18 13:51:53 -0700364
kkinnunenc6cb56f2014-06-24 00:12:27 -0700365 fStateRestore.set(fDrawTarget->drawState());
366
367 fDrawTarget->drawState()->setFromPaint(fPaint, fContext->getMatrix(),
368 fContext->getRenderTarget());
369
370 GR_STATIC_CONST_SAME_STENCIL(kStencilPass,
371 kZero_StencilOp,
372 kZero_StencilOp,
373 kNotEqual_StencilFunc,
374 0xffff,
375 0x0000,
376 0xffff);
377
378 *fDrawTarget->drawState()->stencil() = kStencilPass;
379
cdaltonb85a0aa2014-07-21 15:32:44 -0700380 SkASSERT(0 == fPendingGlyphCount);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700381}
382
cdaltonb2808cd2014-07-25 14:13:57 -0700383inline void GrStencilAndCoverTextContext::appendGlyph(uint16_t glyphID, float x) {
kkinnunenccdaa042014-08-20 01:36:23 -0700384 SkASSERT(GrPathRendering::kTranslateX_PathTransformType == fTransformType);
cdaltonb2808cd2014-07-25 14:13:57 -0700385
cdaltonb85a0aa2014-07-21 15:32:44 -0700386 if (fPendingGlyphCount >= kGlyphBufferSize) {
387 this->flush();
388 }
389
cdaltonb85a0aa2014-07-21 15:32:44 -0700390 fIndexBuffer[fPendingGlyphCount] = glyphID;
cdaltonb2808cd2014-07-25 14:13:57 -0700391 fTransformBuffer[fPendingGlyphCount] = fTextInverseRatio * x;
392
393 ++fPendingGlyphCount;
394}
395
396inline void GrStencilAndCoverTextContext::appendGlyph(uint16_t glyphID, float x, float y) {
kkinnunenccdaa042014-08-20 01:36:23 -0700397 SkASSERT(GrPathRendering::kTranslate_PathTransformType == fTransformType);
cdaltonb2808cd2014-07-25 14:13:57 -0700398
399 if (fPendingGlyphCount >= kGlyphBufferSize) {
400 this->flush();
401 }
402
cdaltonb2808cd2014-07-25 14:13:57 -0700403 fIndexBuffer[fPendingGlyphCount] = glyphID;
404 fTransformBuffer[2 * fPendingGlyphCount] = fTextInverseRatio * x;
cdalton855d83f2014-09-18 13:51:53 -0700405 fTransformBuffer[2 * fPendingGlyphCount + 1] = -fTextInverseRatio * y;
cdaltonb85a0aa2014-07-21 15:32:44 -0700406
407 ++fPendingGlyphCount;
408}
409
410void GrStencilAndCoverTextContext::flush() {
411 if (0 == fPendingGlyphCount) {
kkinnunenc6cb56f2014-06-24 00:12:27 -0700412 return;
413 }
414
cdalton855d83f2014-09-18 13:51:53 -0700415 fDrawTarget->drawPaths(fGlyphs, fIndexBuffer, fPendingGlyphCount,
cdaltonb2808cd2014-07-25 14:13:57 -0700416 fTransformBuffer, fTransformType, SkPath::kWinding_FillType);
cdaltonb85a0aa2014-07-21 15:32:44 -0700417
418 fPendingGlyphCount = 0;
kkinnunenc6cb56f2014-06-24 00:12:27 -0700419}
420
421void GrStencilAndCoverTextContext::finish() {
cdaltonb85a0aa2014-07-21 15:32:44 -0700422 this->flush();
kkinnunenc6cb56f2014-06-24 00:12:27 -0700423
cdalton855d83f2014-09-18 13:51:53 -0700424 fGlyphs->unref();
cdaltonb85a0aa2014-07-21 15:32:44 -0700425 fGlyphs = NULL;
cdalton855d83f2014-09-18 13:51:53 -0700426
427 SkGlyphCache::AttachCache(fGlyphCache);
cdaltonb85a0aa2014-07-21 15:32:44 -0700428 fGlyphCache = NULL;
kkinnunenc6cb56f2014-06-24 00:12:27 -0700429
430 fDrawTarget->drawState()->stencil()->setDisabled();
431 fStateRestore.set(NULL);
cdaltonb2808cd2014-07-25 14:13:57 -0700432 fContext->setMatrix(fContextInitialMatrix);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700433 GrTextContext::finish();
434}
435