blob: 9b4c4d77543cbf06bab967584d9f9296b7cebf95 [file] [log] [blame]
kkinnunenc6cb56f2014-06-24 00:12:27 -07001/*
2 * Copyright 2014 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "GrStencilAndCoverTextContext.h"
jvanverth8c27a182014-10-14 08:45:50 -07009#include "GrBitmapTextContext.h"
kkinnunenc6cb56f2014-06-24 00:12:27 -070010#include "GrDrawTarget.h"
kkinnunenc6cb56f2014-06-24 00:12:27 -070011#include "GrGpu.h"
12#include "GrPath.h"
cdaltonb85a0aa2014-07-21 15:32:44 -070013#include "GrPathRange.h"
kkinnunenc6cb56f2014-06-24 00:12:27 -070014#include "SkDraw.h"
15#include "SkDrawProcs.h"
16#include "SkGlyphCache.h"
17#include "SkGpuDevice.h"
18#include "SkPath.h"
19#include "SkTextMapStateProc.h"
cdalton855d83f2014-09-18 13:51:53 -070020#include "SkTextFormatParams.h"
kkinnunenc6cb56f2014-06-24 00:12:27 -070021
22GrStencilAndCoverTextContext::GrStencilAndCoverTextContext(
23 GrContext* context, const SkDeviceProperties& properties)
24 : GrTextContext(context, properties)
cdaltonb85a0aa2014-07-21 15:32:44 -070025 , fPendingGlyphCount(0) {
kkinnunenc6cb56f2014-06-24 00:12:27 -070026}
27
jvanverth8c27a182014-10-14 08:45:50 -070028GrStencilAndCoverTextContext* GrStencilAndCoverTextContext::Create(GrContext* context,
29 const SkDeviceProperties& props) {
30 GrStencilAndCoverTextContext* textContext = SkNEW_ARGS(GrStencilAndCoverTextContext,
31 (context, props));
32 textContext->fFallbackTextContext = GrBitmapTextContext::Create(context, props);
33
34 return textContext;
35}
36
kkinnunenc6cb56f2014-06-24 00:12:27 -070037GrStencilAndCoverTextContext::~GrStencilAndCoverTextContext() {
38}
39
jvanverth0fedb192014-10-08 09:07:27 -070040bool GrStencilAndCoverTextContext::canDraw(const SkPaint& paint) {
41 if (paint.getRasterizer()) {
42 return false;
43 }
44 if (paint.getMaskFilter()) {
45 return false;
46 }
47 if (paint.getPathEffect()) {
48 return false;
49 }
50
51 // No hairlines unless we can map the 1 px width to the object space.
52 if (paint.getStyle() == SkPaint::kStroke_Style
53 && paint.getStrokeWidth() == 0
54 && fContext->getMatrix().hasPerspective()) {
55 return false;
56 }
57
58 // No color bitmap fonts.
59 SkScalerContext::Rec rec;
60 SkScalerContext::MakeRec(paint, &fDeviceProperties, NULL, &rec);
61 return rec.getFormat() != SkMask::kARGB32_Format;
62}
63
jvanverth8c27a182014-10-14 08:45:50 -070064void GrStencilAndCoverTextContext::onDrawPosText(const GrPaint& paint,
kkinnunenc6cb56f2014-06-24 00:12:27 -070065 const SkPaint& skPaint,
66 const char text[],
67 size_t byteLength,
68 const SkScalar pos[],
fmalita05c4a432014-09-29 06:29:53 -070069 int scalarsPerPosition,
70 const SkPoint& offset) {
kkinnunenc6cb56f2014-06-24 00:12:27 -070071 SkASSERT(byteLength == 0 || text != NULL);
72 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
73
74 // nothing to draw
75 if (text == NULL || byteLength == 0/* || fRC->isEmpty()*/) {
76 return;
77 }
78
79 // This is the fast path. Here we do not bake in the device-transform to
80 // the glyph outline or the advances. This is because we do not need to
81 // position the glyphs at all, since the caller has done the positioning.
82 // The positioning is based on SkPaint::measureText of individual
83 // glyphs. That already uses glyph cache without device transforms. Device
84 // transform is not part of SkPaint::measureText API, and thus we use the
85 // same glyphs as what were measured.
kkinnunenc6cb56f2014-06-24 00:12:27 -070086
fmalita05c4a432014-09-29 06:29:53 -070087 this->init(paint, skPaint, byteLength, kMaxPerformance_RenderMode, offset);
kkinnunenc6cb56f2014-06-24 00:12:27 -070088
89 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
90
kkinnunenc6cb56f2014-06-24 00:12:27 -070091 const char* stop = text + byteLength;
kkinnunenc6cb56f2014-06-24 00:12:27 -070092
93 if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) {
cdaltonb2808cd2014-07-25 14:13:57 -070094 if (1 == scalarsPerPosition) {
kkinnunenccdaa042014-08-20 01:36:23 -070095 fTransformType = GrPathRendering::kTranslateX_PathTransformType;
cdaltonb2808cd2014-07-25 14:13:57 -070096 while (text < stop) {
97 const SkGlyph& glyph = glyphCacheProc(fGlyphCache, &text, 0, 0);
98 if (glyph.fWidth) {
99 this->appendGlyph(glyph.getGlyphID(), *pos);
100 }
101 pos++;
kkinnunenc6cb56f2014-06-24 00:12:27 -0700102 }
cdaltonb2808cd2014-07-25 14:13:57 -0700103 } else {
104 SkASSERT(2 == scalarsPerPosition);
kkinnunenccdaa042014-08-20 01:36:23 -0700105 fTransformType = GrPathRendering::kTranslate_PathTransformType;
cdaltonb2808cd2014-07-25 14:13:57 -0700106 while (text < stop) {
107 const SkGlyph& glyph = glyphCacheProc(fGlyphCache, &text, 0, 0);
108 if (glyph.fWidth) {
109 this->appendGlyph(glyph.getGlyphID(), pos[0], pos[1]);
110 }
111 pos += 2;
112 }
kkinnunenc6cb56f2014-06-24 00:12:27 -0700113 }
114 } else {
kkinnunenccdaa042014-08-20 01:36:23 -0700115 fTransformType = GrPathRendering::kTranslate_PathTransformType;
fmalita05c4a432014-09-29 06:29:53 -0700116 SkTextMapStateProc tmsProc(SkMatrix::I(), SkPoint::Make(0, 0), scalarsPerPosition);
cdaltonb2808cd2014-07-25 14:13:57 -0700117 SkTextAlignProcScalar alignProc(fSkPaint.getTextAlign());
kkinnunenc6cb56f2014-06-24 00:12:27 -0700118 while (text < stop) {
cdaltonb85a0aa2014-07-21 15:32:44 -0700119 const SkGlyph& glyph = glyphCacheProc(fGlyphCache, &text, 0, 0);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700120 if (glyph.fWidth) {
121 SkPoint tmsLoc;
122 tmsProc(pos, &tmsLoc);
123 SkPoint loc;
124 alignProc(tmsLoc, glyph, &loc);
125
cdaltonb85a0aa2014-07-21 15:32:44 -0700126 this->appendGlyph(glyph.getGlyphID(), loc.x(), loc.y());
kkinnunenc6cb56f2014-06-24 00:12:27 -0700127 }
128 pos += scalarsPerPosition;
129 }
130 }
131
132 this->finish();
133}
134
cdalton855d83f2014-09-18 13:51:53 -0700135static GrPathRange* get_gr_glyphs(GrContext* ctx,
136 const SkTypeface* typeface,
137 const SkDescriptor* desc,
138 const SkStrokeRec& stroke) {
139 static const GrCacheID::Domain gGlyphsDomain = GrCacheID::GenerateDomain();
140
141 GrCacheID::Key key;
142 uint64_t* keyData = key.fData64;
143 keyData[0] = desc ? desc->getChecksum() : 0;
144 keyData[0] = (keyData[0] << 32) | (typeface ? typeface->uniqueID() : 0);
145 keyData[1] = GrPath::ComputeStrokeKey(stroke);
146 GrResourceKey resourceKey = GrResourceKey(GrCacheID(gGlyphsDomain, key),
147 GrPathRange::resourceType(), 0);
148
149 SkAutoTUnref<GrPathRange> glyphs(
150 static_cast<GrPathRange*>(ctx->findAndRefCachedResource(resourceKey)));
151 if (NULL == glyphs || (NULL != desc && !glyphs->isEqualTo(*desc))) {
152 glyphs.reset(ctx->getGpu()->pathRendering()->createGlyphs(typeface, desc, stroke));
153 ctx->addResourceToCache(resourceKey, glyphs);
154 }
155
156 return glyphs.detach();
157}
158
kkinnunenc6cb56f2014-06-24 00:12:27 -0700159void GrStencilAndCoverTextContext::init(const GrPaint& paint,
160 const SkPaint& skPaint,
cdaltonb2808cd2014-07-25 14:13:57 -0700161 size_t textByteLength,
cdalton855d83f2014-09-18 13:51:53 -0700162 RenderMode renderMode,
fmalita05c4a432014-09-29 06:29:53 -0700163 const SkPoint& textTranslate) {
kkinnunenc6cb56f2014-06-24 00:12:27 -0700164 GrTextContext::init(paint, skPaint);
165
cdaltonb2808cd2014-07-25 14:13:57 -0700166 fContextInitialMatrix = fContext->getMatrix();
167
cdalton855d83f2014-09-18 13:51:53 -0700168 const bool otherBackendsWillDrawAsPaths =
cdaltonb2808cd2014-07-25 14:13:57 -0700169 SkDraw::ShouldDrawTextAsPaths(skPaint, fContextInitialMatrix);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700170
cdalton855d83f2014-09-18 13:51:53 -0700171 fNeedsDeviceSpaceGlyphs = !otherBackendsWillDrawAsPaths &&
172 kMaxAccuracy_RenderMode == renderMode &&
173 SkToBool(fContextInitialMatrix.getType() &
174 (SkMatrix::kScale_Mask | SkMatrix::kAffine_Mask));
kkinnunenc6cb56f2014-06-24 00:12:27 -0700175
176 if (fNeedsDeviceSpaceGlyphs) {
cdalton855d83f2014-09-18 13:51:53 -0700177 // SkDraw::ShouldDrawTextAsPaths takes care of perspective transforms.
178 SkASSERT(!fContextInitialMatrix.hasPerspective());
fmalita05c4a432014-09-29 06:29:53 -0700179 SkASSERT(textTranslate.isZero()); // TODO: Handle textTranslate in device-space usecase.
cdaltonb2808cd2014-07-25 14:13:57 -0700180
cdalton855d83f2014-09-18 13:51:53 -0700181 fTextRatio = fTextInverseRatio = 1.0f;
182
183 // Glyphs loaded by GPU path rendering have an inverted y-direction.
184 SkMatrix m;
185 m.setScale(1, -1);
186 fContext->setMatrix(m);
187
188 // Post-flip the initial matrix so we're left with just the flip after
189 // the paint preConcats the inverse.
190 m = fContextInitialMatrix;
191 m.postScale(1, -1);
192 fPaint.localCoordChangeInverse(m);
193
194 // The whole shape (including stroke) will be baked into the glyph outlines. Make
195 // NVPR just fill the baked shapes.
196 fGlyphCache = fSkPaint.detachCache(&fDeviceProperties, &fContextInitialMatrix, false);
197 fGlyphs = get_gr_glyphs(fContext, fGlyphCache->getScalerContext()->getTypeface(),
198 &fGlyphCache->getDescriptor(),
199 SkStrokeRec(SkStrokeRec::kFill_InitStyle));
kkinnunenc6cb56f2014-06-24 00:12:27 -0700200 } else {
cdalton855d83f2014-09-18 13:51:53 -0700201 // Don't bake strokes into the glyph outlines. We will stroke the glyphs
202 // using the GPU instead. This is the fast path.
203 SkStrokeRec gpuStroke = SkStrokeRec(fSkPaint);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700204 fSkPaint.setStyle(SkPaint::kFill_Style);
cdalton855d83f2014-09-18 13:51:53 -0700205
206 if (gpuStroke.isHairlineStyle()) {
207 // Approximate hairline stroke.
208 SkScalar strokeWidth = SK_Scalar1 /
209 (SkVector::Make(fContextInitialMatrix.getScaleX(),
210 fContextInitialMatrix.getSkewY()).length());
211 gpuStroke.setStrokeStyle(strokeWidth, false /*strokeAndFill*/);
212
213 } else if (fSkPaint.isFakeBoldText() &&
214#ifdef SK_USE_FREETYPE_EMBOLDEN
215 kMaxPerformance_RenderMode == renderMode &&
216#endif
217 SkStrokeRec::kStroke_Style != gpuStroke.getStyle()) {
218
219 // Instead of baking fake bold into the glyph outlines, do it with the GPU stroke.
220 SkScalar fakeBoldScale = SkScalarInterpFunc(fSkPaint.getTextSize(),
221 kStdFakeBoldInterpKeys,
222 kStdFakeBoldInterpValues,
223 kStdFakeBoldInterpLength);
224 SkScalar extra = SkScalarMul(fSkPaint.getTextSize(), fakeBoldScale);
225 gpuStroke.setStrokeStyle(gpuStroke.needToApply() ? gpuStroke.getWidth() + extra : extra,
226 true /*strokeAndFill*/);
227
228 fSkPaint.setFakeBoldText(false);
229 }
230
231 bool canUseRawPaths;
232
233 if (otherBackendsWillDrawAsPaths || kMaxPerformance_RenderMode == renderMode) {
234 // We can draw the glyphs from canonically sized paths.
235 fTextRatio = fSkPaint.getTextSize() / SkPaint::kCanonicalTextSizeForPaths;
236 fTextInverseRatio = SkPaint::kCanonicalTextSizeForPaths / fSkPaint.getTextSize();
237
238 // Compensate for the glyphs being scaled by fTextRatio.
239 if (!gpuStroke.isFillStyle()) {
240 gpuStroke.setStrokeStyle(gpuStroke.getWidth() / fTextRatio,
241 SkStrokeRec::kStrokeAndFill_Style == gpuStroke.getStyle());
242 }
243
244 fSkPaint.setLinearText(true);
245 fSkPaint.setLCDRenderText(false);
246 fSkPaint.setAutohinted(false);
247 fSkPaint.setHinting(SkPaint::kNo_Hinting);
248 fSkPaint.setSubpixelText(true);
249 fSkPaint.setTextSize(SkIntToScalar(SkPaint::kCanonicalTextSizeForPaths));
250
251 canUseRawPaths = SK_Scalar1 == fSkPaint.getTextScaleX() &&
252 0 == fSkPaint.getTextSkewX() &&
253 !fSkPaint.isFakeBoldText() &&
254 !fSkPaint.isVerticalText();
255 } else {
256 fTextRatio = fTextInverseRatio = 1.0f;
257 canUseRawPaths = false;
258 }
259
260 SkMatrix textMatrix;
fmalita05c4a432014-09-29 06:29:53 -0700261 textMatrix.setTranslate(textTranslate.x(), textTranslate.y());
cdalton855d83f2014-09-18 13:51:53 -0700262 // Glyphs loaded by GPU path rendering have an inverted y-direction.
263 textMatrix.preScale(fTextRatio, -fTextRatio);
264 fPaint.localCoordChange(textMatrix);
265 fContext->concatMatrix(textMatrix);
266
267 fGlyphCache = fSkPaint.detachCache(&fDeviceProperties, NULL, false);
268 fGlyphs = canUseRawPaths ?
269 get_gr_glyphs(fContext, fSkPaint.getTypeface(), NULL, gpuStroke) :
270 get_gr_glyphs(fContext, fGlyphCache->getScalerContext()->getTypeface(),
271 &fGlyphCache->getDescriptor(), gpuStroke);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700272 }
cdalton855d83f2014-09-18 13:51:53 -0700273
kkinnunenc6cb56f2014-06-24 00:12:27 -0700274 fStateRestore.set(fDrawTarget->drawState());
275
276 fDrawTarget->drawState()->setFromPaint(fPaint, fContext->getMatrix(),
277 fContext->getRenderTarget());
278
279 GR_STATIC_CONST_SAME_STENCIL(kStencilPass,
280 kZero_StencilOp,
281 kZero_StencilOp,
282 kNotEqual_StencilFunc,
283 0xffff,
284 0x0000,
285 0xffff);
286
287 *fDrawTarget->drawState()->stencil() = kStencilPass;
288
cdaltonb85a0aa2014-07-21 15:32:44 -0700289 SkASSERT(0 == fPendingGlyphCount);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700290}
291
cdaltonb2808cd2014-07-25 14:13:57 -0700292inline void GrStencilAndCoverTextContext::appendGlyph(uint16_t glyphID, float x) {
kkinnunenccdaa042014-08-20 01:36:23 -0700293 SkASSERT(GrPathRendering::kTranslateX_PathTransformType == fTransformType);
cdaltonb2808cd2014-07-25 14:13:57 -0700294
cdaltonb85a0aa2014-07-21 15:32:44 -0700295 if (fPendingGlyphCount >= kGlyphBufferSize) {
296 this->flush();
297 }
298
cdaltonb85a0aa2014-07-21 15:32:44 -0700299 fIndexBuffer[fPendingGlyphCount] = glyphID;
cdaltonb2808cd2014-07-25 14:13:57 -0700300 fTransformBuffer[fPendingGlyphCount] = fTextInverseRatio * x;
301
302 ++fPendingGlyphCount;
303}
304
305inline void GrStencilAndCoverTextContext::appendGlyph(uint16_t glyphID, float x, float y) {
kkinnunenccdaa042014-08-20 01:36:23 -0700306 SkASSERT(GrPathRendering::kTranslate_PathTransformType == fTransformType);
cdaltonb2808cd2014-07-25 14:13:57 -0700307
308 if (fPendingGlyphCount >= kGlyphBufferSize) {
309 this->flush();
310 }
311
cdaltonb2808cd2014-07-25 14:13:57 -0700312 fIndexBuffer[fPendingGlyphCount] = glyphID;
313 fTransformBuffer[2 * fPendingGlyphCount] = fTextInverseRatio * x;
cdalton855d83f2014-09-18 13:51:53 -0700314 fTransformBuffer[2 * fPendingGlyphCount + 1] = -fTextInverseRatio * y;
cdaltonb85a0aa2014-07-21 15:32:44 -0700315
316 ++fPendingGlyphCount;
317}
318
319void GrStencilAndCoverTextContext::flush() {
320 if (0 == fPendingGlyphCount) {
kkinnunenc6cb56f2014-06-24 00:12:27 -0700321 return;
322 }
323
cdalton855d83f2014-09-18 13:51:53 -0700324 fDrawTarget->drawPaths(fGlyphs, fIndexBuffer, fPendingGlyphCount,
cdaltonb2808cd2014-07-25 14:13:57 -0700325 fTransformBuffer, fTransformType, SkPath::kWinding_FillType);
cdaltonb85a0aa2014-07-21 15:32:44 -0700326
327 fPendingGlyphCount = 0;
kkinnunenc6cb56f2014-06-24 00:12:27 -0700328}
329
330void GrStencilAndCoverTextContext::finish() {
cdaltonb85a0aa2014-07-21 15:32:44 -0700331 this->flush();
kkinnunenc6cb56f2014-06-24 00:12:27 -0700332
cdalton855d83f2014-09-18 13:51:53 -0700333 fGlyphs->unref();
cdaltonb85a0aa2014-07-21 15:32:44 -0700334 fGlyphs = NULL;
cdalton855d83f2014-09-18 13:51:53 -0700335
336 SkGlyphCache::AttachCache(fGlyphCache);
cdaltonb85a0aa2014-07-21 15:32:44 -0700337 fGlyphCache = NULL;
kkinnunenc6cb56f2014-06-24 00:12:27 -0700338
339 fDrawTarget->drawState()->stencil()->setDisabled();
340 fStateRestore.set(NULL);
cdaltonb2808cd2014-07-25 14:13:57 -0700341 fContext->setMatrix(fContextInitialMatrix);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700342 GrTextContext::finish();
343}
344