blob: 330f79d9465fdde3e4568ed503913f8c256a096b [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"
joshualitt1d89e8d2015-04-01 12:40:54 -07009#include "GrAtlasTextContext.h"
jvanverth8c27a182014-10-14 08:45:50 -070010#include "GrBitmapTextContext.h"
kkinnunenc6cb56f2014-06-24 00:12:27 -070011#include "GrDrawTarget.h"
kkinnunenc6cb56f2014-06-24 00:12:27 -070012#include "GrGpu.h"
13#include "GrPath.h"
cdaltonb85a0aa2014-07-21 15:32:44 -070014#include "GrPathRange.h"
jvanverthaab626c2014-10-16 08:04:39 -070015#include "SkAutoKern.h"
kkinnunenc6cb56f2014-06-24 00:12:27 -070016#include "SkDraw.h"
17#include "SkDrawProcs.h"
18#include "SkGlyphCache.h"
19#include "SkGpuDevice.h"
20#include "SkPath.h"
21#include "SkTextMapStateProc.h"
cdalton855d83f2014-09-18 13:51:53 -070022#include "SkTextFormatParams.h"
kkinnunenc6cb56f2014-06-24 00:12:27 -070023
joshualitt6e8cd962015-03-20 10:30:14 -070024GrStencilAndCoverTextContext::GrStencilAndCoverTextContext(GrContext* context,
25 SkGpuDevice* gpuDevice,
26 const SkDeviceProperties& properties)
27 : GrTextContext(context, gpuDevice, properties)
cdalton20b373c2014-12-01 08:38:55 -080028 , fStroke(SkStrokeRec::kFill_InitStyle)
29 , fQueuedGlyphCount(0)
30 , fFallbackGlyphsIdx(kGlyphBufferSize) {
kkinnunenc6cb56f2014-06-24 00:12:27 -070031}
32
joshualitt6e8cd962015-03-20 10:30:14 -070033GrStencilAndCoverTextContext*
34GrStencilAndCoverTextContext::Create(GrContext* context, SkGpuDevice* gpuDevice,
35 const SkDeviceProperties& props) {
jvanverth8c27a182014-10-14 08:45:50 -070036 GrStencilAndCoverTextContext* textContext = SkNEW_ARGS(GrStencilAndCoverTextContext,
joshualitt6e8cd962015-03-20 10:30:14 -070037 (context, gpuDevice, props));
joshualitt7c3a2f82015-03-31 13:32:05 -070038#ifdef USE_BITMAP_TEXTBLOBS
joshualitt9bd2daf2015-04-17 09:30:06 -070039 textContext->fFallbackTextContext = GrAtlasTextContext::Create(context, gpuDevice, props,
40 false);
joshualitt7c3a2f82015-03-31 13:32:05 -070041#else
joshualitt6e8cd962015-03-20 10:30:14 -070042 textContext->fFallbackTextContext = GrBitmapTextContext::Create(context, gpuDevice, props);
joshualitt7c3a2f82015-03-31 13:32:05 -070043#endif
jvanverth8c27a182014-10-14 08:45:50 -070044
45 return textContext;
46}
47
kkinnunenc6cb56f2014-06-24 00:12:27 -070048GrStencilAndCoverTextContext::~GrStencilAndCoverTextContext() {
49}
50
cdaltone68f7362015-03-25 14:02:37 -070051bool GrStencilAndCoverTextContext::canDraw(const GrRenderTarget* rt,
52 const GrClip& clip,
53 const GrPaint& paint,
54 const SkPaint& skPaint,
55 const SkMatrix& viewMatrix) {
56 if (skPaint.getRasterizer()) {
jvanverth0fedb192014-10-08 09:07:27 -070057 return false;
58 }
cdaltone68f7362015-03-25 14:02:37 -070059 if (skPaint.getMaskFilter()) {
jvanverth0fedb192014-10-08 09:07:27 -070060 return false;
61 }
cdaltone68f7362015-03-25 14:02:37 -070062 if (skPaint.getPathEffect()) {
jvanverth0fedb192014-10-08 09:07:27 -070063 return false;
64 }
65
66 // No hairlines unless we can map the 1 px width to the object space.
cdaltone68f7362015-03-25 14:02:37 -070067 if (skPaint.getStyle() == SkPaint::kStroke_Style
68 && skPaint.getStrokeWidth() == 0
joshualitt5531d512014-12-17 15:50:11 -080069 && viewMatrix.hasPerspective()) {
jvanverth0fedb192014-10-08 09:07:27 -070070 return false;
71 }
72
73 // No color bitmap fonts.
74 SkScalerContext::Rec rec;
cdaltone68f7362015-03-25 14:02:37 -070075 SkScalerContext::MakeRec(skPaint, &fDeviceProperties, NULL, &rec);
jvanverth0fedb192014-10-08 09:07:27 -070076 return rec.getFormat() != SkMask::kARGB32_Format;
77}
78
joshualitt25d9c152015-02-18 12:29:52 -080079void GrStencilAndCoverTextContext::onDrawText(GrRenderTarget* rt,
joshualitt570d2f82015-02-25 13:19:48 -080080 const GrClip& clip,
joshualitt25d9c152015-02-18 12:29:52 -080081 const GrPaint& paint,
cdalton20b373c2014-12-01 08:38:55 -080082 const SkPaint& skPaint,
joshualitt5531d512014-12-17 15:50:11 -080083 const SkMatrix& viewMatrix,
cdalton20b373c2014-12-01 08:38:55 -080084 const char text[],
85 size_t byteLength,
joshualitt6e8cd962015-03-20 10:30:14 -070086 SkScalar x, SkScalar y,
87 const SkIRect& regionClipBounds) {
jvanverthaab626c2014-10-16 08:04:39 -070088 SkASSERT(byteLength == 0 || text != NULL);
89
90 if (text == NULL || byteLength == 0 /*|| fRC->isEmpty()*/) {
91 return;
92 }
93
94 // This is the slow path, mainly used by Skia unit tests. The other
95 // backends (8888, gpu, ...) use device-space dependent glyph caches. In
96 // order to match the glyph positions that the other code paths produce, we
97 // must also use device-space dependent glyph cache. This has the
98 // side-effect that the glyph shape outline will be in device-space,
99 // too. This in turn has the side-effect that NVPR can not stroke the paths,
100 // as the stroke in NVPR is defined in object-space.
101 // NOTE: here we have following coincidence that works at the moment:
102 // - When using the device-space glyphs, the transforms we pass to NVPR
103 // instanced drawing are the global transforms, and the view transform is
104 // identity. NVPR can not use non-affine transforms in the instanced
105 // drawing. This is taken care of by SkDraw::ShouldDrawTextAsPaths since it
106 // will turn off the use of device-space glyphs when perspective transforms
107 // are in use.
108
joshualitt6e8cd962015-03-20 10:30:14 -0700109 this->init(rt, clip, paint, skPaint, byteLength, kMaxAccuracy_RenderMode, viewMatrix,
110 regionClipBounds);
jvanverthaab626c2014-10-16 08:04:39 -0700111
112 // Transform our starting point.
cdalton20b373c2014-12-01 08:38:55 -0800113 if (fUsingDeviceSpaceGlyphs) {
jvanverthaab626c2014-10-16 08:04:39 -0700114 SkPoint loc;
115 fContextInitialMatrix.mapXY(x, y, &loc);
116 x = loc.fX;
117 y = loc.fY;
118 }
119
120 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
121
jvanverthaab626c2014-10-16 08:04:39 -0700122 const char* stop = text + byteLength;
123
124 // Measure first if needed.
125 if (fSkPaint.getTextAlign() != SkPaint::kLeft_Align) {
126 SkFixed stopX = 0;
127 SkFixed stopY = 0;
128
129 const char* textPtr = text;
130 while (textPtr < stop) {
131 // We don't need x, y here, since all subpixel variants will have the
132 // same advance.
133 const SkGlyph& glyph = glyphCacheProc(fGlyphCache, &textPtr, 0, 0);
134
135 stopX += glyph.fAdvanceX;
136 stopY += glyph.fAdvanceY;
137 }
138 SkASSERT(textPtr == stop);
139
140 SkScalar alignX = SkFixedToScalar(stopX) * fTextRatio;
141 SkScalar alignY = SkFixedToScalar(stopY) * fTextRatio;
142
143 if (fSkPaint.getTextAlign() == SkPaint::kCenter_Align) {
144 alignX = SkScalarHalf(alignX);
145 alignY = SkScalarHalf(alignY);
146 }
147
148 x -= alignX;
149 y -= alignY;
150 }
151
152 SkAutoKern autokern;
153
154 SkFixed fixedSizeRatio = SkScalarToFixed(fTextRatio);
155
156 SkFixed fx = SkScalarToFixed(x);
157 SkFixed fy = SkScalarToFixed(y);
158 while (text < stop) {
159 const SkGlyph& glyph = glyphCacheProc(fGlyphCache, &text, 0, 0);
bungemand709ea82015-03-17 07:23:39 -0700160 fx += SkFixedMul(autokern.adjust(glyph), fixedSizeRatio);
jvanverthaab626c2014-10-16 08:04:39 -0700161 if (glyph.fWidth) {
cdalton20b373c2014-12-01 08:38:55 -0800162 this->appendGlyph(glyph, SkPoint::Make(SkFixedToScalar(fx), SkFixedToScalar(fy)));
jvanverthaab626c2014-10-16 08:04:39 -0700163 }
164
bungemand709ea82015-03-17 07:23:39 -0700165 fx += SkFixedMul(glyph.fAdvanceX, fixedSizeRatio);
166 fy += SkFixedMul(glyph.fAdvanceY, fixedSizeRatio);
jvanverthaab626c2014-10-16 08:04:39 -0700167 }
168
169 this->finish();
170}
171
joshualitt25d9c152015-02-18 12:29:52 -0800172void GrStencilAndCoverTextContext::onDrawPosText(GrRenderTarget* rt,
joshualitt570d2f82015-02-25 13:19:48 -0800173 const GrClip& clip,
joshualitt25d9c152015-02-18 12:29:52 -0800174 const GrPaint& paint,
cdalton20b373c2014-12-01 08:38:55 -0800175 const SkPaint& skPaint,
joshualitt5531d512014-12-17 15:50:11 -0800176 const SkMatrix& viewMatrix,
cdalton20b373c2014-12-01 08:38:55 -0800177 const char text[],
178 size_t byteLength,
179 const SkScalar pos[],
180 int scalarsPerPosition,
joshualitt6e8cd962015-03-20 10:30:14 -0700181 const SkPoint& offset,
182 const SkIRect& regionClipBounds) {
kkinnunenc6cb56f2014-06-24 00:12:27 -0700183 SkASSERT(byteLength == 0 || text != NULL);
184 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
185
186 // nothing to draw
187 if (text == NULL || byteLength == 0/* || fRC->isEmpty()*/) {
188 return;
189 }
190
191 // This is the fast path. Here we do not bake in the device-transform to
192 // the glyph outline or the advances. This is because we do not need to
193 // position the glyphs at all, since the caller has done the positioning.
194 // The positioning is based on SkPaint::measureText of individual
195 // glyphs. That already uses glyph cache without device transforms. Device
196 // transform is not part of SkPaint::measureText API, and thus we use the
197 // same glyphs as what were measured.
kkinnunenc6cb56f2014-06-24 00:12:27 -0700198
joshualitt6e8cd962015-03-20 10:30:14 -0700199 this->init(rt, clip, paint, skPaint, byteLength, kMaxPerformance_RenderMode, viewMatrix,
200 regionClipBounds);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700201
202 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
203
kkinnunenc6cb56f2014-06-24 00:12:27 -0700204 const char* stop = text + byteLength;
kkinnunenc6cb56f2014-06-24 00:12:27 -0700205
cdalton38e13ad2014-11-07 06:02:15 -0800206 SkTextMapStateProc tmsProc(SkMatrix::I(), offset, scalarsPerPosition);
bungeman79738cc2015-03-11 14:05:29 -0700207 SkTextAlignProc alignProc(fSkPaint.getTextAlign());
cdalton38e13ad2014-11-07 06:02:15 -0800208 while (text < stop) {
209 const SkGlyph& glyph = glyphCacheProc(fGlyphCache, &text, 0, 0);
210 if (glyph.fWidth) {
211 SkPoint tmsLoc;
212 tmsProc(pos, &tmsLoc);
213 SkPoint loc;
214 alignProc(tmsLoc, glyph, &loc);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700215
cdalton20b373c2014-12-01 08:38:55 -0800216 this->appendGlyph(glyph, loc);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700217 }
cdalton38e13ad2014-11-07 06:02:15 -0800218 pos += scalarsPerPosition;
kkinnunenc6cb56f2014-06-24 00:12:27 -0700219 }
220
221 this->finish();
222}
223
cdalton855d83f2014-09-18 13:51:53 -0700224static GrPathRange* get_gr_glyphs(GrContext* ctx,
225 const SkTypeface* typeface,
226 const SkDescriptor* desc,
227 const SkStrokeRec& stroke) {
bsalomon8718aaf2015-02-19 07:24:21 -0800228 static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
229 GrUniqueKey key;
230 GrUniqueKey::Builder builder(&key, kDomain, 4);
bsalomon24db3b12015-01-23 04:24:04 -0800231 struct GlyphKey {
232 uint32_t fChecksum;
233 uint32_t fTypeface;
234 uint64_t fStroke;
235 };
236 GlyphKey* glyphKey = reinterpret_cast<GlyphKey*>(&builder[0]);
237 glyphKey->fChecksum = desc ? desc->getChecksum() : 0;
238 glyphKey->fTypeface = typeface ? typeface->uniqueID() : 0;
239 glyphKey->fStroke = GrPath::ComputeStrokeKey(stroke);
240 builder.finish();
cdalton855d83f2014-09-18 13:51:53 -0700241
242 SkAutoTUnref<GrPathRange> glyphs(
bsalomon24db3b12015-01-23 04:24:04 -0800243 static_cast<GrPathRange*>(ctx->findAndRefCachedResource(key)));
cdalton855d83f2014-09-18 13:51:53 -0700244 if (NULL == glyphs || (NULL != desc && !glyphs->isEqualTo(*desc))) {
245 glyphs.reset(ctx->getGpu()->pathRendering()->createGlyphs(typeface, desc, stroke));
bsalomon24db3b12015-01-23 04:24:04 -0800246 ctx->addResourceToCache(key, glyphs);
cdalton855d83f2014-09-18 13:51:53 -0700247 }
248
249 return glyphs.detach();
250}
251
joshualitt25d9c152015-02-18 12:29:52 -0800252void GrStencilAndCoverTextContext::init(GrRenderTarget* rt,
joshualitt570d2f82015-02-25 13:19:48 -0800253 const GrClip& clip,
joshualitt25d9c152015-02-18 12:29:52 -0800254 const GrPaint& paint,
kkinnunenc6cb56f2014-06-24 00:12:27 -0700255 const SkPaint& skPaint,
cdaltonb2808cd2014-07-25 14:13:57 -0700256 size_t textByteLength,
joshualitt5531d512014-12-17 15:50:11 -0800257 RenderMode renderMode,
joshualitt6e8cd962015-03-20 10:30:14 -0700258 const SkMatrix& viewMatrix,
259 const SkIRect& regionClipBounds) {
260 GrTextContext::init(rt, clip, paint, skPaint, regionClipBounds);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700261
joshualitt5531d512014-12-17 15:50:11 -0800262 fContextInitialMatrix = viewMatrix;
263 fViewMatrix = viewMatrix;
joshualitt290c09b2014-12-19 13:45:20 -0800264 fLocalMatrix = SkMatrix::I();
cdaltonb2808cd2014-07-25 14:13:57 -0700265
cdalton855d83f2014-09-18 13:51:53 -0700266 const bool otherBackendsWillDrawAsPaths =
cdaltonb2808cd2014-07-25 14:13:57 -0700267 SkDraw::ShouldDrawTextAsPaths(skPaint, fContextInitialMatrix);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700268
cdalton20b373c2014-12-01 08:38:55 -0800269 fUsingDeviceSpaceGlyphs = !otherBackendsWillDrawAsPaths &&
cdalton855d83f2014-09-18 13:51:53 -0700270 kMaxAccuracy_RenderMode == renderMode &&
271 SkToBool(fContextInitialMatrix.getType() &
272 (SkMatrix::kScale_Mask | SkMatrix::kAffine_Mask));
kkinnunenc6cb56f2014-06-24 00:12:27 -0700273
cdalton20b373c2014-12-01 08:38:55 -0800274 if (fUsingDeviceSpaceGlyphs) {
cdalton855d83f2014-09-18 13:51:53 -0700275 // SkDraw::ShouldDrawTextAsPaths takes care of perspective transforms.
276 SkASSERT(!fContextInitialMatrix.hasPerspective());
cdaltonb2808cd2014-07-25 14:13:57 -0700277
cdalton20b373c2014-12-01 08:38:55 -0800278 // The whole shape (including stroke) will be baked into the glyph outlines. Make
279 // NVPR just fill the baked shapes.
280 fStroke = SkStrokeRec(SkStrokeRec::kFill_InitStyle);
281
cdalton855d83f2014-09-18 13:51:53 -0700282 fTextRatio = fTextInverseRatio = 1.0f;
283
284 // Glyphs loaded by GPU path rendering have an inverted y-direction.
285 SkMatrix m;
286 m.setScale(1, -1);
joshualitt5531d512014-12-17 15:50:11 -0800287 fViewMatrix = m;
cdalton855d83f2014-09-18 13:51:53 -0700288
289 // Post-flip the initial matrix so we're left with just the flip after
290 // the paint preConcats the inverse.
291 m = fContextInitialMatrix;
292 m.postScale(1, -1);
joshualitt290c09b2014-12-19 13:45:20 -0800293 if (!m.invert(&fLocalMatrix)) {
294 SkDebugf("Not invertible!\n");
295 return;
296 }
cdalton855d83f2014-09-18 13:51:53 -0700297
cdaltone05162d2014-12-01 08:57:33 -0800298 fGlyphCache = fSkPaint.detachCache(&fDeviceProperties, &fContextInitialMatrix,
299 true /*ignoreGamma*/);
cdalton855d83f2014-09-18 13:51:53 -0700300 fGlyphs = get_gr_glyphs(fContext, fGlyphCache->getScalerContext()->getTypeface(),
cdalton20b373c2014-12-01 08:38:55 -0800301 &fGlyphCache->getDescriptor(), fStroke);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700302 } else {
cdalton855d83f2014-09-18 13:51:53 -0700303 // Don't bake strokes into the glyph outlines. We will stroke the glyphs
304 // using the GPU instead. This is the fast path.
cdalton20b373c2014-12-01 08:38:55 -0800305 fStroke = SkStrokeRec(fSkPaint);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700306 fSkPaint.setStyle(SkPaint::kFill_Style);
cdalton855d83f2014-09-18 13:51:53 -0700307
cdalton20b373c2014-12-01 08:38:55 -0800308 if (fStroke.isHairlineStyle()) {
cdalton855d83f2014-09-18 13:51:53 -0700309 // Approximate hairline stroke.
310 SkScalar strokeWidth = SK_Scalar1 /
311 (SkVector::Make(fContextInitialMatrix.getScaleX(),
312 fContextInitialMatrix.getSkewY()).length());
cdalton20b373c2014-12-01 08:38:55 -0800313 fStroke.setStrokeStyle(strokeWidth, false /*strokeAndFill*/);
cdalton855d83f2014-09-18 13:51:53 -0700314
315 } else if (fSkPaint.isFakeBoldText() &&
316#ifdef SK_USE_FREETYPE_EMBOLDEN
317 kMaxPerformance_RenderMode == renderMode &&
318#endif
cdalton20b373c2014-12-01 08:38:55 -0800319 SkStrokeRec::kStroke_Style != fStroke.getStyle()) {
cdalton855d83f2014-09-18 13:51:53 -0700320
321 // Instead of baking fake bold into the glyph outlines, do it with the GPU stroke.
322 SkScalar fakeBoldScale = SkScalarInterpFunc(fSkPaint.getTextSize(),
323 kStdFakeBoldInterpKeys,
324 kStdFakeBoldInterpValues,
325 kStdFakeBoldInterpLength);
326 SkScalar extra = SkScalarMul(fSkPaint.getTextSize(), fakeBoldScale);
cdalton20b373c2014-12-01 08:38:55 -0800327 fStroke.setStrokeStyle(fStroke.needToApply() ? fStroke.getWidth() + extra : extra,
328 true /*strokeAndFill*/);
cdalton855d83f2014-09-18 13:51:53 -0700329
330 fSkPaint.setFakeBoldText(false);
331 }
332
333 bool canUseRawPaths;
334
335 if (otherBackendsWillDrawAsPaths || kMaxPerformance_RenderMode == renderMode) {
336 // We can draw the glyphs from canonically sized paths.
337 fTextRatio = fSkPaint.getTextSize() / SkPaint::kCanonicalTextSizeForPaths;
338 fTextInverseRatio = SkPaint::kCanonicalTextSizeForPaths / fSkPaint.getTextSize();
339
340 // Compensate for the glyphs being scaled by fTextRatio.
cdalton20b373c2014-12-01 08:38:55 -0800341 if (!fStroke.isFillStyle()) {
342 fStroke.setStrokeStyle(fStroke.getWidth() / fTextRatio,
343 SkStrokeRec::kStrokeAndFill_Style == fStroke.getStyle());
cdalton855d83f2014-09-18 13:51:53 -0700344 }
345
346 fSkPaint.setLinearText(true);
347 fSkPaint.setLCDRenderText(false);
348 fSkPaint.setAutohinted(false);
349 fSkPaint.setHinting(SkPaint::kNo_Hinting);
350 fSkPaint.setSubpixelText(true);
351 fSkPaint.setTextSize(SkIntToScalar(SkPaint::kCanonicalTextSizeForPaths));
352
353 canUseRawPaths = SK_Scalar1 == fSkPaint.getTextScaleX() &&
354 0 == fSkPaint.getTextSkewX() &&
355 !fSkPaint.isFakeBoldText() &&
356 !fSkPaint.isVerticalText();
357 } else {
358 fTextRatio = fTextInverseRatio = 1.0f;
359 canUseRawPaths = false;
360 }
361
362 SkMatrix textMatrix;
cdalton855d83f2014-09-18 13:51:53 -0700363 // Glyphs loaded by GPU path rendering have an inverted y-direction.
cdalton38e13ad2014-11-07 06:02:15 -0800364 textMatrix.setScale(fTextRatio, -fTextRatio);
joshualitt5531d512014-12-17 15:50:11 -0800365 fViewMatrix.preConcat(textMatrix);
joshualitt290c09b2014-12-19 13:45:20 -0800366 fLocalMatrix = textMatrix;
cdalton855d83f2014-09-18 13:51:53 -0700367
cdaltone05162d2014-12-01 08:57:33 -0800368 fGlyphCache = fSkPaint.detachCache(&fDeviceProperties, NULL, true /*ignoreGamma*/);
cdalton855d83f2014-09-18 13:51:53 -0700369 fGlyphs = canUseRawPaths ?
cdalton20b373c2014-12-01 08:38:55 -0800370 get_gr_glyphs(fContext, fSkPaint.getTypeface(), NULL, fStroke) :
cdalton855d83f2014-09-18 13:51:53 -0700371 get_gr_glyphs(fContext, fGlyphCache->getScalerContext()->getTypeface(),
cdalton20b373c2014-12-01 08:38:55 -0800372 &fGlyphCache->getDescriptor(), fStroke);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700373 }
cdalton855d83f2014-09-18 13:51:53 -0700374
egdaniel8dd688b2015-01-22 10:16:09 -0800375 fStateRestore.set(&fPipelineBuilder);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700376
joshualitt44701df2015-02-23 14:44:57 -0800377 fPipelineBuilder.setFromPaint(fPaint, fRenderTarget, fClip);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700378
379 GR_STATIC_CONST_SAME_STENCIL(kStencilPass,
380 kZero_StencilOp,
381 kZero_StencilOp,
382 kNotEqual_StencilFunc,
383 0xffff,
384 0x0000,
385 0xffff);
386
egdaniel8dd688b2015-01-22 10:16:09 -0800387 *fPipelineBuilder.stencil() = kStencilPass;
kkinnunenc6cb56f2014-06-24 00:12:27 -0700388
cdalton20b373c2014-12-01 08:38:55 -0800389 SkASSERT(0 == fQueuedGlyphCount);
390 SkASSERT(kGlyphBufferSize == fFallbackGlyphsIdx);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700391}
392
joshualitt5531d512014-12-17 15:50:11 -0800393bool GrStencilAndCoverTextContext::mapToFallbackContext(SkMatrix* inverse) {
cdalton20b373c2014-12-01 08:38:55 -0800394 // The current view matrix is flipped because GPU path rendering glyphs have an
395 // inverted y-direction. Unflip the view matrix for the fallback context. If using
396 // device-space glyphs, we'll also need to restore the original view matrix since
397 // we moved that transfomation into our local glyph cache for this scenario. Also
398 // track the inverse operation so the caller can unmap the paint and glyph positions.
399 if (fUsingDeviceSpaceGlyphs) {
joshualitt5531d512014-12-17 15:50:11 -0800400 fViewMatrix = fContextInitialMatrix;
cdalton20b373c2014-12-01 08:38:55 -0800401 if (!fContextInitialMatrix.invert(inverse)) {
402 return false;
403 }
404 inverse->preScale(1, -1);
405 } else {
406 inverse->setScale(1, -1);
407 const SkMatrix& unflip = *inverse; // unflip is equal to its own inverse.
joshualitt5531d512014-12-17 15:50:11 -0800408 fViewMatrix.preConcat(unflip);
cdalton20b373c2014-12-01 08:38:55 -0800409 }
410 return true;
411}
412
413inline void GrStencilAndCoverTextContext::appendGlyph(const SkGlyph& glyph, const SkPoint& pos) {
414 if (fQueuedGlyphCount >= fFallbackGlyphsIdx) {
415 SkASSERT(fQueuedGlyphCount == fFallbackGlyphsIdx);
cdaltonb2808cd2014-07-25 14:13:57 -0700416 this->flush();
417 }
418
cdalton20b373c2014-12-01 08:38:55 -0800419 // Stick the glyphs we can't draw at the end of the buffer, growing backwards.
420 int index = (SkMask::kARGB32_Format == glyph.fMaskFormat) ?
421 --fFallbackGlyphsIdx : fQueuedGlyphCount++;
cdaltonb85a0aa2014-07-21 15:32:44 -0700422
cdalton20b373c2014-12-01 08:38:55 -0800423 fGlyphIndices[index] = glyph.getGlyphID();
424 fGlyphPositions[index].set(fTextInverseRatio * pos.x(), -fTextInverseRatio * pos.y());
425}
426
427static const SkScalar* get_xy_scalar_array(const SkPoint* pointArray) {
428 GR_STATIC_ASSERT(2 * sizeof(SkScalar) == sizeof(SkPoint));
429 GR_STATIC_ASSERT(0 == offsetof(SkPoint, fX));
430
431 return &pointArray[0].fX;
cdaltonb85a0aa2014-07-21 15:32:44 -0700432}
433
434void GrStencilAndCoverTextContext::flush() {
cdalton20b373c2014-12-01 08:38:55 -0800435 if (fQueuedGlyphCount > 0) {
joshualitt8059eb92014-12-29 15:10:07 -0800436 SkAutoTUnref<GrPathProcessor> pp(GrPathProcessor::Create(fPaint.getColor(),
437 fViewMatrix,
438 fLocalMatrix));
egdaniel8dd688b2015-01-22 10:16:09 -0800439 fDrawTarget->drawPaths(&fPipelineBuilder, pp, fGlyphs,
cdalton20b373c2014-12-01 08:38:55 -0800440 fGlyphIndices, GrPathRange::kU16_PathIndexType,
441 get_xy_scalar_array(fGlyphPositions),
442 GrPathRendering::kTranslate_PathTransformType,
443 fQueuedGlyphCount, GrPathRendering::kWinding_FillType);
444
445 fQueuedGlyphCount = 0;
kkinnunenc6cb56f2014-06-24 00:12:27 -0700446 }
447
cdalton20b373c2014-12-01 08:38:55 -0800448 if (fFallbackGlyphsIdx < kGlyphBufferSize) {
449 int fallbackGlyphCount = kGlyphBufferSize - fFallbackGlyphsIdx;
cdaltonb85a0aa2014-07-21 15:32:44 -0700450
cdalton20b373c2014-12-01 08:38:55 -0800451 GrPaint paintFallback(fPaint);
452
453 SkPaint skPaintFallback(fSkPaint);
454 if (!fUsingDeviceSpaceGlyphs) {
455 fStroke.applyToPaint(&skPaintFallback);
456 }
457 skPaintFallback.setTextAlign(SkPaint::kLeft_Align); // Align has already been accounted for.
458 skPaintFallback.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
459
cdalton20b373c2014-12-01 08:38:55 -0800460 SkMatrix inverse;
joshualitt5531d512014-12-17 15:50:11 -0800461 if (this->mapToFallbackContext(&inverse)) {
cdalton20b373c2014-12-01 08:38:55 -0800462 inverse.mapPoints(&fGlyphPositions[fFallbackGlyphsIdx], fallbackGlyphCount);
463 }
464
joshualitt570d2f82015-02-25 13:19:48 -0800465 fFallbackTextContext->drawPosText(fRenderTarget, fClip, paintFallback, skPaintFallback,
joshualitt25d9c152015-02-18 12:29:52 -0800466 fViewMatrix, (char*)&fGlyphIndices[fFallbackGlyphsIdx],
cdalton20b373c2014-12-01 08:38:55 -0800467 2 * fallbackGlyphCount,
468 get_xy_scalar_array(&fGlyphPositions[fFallbackGlyphsIdx]),
joshualitt6e8cd962015-03-20 10:30:14 -0700469 2, SkPoint::Make(0, 0), fRegionClipBounds);
cdalton20b373c2014-12-01 08:38:55 -0800470
471 fFallbackGlyphsIdx = kGlyphBufferSize;
472 }
kkinnunenc6cb56f2014-06-24 00:12:27 -0700473}
474
475void GrStencilAndCoverTextContext::finish() {
cdaltonb85a0aa2014-07-21 15:32:44 -0700476 this->flush();
kkinnunenc6cb56f2014-06-24 00:12:27 -0700477
cdalton855d83f2014-09-18 13:51:53 -0700478 fGlyphs->unref();
cdaltonb85a0aa2014-07-21 15:32:44 -0700479 fGlyphs = NULL;
cdalton855d83f2014-09-18 13:51:53 -0700480
481 SkGlyphCache::AttachCache(fGlyphCache);
cdaltonb85a0aa2014-07-21 15:32:44 -0700482 fGlyphCache = NULL;
kkinnunenc6cb56f2014-06-24 00:12:27 -0700483
egdaniel8dd688b2015-01-22 10:16:09 -0800484 fPipelineBuilder.stencil()->setDisabled();
kkinnunenc6cb56f2014-06-24 00:12:27 -0700485 fStateRestore.set(NULL);
joshualitt5531d512014-12-17 15:50:11 -0800486 fViewMatrix = fContextInitialMatrix;
kkinnunenc6cb56f2014-06-24 00:12:27 -0700487 GrTextContext::finish();
488}
489