blob: fbd32fc8561bb11b500eec9ca9188e903ecd4848 [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"
robertphillipsea461502015-05-26 11:38:03 -070010#include "GrDrawContext.h"
kkinnunenc6cb56f2014-06-24 00:12:27 -070011#include "GrDrawTarget.h"
kkinnunenc6cb56f2014-06-24 00:12:27 -070012#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
bsalomon1fcc01c2015-09-09 09:48:06 -070024#include "batches/GrDrawPathBatch.h"
25
joshualitt6e8cd962015-03-20 10:30:14 -070026GrStencilAndCoverTextContext::GrStencilAndCoverTextContext(GrContext* context,
robertphillipsfcf78292015-06-19 11:49:52 -070027 const SkSurfaceProps& surfaceProps)
bsalomon1fcc01c2015-09-09 09:48:06 -070028 : INHERITED(context, surfaceProps)
29 , fDraw(nullptr)
30 , fStroke(SkStrokeRec::kFill_InitStyle) {
kkinnunenc6cb56f2014-06-24 00:12:27 -070031}
32
joshualitt6e8cd962015-03-20 10:30:14 -070033GrStencilAndCoverTextContext*
robertphillipsf6703fa2015-09-01 05:36:47 -070034GrStencilAndCoverTextContext::Create(GrContext* context, const SkSurfaceProps& surfaceProps) {
35 GrStencilAndCoverTextContext* textContext =
36 new GrStencilAndCoverTextContext(context, surfaceProps);
37 textContext->fFallbackTextContext = GrAtlasTextContext::Create(context, surfaceProps);
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 }
kkinnunen50b58e62015-05-18 23:02:07 -070056 if (SkPathEffect* pe = skPaint.getPathEffect()) {
halcanary96fcdcc2015-08-27 07:41:13 -070057 if (pe->asADash(nullptr) != SkPathEffect::kDash_DashType) {
kkinnunen50b58e62015-05-18 23:02:07 -070058 return false;
59 }
jvanverth0fedb192014-10-08 09:07:27 -070060 }
61
62 // No hairlines unless we can map the 1 px width to the object space.
cdaltone68f7362015-03-25 14:02:37 -070063 if (skPaint.getStyle() == SkPaint::kStroke_Style
64 && skPaint.getStrokeWidth() == 0
joshualitt5531d512014-12-17 15:50:11 -080065 && viewMatrix.hasPerspective()) {
jvanverth0fedb192014-10-08 09:07:27 -070066 return false;
67 }
68
69 // No color bitmap fonts.
70 SkScalerContext::Rec rec;
halcanary96fcdcc2015-08-27 07:41:13 -070071 SkScalerContext::MakeRec(skPaint, &fSurfaceProps, nullptr, &rec);
jvanverth0fedb192014-10-08 09:07:27 -070072 return rec.getFormat() != SkMask::kARGB32_Format;
73}
74
robertphillipsf6703fa2015-09-01 05:36:47 -070075void GrStencilAndCoverTextContext::onDrawText(GrDrawContext* dc, GrRenderTarget* rt,
joshualitt570d2f82015-02-25 13:19:48 -080076 const GrClip& clip,
joshualitt25d9c152015-02-18 12:29:52 -080077 const GrPaint& paint,
cdalton20b373c2014-12-01 08:38:55 -080078 const SkPaint& skPaint,
joshualitt5531d512014-12-17 15:50:11 -080079 const SkMatrix& viewMatrix,
cdalton20b373c2014-12-01 08:38:55 -080080 const char text[],
81 size_t byteLength,
joshualitt6e8cd962015-03-20 10:30:14 -070082 SkScalar x, SkScalar y,
83 const SkIRect& regionClipBounds) {
halcanary96fcdcc2015-08-27 07:41:13 -070084 SkASSERT(byteLength == 0 || text != nullptr);
jvanverthaab626c2014-10-16 08:04:39 -070085
halcanary96fcdcc2015-08-27 07:41:13 -070086 if (text == nullptr || byteLength == 0 /*|| fRC->isEmpty()*/) {
jvanverthaab626c2014-10-16 08:04:39 -070087 return;
88 }
89
90 // This is the slow path, mainly used by Skia unit tests. The other
91 // backends (8888, gpu, ...) use device-space dependent glyph caches. In
92 // order to match the glyph positions that the other code paths produce, we
93 // must also use device-space dependent glyph cache. This has the
94 // side-effect that the glyph shape outline will be in device-space,
95 // too. This in turn has the side-effect that NVPR can not stroke the paths,
96 // as the stroke in NVPR is defined in object-space.
97 // NOTE: here we have following coincidence that works at the moment:
98 // - When using the device-space glyphs, the transforms we pass to NVPR
99 // instanced drawing are the global transforms, and the view transform is
100 // identity. NVPR can not use non-affine transforms in the instanced
101 // drawing. This is taken care of by SkDraw::ShouldDrawTextAsPaths since it
102 // will turn off the use of device-space glyphs when perspective transforms
103 // are in use.
104
joshualitt6e8cd962015-03-20 10:30:14 -0700105 this->init(rt, clip, paint, skPaint, byteLength, kMaxAccuracy_RenderMode, viewMatrix,
106 regionClipBounds);
jvanverthaab626c2014-10-16 08:04:39 -0700107
108 // Transform our starting point.
cdalton20b373c2014-12-01 08:38:55 -0800109 if (fUsingDeviceSpaceGlyphs) {
jvanverthaab626c2014-10-16 08:04:39 -0700110 SkPoint loc;
111 fContextInitialMatrix.mapXY(x, y, &loc);
112 x = loc.fX;
113 y = loc.fY;
114 }
115
116 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
117
jvanverthaab626c2014-10-16 08:04:39 -0700118 const char* stop = text + byteLength;
119
120 // Measure first if needed.
121 if (fSkPaint.getTextAlign() != SkPaint::kLeft_Align) {
122 SkFixed stopX = 0;
123 SkFixed stopY = 0;
124
125 const char* textPtr = text;
126 while (textPtr < stop) {
127 // We don't need x, y here, since all subpixel variants will have the
128 // same advance.
129 const SkGlyph& glyph = glyphCacheProc(fGlyphCache, &textPtr, 0, 0);
130
131 stopX += glyph.fAdvanceX;
132 stopY += glyph.fAdvanceY;
133 }
134 SkASSERT(textPtr == stop);
135
136 SkScalar alignX = SkFixedToScalar(stopX) * fTextRatio;
137 SkScalar alignY = SkFixedToScalar(stopY) * fTextRatio;
138
139 if (fSkPaint.getTextAlign() == SkPaint::kCenter_Align) {
140 alignX = SkScalarHalf(alignX);
141 alignY = SkScalarHalf(alignY);
142 }
143
144 x -= alignX;
145 y -= alignY;
146 }
147
148 SkAutoKern autokern;
149
150 SkFixed fixedSizeRatio = SkScalarToFixed(fTextRatio);
151
152 SkFixed fx = SkScalarToFixed(x);
153 SkFixed fy = SkScalarToFixed(y);
154 while (text < stop) {
155 const SkGlyph& glyph = glyphCacheProc(fGlyphCache, &text, 0, 0);
bungemand709ea82015-03-17 07:23:39 -0700156 fx += SkFixedMul(autokern.adjust(glyph), fixedSizeRatio);
jvanverthaab626c2014-10-16 08:04:39 -0700157 if (glyph.fWidth) {
bsalomon1fcc01c2015-09-09 09:48:06 -0700158 this->appendGlyph(glyph, SkPoint::Make(SkFixedToScalar(fx), SkFixedToScalar(fy)));
jvanverthaab626c2014-10-16 08:04:39 -0700159 }
160
bungemand709ea82015-03-17 07:23:39 -0700161 fx += SkFixedMul(glyph.fAdvanceX, fixedSizeRatio);
162 fy += SkFixedMul(glyph.fAdvanceY, fixedSizeRatio);
jvanverthaab626c2014-10-16 08:04:39 -0700163 }
164
robertphillipsf6703fa2015-09-01 05:36:47 -0700165 this->finish(dc);
jvanverthaab626c2014-10-16 08:04:39 -0700166}
167
robertphillipsf6703fa2015-09-01 05:36:47 -0700168void GrStencilAndCoverTextContext::onDrawPosText(GrDrawContext* dc, GrRenderTarget* rt,
joshualitt570d2f82015-02-25 13:19:48 -0800169 const GrClip& clip,
joshualitt25d9c152015-02-18 12:29:52 -0800170 const GrPaint& paint,
cdalton20b373c2014-12-01 08:38:55 -0800171 const SkPaint& skPaint,
joshualitt5531d512014-12-17 15:50:11 -0800172 const SkMatrix& viewMatrix,
cdalton20b373c2014-12-01 08:38:55 -0800173 const char text[],
174 size_t byteLength,
175 const SkScalar pos[],
176 int scalarsPerPosition,
joshualitt6e8cd962015-03-20 10:30:14 -0700177 const SkPoint& offset,
178 const SkIRect& regionClipBounds) {
halcanary96fcdcc2015-08-27 07:41:13 -0700179 SkASSERT(byteLength == 0 || text != nullptr);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700180 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
181
182 // nothing to draw
halcanary96fcdcc2015-08-27 07:41:13 -0700183 if (text == nullptr || byteLength == 0/* || fRC->isEmpty()*/) {
kkinnunenc6cb56f2014-06-24 00:12:27 -0700184 return;
185 }
186
187 // This is the fast path. Here we do not bake in the device-transform to
188 // the glyph outline or the advances. This is because we do not need to
189 // position the glyphs at all, since the caller has done the positioning.
190 // The positioning is based on SkPaint::measureText of individual
191 // glyphs. That already uses glyph cache without device transforms. Device
192 // transform is not part of SkPaint::measureText API, and thus we use the
193 // same glyphs as what were measured.
kkinnunenc6cb56f2014-06-24 00:12:27 -0700194
joshualitt6e8cd962015-03-20 10:30:14 -0700195 this->init(rt, clip, paint, skPaint, byteLength, kMaxPerformance_RenderMode, viewMatrix,
196 regionClipBounds);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700197
198 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
199
kkinnunenc6cb56f2014-06-24 00:12:27 -0700200 const char* stop = text + byteLength;
kkinnunenc6cb56f2014-06-24 00:12:27 -0700201
cdalton38e13ad2014-11-07 06:02:15 -0800202 SkTextMapStateProc tmsProc(SkMatrix::I(), offset, scalarsPerPosition);
bungeman79738cc2015-03-11 14:05:29 -0700203 SkTextAlignProc alignProc(fSkPaint.getTextAlign());
cdalton38e13ad2014-11-07 06:02:15 -0800204 while (text < stop) {
205 const SkGlyph& glyph = glyphCacheProc(fGlyphCache, &text, 0, 0);
206 if (glyph.fWidth) {
207 SkPoint tmsLoc;
208 tmsProc(pos, &tmsLoc);
209 SkPoint loc;
210 alignProc(tmsLoc, glyph, &loc);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700211
bsalomon1fcc01c2015-09-09 09:48:06 -0700212 this->appendGlyph(glyph, loc);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700213 }
cdalton38e13ad2014-11-07 06:02:15 -0800214 pos += scalarsPerPosition;
kkinnunenc6cb56f2014-06-24 00:12:27 -0700215 }
216
robertphillipsf6703fa2015-09-01 05:36:47 -0700217 this->finish(dc);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700218}
219
cdalton855d83f2014-09-18 13:51:53 -0700220static GrPathRange* get_gr_glyphs(GrContext* ctx,
221 const SkTypeface* typeface,
222 const SkDescriptor* desc,
kkinnunen50b58e62015-05-18 23:02:07 -0700223 const GrStrokeInfo& stroke) {
224
225 static const GrUniqueKey::Domain kPathGlyphDomain = GrUniqueKey::GenerateDomain();
226 int strokeDataCount = stroke.computeUniqueKeyFragmentData32Cnt();
227 GrUniqueKey glyphKey;
228 GrUniqueKey::Builder builder(&glyphKey, kPathGlyphDomain, 2 + strokeDataCount);
229 reinterpret_cast<uint32_t&>(builder[0]) = desc ? desc->getChecksum() : 0;
230 reinterpret_cast<uint32_t&>(builder[1]) = typeface ? typeface->uniqueID() : 0;
231 if (strokeDataCount > 0) {
232 stroke.asUniqueKeyFragment(&builder[2]);
233 }
bsalomon24db3b12015-01-23 04:24:04 -0800234 builder.finish();
cdalton855d83f2014-09-18 13:51:53 -0700235
236 SkAutoTUnref<GrPathRange> glyphs(
kkinnunen50b58e62015-05-18 23:02:07 -0700237 static_cast<GrPathRange*>(
238 ctx->resourceProvider()->findAndRefResourceByUniqueKey(glyphKey)));
halcanary96fcdcc2015-08-27 07:41:13 -0700239 if (nullptr == glyphs) {
bsalomon706f08f2015-05-22 07:35:58 -0700240 glyphs.reset(ctx->resourceProvider()->createGlyphs(typeface, desc, stroke));
kkinnunen50b58e62015-05-18 23:02:07 -0700241 ctx->resourceProvider()->assignUniqueKeyToResource(glyphKey, glyphs);
242 } else {
halcanary96fcdcc2015-08-27 07:41:13 -0700243 SkASSERT(nullptr == desc || glyphs->isEqualTo(*desc));
cdalton855d83f2014-09-18 13:51:53 -0700244 }
245
246 return glyphs.detach();
247}
248
joshualitt25d9c152015-02-18 12:29:52 -0800249void GrStencilAndCoverTextContext::init(GrRenderTarget* rt,
joshualitt570d2f82015-02-25 13:19:48 -0800250 const GrClip& clip,
joshualitt25d9c152015-02-18 12:29:52 -0800251 const GrPaint& paint,
kkinnunenc6cb56f2014-06-24 00:12:27 -0700252 const SkPaint& skPaint,
cdaltonb2808cd2014-07-25 14:13:57 -0700253 size_t textByteLength,
joshualitt5531d512014-12-17 15:50:11 -0800254 RenderMode renderMode,
joshualitt6e8cd962015-03-20 10:30:14 -0700255 const SkMatrix& viewMatrix,
256 const SkIRect& regionClipBounds) {
joshualitt9df46592015-07-09 10:55:28 -0700257 fClip = clip;
258
259 fRenderTarget.reset(SkRef(rt));
260
261 fRegionClipBounds = regionClipBounds;
262 fClip.getConservativeBounds(fRenderTarget->width(), fRenderTarget->height(), &fClipRect);
263
264 fPaint = paint;
265 fSkPaint = skPaint;
kkinnunenc6cb56f2014-06-24 00:12:27 -0700266
joshualitt5531d512014-12-17 15:50:11 -0800267 fContextInitialMatrix = viewMatrix;
268 fViewMatrix = viewMatrix;
joshualitt290c09b2014-12-19 13:45:20 -0800269 fLocalMatrix = SkMatrix::I();
cdaltonb2808cd2014-07-25 14:13:57 -0700270
cdalton855d83f2014-09-18 13:51:53 -0700271 const bool otherBackendsWillDrawAsPaths =
cdaltonb2808cd2014-07-25 14:13:57 -0700272 SkDraw::ShouldDrawTextAsPaths(skPaint, fContextInitialMatrix);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700273
cdalton20b373c2014-12-01 08:38:55 -0800274 fUsingDeviceSpaceGlyphs = !otherBackendsWillDrawAsPaths &&
cdalton855d83f2014-09-18 13:51:53 -0700275 kMaxAccuracy_RenderMode == renderMode &&
276 SkToBool(fContextInitialMatrix.getType() &
277 (SkMatrix::kScale_Mask | SkMatrix::kAffine_Mask));
kkinnunenc6cb56f2014-06-24 00:12:27 -0700278
cdalton20b373c2014-12-01 08:38:55 -0800279 if (fUsingDeviceSpaceGlyphs) {
cdalton855d83f2014-09-18 13:51:53 -0700280 // SkDraw::ShouldDrawTextAsPaths takes care of perspective transforms.
281 SkASSERT(!fContextInitialMatrix.hasPerspective());
cdaltonb2808cd2014-07-25 14:13:57 -0700282
cdalton20b373c2014-12-01 08:38:55 -0800283 // The whole shape (including stroke) will be baked into the glyph outlines. Make
284 // NVPR just fill the baked shapes.
kkinnunen50b58e62015-05-18 23:02:07 -0700285 fStroke = GrStrokeInfo(SkStrokeRec::kFill_InitStyle);
cdalton20b373c2014-12-01 08:38:55 -0800286
cdalton855d83f2014-09-18 13:51:53 -0700287 fTextRatio = fTextInverseRatio = 1.0f;
288
289 // Glyphs loaded by GPU path rendering have an inverted y-direction.
290 SkMatrix m;
291 m.setScale(1, -1);
joshualitt5531d512014-12-17 15:50:11 -0800292 fViewMatrix = m;
cdalton855d83f2014-09-18 13:51:53 -0700293
294 // Post-flip the initial matrix so we're left with just the flip after
295 // the paint preConcats the inverse.
296 m = fContextInitialMatrix;
297 m.postScale(1, -1);
joshualitt290c09b2014-12-19 13:45:20 -0800298 if (!m.invert(&fLocalMatrix)) {
299 SkDebugf("Not invertible!\n");
300 return;
301 }
cdalton855d83f2014-09-18 13:51:53 -0700302
robertphillipsfcf78292015-06-19 11:49:52 -0700303 fGlyphCache = fSkPaint.detachCache(&fSurfaceProps, &fContextInitialMatrix,
cdaltone05162d2014-12-01 08:57:33 -0800304 true /*ignoreGamma*/);
cdalton855d83f2014-09-18 13:51:53 -0700305 fGlyphs = get_gr_glyphs(fContext, fGlyphCache->getScalerContext()->getTypeface(),
cdalton20b373c2014-12-01 08:38:55 -0800306 &fGlyphCache->getDescriptor(), fStroke);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700307 } else {
cdalton855d83f2014-09-18 13:51:53 -0700308 // Don't bake strokes into the glyph outlines. We will stroke the glyphs
309 // using the GPU instead. This is the fast path.
kkinnunen50b58e62015-05-18 23:02:07 -0700310 fStroke = GrStrokeInfo(fSkPaint);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700311 fSkPaint.setStyle(SkPaint::kFill_Style);
cdalton855d83f2014-09-18 13:51:53 -0700312
cdalton20b373c2014-12-01 08:38:55 -0800313 if (fStroke.isHairlineStyle()) {
cdalton855d83f2014-09-18 13:51:53 -0700314 // Approximate hairline stroke.
315 SkScalar strokeWidth = SK_Scalar1 /
316 (SkVector::Make(fContextInitialMatrix.getScaleX(),
317 fContextInitialMatrix.getSkewY()).length());
cdalton20b373c2014-12-01 08:38:55 -0800318 fStroke.setStrokeStyle(strokeWidth, false /*strokeAndFill*/);
cdalton855d83f2014-09-18 13:51:53 -0700319
320 } else if (fSkPaint.isFakeBoldText() &&
321#ifdef SK_USE_FREETYPE_EMBOLDEN
322 kMaxPerformance_RenderMode == renderMode &&
323#endif
cdalton20b373c2014-12-01 08:38:55 -0800324 SkStrokeRec::kStroke_Style != fStroke.getStyle()) {
cdalton855d83f2014-09-18 13:51:53 -0700325
326 // Instead of baking fake bold into the glyph outlines, do it with the GPU stroke.
327 SkScalar fakeBoldScale = SkScalarInterpFunc(fSkPaint.getTextSize(),
328 kStdFakeBoldInterpKeys,
329 kStdFakeBoldInterpValues,
330 kStdFakeBoldInterpLength);
331 SkScalar extra = SkScalarMul(fSkPaint.getTextSize(), fakeBoldScale);
cdalton20b373c2014-12-01 08:38:55 -0800332 fStroke.setStrokeStyle(fStroke.needToApply() ? fStroke.getWidth() + extra : extra,
333 true /*strokeAndFill*/);
cdalton855d83f2014-09-18 13:51:53 -0700334
335 fSkPaint.setFakeBoldText(false);
336 }
337
338 bool canUseRawPaths;
kkinnunen50b58e62015-05-18 23:02:07 -0700339 if (!fStroke.isDashed() && (otherBackendsWillDrawAsPaths ||
340 kMaxPerformance_RenderMode == renderMode)) {
cdalton855d83f2014-09-18 13:51:53 -0700341 // We can draw the glyphs from canonically sized paths.
342 fTextRatio = fSkPaint.getTextSize() / SkPaint::kCanonicalTextSizeForPaths;
343 fTextInverseRatio = SkPaint::kCanonicalTextSizeForPaths / fSkPaint.getTextSize();
344
345 // Compensate for the glyphs being scaled by fTextRatio.
cdalton20b373c2014-12-01 08:38:55 -0800346 if (!fStroke.isFillStyle()) {
347 fStroke.setStrokeStyle(fStroke.getWidth() / fTextRatio,
348 SkStrokeRec::kStrokeAndFill_Style == fStroke.getStyle());
cdalton855d83f2014-09-18 13:51:53 -0700349 }
350
351 fSkPaint.setLinearText(true);
352 fSkPaint.setLCDRenderText(false);
353 fSkPaint.setAutohinted(false);
354 fSkPaint.setHinting(SkPaint::kNo_Hinting);
355 fSkPaint.setSubpixelText(true);
356 fSkPaint.setTextSize(SkIntToScalar(SkPaint::kCanonicalTextSizeForPaths));
357
358 canUseRawPaths = SK_Scalar1 == fSkPaint.getTextScaleX() &&
359 0 == fSkPaint.getTextSkewX() &&
360 !fSkPaint.isFakeBoldText() &&
361 !fSkPaint.isVerticalText();
362 } else {
363 fTextRatio = fTextInverseRatio = 1.0f;
364 canUseRawPaths = false;
365 }
366
367 SkMatrix textMatrix;
cdalton855d83f2014-09-18 13:51:53 -0700368 // Glyphs loaded by GPU path rendering have an inverted y-direction.
cdalton38e13ad2014-11-07 06:02:15 -0800369 textMatrix.setScale(fTextRatio, -fTextRatio);
joshualitt5531d512014-12-17 15:50:11 -0800370 fViewMatrix.preConcat(textMatrix);
joshualitt290c09b2014-12-19 13:45:20 -0800371 fLocalMatrix = textMatrix;
cdalton855d83f2014-09-18 13:51:53 -0700372
halcanary96fcdcc2015-08-27 07:41:13 -0700373 fGlyphCache = fSkPaint.detachCache(&fSurfaceProps, nullptr, true /*ignoreGamma*/);
cdalton855d83f2014-09-18 13:51:53 -0700374 fGlyphs = canUseRawPaths ?
halcanary96fcdcc2015-08-27 07:41:13 -0700375 get_gr_glyphs(fContext, fSkPaint.getTypeface(), nullptr, fStroke) :
cdalton855d83f2014-09-18 13:51:53 -0700376 get_gr_glyphs(fContext, fGlyphCache->getScalerContext()->getTypeface(),
cdalton20b373c2014-12-01 08:38:55 -0800377 &fGlyphCache->getDescriptor(), fStroke);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700378 }
cdalton855d83f2014-09-18 13:51:53 -0700379
kkinnunenc6cb56f2014-06-24 00:12:27 -0700380}
381
joshualitt5531d512014-12-17 15:50:11 -0800382bool GrStencilAndCoverTextContext::mapToFallbackContext(SkMatrix* inverse) {
cdalton20b373c2014-12-01 08:38:55 -0800383 // The current view matrix is flipped because GPU path rendering glyphs have an
384 // inverted y-direction. Unflip the view matrix for the fallback context. If using
385 // device-space glyphs, we'll also need to restore the original view matrix since
386 // we moved that transfomation into our local glyph cache for this scenario. Also
387 // track the inverse operation so the caller can unmap the paint and glyph positions.
388 if (fUsingDeviceSpaceGlyphs) {
joshualitt5531d512014-12-17 15:50:11 -0800389 fViewMatrix = fContextInitialMatrix;
cdalton20b373c2014-12-01 08:38:55 -0800390 if (!fContextInitialMatrix.invert(inverse)) {
391 return false;
392 }
393 inverse->preScale(1, -1);
394 } else {
395 inverse->setScale(1, -1);
396 const SkMatrix& unflip = *inverse; // unflip is equal to its own inverse.
joshualitt5531d512014-12-17 15:50:11 -0800397 fViewMatrix.preConcat(unflip);
cdalton20b373c2014-12-01 08:38:55 -0800398 }
399 return true;
400}
401
bsalomon1fcc01c2015-09-09 09:48:06 -0700402inline void GrStencilAndCoverTextContext::appendGlyph(const SkGlyph& glyph, const SkPoint& pos) {
403 // Stick the glyphs we can't draw into the fallback arrays.
404 if (SkMask::kARGB32_Format == glyph.fMaskFormat) {
405 fFallbackIndices.push_back(glyph.getGlyphID());
406 fFallbackPositions.push_back().set(fTextInverseRatio * pos.x(),
407 -fTextInverseRatio * pos.y());
408 } else {
409 // TODO: infer the reserve count from the text length.
410 if (!fDraw) {
411 fDraw = GrPathRangeDraw::Create(fGlyphs,
412 GrPathRendering::kTranslate_PathTransformType,
413 64);
414 }
415 float translate[] = { fTextInverseRatio * pos.x(), -fTextInverseRatio * pos.y() };
416 fDraw->append(glyph.getGlyphID(), translate);
cdaltonb2808cd2014-07-25 14:13:57 -0700417 }
cdalton20b373c2014-12-01 08:38:55 -0800418}
419
420static const SkScalar* get_xy_scalar_array(const SkPoint* pointArray) {
421 GR_STATIC_ASSERT(2 * sizeof(SkScalar) == sizeof(SkPoint));
422 GR_STATIC_ASSERT(0 == offsetof(SkPoint, fX));
423
424 return &pointArray[0].fX;
cdaltonb85a0aa2014-07-21 15:32:44 -0700425}
426
robertphillipsf6703fa2015-09-01 05:36:47 -0700427void GrStencilAndCoverTextContext::flush(GrDrawContext* dc) {
bsalomon1fcc01c2015-09-09 09:48:06 -0700428 if (fDraw) {
429 SkASSERT(fDraw->count());
robertphillipsea461502015-05-26 11:38:03 -0700430
joshualitt7b670db2015-07-09 13:25:02 -0700431 // We should only be flushing about once every run. However, if this impacts performance
432 // we could move the creation of the GrPipelineBuilder earlier.
433 GrPipelineBuilder pipelineBuilder(fPaint, fRenderTarget, fClip);
434 SkASSERT(fRenderTarget->isStencilBufferMultisampled() || !fPaint.isAntiAlias());
435 pipelineBuilder.setState(GrPipelineBuilder::kHWAntialias_Flag, fPaint.isAntiAlias());
436
437 GR_STATIC_CONST_SAME_STENCIL(kStencilPass,
438 kZero_StencilOp,
vbuzinovc5d58f02015-08-21 05:24:24 -0700439 kKeep_StencilOp,
joshualitt7b670db2015-07-09 13:25:02 -0700440 kNotEqual_StencilFunc,
441 0xffff,
442 0x0000,
443 0xffff);
444
445 *pipelineBuilder.stencil() = kStencilPass;
446
joshualittf2384692015-09-10 11:00:51 -0700447 dc->drawPathsFromRange(&pipelineBuilder, fViewMatrix, fLocalMatrix, fPaint.getColor(),
448 fDraw, GrPathRendering::kWinding_FillType);
bsalomon1fcc01c2015-09-09 09:48:06 -0700449 fDraw->unref();
450 fDraw = nullptr;
kkinnunenc6cb56f2014-06-24 00:12:27 -0700451 }
452
bsalomon1fcc01c2015-09-09 09:48:06 -0700453 if (fFallbackIndices.count()) {
454 SkASSERT(fFallbackPositions.count() == fFallbackIndices.count());
cdalton20b373c2014-12-01 08:38:55 -0800455 GrPaint paintFallback(fPaint);
456
457 SkPaint skPaintFallback(fSkPaint);
458 if (!fUsingDeviceSpaceGlyphs) {
459 fStroke.applyToPaint(&skPaintFallback);
460 }
461 skPaintFallback.setTextAlign(SkPaint::kLeft_Align); // Align has already been accounted for.
462 skPaintFallback.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
463
cdalton20b373c2014-12-01 08:38:55 -0800464 SkMatrix inverse;
joshualitt5531d512014-12-17 15:50:11 -0800465 if (this->mapToFallbackContext(&inverse)) {
bsalomon1fcc01c2015-09-09 09:48:06 -0700466 inverse.mapPoints(fFallbackPositions.begin(), fFallbackPositions.count());
cdalton20b373c2014-12-01 08:38:55 -0800467 }
468
robertphillipsf6703fa2015-09-01 05:36:47 -0700469 fFallbackTextContext->drawPosText(dc, fRenderTarget, fClip, paintFallback, skPaintFallback,
bsalomon1fcc01c2015-09-09 09:48:06 -0700470 fViewMatrix, (char*)fFallbackIndices.begin(),
471 sizeof(uint16_t) * fFallbackIndices.count(),
472 get_xy_scalar_array(fFallbackPositions.begin()),
joshualitt6e8cd962015-03-20 10:30:14 -0700473 2, SkPoint::Make(0, 0), fRegionClipBounds);
bsalomon1fcc01c2015-09-09 09:48:06 -0700474 fFallbackIndices.reset();
475 fFallbackPositions.reset();
cdalton20b373c2014-12-01 08:38:55 -0800476 }
kkinnunenc6cb56f2014-06-24 00:12:27 -0700477}
478
robertphillipsf6703fa2015-09-01 05:36:47 -0700479void GrStencilAndCoverTextContext::finish(GrDrawContext* dc) {
480 this->flush(dc);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700481
bsalomon1fcc01c2015-09-09 09:48:06 -0700482 SkASSERT(!fDraw);
483 SkASSERT(!fFallbackIndices.count());
484 SkASSERT(!fFallbackPositions.count());
485
cdalton855d83f2014-09-18 13:51:53 -0700486 fGlyphs->unref();
halcanary96fcdcc2015-08-27 07:41:13 -0700487 fGlyphs = nullptr;
cdalton855d83f2014-09-18 13:51:53 -0700488
489 SkGlyphCache::AttachCache(fGlyphCache);
halcanary96fcdcc2015-08-27 07:41:13 -0700490 fGlyphCache = nullptr;
kkinnunenc6cb56f2014-06-24 00:12:27 -0700491
joshualitt5531d512014-12-17 15:50:11 -0800492 fViewMatrix = fContextInitialMatrix;
kkinnunenc6cb56f2014-06-24 00:12:27 -0700493}