blob: e43387e66fc898f2affd2039c35c21ea986a291a [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"
jvanverthaab626c2014-10-16 08:04:39 -070014#include "SkAutoKern.h"
kkinnunenc6cb56f2014-06-24 00:12:27 -070015#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)
cdalton20b373c2014-12-01 08:38:55 -080026 , fStroke(SkStrokeRec::kFill_InitStyle)
27 , fQueuedGlyphCount(0)
28 , fFallbackGlyphsIdx(kGlyphBufferSize) {
kkinnunenc6cb56f2014-06-24 00:12:27 -070029}
30
jvanverth8c27a182014-10-14 08:45:50 -070031GrStencilAndCoverTextContext* GrStencilAndCoverTextContext::Create(GrContext* context,
32 const SkDeviceProperties& props) {
33 GrStencilAndCoverTextContext* textContext = SkNEW_ARGS(GrStencilAndCoverTextContext,
34 (context, props));
35 textContext->fFallbackTextContext = GrBitmapTextContext::Create(context, props);
36
37 return textContext;
38}
39
kkinnunenc6cb56f2014-06-24 00:12:27 -070040GrStencilAndCoverTextContext::~GrStencilAndCoverTextContext() {
41}
42
joshualitt5531d512014-12-17 15:50:11 -080043bool GrStencilAndCoverTextContext::canDraw(const SkPaint& paint, const SkMatrix& viewMatrix) {
jvanverth0fedb192014-10-08 09:07:27 -070044 if (paint.getRasterizer()) {
45 return false;
46 }
47 if (paint.getMaskFilter()) {
48 return false;
49 }
50 if (paint.getPathEffect()) {
51 return false;
52 }
53
54 // No hairlines unless we can map the 1 px width to the object space.
55 if (paint.getStyle() == SkPaint::kStroke_Style
56 && paint.getStrokeWidth() == 0
joshualitt5531d512014-12-17 15:50:11 -080057 && viewMatrix.hasPerspective()) {
jvanverth0fedb192014-10-08 09:07:27 -070058 return false;
59 }
60
61 // No color bitmap fonts.
62 SkScalerContext::Rec rec;
63 SkScalerContext::MakeRec(paint, &fDeviceProperties, NULL, &rec);
64 return rec.getFormat() != SkMask::kARGB32_Format;
65}
66
joshualitt25d9c152015-02-18 12:29:52 -080067void GrStencilAndCoverTextContext::onDrawText(GrRenderTarget* rt,
joshualitt570d2f82015-02-25 13:19:48 -080068 const GrClip& clip,
joshualitt25d9c152015-02-18 12:29:52 -080069 const GrPaint& paint,
cdalton20b373c2014-12-01 08:38:55 -080070 const SkPaint& skPaint,
joshualitt5531d512014-12-17 15:50:11 -080071 const SkMatrix& viewMatrix,
cdalton20b373c2014-12-01 08:38:55 -080072 const char text[],
73 size_t byteLength,
74 SkScalar x, SkScalar y) {
jvanverthaab626c2014-10-16 08:04:39 -070075 SkASSERT(byteLength == 0 || text != NULL);
76
77 if (text == NULL || byteLength == 0 /*|| fRC->isEmpty()*/) {
78 return;
79 }
80
81 // This is the slow path, mainly used by Skia unit tests. The other
82 // backends (8888, gpu, ...) use device-space dependent glyph caches. In
83 // order to match the glyph positions that the other code paths produce, we
84 // must also use device-space dependent glyph cache. This has the
85 // side-effect that the glyph shape outline will be in device-space,
86 // too. This in turn has the side-effect that NVPR can not stroke the paths,
87 // as the stroke in NVPR is defined in object-space.
88 // NOTE: here we have following coincidence that works at the moment:
89 // - When using the device-space glyphs, the transforms we pass to NVPR
90 // instanced drawing are the global transforms, and the view transform is
91 // identity. NVPR can not use non-affine transforms in the instanced
92 // drawing. This is taken care of by SkDraw::ShouldDrawTextAsPaths since it
93 // will turn off the use of device-space glyphs when perspective transforms
94 // are in use.
95
joshualitt570d2f82015-02-25 13:19:48 -080096 this->init(rt, clip, paint, skPaint, byteLength, kMaxAccuracy_RenderMode, viewMatrix);
jvanverthaab626c2014-10-16 08:04:39 -070097
98 // Transform our starting point.
cdalton20b373c2014-12-01 08:38:55 -080099 if (fUsingDeviceSpaceGlyphs) {
jvanverthaab626c2014-10-16 08:04:39 -0700100 SkPoint loc;
101 fContextInitialMatrix.mapXY(x, y, &loc);
102 x = loc.fX;
103 y = loc.fY;
104 }
105
106 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
107
jvanverthaab626c2014-10-16 08:04:39 -0700108 const char* stop = text + byteLength;
109
110 // Measure first if needed.
111 if (fSkPaint.getTextAlign() != SkPaint::kLeft_Align) {
112 SkFixed stopX = 0;
113 SkFixed stopY = 0;
114
115 const char* textPtr = text;
116 while (textPtr < stop) {
117 // We don't need x, y here, since all subpixel variants will have the
118 // same advance.
119 const SkGlyph& glyph = glyphCacheProc(fGlyphCache, &textPtr, 0, 0);
120
121 stopX += glyph.fAdvanceX;
122 stopY += glyph.fAdvanceY;
123 }
124 SkASSERT(textPtr == stop);
125
126 SkScalar alignX = SkFixedToScalar(stopX) * fTextRatio;
127 SkScalar alignY = SkFixedToScalar(stopY) * fTextRatio;
128
129 if (fSkPaint.getTextAlign() == SkPaint::kCenter_Align) {
130 alignX = SkScalarHalf(alignX);
131 alignY = SkScalarHalf(alignY);
132 }
133
134 x -= alignX;
135 y -= alignY;
136 }
137
138 SkAutoKern autokern;
139
140 SkFixed fixedSizeRatio = SkScalarToFixed(fTextRatio);
141
142 SkFixed fx = SkScalarToFixed(x);
143 SkFixed fy = SkScalarToFixed(y);
144 while (text < stop) {
145 const SkGlyph& glyph = glyphCacheProc(fGlyphCache, &text, 0, 0);
146 fx += SkFixedMul_portable(autokern.adjust(glyph), fixedSizeRatio);
147 if (glyph.fWidth) {
cdalton20b373c2014-12-01 08:38:55 -0800148 this->appendGlyph(glyph, SkPoint::Make(SkFixedToScalar(fx), SkFixedToScalar(fy)));
jvanverthaab626c2014-10-16 08:04:39 -0700149 }
150
151 fx += SkFixedMul_portable(glyph.fAdvanceX, fixedSizeRatio);
152 fy += SkFixedMul_portable(glyph.fAdvanceY, fixedSizeRatio);
153 }
154
155 this->finish();
156}
157
joshualitt25d9c152015-02-18 12:29:52 -0800158void GrStencilAndCoverTextContext::onDrawPosText(GrRenderTarget* rt,
joshualitt570d2f82015-02-25 13:19:48 -0800159 const GrClip& clip,
joshualitt25d9c152015-02-18 12:29:52 -0800160 const GrPaint& paint,
cdalton20b373c2014-12-01 08:38:55 -0800161 const SkPaint& skPaint,
joshualitt5531d512014-12-17 15:50:11 -0800162 const SkMatrix& viewMatrix,
cdalton20b373c2014-12-01 08:38:55 -0800163 const char text[],
164 size_t byteLength,
165 const SkScalar pos[],
166 int scalarsPerPosition,
167 const SkPoint& offset) {
kkinnunenc6cb56f2014-06-24 00:12:27 -0700168 SkASSERT(byteLength == 0 || text != NULL);
169 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
170
171 // nothing to draw
172 if (text == NULL || byteLength == 0/* || fRC->isEmpty()*/) {
173 return;
174 }
175
176 // This is the fast path. Here we do not bake in the device-transform to
177 // the glyph outline or the advances. This is because we do not need to
178 // position the glyphs at all, since the caller has done the positioning.
179 // The positioning is based on SkPaint::measureText of individual
180 // glyphs. That already uses glyph cache without device transforms. Device
181 // transform is not part of SkPaint::measureText API, and thus we use the
182 // same glyphs as what were measured.
kkinnunenc6cb56f2014-06-24 00:12:27 -0700183
joshualitt570d2f82015-02-25 13:19:48 -0800184 this->init(rt, clip, paint, skPaint, byteLength, kMaxPerformance_RenderMode, viewMatrix);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700185
186 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
187
kkinnunenc6cb56f2014-06-24 00:12:27 -0700188 const char* stop = text + byteLength;
kkinnunenc6cb56f2014-06-24 00:12:27 -0700189
cdalton38e13ad2014-11-07 06:02:15 -0800190 SkTextMapStateProc tmsProc(SkMatrix::I(), offset, scalarsPerPosition);
bungeman79738cc2015-03-11 14:05:29 -0700191 SkTextAlignProc alignProc(fSkPaint.getTextAlign());
cdalton38e13ad2014-11-07 06:02:15 -0800192 while (text < stop) {
193 const SkGlyph& glyph = glyphCacheProc(fGlyphCache, &text, 0, 0);
194 if (glyph.fWidth) {
195 SkPoint tmsLoc;
196 tmsProc(pos, &tmsLoc);
197 SkPoint loc;
198 alignProc(tmsLoc, glyph, &loc);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700199
cdalton20b373c2014-12-01 08:38:55 -0800200 this->appendGlyph(glyph, loc);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700201 }
cdalton38e13ad2014-11-07 06:02:15 -0800202 pos += scalarsPerPosition;
kkinnunenc6cb56f2014-06-24 00:12:27 -0700203 }
204
205 this->finish();
206}
207
cdalton855d83f2014-09-18 13:51:53 -0700208static GrPathRange* get_gr_glyphs(GrContext* ctx,
209 const SkTypeface* typeface,
210 const SkDescriptor* desc,
211 const SkStrokeRec& stroke) {
bsalomon8718aaf2015-02-19 07:24:21 -0800212 static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
213 GrUniqueKey key;
214 GrUniqueKey::Builder builder(&key, kDomain, 4);
bsalomon24db3b12015-01-23 04:24:04 -0800215 struct GlyphKey {
216 uint32_t fChecksum;
217 uint32_t fTypeface;
218 uint64_t fStroke;
219 };
220 GlyphKey* glyphKey = reinterpret_cast<GlyphKey*>(&builder[0]);
221 glyphKey->fChecksum = desc ? desc->getChecksum() : 0;
222 glyphKey->fTypeface = typeface ? typeface->uniqueID() : 0;
223 glyphKey->fStroke = GrPath::ComputeStrokeKey(stroke);
224 builder.finish();
cdalton855d83f2014-09-18 13:51:53 -0700225
226 SkAutoTUnref<GrPathRange> glyphs(
bsalomon24db3b12015-01-23 04:24:04 -0800227 static_cast<GrPathRange*>(ctx->findAndRefCachedResource(key)));
cdalton855d83f2014-09-18 13:51:53 -0700228 if (NULL == glyphs || (NULL != desc && !glyphs->isEqualTo(*desc))) {
229 glyphs.reset(ctx->getGpu()->pathRendering()->createGlyphs(typeface, desc, stroke));
bsalomon24db3b12015-01-23 04:24:04 -0800230 ctx->addResourceToCache(key, glyphs);
cdalton855d83f2014-09-18 13:51:53 -0700231 }
232
233 return glyphs.detach();
234}
235
joshualitt25d9c152015-02-18 12:29:52 -0800236void GrStencilAndCoverTextContext::init(GrRenderTarget* rt,
joshualitt570d2f82015-02-25 13:19:48 -0800237 const GrClip& clip,
joshualitt25d9c152015-02-18 12:29:52 -0800238 const GrPaint& paint,
kkinnunenc6cb56f2014-06-24 00:12:27 -0700239 const SkPaint& skPaint,
cdaltonb2808cd2014-07-25 14:13:57 -0700240 size_t textByteLength,
joshualitt5531d512014-12-17 15:50:11 -0800241 RenderMode renderMode,
242 const SkMatrix& viewMatrix) {
joshualitt570d2f82015-02-25 13:19:48 -0800243 GrTextContext::init(rt, clip, paint, skPaint);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700244
joshualitt5531d512014-12-17 15:50:11 -0800245 fContextInitialMatrix = viewMatrix;
246 fViewMatrix = viewMatrix;
joshualitt290c09b2014-12-19 13:45:20 -0800247 fLocalMatrix = SkMatrix::I();
cdaltonb2808cd2014-07-25 14:13:57 -0700248
cdalton855d83f2014-09-18 13:51:53 -0700249 const bool otherBackendsWillDrawAsPaths =
cdaltonb2808cd2014-07-25 14:13:57 -0700250 SkDraw::ShouldDrawTextAsPaths(skPaint, fContextInitialMatrix);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700251
cdalton20b373c2014-12-01 08:38:55 -0800252 fUsingDeviceSpaceGlyphs = !otherBackendsWillDrawAsPaths &&
cdalton855d83f2014-09-18 13:51:53 -0700253 kMaxAccuracy_RenderMode == renderMode &&
254 SkToBool(fContextInitialMatrix.getType() &
255 (SkMatrix::kScale_Mask | SkMatrix::kAffine_Mask));
kkinnunenc6cb56f2014-06-24 00:12:27 -0700256
cdalton20b373c2014-12-01 08:38:55 -0800257 if (fUsingDeviceSpaceGlyphs) {
cdalton855d83f2014-09-18 13:51:53 -0700258 // SkDraw::ShouldDrawTextAsPaths takes care of perspective transforms.
259 SkASSERT(!fContextInitialMatrix.hasPerspective());
cdaltonb2808cd2014-07-25 14:13:57 -0700260
cdalton20b373c2014-12-01 08:38:55 -0800261 // The whole shape (including stroke) will be baked into the glyph outlines. Make
262 // NVPR just fill the baked shapes.
263 fStroke = SkStrokeRec(SkStrokeRec::kFill_InitStyle);
264
cdalton855d83f2014-09-18 13:51:53 -0700265 fTextRatio = fTextInverseRatio = 1.0f;
266
267 // Glyphs loaded by GPU path rendering have an inverted y-direction.
268 SkMatrix m;
269 m.setScale(1, -1);
joshualitt5531d512014-12-17 15:50:11 -0800270 fViewMatrix = m;
cdalton855d83f2014-09-18 13:51:53 -0700271
272 // Post-flip the initial matrix so we're left with just the flip after
273 // the paint preConcats the inverse.
274 m = fContextInitialMatrix;
275 m.postScale(1, -1);
joshualitt290c09b2014-12-19 13:45:20 -0800276 if (!m.invert(&fLocalMatrix)) {
277 SkDebugf("Not invertible!\n");
278 return;
279 }
cdalton855d83f2014-09-18 13:51:53 -0700280
cdaltone05162d2014-12-01 08:57:33 -0800281 fGlyphCache = fSkPaint.detachCache(&fDeviceProperties, &fContextInitialMatrix,
282 true /*ignoreGamma*/);
cdalton855d83f2014-09-18 13:51:53 -0700283 fGlyphs = get_gr_glyphs(fContext, fGlyphCache->getScalerContext()->getTypeface(),
cdalton20b373c2014-12-01 08:38:55 -0800284 &fGlyphCache->getDescriptor(), fStroke);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700285 } else {
cdalton855d83f2014-09-18 13:51:53 -0700286 // Don't bake strokes into the glyph outlines. We will stroke the glyphs
287 // using the GPU instead. This is the fast path.
cdalton20b373c2014-12-01 08:38:55 -0800288 fStroke = SkStrokeRec(fSkPaint);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700289 fSkPaint.setStyle(SkPaint::kFill_Style);
cdalton855d83f2014-09-18 13:51:53 -0700290
cdalton20b373c2014-12-01 08:38:55 -0800291 if (fStroke.isHairlineStyle()) {
cdalton855d83f2014-09-18 13:51:53 -0700292 // Approximate hairline stroke.
293 SkScalar strokeWidth = SK_Scalar1 /
294 (SkVector::Make(fContextInitialMatrix.getScaleX(),
295 fContextInitialMatrix.getSkewY()).length());
cdalton20b373c2014-12-01 08:38:55 -0800296 fStroke.setStrokeStyle(strokeWidth, false /*strokeAndFill*/);
cdalton855d83f2014-09-18 13:51:53 -0700297
298 } else if (fSkPaint.isFakeBoldText() &&
299#ifdef SK_USE_FREETYPE_EMBOLDEN
300 kMaxPerformance_RenderMode == renderMode &&
301#endif
cdalton20b373c2014-12-01 08:38:55 -0800302 SkStrokeRec::kStroke_Style != fStroke.getStyle()) {
cdalton855d83f2014-09-18 13:51:53 -0700303
304 // Instead of baking fake bold into the glyph outlines, do it with the GPU stroke.
305 SkScalar fakeBoldScale = SkScalarInterpFunc(fSkPaint.getTextSize(),
306 kStdFakeBoldInterpKeys,
307 kStdFakeBoldInterpValues,
308 kStdFakeBoldInterpLength);
309 SkScalar extra = SkScalarMul(fSkPaint.getTextSize(), fakeBoldScale);
cdalton20b373c2014-12-01 08:38:55 -0800310 fStroke.setStrokeStyle(fStroke.needToApply() ? fStroke.getWidth() + extra : extra,
311 true /*strokeAndFill*/);
cdalton855d83f2014-09-18 13:51:53 -0700312
313 fSkPaint.setFakeBoldText(false);
314 }
315
316 bool canUseRawPaths;
317
318 if (otherBackendsWillDrawAsPaths || kMaxPerformance_RenderMode == renderMode) {
319 // We can draw the glyphs from canonically sized paths.
320 fTextRatio = fSkPaint.getTextSize() / SkPaint::kCanonicalTextSizeForPaths;
321 fTextInverseRatio = SkPaint::kCanonicalTextSizeForPaths / fSkPaint.getTextSize();
322
323 // Compensate for the glyphs being scaled by fTextRatio.
cdalton20b373c2014-12-01 08:38:55 -0800324 if (!fStroke.isFillStyle()) {
325 fStroke.setStrokeStyle(fStroke.getWidth() / fTextRatio,
326 SkStrokeRec::kStrokeAndFill_Style == fStroke.getStyle());
cdalton855d83f2014-09-18 13:51:53 -0700327 }
328
329 fSkPaint.setLinearText(true);
330 fSkPaint.setLCDRenderText(false);
331 fSkPaint.setAutohinted(false);
332 fSkPaint.setHinting(SkPaint::kNo_Hinting);
333 fSkPaint.setSubpixelText(true);
334 fSkPaint.setTextSize(SkIntToScalar(SkPaint::kCanonicalTextSizeForPaths));
335
336 canUseRawPaths = SK_Scalar1 == fSkPaint.getTextScaleX() &&
337 0 == fSkPaint.getTextSkewX() &&
338 !fSkPaint.isFakeBoldText() &&
339 !fSkPaint.isVerticalText();
340 } else {
341 fTextRatio = fTextInverseRatio = 1.0f;
342 canUseRawPaths = false;
343 }
344
345 SkMatrix textMatrix;
cdalton855d83f2014-09-18 13:51:53 -0700346 // Glyphs loaded by GPU path rendering have an inverted y-direction.
cdalton38e13ad2014-11-07 06:02:15 -0800347 textMatrix.setScale(fTextRatio, -fTextRatio);
joshualitt5531d512014-12-17 15:50:11 -0800348 fViewMatrix.preConcat(textMatrix);
joshualitt290c09b2014-12-19 13:45:20 -0800349 fLocalMatrix = textMatrix;
cdalton855d83f2014-09-18 13:51:53 -0700350
cdaltone05162d2014-12-01 08:57:33 -0800351 fGlyphCache = fSkPaint.detachCache(&fDeviceProperties, NULL, true /*ignoreGamma*/);
cdalton855d83f2014-09-18 13:51:53 -0700352 fGlyphs = canUseRawPaths ?
cdalton20b373c2014-12-01 08:38:55 -0800353 get_gr_glyphs(fContext, fSkPaint.getTypeface(), NULL, fStroke) :
cdalton855d83f2014-09-18 13:51:53 -0700354 get_gr_glyphs(fContext, fGlyphCache->getScalerContext()->getTypeface(),
cdalton20b373c2014-12-01 08:38:55 -0800355 &fGlyphCache->getDescriptor(), fStroke);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700356 }
cdalton855d83f2014-09-18 13:51:53 -0700357
egdaniel8dd688b2015-01-22 10:16:09 -0800358 fStateRestore.set(&fPipelineBuilder);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700359
joshualitt44701df2015-02-23 14:44:57 -0800360 fPipelineBuilder.setFromPaint(fPaint, fRenderTarget, fClip);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700361
362 GR_STATIC_CONST_SAME_STENCIL(kStencilPass,
363 kZero_StencilOp,
364 kZero_StencilOp,
365 kNotEqual_StencilFunc,
366 0xffff,
367 0x0000,
368 0xffff);
369
egdaniel8dd688b2015-01-22 10:16:09 -0800370 *fPipelineBuilder.stencil() = kStencilPass;
kkinnunenc6cb56f2014-06-24 00:12:27 -0700371
cdalton20b373c2014-12-01 08:38:55 -0800372 SkASSERT(0 == fQueuedGlyphCount);
373 SkASSERT(kGlyphBufferSize == fFallbackGlyphsIdx);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700374}
375
joshualitt5531d512014-12-17 15:50:11 -0800376bool GrStencilAndCoverTextContext::mapToFallbackContext(SkMatrix* inverse) {
cdalton20b373c2014-12-01 08:38:55 -0800377 // The current view matrix is flipped because GPU path rendering glyphs have an
378 // inverted y-direction. Unflip the view matrix for the fallback context. If using
379 // device-space glyphs, we'll also need to restore the original view matrix since
380 // we moved that transfomation into our local glyph cache for this scenario. Also
381 // track the inverse operation so the caller can unmap the paint and glyph positions.
382 if (fUsingDeviceSpaceGlyphs) {
joshualitt5531d512014-12-17 15:50:11 -0800383 fViewMatrix = fContextInitialMatrix;
cdalton20b373c2014-12-01 08:38:55 -0800384 if (!fContextInitialMatrix.invert(inverse)) {
385 return false;
386 }
387 inverse->preScale(1, -1);
388 } else {
389 inverse->setScale(1, -1);
390 const SkMatrix& unflip = *inverse; // unflip is equal to its own inverse.
joshualitt5531d512014-12-17 15:50:11 -0800391 fViewMatrix.preConcat(unflip);
cdalton20b373c2014-12-01 08:38:55 -0800392 }
393 return true;
394}
395
396inline void GrStencilAndCoverTextContext::appendGlyph(const SkGlyph& glyph, const SkPoint& pos) {
397 if (fQueuedGlyphCount >= fFallbackGlyphsIdx) {
398 SkASSERT(fQueuedGlyphCount == fFallbackGlyphsIdx);
cdaltonb2808cd2014-07-25 14:13:57 -0700399 this->flush();
400 }
401
cdalton20b373c2014-12-01 08:38:55 -0800402 // Stick the glyphs we can't draw at the end of the buffer, growing backwards.
403 int index = (SkMask::kARGB32_Format == glyph.fMaskFormat) ?
404 --fFallbackGlyphsIdx : fQueuedGlyphCount++;
cdaltonb85a0aa2014-07-21 15:32:44 -0700405
cdalton20b373c2014-12-01 08:38:55 -0800406 fGlyphIndices[index] = glyph.getGlyphID();
407 fGlyphPositions[index].set(fTextInverseRatio * pos.x(), -fTextInverseRatio * pos.y());
408}
409
410static const SkScalar* get_xy_scalar_array(const SkPoint* pointArray) {
411 GR_STATIC_ASSERT(2 * sizeof(SkScalar) == sizeof(SkPoint));
412 GR_STATIC_ASSERT(0 == offsetof(SkPoint, fX));
413
414 return &pointArray[0].fX;
cdaltonb85a0aa2014-07-21 15:32:44 -0700415}
416
417void GrStencilAndCoverTextContext::flush() {
cdalton20b373c2014-12-01 08:38:55 -0800418 if (fQueuedGlyphCount > 0) {
joshualitt8059eb92014-12-29 15:10:07 -0800419 SkAutoTUnref<GrPathProcessor> pp(GrPathProcessor::Create(fPaint.getColor(),
420 fViewMatrix,
421 fLocalMatrix));
egdaniel8dd688b2015-01-22 10:16:09 -0800422 fDrawTarget->drawPaths(&fPipelineBuilder, pp, fGlyphs,
cdalton20b373c2014-12-01 08:38:55 -0800423 fGlyphIndices, GrPathRange::kU16_PathIndexType,
424 get_xy_scalar_array(fGlyphPositions),
425 GrPathRendering::kTranslate_PathTransformType,
426 fQueuedGlyphCount, GrPathRendering::kWinding_FillType);
427
428 fQueuedGlyphCount = 0;
kkinnunenc6cb56f2014-06-24 00:12:27 -0700429 }
430
cdalton20b373c2014-12-01 08:38:55 -0800431 if (fFallbackGlyphsIdx < kGlyphBufferSize) {
432 int fallbackGlyphCount = kGlyphBufferSize - fFallbackGlyphsIdx;
cdaltonb85a0aa2014-07-21 15:32:44 -0700433
cdalton20b373c2014-12-01 08:38:55 -0800434 GrPaint paintFallback(fPaint);
435
436 SkPaint skPaintFallback(fSkPaint);
437 if (!fUsingDeviceSpaceGlyphs) {
438 fStroke.applyToPaint(&skPaintFallback);
439 }
440 skPaintFallback.setTextAlign(SkPaint::kLeft_Align); // Align has already been accounted for.
441 skPaintFallback.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
442
cdalton20b373c2014-12-01 08:38:55 -0800443 SkMatrix inverse;
joshualitt5531d512014-12-17 15:50:11 -0800444 if (this->mapToFallbackContext(&inverse)) {
cdalton20b373c2014-12-01 08:38:55 -0800445 inverse.mapPoints(&fGlyphPositions[fFallbackGlyphsIdx], fallbackGlyphCount);
446 }
447
joshualitt570d2f82015-02-25 13:19:48 -0800448 fFallbackTextContext->drawPosText(fRenderTarget, fClip, paintFallback, skPaintFallback,
joshualitt25d9c152015-02-18 12:29:52 -0800449 fViewMatrix, (char*)&fGlyphIndices[fFallbackGlyphsIdx],
cdalton20b373c2014-12-01 08:38:55 -0800450 2 * fallbackGlyphCount,
451 get_xy_scalar_array(&fGlyphPositions[fFallbackGlyphsIdx]),
452 2, SkPoint::Make(0, 0));
453
454 fFallbackGlyphsIdx = kGlyphBufferSize;
455 }
kkinnunenc6cb56f2014-06-24 00:12:27 -0700456}
457
458void GrStencilAndCoverTextContext::finish() {
cdaltonb85a0aa2014-07-21 15:32:44 -0700459 this->flush();
kkinnunenc6cb56f2014-06-24 00:12:27 -0700460
cdalton855d83f2014-09-18 13:51:53 -0700461 fGlyphs->unref();
cdaltonb85a0aa2014-07-21 15:32:44 -0700462 fGlyphs = NULL;
cdalton855d83f2014-09-18 13:51:53 -0700463
464 SkGlyphCache::AttachCache(fGlyphCache);
cdaltonb85a0aa2014-07-21 15:32:44 -0700465 fGlyphCache = NULL;
kkinnunenc6cb56f2014-06-24 00:12:27 -0700466
egdaniel8dd688b2015-01-22 10:16:09 -0800467 fPipelineBuilder.stencil()->setDisabled();
kkinnunenc6cb56f2014-06-24 00:12:27 -0700468 fStateRestore.set(NULL);
joshualitt5531d512014-12-17 15:50:11 -0800469 fViewMatrix = fContextInitialMatrix;
kkinnunenc6cb56f2014-06-24 00:12:27 -0700470 GrTextContext::finish();
471}
472