blob: 172a856eb11871f70fddc8eaf5a4dcf003bf78b4 [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
joshualitt6e8cd962015-03-20 10:30:14 -070023GrStencilAndCoverTextContext::GrStencilAndCoverTextContext(GrContext* context,
24 SkGpuDevice* gpuDevice,
25 const SkDeviceProperties& properties)
26 : GrTextContext(context, gpuDevice, properties)
cdalton20b373c2014-12-01 08:38:55 -080027 , fStroke(SkStrokeRec::kFill_InitStyle)
28 , fQueuedGlyphCount(0)
29 , fFallbackGlyphsIdx(kGlyphBufferSize) {
kkinnunenc6cb56f2014-06-24 00:12:27 -070030}
31
joshualitt6e8cd962015-03-20 10:30:14 -070032GrStencilAndCoverTextContext*
33GrStencilAndCoverTextContext::Create(GrContext* context, SkGpuDevice* gpuDevice,
34 const SkDeviceProperties& props) {
jvanverth8c27a182014-10-14 08:45:50 -070035 GrStencilAndCoverTextContext* textContext = SkNEW_ARGS(GrStencilAndCoverTextContext,
joshualitt6e8cd962015-03-20 10:30:14 -070036 (context, gpuDevice, props));
37 textContext->fFallbackTextContext = GrBitmapTextContext::Create(context, gpuDevice, props);
jvanverth8c27a182014-10-14 08:45:50 -070038
39 return textContext;
40}
41
kkinnunenc6cb56f2014-06-24 00:12:27 -070042GrStencilAndCoverTextContext::~GrStencilAndCoverTextContext() {
43}
44
cdaltone68f7362015-03-25 14:02:37 -070045bool GrStencilAndCoverTextContext::canDraw(const GrRenderTarget* rt,
46 const GrClip& clip,
47 const GrPaint& paint,
48 const SkPaint& skPaint,
49 const SkMatrix& viewMatrix) {
50 if (skPaint.getRasterizer()) {
jvanverth0fedb192014-10-08 09:07:27 -070051 return false;
52 }
cdaltone68f7362015-03-25 14:02:37 -070053 if (skPaint.getMaskFilter()) {
jvanverth0fedb192014-10-08 09:07:27 -070054 return false;
55 }
cdaltone68f7362015-03-25 14:02:37 -070056 if (skPaint.getPathEffect()) {
jvanverth0fedb192014-10-08 09:07:27 -070057 return false;
58 }
59
60 // No hairlines unless we can map the 1 px width to the object space.
cdaltone68f7362015-03-25 14:02:37 -070061 if (skPaint.getStyle() == SkPaint::kStroke_Style
62 && skPaint.getStrokeWidth() == 0
joshualitt5531d512014-12-17 15:50:11 -080063 && viewMatrix.hasPerspective()) {
jvanverth0fedb192014-10-08 09:07:27 -070064 return false;
65 }
66
67 // No color bitmap fonts.
68 SkScalerContext::Rec rec;
cdaltone68f7362015-03-25 14:02:37 -070069 SkScalerContext::MakeRec(skPaint, &fDeviceProperties, NULL, &rec);
jvanverth0fedb192014-10-08 09:07:27 -070070 return rec.getFormat() != SkMask::kARGB32_Format;
71}
72
joshualitt25d9c152015-02-18 12:29:52 -080073void GrStencilAndCoverTextContext::onDrawText(GrRenderTarget* rt,
joshualitt570d2f82015-02-25 13:19:48 -080074 const GrClip& clip,
joshualitt25d9c152015-02-18 12:29:52 -080075 const GrPaint& paint,
cdalton20b373c2014-12-01 08:38:55 -080076 const SkPaint& skPaint,
joshualitt5531d512014-12-17 15:50:11 -080077 const SkMatrix& viewMatrix,
cdalton20b373c2014-12-01 08:38:55 -080078 const char text[],
79 size_t byteLength,
joshualitt6e8cd962015-03-20 10:30:14 -070080 SkScalar x, SkScalar y,
81 const SkIRect& regionClipBounds) {
jvanverthaab626c2014-10-16 08:04:39 -070082 SkASSERT(byteLength == 0 || text != NULL);
83
84 if (text == NULL || byteLength == 0 /*|| fRC->isEmpty()*/) {
85 return;
86 }
87
88 // This is the slow path, mainly used by Skia unit tests. The other
89 // backends (8888, gpu, ...) use device-space dependent glyph caches. In
90 // order to match the glyph positions that the other code paths produce, we
91 // must also use device-space dependent glyph cache. This has the
92 // side-effect that the glyph shape outline will be in device-space,
93 // too. This in turn has the side-effect that NVPR can not stroke the paths,
94 // as the stroke in NVPR is defined in object-space.
95 // NOTE: here we have following coincidence that works at the moment:
96 // - When using the device-space glyphs, the transforms we pass to NVPR
97 // instanced drawing are the global transforms, and the view transform is
98 // identity. NVPR can not use non-affine transforms in the instanced
99 // drawing. This is taken care of by SkDraw::ShouldDrawTextAsPaths since it
100 // will turn off the use of device-space glyphs when perspective transforms
101 // are in use.
102
joshualitt6e8cd962015-03-20 10:30:14 -0700103 this->init(rt, clip, paint, skPaint, byteLength, kMaxAccuracy_RenderMode, viewMatrix,
104 regionClipBounds);
jvanverthaab626c2014-10-16 08:04:39 -0700105
106 // Transform our starting point.
cdalton20b373c2014-12-01 08:38:55 -0800107 if (fUsingDeviceSpaceGlyphs) {
jvanverthaab626c2014-10-16 08:04:39 -0700108 SkPoint loc;
109 fContextInitialMatrix.mapXY(x, y, &loc);
110 x = loc.fX;
111 y = loc.fY;
112 }
113
114 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
115
jvanverthaab626c2014-10-16 08:04:39 -0700116 const char* stop = text + byteLength;
117
118 // Measure first if needed.
119 if (fSkPaint.getTextAlign() != SkPaint::kLeft_Align) {
120 SkFixed stopX = 0;
121 SkFixed stopY = 0;
122
123 const char* textPtr = text;
124 while (textPtr < stop) {
125 // We don't need x, y here, since all subpixel variants will have the
126 // same advance.
127 const SkGlyph& glyph = glyphCacheProc(fGlyphCache, &textPtr, 0, 0);
128
129 stopX += glyph.fAdvanceX;
130 stopY += glyph.fAdvanceY;
131 }
132 SkASSERT(textPtr == stop);
133
134 SkScalar alignX = SkFixedToScalar(stopX) * fTextRatio;
135 SkScalar alignY = SkFixedToScalar(stopY) * fTextRatio;
136
137 if (fSkPaint.getTextAlign() == SkPaint::kCenter_Align) {
138 alignX = SkScalarHalf(alignX);
139 alignY = SkScalarHalf(alignY);
140 }
141
142 x -= alignX;
143 y -= alignY;
144 }
145
146 SkAutoKern autokern;
147
148 SkFixed fixedSizeRatio = SkScalarToFixed(fTextRatio);
149
150 SkFixed fx = SkScalarToFixed(x);
151 SkFixed fy = SkScalarToFixed(y);
152 while (text < stop) {
153 const SkGlyph& glyph = glyphCacheProc(fGlyphCache, &text, 0, 0);
bungemand709ea82015-03-17 07:23:39 -0700154 fx += SkFixedMul(autokern.adjust(glyph), fixedSizeRatio);
jvanverthaab626c2014-10-16 08:04:39 -0700155 if (glyph.fWidth) {
cdalton20b373c2014-12-01 08:38:55 -0800156 this->appendGlyph(glyph, SkPoint::Make(SkFixedToScalar(fx), SkFixedToScalar(fy)));
jvanverthaab626c2014-10-16 08:04:39 -0700157 }
158
bungemand709ea82015-03-17 07:23:39 -0700159 fx += SkFixedMul(glyph.fAdvanceX, fixedSizeRatio);
160 fy += SkFixedMul(glyph.fAdvanceY, fixedSizeRatio);
jvanverthaab626c2014-10-16 08:04:39 -0700161 }
162
163 this->finish();
164}
165
joshualitt25d9c152015-02-18 12:29:52 -0800166void GrStencilAndCoverTextContext::onDrawPosText(GrRenderTarget* rt,
joshualitt570d2f82015-02-25 13:19:48 -0800167 const GrClip& clip,
joshualitt25d9c152015-02-18 12:29:52 -0800168 const GrPaint& paint,
cdalton20b373c2014-12-01 08:38:55 -0800169 const SkPaint& skPaint,
joshualitt5531d512014-12-17 15:50:11 -0800170 const SkMatrix& viewMatrix,
cdalton20b373c2014-12-01 08:38:55 -0800171 const char text[],
172 size_t byteLength,
173 const SkScalar pos[],
174 int scalarsPerPosition,
joshualitt6e8cd962015-03-20 10:30:14 -0700175 const SkPoint& offset,
176 const SkIRect& regionClipBounds) {
kkinnunenc6cb56f2014-06-24 00:12:27 -0700177 SkASSERT(byteLength == 0 || text != NULL);
178 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
179
180 // nothing to draw
181 if (text == NULL || byteLength == 0/* || fRC->isEmpty()*/) {
182 return;
183 }
184
185 // This is the fast path. Here we do not bake in the device-transform to
186 // the glyph outline or the advances. This is because we do not need to
187 // position the glyphs at all, since the caller has done the positioning.
188 // The positioning is based on SkPaint::measureText of individual
189 // glyphs. That already uses glyph cache without device transforms. Device
190 // transform is not part of SkPaint::measureText API, and thus we use the
191 // same glyphs as what were measured.
kkinnunenc6cb56f2014-06-24 00:12:27 -0700192
joshualitt6e8cd962015-03-20 10:30:14 -0700193 this->init(rt, clip, paint, skPaint, byteLength, kMaxPerformance_RenderMode, viewMatrix,
194 regionClipBounds);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700195
196 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
197
kkinnunenc6cb56f2014-06-24 00:12:27 -0700198 const char* stop = text + byteLength;
kkinnunenc6cb56f2014-06-24 00:12:27 -0700199
cdalton38e13ad2014-11-07 06:02:15 -0800200 SkTextMapStateProc tmsProc(SkMatrix::I(), offset, scalarsPerPosition);
bungeman79738cc2015-03-11 14:05:29 -0700201 SkTextAlignProc alignProc(fSkPaint.getTextAlign());
cdalton38e13ad2014-11-07 06:02:15 -0800202 while (text < stop) {
203 const SkGlyph& glyph = glyphCacheProc(fGlyphCache, &text, 0, 0);
204 if (glyph.fWidth) {
205 SkPoint tmsLoc;
206 tmsProc(pos, &tmsLoc);
207 SkPoint loc;
208 alignProc(tmsLoc, glyph, &loc);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700209
cdalton20b373c2014-12-01 08:38:55 -0800210 this->appendGlyph(glyph, loc);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700211 }
cdalton38e13ad2014-11-07 06:02:15 -0800212 pos += scalarsPerPosition;
kkinnunenc6cb56f2014-06-24 00:12:27 -0700213 }
214
215 this->finish();
216}
217
cdalton855d83f2014-09-18 13:51:53 -0700218static GrPathRange* get_gr_glyphs(GrContext* ctx,
219 const SkTypeface* typeface,
220 const SkDescriptor* desc,
221 const SkStrokeRec& stroke) {
bsalomon8718aaf2015-02-19 07:24:21 -0800222 static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
223 GrUniqueKey key;
224 GrUniqueKey::Builder builder(&key, kDomain, 4);
bsalomon24db3b12015-01-23 04:24:04 -0800225 struct GlyphKey {
226 uint32_t fChecksum;
227 uint32_t fTypeface;
228 uint64_t fStroke;
229 };
230 GlyphKey* glyphKey = reinterpret_cast<GlyphKey*>(&builder[0]);
231 glyphKey->fChecksum = desc ? desc->getChecksum() : 0;
232 glyphKey->fTypeface = typeface ? typeface->uniqueID() : 0;
233 glyphKey->fStroke = GrPath::ComputeStrokeKey(stroke);
234 builder.finish();
cdalton855d83f2014-09-18 13:51:53 -0700235
236 SkAutoTUnref<GrPathRange> glyphs(
bsalomon24db3b12015-01-23 04:24:04 -0800237 static_cast<GrPathRange*>(ctx->findAndRefCachedResource(key)));
cdalton855d83f2014-09-18 13:51:53 -0700238 if (NULL == glyphs || (NULL != desc && !glyphs->isEqualTo(*desc))) {
239 glyphs.reset(ctx->getGpu()->pathRendering()->createGlyphs(typeface, desc, stroke));
bsalomon24db3b12015-01-23 04:24:04 -0800240 ctx->addResourceToCache(key, glyphs);
cdalton855d83f2014-09-18 13:51:53 -0700241 }
242
243 return glyphs.detach();
244}
245
joshualitt25d9c152015-02-18 12:29:52 -0800246void GrStencilAndCoverTextContext::init(GrRenderTarget* rt,
joshualitt570d2f82015-02-25 13:19:48 -0800247 const GrClip& clip,
joshualitt25d9c152015-02-18 12:29:52 -0800248 const GrPaint& paint,
kkinnunenc6cb56f2014-06-24 00:12:27 -0700249 const SkPaint& skPaint,
cdaltonb2808cd2014-07-25 14:13:57 -0700250 size_t textByteLength,
joshualitt5531d512014-12-17 15:50:11 -0800251 RenderMode renderMode,
joshualitt6e8cd962015-03-20 10:30:14 -0700252 const SkMatrix& viewMatrix,
253 const SkIRect& regionClipBounds) {
254 GrTextContext::init(rt, clip, paint, skPaint, regionClipBounds);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700255
joshualitt5531d512014-12-17 15:50:11 -0800256 fContextInitialMatrix = viewMatrix;
257 fViewMatrix = viewMatrix;
joshualitt290c09b2014-12-19 13:45:20 -0800258 fLocalMatrix = SkMatrix::I();
cdaltonb2808cd2014-07-25 14:13:57 -0700259
cdalton855d83f2014-09-18 13:51:53 -0700260 const bool otherBackendsWillDrawAsPaths =
cdaltonb2808cd2014-07-25 14:13:57 -0700261 SkDraw::ShouldDrawTextAsPaths(skPaint, fContextInitialMatrix);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700262
cdalton20b373c2014-12-01 08:38:55 -0800263 fUsingDeviceSpaceGlyphs = !otherBackendsWillDrawAsPaths &&
cdalton855d83f2014-09-18 13:51:53 -0700264 kMaxAccuracy_RenderMode == renderMode &&
265 SkToBool(fContextInitialMatrix.getType() &
266 (SkMatrix::kScale_Mask | SkMatrix::kAffine_Mask));
kkinnunenc6cb56f2014-06-24 00:12:27 -0700267
cdalton20b373c2014-12-01 08:38:55 -0800268 if (fUsingDeviceSpaceGlyphs) {
cdalton855d83f2014-09-18 13:51:53 -0700269 // SkDraw::ShouldDrawTextAsPaths takes care of perspective transforms.
270 SkASSERT(!fContextInitialMatrix.hasPerspective());
cdaltonb2808cd2014-07-25 14:13:57 -0700271
cdalton20b373c2014-12-01 08:38:55 -0800272 // The whole shape (including stroke) will be baked into the glyph outlines. Make
273 // NVPR just fill the baked shapes.
274 fStroke = SkStrokeRec(SkStrokeRec::kFill_InitStyle);
275
cdalton855d83f2014-09-18 13:51:53 -0700276 fTextRatio = fTextInverseRatio = 1.0f;
277
278 // Glyphs loaded by GPU path rendering have an inverted y-direction.
279 SkMatrix m;
280 m.setScale(1, -1);
joshualitt5531d512014-12-17 15:50:11 -0800281 fViewMatrix = m;
cdalton855d83f2014-09-18 13:51:53 -0700282
283 // Post-flip the initial matrix so we're left with just the flip after
284 // the paint preConcats the inverse.
285 m = fContextInitialMatrix;
286 m.postScale(1, -1);
joshualitt290c09b2014-12-19 13:45:20 -0800287 if (!m.invert(&fLocalMatrix)) {
288 SkDebugf("Not invertible!\n");
289 return;
290 }
cdalton855d83f2014-09-18 13:51:53 -0700291
cdaltone05162d2014-12-01 08:57:33 -0800292 fGlyphCache = fSkPaint.detachCache(&fDeviceProperties, &fContextInitialMatrix,
293 true /*ignoreGamma*/);
cdalton855d83f2014-09-18 13:51:53 -0700294 fGlyphs = get_gr_glyphs(fContext, fGlyphCache->getScalerContext()->getTypeface(),
cdalton20b373c2014-12-01 08:38:55 -0800295 &fGlyphCache->getDescriptor(), fStroke);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700296 } else {
cdalton855d83f2014-09-18 13:51:53 -0700297 // Don't bake strokes into the glyph outlines. We will stroke the glyphs
298 // using the GPU instead. This is the fast path.
cdalton20b373c2014-12-01 08:38:55 -0800299 fStroke = SkStrokeRec(fSkPaint);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700300 fSkPaint.setStyle(SkPaint::kFill_Style);
cdalton855d83f2014-09-18 13:51:53 -0700301
cdalton20b373c2014-12-01 08:38:55 -0800302 if (fStroke.isHairlineStyle()) {
cdalton855d83f2014-09-18 13:51:53 -0700303 // Approximate hairline stroke.
304 SkScalar strokeWidth = SK_Scalar1 /
305 (SkVector::Make(fContextInitialMatrix.getScaleX(),
306 fContextInitialMatrix.getSkewY()).length());
cdalton20b373c2014-12-01 08:38:55 -0800307 fStroke.setStrokeStyle(strokeWidth, false /*strokeAndFill*/);
cdalton855d83f2014-09-18 13:51:53 -0700308
309 } else if (fSkPaint.isFakeBoldText() &&
310#ifdef SK_USE_FREETYPE_EMBOLDEN
311 kMaxPerformance_RenderMode == renderMode &&
312#endif
cdalton20b373c2014-12-01 08:38:55 -0800313 SkStrokeRec::kStroke_Style != fStroke.getStyle()) {
cdalton855d83f2014-09-18 13:51:53 -0700314
315 // Instead of baking fake bold into the glyph outlines, do it with the GPU stroke.
316 SkScalar fakeBoldScale = SkScalarInterpFunc(fSkPaint.getTextSize(),
317 kStdFakeBoldInterpKeys,
318 kStdFakeBoldInterpValues,
319 kStdFakeBoldInterpLength);
320 SkScalar extra = SkScalarMul(fSkPaint.getTextSize(), fakeBoldScale);
cdalton20b373c2014-12-01 08:38:55 -0800321 fStroke.setStrokeStyle(fStroke.needToApply() ? fStroke.getWidth() + extra : extra,
322 true /*strokeAndFill*/);
cdalton855d83f2014-09-18 13:51:53 -0700323
324 fSkPaint.setFakeBoldText(false);
325 }
326
327 bool canUseRawPaths;
328
329 if (otherBackendsWillDrawAsPaths || kMaxPerformance_RenderMode == renderMode) {
330 // We can draw the glyphs from canonically sized paths.
331 fTextRatio = fSkPaint.getTextSize() / SkPaint::kCanonicalTextSizeForPaths;
332 fTextInverseRatio = SkPaint::kCanonicalTextSizeForPaths / fSkPaint.getTextSize();
333
334 // Compensate for the glyphs being scaled by fTextRatio.
cdalton20b373c2014-12-01 08:38:55 -0800335 if (!fStroke.isFillStyle()) {
336 fStroke.setStrokeStyle(fStroke.getWidth() / fTextRatio,
337 SkStrokeRec::kStrokeAndFill_Style == fStroke.getStyle());
cdalton855d83f2014-09-18 13:51:53 -0700338 }
339
340 fSkPaint.setLinearText(true);
341 fSkPaint.setLCDRenderText(false);
342 fSkPaint.setAutohinted(false);
343 fSkPaint.setHinting(SkPaint::kNo_Hinting);
344 fSkPaint.setSubpixelText(true);
345 fSkPaint.setTextSize(SkIntToScalar(SkPaint::kCanonicalTextSizeForPaths));
346
347 canUseRawPaths = SK_Scalar1 == fSkPaint.getTextScaleX() &&
348 0 == fSkPaint.getTextSkewX() &&
349 !fSkPaint.isFakeBoldText() &&
350 !fSkPaint.isVerticalText();
351 } else {
352 fTextRatio = fTextInverseRatio = 1.0f;
353 canUseRawPaths = false;
354 }
355
356 SkMatrix textMatrix;
cdalton855d83f2014-09-18 13:51:53 -0700357 // Glyphs loaded by GPU path rendering have an inverted y-direction.
cdalton38e13ad2014-11-07 06:02:15 -0800358 textMatrix.setScale(fTextRatio, -fTextRatio);
joshualitt5531d512014-12-17 15:50:11 -0800359 fViewMatrix.preConcat(textMatrix);
joshualitt290c09b2014-12-19 13:45:20 -0800360 fLocalMatrix = textMatrix;
cdalton855d83f2014-09-18 13:51:53 -0700361
cdaltone05162d2014-12-01 08:57:33 -0800362 fGlyphCache = fSkPaint.detachCache(&fDeviceProperties, NULL, true /*ignoreGamma*/);
cdalton855d83f2014-09-18 13:51:53 -0700363 fGlyphs = canUseRawPaths ?
cdalton20b373c2014-12-01 08:38:55 -0800364 get_gr_glyphs(fContext, fSkPaint.getTypeface(), NULL, fStroke) :
cdalton855d83f2014-09-18 13:51:53 -0700365 get_gr_glyphs(fContext, fGlyphCache->getScalerContext()->getTypeface(),
cdalton20b373c2014-12-01 08:38:55 -0800366 &fGlyphCache->getDescriptor(), fStroke);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700367 }
cdalton855d83f2014-09-18 13:51:53 -0700368
egdaniel8dd688b2015-01-22 10:16:09 -0800369 fStateRestore.set(&fPipelineBuilder);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700370
joshualitt44701df2015-02-23 14:44:57 -0800371 fPipelineBuilder.setFromPaint(fPaint, fRenderTarget, fClip);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700372
373 GR_STATIC_CONST_SAME_STENCIL(kStencilPass,
374 kZero_StencilOp,
375 kZero_StencilOp,
376 kNotEqual_StencilFunc,
377 0xffff,
378 0x0000,
379 0xffff);
380
egdaniel8dd688b2015-01-22 10:16:09 -0800381 *fPipelineBuilder.stencil() = kStencilPass;
kkinnunenc6cb56f2014-06-24 00:12:27 -0700382
cdalton20b373c2014-12-01 08:38:55 -0800383 SkASSERT(0 == fQueuedGlyphCount);
384 SkASSERT(kGlyphBufferSize == fFallbackGlyphsIdx);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700385}
386
joshualitt5531d512014-12-17 15:50:11 -0800387bool GrStencilAndCoverTextContext::mapToFallbackContext(SkMatrix* inverse) {
cdalton20b373c2014-12-01 08:38:55 -0800388 // The current view matrix is flipped because GPU path rendering glyphs have an
389 // inverted y-direction. Unflip the view matrix for the fallback context. If using
390 // device-space glyphs, we'll also need to restore the original view matrix since
391 // we moved that transfomation into our local glyph cache for this scenario. Also
392 // track the inverse operation so the caller can unmap the paint and glyph positions.
393 if (fUsingDeviceSpaceGlyphs) {
joshualitt5531d512014-12-17 15:50:11 -0800394 fViewMatrix = fContextInitialMatrix;
cdalton20b373c2014-12-01 08:38:55 -0800395 if (!fContextInitialMatrix.invert(inverse)) {
396 return false;
397 }
398 inverse->preScale(1, -1);
399 } else {
400 inverse->setScale(1, -1);
401 const SkMatrix& unflip = *inverse; // unflip is equal to its own inverse.
joshualitt5531d512014-12-17 15:50:11 -0800402 fViewMatrix.preConcat(unflip);
cdalton20b373c2014-12-01 08:38:55 -0800403 }
404 return true;
405}
406
407inline void GrStencilAndCoverTextContext::appendGlyph(const SkGlyph& glyph, const SkPoint& pos) {
408 if (fQueuedGlyphCount >= fFallbackGlyphsIdx) {
409 SkASSERT(fQueuedGlyphCount == fFallbackGlyphsIdx);
cdaltonb2808cd2014-07-25 14:13:57 -0700410 this->flush();
411 }
412
cdalton20b373c2014-12-01 08:38:55 -0800413 // Stick the glyphs we can't draw at the end of the buffer, growing backwards.
414 int index = (SkMask::kARGB32_Format == glyph.fMaskFormat) ?
415 --fFallbackGlyphsIdx : fQueuedGlyphCount++;
cdaltonb85a0aa2014-07-21 15:32:44 -0700416
cdalton20b373c2014-12-01 08:38:55 -0800417 fGlyphIndices[index] = glyph.getGlyphID();
418 fGlyphPositions[index].set(fTextInverseRatio * pos.x(), -fTextInverseRatio * pos.y());
419}
420
421static const SkScalar* get_xy_scalar_array(const SkPoint* pointArray) {
422 GR_STATIC_ASSERT(2 * sizeof(SkScalar) == sizeof(SkPoint));
423 GR_STATIC_ASSERT(0 == offsetof(SkPoint, fX));
424
425 return &pointArray[0].fX;
cdaltonb85a0aa2014-07-21 15:32:44 -0700426}
427
428void GrStencilAndCoverTextContext::flush() {
cdalton20b373c2014-12-01 08:38:55 -0800429 if (fQueuedGlyphCount > 0) {
joshualitt8059eb92014-12-29 15:10:07 -0800430 SkAutoTUnref<GrPathProcessor> pp(GrPathProcessor::Create(fPaint.getColor(),
431 fViewMatrix,
432 fLocalMatrix));
egdaniel8dd688b2015-01-22 10:16:09 -0800433 fDrawTarget->drawPaths(&fPipelineBuilder, pp, fGlyphs,
cdalton20b373c2014-12-01 08:38:55 -0800434 fGlyphIndices, GrPathRange::kU16_PathIndexType,
435 get_xy_scalar_array(fGlyphPositions),
436 GrPathRendering::kTranslate_PathTransformType,
437 fQueuedGlyphCount, GrPathRendering::kWinding_FillType);
438
439 fQueuedGlyphCount = 0;
kkinnunenc6cb56f2014-06-24 00:12:27 -0700440 }
441
cdalton20b373c2014-12-01 08:38:55 -0800442 if (fFallbackGlyphsIdx < kGlyphBufferSize) {
443 int fallbackGlyphCount = kGlyphBufferSize - fFallbackGlyphsIdx;
cdaltonb85a0aa2014-07-21 15:32:44 -0700444
cdalton20b373c2014-12-01 08:38:55 -0800445 GrPaint paintFallback(fPaint);
446
447 SkPaint skPaintFallback(fSkPaint);
448 if (!fUsingDeviceSpaceGlyphs) {
449 fStroke.applyToPaint(&skPaintFallback);
450 }
451 skPaintFallback.setTextAlign(SkPaint::kLeft_Align); // Align has already been accounted for.
452 skPaintFallback.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
453
cdalton20b373c2014-12-01 08:38:55 -0800454 SkMatrix inverse;
joshualitt5531d512014-12-17 15:50:11 -0800455 if (this->mapToFallbackContext(&inverse)) {
cdalton20b373c2014-12-01 08:38:55 -0800456 inverse.mapPoints(&fGlyphPositions[fFallbackGlyphsIdx], fallbackGlyphCount);
457 }
458
joshualitt570d2f82015-02-25 13:19:48 -0800459 fFallbackTextContext->drawPosText(fRenderTarget, fClip, paintFallback, skPaintFallback,
joshualitt25d9c152015-02-18 12:29:52 -0800460 fViewMatrix, (char*)&fGlyphIndices[fFallbackGlyphsIdx],
cdalton20b373c2014-12-01 08:38:55 -0800461 2 * fallbackGlyphCount,
462 get_xy_scalar_array(&fGlyphPositions[fFallbackGlyphsIdx]),
joshualitt6e8cd962015-03-20 10:30:14 -0700463 2, SkPoint::Make(0, 0), fRegionClipBounds);
cdalton20b373c2014-12-01 08:38:55 -0800464
465 fFallbackGlyphsIdx = kGlyphBufferSize;
466 }
kkinnunenc6cb56f2014-06-24 00:12:27 -0700467}
468
469void GrStencilAndCoverTextContext::finish() {
cdaltonb85a0aa2014-07-21 15:32:44 -0700470 this->flush();
kkinnunenc6cb56f2014-06-24 00:12:27 -0700471
cdalton855d83f2014-09-18 13:51:53 -0700472 fGlyphs->unref();
cdaltonb85a0aa2014-07-21 15:32:44 -0700473 fGlyphs = NULL;
cdalton855d83f2014-09-18 13:51:53 -0700474
475 SkGlyphCache::AttachCache(fGlyphCache);
cdaltonb85a0aa2014-07-21 15:32:44 -0700476 fGlyphCache = NULL;
kkinnunenc6cb56f2014-06-24 00:12:27 -0700477
egdaniel8dd688b2015-01-22 10:16:09 -0800478 fPipelineBuilder.stencil()->setDisabled();
kkinnunenc6cb56f2014-06-24 00:12:27 -0700479 fStateRestore.set(NULL);
joshualitt5531d512014-12-17 15:50:11 -0800480 fViewMatrix = fContextInitialMatrix;
kkinnunenc6cb56f2014-06-24 00:12:27 -0700481 GrTextContext::finish();
482}
483