blob: 34319516d52a02166395fe0fc6acb644791a31b0 [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"
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"
bsalomond309e7a2015-04-30 14:18:54 -070014#include "GrResourceProvider.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));
joshualitt9bd2daf2015-04-17 09:30:06 -070038 textContext->fFallbackTextContext = GrAtlasTextContext::Create(context, gpuDevice, props,
39 false);
jvanverth8c27a182014-10-14 08:45:50 -070040
41 return textContext;
42}
43
kkinnunenc6cb56f2014-06-24 00:12:27 -070044GrStencilAndCoverTextContext::~GrStencilAndCoverTextContext() {
45}
46
cdaltone68f7362015-03-25 14:02:37 -070047bool GrStencilAndCoverTextContext::canDraw(const GrRenderTarget* rt,
48 const GrClip& clip,
49 const GrPaint& paint,
50 const SkPaint& skPaint,
51 const SkMatrix& viewMatrix) {
52 if (skPaint.getRasterizer()) {
jvanverth0fedb192014-10-08 09:07:27 -070053 return false;
54 }
cdaltone68f7362015-03-25 14:02:37 -070055 if (skPaint.getMaskFilter()) {
jvanverth0fedb192014-10-08 09:07:27 -070056 return false;
57 }
kkinnunen50b58e62015-05-18 23:02:07 -070058 if (SkPathEffect* pe = skPaint.getPathEffect()) {
59 if (pe->asADash(NULL) != SkPathEffect::kDash_DashType) {
60 return false;
61 }
jvanverth0fedb192014-10-08 09:07:27 -070062 }
63
64 // No hairlines unless we can map the 1 px width to the object space.
cdaltone68f7362015-03-25 14:02:37 -070065 if (skPaint.getStyle() == SkPaint::kStroke_Style
66 && skPaint.getStrokeWidth() == 0
joshualitt5531d512014-12-17 15:50:11 -080067 && viewMatrix.hasPerspective()) {
jvanverth0fedb192014-10-08 09:07:27 -070068 return false;
69 }
70
71 // No color bitmap fonts.
72 SkScalerContext::Rec rec;
cdaltone68f7362015-03-25 14:02:37 -070073 SkScalerContext::MakeRec(skPaint, &fDeviceProperties, NULL, &rec);
jvanverth0fedb192014-10-08 09:07:27 -070074 return rec.getFormat() != SkMask::kARGB32_Format;
75}
76
joshualitt25d9c152015-02-18 12:29:52 -080077void GrStencilAndCoverTextContext::onDrawText(GrRenderTarget* rt,
joshualitt570d2f82015-02-25 13:19:48 -080078 const GrClip& clip,
joshualitt25d9c152015-02-18 12:29:52 -080079 const GrPaint& paint,
cdalton20b373c2014-12-01 08:38:55 -080080 const SkPaint& skPaint,
joshualitt5531d512014-12-17 15:50:11 -080081 const SkMatrix& viewMatrix,
cdalton20b373c2014-12-01 08:38:55 -080082 const char text[],
83 size_t byteLength,
joshualitt6e8cd962015-03-20 10:30:14 -070084 SkScalar x, SkScalar y,
85 const SkIRect& regionClipBounds) {
jvanverthaab626c2014-10-16 08:04:39 -070086 SkASSERT(byteLength == 0 || text != NULL);
87
88 if (text == NULL || byteLength == 0 /*|| fRC->isEmpty()*/) {
89 return;
90 }
91
92 // This is the slow path, mainly used by Skia unit tests. The other
93 // backends (8888, gpu, ...) use device-space dependent glyph caches. In
94 // order to match the glyph positions that the other code paths produce, we
95 // must also use device-space dependent glyph cache. This has the
96 // side-effect that the glyph shape outline will be in device-space,
97 // too. This in turn has the side-effect that NVPR can not stroke the paths,
98 // as the stroke in NVPR is defined in object-space.
99 // NOTE: here we have following coincidence that works at the moment:
100 // - When using the device-space glyphs, the transforms we pass to NVPR
101 // instanced drawing are the global transforms, and the view transform is
102 // identity. NVPR can not use non-affine transforms in the instanced
103 // drawing. This is taken care of by SkDraw::ShouldDrawTextAsPaths since it
104 // will turn off the use of device-space glyphs when perspective transforms
105 // are in use.
106
joshualitt6e8cd962015-03-20 10:30:14 -0700107 this->init(rt, clip, paint, skPaint, byteLength, kMaxAccuracy_RenderMode, viewMatrix,
108 regionClipBounds);
jvanverthaab626c2014-10-16 08:04:39 -0700109
110 // Transform our starting point.
cdalton20b373c2014-12-01 08:38:55 -0800111 if (fUsingDeviceSpaceGlyphs) {
jvanverthaab626c2014-10-16 08:04:39 -0700112 SkPoint loc;
113 fContextInitialMatrix.mapXY(x, y, &loc);
114 x = loc.fX;
115 y = loc.fY;
116 }
117
118 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
119
jvanverthaab626c2014-10-16 08:04:39 -0700120 const char* stop = text + byteLength;
121
122 // Measure first if needed.
123 if (fSkPaint.getTextAlign() != SkPaint::kLeft_Align) {
124 SkFixed stopX = 0;
125 SkFixed stopY = 0;
126
127 const char* textPtr = text;
128 while (textPtr < stop) {
129 // We don't need x, y here, since all subpixel variants will have the
130 // same advance.
131 const SkGlyph& glyph = glyphCacheProc(fGlyphCache, &textPtr, 0, 0);
132
133 stopX += glyph.fAdvanceX;
134 stopY += glyph.fAdvanceY;
135 }
136 SkASSERT(textPtr == stop);
137
138 SkScalar alignX = SkFixedToScalar(stopX) * fTextRatio;
139 SkScalar alignY = SkFixedToScalar(stopY) * fTextRatio;
140
141 if (fSkPaint.getTextAlign() == SkPaint::kCenter_Align) {
142 alignX = SkScalarHalf(alignX);
143 alignY = SkScalarHalf(alignY);
144 }
145
146 x -= alignX;
147 y -= alignY;
148 }
149
150 SkAutoKern autokern;
151
152 SkFixed fixedSizeRatio = SkScalarToFixed(fTextRatio);
153
154 SkFixed fx = SkScalarToFixed(x);
155 SkFixed fy = SkScalarToFixed(y);
156 while (text < stop) {
157 const SkGlyph& glyph = glyphCacheProc(fGlyphCache, &text, 0, 0);
bungemand709ea82015-03-17 07:23:39 -0700158 fx += SkFixedMul(autokern.adjust(glyph), fixedSizeRatio);
jvanverthaab626c2014-10-16 08:04:39 -0700159 if (glyph.fWidth) {
cdalton20b373c2014-12-01 08:38:55 -0800160 this->appendGlyph(glyph, SkPoint::Make(SkFixedToScalar(fx), SkFixedToScalar(fy)));
jvanverthaab626c2014-10-16 08:04:39 -0700161 }
162
bungemand709ea82015-03-17 07:23:39 -0700163 fx += SkFixedMul(glyph.fAdvanceX, fixedSizeRatio);
164 fy += SkFixedMul(glyph.fAdvanceY, fixedSizeRatio);
jvanverthaab626c2014-10-16 08:04:39 -0700165 }
166
167 this->finish();
168}
169
joshualitt25d9c152015-02-18 12:29:52 -0800170void GrStencilAndCoverTextContext::onDrawPosText(GrRenderTarget* rt,
joshualitt570d2f82015-02-25 13:19:48 -0800171 const GrClip& clip,
joshualitt25d9c152015-02-18 12:29:52 -0800172 const GrPaint& paint,
cdalton20b373c2014-12-01 08:38:55 -0800173 const SkPaint& skPaint,
joshualitt5531d512014-12-17 15:50:11 -0800174 const SkMatrix& viewMatrix,
cdalton20b373c2014-12-01 08:38:55 -0800175 const char text[],
176 size_t byteLength,
177 const SkScalar pos[],
178 int scalarsPerPosition,
joshualitt6e8cd962015-03-20 10:30:14 -0700179 const SkPoint& offset,
180 const SkIRect& regionClipBounds) {
kkinnunenc6cb56f2014-06-24 00:12:27 -0700181 SkASSERT(byteLength == 0 || text != NULL);
182 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
183
184 // nothing to draw
185 if (text == NULL || byteLength == 0/* || fRC->isEmpty()*/) {
186 return;
187 }
188
189 // This is the fast path. Here we do not bake in the device-transform to
190 // the glyph outline or the advances. This is because we do not need to
191 // position the glyphs at all, since the caller has done the positioning.
192 // The positioning is based on SkPaint::measureText of individual
193 // glyphs. That already uses glyph cache without device transforms. Device
194 // transform is not part of SkPaint::measureText API, and thus we use the
195 // same glyphs as what were measured.
kkinnunenc6cb56f2014-06-24 00:12:27 -0700196
joshualitt6e8cd962015-03-20 10:30:14 -0700197 this->init(rt, clip, paint, skPaint, byteLength, kMaxPerformance_RenderMode, viewMatrix,
198 regionClipBounds);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700199
200 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
201
kkinnunenc6cb56f2014-06-24 00:12:27 -0700202 const char* stop = text + byteLength;
kkinnunenc6cb56f2014-06-24 00:12:27 -0700203
cdalton38e13ad2014-11-07 06:02:15 -0800204 SkTextMapStateProc tmsProc(SkMatrix::I(), offset, scalarsPerPosition);
bungeman79738cc2015-03-11 14:05:29 -0700205 SkTextAlignProc alignProc(fSkPaint.getTextAlign());
cdalton38e13ad2014-11-07 06:02:15 -0800206 while (text < stop) {
207 const SkGlyph& glyph = glyphCacheProc(fGlyphCache, &text, 0, 0);
208 if (glyph.fWidth) {
209 SkPoint tmsLoc;
210 tmsProc(pos, &tmsLoc);
211 SkPoint loc;
212 alignProc(tmsLoc, glyph, &loc);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700213
cdalton20b373c2014-12-01 08:38:55 -0800214 this->appendGlyph(glyph, loc);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700215 }
cdalton38e13ad2014-11-07 06:02:15 -0800216 pos += scalarsPerPosition;
kkinnunenc6cb56f2014-06-24 00:12:27 -0700217 }
218
219 this->finish();
220}
221
cdalton855d83f2014-09-18 13:51:53 -0700222static GrPathRange* get_gr_glyphs(GrContext* ctx,
223 const SkTypeface* typeface,
224 const SkDescriptor* desc,
kkinnunen50b58e62015-05-18 23:02:07 -0700225 const GrStrokeInfo& stroke) {
226
227 static const GrUniqueKey::Domain kPathGlyphDomain = GrUniqueKey::GenerateDomain();
228 int strokeDataCount = stroke.computeUniqueKeyFragmentData32Cnt();
229 GrUniqueKey glyphKey;
230 GrUniqueKey::Builder builder(&glyphKey, kPathGlyphDomain, 2 + strokeDataCount);
231 reinterpret_cast<uint32_t&>(builder[0]) = desc ? desc->getChecksum() : 0;
232 reinterpret_cast<uint32_t&>(builder[1]) = typeface ? typeface->uniqueID() : 0;
233 if (strokeDataCount > 0) {
234 stroke.asUniqueKeyFragment(&builder[2]);
235 }
bsalomon24db3b12015-01-23 04:24:04 -0800236 builder.finish();
cdalton855d83f2014-09-18 13:51:53 -0700237
238 SkAutoTUnref<GrPathRange> glyphs(
kkinnunen50b58e62015-05-18 23:02:07 -0700239 static_cast<GrPathRange*>(
240 ctx->resourceProvider()->findAndRefResourceByUniqueKey(glyphKey)));
241 if (NULL == glyphs) {
cdalton855d83f2014-09-18 13:51:53 -0700242 glyphs.reset(ctx->getGpu()->pathRendering()->createGlyphs(typeface, desc, stroke));
kkinnunen50b58e62015-05-18 23:02:07 -0700243 ctx->resourceProvider()->assignUniqueKeyToResource(glyphKey, glyphs);
244 } else {
245 SkASSERT(NULL == desc || glyphs->isEqualTo(*desc));
cdalton855d83f2014-09-18 13:51:53 -0700246 }
247
248 return glyphs.detach();
249}
250
joshualitt25d9c152015-02-18 12:29:52 -0800251void GrStencilAndCoverTextContext::init(GrRenderTarget* rt,
joshualitt570d2f82015-02-25 13:19:48 -0800252 const GrClip& clip,
joshualitt25d9c152015-02-18 12:29:52 -0800253 const GrPaint& paint,
kkinnunenc6cb56f2014-06-24 00:12:27 -0700254 const SkPaint& skPaint,
cdaltonb2808cd2014-07-25 14:13:57 -0700255 size_t textByteLength,
joshualitt5531d512014-12-17 15:50:11 -0800256 RenderMode renderMode,
joshualitt6e8cd962015-03-20 10:30:14 -0700257 const SkMatrix& viewMatrix,
258 const SkIRect& regionClipBounds) {
259 GrTextContext::init(rt, clip, paint, skPaint, regionClipBounds);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700260
joshualitt5531d512014-12-17 15:50:11 -0800261 fContextInitialMatrix = viewMatrix;
262 fViewMatrix = viewMatrix;
joshualitt290c09b2014-12-19 13:45:20 -0800263 fLocalMatrix = SkMatrix::I();
cdaltonb2808cd2014-07-25 14:13:57 -0700264
cdalton855d83f2014-09-18 13:51:53 -0700265 const bool otherBackendsWillDrawAsPaths =
cdaltonb2808cd2014-07-25 14:13:57 -0700266 SkDraw::ShouldDrawTextAsPaths(skPaint, fContextInitialMatrix);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700267
cdalton20b373c2014-12-01 08:38:55 -0800268 fUsingDeviceSpaceGlyphs = !otherBackendsWillDrawAsPaths &&
cdalton855d83f2014-09-18 13:51:53 -0700269 kMaxAccuracy_RenderMode == renderMode &&
270 SkToBool(fContextInitialMatrix.getType() &
271 (SkMatrix::kScale_Mask | SkMatrix::kAffine_Mask));
kkinnunenc6cb56f2014-06-24 00:12:27 -0700272
cdalton20b373c2014-12-01 08:38:55 -0800273 if (fUsingDeviceSpaceGlyphs) {
cdalton855d83f2014-09-18 13:51:53 -0700274 // SkDraw::ShouldDrawTextAsPaths takes care of perspective transforms.
275 SkASSERT(!fContextInitialMatrix.hasPerspective());
cdaltonb2808cd2014-07-25 14:13:57 -0700276
cdalton20b373c2014-12-01 08:38:55 -0800277 // The whole shape (including stroke) will be baked into the glyph outlines. Make
278 // NVPR just fill the baked shapes.
kkinnunen50b58e62015-05-18 23:02:07 -0700279 fStroke = GrStrokeInfo(SkStrokeRec::kFill_InitStyle);
cdalton20b373c2014-12-01 08:38:55 -0800280
cdalton855d83f2014-09-18 13:51:53 -0700281 fTextRatio = fTextInverseRatio = 1.0f;
282
283 // Glyphs loaded by GPU path rendering have an inverted y-direction.
284 SkMatrix m;
285 m.setScale(1, -1);
joshualitt5531d512014-12-17 15:50:11 -0800286 fViewMatrix = m;
cdalton855d83f2014-09-18 13:51:53 -0700287
288 // Post-flip the initial matrix so we're left with just the flip after
289 // the paint preConcats the inverse.
290 m = fContextInitialMatrix;
291 m.postScale(1, -1);
joshualitt290c09b2014-12-19 13:45:20 -0800292 if (!m.invert(&fLocalMatrix)) {
293 SkDebugf("Not invertible!\n");
294 return;
295 }
cdalton855d83f2014-09-18 13:51:53 -0700296
cdaltone05162d2014-12-01 08:57:33 -0800297 fGlyphCache = fSkPaint.detachCache(&fDeviceProperties, &fContextInitialMatrix,
298 true /*ignoreGamma*/);
cdalton855d83f2014-09-18 13:51:53 -0700299 fGlyphs = get_gr_glyphs(fContext, fGlyphCache->getScalerContext()->getTypeface(),
cdalton20b373c2014-12-01 08:38:55 -0800300 &fGlyphCache->getDescriptor(), fStroke);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700301 } else {
cdalton855d83f2014-09-18 13:51:53 -0700302 // Don't bake strokes into the glyph outlines. We will stroke the glyphs
303 // using the GPU instead. This is the fast path.
kkinnunen50b58e62015-05-18 23:02:07 -0700304 fStroke = GrStrokeInfo(fSkPaint);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700305 fSkPaint.setStyle(SkPaint::kFill_Style);
cdalton855d83f2014-09-18 13:51:53 -0700306
cdalton20b373c2014-12-01 08:38:55 -0800307 if (fStroke.isHairlineStyle()) {
cdalton855d83f2014-09-18 13:51:53 -0700308 // Approximate hairline stroke.
309 SkScalar strokeWidth = SK_Scalar1 /
310 (SkVector::Make(fContextInitialMatrix.getScaleX(),
311 fContextInitialMatrix.getSkewY()).length());
cdalton20b373c2014-12-01 08:38:55 -0800312 fStroke.setStrokeStyle(strokeWidth, false /*strokeAndFill*/);
cdalton855d83f2014-09-18 13:51:53 -0700313
314 } else if (fSkPaint.isFakeBoldText() &&
315#ifdef SK_USE_FREETYPE_EMBOLDEN
316 kMaxPerformance_RenderMode == renderMode &&
317#endif
cdalton20b373c2014-12-01 08:38:55 -0800318 SkStrokeRec::kStroke_Style != fStroke.getStyle()) {
cdalton855d83f2014-09-18 13:51:53 -0700319
320 // Instead of baking fake bold into the glyph outlines, do it with the GPU stroke.
321 SkScalar fakeBoldScale = SkScalarInterpFunc(fSkPaint.getTextSize(),
322 kStdFakeBoldInterpKeys,
323 kStdFakeBoldInterpValues,
324 kStdFakeBoldInterpLength);
325 SkScalar extra = SkScalarMul(fSkPaint.getTextSize(), fakeBoldScale);
cdalton20b373c2014-12-01 08:38:55 -0800326 fStroke.setStrokeStyle(fStroke.needToApply() ? fStroke.getWidth() + extra : extra,
327 true /*strokeAndFill*/);
cdalton855d83f2014-09-18 13:51:53 -0700328
329 fSkPaint.setFakeBoldText(false);
330 }
331
332 bool canUseRawPaths;
kkinnunen50b58e62015-05-18 23:02:07 -0700333 if (!fStroke.isDashed() && (otherBackendsWillDrawAsPaths ||
334 kMaxPerformance_RenderMode == renderMode)) {
cdalton855d83f2014-09-18 13:51:53 -0700335 // We can draw the glyphs from canonically sized paths.
336 fTextRatio = fSkPaint.getTextSize() / SkPaint::kCanonicalTextSizeForPaths;
337 fTextInverseRatio = SkPaint::kCanonicalTextSizeForPaths / fSkPaint.getTextSize();
338
339 // Compensate for the glyphs being scaled by fTextRatio.
cdalton20b373c2014-12-01 08:38:55 -0800340 if (!fStroke.isFillStyle()) {
341 fStroke.setStrokeStyle(fStroke.getWidth() / fTextRatio,
342 SkStrokeRec::kStrokeAndFill_Style == fStroke.getStyle());
cdalton855d83f2014-09-18 13:51:53 -0700343 }
344
345 fSkPaint.setLinearText(true);
346 fSkPaint.setLCDRenderText(false);
347 fSkPaint.setAutohinted(false);
348 fSkPaint.setHinting(SkPaint::kNo_Hinting);
349 fSkPaint.setSubpixelText(true);
350 fSkPaint.setTextSize(SkIntToScalar(SkPaint::kCanonicalTextSizeForPaths));
351
352 canUseRawPaths = SK_Scalar1 == fSkPaint.getTextScaleX() &&
353 0 == fSkPaint.getTextSkewX() &&
354 !fSkPaint.isFakeBoldText() &&
355 !fSkPaint.isVerticalText();
356 } else {
357 fTextRatio = fTextInverseRatio = 1.0f;
358 canUseRawPaths = false;
359 }
360
361 SkMatrix textMatrix;
cdalton855d83f2014-09-18 13:51:53 -0700362 // Glyphs loaded by GPU path rendering have an inverted y-direction.
cdalton38e13ad2014-11-07 06:02:15 -0800363 textMatrix.setScale(fTextRatio, -fTextRatio);
joshualitt5531d512014-12-17 15:50:11 -0800364 fViewMatrix.preConcat(textMatrix);
joshualitt290c09b2014-12-19 13:45:20 -0800365 fLocalMatrix = textMatrix;
cdalton855d83f2014-09-18 13:51:53 -0700366
cdaltone05162d2014-12-01 08:57:33 -0800367 fGlyphCache = fSkPaint.detachCache(&fDeviceProperties, NULL, true /*ignoreGamma*/);
cdalton855d83f2014-09-18 13:51:53 -0700368 fGlyphs = canUseRawPaths ?
cdalton20b373c2014-12-01 08:38:55 -0800369 get_gr_glyphs(fContext, fSkPaint.getTypeface(), NULL, fStroke) :
cdalton855d83f2014-09-18 13:51:53 -0700370 get_gr_glyphs(fContext, fGlyphCache->getScalerContext()->getTypeface(),
cdalton20b373c2014-12-01 08:38:55 -0800371 &fGlyphCache->getDescriptor(), fStroke);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700372 }
cdalton855d83f2014-09-18 13:51:53 -0700373
egdaniel8dd688b2015-01-22 10:16:09 -0800374 fStateRestore.set(&fPipelineBuilder);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700375
joshualitt44701df2015-02-23 14:44:57 -0800376 fPipelineBuilder.setFromPaint(fPaint, fRenderTarget, fClip);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700377
378 GR_STATIC_CONST_SAME_STENCIL(kStencilPass,
379 kZero_StencilOp,
380 kZero_StencilOp,
381 kNotEqual_StencilFunc,
382 0xffff,
383 0x0000,
384 0xffff);
385
egdaniel8dd688b2015-01-22 10:16:09 -0800386 *fPipelineBuilder.stencil() = kStencilPass;
kkinnunenc6cb56f2014-06-24 00:12:27 -0700387
cdalton20b373c2014-12-01 08:38:55 -0800388 SkASSERT(0 == fQueuedGlyphCount);
389 SkASSERT(kGlyphBufferSize == fFallbackGlyphsIdx);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700390}
391
joshualitt5531d512014-12-17 15:50:11 -0800392bool GrStencilAndCoverTextContext::mapToFallbackContext(SkMatrix* inverse) {
cdalton20b373c2014-12-01 08:38:55 -0800393 // The current view matrix is flipped because GPU path rendering glyphs have an
394 // inverted y-direction. Unflip the view matrix for the fallback context. If using
395 // device-space glyphs, we'll also need to restore the original view matrix since
396 // we moved that transfomation into our local glyph cache for this scenario. Also
397 // track the inverse operation so the caller can unmap the paint and glyph positions.
398 if (fUsingDeviceSpaceGlyphs) {
joshualitt5531d512014-12-17 15:50:11 -0800399 fViewMatrix = fContextInitialMatrix;
cdalton20b373c2014-12-01 08:38:55 -0800400 if (!fContextInitialMatrix.invert(inverse)) {
401 return false;
402 }
403 inverse->preScale(1, -1);
404 } else {
405 inverse->setScale(1, -1);
406 const SkMatrix& unflip = *inverse; // unflip is equal to its own inverse.
joshualitt5531d512014-12-17 15:50:11 -0800407 fViewMatrix.preConcat(unflip);
cdalton20b373c2014-12-01 08:38:55 -0800408 }
409 return true;
410}
411
412inline void GrStencilAndCoverTextContext::appendGlyph(const SkGlyph& glyph, const SkPoint& pos) {
413 if (fQueuedGlyphCount >= fFallbackGlyphsIdx) {
414 SkASSERT(fQueuedGlyphCount == fFallbackGlyphsIdx);
cdaltonb2808cd2014-07-25 14:13:57 -0700415 this->flush();
416 }
417
cdalton20b373c2014-12-01 08:38:55 -0800418 // Stick the glyphs we can't draw at the end of the buffer, growing backwards.
419 int index = (SkMask::kARGB32_Format == glyph.fMaskFormat) ?
420 --fFallbackGlyphsIdx : fQueuedGlyphCount++;
cdaltonb85a0aa2014-07-21 15:32:44 -0700421
cdalton20b373c2014-12-01 08:38:55 -0800422 fGlyphIndices[index] = glyph.getGlyphID();
423 fGlyphPositions[index].set(fTextInverseRatio * pos.x(), -fTextInverseRatio * pos.y());
424}
425
426static const SkScalar* get_xy_scalar_array(const SkPoint* pointArray) {
427 GR_STATIC_ASSERT(2 * sizeof(SkScalar) == sizeof(SkPoint));
428 GR_STATIC_ASSERT(0 == offsetof(SkPoint, fX));
429
430 return &pointArray[0].fX;
cdaltonb85a0aa2014-07-21 15:32:44 -0700431}
432
433void GrStencilAndCoverTextContext::flush() {
cdalton20b373c2014-12-01 08:38:55 -0800434 if (fQueuedGlyphCount > 0) {
joshualitt8059eb92014-12-29 15:10:07 -0800435 SkAutoTUnref<GrPathProcessor> pp(GrPathProcessor::Create(fPaint.getColor(),
436 fViewMatrix,
437 fLocalMatrix));
egdaniel8dd688b2015-01-22 10:16:09 -0800438 fDrawTarget->drawPaths(&fPipelineBuilder, pp, fGlyphs,
cdalton20b373c2014-12-01 08:38:55 -0800439 fGlyphIndices, GrPathRange::kU16_PathIndexType,
440 get_xy_scalar_array(fGlyphPositions),
441 GrPathRendering::kTranslate_PathTransformType,
442 fQueuedGlyphCount, GrPathRendering::kWinding_FillType);
443
444 fQueuedGlyphCount = 0;
kkinnunenc6cb56f2014-06-24 00:12:27 -0700445 }
446
cdalton20b373c2014-12-01 08:38:55 -0800447 if (fFallbackGlyphsIdx < kGlyphBufferSize) {
448 int fallbackGlyphCount = kGlyphBufferSize - fFallbackGlyphsIdx;
cdaltonb85a0aa2014-07-21 15:32:44 -0700449
cdalton20b373c2014-12-01 08:38:55 -0800450 GrPaint paintFallback(fPaint);
451
452 SkPaint skPaintFallback(fSkPaint);
453 if (!fUsingDeviceSpaceGlyphs) {
454 fStroke.applyToPaint(&skPaintFallback);
455 }
456 skPaintFallback.setTextAlign(SkPaint::kLeft_Align); // Align has already been accounted for.
457 skPaintFallback.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
458
cdalton20b373c2014-12-01 08:38:55 -0800459 SkMatrix inverse;
joshualitt5531d512014-12-17 15:50:11 -0800460 if (this->mapToFallbackContext(&inverse)) {
cdalton20b373c2014-12-01 08:38:55 -0800461 inverse.mapPoints(&fGlyphPositions[fFallbackGlyphsIdx], fallbackGlyphCount);
462 }
463
joshualitt570d2f82015-02-25 13:19:48 -0800464 fFallbackTextContext->drawPosText(fRenderTarget, fClip, paintFallback, skPaintFallback,
joshualitt25d9c152015-02-18 12:29:52 -0800465 fViewMatrix, (char*)&fGlyphIndices[fFallbackGlyphsIdx],
cdalton20b373c2014-12-01 08:38:55 -0800466 2 * fallbackGlyphCount,
467 get_xy_scalar_array(&fGlyphPositions[fFallbackGlyphsIdx]),
joshualitt6e8cd962015-03-20 10:30:14 -0700468 2, SkPoint::Make(0, 0), fRegionClipBounds);
cdalton20b373c2014-12-01 08:38:55 -0800469
470 fFallbackGlyphsIdx = kGlyphBufferSize;
471 }
kkinnunenc6cb56f2014-06-24 00:12:27 -0700472}
473
474void GrStencilAndCoverTextContext::finish() {
cdaltonb85a0aa2014-07-21 15:32:44 -0700475 this->flush();
kkinnunenc6cb56f2014-06-24 00:12:27 -0700476
cdalton855d83f2014-09-18 13:51:53 -0700477 fGlyphs->unref();
cdaltonb85a0aa2014-07-21 15:32:44 -0700478 fGlyphs = NULL;
cdalton855d83f2014-09-18 13:51:53 -0700479
480 SkGlyphCache::AttachCache(fGlyphCache);
cdaltonb85a0aa2014-07-21 15:32:44 -0700481 fGlyphCache = NULL;
kkinnunenc6cb56f2014-06-24 00:12:27 -0700482
egdaniel8dd688b2015-01-22 10:16:09 -0800483 fPipelineBuilder.stencil()->setDisabled();
kkinnunenc6cb56f2014-06-24 00:12:27 -0700484 fStateRestore.set(NULL);
joshualitt5531d512014-12-17 15:50:11 -0800485 fViewMatrix = fContextInitialMatrix;
kkinnunenc6cb56f2014-06-24 00:12:27 -0700486 GrTextContext::finish();
487}
488