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