2 * Copyright 2014 Google Inc.
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
8 #include "GrStencilAndCoverTextContext.h"
9 #include "GrDrawTarget.h"
12 #include "GrPathRange.h"
13 #include "SkAutoKern.h"
15 #include "SkDrawProcs.h"
16 #include "SkGlyphCache.h"
17 #include "SkGpuDevice.h"
19 #include "SkTextMapStateProc.h"
20 #include "SkTextFormatParams.h"
22 GrStencilAndCoverTextContext::GrStencilAndCoverTextContext(
23 GrContext* context, const SkDeviceProperties& properties)
24 : GrTextContext(context, properties)
25 , fPendingGlyphCount(0) {
28 GrStencilAndCoverTextContext::~GrStencilAndCoverTextContext() {
31 void GrStencilAndCoverTextContext::drawText(const GrPaint& paint,
32 const SkPaint& skPaint,
35 SkScalar x, SkScalar y) {
36 SkASSERT(byteLength == 0 || text != NULL);
38 if (text == NULL || byteLength == 0 /*|| fRC->isEmpty()*/) {
42 // This is the slow path, mainly used by Skia unit tests. The other
43 // backends (8888, gpu, ...) use device-space dependent glyph caches. In
44 // order to match the glyph positions that the other code paths produce, we
45 // must also use device-space dependent glyph cache. This has the
46 // side-effect that the glyph shape outline will be in device-space,
47 // too. This in turn has the side-effect that NVPR can not stroke the paths,
48 // as the stroke in NVPR is defined in object-space.
49 // NOTE: here we have following coincidence that works at the moment:
50 // - When using the device-space glyphs, the transforms we pass to NVPR
51 // instanced drawing are the global transforms, and the view transform is
52 // identity. NVPR can not use non-affine transforms in the instanced
53 // drawing. This is taken care of by SkDraw::ShouldDrawTextAsPaths since it
54 // will turn off the use of device-space glyphs when perspective transforms
57 this->init(paint, skPaint, byteLength, kMaxAccuracy_RenderMode);
59 // Transform our starting point.
60 if (fNeedsDeviceSpaceGlyphs) {
62 fContextInitialMatrix.mapXY(x, y, &loc);
67 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
69 fTransformType = GrPathRendering::kTranslate_PathTransformType;
71 const char* stop = text + byteLength;
73 // Measure first if needed.
74 if (fSkPaint.getTextAlign() != SkPaint::kLeft_Align) {
78 const char* textPtr = text;
79 while (textPtr < stop) {
80 // We don't need x, y here, since all subpixel variants will have the
82 const SkGlyph& glyph = glyphCacheProc(fGlyphCache, &textPtr, 0, 0);
84 stopX += glyph.fAdvanceX;
85 stopY += glyph.fAdvanceY;
87 SkASSERT(textPtr == stop);
89 SkScalar alignX = SkFixedToScalar(stopX) * fTextRatio;
90 SkScalar alignY = SkFixedToScalar(stopY) * fTextRatio;
92 if (fSkPaint.getTextAlign() == SkPaint::kCenter_Align) {
93 alignX = SkScalarHalf(alignX);
94 alignY = SkScalarHalf(alignY);
103 SkFixed fixedSizeRatio = SkScalarToFixed(fTextRatio);
105 SkFixed fx = SkScalarToFixed(x);
106 SkFixed fy = SkScalarToFixed(y);
107 while (text < stop) {
108 const SkGlyph& glyph = glyphCacheProc(fGlyphCache, &text, 0, 0);
109 fx += SkFixedMul_portable(autokern.adjust(glyph), fixedSizeRatio);
111 this->appendGlyph(glyph.getGlyphID(), SkFixedToScalar(fx), SkFixedToScalar(fy));
114 fx += SkFixedMul_portable(glyph.fAdvanceX, fixedSizeRatio);
115 fy += SkFixedMul_portable(glyph.fAdvanceY, fixedSizeRatio);
121 void GrStencilAndCoverTextContext::drawPosText(const GrPaint& paint,
122 const SkPaint& skPaint,
125 const SkScalar pos[],
127 int scalarsPerPosition) {
128 SkASSERT(byteLength == 0 || text != NULL);
129 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
132 if (text == NULL || byteLength == 0/* || fRC->isEmpty()*/) {
136 // This is the fast path. Here we do not bake in the device-transform to
137 // the glyph outline or the advances. This is because we do not need to
138 // position the glyphs at all, since the caller has done the positioning.
139 // The positioning is based on SkPaint::measureText of individual
140 // glyphs. That already uses glyph cache without device transforms. Device
141 // transform is not part of SkPaint::measureText API, and thus we use the
142 // same glyphs as what were measured.
144 const float textTranslateY = (1 == scalarsPerPosition ? constY : 0);
145 this->init(paint, skPaint, byteLength, kMaxPerformance_RenderMode, textTranslateY);
147 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
149 const char* stop = text + byteLength;
151 if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) {
152 if (1 == scalarsPerPosition) {
153 fTransformType = GrPathRendering::kTranslateX_PathTransformType;
154 while (text < stop) {
155 const SkGlyph& glyph = glyphCacheProc(fGlyphCache, &text, 0, 0);
157 this->appendGlyph(glyph.getGlyphID(), *pos);
162 SkASSERT(2 == scalarsPerPosition);
163 fTransformType = GrPathRendering::kTranslate_PathTransformType;
164 while (text < stop) {
165 const SkGlyph& glyph = glyphCacheProc(fGlyphCache, &text, 0, 0);
167 this->appendGlyph(glyph.getGlyphID(), pos[0], pos[1]);
173 fTransformType = GrPathRendering::kTranslate_PathTransformType;
174 SkTextMapStateProc tmsProc(SkMatrix::I(), 0, scalarsPerPosition);
175 SkTextAlignProcScalar alignProc(fSkPaint.getTextAlign());
176 while (text < stop) {
177 const SkGlyph& glyph = glyphCacheProc(fGlyphCache, &text, 0, 0);
180 tmsProc(pos, &tmsLoc);
182 alignProc(tmsLoc, glyph, &loc);
184 this->appendGlyph(glyph.getGlyphID(), loc.x(), loc.y());
186 pos += scalarsPerPosition;
193 bool GrStencilAndCoverTextContext::canDraw(const SkPaint& paint) {
194 if (paint.getRasterizer()) {
197 if (paint.getMaskFilter()) {
200 if (paint.getPathEffect()) {
204 // No hairlines unless we can map the 1 px width to the object space.
205 if (paint.getStyle() == SkPaint::kStroke_Style
206 && paint.getStrokeWidth() == 0
207 && fContext->getMatrix().hasPerspective()) {
211 // No color bitmap fonts.
212 SkScalerContext::Rec rec;
213 SkScalerContext::MakeRec(paint, &fDeviceProperties, NULL, &rec);
214 return rec.getFormat() != SkMask::kARGB32_Format;
217 static GrPathRange* get_gr_glyphs(GrContext* ctx,
218 const SkTypeface* typeface,
219 const SkDescriptor* desc,
220 const SkStrokeRec& stroke) {
221 static const GrCacheID::Domain gGlyphsDomain = GrCacheID::GenerateDomain();
224 uint64_t* keyData = key.fData64;
225 keyData[0] = desc ? desc->getChecksum() : 0;
226 keyData[0] = (keyData[0] << 32) | (typeface ? typeface->uniqueID() : 0);
227 keyData[1] = GrPath::ComputeStrokeKey(stroke);
228 GrResourceKey resourceKey = GrResourceKey(GrCacheID(gGlyphsDomain, key),
229 GrPathRange::resourceType(), 0);
231 SkAutoTUnref<GrPathRange> glyphs(
232 static_cast<GrPathRange*>(ctx->findAndRefCachedResource(resourceKey)));
233 if (NULL == glyphs || (NULL != desc && !glyphs->isEqualTo(*desc))) {
234 glyphs.reset(ctx->getGpu()->pathRendering()->createGlyphs(typeface, desc, stroke));
235 ctx->addResourceToCache(resourceKey, glyphs);
238 return glyphs.detach();
241 void GrStencilAndCoverTextContext::init(const GrPaint& paint,
242 const SkPaint& skPaint,
243 size_t textByteLength,
244 RenderMode renderMode,
245 SkScalar textTranslateY) {
246 GrTextContext::init(paint, skPaint);
248 fContextInitialMatrix = fContext->getMatrix();
250 const bool otherBackendsWillDrawAsPaths =
251 SkDraw::ShouldDrawTextAsPaths(skPaint, fContextInitialMatrix);
253 fNeedsDeviceSpaceGlyphs = !otherBackendsWillDrawAsPaths &&
254 kMaxAccuracy_RenderMode == renderMode &&
255 SkToBool(fContextInitialMatrix.getType() &
256 (SkMatrix::kScale_Mask | SkMatrix::kAffine_Mask));
258 if (fNeedsDeviceSpaceGlyphs) {
259 // SkDraw::ShouldDrawTextAsPaths takes care of perspective transforms.
260 SkASSERT(!fContextInitialMatrix.hasPerspective());
261 SkASSERT(0 == textTranslateY); // TODO: Handle textTranslateY in device-space usecase.
263 fTextRatio = fTextInverseRatio = 1.0f;
265 // Glyphs loaded by GPU path rendering have an inverted y-direction.
268 fContext->setMatrix(m);
270 // Post-flip the initial matrix so we're left with just the flip after
271 // the paint preConcats the inverse.
272 m = fContextInitialMatrix;
274 fPaint.localCoordChangeInverse(m);
276 // The whole shape (including stroke) will be baked into the glyph outlines. Make
277 // NVPR just fill the baked shapes.
278 fGlyphCache = fSkPaint.detachCache(&fDeviceProperties, &fContextInitialMatrix, false);
279 fGlyphs = get_gr_glyphs(fContext, fGlyphCache->getScalerContext()->getTypeface(),
280 &fGlyphCache->getDescriptor(),
281 SkStrokeRec(SkStrokeRec::kFill_InitStyle));
283 // Don't bake strokes into the glyph outlines. We will stroke the glyphs
284 // using the GPU instead. This is the fast path.
285 SkStrokeRec gpuStroke = SkStrokeRec(fSkPaint);
286 fSkPaint.setStyle(SkPaint::kFill_Style);
288 if (gpuStroke.isHairlineStyle()) {
289 // Approximate hairline stroke.
290 SkScalar strokeWidth = SK_Scalar1 /
291 (SkVector::Make(fContextInitialMatrix.getScaleX(),
292 fContextInitialMatrix.getSkewY()).length());
293 gpuStroke.setStrokeStyle(strokeWidth, false /*strokeAndFill*/);
295 } else if (fSkPaint.isFakeBoldText() &&
296 #ifdef SK_USE_FREETYPE_EMBOLDEN
297 kMaxPerformance_RenderMode == renderMode &&
299 SkStrokeRec::kStroke_Style != gpuStroke.getStyle()) {
301 // Instead of baking fake bold into the glyph outlines, do it with the GPU stroke.
302 SkScalar fakeBoldScale = SkScalarInterpFunc(fSkPaint.getTextSize(),
303 kStdFakeBoldInterpKeys,
304 kStdFakeBoldInterpValues,
305 kStdFakeBoldInterpLength);
306 SkScalar extra = SkScalarMul(fSkPaint.getTextSize(), fakeBoldScale);
307 gpuStroke.setStrokeStyle(gpuStroke.needToApply() ? gpuStroke.getWidth() + extra : extra,
308 true /*strokeAndFill*/);
310 fSkPaint.setFakeBoldText(false);
315 if (otherBackendsWillDrawAsPaths || kMaxPerformance_RenderMode == renderMode) {
316 // We can draw the glyphs from canonically sized paths.
317 fTextRatio = fSkPaint.getTextSize() / SkPaint::kCanonicalTextSizeForPaths;
318 fTextInverseRatio = SkPaint::kCanonicalTextSizeForPaths / fSkPaint.getTextSize();
320 // Compensate for the glyphs being scaled by fTextRatio.
321 if (!gpuStroke.isFillStyle()) {
322 gpuStroke.setStrokeStyle(gpuStroke.getWidth() / fTextRatio,
323 SkStrokeRec::kStrokeAndFill_Style == gpuStroke.getStyle());
326 fSkPaint.setLinearText(true);
327 fSkPaint.setLCDRenderText(false);
328 fSkPaint.setAutohinted(false);
329 fSkPaint.setHinting(SkPaint::kNo_Hinting);
330 fSkPaint.setSubpixelText(true);
331 fSkPaint.setTextSize(SkIntToScalar(SkPaint::kCanonicalTextSizeForPaths));
333 canUseRawPaths = SK_Scalar1 == fSkPaint.getTextScaleX() &&
334 0 == fSkPaint.getTextSkewX() &&
335 !fSkPaint.isFakeBoldText() &&
336 !fSkPaint.isVerticalText();
338 fTextRatio = fTextInverseRatio = 1.0f;
339 canUseRawPaths = false;
343 textMatrix.setTranslate(0, textTranslateY);
344 // Glyphs loaded by GPU path rendering have an inverted y-direction.
345 textMatrix.preScale(fTextRatio, -fTextRatio);
346 fPaint.localCoordChange(textMatrix);
347 fContext->concatMatrix(textMatrix);
349 fGlyphCache = fSkPaint.detachCache(&fDeviceProperties, NULL, false);
350 fGlyphs = canUseRawPaths ?
351 get_gr_glyphs(fContext, fSkPaint.getTypeface(), NULL, gpuStroke) :
352 get_gr_glyphs(fContext, fGlyphCache->getScalerContext()->getTypeface(),
353 &fGlyphCache->getDescriptor(), gpuStroke);
356 fStateRestore.set(fDrawTarget->drawState());
358 fDrawTarget->drawState()->setFromPaint(fPaint, fContext->getMatrix(),
359 fContext->getRenderTarget());
361 GR_STATIC_CONST_SAME_STENCIL(kStencilPass,
364 kNotEqual_StencilFunc,
369 *fDrawTarget->drawState()->stencil() = kStencilPass;
371 SkASSERT(0 == fPendingGlyphCount);
374 inline void GrStencilAndCoverTextContext::appendGlyph(uint16_t glyphID, float x) {
375 SkASSERT(GrPathRendering::kTranslateX_PathTransformType == fTransformType);
377 if (fPendingGlyphCount >= kGlyphBufferSize) {
381 fIndexBuffer[fPendingGlyphCount] = glyphID;
382 fTransformBuffer[fPendingGlyphCount] = fTextInverseRatio * x;
384 ++fPendingGlyphCount;
387 inline void GrStencilAndCoverTextContext::appendGlyph(uint16_t glyphID, float x, float y) {
388 SkASSERT(GrPathRendering::kTranslate_PathTransformType == fTransformType);
390 if (fPendingGlyphCount >= kGlyphBufferSize) {
394 fIndexBuffer[fPendingGlyphCount] = glyphID;
395 fTransformBuffer[2 * fPendingGlyphCount] = fTextInverseRatio * x;
396 fTransformBuffer[2 * fPendingGlyphCount + 1] = -fTextInverseRatio * y;
398 ++fPendingGlyphCount;
401 void GrStencilAndCoverTextContext::flush() {
402 if (0 == fPendingGlyphCount) {
406 fDrawTarget->drawPaths(fGlyphs, fIndexBuffer, fPendingGlyphCount,
407 fTransformBuffer, fTransformType, SkPath::kWinding_FillType);
409 fPendingGlyphCount = 0;
412 void GrStencilAndCoverTextContext::finish() {
418 SkGlyphCache::AttachCache(fGlyphCache);
421 fDrawTarget->drawState()->stencil()->setDisabled();
422 fStateRestore.set(NULL);
423 fContext->setMatrix(fContextInitialMatrix);
424 GrTextContext::finish();