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 |