blob: 65c26423d1391edd69193620234af1e344e5303f [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"
9#include "GrDrawTarget.h"
kkinnunenc6cb56f2014-06-24 00:12:27 -070010#include "GrGpu.h"
11#include "GrPath.h"
cdaltonb85a0aa2014-07-21 15:32:44 -070012#include "GrPathRange.h"
kkinnunenc6cb56f2014-06-24 00:12:27 -070013#include "SkAutoKern.h"
14#include "SkDraw.h"
15#include "SkDrawProcs.h"
16#include "SkGlyphCache.h"
17#include "SkGpuDevice.h"
18#include "SkPath.h"
19#include "SkTextMapStateProc.h"
20
cdaltonb85a0aa2014-07-21 15:32:44 -070021class GrStencilAndCoverTextContext::GlyphPathRange : public GrCacheable {
22 static const int kMaxGlyphCount = 1 << 16; // Glyph IDs are uint16_t's
23 static const int kGlyphGroupSize = 16; // Glyphs get tracked in groups of 16
24
25public:
26 static GlyphPathRange* Create(GrContext* context,
27 SkGlyphCache* cache,
28 const SkStrokeRec& stroke) {
29 static const GrCacheID::Domain gGlyphPathRangeDomain = GrCacheID::GenerateDomain();
30
31 GrCacheID::Key key;
32 key.fData32[0] = cache->getDescriptor().getChecksum();
33 key.fData32[1] = cache->getScalerContext()->getTypeface()->uniqueID();
34 key.fData64[1] = GrPath::ComputeStrokeKey(stroke);
35
36 GrResourceKey resourceKey(GrCacheID(gGlyphPathRangeDomain, key),
37 GrPathRange::resourceType(), 0);
38 SkAutoTUnref<GlyphPathRange> glyphs(
39 static_cast<GlyphPathRange*>(context->findAndRefCachedResource(resourceKey)));
40
41 if (NULL == glyphs ||
42 !glyphs->fDesc->equals(cache->getDescriptor() /*checksum collision*/)) {
43 glyphs.reset(SkNEW_ARGS(GlyphPathRange, (context, cache->getDescriptor(), stroke)));
44 context->addResourceToCache(resourceKey, glyphs);
45 }
46
47 return glyphs.detach();
48 }
49
50 const GrPathRange* pathRange() const { return fPathRange.get(); }
51
52 void preloadGlyph(uint16_t glyphID, SkGlyphCache* cache) {
53 const uint16_t groupIndex = glyphID / kGlyphGroupSize;
54 const uint16_t groupByte = groupIndex >> 3;
55 const uint8_t groupBit = 1 << (groupIndex & 7);
56
57 const bool hasGlyph = 0 != (fLoadedGlyphs[groupByte] & groupBit);
58 if (hasGlyph) {
59 return;
60 }
61
62 // We track which glyphs are loaded in groups of kGlyphGroupSize. To
63 // mark a glyph loaded we need to load the entire group.
64 const uint16_t groupFirstID = groupIndex * kGlyphGroupSize;
65 const uint16_t groupLastID = groupFirstID + kGlyphGroupSize - 1;
66 SkPath skPath;
67 for (int id = groupFirstID; id <= groupLastID; ++id) {
68 const SkGlyph& skGlyph = cache->getGlyphIDMetrics(id);
69 if (const SkPath* skPath = cache->findPath(skGlyph)) {
70 fPathRange->initAt(id, *skPath);
71 } // GrGpu::drawPaths will silently ignore undefined paths.
72 }
73
74 fLoadedGlyphs[groupByte] |= groupBit;
75 this->didChangeGpuMemorySize();
76 }
77
78 // GrCacheable overrides
79 virtual size_t gpuMemorySize() const SK_OVERRIDE { return fPathRange->gpuMemorySize(); }
80 virtual bool isValidOnGpu() const SK_OVERRIDE { return fPathRange->isValidOnGpu(); }
81
82private:
83 GlyphPathRange(GrContext* context, const SkDescriptor& desc, const SkStrokeRec& stroke)
84 : fDesc(desc.copy())
85 // We reserve a range of kMaxGlyphCount paths because of fallbacks fonts. We
86 // can't know exactly how many glyphs we might need without preloading every
87 // fallback, which we don't want to do at this point.
88 , fPathRange(context->getGpu()->createPathRange(kMaxGlyphCount, stroke)) {
89 memset(fLoadedGlyphs, 0, sizeof(fLoadedGlyphs));
90 }
91
92 ~GlyphPathRange() {
93 SkDescriptor::Free(fDesc);
94 }
95
96 static const int kMaxGroupCount = (kMaxGlyphCount + (kGlyphGroupSize - 1)) / kGlyphGroupSize;
97 SkDescriptor* const fDesc;
98 uint8_t fLoadedGlyphs[(kMaxGroupCount + 7) >> 3]; // One bit per glyph group
99 SkAutoTUnref<GrPathRange> fPathRange;
100
101 typedef GrCacheable INHERITED;
102};
103
kkinnunenc6cb56f2014-06-24 00:12:27 -0700104
105GrStencilAndCoverTextContext::GrStencilAndCoverTextContext(
106 GrContext* context, const SkDeviceProperties& properties)
107 : GrTextContext(context, properties)
cdaltonb85a0aa2014-07-21 15:32:44 -0700108 , fStroke(SkStrokeRec::kFill_InitStyle)
109 , fPendingGlyphCount(0) {
kkinnunenc6cb56f2014-06-24 00:12:27 -0700110}
111
112GrStencilAndCoverTextContext::~GrStencilAndCoverTextContext() {
113}
114
115void GrStencilAndCoverTextContext::drawText(const GrPaint& paint,
116 const SkPaint& skPaint,
117 const char text[],
118 size_t byteLength,
119 SkScalar x, SkScalar y) {
120 SkASSERT(byteLength == 0 || text != NULL);
121
122 if (text == NULL || byteLength == 0 /*|| fRC->isEmpty()*/) {
123 return;
124 }
125
126 // This is the slow path, mainly used by Skia unit tests. The other
127 // backends (8888, gpu, ...) use device-space dependent glyph caches. In
128 // order to match the glyph positions that the other code paths produce, we
129 // must also use device-space dependent glyph cache. This has the
130 // side-effect that the glyph shape outline will be in device-space,
131 // too. This in turn has the side-effect that NVPR can not stroke the paths,
132 // as the stroke in NVPR is defined in object-space.
133 // NOTE: here we have following coincidence that works at the moment:
134 // - When using the device-space glyphs, the transforms we pass to NVPR
135 // instanced drawing are the global transforms, and the view transform is
136 // identity. NVPR can not use non-affine transforms in the instanced
137 // drawing. This is taken care of by SkDraw::ShouldDrawTextAsPaths since it
138 // will turn off the use of device-space glyphs when perspective transforms
139 // are in use.
140
141 fGlyphTransform = fContext->getMatrix();
142
143 this->init(paint, skPaint, byteLength);
144
145 SkMatrix* glyphCacheTransform = NULL;
146 // Transform our starting point.
147 if (fNeedsDeviceSpaceGlyphs) {
148 SkPoint loc;
149 fGlyphTransform.mapXY(x, y, &loc);
150 x = loc.fX;
151 y = loc.fY;
152 glyphCacheTransform = &fGlyphTransform;
153 }
154
155 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
156 SkAutoGlyphCache autoCache(fSkPaint, &fDeviceProperties, glyphCacheTransform);
cdaltonb85a0aa2014-07-21 15:32:44 -0700157 fGlyphCache = autoCache.getCache();
158 fGlyphs = GlyphPathRange::Create(fContext, fGlyphCache, fStroke);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700159
160 const char* stop = text + byteLength;
161
162 // Measure first if needed.
163 if (fSkPaint.getTextAlign() != SkPaint::kLeft_Align) {
164 SkFixed stopX = 0;
165 SkFixed stopY = 0;
166
167 const char* textPtr = text;
168 while (textPtr < stop) {
169 // We don't need x, y here, since all subpixel variants will have the
170 // same advance.
cdaltonb85a0aa2014-07-21 15:32:44 -0700171 const SkGlyph& glyph = glyphCacheProc(fGlyphCache, &textPtr, 0, 0);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700172
173 stopX += glyph.fAdvanceX;
174 stopY += glyph.fAdvanceY;
175 }
176 SkASSERT(textPtr == stop);
177
178 SkScalar alignX = SkFixedToScalar(stopX) * fTextRatio;
179 SkScalar alignY = SkFixedToScalar(stopY) * fTextRatio;
180
181 if (fSkPaint.getTextAlign() == SkPaint::kCenter_Align) {
182 alignX = SkScalarHalf(alignX);
183 alignY = SkScalarHalf(alignY);
184 }
185
186 x -= alignX;
187 y -= alignY;
188 }
189
190 SkAutoKern autokern;
191
192 SkFixed fixedSizeRatio = SkScalarToFixed(fTextRatio);
193
194 SkFixed fx = SkScalarToFixed(x);
195 SkFixed fy = SkScalarToFixed(y);
196 while (text < stop) {
cdaltonb85a0aa2014-07-21 15:32:44 -0700197 const SkGlyph& glyph = glyphCacheProc(fGlyphCache, &text, 0, 0);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700198 fx += SkFixedMul_portable(autokern.adjust(glyph), fixedSizeRatio);
199 if (glyph.fWidth) {
cdaltonb85a0aa2014-07-21 15:32:44 -0700200 this->appendGlyph(glyph.getGlyphID(), SkFixedToScalar(fx), SkFixedToScalar(fy));
kkinnunenc6cb56f2014-06-24 00:12:27 -0700201 }
202
203 fx += SkFixedMul_portable(glyph.fAdvanceX, fixedSizeRatio);
204 fy += SkFixedMul_portable(glyph.fAdvanceY, fixedSizeRatio);
205 }
206
207 this->finish();
208}
209
210void GrStencilAndCoverTextContext::drawPosText(const GrPaint& paint,
211 const SkPaint& skPaint,
212 const char text[],
213 size_t byteLength,
214 const SkScalar pos[],
215 SkScalar constY,
216 int scalarsPerPosition) {
217 SkASSERT(byteLength == 0 || text != NULL);
218 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
219
220 // nothing to draw
221 if (text == NULL || byteLength == 0/* || fRC->isEmpty()*/) {
222 return;
223 }
224
225 // This is the fast path. Here we do not bake in the device-transform to
226 // the glyph outline or the advances. This is because we do not need to
227 // position the glyphs at all, since the caller has done the positioning.
228 // The positioning is based on SkPaint::measureText of individual
229 // glyphs. That already uses glyph cache without device transforms. Device
230 // transform is not part of SkPaint::measureText API, and thus we use the
231 // same glyphs as what were measured.
232 fGlyphTransform.reset();
233
234 this->init(paint, skPaint, byteLength);
235
236 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
237
238 SkAutoGlyphCache autoCache(fSkPaint, &fDeviceProperties, NULL);
cdaltonb85a0aa2014-07-21 15:32:44 -0700239 fGlyphCache = autoCache.getCache();
240 fGlyphs = GlyphPathRange::Create(fContext, fGlyphCache, fStroke);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700241
242 const char* stop = text + byteLength;
243 SkTextAlignProcScalar alignProc(fSkPaint.getTextAlign());
244 SkTextMapStateProc tmsProc(SkMatrix::I(), constY, scalarsPerPosition);
245
246 if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) {
247 while (text < stop) {
248 SkPoint loc;
249 tmsProc(pos, &loc);
cdaltonb85a0aa2014-07-21 15:32:44 -0700250 const SkGlyph& glyph = glyphCacheProc(fGlyphCache, &text, 0, 0);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700251 if (glyph.fWidth) {
cdaltonb85a0aa2014-07-21 15:32:44 -0700252 this->appendGlyph(glyph.getGlyphID(), loc.x(), loc.y());
kkinnunenc6cb56f2014-06-24 00:12:27 -0700253 }
254 pos += scalarsPerPosition;
255 }
256 } else {
257 while (text < stop) {
cdaltonb85a0aa2014-07-21 15:32:44 -0700258 const SkGlyph& glyph = glyphCacheProc(fGlyphCache, &text, 0, 0);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700259 if (glyph.fWidth) {
260 SkPoint tmsLoc;
261 tmsProc(pos, &tmsLoc);
262 SkPoint loc;
263 alignProc(tmsLoc, glyph, &loc);
264
cdaltonb85a0aa2014-07-21 15:32:44 -0700265 this->appendGlyph(glyph.getGlyphID(), loc.x(), loc.y());
kkinnunenc6cb56f2014-06-24 00:12:27 -0700266 }
267 pos += scalarsPerPosition;
268 }
269 }
270
271 this->finish();
272}
273
274bool GrStencilAndCoverTextContext::canDraw(const SkPaint& paint) {
275 if (paint.getRasterizer()) {
276 return false;
277 }
278 if (paint.getMaskFilter()) {
279 return false;
280 }
281 if (paint.getPathEffect()) {
282 return false;
283 }
284
285 // No hairlines unless we can map the 1 px width to the object space.
286 if (paint.getStyle() == SkPaint::kStroke_Style
287 && paint.getStrokeWidth() == 0
288 && fContext->getMatrix().hasPerspective()) {
289 return false;
290 }
291
292 // No color bitmap fonts.
293 SkScalerContext::Rec rec;
294 SkScalerContext::MakeRec(paint, &fDeviceProperties, NULL, &rec);
295 return rec.getFormat() != SkMask::kARGB32_Format;
296}
297
298void GrStencilAndCoverTextContext::init(const GrPaint& paint,
299 const SkPaint& skPaint,
300 size_t textByteLength) {
301 GrTextContext::init(paint, skPaint);
302
303 bool otherBackendsWillDrawAsPaths =
304 SkDraw::ShouldDrawTextAsPaths(skPaint, fContext->getMatrix());
305
306 if (otherBackendsWillDrawAsPaths) {
307 // This is to reproduce SkDraw::drawText_asPaths glyph positions.
308 fSkPaint.setLinearText(true);
309 fTextRatio = fSkPaint.getTextSize() / SkPaint::kCanonicalTextSizeForPaths;
310 fSkPaint.setTextSize(SkIntToScalar(SkPaint::kCanonicalTextSizeForPaths));
311 if (fSkPaint.getStyle() != SkPaint::kFill_Style) {
312 // Compensate the glyphs being scaled up by fTextRatio by scaling the
313 // stroke down.
314 fSkPaint.setStrokeWidth(fSkPaint.getStrokeWidth() / fTextRatio);
315 }
316 fNeedsDeviceSpaceGlyphs = false;
317 } else {
318 fTextRatio = 1.0f;
319 fNeedsDeviceSpaceGlyphs = (fGlyphTransform.getType() &
320 (SkMatrix::kScale_Mask | SkMatrix::kAffine_Mask)) != 0;
321 // SkDraw::ShouldDrawTextAsPaths takes care of perspective transforms.
322 SkASSERT(!fGlyphTransform.hasPerspective());
323 if (fNeedsDeviceSpaceGlyphs) {
324 fPaint.localCoordChangeInverse(fGlyphTransform);
325 fContext->setIdentityMatrix();
326 }
327 }
328
329 fStroke = SkStrokeRec(fSkPaint);
330
331 if (fNeedsDeviceSpaceGlyphs) {
332 // The whole shape is baked into the glyph. Make NVPR just fill the
333 // baked shape.
334 fStroke.setStrokeStyle(-1, false);
335 } else {
336 if (fSkPaint.getStrokeWidth() == 0.0f) {
337 if (fSkPaint.getStyle() == SkPaint::kStrokeAndFill_Style) {
338 fStroke.setStrokeStyle(-1, false);
339 } else if (fSkPaint.getStyle() == SkPaint::kStroke_Style) {
340 // Approximate hairline stroke.
341 const SkMatrix& ctm = fContext->getMatrix();
342 SkScalar strokeWidth = SK_Scalar1 /
343 (fTextRatio * SkVector::Make(ctm.getScaleX(), ctm.getSkewY()).length());
344 fStroke.setStrokeStyle(strokeWidth, false);
345 }
346 }
347
348 // Make glyph cache produce paths geometry for fill. We will stroke them
349 // by passing fStroke to drawPath. This is the fast path.
350 fSkPaint.setStyle(SkPaint::kFill_Style);
351 }
352 fStateRestore.set(fDrawTarget->drawState());
353
354 fDrawTarget->drawState()->setFromPaint(fPaint, fContext->getMatrix(),
355 fContext->getRenderTarget());
356
357 GR_STATIC_CONST_SAME_STENCIL(kStencilPass,
358 kZero_StencilOp,
359 kZero_StencilOp,
360 kNotEqual_StencilFunc,
361 0xffff,
362 0x0000,
363 0xffff);
364
365 *fDrawTarget->drawState()->stencil() = kStencilPass;
366
cdaltonb85a0aa2014-07-21 15:32:44 -0700367 SkASSERT(0 == fPendingGlyphCount);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700368}
369
cdaltonb85a0aa2014-07-21 15:32:44 -0700370inline void GrStencilAndCoverTextContext::appendGlyph(uint16_t glyphID, float x, float y) {
371 if (fPendingGlyphCount >= kGlyphBufferSize) {
372 this->flush();
373 }
374
375 fGlyphs->preloadGlyph(glyphID, fGlyphCache);
376
377 fIndexBuffer[fPendingGlyphCount] = glyphID;
378 fTransformBuffer[6 * fPendingGlyphCount + 0] = fTextRatio;
379 fTransformBuffer[6 * fPendingGlyphCount + 1] = 0;
380 fTransformBuffer[6 * fPendingGlyphCount + 2] = x;
381 fTransformBuffer[6 * fPendingGlyphCount + 3] = 0;
382 fTransformBuffer[6 * fPendingGlyphCount + 4] = fTextRatio;
383 fTransformBuffer[6 * fPendingGlyphCount + 5] = y;
384
385 ++fPendingGlyphCount;
386}
387
388void GrStencilAndCoverTextContext::flush() {
389 if (0 == fPendingGlyphCount) {
kkinnunenc6cb56f2014-06-24 00:12:27 -0700390 return;
391 }
392
cdaltonb85a0aa2014-07-21 15:32:44 -0700393 fDrawTarget->drawPaths(fGlyphs->pathRange(), fIndexBuffer, fPendingGlyphCount,
394 fTransformBuffer, GrDrawTarget::kAffine_PathTransformType,
395 SkPath::kWinding_FillType);
396
397 fPendingGlyphCount = 0;
kkinnunenc6cb56f2014-06-24 00:12:27 -0700398}
399
400void GrStencilAndCoverTextContext::finish() {
cdaltonb85a0aa2014-07-21 15:32:44 -0700401 this->flush();
kkinnunenc6cb56f2014-06-24 00:12:27 -0700402
cdaltonb85a0aa2014-07-21 15:32:44 -0700403 SkSafeUnref(fGlyphs);
404 fGlyphs = NULL;
405 fGlyphCache = NULL;
kkinnunenc6cb56f2014-06-24 00:12:27 -0700406
407 fDrawTarget->drawState()->stencil()->setDisabled();
408 fStateRestore.set(NULL);
409 if (fNeedsDeviceSpaceGlyphs) {
410 fContext->setMatrix(fGlyphTransform);
411 }
412 GrTextContext::finish();
413}
414