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 "GrGpu.h" | 10 #include "GrGpu.h" |
(...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
133 // too. This in turn has the side-effect that NVPR can not stroke the paths, | 133 // too. This in turn has the side-effect that NVPR can not stroke the paths, |
134 // as the stroke in NVPR is defined in object-space. | 134 // as the stroke in NVPR is defined in object-space. |
135 // NOTE: here we have following coincidence that works at the moment: | 135 // NOTE: here we have following coincidence that works at the moment: |
136 // - When using the device-space glyphs, the transforms we pass to NVPR | 136 // - When using the device-space glyphs, the transforms we pass to NVPR |
137 // instanced drawing are the global transforms, and the view transform is | 137 // instanced drawing are the global transforms, and the view transform is |
138 // identity. NVPR can not use non-affine transforms in the instanced | 138 // identity. NVPR can not use non-affine transforms in the instanced |
139 // drawing. This is taken care of by SkDraw::ShouldDrawTextAsPaths since it | 139 // drawing. This is taken care of by SkDraw::ShouldDrawTextAsPaths since it |
140 // will turn off the use of device-space glyphs when perspective transforms | 140 // will turn off the use of device-space glyphs when perspective transforms |
141 // are in use. | 141 // are in use. |
142 | 142 |
143 fGlyphTransform = fContext->getMatrix(); | 143 this->init(paint, skPaint, byteLength, kUseIfNeeded_DeviceSpaceGlyphsBehavio r); |
144 | |
145 this->init(paint, skPaint, byteLength); | |
146 | 144 |
147 SkMatrix* glyphCacheTransform = NULL; | 145 SkMatrix* glyphCacheTransform = NULL; |
148 // Transform our starting point. | 146 // Transform our starting point. |
149 if (fNeedsDeviceSpaceGlyphs) { | 147 if (fNeedsDeviceSpaceGlyphs) { |
150 SkPoint loc; | 148 SkPoint loc; |
151 fGlyphTransform.mapXY(x, y, &loc); | 149 fContextInitialMatrix.mapXY(x, y, &loc); |
152 x = loc.fX; | 150 x = loc.fX; |
153 y = loc.fY; | 151 y = loc.fY; |
154 glyphCacheTransform = &fGlyphTransform; | 152 glyphCacheTransform = &fContextInitialMatrix; |
155 } | 153 } |
156 | 154 |
157 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc(); | 155 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc(); |
158 SkAutoGlyphCache autoCache(fSkPaint, &fDeviceProperties, glyphCacheTransform ); | 156 SkAutoGlyphCache autoCache(fSkPaint, &fDeviceProperties, glyphCacheTransform ); |
159 fGlyphCache = autoCache.getCache(); | 157 fGlyphCache = autoCache.getCache(); |
160 fGlyphs = GlyphPathRange::Create(fContext, fGlyphCache, fStroke); | 158 fGlyphs = GlyphPathRange::Create(fContext, fGlyphCache, fStroke); |
159 fTransformType = GrDrawTarget::kTranslate_PathTransformType; | |
161 | 160 |
162 const char* stop = text + byteLength; | 161 const char* stop = text + byteLength; |
163 | 162 |
164 // Measure first if needed. | 163 // Measure first if needed. |
165 if (fSkPaint.getTextAlign() != SkPaint::kLeft_Align) { | 164 if (fSkPaint.getTextAlign() != SkPaint::kLeft_Align) { |
166 SkFixed stopX = 0; | 165 SkFixed stopX = 0; |
167 SkFixed stopY = 0; | 166 SkFixed stopY = 0; |
168 | 167 |
169 const char* textPtr = text; | 168 const char* textPtr = text; |
170 while (textPtr < stop) { | 169 while (textPtr < stop) { |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
224 return; | 223 return; |
225 } | 224 } |
226 | 225 |
227 // This is the fast path. Here we do not bake in the device-transform to | 226 // This is the fast path. Here we do not bake in the device-transform to |
228 // the glyph outline or the advances. This is because we do not need to | 227 // the glyph outline or the advances. This is because we do not need to |
229 // position the glyphs at all, since the caller has done the positioning. | 228 // position the glyphs at all, since the caller has done the positioning. |
230 // The positioning is based on SkPaint::measureText of individual | 229 // The positioning is based on SkPaint::measureText of individual |
231 // glyphs. That already uses glyph cache without device transforms. Device | 230 // glyphs. That already uses glyph cache without device transforms. Device |
232 // transform is not part of SkPaint::measureText API, and thus we use the | 231 // transform is not part of SkPaint::measureText API, and thus we use the |
233 // same glyphs as what were measured. | 232 // same glyphs as what were measured. |
234 fGlyphTransform.reset(); | |
235 | 233 |
236 this->init(paint, skPaint, byteLength); | 234 const float textTranslateY = (1 == scalarsPerPosition ? constY : 0); |
235 this->init(paint, skPaint, byteLength, kDoNotUse_DeviceSpaceGlyphsBehavior, textTranslateY); | |
237 | 236 |
238 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc(); | 237 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc(); |
239 | 238 |
240 SkAutoGlyphCache autoCache(fSkPaint, &fDeviceProperties, NULL); | 239 SkAutoGlyphCache autoCache(fSkPaint, &fDeviceProperties, NULL); |
241 fGlyphCache = autoCache.getCache(); | 240 fGlyphCache = autoCache.getCache(); |
242 fGlyphs = GlyphPathRange::Create(fContext, fGlyphCache, fStroke); | 241 fGlyphs = GlyphPathRange::Create(fContext, fGlyphCache, fStroke); |
243 | 242 |
244 const char* stop = text + byteLength; | 243 const char* stop = text + byteLength; |
245 SkTextAlignProcScalar alignProc(fSkPaint.getTextAlign()); | |
246 SkTextMapStateProc tmsProc(SkMatrix::I(), constY, scalarsPerPosition); | |
247 | 244 |
248 if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) { | 245 if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) { |
249 while (text < stop) { | 246 if (1 == scalarsPerPosition) { |
250 SkPoint loc; | 247 fTransformType = GrDrawTarget::kTranslateX_PathTransformType; |
251 tmsProc(pos, &loc); | 248 while (text < stop) { |
252 const SkGlyph& glyph = glyphCacheProc(fGlyphCache, &text, 0, 0); | 249 const SkGlyph& glyph = glyphCacheProc(fGlyphCache, &text, 0, 0); |
253 if (glyph.fWidth) { | 250 if (glyph.fWidth) { |
254 this->appendGlyph(glyph.getGlyphID(), loc.x(), loc.y()); | 251 this->appendGlyph(glyph.getGlyphID(), *pos); |
252 } | |
253 pos++; | |
255 } | 254 } |
256 pos += scalarsPerPosition; | 255 } else { |
256 SkASSERT(2 == scalarsPerPosition); | |
257 fTransformType = GrDrawTarget::kTranslate_PathTransformType; | |
258 while (text < stop) { | |
259 const SkGlyph& glyph = glyphCacheProc(fGlyphCache, &text, 0, 0); | |
260 if (glyph.fWidth) { | |
261 this->appendGlyph(glyph.getGlyphID(), pos[0], pos[1]); | |
262 } | |
263 pos += 2; | |
264 } | |
257 } | 265 } |
258 } else { | 266 } else { |
Chris Dalton
2014/07/18 22:23:30
We could do a translate-x here too if we could ver
jvanverth1
2014/07/21 17:23:13
Not sure -- adding bungeman to see if he knows.
bungeman-skia
2014/07/21 17:51:27
I'm not sure I understand. You've used scalersPerP
| |
267 fTransformType = GrDrawTarget::kTranslate_PathTransformType; | |
268 SkTextMapStateProc tmsProc(SkMatrix::I(), 0, scalarsPerPosition); | |
269 SkTextAlignProcScalar alignProc(fSkPaint.getTextAlign()); | |
259 while (text < stop) { | 270 while (text < stop) { |
260 const SkGlyph& glyph = glyphCacheProc(fGlyphCache, &text, 0, 0); | 271 const SkGlyph& glyph = glyphCacheProc(fGlyphCache, &text, 0, 0); |
261 if (glyph.fWidth) { | 272 if (glyph.fWidth) { |
262 SkPoint tmsLoc; | 273 SkPoint tmsLoc; |
263 tmsProc(pos, &tmsLoc); | 274 tmsProc(pos, &tmsLoc); |
264 SkPoint loc; | 275 SkPoint loc; |
265 alignProc(tmsLoc, glyph, &loc); | 276 alignProc(tmsLoc, glyph, &loc); |
266 | 277 |
267 this->appendGlyph(glyph.getGlyphID(), loc.x(), loc.y()); | 278 this->appendGlyph(glyph.getGlyphID(), loc.x(), loc.y()); |
268 } | 279 } |
(...skipping 23 matching lines...) Expand all Loading... | |
292 } | 303 } |
293 | 304 |
294 // No color bitmap fonts. | 305 // No color bitmap fonts. |
295 SkScalerContext::Rec rec; | 306 SkScalerContext::Rec rec; |
296 SkScalerContext::MakeRec(paint, &fDeviceProperties, NULL, &rec); | 307 SkScalerContext::MakeRec(paint, &fDeviceProperties, NULL, &rec); |
297 return rec.getFormat() != SkMask::kARGB32_Format; | 308 return rec.getFormat() != SkMask::kARGB32_Format; |
298 } | 309 } |
299 | 310 |
300 void GrStencilAndCoverTextContext::init(const GrPaint& paint, | 311 void GrStencilAndCoverTextContext::init(const GrPaint& paint, |
301 const SkPaint& skPaint, | 312 const SkPaint& skPaint, |
302 size_t textByteLength) { | 313 size_t textByteLength, |
314 DeviceSpaceGlyphsBehavior deviceSpaceGly phsBehavior, | |
315 SkScalar textTranslateY) { | |
303 GrTextContext::init(paint, skPaint); | 316 GrTextContext::init(paint, skPaint); |
304 | 317 |
318 fContextInitialMatrix = fContext->getMatrix(); | |
319 | |
305 bool otherBackendsWillDrawAsPaths = | 320 bool otherBackendsWillDrawAsPaths = |
306 SkDraw::ShouldDrawTextAsPaths(skPaint, fContext->getMatrix()); | 321 SkDraw::ShouldDrawTextAsPaths(skPaint, fContextInitialMatrix); |
307 | 322 |
308 if (otherBackendsWillDrawAsPaths) { | 323 if (otherBackendsWillDrawAsPaths) { |
309 // This is to reproduce SkDraw::drawText_asPaths glyph positions. | 324 // This is to reproduce SkDraw::drawText_asPaths glyph positions. |
310 fSkPaint.setLinearText(true); | 325 fSkPaint.setLinearText(true); |
311 fTextRatio = fSkPaint.getTextSize() / SkPaint::kCanonicalTextSizeForPath s; | 326 fTextRatio = fSkPaint.getTextSize() / SkPaint::kCanonicalTextSizeForPath s; |
327 fTextInverseRatio = SkPaint::kCanonicalTextSizeForPaths / fSkPaint.getTe xtSize(); | |
312 fSkPaint.setTextSize(SkIntToScalar(SkPaint::kCanonicalTextSizeForPaths)) ; | 328 fSkPaint.setTextSize(SkIntToScalar(SkPaint::kCanonicalTextSizeForPaths)) ; |
313 if (fSkPaint.getStyle() != SkPaint::kFill_Style) { | 329 if (fSkPaint.getStyle() != SkPaint::kFill_Style) { |
314 // Compensate the glyphs being scaled up by fTextRatio by scaling th e | 330 // Compensate the glyphs being scaled up by fTextRatio by scaling th e |
315 // stroke down. | 331 // stroke down. |
316 fSkPaint.setStrokeWidth(fSkPaint.getStrokeWidth() / fTextRatio); | 332 fSkPaint.setStrokeWidth(fSkPaint.getStrokeWidth() / fTextRatio); |
317 } | 333 } |
318 fNeedsDeviceSpaceGlyphs = false; | 334 fNeedsDeviceSpaceGlyphs = false; |
319 } else { | 335 } else { |
320 fTextRatio = 1.0f; | 336 fTextRatio = fTextInverseRatio = 1.0f; |
321 fNeedsDeviceSpaceGlyphs = (fGlyphTransform.getType() & | 337 fNeedsDeviceSpaceGlyphs = |
322 (SkMatrix::kScale_Mask | SkMatrix::kAffine_Mask)) != 0; | 338 kUseIfNeeded_DeviceSpaceGlyphsBehavior == deviceSpaceGlyphsBehavior && |
339 (fContextInitialMatrix.getType() & | |
340 (SkMatrix::kScale_Mask | SkMatrix::kAffine_Mask)) != 0; | |
323 // SkDraw::ShouldDrawTextAsPaths takes care of perspective transforms. | 341 // SkDraw::ShouldDrawTextAsPaths takes care of perspective transforms. |
324 SkASSERT(!fGlyphTransform.hasPerspective()); | 342 SkASSERT(!fContextInitialMatrix.hasPerspective()); |
325 if (fNeedsDeviceSpaceGlyphs) { | |
326 fPaint.localCoordChangeInverse(fGlyphTransform); | |
327 fContext->setIdentityMatrix(); | |
328 } | |
329 } | 343 } |
330 | 344 |
331 fStroke = SkStrokeRec(fSkPaint); | 345 fStroke = SkStrokeRec(fSkPaint); |
332 | 346 |
333 if (fNeedsDeviceSpaceGlyphs) { | 347 if (fNeedsDeviceSpaceGlyphs) { |
348 SkASSERT(1.0f == fTextRatio); | |
349 SkASSERT(0.0f == textTranslateY); | |
350 fPaint.localCoordChangeInverse(fContextInitialMatrix); | |
351 fContext->setIdentityMatrix(); | |
352 | |
334 // The whole shape is baked into the glyph. Make NVPR just fill the | 353 // The whole shape is baked into the glyph. Make NVPR just fill the |
335 // baked shape. | 354 // baked shape. |
336 fStroke.setStrokeStyle(-1, false); | 355 fStroke.setStrokeStyle(-1, false); |
337 } else { | 356 } else { |
357 if (1.0f != fTextRatio || 0.0f != textTranslateY) { | |
358 SkMatrix textMatrix; | |
359 textMatrix.setTranslate(0, textTranslateY); | |
360 textMatrix.preScale(fTextRatio, fTextRatio); | |
361 fPaint.localCoordChange(textMatrix); | |
362 fContext->concatMatrix(textMatrix); | |
363 } | |
364 | |
338 if (fSkPaint.getStrokeWidth() == 0.0f) { | 365 if (fSkPaint.getStrokeWidth() == 0.0f) { |
339 if (fSkPaint.getStyle() == SkPaint::kStrokeAndFill_Style) { | 366 if (fSkPaint.getStyle() == SkPaint::kStrokeAndFill_Style) { |
340 fStroke.setStrokeStyle(-1, false); | 367 fStroke.setStrokeStyle(-1, false); |
341 } else if (fSkPaint.getStyle() == SkPaint::kStroke_Style) { | 368 } else if (fSkPaint.getStyle() == SkPaint::kStroke_Style) { |
342 // Approximate hairline stroke. | 369 // Approximate hairline stroke. |
343 const SkMatrix& ctm = fContext->getMatrix(); | 370 const SkMatrix& ctm = fContext->getMatrix(); |
344 SkScalar strokeWidth = SK_Scalar1 / | 371 SkScalar strokeWidth = SK_Scalar1 / |
345 (fTextRatio * SkVector::Make(ctm.getScaleX(), ctm.getSkewY() ).length()); | 372 (SkVector::Make(ctm.getScaleX(), ctm.getSkewY()).length()); |
346 fStroke.setStrokeStyle(strokeWidth, false); | 373 fStroke.setStrokeStyle(strokeWidth, false); |
347 } | 374 } |
348 } | 375 } |
349 | 376 |
350 // Make glyph cache produce paths geometry for fill. We will stroke them | 377 // Make glyph cache produce paths geometry for fill. We will stroke them |
351 // by passing fStroke to drawPath. This is the fast path. | 378 // by passing fStroke to drawPath. This is the fast path. |
352 fSkPaint.setStyle(SkPaint::kFill_Style); | 379 fSkPaint.setStyle(SkPaint::kFill_Style); |
353 } | 380 } |
354 fStateRestore.set(fDrawTarget->drawState()); | 381 fStateRestore.set(fDrawTarget->drawState()); |
355 | 382 |
356 fDrawTarget->drawState()->setFromPaint(fPaint, fContext->getMatrix(), | 383 fDrawTarget->drawState()->setFromPaint(fPaint, fContext->getMatrix(), |
357 fContext->getRenderTarget()); | 384 fContext->getRenderTarget()); |
358 | 385 |
359 GR_STATIC_CONST_SAME_STENCIL(kStencilPass, | 386 GR_STATIC_CONST_SAME_STENCIL(kStencilPass, |
360 kZero_StencilOp, | 387 kZero_StencilOp, |
361 kZero_StencilOp, | 388 kZero_StencilOp, |
362 kNotEqual_StencilFunc, | 389 kNotEqual_StencilFunc, |
363 0xffff, | 390 0xffff, |
364 0x0000, | 391 0x0000, |
365 0xffff); | 392 0xffff); |
366 | 393 |
367 *fDrawTarget->drawState()->stencil() = kStencilPass; | 394 *fDrawTarget->drawState()->stencil() = kStencilPass; |
368 | 395 |
369 SkASSERT(0 == fPendingGlyphCount); | 396 SkASSERT(0 == fPendingGlyphCount); |
370 } | 397 } |
371 | 398 |
372 inline void GrStencilAndCoverTextContext::appendGlyph(uint16_t glyphID, float x, float y) { | 399 inline void GrStencilAndCoverTextContext::appendGlyph(uint16_t glyphID, float x) { |
400 SkASSERT(GrDrawTarget::kTranslateX_PathTransformType == fTransformType); | |
401 | |
373 if (fPendingGlyphCount >= kGlyphBufferSize) { | 402 if (fPendingGlyphCount >= kGlyphBufferSize) { |
374 this->flush(); | 403 this->flush(); |
375 } | 404 } |
405 | |
406 fGlyphs->preloadGlyph(glyphID, fGlyphCache); | |
407 | |
408 fIndexBuffer[fPendingGlyphCount] = glyphID; | |
409 fTransformBuffer[fPendingGlyphCount] = fTextInverseRatio * x; | |
410 | |
411 ++fPendingGlyphCount; | |
412 } | |
413 | |
414 inline void GrStencilAndCoverTextContext::appendGlyph(uint16_t glyphID, float x, float y) { | |
415 SkASSERT(GrDrawTarget::kTranslate_PathTransformType == fTransformType); | |
416 | |
417 if (fPendingGlyphCount >= kGlyphBufferSize) { | |
418 this->flush(); | |
419 } | |
376 | 420 |
377 fGlyphs->preloadGlyph(glyphID, fGlyphCache); | 421 fGlyphs->preloadGlyph(glyphID, fGlyphCache); |
378 | 422 |
379 fIndexBuffer[fPendingGlyphCount] = glyphID; | 423 fIndexBuffer[fPendingGlyphCount] = glyphID; |
380 fTransformBuffer[6 * fPendingGlyphCount + 0] = fTextRatio; | 424 fTransformBuffer[2 * fPendingGlyphCount] = fTextInverseRatio * x; |
381 fTransformBuffer[6 * fPendingGlyphCount + 1] = 0; | 425 fTransformBuffer[2 * fPendingGlyphCount + 1] = fTextInverseRatio * y; |
382 fTransformBuffer[6 * fPendingGlyphCount + 2] = x; | |
383 fTransformBuffer[6 * fPendingGlyphCount + 3] = 0; | |
384 fTransformBuffer[6 * fPendingGlyphCount + 4] = fTextRatio; | |
385 fTransformBuffer[6 * fPendingGlyphCount + 5] = y; | |
386 | 426 |
387 ++fPendingGlyphCount; | 427 ++fPendingGlyphCount; |
388 } | 428 } |
389 | 429 |
390 void GrStencilAndCoverTextContext::flush() { | 430 void GrStencilAndCoverTextContext::flush() { |
391 if (0 == fPendingGlyphCount) { | 431 if (0 == fPendingGlyphCount) { |
392 return; | 432 return; |
393 } | 433 } |
394 | 434 |
395 fDrawTarget->drawPaths(fGlyphs->pathRange(), fIndexBuffer, fPendingGlyphCoun t, | 435 fDrawTarget->drawPaths(fGlyphs->pathRange(), fIndexBuffer, fPendingGlyphCoun t, |
396 fTransformBuffer, GrDrawTarget::kAffine_PathTransform Type, | 436 fTransformBuffer, fTransformType, SkPath::kWinding_Fi llType); |
397 SkPath::kWinding_FillType); | |
398 | 437 |
399 fPendingGlyphCount = 0; | 438 fPendingGlyphCount = 0; |
400 } | 439 } |
401 | 440 |
402 void GrStencilAndCoverTextContext::finish() { | 441 void GrStencilAndCoverTextContext::finish() { |
403 this->flush(); | 442 this->flush(); |
404 | 443 |
405 SkSafeUnref(fGlyphs); | 444 SkSafeUnref(fGlyphs); |
406 fGlyphs = NULL; | 445 fGlyphs = NULL; |
407 fGlyphCache = NULL; | 446 fGlyphCache = NULL; |
408 | 447 |
409 fDrawTarget->drawState()->stencil()->setDisabled(); | 448 fDrawTarget->drawState()->stencil()->setDisabled(); |
410 fStateRestore.set(NULL); | 449 fStateRestore.set(NULL); |
411 if (fNeedsDeviceSpaceGlyphs) { | 450 fContext->setMatrix(fContextInitialMatrix); |
412 fContext->setMatrix(fGlyphTransform); | |
413 } | |
414 GrTextContext::finish(); | 451 GrTextContext::finish(); |
415 } | 452 } |
416 | 453 |
OLD | NEW |