blob: 479213b21c1b46c58d137a80968ae31a3b305f6f [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
joshualitt6e8cd962015-03-20 10:30:14 -070024GrStencilAndCoverTextContext::GrStencilAndCoverTextContext(GrContext* context,
robertphillipsfcf78292015-06-19 11:49:52 -070025 const SkSurfaceProps& surfaceProps)
robertphillipsf6703fa2015-09-01 05:36:47 -070026 : GrTextContext(context, surfaceProps)
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*
robertphillipsf6703fa2015-09-01 05:36:47 -070033GrStencilAndCoverTextContext::Create(GrContext* context, const SkSurfaceProps& surfaceProps) {
34 GrStencilAndCoverTextContext* textContext =
35 new GrStencilAndCoverTextContext(context, surfaceProps);
36 textContext->fFallbackTextContext = GrAtlasTextContext::Create(context, surfaceProps);
jvanverth8c27a182014-10-14 08:45:50 -070037
38 return textContext;
39}
40
kkinnunenc6cb56f2014-06-24 00:12:27 -070041GrStencilAndCoverTextContext::~GrStencilAndCoverTextContext() {
42}
43
cdaltone68f7362015-03-25 14:02:37 -070044bool GrStencilAndCoverTextContext::canDraw(const GrRenderTarget* rt,
45 const GrClip& clip,
46 const GrPaint& paint,
47 const SkPaint& skPaint,
48 const SkMatrix& viewMatrix) {
49 if (skPaint.getRasterizer()) {
jvanverth0fedb192014-10-08 09:07:27 -070050 return false;
51 }
cdaltone68f7362015-03-25 14:02:37 -070052 if (skPaint.getMaskFilter()) {
jvanverth0fedb192014-10-08 09:07:27 -070053 return false;
54 }
kkinnunen50b58e62015-05-18 23:02:07 -070055 if (SkPathEffect* pe = skPaint.getPathEffect()) {
halcanary96fcdcc2015-08-27 07:41:13 -070056 if (pe->asADash(nullptr) != SkPathEffect::kDash_DashType) {
kkinnunen50b58e62015-05-18 23:02:07 -070057 return false;
58 }
jvanverth0fedb192014-10-08 09:07:27 -070059 }
60
61 // No hairlines unless we can map the 1 px width to the object space.
cdaltone68f7362015-03-25 14:02:37 -070062 if (skPaint.getStyle() == SkPaint::kStroke_Style
63 && skPaint.getStrokeWidth() == 0
joshualitt5531d512014-12-17 15:50:11 -080064 && viewMatrix.hasPerspective()) {
jvanverth0fedb192014-10-08 09:07:27 -070065 return false;
66 }
67
68 // No color bitmap fonts.
69 SkScalerContext::Rec rec;
halcanary96fcdcc2015-08-27 07:41:13 -070070 SkScalerContext::MakeRec(skPaint, &fSurfaceProps, nullptr, &rec);
jvanverth0fedb192014-10-08 09:07:27 -070071 return rec.getFormat() != SkMask::kARGB32_Format;
72}
73
robertphillipsf6703fa2015-09-01 05:36:47 -070074void GrStencilAndCoverTextContext::onDrawText(GrDrawContext* dc, GrRenderTarget* rt,
joshualitt570d2f82015-02-25 13:19:48 -080075 const GrClip& clip,
joshualitt25d9c152015-02-18 12:29:52 -080076 const GrPaint& paint,
cdalton20b373c2014-12-01 08:38:55 -080077 const SkPaint& skPaint,
joshualitt5531d512014-12-17 15:50:11 -080078 const SkMatrix& viewMatrix,
cdalton20b373c2014-12-01 08:38:55 -080079 const char text[],
80 size_t byteLength,
joshualitt6e8cd962015-03-20 10:30:14 -070081 SkScalar x, SkScalar y,
82 const SkIRect& regionClipBounds) {
halcanary96fcdcc2015-08-27 07:41:13 -070083 SkASSERT(byteLength == 0 || text != nullptr);
jvanverthaab626c2014-10-16 08:04:39 -070084
halcanary96fcdcc2015-08-27 07:41:13 -070085 if (text == nullptr || byteLength == 0 /*|| fRC->isEmpty()*/) {
jvanverthaab626c2014-10-16 08:04:39 -070086 return;
87 }
88
89 // This is the slow path, mainly used by Skia unit tests. The other
90 // backends (8888, gpu, ...) use device-space dependent glyph caches. In
91 // order to match the glyph positions that the other code paths produce, we
92 // must also use device-space dependent glyph cache. This has the
93 // side-effect that the glyph shape outline will be in device-space,
94 // too. This in turn has the side-effect that NVPR can not stroke the paths,
95 // as the stroke in NVPR is defined in object-space.
96 // NOTE: here we have following coincidence that works at the moment:
97 // - When using the device-space glyphs, the transforms we pass to NVPR
98 // instanced drawing are the global transforms, and the view transform is
99 // identity. NVPR can not use non-affine transforms in the instanced
100 // drawing. This is taken care of by SkDraw::ShouldDrawTextAsPaths since it
101 // will turn off the use of device-space glyphs when perspective transforms
102 // are in use.
103
joshualitt6e8cd962015-03-20 10:30:14 -0700104 this->init(rt, clip, paint, skPaint, byteLength, kMaxAccuracy_RenderMode, viewMatrix,
105 regionClipBounds);
jvanverthaab626c2014-10-16 08:04:39 -0700106
107 // Transform our starting point.
cdalton20b373c2014-12-01 08:38:55 -0800108 if (fUsingDeviceSpaceGlyphs) {
jvanverthaab626c2014-10-16 08:04:39 -0700109 SkPoint loc;
110 fContextInitialMatrix.mapXY(x, y, &loc);
111 x = loc.fX;
112 y = loc.fY;
113 }
114
115 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
116
jvanverthaab626c2014-10-16 08:04:39 -0700117 const char* stop = text + byteLength;
118
119 // Measure first if needed.
120 if (fSkPaint.getTextAlign() != SkPaint::kLeft_Align) {
121 SkFixed stopX = 0;
122 SkFixed stopY = 0;
123
124 const char* textPtr = text;
125 while (textPtr < stop) {
126 // We don't need x, y here, since all subpixel variants will have the
127 // same advance.
128 const SkGlyph& glyph = glyphCacheProc(fGlyphCache, &textPtr, 0, 0);
129
130 stopX += glyph.fAdvanceX;
131 stopY += glyph.fAdvanceY;
132 }
133 SkASSERT(textPtr == stop);
134
135 SkScalar alignX = SkFixedToScalar(stopX) * fTextRatio;
136 SkScalar alignY = SkFixedToScalar(stopY) * fTextRatio;
137
138 if (fSkPaint.getTextAlign() == SkPaint::kCenter_Align) {
139 alignX = SkScalarHalf(alignX);
140 alignY = SkScalarHalf(alignY);
141 }
142
143 x -= alignX;
144 y -= alignY;
145 }
146
147 SkAutoKern autokern;
148
149 SkFixed fixedSizeRatio = SkScalarToFixed(fTextRatio);
150
151 SkFixed fx = SkScalarToFixed(x);
152 SkFixed fy = SkScalarToFixed(y);
153 while (text < stop) {
154 const SkGlyph& glyph = glyphCacheProc(fGlyphCache, &text, 0, 0);
bungemand709ea82015-03-17 07:23:39 -0700155 fx += SkFixedMul(autokern.adjust(glyph), fixedSizeRatio);
jvanverthaab626c2014-10-16 08:04:39 -0700156 if (glyph.fWidth) {
robertphillipsf6703fa2015-09-01 05:36:47 -0700157 this->appendGlyph(dc, glyph, SkPoint::Make(SkFixedToScalar(fx), SkFixedToScalar(fy)));
jvanverthaab626c2014-10-16 08:04:39 -0700158 }
159
bungemand709ea82015-03-17 07:23:39 -0700160 fx += SkFixedMul(glyph.fAdvanceX, fixedSizeRatio);
161 fy += SkFixedMul(glyph.fAdvanceY, fixedSizeRatio);
jvanverthaab626c2014-10-16 08:04:39 -0700162 }
163
robertphillipsf6703fa2015-09-01 05:36:47 -0700164 this->finish(dc);
jvanverthaab626c2014-10-16 08:04:39 -0700165}
166
robertphillipsf6703fa2015-09-01 05:36:47 -0700167void GrStencilAndCoverTextContext::onDrawPosText(GrDrawContext* dc, GrRenderTarget* rt,
joshualitt570d2f82015-02-25 13:19:48 -0800168 const GrClip& clip,
joshualitt25d9c152015-02-18 12:29:52 -0800169 const GrPaint& paint,
cdalton20b373c2014-12-01 08:38:55 -0800170 const SkPaint& skPaint,
joshualitt5531d512014-12-17 15:50:11 -0800171 const SkMatrix& viewMatrix,
cdalton20b373c2014-12-01 08:38:55 -0800172 const char text[],
173 size_t byteLength,
174 const SkScalar pos[],
175 int scalarsPerPosition,
joshualitt6e8cd962015-03-20 10:30:14 -0700176 const SkPoint& offset,
177 const SkIRect& regionClipBounds) {
halcanary96fcdcc2015-08-27 07:41:13 -0700178 SkASSERT(byteLength == 0 || text != nullptr);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700179 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
180
181 // nothing to draw
halcanary96fcdcc2015-08-27 07:41:13 -0700182 if (text == nullptr || byteLength == 0/* || fRC->isEmpty()*/) {
kkinnunenc6cb56f2014-06-24 00:12:27 -0700183 return;
184 }
185
186 // This is the fast path. Here we do not bake in the device-transform to
187 // the glyph outline or the advances. This is because we do not need to
188 // position the glyphs at all, since the caller has done the positioning.
189 // The positioning is based on SkPaint::measureText of individual
190 // glyphs. That already uses glyph cache without device transforms. Device
191 // transform is not part of SkPaint::measureText API, and thus we use the
192 // same glyphs as what were measured.
kkinnunenc6cb56f2014-06-24 00:12:27 -0700193
joshualitt6e8cd962015-03-20 10:30:14 -0700194 this->init(rt, clip, paint, skPaint, byteLength, kMaxPerformance_RenderMode, viewMatrix,
195 regionClipBounds);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700196
197 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
198
kkinnunenc6cb56f2014-06-24 00:12:27 -0700199 const char* stop = text + byteLength;
kkinnunenc6cb56f2014-06-24 00:12:27 -0700200
cdalton38e13ad2014-11-07 06:02:15 -0800201 SkTextMapStateProc tmsProc(SkMatrix::I(), offset, scalarsPerPosition);
bungeman79738cc2015-03-11 14:05:29 -0700202 SkTextAlignProc alignProc(fSkPaint.getTextAlign());
cdalton38e13ad2014-11-07 06:02:15 -0800203 while (text < stop) {
204 const SkGlyph& glyph = glyphCacheProc(fGlyphCache, &text, 0, 0);
205 if (glyph.fWidth) {
206 SkPoint tmsLoc;
207 tmsProc(pos, &tmsLoc);
208 SkPoint loc;
209 alignProc(tmsLoc, glyph, &loc);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700210
robertphillipsf6703fa2015-09-01 05:36:47 -0700211 this->appendGlyph(dc, glyph, loc);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700212 }
cdalton38e13ad2014-11-07 06:02:15 -0800213 pos += scalarsPerPosition;
kkinnunenc6cb56f2014-06-24 00:12:27 -0700214 }
215
robertphillipsf6703fa2015-09-01 05:36:47 -0700216 this->finish(dc);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700217}
218
cdalton855d83f2014-09-18 13:51:53 -0700219static GrPathRange* get_gr_glyphs(GrContext* ctx,
220 const SkTypeface* typeface,
221 const SkDescriptor* desc,
kkinnunen50b58e62015-05-18 23:02:07 -0700222 const GrStrokeInfo& stroke) {
223
224 static const GrUniqueKey::Domain kPathGlyphDomain = GrUniqueKey::GenerateDomain();
225 int strokeDataCount = stroke.computeUniqueKeyFragmentData32Cnt();
226 GrUniqueKey glyphKey;
227 GrUniqueKey::Builder builder(&glyphKey, kPathGlyphDomain, 2 + strokeDataCount);
228 reinterpret_cast<uint32_t&>(builder[0]) = desc ? desc->getChecksum() : 0;
229 reinterpret_cast<uint32_t&>(builder[1]) = typeface ? typeface->uniqueID() : 0;
230 if (strokeDataCount > 0) {
231 stroke.asUniqueKeyFragment(&builder[2]);
232 }
bsalomon24db3b12015-01-23 04:24:04 -0800233 builder.finish();
cdalton855d83f2014-09-18 13:51:53 -0700234
235 SkAutoTUnref<GrPathRange> glyphs(
kkinnunen50b58e62015-05-18 23:02:07 -0700236 static_cast<GrPathRange*>(
237 ctx->resourceProvider()->findAndRefResourceByUniqueKey(glyphKey)));
halcanary96fcdcc2015-08-27 07:41:13 -0700238 if (nullptr == glyphs) {
bsalomon706f08f2015-05-22 07:35:58 -0700239 glyphs.reset(ctx->resourceProvider()->createGlyphs(typeface, desc, stroke));
kkinnunen50b58e62015-05-18 23:02:07 -0700240 ctx->resourceProvider()->assignUniqueKeyToResource(glyphKey, glyphs);
241 } else {
halcanary96fcdcc2015-08-27 07:41:13 -0700242 SkASSERT(nullptr == desc || glyphs->isEqualTo(*desc));
cdalton855d83f2014-09-18 13:51:53 -0700243 }
244
245 return glyphs.detach();
246}
247
joshualitt25d9c152015-02-18 12:29:52 -0800248void GrStencilAndCoverTextContext::init(GrRenderTarget* rt,
joshualitt570d2f82015-02-25 13:19:48 -0800249 const GrClip& clip,
joshualitt25d9c152015-02-18 12:29:52 -0800250 const GrPaint& paint,
kkinnunenc6cb56f2014-06-24 00:12:27 -0700251 const SkPaint& skPaint,
cdaltonb2808cd2014-07-25 14:13:57 -0700252 size_t textByteLength,
joshualitt5531d512014-12-17 15:50:11 -0800253 RenderMode renderMode,
joshualitt6e8cd962015-03-20 10:30:14 -0700254 const SkMatrix& viewMatrix,
255 const SkIRect& regionClipBounds) {
joshualitt9df46592015-07-09 10:55:28 -0700256 fClip = clip;
257
258 fRenderTarget.reset(SkRef(rt));
259
260 fRegionClipBounds = regionClipBounds;
261 fClip.getConservativeBounds(fRenderTarget->width(), fRenderTarget->height(), &fClipRect);
262
263 fPaint = paint;
264 fSkPaint = skPaint;
kkinnunenc6cb56f2014-06-24 00:12:27 -0700265
joshualitt5531d512014-12-17 15:50:11 -0800266 fContextInitialMatrix = viewMatrix;
267 fViewMatrix = viewMatrix;
joshualitt290c09b2014-12-19 13:45:20 -0800268 fLocalMatrix = SkMatrix::I();
cdaltonb2808cd2014-07-25 14:13:57 -0700269
cdalton855d83f2014-09-18 13:51:53 -0700270 const bool otherBackendsWillDrawAsPaths =
cdaltonb2808cd2014-07-25 14:13:57 -0700271 SkDraw::ShouldDrawTextAsPaths(skPaint, fContextInitialMatrix);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700272
cdalton20b373c2014-12-01 08:38:55 -0800273 fUsingDeviceSpaceGlyphs = !otherBackendsWillDrawAsPaths &&
cdalton855d83f2014-09-18 13:51:53 -0700274 kMaxAccuracy_RenderMode == renderMode &&
275 SkToBool(fContextInitialMatrix.getType() &
276 (SkMatrix::kScale_Mask | SkMatrix::kAffine_Mask));
kkinnunenc6cb56f2014-06-24 00:12:27 -0700277
cdalton20b373c2014-12-01 08:38:55 -0800278 if (fUsingDeviceSpaceGlyphs) {
cdalton855d83f2014-09-18 13:51:53 -0700279 // SkDraw::ShouldDrawTextAsPaths takes care of perspective transforms.
280 SkASSERT(!fContextInitialMatrix.hasPerspective());
cdaltonb2808cd2014-07-25 14:13:57 -0700281
cdalton20b373c2014-12-01 08:38:55 -0800282 // The whole shape (including stroke) will be baked into the glyph outlines. Make
283 // NVPR just fill the baked shapes.
kkinnunen50b58e62015-05-18 23:02:07 -0700284 fStroke = GrStrokeInfo(SkStrokeRec::kFill_InitStyle);
cdalton20b373c2014-12-01 08:38:55 -0800285
cdalton855d83f2014-09-18 13:51:53 -0700286 fTextRatio = fTextInverseRatio = 1.0f;
287
288 // Glyphs loaded by GPU path rendering have an inverted y-direction.
289 SkMatrix m;
290 m.setScale(1, -1);
joshualitt5531d512014-12-17 15:50:11 -0800291 fViewMatrix = m;
cdalton855d83f2014-09-18 13:51:53 -0700292
293 // Post-flip the initial matrix so we're left with just the flip after
294 // the paint preConcats the inverse.
295 m = fContextInitialMatrix;
296 m.postScale(1, -1);
joshualitt290c09b2014-12-19 13:45:20 -0800297 if (!m.invert(&fLocalMatrix)) {
298 SkDebugf("Not invertible!\n");
299 return;
300 }
cdalton855d83f2014-09-18 13:51:53 -0700301
robertphillipsfcf78292015-06-19 11:49:52 -0700302 fGlyphCache = fSkPaint.detachCache(&fSurfaceProps, &fContextInitialMatrix,
cdaltone05162d2014-12-01 08:57:33 -0800303 true /*ignoreGamma*/);
cdalton855d83f2014-09-18 13:51:53 -0700304 fGlyphs = get_gr_glyphs(fContext, fGlyphCache->getScalerContext()->getTypeface(),
cdalton20b373c2014-12-01 08:38:55 -0800305 &fGlyphCache->getDescriptor(), fStroke);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700306 } else {
cdalton855d83f2014-09-18 13:51:53 -0700307 // Don't bake strokes into the glyph outlines. We will stroke the glyphs
308 // using the GPU instead. This is the fast path.
kkinnunen50b58e62015-05-18 23:02:07 -0700309 fStroke = GrStrokeInfo(fSkPaint);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700310 fSkPaint.setStyle(SkPaint::kFill_Style);
cdalton855d83f2014-09-18 13:51:53 -0700311
cdalton20b373c2014-12-01 08:38:55 -0800312 if (fStroke.isHairlineStyle()) {
cdalton855d83f2014-09-18 13:51:53 -0700313 // Approximate hairline stroke.
314 SkScalar strokeWidth = SK_Scalar1 /
315 (SkVector::Make(fContextInitialMatrix.getScaleX(),
316 fContextInitialMatrix.getSkewY()).length());
cdalton20b373c2014-12-01 08:38:55 -0800317 fStroke.setStrokeStyle(strokeWidth, false /*strokeAndFill*/);
cdalton855d83f2014-09-18 13:51:53 -0700318
319 } else if (fSkPaint.isFakeBoldText() &&
320#ifdef SK_USE_FREETYPE_EMBOLDEN
321 kMaxPerformance_RenderMode == renderMode &&
322#endif
cdalton20b373c2014-12-01 08:38:55 -0800323 SkStrokeRec::kStroke_Style != fStroke.getStyle()) {
cdalton855d83f2014-09-18 13:51:53 -0700324
325 // Instead of baking fake bold into the glyph outlines, do it with the GPU stroke.
326 SkScalar fakeBoldScale = SkScalarInterpFunc(fSkPaint.getTextSize(),
327 kStdFakeBoldInterpKeys,
328 kStdFakeBoldInterpValues,
329 kStdFakeBoldInterpLength);
330 SkScalar extra = SkScalarMul(fSkPaint.getTextSize(), fakeBoldScale);
cdalton20b373c2014-12-01 08:38:55 -0800331 fStroke.setStrokeStyle(fStroke.needToApply() ? fStroke.getWidth() + extra : extra,
332 true /*strokeAndFill*/);
cdalton855d83f2014-09-18 13:51:53 -0700333
334 fSkPaint.setFakeBoldText(false);
335 }
336
337 bool canUseRawPaths;
kkinnunen50b58e62015-05-18 23:02:07 -0700338 if (!fStroke.isDashed() && (otherBackendsWillDrawAsPaths ||
339 kMaxPerformance_RenderMode == renderMode)) {
cdalton855d83f2014-09-18 13:51:53 -0700340 // We can draw the glyphs from canonically sized paths.
341 fTextRatio = fSkPaint.getTextSize() / SkPaint::kCanonicalTextSizeForPaths;
342 fTextInverseRatio = SkPaint::kCanonicalTextSizeForPaths / fSkPaint.getTextSize();
343
344 // Compensate for the glyphs being scaled by fTextRatio.
cdalton20b373c2014-12-01 08:38:55 -0800345 if (!fStroke.isFillStyle()) {
346 fStroke.setStrokeStyle(fStroke.getWidth() / fTextRatio,
347 SkStrokeRec::kStrokeAndFill_Style == fStroke.getStyle());
cdalton855d83f2014-09-18 13:51:53 -0700348 }
349
350 fSkPaint.setLinearText(true);
351 fSkPaint.setLCDRenderText(false);
352 fSkPaint.setAutohinted(false);
353 fSkPaint.setHinting(SkPaint::kNo_Hinting);
354 fSkPaint.setSubpixelText(true);
355 fSkPaint.setTextSize(SkIntToScalar(SkPaint::kCanonicalTextSizeForPaths));
356
357 canUseRawPaths = SK_Scalar1 == fSkPaint.getTextScaleX() &&
358 0 == fSkPaint.getTextSkewX() &&
359 !fSkPaint.isFakeBoldText() &&
360 !fSkPaint.isVerticalText();
361 } else {
362 fTextRatio = fTextInverseRatio = 1.0f;
363 canUseRawPaths = false;
364 }
365
366 SkMatrix textMatrix;
cdalton855d83f2014-09-18 13:51:53 -0700367 // Glyphs loaded by GPU path rendering have an inverted y-direction.
cdalton38e13ad2014-11-07 06:02:15 -0800368 textMatrix.setScale(fTextRatio, -fTextRatio);
joshualitt5531d512014-12-17 15:50:11 -0800369 fViewMatrix.preConcat(textMatrix);
joshualitt290c09b2014-12-19 13:45:20 -0800370 fLocalMatrix = textMatrix;
cdalton855d83f2014-09-18 13:51:53 -0700371
halcanary96fcdcc2015-08-27 07:41:13 -0700372 fGlyphCache = fSkPaint.detachCache(&fSurfaceProps, nullptr, true /*ignoreGamma*/);
cdalton855d83f2014-09-18 13:51:53 -0700373 fGlyphs = canUseRawPaths ?
halcanary96fcdcc2015-08-27 07:41:13 -0700374 get_gr_glyphs(fContext, fSkPaint.getTypeface(), nullptr, fStroke) :
cdalton855d83f2014-09-18 13:51:53 -0700375 get_gr_glyphs(fContext, fGlyphCache->getScalerContext()->getTypeface(),
cdalton20b373c2014-12-01 08:38:55 -0800376 &fGlyphCache->getDescriptor(), fStroke);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700377 }
cdalton855d83f2014-09-18 13:51:53 -0700378
kkinnunenc6cb56f2014-06-24 00:12:27 -0700379}
380
joshualitt5531d512014-12-17 15:50:11 -0800381bool GrStencilAndCoverTextContext::mapToFallbackContext(SkMatrix* inverse) {
cdalton20b373c2014-12-01 08:38:55 -0800382 // The current view matrix is flipped because GPU path rendering glyphs have an
383 // inverted y-direction. Unflip the view matrix for the fallback context. If using
384 // device-space glyphs, we'll also need to restore the original view matrix since
385 // we moved that transfomation into our local glyph cache for this scenario. Also
386 // track the inverse operation so the caller can unmap the paint and glyph positions.
387 if (fUsingDeviceSpaceGlyphs) {
joshualitt5531d512014-12-17 15:50:11 -0800388 fViewMatrix = fContextInitialMatrix;
cdalton20b373c2014-12-01 08:38:55 -0800389 if (!fContextInitialMatrix.invert(inverse)) {
390 return false;
391 }
392 inverse->preScale(1, -1);
393 } else {
394 inverse->setScale(1, -1);
395 const SkMatrix& unflip = *inverse; // unflip is equal to its own inverse.
joshualitt5531d512014-12-17 15:50:11 -0800396 fViewMatrix.preConcat(unflip);
cdalton20b373c2014-12-01 08:38:55 -0800397 }
398 return true;
399}
400
robertphillipsf6703fa2015-09-01 05:36:47 -0700401inline void GrStencilAndCoverTextContext::appendGlyph(GrDrawContext* dc,
402 const SkGlyph& glyph,
403 const SkPoint& pos) {
cdalton20b373c2014-12-01 08:38:55 -0800404 if (fQueuedGlyphCount >= fFallbackGlyphsIdx) {
405 SkASSERT(fQueuedGlyphCount == fFallbackGlyphsIdx);
robertphillipsf6703fa2015-09-01 05:36:47 -0700406 this->flush(dc);
cdaltonb2808cd2014-07-25 14:13:57 -0700407 }
408
cdalton20b373c2014-12-01 08:38:55 -0800409 // Stick the glyphs we can't draw at the end of the buffer, growing backwards.
410 int index = (SkMask::kARGB32_Format == glyph.fMaskFormat) ?
411 --fFallbackGlyphsIdx : fQueuedGlyphCount++;
cdaltonb85a0aa2014-07-21 15:32:44 -0700412
cdalton20b373c2014-12-01 08:38:55 -0800413 fGlyphIndices[index] = glyph.getGlyphID();
414 fGlyphPositions[index].set(fTextInverseRatio * pos.x(), -fTextInverseRatio * pos.y());
415}
416
417static const SkScalar* get_xy_scalar_array(const SkPoint* pointArray) {
418 GR_STATIC_ASSERT(2 * sizeof(SkScalar) == sizeof(SkPoint));
419 GR_STATIC_ASSERT(0 == offsetof(SkPoint, fX));
420
421 return &pointArray[0].fX;
cdaltonb85a0aa2014-07-21 15:32:44 -0700422}
423
robertphillipsf6703fa2015-09-01 05:36:47 -0700424void GrStencilAndCoverTextContext::flush(GrDrawContext* dc) {
cdalton20b373c2014-12-01 08:38:55 -0800425 if (fQueuedGlyphCount > 0) {
joshualitt8059eb92014-12-29 15:10:07 -0800426 SkAutoTUnref<GrPathProcessor> pp(GrPathProcessor::Create(fPaint.getColor(),
427 fViewMatrix,
428 fLocalMatrix));
robertphillipsea461502015-05-26 11:38:03 -0700429
joshualitt7b670db2015-07-09 13:25:02 -0700430 // We should only be flushing about once every run. However, if this impacts performance
431 // we could move the creation of the GrPipelineBuilder earlier.
432 GrPipelineBuilder pipelineBuilder(fPaint, fRenderTarget, fClip);
433 SkASSERT(fRenderTarget->isStencilBufferMultisampled() || !fPaint.isAntiAlias());
434 pipelineBuilder.setState(GrPipelineBuilder::kHWAntialias_Flag, fPaint.isAntiAlias());
435
436 GR_STATIC_CONST_SAME_STENCIL(kStencilPass,
437 kZero_StencilOp,
vbuzinovc5d58f02015-08-21 05:24:24 -0700438 kKeep_StencilOp,
joshualitt7b670db2015-07-09 13:25:02 -0700439 kNotEqual_StencilFunc,
440 0xffff,
441 0x0000,
442 0xffff);
443
444 *pipelineBuilder.stencil() = kStencilPass;
445
joshualitt7b670db2015-07-09 13:25:02 -0700446 SkASSERT(kGlyphBufferSize == fFallbackGlyphsIdx);
447
robertphillipsf6703fa2015-09-01 05:36:47 -0700448 dc->drawPaths(&pipelineBuilder, pp, fGlyphs,
449 fGlyphIndices, GrPathRange::kU16_PathIndexType,
450 get_xy_scalar_array(fGlyphPositions),
451 GrPathRendering::kTranslate_PathTransformType,
452 fQueuedGlyphCount, GrPathRendering::kWinding_FillType);
cdalton20b373c2014-12-01 08:38:55 -0800453
454 fQueuedGlyphCount = 0;
kkinnunenc6cb56f2014-06-24 00:12:27 -0700455 }
456
cdalton20b373c2014-12-01 08:38:55 -0800457 if (fFallbackGlyphsIdx < kGlyphBufferSize) {
458 int fallbackGlyphCount = kGlyphBufferSize - fFallbackGlyphsIdx;
cdaltonb85a0aa2014-07-21 15:32:44 -0700459
cdalton20b373c2014-12-01 08:38:55 -0800460 GrPaint paintFallback(fPaint);
461
462 SkPaint skPaintFallback(fSkPaint);
463 if (!fUsingDeviceSpaceGlyphs) {
464 fStroke.applyToPaint(&skPaintFallback);
465 }
466 skPaintFallback.setTextAlign(SkPaint::kLeft_Align); // Align has already been accounted for.
467 skPaintFallback.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
468
cdalton20b373c2014-12-01 08:38:55 -0800469 SkMatrix inverse;
joshualitt5531d512014-12-17 15:50:11 -0800470 if (this->mapToFallbackContext(&inverse)) {
cdalton20b373c2014-12-01 08:38:55 -0800471 inverse.mapPoints(&fGlyphPositions[fFallbackGlyphsIdx], fallbackGlyphCount);
472 }
473
robertphillipsf6703fa2015-09-01 05:36:47 -0700474 fFallbackTextContext->drawPosText(dc, fRenderTarget, fClip, paintFallback, skPaintFallback,
joshualitt25d9c152015-02-18 12:29:52 -0800475 fViewMatrix, (char*)&fGlyphIndices[fFallbackGlyphsIdx],
cdalton20b373c2014-12-01 08:38:55 -0800476 2 * fallbackGlyphCount,
477 get_xy_scalar_array(&fGlyphPositions[fFallbackGlyphsIdx]),
joshualitt6e8cd962015-03-20 10:30:14 -0700478 2, SkPoint::Make(0, 0), fRegionClipBounds);
cdalton20b373c2014-12-01 08:38:55 -0800479
480 fFallbackGlyphsIdx = kGlyphBufferSize;
481 }
kkinnunenc6cb56f2014-06-24 00:12:27 -0700482}
483
robertphillipsf6703fa2015-09-01 05:36:47 -0700484void GrStencilAndCoverTextContext::finish(GrDrawContext* dc) {
485 this->flush(dc);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700486
cdalton855d83f2014-09-18 13:51:53 -0700487 fGlyphs->unref();
halcanary96fcdcc2015-08-27 07:41:13 -0700488 fGlyphs = nullptr;
cdalton855d83f2014-09-18 13:51:53 -0700489
490 SkGlyphCache::AttachCache(fGlyphCache);
halcanary96fcdcc2015-08-27 07:41:13 -0700491 fGlyphCache = nullptr;
kkinnunenc6cb56f2014-06-24 00:12:27 -0700492
joshualitt5531d512014-12-17 15:50:11 -0800493 fViewMatrix = fContextInitialMatrix;
kkinnunenc6cb56f2014-06-24 00:12:27 -0700494}
495