1 /*
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"
9 #include "GrAtlasTextContext.h"
10 #include "GrDrawTarget.h"
11 #include "GrGpu.h"
12 #include "GrPath.h"
13 #include "GrPathRange.h"
14 #include "GrResourceProvider.h"
15 #include "SkAutoKern.h"
16 #include "SkDraw.h"
17 #include "SkDrawProcs.h"
18 #include "SkGlyphCache.h"
19 #include "SkGpuDevice.h"
20 #include "SkPath.h"
21 #include "SkTextMapStateProc.h"
22 #include "SkTextFormatParams.h"
23 
GrStencilAndCoverTextContext(GrContext * context,SkGpuDevice * gpuDevice,const SkDeviceProperties & properties)24 GrStencilAndCoverTextContext::GrStencilAndCoverTextContext(GrContext* context,
25                                                            SkGpuDevice* gpuDevice,
26                                                            const SkDeviceProperties& properties)
27     : GrTextContext(context, gpuDevice, properties)
28     , fStroke(SkStrokeRec::kFill_InitStyle)
29     , fQueuedGlyphCount(0)
30     , fFallbackGlyphsIdx(kGlyphBufferSize) {
31 }
32 
33 GrStencilAndCoverTextContext*
Create(GrContext * context,SkGpuDevice * gpuDevice,const SkDeviceProperties & props)34 GrStencilAndCoverTextContext::Create(GrContext* context, SkGpuDevice* gpuDevice,
35                                      const SkDeviceProperties& props) {
36     GrStencilAndCoverTextContext* textContext = SkNEW_ARGS(GrStencilAndCoverTextContext,
37                                                            (context, gpuDevice, props));
38     textContext->fFallbackTextContext = GrAtlasTextContext::Create(context, gpuDevice, props,
39                                                                    false);
40 
41     return textContext;
42 }
43 
~GrStencilAndCoverTextContext()44 GrStencilAndCoverTextContext::~GrStencilAndCoverTextContext() {
45 }
46 
canDraw(const GrRenderTarget * rt,const GrClip & clip,const GrPaint & paint,const SkPaint & skPaint,const SkMatrix & viewMatrix)47 bool 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()) {
53         return false;
54     }
55     if (skPaint.getMaskFilter()) {
56         return false;
57     }
58     if (skPaint.getPathEffect()) {
59         return false;
60     }
61 
62     // No hairlines unless we can map the 1 px width to the object space.
63     if (skPaint.getStyle() == SkPaint::kStroke_Style
64         && skPaint.getStrokeWidth() == 0
65         && viewMatrix.hasPerspective()) {
66         return false;
67     }
68 
69     // No color bitmap fonts.
70     SkScalerContext::Rec    rec;
71     SkScalerContext::MakeRec(skPaint, &fDeviceProperties, NULL, &rec);
72     return rec.getFormat() != SkMask::kARGB32_Format;
73 }
74 
onDrawText(GrRenderTarget * rt,const GrClip & clip,const GrPaint & paint,const SkPaint & skPaint,const SkMatrix & viewMatrix,const char text[],size_t byteLength,SkScalar x,SkScalar y,const SkIRect & regionClipBounds)75 void GrStencilAndCoverTextContext::onDrawText(GrRenderTarget* rt,
76                                               const GrClip& clip,
77                                               const GrPaint& paint,
78                                               const SkPaint& skPaint,
79                                               const SkMatrix& viewMatrix,
80                                               const char text[],
81                                               size_t byteLength,
82                                               SkScalar x, SkScalar y,
83                                               const SkIRect& regionClipBounds) {
84     SkASSERT(byteLength == 0 || text != NULL);
85 
86     if (text == NULL || byteLength == 0 /*|| fRC->isEmpty()*/) {
87         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 
105     this->init(rt, clip, paint, skPaint, byteLength, kMaxAccuracy_RenderMode, viewMatrix,
106                regionClipBounds);
107 
108     // Transform our starting point.
109     if (fUsingDeviceSpaceGlyphs) {
110         SkPoint loc;
111         fContextInitialMatrix.mapXY(x, y, &loc);
112         x = loc.fX;
113         y = loc.fY;
114     }
115 
116     SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
117 
118     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);
156         fx += SkFixedMul(autokern.adjust(glyph), fixedSizeRatio);
157         if (glyph.fWidth) {
158             this->appendGlyph(glyph, SkPoint::Make(SkFixedToScalar(fx), SkFixedToScalar(fy)));
159         }
160 
161         fx += SkFixedMul(glyph.fAdvanceX, fixedSizeRatio);
162         fy += SkFixedMul(glyph.fAdvanceY, fixedSizeRatio);
163     }
164 
165     this->finish();
166 }
167 
onDrawPosText(GrRenderTarget * rt,const GrClip & clip,const GrPaint & paint,const SkPaint & skPaint,const SkMatrix & viewMatrix,const char text[],size_t byteLength,const SkScalar pos[],int scalarsPerPosition,const SkPoint & offset,const SkIRect & regionClipBounds)168 void GrStencilAndCoverTextContext::onDrawPosText(GrRenderTarget* rt,
169                                                  const GrClip& clip,
170                                                  const GrPaint& paint,
171                                                  const SkPaint& skPaint,
172                                                  const SkMatrix& viewMatrix,
173                                                  const char text[],
174                                                  size_t byteLength,
175                                                  const SkScalar pos[],
176                                                  int scalarsPerPosition,
177                                                  const SkPoint& offset,
178                                                  const SkIRect& regionClipBounds) {
179     SkASSERT(byteLength == 0 || text != NULL);
180     SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
181 
182     // nothing to draw
183     if (text == NULL || byteLength == 0/* || fRC->isEmpty()*/) {
184         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.
194 
195     this->init(rt, clip, paint, skPaint, byteLength, kMaxPerformance_RenderMode, viewMatrix,
196                regionClipBounds);
197 
198     SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
199 
200     const char* stop = text + byteLength;
201 
202     SkTextMapStateProc tmsProc(SkMatrix::I(), offset, scalarsPerPosition);
203     SkTextAlignProc alignProc(fSkPaint.getTextAlign());
204     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);
211 
212             this->appendGlyph(glyph, loc);
213         }
214         pos += scalarsPerPosition;
215     }
216 
217     this->finish();
218 }
219 
get_gr_glyphs(GrContext * ctx,const SkTypeface * typeface,const SkDescriptor * desc,const SkStrokeRec & stroke)220 static GrPathRange* get_gr_glyphs(GrContext* ctx,
221                                   const SkTypeface* typeface,
222                                   const SkDescriptor* desc,
223                                   const SkStrokeRec& stroke) {
224     static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
225     GrUniqueKey key;
226     GrUniqueKey::Builder builder(&key, kDomain, 4);
227     struct GlyphKey {
228         uint32_t fChecksum;
229         uint32_t fTypeface;
230         uint64_t fStroke;
231     };
232     GlyphKey* glyphKey = reinterpret_cast<GlyphKey*>(&builder[0]);
233     glyphKey->fChecksum = desc ? desc->getChecksum() : 0;
234     glyphKey->fTypeface = typeface ? typeface->uniqueID() : 0;
235     glyphKey->fStroke = GrPath::ComputeStrokeKey(stroke);
236     builder.finish();
237 
238     SkAutoTUnref<GrPathRange> glyphs(
239         static_cast<GrPathRange*>(ctx->resourceProvider()->findAndRefResourceByUniqueKey(key)));
240     if (NULL == glyphs || (NULL != desc && !glyphs->isEqualTo(*desc))) {
241         glyphs.reset(ctx->getGpu()->pathRendering()->createGlyphs(typeface, desc, stroke));
242         ctx->resourceProvider()->assignUniqueKeyToResource(key, glyphs);
243     }
244 
245     return glyphs.detach();
246 }
247 
init(GrRenderTarget * rt,const GrClip & clip,const GrPaint & paint,const SkPaint & skPaint,size_t textByteLength,RenderMode renderMode,const SkMatrix & viewMatrix,const SkIRect & regionClipBounds)248 void GrStencilAndCoverTextContext::init(GrRenderTarget* rt,
249                                         const GrClip& clip,
250                                         const GrPaint& paint,
251                                         const SkPaint& skPaint,
252                                         size_t textByteLength,
253                                         RenderMode renderMode,
254                                         const SkMatrix& viewMatrix,
255                                         const SkIRect& regionClipBounds) {
256     GrTextContext::init(rt, clip, paint, skPaint, regionClipBounds);
257 
258     fContextInitialMatrix = viewMatrix;
259     fViewMatrix = viewMatrix;
260     fLocalMatrix = SkMatrix::I();
261 
262     const bool otherBackendsWillDrawAsPaths =
263         SkDraw::ShouldDrawTextAsPaths(skPaint, fContextInitialMatrix);
264 
265     fUsingDeviceSpaceGlyphs = !otherBackendsWillDrawAsPaths &&
266                               kMaxAccuracy_RenderMode == renderMode &&
267                               SkToBool(fContextInitialMatrix.getType() &
268                                        (SkMatrix::kScale_Mask | SkMatrix::kAffine_Mask));
269 
270     if (fUsingDeviceSpaceGlyphs) {
271         // SkDraw::ShouldDrawTextAsPaths takes care of perspective transforms.
272         SkASSERT(!fContextInitialMatrix.hasPerspective());
273 
274         // The whole shape (including stroke) will be baked into the glyph outlines. Make
275         // NVPR just fill the baked shapes.
276         fStroke = SkStrokeRec(SkStrokeRec::kFill_InitStyle);
277 
278         fTextRatio = fTextInverseRatio = 1.0f;
279 
280         // Glyphs loaded by GPU path rendering have an inverted y-direction.
281         SkMatrix m;
282         m.setScale(1, -1);
283         fViewMatrix = m;
284 
285         // Post-flip the initial matrix so we're left with just the flip after
286         // the paint preConcats the inverse.
287         m = fContextInitialMatrix;
288         m.postScale(1, -1);
289         if (!m.invert(&fLocalMatrix)) {
290             SkDebugf("Not invertible!\n");
291             return;
292         }
293 
294         fGlyphCache = fSkPaint.detachCache(&fDeviceProperties, &fContextInitialMatrix,
295                                            true /*ignoreGamma*/);
296         fGlyphs = get_gr_glyphs(fContext, fGlyphCache->getScalerContext()->getTypeface(),
297                                 &fGlyphCache->getDescriptor(), fStroke);
298     } else {
299         // Don't bake strokes into the glyph outlines. We will stroke the glyphs
300         // using the GPU instead. This is the fast path.
301         fStroke = SkStrokeRec(fSkPaint);
302         fSkPaint.setStyle(SkPaint::kFill_Style);
303 
304         if (fStroke.isHairlineStyle()) {
305             // Approximate hairline stroke.
306             SkScalar strokeWidth = SK_Scalar1 /
307                 (SkVector::Make(fContextInitialMatrix.getScaleX(),
308                                 fContextInitialMatrix.getSkewY()).length());
309             fStroke.setStrokeStyle(strokeWidth, false /*strokeAndFill*/);
310 
311         } else if (fSkPaint.isFakeBoldText() &&
312 #ifdef SK_USE_FREETYPE_EMBOLDEN
313                    kMaxPerformance_RenderMode == renderMode &&
314 #endif
315                    SkStrokeRec::kStroke_Style != fStroke.getStyle()) {
316 
317             // Instead of baking fake bold into the glyph outlines, do it with the GPU stroke.
318             SkScalar fakeBoldScale = SkScalarInterpFunc(fSkPaint.getTextSize(),
319                                                         kStdFakeBoldInterpKeys,
320                                                         kStdFakeBoldInterpValues,
321                                                         kStdFakeBoldInterpLength);
322             SkScalar extra = SkScalarMul(fSkPaint.getTextSize(), fakeBoldScale);
323             fStroke.setStrokeStyle(fStroke.needToApply() ? fStroke.getWidth() + extra : extra,
324                                    true /*strokeAndFill*/);
325 
326             fSkPaint.setFakeBoldText(false);
327         }
328 
329         bool canUseRawPaths;
330 
331         if (otherBackendsWillDrawAsPaths || kMaxPerformance_RenderMode == renderMode) {
332             // We can draw the glyphs from canonically sized paths.
333             fTextRatio = fSkPaint.getTextSize() / SkPaint::kCanonicalTextSizeForPaths;
334             fTextInverseRatio = SkPaint::kCanonicalTextSizeForPaths / fSkPaint.getTextSize();
335 
336             // Compensate for the glyphs being scaled by fTextRatio.
337             if (!fStroke.isFillStyle()) {
338                 fStroke.setStrokeStyle(fStroke.getWidth() / fTextRatio,
339                                        SkStrokeRec::kStrokeAndFill_Style == fStroke.getStyle());
340             }
341 
342             fSkPaint.setLinearText(true);
343             fSkPaint.setLCDRenderText(false);
344             fSkPaint.setAutohinted(false);
345             fSkPaint.setHinting(SkPaint::kNo_Hinting);
346             fSkPaint.setSubpixelText(true);
347             fSkPaint.setTextSize(SkIntToScalar(SkPaint::kCanonicalTextSizeForPaths));
348 
349             canUseRawPaths = SK_Scalar1 == fSkPaint.getTextScaleX() &&
350                              0 == fSkPaint.getTextSkewX() &&
351                              !fSkPaint.isFakeBoldText() &&
352                              !fSkPaint.isVerticalText();
353         } else {
354             fTextRatio = fTextInverseRatio = 1.0f;
355             canUseRawPaths = false;
356         }
357 
358         SkMatrix textMatrix;
359         // Glyphs loaded by GPU path rendering have an inverted y-direction.
360         textMatrix.setScale(fTextRatio, -fTextRatio);
361         fViewMatrix.preConcat(textMatrix);
362         fLocalMatrix = textMatrix;
363 
364         fGlyphCache = fSkPaint.detachCache(&fDeviceProperties, NULL, true /*ignoreGamma*/);
365         fGlyphs = canUseRawPaths ?
366                       get_gr_glyphs(fContext, fSkPaint.getTypeface(), NULL, fStroke) :
367                       get_gr_glyphs(fContext, fGlyphCache->getScalerContext()->getTypeface(),
368                                     &fGlyphCache->getDescriptor(), fStroke);
369     }
370 
371     fStateRestore.set(&fPipelineBuilder);
372 
373     fPipelineBuilder.setFromPaint(fPaint, fRenderTarget, fClip);
374 
375     GR_STATIC_CONST_SAME_STENCIL(kStencilPass,
376                                  kZero_StencilOp,
377                                  kZero_StencilOp,
378                                  kNotEqual_StencilFunc,
379                                  0xffff,
380                                  0x0000,
381                                  0xffff);
382 
383     *fPipelineBuilder.stencil() = kStencilPass;
384 
385     SkASSERT(0 == fQueuedGlyphCount);
386     SkASSERT(kGlyphBufferSize == fFallbackGlyphsIdx);
387 }
388 
mapToFallbackContext(SkMatrix * inverse)389 bool GrStencilAndCoverTextContext::mapToFallbackContext(SkMatrix* inverse) {
390     // The current view matrix is flipped because GPU path rendering glyphs have an
391     // inverted y-direction. Unflip the view matrix for the fallback context. If using
392     // device-space glyphs, we'll also need to restore the original view matrix since
393     // we moved that transfomation into our local glyph cache for this scenario. Also
394     // track the inverse operation so the caller can unmap the paint and glyph positions.
395     if (fUsingDeviceSpaceGlyphs) {
396         fViewMatrix = fContextInitialMatrix;
397         if (!fContextInitialMatrix.invert(inverse)) {
398             return false;
399         }
400         inverse->preScale(1, -1);
401     } else {
402         inverse->setScale(1, -1);
403         const SkMatrix& unflip = *inverse; // unflip is equal to its own inverse.
404         fViewMatrix.preConcat(unflip);
405     }
406     return true;
407 }
408 
appendGlyph(const SkGlyph & glyph,const SkPoint & pos)409 inline void GrStencilAndCoverTextContext::appendGlyph(const SkGlyph& glyph, const SkPoint& pos) {
410     if (fQueuedGlyphCount >= fFallbackGlyphsIdx) {
411         SkASSERT(fQueuedGlyphCount == fFallbackGlyphsIdx);
412         this->flush();
413     }
414 
415     // Stick the glyphs we can't draw at the end of the buffer, growing backwards.
416     int index = (SkMask::kARGB32_Format == glyph.fMaskFormat) ?
417                 --fFallbackGlyphsIdx : fQueuedGlyphCount++;
418 
419     fGlyphIndices[index] = glyph.getGlyphID();
420     fGlyphPositions[index].set(fTextInverseRatio * pos.x(), -fTextInverseRatio * pos.y());
421 }
422 
get_xy_scalar_array(const SkPoint * pointArray)423 static const SkScalar* get_xy_scalar_array(const SkPoint* pointArray) {
424     GR_STATIC_ASSERT(2 * sizeof(SkScalar) == sizeof(SkPoint));
425     GR_STATIC_ASSERT(0 == offsetof(SkPoint, fX));
426 
427     return &pointArray[0].fX;
428 }
429 
flush()430 void GrStencilAndCoverTextContext::flush() {
431     if (fQueuedGlyphCount > 0) {
432         SkAutoTUnref<GrPathProcessor> pp(GrPathProcessor::Create(fPaint.getColor(),
433                                                                  fViewMatrix,
434                                                                  fLocalMatrix));
435         fDrawTarget->drawPaths(&fPipelineBuilder, pp, fGlyphs,
436                                fGlyphIndices, GrPathRange::kU16_PathIndexType,
437                                get_xy_scalar_array(fGlyphPositions),
438                                GrPathRendering::kTranslate_PathTransformType,
439                                fQueuedGlyphCount, GrPathRendering::kWinding_FillType);
440 
441         fQueuedGlyphCount = 0;
442     }
443 
444     if (fFallbackGlyphsIdx < kGlyphBufferSize) {
445         int fallbackGlyphCount = kGlyphBufferSize - fFallbackGlyphsIdx;
446 
447         GrPaint paintFallback(fPaint);
448 
449         SkPaint skPaintFallback(fSkPaint);
450         if (!fUsingDeviceSpaceGlyphs) {
451             fStroke.applyToPaint(&skPaintFallback);
452         }
453         skPaintFallback.setTextAlign(SkPaint::kLeft_Align); // Align has already been accounted for.
454         skPaintFallback.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
455 
456         SkMatrix inverse;
457         if (this->mapToFallbackContext(&inverse)) {
458             inverse.mapPoints(&fGlyphPositions[fFallbackGlyphsIdx], fallbackGlyphCount);
459         }
460 
461         fFallbackTextContext->drawPosText(fRenderTarget, fClip, paintFallback, skPaintFallback,
462                                           fViewMatrix, (char*)&fGlyphIndices[fFallbackGlyphsIdx],
463                                           2 * fallbackGlyphCount,
464                                           get_xy_scalar_array(&fGlyphPositions[fFallbackGlyphsIdx]),
465                                           2, SkPoint::Make(0, 0), fRegionClipBounds);
466 
467         fFallbackGlyphsIdx = kGlyphBufferSize;
468     }
469 }
470 
finish()471 void GrStencilAndCoverTextContext::finish() {
472     this->flush();
473 
474     fGlyphs->unref();
475     fGlyphs = NULL;
476 
477     SkGlyphCache::AttachCache(fGlyphCache);
478     fGlyphCache = NULL;
479 
480     fPipelineBuilder.stencil()->setDisabled();
481     fStateRestore.set(NULL);
482     fViewMatrix = fContextInitialMatrix;
483     GrTextContext::finish();
484 }
485 
486