OLD | NEW |
1 /* | 1 /* |
2 * Copyright 2014 Google Inc. | 2 * Copyright 2014 Google Inc. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
6 */ | 6 */ |
7 | 7 |
8 #include "GrStencilAndCoverTextContext.h" | 8 #include "GrStencilAndCoverTextContext.h" |
9 #include "GrDrawTarget.h" | 9 #include "GrDrawTarget.h" |
10 #include "GrFontScaler.h" | |
11 #include "GrGpu.h" | 10 #include "GrGpu.h" |
12 #include "GrPath.h" | 11 #include "GrPath.h" |
13 #include "GrTextStrike.h" | 12 #include "GrPathRange.h" |
14 #include "GrTextStrike_impl.h" | |
15 #include "SkAutoKern.h" | 13 #include "SkAutoKern.h" |
16 #include "SkDraw.h" | 14 #include "SkDraw.h" |
17 #include "SkDrawProcs.h" | 15 #include "SkDrawProcs.h" |
18 #include "SkGlyphCache.h" | 16 #include "SkGlyphCache.h" |
19 #include "SkGpuDevice.h" | 17 #include "SkGpuDevice.h" |
20 #include "SkPath.h" | 18 #include "SkPath.h" |
21 #include "SkTextMapStateProc.h" | 19 #include "SkTextMapStateProc.h" |
22 | 20 |
23 static const int kMaxReservedGlyphs = 64; | 21 class 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 |
| 25 public: |
| 26 static GlyphPathRange* Create(GrContext* context, |
| 27 SkGlyphCache* cache, |
| 28 const SkStrokeRec& stroke) { |
| 29 static const GrCacheID::Domain gGlyphPathRangeDomain = GrCacheID::Genera
teDomain(); |
| 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(resou
rceKey))); |
| 40 |
| 41 if (NULL == glyphs || |
| 42 !glyphs->fDesc->equals(cache->getDescriptor() /*checksum collision*/
)) { |
| 43 glyphs.reset(SkNEW_ARGS(GlyphPathRange, (context, cache->getDescript
or(), 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->gpuMem
orySize(); } |
| 80 virtual bool isValidOnGpu() const SK_OVERRIDE { return fPathRange->isValidOn
Gpu(); } |
| 81 |
| 82 private: |
| 83 GlyphPathRange(GrContext* context, const SkDescriptor& desc, const SkStrokeR
ec& 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 e
very |
| 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 |
24 | 104 |
25 GrStencilAndCoverTextContext::GrStencilAndCoverTextContext( | 105 GrStencilAndCoverTextContext::GrStencilAndCoverTextContext( |
26 GrContext* context, const SkDeviceProperties& properties) | 106 GrContext* context, const SkDeviceProperties& properties) |
27 : GrTextContext(context, properties) | 107 : GrTextContext(context, properties) |
28 , fStroke(SkStrokeRec::kFill_InitStyle) { | 108 , fStroke(SkStrokeRec::kFill_InitStyle) |
| 109 , fPendingGlyphCount(0) { |
29 } | 110 } |
30 | 111 |
31 GrStencilAndCoverTextContext::~GrStencilAndCoverTextContext() { | 112 GrStencilAndCoverTextContext::~GrStencilAndCoverTextContext() { |
32 } | 113 } |
33 | 114 |
34 void GrStencilAndCoverTextContext::drawText(const GrPaint& paint, | 115 void GrStencilAndCoverTextContext::drawText(const GrPaint& paint, |
35 const SkPaint& skPaint, | 116 const SkPaint& skPaint, |
36 const char text[], | 117 const char text[], |
37 size_t byteLength, | 118 size_t byteLength, |
38 SkScalar x, SkScalar y) { | 119 SkScalar x, SkScalar y) { |
(...skipping 27 matching lines...) Expand all Loading... |
66 if (fNeedsDeviceSpaceGlyphs) { | 147 if (fNeedsDeviceSpaceGlyphs) { |
67 SkPoint loc; | 148 SkPoint loc; |
68 fGlyphTransform.mapXY(x, y, &loc); | 149 fGlyphTransform.mapXY(x, y, &loc); |
69 x = loc.fX; | 150 x = loc.fX; |
70 y = loc.fY; | 151 y = loc.fY; |
71 glyphCacheTransform = &fGlyphTransform; | 152 glyphCacheTransform = &fGlyphTransform; |
72 } | 153 } |
73 | 154 |
74 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc(); | 155 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc(); |
75 SkAutoGlyphCache autoCache(fSkPaint, &fDeviceProperties, glyphCacheTransform
); | 156 SkAutoGlyphCache autoCache(fSkPaint, &fDeviceProperties, glyphCacheTransform
); |
76 SkGlyphCache* cache = autoCache.getCache(); | 157 fGlyphCache = autoCache.getCache(); |
77 GrFontScaler* scaler = GetGrFontScaler(cache); | 158 fGlyphs = GlyphPathRange::Create(fContext, fGlyphCache, fStroke); |
78 GrTextStrike* strike = | |
79 fContext->getFontCache()->getStrike(scaler, true); | |
80 | 159 |
81 const char* stop = text + byteLength; | 160 const char* stop = text + byteLength; |
82 | 161 |
83 // Measure first if needed. | 162 // Measure first if needed. |
84 if (fSkPaint.getTextAlign() != SkPaint::kLeft_Align) { | 163 if (fSkPaint.getTextAlign() != SkPaint::kLeft_Align) { |
85 SkFixed stopX = 0; | 164 SkFixed stopX = 0; |
86 SkFixed stopY = 0; | 165 SkFixed stopY = 0; |
87 | 166 |
88 const char* textPtr = text; | 167 const char* textPtr = text; |
89 while (textPtr < stop) { | 168 while (textPtr < stop) { |
90 // We don't need x, y here, since all subpixel variants will have th
e | 169 // We don't need x, y here, since all subpixel variants will have th
e |
91 // same advance. | 170 // same advance. |
92 const SkGlyph& glyph = glyphCacheProc(cache, &textPtr, 0, 0); | 171 const SkGlyph& glyph = glyphCacheProc(fGlyphCache, &textPtr, 0, 0); |
93 | 172 |
94 stopX += glyph.fAdvanceX; | 173 stopX += glyph.fAdvanceX; |
95 stopY += glyph.fAdvanceY; | 174 stopY += glyph.fAdvanceY; |
96 } | 175 } |
97 SkASSERT(textPtr == stop); | 176 SkASSERT(textPtr == stop); |
98 | 177 |
99 SkScalar alignX = SkFixedToScalar(stopX) * fTextRatio; | 178 SkScalar alignX = SkFixedToScalar(stopX) * fTextRatio; |
100 SkScalar alignY = SkFixedToScalar(stopY) * fTextRatio; | 179 SkScalar alignY = SkFixedToScalar(stopY) * fTextRatio; |
101 | 180 |
102 if (fSkPaint.getTextAlign() == SkPaint::kCenter_Align) { | 181 if (fSkPaint.getTextAlign() == SkPaint::kCenter_Align) { |
103 alignX = SkScalarHalf(alignX); | 182 alignX = SkScalarHalf(alignX); |
104 alignY = SkScalarHalf(alignY); | 183 alignY = SkScalarHalf(alignY); |
105 } | 184 } |
106 | 185 |
107 x -= alignX; | 186 x -= alignX; |
108 y -= alignY; | 187 y -= alignY; |
109 } | 188 } |
110 | 189 |
111 SkAutoKern autokern; | 190 SkAutoKern autokern; |
112 | 191 |
113 SkFixed fixedSizeRatio = SkScalarToFixed(fTextRatio); | 192 SkFixed fixedSizeRatio = SkScalarToFixed(fTextRatio); |
114 | 193 |
115 SkFixed fx = SkScalarToFixed(x); | 194 SkFixed fx = SkScalarToFixed(x); |
116 SkFixed fy = SkScalarToFixed(y); | 195 SkFixed fy = SkScalarToFixed(y); |
117 while (text < stop) { | 196 while (text < stop) { |
118 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0); | 197 const SkGlyph& glyph = glyphCacheProc(fGlyphCache, &text, 0, 0); |
119 fx += SkFixedMul_portable(autokern.adjust(glyph), fixedSizeRatio); | 198 fx += SkFixedMul_portable(autokern.adjust(glyph), fixedSizeRatio); |
120 if (glyph.fWidth) { | 199 if (glyph.fWidth) { |
121 this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(), | 200 this->appendGlyph(glyph.getGlyphID(), SkFixedToScalar(fx), SkFixedTo
Scalar(fy)); |
122 glyph.getSubXFixed(), | |
123 glyph.getSubYFixed()), | |
124 SkPoint::Make( | |
125 SkFixedToScalar(fx), | |
126 SkFixedToScalar(fy)), | |
127 strike, | |
128 scaler); | |
129 } | 201 } |
130 | 202 |
131 fx += SkFixedMul_portable(glyph.fAdvanceX, fixedSizeRatio); | 203 fx += SkFixedMul_portable(glyph.fAdvanceX, fixedSizeRatio); |
132 fy += SkFixedMul_portable(glyph.fAdvanceY, fixedSizeRatio); | 204 fy += SkFixedMul_portable(glyph.fAdvanceY, fixedSizeRatio); |
133 } | 205 } |
134 | 206 |
135 this->finish(); | 207 this->finish(); |
136 } | 208 } |
137 | 209 |
138 void GrStencilAndCoverTextContext::drawPosText(const GrPaint& paint, | 210 void GrStencilAndCoverTextContext::drawPosText(const GrPaint& paint, |
(...skipping 18 matching lines...) Expand all Loading... |
157 // glyphs. That already uses glyph cache without device transforms. Device | 229 // glyphs. That already uses glyph cache without device transforms. Device |
158 // transform is not part of SkPaint::measureText API, and thus we use the | 230 // transform is not part of SkPaint::measureText API, and thus we use the |
159 // same glyphs as what were measured. | 231 // same glyphs as what were measured. |
160 fGlyphTransform.reset(); | 232 fGlyphTransform.reset(); |
161 | 233 |
162 this->init(paint, skPaint, byteLength); | 234 this->init(paint, skPaint, byteLength); |
163 | 235 |
164 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc(); | 236 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc(); |
165 | 237 |
166 SkAutoGlyphCache autoCache(fSkPaint, &fDeviceProperties, NULL); | 238 SkAutoGlyphCache autoCache(fSkPaint, &fDeviceProperties, NULL); |
167 SkGlyphCache* cache = autoCache.getCache(); | 239 fGlyphCache = autoCache.getCache(); |
168 GrFontScaler* scaler = GetGrFontScaler(cache); | 240 fGlyphs = GlyphPathRange::Create(fContext, fGlyphCache, fStroke); |
169 GrTextStrike* strike = | |
170 fContext->getFontCache()->getStrike(scaler, true); | |
171 | 241 |
172 const char* stop = text + byteLength; | 242 const char* stop = text + byteLength; |
173 SkTextAlignProcScalar alignProc(fSkPaint.getTextAlign()); | 243 SkTextAlignProcScalar alignProc(fSkPaint.getTextAlign()); |
174 SkTextMapStateProc tmsProc(SkMatrix::I(), constY, scalarsPerPosition); | 244 SkTextMapStateProc tmsProc(SkMatrix::I(), constY, scalarsPerPosition); |
175 | 245 |
176 if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) { | 246 if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) { |
177 while (text < stop) { | 247 while (text < stop) { |
178 SkPoint loc; | 248 SkPoint loc; |
179 tmsProc(pos, &loc); | 249 tmsProc(pos, &loc); |
180 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0); | 250 const SkGlyph& glyph = glyphCacheProc(fGlyphCache, &text, 0, 0); |
181 if (glyph.fWidth) { | 251 if (glyph.fWidth) { |
182 this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(), | 252 this->appendGlyph(glyph.getGlyphID(), loc.x(), loc.y()); |
183 glyph.getSubXFixed(), | |
184 glyph.getSubYFixed()), | |
185 loc, | |
186 strike, | |
187 scaler); | |
188 } | 253 } |
189 pos += scalarsPerPosition; | 254 pos += scalarsPerPosition; |
190 } | 255 } |
191 } else { | 256 } else { |
192 while (text < stop) { | 257 while (text < stop) { |
193 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0); | 258 const SkGlyph& glyph = glyphCacheProc(fGlyphCache, &text, 0, 0); |
194 if (glyph.fWidth) { | 259 if (glyph.fWidth) { |
195 SkPoint tmsLoc; | 260 SkPoint tmsLoc; |
196 tmsProc(pos, &tmsLoc); | 261 tmsProc(pos, &tmsLoc); |
197 SkPoint loc; | 262 SkPoint loc; |
198 alignProc(tmsLoc, glyph, &loc); | 263 alignProc(tmsLoc, glyph, &loc); |
199 | 264 |
200 this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(), | 265 this->appendGlyph(glyph.getGlyphID(), loc.x(), loc.y()); |
201 glyph.getSubXFixed(), | |
202 glyph.getSubYFixed()), | |
203 loc, | |
204 strike, | |
205 scaler); | |
206 | |
207 } | 266 } |
208 pos += scalarsPerPosition; | 267 pos += scalarsPerPosition; |
209 } | 268 } |
210 } | 269 } |
211 | 270 |
212 this->finish(); | 271 this->finish(); |
213 } | 272 } |
214 | 273 |
215 bool GrStencilAndCoverTextContext::canDraw(const SkPaint& paint) { | 274 bool GrStencilAndCoverTextContext::canDraw(const SkPaint& paint) { |
216 if (paint.getRasterizer()) { | 275 if (paint.getRasterizer()) { |
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
298 GR_STATIC_CONST_SAME_STENCIL(kStencilPass, | 357 GR_STATIC_CONST_SAME_STENCIL(kStencilPass, |
299 kZero_StencilOp, | 358 kZero_StencilOp, |
300 kZero_StencilOp, | 359 kZero_StencilOp, |
301 kNotEqual_StencilFunc, | 360 kNotEqual_StencilFunc, |
302 0xffff, | 361 0xffff, |
303 0x0000, | 362 0x0000, |
304 0xffff); | 363 0xffff); |
305 | 364 |
306 *fDrawTarget->drawState()->stencil() = kStencilPass; | 365 *fDrawTarget->drawState()->stencil() = kStencilPass; |
307 | 366 |
308 size_t reserveAmount; | 367 SkASSERT(0 == fPendingGlyphCount); |
309 switch (skPaint.getTextEncoding()) { | |
310 default: | |
311 SkASSERT(false); | |
312 case SkPaint::kUTF8_TextEncoding: | |
313 reserveAmount = textByteLength; | |
314 break; | |
315 case SkPaint::kUTF16_TextEncoding: | |
316 reserveAmount = textByteLength / 2; | |
317 break; | |
318 case SkPaint::kUTF32_TextEncoding: | |
319 case SkPaint::kGlyphID_TextEncoding: | |
320 reserveAmount = textByteLength / 4; | |
321 break; | |
322 } | |
323 fPaths.setReserve(reserveAmount); | |
324 fTransforms.setReserve(reserveAmount); | |
325 } | 368 } |
326 | 369 |
327 inline void GrStencilAndCoverTextContext::appendGlyph(GrGlyph::PackedID glyphID, | 370 inline void GrStencilAndCoverTextContext::appendGlyph(uint16_t glyphID, float x,
float y) { |
328 const SkPoint& pos, | 371 if (fPendingGlyphCount >= kGlyphBufferSize) { |
329 GrTextStrike* strike, | 372 this->flush(); |
330 GrFontScaler* scaler) { | 373 } |
331 GrGlyph* glyph = strike->getGlyph(glyphID, scaler); | 374 |
332 if (NULL == glyph || glyph->fBounds.isEmpty()) { | 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 |
| 388 void GrStencilAndCoverTextContext::flush() { |
| 389 if (0 == fPendingGlyphCount) { |
333 return; | 390 return; |
334 } | 391 } |
335 | 392 |
336 if (scaler->getGlyphPath(glyph->glyphID(), &fTmpPath)) { | 393 fDrawTarget->drawPaths(fGlyphs->pathRange(), fIndexBuffer, fPendingGlyphCoun
t, |
337 if (!fTmpPath.isEmpty()) { | 394 fTransformBuffer, GrDrawTarget::kAffine_PathTransform
Type, |
338 *fPaths.append() = fContext->createPath(fTmpPath, fStroke); | 395 SkPath::kWinding_FillType); |
339 SkMatrix* t = fTransforms.append(); | 396 |
340 t->setTranslate(pos.fX, pos.fY); | 397 fPendingGlyphCount = 0; |
341 t->preScale(fTextRatio, fTextRatio); | |
342 } | |
343 } | |
344 } | 398 } |
345 | 399 |
346 void GrStencilAndCoverTextContext::finish() { | 400 void GrStencilAndCoverTextContext::finish() { |
347 if (fPaths.count() > 0) { | 401 this->flush(); |
348 fDrawTarget->drawPaths(static_cast<size_t>(fPaths.count()), | |
349 fPaths.begin(), fTransforms.begin(), | |
350 SkPath::kWinding_FillType, fStroke.getStyle()); | |
351 | 402 |
352 for (int i = 0; i < fPaths.count(); ++i) { | 403 SkSafeUnref(fGlyphs); |
353 fPaths[i]->unref(); | 404 fGlyphs = NULL; |
354 } | 405 fGlyphCache = NULL; |
355 if (fPaths.count() > kMaxReservedGlyphs) { | |
356 fPaths.reset(); | |
357 fTransforms.reset(); | |
358 } else { | |
359 fPaths.rewind(); | |
360 fTransforms.rewind(); | |
361 } | |
362 } | |
363 fTmpPath.reset(); | |
364 | 406 |
365 fDrawTarget->drawState()->stencil()->setDisabled(); | 407 fDrawTarget->drawState()->stencil()->setDisabled(); |
366 fStateRestore.set(NULL); | 408 fStateRestore.set(NULL); |
367 if (fNeedsDeviceSpaceGlyphs) { | 409 if (fNeedsDeviceSpaceGlyphs) { |
368 fContext->setMatrix(fGlyphTransform); | 410 fContext->setMatrix(fGlyphTransform); |
369 } | 411 } |
370 GrTextContext::finish(); | 412 GrTextContext::finish(); |
371 } | 413 } |
372 | 414 |
OLD | NEW |