SkGpuDevice.cpp revision f97d65dc256111f1de6bbf3521c7cd3cf3e70f60
1/* 2 * Copyright 2011 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 "SkGpuDevice.h" 9 10#include "effects/GrBicubicEffect.h" 11#include "effects/GrTextureDomain.h" 12#include "effects/GrSimpleTextureEffect.h" 13 14#include "GrContext.h" 15#include "GrBitmapTextContext.h" 16#include "GrDistanceFieldTextContext.h" 17#include "GrLayerCache.h" 18#include "GrPictureUtils.h" 19 20#include "SkGrTexturePixelRef.h" 21 22#include "SkBounder.h" 23#include "SkColorFilter.h" 24#include "SkDeviceImageFilterProxy.h" 25#include "SkDrawProcs.h" 26#include "SkGlyphCache.h" 27#include "SkImageFilter.h" 28#include "SkMaskFilter.h" 29#include "SkPathEffect.h" 30#include "SkPicture.h" 31#include "SkPicturePlayback.h" 32#include "SkRRect.h" 33#include "SkStroke.h" 34#include "SkSurface.h" 35#include "SkTLazy.h" 36#include "SkUtils.h" 37#include "SkErrorInternals.h" 38 39#define CACHE_COMPATIBLE_DEVICE_TEXTURES 1 40 41#if 0 42 extern bool (*gShouldDrawProc)(); 43 #define CHECK_SHOULD_DRAW(draw, forceI) \ 44 do { \ 45 if (gShouldDrawProc && !gShouldDrawProc()) return; \ 46 this->prepareDraw(draw, forceI); \ 47 } while (0) 48#else 49 #define CHECK_SHOULD_DRAW(draw, forceI) this->prepareDraw(draw, forceI) 50#endif 51 52// This constant represents the screen alignment criterion in texels for 53// requiring texture domain clamping to prevent color bleeding when drawing 54// a sub region of a larger source image. 55#define COLOR_BLEED_TOLERANCE 0.001f 56 57#define DO_DEFERRED_CLEAR() \ 58 do { \ 59 if (fNeedClear) { \ 60 this->clear(SK_ColorTRANSPARENT); \ 61 } \ 62 } while (false) \ 63 64/////////////////////////////////////////////////////////////////////////////// 65 66#define CHECK_FOR_ANNOTATION(paint) \ 67 do { if (paint.getAnnotation()) { return; } } while (0) 68 69/////////////////////////////////////////////////////////////////////////////// 70 71 72class SkGpuDevice::SkAutoCachedTexture : public ::SkNoncopyable { 73public: 74 SkAutoCachedTexture() 75 : fDevice(NULL) 76 , fTexture(NULL) { 77 } 78 79 SkAutoCachedTexture(SkGpuDevice* device, 80 const SkBitmap& bitmap, 81 const GrTextureParams* params, 82 GrTexture** texture) 83 : fDevice(NULL) 84 , fTexture(NULL) { 85 SkASSERT(NULL != texture); 86 *texture = this->set(device, bitmap, params); 87 } 88 89 ~SkAutoCachedTexture() { 90 if (NULL != fTexture) { 91 GrUnlockAndUnrefCachedBitmapTexture(fTexture); 92 } 93 } 94 95 GrTexture* set(SkGpuDevice* device, 96 const SkBitmap& bitmap, 97 const GrTextureParams* params) { 98 if (NULL != fTexture) { 99 GrUnlockAndUnrefCachedBitmapTexture(fTexture); 100 fTexture = NULL; 101 } 102 fDevice = device; 103 GrTexture* result = (GrTexture*)bitmap.getTexture(); 104 if (NULL == result) { 105 // Cannot return the native texture so look it up in our cache 106 fTexture = GrLockAndRefCachedBitmapTexture(device->context(), bitmap, params); 107 result = fTexture; 108 } 109 return result; 110 } 111 112private: 113 SkGpuDevice* fDevice; 114 GrTexture* fTexture; 115}; 116 117/////////////////////////////////////////////////////////////////////////////// 118 119struct GrSkDrawProcs : public SkDrawProcs { 120public: 121 GrContext* fContext; 122 GrTextContext* fTextContext; 123 GrFontScaler* fFontScaler; // cached in the skia glyphcache 124}; 125 126/////////////////////////////////////////////////////////////////////////////// 127 128static SkBitmap::Config grConfig2skConfig(GrPixelConfig config, bool* isOpaque) { 129 switch (config) { 130 case kAlpha_8_GrPixelConfig: 131 *isOpaque = false; 132 return SkBitmap::kA8_Config; 133 case kRGB_565_GrPixelConfig: 134 *isOpaque = true; 135 return SkBitmap::kRGB_565_Config; 136 case kRGBA_4444_GrPixelConfig: 137 *isOpaque = false; 138 return SkBitmap::kARGB_4444_Config; 139 case kSkia8888_GrPixelConfig: 140 // we don't currently have a way of knowing whether 141 // a 8888 is opaque based on the config. 142 *isOpaque = false; 143 return SkBitmap::kARGB_8888_Config; 144 default: 145 *isOpaque = false; 146 return SkBitmap::kNo_Config; 147 } 148} 149 150/* 151 * GrRenderTarget does not know its opaqueness, only its config, so we have 152 * to make conservative guesses when we return an "equivalent" bitmap. 153 */ 154static SkBitmap make_bitmap(GrContext* context, GrRenderTarget* renderTarget) { 155 bool isOpaque; 156 SkBitmap::Config config = grConfig2skConfig(renderTarget->config(), &isOpaque); 157 158 SkBitmap bitmap; 159 bitmap.setConfig(config, renderTarget->width(), renderTarget->height(), 0, 160 isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType); 161 return bitmap; 162} 163 164SkGpuDevice* SkGpuDevice::Create(GrSurface* surface, unsigned flags) { 165 SkASSERT(NULL != surface); 166 if (NULL == surface->asRenderTarget() || NULL == surface->getContext()) { 167 return NULL; 168 } 169 if (surface->asTexture()) { 170 return SkNEW_ARGS(SkGpuDevice, (surface->getContext(), surface->asTexture(), flags)); 171 } else { 172 return SkNEW_ARGS(SkGpuDevice, (surface->getContext(), surface->asRenderTarget(), flags)); 173 } 174} 175 176SkGpuDevice::SkGpuDevice(GrContext* context, GrTexture* texture, unsigned flags) 177 : SkBitmapDevice(make_bitmap(context, texture->asRenderTarget())) { 178 this->initFromRenderTarget(context, texture->asRenderTarget(), flags); 179} 180 181SkGpuDevice::SkGpuDevice(GrContext* context, GrRenderTarget* renderTarget, unsigned flags) 182 : SkBitmapDevice(make_bitmap(context, renderTarget)) { 183 this->initFromRenderTarget(context, renderTarget, flags); 184} 185 186void SkGpuDevice::initFromRenderTarget(GrContext* context, 187 GrRenderTarget* renderTarget, 188 unsigned flags) { 189 fDrawProcs = NULL; 190 191 fContext = context; 192 fContext->ref(); 193 194 bool useDFFonts = !!(flags & kDFFonts_Flag); 195 fMainTextContext = SkNEW_ARGS(GrDistanceFieldTextContext, (fContext, fLeakyProperties, 196 useDFFonts)); 197 fFallbackTextContext = SkNEW_ARGS(GrBitmapTextContext, (fContext, fLeakyProperties)); 198 199 fRenderTarget = NULL; 200 fNeedClear = flags & kNeedClear_Flag; 201 202 SkASSERT(NULL != renderTarget); 203 fRenderTarget = renderTarget; 204 fRenderTarget->ref(); 205 206 // Hold onto to the texture in the pixel ref (if there is one) because the texture holds a ref 207 // on the RT but not vice-versa. 208 // TODO: Remove this trickery once we figure out how to make SkGrPixelRef do this without 209 // busting chrome (for a currently unknown reason). 210 GrSurface* surface = fRenderTarget->asTexture(); 211 if (NULL == surface) { 212 surface = fRenderTarget; 213 } 214 215 SkImageInfo info; 216 surface->asImageInfo(&info); 217 SkPixelRef* pr = SkNEW_ARGS(SkGrPixelRef, (info, surface, SkToBool(flags & kCached_Flag))); 218 219 this->setPixelRef(pr)->unref(); 220} 221 222SkGpuDevice* SkGpuDevice::Create(GrContext* context, const SkImageInfo& origInfo, 223 int sampleCount) { 224 if (kUnknown_SkColorType == origInfo.colorType() || 225 origInfo.width() < 0 || origInfo.height() < 0) { 226 return NULL; 227 } 228 229 SkImageInfo info = origInfo; 230 // TODO: perhas we can loosen this check now that colortype is more detailed 231 // e.g. can we support both RGBA and BGRA here? 232 if (kRGB_565_SkColorType == info.colorType()) { 233 info.fAlphaType = kOpaque_SkAlphaType; // force this setting 234 } else { 235 info.fColorType = kN32_SkColorType; 236 if (kOpaque_SkAlphaType != info.alphaType()) { 237 info.fAlphaType = kPremul_SkAlphaType; // force this setting 238 } 239 } 240 241 GrTextureDesc desc; 242 desc.fFlags = kRenderTarget_GrTextureFlagBit; 243 desc.fWidth = info.width(); 244 desc.fHeight = info.height(); 245 desc.fConfig = SkImageInfo2GrPixelConfig(info); 246 desc.fSampleCnt = sampleCount; 247 248 SkAutoTUnref<GrTexture> texture(context->createUncachedTexture(desc, NULL, 0)); 249 if (!texture.get()) { 250 return NULL; 251 } 252 253 return SkNEW_ARGS(SkGpuDevice, (context, texture.get())); 254} 255 256SkGpuDevice::~SkGpuDevice() { 257 if (fDrawProcs) { 258 delete fDrawProcs; 259 } 260 261 delete fMainTextContext; 262 delete fFallbackTextContext; 263 264 // The GrContext takes a ref on the target. We don't want to cause the render 265 // target to be unnecessarily kept alive. 266 if (fContext->getRenderTarget() == fRenderTarget) { 267 fContext->setRenderTarget(NULL); 268 } 269 270 if (fContext->getClip() == &fClipData) { 271 fContext->setClip(NULL); 272 } 273 274 SkSafeUnref(fRenderTarget); 275 fContext->unref(); 276} 277 278/////////////////////////////////////////////////////////////////////////////// 279 280void SkGpuDevice::makeRenderTargetCurrent() { 281 DO_DEFERRED_CLEAR(); 282 fContext->setRenderTarget(fRenderTarget); 283} 284 285/////////////////////////////////////////////////////////////////////////////// 286 287bool SkGpuDevice::onReadPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRowBytes, 288 int x, int y) { 289 DO_DEFERRED_CLEAR(); 290 291 // TODO: teach fRenderTarget to take ImageInfo directly to specify the src pixels 292 GrPixelConfig config = SkImageInfo2GrPixelConfig(dstInfo); 293 if (kUnknown_GrPixelConfig == config) { 294 return false; 295 } 296 297 uint32_t flags = 0; 298 if (kUnpremul_SkAlphaType == dstInfo.alphaType()) { 299 flags = GrContext::kUnpremul_PixelOpsFlag; 300 } 301 return fContext->readRenderTargetPixels(fRenderTarget, x, y, dstInfo.width(), dstInfo.height(), 302 config, dstPixels, dstRowBytes, flags); 303} 304 305bool SkGpuDevice::onWritePixels(const SkImageInfo& info, const void* pixels, size_t rowBytes, 306 int x, int y) { 307 // TODO: teach fRenderTarget to take ImageInfo directly to specify the src pixels 308 GrPixelConfig config = SkImageInfo2GrPixelConfig(info); 309 if (kUnknown_GrPixelConfig == config) { 310 return false; 311 } 312 uint32_t flags = 0; 313 if (kUnpremul_SkAlphaType == info.alphaType()) { 314 flags = GrContext::kUnpremul_PixelOpsFlag; 315 } 316 fRenderTarget->writePixels(x, y, info.width(), info.height(), config, pixels, rowBytes, flags); 317 318 // need to bump our genID for compatibility with clients that "know" we have a bitmap 319 this->onAccessBitmap().notifyPixelsChanged(); 320 321 return true; 322} 323 324const SkBitmap& SkGpuDevice::onAccessBitmap() { 325 DO_DEFERRED_CLEAR(); 326 return INHERITED::onAccessBitmap(); 327} 328 329void SkGpuDevice::onAttachToCanvas(SkCanvas* canvas) { 330 INHERITED::onAttachToCanvas(canvas); 331 332 // Canvas promises that this ptr is valid until onDetachFromCanvas is called 333 fClipData.fClipStack = canvas->getClipStack(); 334} 335 336void SkGpuDevice::onDetachFromCanvas() { 337 INHERITED::onDetachFromCanvas(); 338 fClipData.fClipStack = NULL; 339} 340 341// call this every draw call, to ensure that the context reflects our state, 342// and not the state from some other canvas/device 343void SkGpuDevice::prepareDraw(const SkDraw& draw, bool forceIdentity) { 344 SkASSERT(NULL != fClipData.fClipStack); 345 346 fContext->setRenderTarget(fRenderTarget); 347 348 SkASSERT(draw.fClipStack && draw.fClipStack == fClipData.fClipStack); 349 350 if (forceIdentity) { 351 fContext->setIdentityMatrix(); 352 } else { 353 fContext->setMatrix(*draw.fMatrix); 354 } 355 fClipData.fOrigin = this->getOrigin(); 356 357 fContext->setClip(&fClipData); 358 359 DO_DEFERRED_CLEAR(); 360} 361 362GrRenderTarget* SkGpuDevice::accessRenderTarget() { 363 DO_DEFERRED_CLEAR(); 364 return fRenderTarget; 365} 366 367/////////////////////////////////////////////////////////////////////////////// 368 369SK_COMPILE_ASSERT(SkShader::kNone_BitmapType == 0, shader_type_mismatch); 370SK_COMPILE_ASSERT(SkShader::kDefault_BitmapType == 1, shader_type_mismatch); 371SK_COMPILE_ASSERT(SkShader::kRadial_BitmapType == 2, shader_type_mismatch); 372SK_COMPILE_ASSERT(SkShader::kSweep_BitmapType == 3, shader_type_mismatch); 373SK_COMPILE_ASSERT(SkShader::kTwoPointRadial_BitmapType == 4, 374 shader_type_mismatch); 375SK_COMPILE_ASSERT(SkShader::kTwoPointConical_BitmapType == 5, 376 shader_type_mismatch); 377SK_COMPILE_ASSERT(SkShader::kLinear_BitmapType == 6, shader_type_mismatch); 378SK_COMPILE_ASSERT(SkShader::kLast_BitmapType == 6, shader_type_mismatch); 379 380namespace { 381 382// converts a SkPaint to a GrPaint, ignoring the skPaint's shader 383// justAlpha indicates that skPaint's alpha should be used rather than the color 384// Callers may subsequently modify the GrPaint. Setting constantColor indicates 385// that the final paint will draw the same color at every pixel. This allows 386// an optimization where the the color filter can be applied to the skPaint's 387// color once while converting to GrPaint and then ignored. 388inline bool skPaint2GrPaintNoShader(SkGpuDevice* dev, 389 const SkPaint& skPaint, 390 bool justAlpha, 391 bool constantColor, 392 GrPaint* grPaint) { 393 394 grPaint->setDither(skPaint.isDither()); 395 grPaint->setAntiAlias(skPaint.isAntiAlias()); 396 397 SkXfermode::Coeff sm; 398 SkXfermode::Coeff dm; 399 400 SkXfermode* mode = skPaint.getXfermode(); 401 GrEffectRef* xferEffect = NULL; 402 if (SkXfermode::AsNewEffectOrCoeff(mode, &xferEffect, &sm, &dm)) { 403 if (NULL != xferEffect) { 404 grPaint->addColorEffect(xferEffect)->unref(); 405 sm = SkXfermode::kOne_Coeff; 406 dm = SkXfermode::kZero_Coeff; 407 } 408 } else { 409 //SkDEBUGCODE(SkDebugf("Unsupported xfer mode.\n");) 410#if 0 411 return false; 412#else 413 // Fall back to src-over 414 sm = SkXfermode::kOne_Coeff; 415 dm = SkXfermode::kISA_Coeff; 416#endif 417 } 418 grPaint->setBlendFunc(sk_blend_to_grblend(sm), sk_blend_to_grblend(dm)); 419 420 if (justAlpha) { 421 uint8_t alpha = skPaint.getAlpha(); 422 grPaint->setColor(GrColorPackRGBA(alpha, alpha, alpha, alpha)); 423 // justAlpha is currently set to true only if there is a texture, 424 // so constantColor should not also be true. 425 SkASSERT(!constantColor); 426 } else { 427 grPaint->setColor(SkColor2GrColor(skPaint.getColor())); 428 } 429 430 SkColorFilter* colorFilter = skPaint.getColorFilter(); 431 if (NULL != colorFilter) { 432 // if the source color is a constant then apply the filter here once rather than per pixel 433 // in a shader. 434 if (constantColor) { 435 SkColor filtered = colorFilter->filterColor(skPaint.getColor()); 436 grPaint->setColor(SkColor2GrColor(filtered)); 437 } else { 438 SkAutoTUnref<GrEffectRef> effect(colorFilter->asNewEffect(dev->context())); 439 if (NULL != effect.get()) { 440 grPaint->addColorEffect(effect); 441 } 442 } 443 } 444 445 return true; 446} 447 448// This function is similar to skPaint2GrPaintNoShader but also converts 449// skPaint's shader to a GrTexture/GrEffectStage if possible. The texture to 450// be used is set on grPaint and returned in param act. constantColor has the 451// same meaning as in skPaint2GrPaintNoShader. 452inline bool skPaint2GrPaintShader(SkGpuDevice* dev, 453 const SkPaint& skPaint, 454 bool constantColor, 455 GrPaint* grPaint) { 456 SkShader* shader = skPaint.getShader(); 457 if (NULL == shader) { 458 return skPaint2GrPaintNoShader(dev, skPaint, false, constantColor, grPaint); 459 } 460 461 // SkShader::asNewEffect() may do offscreen rendering. Setup default drawing state and require 462 // the shader to set a render target . 463 GrContext::AutoWideOpenIdentityDraw awo(dev->context(), NULL); 464 465 // setup the shader as the first color effect on the paint 466 SkAutoTUnref<GrEffectRef> effect(shader->asNewEffect(dev->context(), skPaint)); 467 if (NULL != effect.get()) { 468 grPaint->addColorEffect(effect); 469 // Now setup the rest of the paint. 470 return skPaint2GrPaintNoShader(dev, skPaint, true, false, grPaint); 471 } else { 472 // We still don't have SkColorShader::asNewEffect() implemented. 473 SkShader::GradientInfo info; 474 SkColor color; 475 476 info.fColors = &color; 477 info.fColorOffsets = NULL; 478 info.fColorCount = 1; 479 if (SkShader::kColor_GradientType == shader->asAGradient(&info)) { 480 SkPaint copy(skPaint); 481 copy.setShader(NULL); 482 // modulate the paint alpha by the shader's solid color alpha 483 U8CPU newA = SkMulDiv255Round(SkColorGetA(color), copy.getAlpha()); 484 copy.setColor(SkColorSetA(color, newA)); 485 return skPaint2GrPaintNoShader(dev, copy, false, constantColor, grPaint); 486 } else { 487 return false; 488 } 489 } 490} 491} 492 493/////////////////////////////////////////////////////////////////////////////// 494 495SkBitmap::Config SkGpuDevice::config() const { 496 if (NULL == fRenderTarget) { 497 return SkBitmap::kNo_Config; 498 } 499 500 bool isOpaque; 501 return grConfig2skConfig(fRenderTarget->config(), &isOpaque); 502} 503 504void SkGpuDevice::clear(SkColor color) { 505 SkIRect rect = SkIRect::MakeWH(this->width(), this->height()); 506 fContext->clear(&rect, SkColor2GrColor(color), true, fRenderTarget); 507 fNeedClear = false; 508} 509 510void SkGpuDevice::drawPaint(const SkDraw& draw, const SkPaint& paint) { 511 CHECK_SHOULD_DRAW(draw, false); 512 513 GrPaint grPaint; 514 if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) { 515 return; 516 } 517 518 fContext->drawPaint(grPaint); 519} 520 521// must be in SkCanvas::PointMode order 522static const GrPrimitiveType gPointMode2PrimtiveType[] = { 523 kPoints_GrPrimitiveType, 524 kLines_GrPrimitiveType, 525 kLineStrip_GrPrimitiveType 526}; 527 528void SkGpuDevice::drawPoints(const SkDraw& draw, SkCanvas::PointMode mode, 529 size_t count, const SkPoint pts[], const SkPaint& paint) { 530 CHECK_FOR_ANNOTATION(paint); 531 CHECK_SHOULD_DRAW(draw, false); 532 533 SkScalar width = paint.getStrokeWidth(); 534 if (width < 0) { 535 return; 536 } 537 538 // we only handle hairlines and paints without path effects or mask filters, 539 // else we let the SkDraw call our drawPath() 540 if (width > 0 || paint.getPathEffect() || paint.getMaskFilter()) { 541 draw.drawPoints(mode, count, pts, paint, true); 542 return; 543 } 544 545 GrPaint grPaint; 546 if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) { 547 return; 548 } 549 550 fContext->drawVertices(grPaint, 551 gPointMode2PrimtiveType[mode], 552 SkToS32(count), 553 (SkPoint*)pts, 554 NULL, 555 NULL, 556 NULL, 557 0); 558} 559 560/////////////////////////////////////////////////////////////////////////////// 561 562void SkGpuDevice::drawRect(const SkDraw& draw, const SkRect& rect, 563 const SkPaint& paint) { 564 CHECK_FOR_ANNOTATION(paint); 565 CHECK_SHOULD_DRAW(draw, false); 566 567 bool doStroke = paint.getStyle() != SkPaint::kFill_Style; 568 SkScalar width = paint.getStrokeWidth(); 569 570 /* 571 We have special code for hairline strokes, miter-strokes, bevel-stroke 572 and fills. Anything else we just call our path code. 573 */ 574 bool usePath = doStroke && width > 0 && 575 (paint.getStrokeJoin() == SkPaint::kRound_Join || 576 (paint.getStrokeJoin() == SkPaint::kBevel_Join && rect.isEmpty())); 577 // another two reasons we might need to call drawPath... 578 if (paint.getMaskFilter() || paint.getPathEffect()) { 579 usePath = true; 580 } 581 if (!usePath && paint.isAntiAlias() && !fContext->getMatrix().rectStaysRect()) { 582#if defined(SHADER_AA_FILL_RECT) || !defined(IGNORE_ROT_AA_RECT_OPT) 583 if (doStroke) { 584#endif 585 usePath = true; 586#if defined(SHADER_AA_FILL_RECT) || !defined(IGNORE_ROT_AA_RECT_OPT) 587 } else { 588 usePath = !fContext->getMatrix().preservesRightAngles(); 589 } 590#endif 591 } 592 // until we can both stroke and fill rectangles 593 if (paint.getStyle() == SkPaint::kStrokeAndFill_Style) { 594 usePath = true; 595 } 596 597 if (usePath) { 598 SkPath path; 599 path.addRect(rect); 600 this->drawPath(draw, path, paint, NULL, true); 601 return; 602 } 603 604 GrPaint grPaint; 605 if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) { 606 return; 607 } 608 609 if (!doStroke) { 610 fContext->drawRect(grPaint, rect); 611 } else { 612 SkStrokeRec stroke(paint); 613 fContext->drawRect(grPaint, rect, &stroke); 614 } 615} 616 617/////////////////////////////////////////////////////////////////////////////// 618 619void SkGpuDevice::drawRRect(const SkDraw& draw, const SkRRect& rect, 620 const SkPaint& paint) { 621 CHECK_FOR_ANNOTATION(paint); 622 CHECK_SHOULD_DRAW(draw, false); 623 624 GrPaint grPaint; 625 if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) { 626 return; 627 } 628 629 SkStrokeRec stroke(paint); 630 if (paint.getMaskFilter()) { 631 // try to hit the fast path for drawing filtered round rects 632 633 SkRRect devRRect; 634 if (rect.transform(fContext->getMatrix(), &devRRect)) { 635 if (devRRect.allCornersCircular()) { 636 SkRect maskRect; 637 if (paint.getMaskFilter()->canFilterMaskGPU(devRRect.rect(), 638 draw.fClip->getBounds(), 639 fContext->getMatrix(), 640 &maskRect)) { 641 SkIRect finalIRect; 642 maskRect.roundOut(&finalIRect); 643 if (draw.fClip->quickReject(finalIRect)) { 644 // clipped out 645 return; 646 } 647 if (NULL != draw.fBounder && !draw.fBounder->doIRect(finalIRect)) { 648 // nothing to draw 649 return; 650 } 651 if (paint.getMaskFilter()->directFilterRRectMaskGPU(fContext, &grPaint, 652 stroke, devRRect)) { 653 return; 654 } 655 } 656 657 } 658 } 659 660 } 661 662 if (paint.getMaskFilter() || paint.getPathEffect()) { 663 SkPath path; 664 path.addRRect(rect); 665 this->drawPath(draw, path, paint, NULL, true); 666 return; 667 } 668 669 fContext->drawRRect(grPaint, rect, stroke); 670} 671 672void SkGpuDevice::drawDRRect(const SkDraw& draw, const SkRRect& outer, 673 const SkRRect& inner, const SkPaint& paint) { 674 SkStrokeRec stroke(paint); 675 if (stroke.isFillStyle()) { 676 677 CHECK_FOR_ANNOTATION(paint); 678 CHECK_SHOULD_DRAW(draw, false); 679 680 GrPaint grPaint; 681 if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) { 682 return; 683 } 684 685 if (NULL == paint.getMaskFilter() && NULL == paint.getPathEffect()) { 686 fContext->drawDRRect(grPaint, outer, inner); 687 return; 688 } 689 } 690 691 SkPath path; 692 path.addRRect(outer); 693 path.addRRect(inner); 694 path.setFillType(SkPath::kEvenOdd_FillType); 695 696 this->drawPath(draw, path, paint, NULL, true); 697} 698 699 700///////////////////////////////////////////////////////////////////////////// 701 702void SkGpuDevice::drawOval(const SkDraw& draw, const SkRect& oval, 703 const SkPaint& paint) { 704 CHECK_FOR_ANNOTATION(paint); 705 CHECK_SHOULD_DRAW(draw, false); 706 707 bool usePath = false; 708 // some basic reasons we might need to call drawPath... 709 if (paint.getMaskFilter() || paint.getPathEffect()) { 710 usePath = true; 711 } 712 713 if (usePath) { 714 SkPath path; 715 path.addOval(oval); 716 this->drawPath(draw, path, paint, NULL, true); 717 return; 718 } 719 720 GrPaint grPaint; 721 if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) { 722 return; 723 } 724 SkStrokeRec stroke(paint); 725 726 fContext->drawOval(grPaint, oval, stroke); 727} 728 729#include "SkMaskFilter.h" 730#include "SkBounder.h" 731 732/////////////////////////////////////////////////////////////////////////////// 733 734// helpers for applying mask filters 735namespace { 736 737// Draw a mask using the supplied paint. Since the coverage/geometry 738// is already burnt into the mask this boils down to a rect draw. 739// Return true if the mask was successfully drawn. 740bool draw_mask(GrContext* context, const SkRect& maskRect, 741 GrPaint* grp, GrTexture* mask) { 742 GrContext::AutoMatrix am; 743 if (!am.setIdentity(context, grp)) { 744 return false; 745 } 746 747 SkMatrix matrix; 748 matrix.setTranslate(-maskRect.fLeft, -maskRect.fTop); 749 matrix.postIDiv(mask->width(), mask->height()); 750 751 grp->addCoverageEffect(GrSimpleTextureEffect::Create(mask, matrix))->unref(); 752 context->drawRect(*grp, maskRect); 753 return true; 754} 755 756bool draw_with_mask_filter(GrContext* context, const SkPath& devPath, 757 SkMaskFilter* filter, const SkRegion& clip, SkBounder* bounder, 758 GrPaint* grp, SkPaint::Style style) { 759 SkMask srcM, dstM; 760 761 if (!SkDraw::DrawToMask(devPath, &clip.getBounds(), filter, &context->getMatrix(), &srcM, 762 SkMask::kComputeBoundsAndRenderImage_CreateMode, style)) { 763 return false; 764 } 765 SkAutoMaskFreeImage autoSrc(srcM.fImage); 766 767 if (!filter->filterMask(&dstM, srcM, context->getMatrix(), NULL)) { 768 return false; 769 } 770 // this will free-up dstM when we're done (allocated in filterMask()) 771 SkAutoMaskFreeImage autoDst(dstM.fImage); 772 773 if (clip.quickReject(dstM.fBounds)) { 774 return false; 775 } 776 if (bounder && !bounder->doIRect(dstM.fBounds)) { 777 return false; 778 } 779 780 // we now have a device-aligned 8bit mask in dstM, ready to be drawn using 781 // the current clip (and identity matrix) and GrPaint settings 782 GrTextureDesc desc; 783 desc.fWidth = dstM.fBounds.width(); 784 desc.fHeight = dstM.fBounds.height(); 785 desc.fConfig = kAlpha_8_GrPixelConfig; 786 787 GrAutoScratchTexture ast(context, desc); 788 GrTexture* texture = ast.texture(); 789 790 if (NULL == texture) { 791 return false; 792 } 793 texture->writePixels(0, 0, desc.fWidth, desc.fHeight, desc.fConfig, 794 dstM.fImage, dstM.fRowBytes); 795 796 SkRect maskRect = SkRect::Make(dstM.fBounds); 797 798 return draw_mask(context, maskRect, grp, texture); 799} 800 801// Create a mask of 'devPath' and place the result in 'mask'. Return true on 802// success; false otherwise. 803bool create_mask_GPU(GrContext* context, 804 const SkRect& maskRect, 805 const SkPath& devPath, 806 const SkStrokeRec& stroke, 807 bool doAA, 808 GrAutoScratchTexture* mask) { 809 GrTextureDesc desc; 810 desc.fFlags = kRenderTarget_GrTextureFlagBit; 811 desc.fWidth = SkScalarCeilToInt(maskRect.width()); 812 desc.fHeight = SkScalarCeilToInt(maskRect.height()); 813 // We actually only need A8, but it often isn't supported as a 814 // render target so default to RGBA_8888 815 desc.fConfig = kRGBA_8888_GrPixelConfig; 816 if (context->isConfigRenderable(kAlpha_8_GrPixelConfig, false)) { 817 desc.fConfig = kAlpha_8_GrPixelConfig; 818 } 819 820 mask->set(context, desc); 821 if (NULL == mask->texture()) { 822 return false; 823 } 824 825 GrTexture* maskTexture = mask->texture(); 826 SkRect clipRect = SkRect::MakeWH(maskRect.width(), maskRect.height()); 827 828 GrContext::AutoRenderTarget art(context, maskTexture->asRenderTarget()); 829 GrContext::AutoClip ac(context, clipRect); 830 831 context->clear(NULL, 0x0, true); 832 833 GrPaint tempPaint; 834 if (doAA) { 835 tempPaint.setAntiAlias(true); 836 // AA uses the "coverage" stages on GrDrawTarget. Coverage with a dst 837 // blend coeff of zero requires dual source blending support in order 838 // to properly blend partially covered pixels. This means the AA 839 // code path may not be taken. So we use a dst blend coeff of ISA. We 840 // could special case AA draws to a dst surface with known alpha=0 to 841 // use a zero dst coeff when dual source blending isn't available. 842 tempPaint.setBlendFunc(kOne_GrBlendCoeff, kISC_GrBlendCoeff); 843 } 844 845 GrContext::AutoMatrix am; 846 847 // Draw the mask into maskTexture with the path's top-left at the origin using tempPaint. 848 SkMatrix translate; 849 translate.setTranslate(-maskRect.fLeft, -maskRect.fTop); 850 am.set(context, translate); 851 context->drawPath(tempPaint, devPath, stroke); 852 return true; 853} 854 855SkBitmap wrap_texture(GrTexture* texture) { 856 SkImageInfo info; 857 texture->asImageInfo(&info); 858 859 SkBitmap result; 860 result.setConfig(info); 861 result.setPixelRef(SkNEW_ARGS(SkGrPixelRef, (info, texture)))->unref(); 862 return result; 863} 864 865}; 866 867void SkGpuDevice::drawPath(const SkDraw& draw, const SkPath& origSrcPath, 868 const SkPaint& paint, const SkMatrix* prePathMatrix, 869 bool pathIsMutable) { 870 CHECK_FOR_ANNOTATION(paint); 871 CHECK_SHOULD_DRAW(draw, false); 872 873 GrPaint grPaint; 874 if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) { 875 return; 876 } 877 878 // If we have a prematrix, apply it to the path, optimizing for the case 879 // where the original path can in fact be modified in place (even though 880 // its parameter type is const). 881 SkPath* pathPtr = const_cast<SkPath*>(&origSrcPath); 882 SkTLazy<SkPath> tmpPath; 883 SkTLazy<SkPath> effectPath; 884 885 if (prePathMatrix) { 886 SkPath* result = pathPtr; 887 888 if (!pathIsMutable) { 889 result = tmpPath.init(); 890 pathIsMutable = true; 891 } 892 // should I push prePathMatrix on our MV stack temporarily, instead 893 // of applying it here? See SkDraw.cpp 894 pathPtr->transform(*prePathMatrix, result); 895 pathPtr = result; 896 } 897 // at this point we're done with prePathMatrix 898 SkDEBUGCODE(prePathMatrix = (const SkMatrix*)0x50FF8001;) 899 900 SkStrokeRec stroke(paint); 901 SkPathEffect* pathEffect = paint.getPathEffect(); 902 const SkRect* cullRect = NULL; // TODO: what is our bounds? 903 if (pathEffect && pathEffect->filterPath(effectPath.init(), *pathPtr, &stroke, 904 cullRect)) { 905 pathPtr = effectPath.get(); 906 pathIsMutable = true; 907 } 908 909 if (paint.getMaskFilter()) { 910 if (!stroke.isHairlineStyle()) { 911 SkPath* strokedPath = pathIsMutable ? pathPtr : tmpPath.init(); 912 if (stroke.applyToPath(strokedPath, *pathPtr)) { 913 pathPtr = strokedPath; 914 pathIsMutable = true; 915 stroke.setFillStyle(); 916 } 917 } 918 919 // avoid possibly allocating a new path in transform if we can 920 SkPath* devPathPtr = pathIsMutable ? pathPtr : tmpPath.init(); 921 922 // transform the path into device space 923 pathPtr->transform(fContext->getMatrix(), devPathPtr); 924 925 SkRect maskRect; 926 if (paint.getMaskFilter()->canFilterMaskGPU(devPathPtr->getBounds(), 927 draw.fClip->getBounds(), 928 fContext->getMatrix(), 929 &maskRect)) { 930 // The context's matrix may change while creating the mask, so save the CTM here to 931 // pass to filterMaskGPU. 932 const SkMatrix ctm = fContext->getMatrix(); 933 934 SkIRect finalIRect; 935 maskRect.roundOut(&finalIRect); 936 if (draw.fClip->quickReject(finalIRect)) { 937 // clipped out 938 return; 939 } 940 if (NULL != draw.fBounder && !draw.fBounder->doIRect(finalIRect)) { 941 // nothing to draw 942 return; 943 } 944 945 if (paint.getMaskFilter()->directFilterMaskGPU(fContext, &grPaint, 946 stroke, *devPathPtr)) { 947 // the mask filter was able to draw itself directly, so there's nothing 948 // left to do. 949 return; 950 } 951 952 GrAutoScratchTexture mask; 953 954 if (create_mask_GPU(fContext, maskRect, *devPathPtr, stroke, 955 grPaint.isAntiAlias(), &mask)) { 956 GrTexture* filtered; 957 958 if (paint.getMaskFilter()->filterMaskGPU(mask.texture(), 959 ctm, maskRect, &filtered, true)) { 960 // filterMaskGPU gives us ownership of a ref to the result 961 SkAutoTUnref<GrTexture> atu(filtered); 962 963 // If the scratch texture that we used as the filter src also holds the filter 964 // result then we must detach so that this texture isn't recycled for a later 965 // draw. 966 if (filtered == mask.texture()) { 967 mask.detach(); 968 filtered->unref(); // detach transfers GrAutoScratchTexture's ref to us. 969 } 970 971 if (draw_mask(fContext, maskRect, &grPaint, filtered)) { 972 // This path is completely drawn 973 return; 974 } 975 } 976 } 977 } 978 979 // draw the mask on the CPU - this is a fallthrough path in case the 980 // GPU path fails 981 SkPaint::Style style = stroke.isHairlineStyle() ? SkPaint::kStroke_Style : 982 SkPaint::kFill_Style; 983 draw_with_mask_filter(fContext, *devPathPtr, paint.getMaskFilter(), 984 *draw.fClip, draw.fBounder, &grPaint, style); 985 return; 986 } 987 988 fContext->drawPath(grPaint, *pathPtr, stroke); 989} 990 991static const int kBmpSmallTileSize = 1 << 10; 992 993static inline int get_tile_count(const SkIRect& srcRect, int tileSize) { 994 int tilesX = (srcRect.fRight / tileSize) - (srcRect.fLeft / tileSize) + 1; 995 int tilesY = (srcRect.fBottom / tileSize) - (srcRect.fTop / tileSize) + 1; 996 return tilesX * tilesY; 997} 998 999static int determine_tile_size(const SkBitmap& bitmap, const SkIRect& src, int maxTileSize) { 1000 if (maxTileSize <= kBmpSmallTileSize) { 1001 return maxTileSize; 1002 } 1003 1004 size_t maxTileTotalTileSize = get_tile_count(src, maxTileSize); 1005 size_t smallTotalTileSize = get_tile_count(src, kBmpSmallTileSize); 1006 1007 maxTileTotalTileSize *= maxTileSize * maxTileSize; 1008 smallTotalTileSize *= kBmpSmallTileSize * kBmpSmallTileSize; 1009 1010 if (maxTileTotalTileSize > 2 * smallTotalTileSize) { 1011 return kBmpSmallTileSize; 1012 } else { 1013 return maxTileSize; 1014 } 1015} 1016 1017// Given a bitmap, an optional src rect, and a context with a clip and matrix determine what 1018// pixels from the bitmap are necessary. 1019static void determine_clipped_src_rect(const GrContext* context, 1020 const SkBitmap& bitmap, 1021 const SkRect* srcRectPtr, 1022 SkIRect* clippedSrcIRect) { 1023 const GrClipData* clip = context->getClip(); 1024 clip->getConservativeBounds(context->getRenderTarget(), clippedSrcIRect, NULL); 1025 SkMatrix inv; 1026 if (!context->getMatrix().invert(&inv)) { 1027 clippedSrcIRect->setEmpty(); 1028 return; 1029 } 1030 SkRect clippedSrcRect = SkRect::Make(*clippedSrcIRect); 1031 inv.mapRect(&clippedSrcRect); 1032 if (NULL != srcRectPtr) { 1033 // we've setup src space 0,0 to map to the top left of the src rect. 1034 clippedSrcRect.offset(srcRectPtr->fLeft, srcRectPtr->fTop); 1035 if (!clippedSrcRect.intersect(*srcRectPtr)) { 1036 clippedSrcIRect->setEmpty(); 1037 return; 1038 } 1039 } 1040 clippedSrcRect.roundOut(clippedSrcIRect); 1041 SkIRect bmpBounds = SkIRect::MakeWH(bitmap.width(), bitmap.height()); 1042 if (!clippedSrcIRect->intersect(bmpBounds)) { 1043 clippedSrcIRect->setEmpty(); 1044 } 1045} 1046 1047bool SkGpuDevice::shouldTileBitmap(const SkBitmap& bitmap, 1048 const GrTextureParams& params, 1049 const SkRect* srcRectPtr, 1050 int maxTileSize, 1051 int* tileSize, 1052 SkIRect* clippedSrcRect) const { 1053 // if bitmap is explictly texture backed then just use the texture 1054 if (NULL != bitmap.getTexture()) { 1055 return false; 1056 } 1057 1058 // if it's larger than the max tile size, then we have no choice but tiling. 1059 if (bitmap.width() > maxTileSize || bitmap.height() > maxTileSize) { 1060 determine_clipped_src_rect(fContext, bitmap, srcRectPtr, clippedSrcRect); 1061 *tileSize = determine_tile_size(bitmap, *clippedSrcRect, maxTileSize); 1062 return true; 1063 } 1064 1065 if (bitmap.width() * bitmap.height() < 4 * kBmpSmallTileSize * kBmpSmallTileSize) { 1066 return false; 1067 } 1068 1069 // if the entire texture is already in our cache then no reason to tile it 1070 if (GrIsBitmapInCache(fContext, bitmap, ¶ms)) { 1071 return false; 1072 } 1073 1074 // At this point we know we could do the draw by uploading the entire bitmap 1075 // as a texture. However, if the texture would be large compared to the 1076 // cache size and we don't require most of it for this draw then tile to 1077 // reduce the amount of upload and cache spill. 1078 1079 // assumption here is that sw bitmap size is a good proxy for its size as 1080 // a texture 1081 size_t bmpSize = bitmap.getSize(); 1082 size_t cacheSize; 1083 fContext->getTextureCacheLimits(NULL, &cacheSize); 1084 if (bmpSize < cacheSize / 2) { 1085 return false; 1086 } 1087 1088 // Figure out how much of the src we will need based on the src rect and clipping. 1089 determine_clipped_src_rect(fContext, bitmap, srcRectPtr, clippedSrcRect); 1090 *tileSize = kBmpSmallTileSize; // already know whole bitmap fits in one max sized tile. 1091 size_t usedTileBytes = get_tile_count(*clippedSrcRect, kBmpSmallTileSize) * 1092 kBmpSmallTileSize * kBmpSmallTileSize; 1093 1094 return usedTileBytes < 2 * bmpSize; 1095} 1096 1097void SkGpuDevice::drawBitmap(const SkDraw& origDraw, 1098 const SkBitmap& bitmap, 1099 const SkMatrix& m, 1100 const SkPaint& paint) { 1101 SkMatrix concat; 1102 SkTCopyOnFirstWrite<SkDraw> draw(origDraw); 1103 if (!m.isIdentity()) { 1104 concat.setConcat(*draw->fMatrix, m); 1105 draw.writable()->fMatrix = &concat; 1106 } 1107 this->drawBitmapCommon(*draw, bitmap, NULL, NULL, paint, SkCanvas::kNone_DrawBitmapRectFlag); 1108} 1109 1110// This method outsets 'iRect' by 'outset' all around and then clamps its extents to 1111// 'clamp'. 'offset' is adjusted to remain positioned over the top-left corner 1112// of 'iRect' for all possible outsets/clamps. 1113static inline void clamped_outset_with_offset(SkIRect* iRect, 1114 int outset, 1115 SkPoint* offset, 1116 const SkIRect& clamp) { 1117 iRect->outset(outset, outset); 1118 1119 int leftClampDelta = clamp.fLeft - iRect->fLeft; 1120 if (leftClampDelta > 0) { 1121 offset->fX -= outset - leftClampDelta; 1122 iRect->fLeft = clamp.fLeft; 1123 } else { 1124 offset->fX -= outset; 1125 } 1126 1127 int topClampDelta = clamp.fTop - iRect->fTop; 1128 if (topClampDelta > 0) { 1129 offset->fY -= outset - topClampDelta; 1130 iRect->fTop = clamp.fTop; 1131 } else { 1132 offset->fY -= outset; 1133 } 1134 1135 if (iRect->fRight > clamp.fRight) { 1136 iRect->fRight = clamp.fRight; 1137 } 1138 if (iRect->fBottom > clamp.fBottom) { 1139 iRect->fBottom = clamp.fBottom; 1140 } 1141} 1142 1143void SkGpuDevice::drawBitmapCommon(const SkDraw& draw, 1144 const SkBitmap& bitmap, 1145 const SkRect* srcRectPtr, 1146 const SkSize* dstSizePtr, 1147 const SkPaint& paint, 1148 SkCanvas::DrawBitmapRectFlags flags) { 1149 CHECK_SHOULD_DRAW(draw, false); 1150 1151 SkRect srcRect; 1152 SkSize dstSize; 1153 // If there is no src rect, or the src rect contains the entire bitmap then we're effectively 1154 // in the (easier) bleed case, so update flags. 1155 if (NULL == srcRectPtr) { 1156 SkScalar w = SkIntToScalar(bitmap.width()); 1157 SkScalar h = SkIntToScalar(bitmap.height()); 1158 dstSize.fWidth = w; 1159 dstSize.fHeight = h; 1160 srcRect.set(0, 0, w, h); 1161 flags = (SkCanvas::DrawBitmapRectFlags) (flags | SkCanvas::kBleed_DrawBitmapRectFlag); 1162 } else { 1163 SkASSERT(NULL != dstSizePtr); 1164 srcRect = *srcRectPtr; 1165 dstSize = *dstSizePtr; 1166 if (srcRect.fLeft <= 0 && srcRect.fTop <= 0 && 1167 srcRect.fRight >= bitmap.width() && srcRect.fBottom >= bitmap.height()) { 1168 flags = (SkCanvas::DrawBitmapRectFlags) (flags | SkCanvas::kBleed_DrawBitmapRectFlag); 1169 } 1170 } 1171 1172 if (paint.getMaskFilter()){ 1173 // Convert the bitmap to a shader so that the rect can be drawn 1174 // through drawRect, which supports mask filters. 1175 SkBitmap tmp; // subset of bitmap, if necessary 1176 const SkBitmap* bitmapPtr = &bitmap; 1177 SkMatrix localM; 1178 if (NULL != srcRectPtr) { 1179 localM.setTranslate(-srcRectPtr->fLeft, -srcRectPtr->fTop); 1180 localM.postScale(dstSize.fWidth / srcRectPtr->width(), 1181 dstSize.fHeight / srcRectPtr->height()); 1182 // In bleed mode we position and trim the bitmap based on the src rect which is 1183 // already accounted for in 'm' and 'srcRect'. In clamp mode we need to chop out 1184 // the desired portion of the bitmap and then update 'm' and 'srcRect' to 1185 // compensate. 1186 if (!(SkCanvas::kBleed_DrawBitmapRectFlag & flags)) { 1187 SkIRect iSrc; 1188 srcRect.roundOut(&iSrc); 1189 1190 SkPoint offset = SkPoint::Make(SkIntToScalar(iSrc.fLeft), 1191 SkIntToScalar(iSrc.fTop)); 1192 1193 if (!bitmap.extractSubset(&tmp, iSrc)) { 1194 return; // extraction failed 1195 } 1196 bitmapPtr = &tmp; 1197 srcRect.offset(-offset.fX, -offset.fY); 1198 1199 // The source rect has changed so update the matrix 1200 localM.preTranslate(offset.fX, offset.fY); 1201 } 1202 } else { 1203 localM.reset(); 1204 } 1205 1206 SkPaint paintWithShader(paint); 1207 paintWithShader.setShader(SkShader::CreateBitmapShader(*bitmapPtr, 1208 SkShader::kClamp_TileMode, SkShader::kClamp_TileMode, &localM))->unref(); 1209 SkRect dstRect = {0, 0, dstSize.fWidth, dstSize.fHeight}; 1210 this->drawRect(draw, dstRect, paintWithShader); 1211 1212 return; 1213 } 1214 1215 // If there is no mask filter than it is OK to handle the src rect -> dst rect scaling using 1216 // the view matrix rather than a local matrix. 1217 SkMatrix m; 1218 m.setScale(dstSize.fWidth / srcRect.width(), 1219 dstSize.fHeight / srcRect.height()); 1220 fContext->concatMatrix(m); 1221 1222 GrTextureParams params; 1223 SkPaint::FilterLevel paintFilterLevel = paint.getFilterLevel(); 1224 GrTextureParams::FilterMode textureFilterMode; 1225 1226 int tileFilterPad; 1227 bool doBicubic = false; 1228 1229 switch(paintFilterLevel) { 1230 case SkPaint::kNone_FilterLevel: 1231 tileFilterPad = 0; 1232 textureFilterMode = GrTextureParams::kNone_FilterMode; 1233 break; 1234 case SkPaint::kLow_FilterLevel: 1235 tileFilterPad = 1; 1236 textureFilterMode = GrTextureParams::kBilerp_FilterMode; 1237 break; 1238 case SkPaint::kMedium_FilterLevel: 1239 tileFilterPad = 1; 1240 if (fContext->getMatrix().getMinStretch() < SK_Scalar1) { 1241 textureFilterMode = GrTextureParams::kMipMap_FilterMode; 1242 } else { 1243 // Don't trigger MIP level generation unnecessarily. 1244 textureFilterMode = GrTextureParams::kBilerp_FilterMode; 1245 } 1246 break; 1247 case SkPaint::kHigh_FilterLevel: 1248 // Minification can look bad with the bicubic effect. 1249 if (fContext->getMatrix().getMinStretch() >= SK_Scalar1) { 1250 // We will install an effect that does the filtering in the shader. 1251 textureFilterMode = GrTextureParams::kNone_FilterMode; 1252 tileFilterPad = GrBicubicEffect::kFilterTexelPad; 1253 doBicubic = true; 1254 } else { 1255 textureFilterMode = GrTextureParams::kMipMap_FilterMode; 1256 tileFilterPad = 1; 1257 } 1258 break; 1259 default: 1260 SkErrorInternals::SetError( kInvalidPaint_SkError, 1261 "Sorry, I don't understand the filtering " 1262 "mode you asked for. Falling back to " 1263 "MIPMaps."); 1264 tileFilterPad = 1; 1265 textureFilterMode = GrTextureParams::kMipMap_FilterMode; 1266 break; 1267 } 1268 1269 params.setFilterMode(textureFilterMode); 1270 1271 int maxTileSize = fContext->getMaxTextureSize() - 2 * tileFilterPad; 1272 int tileSize; 1273 1274 SkIRect clippedSrcRect; 1275 if (this->shouldTileBitmap(bitmap, params, srcRectPtr, maxTileSize, &tileSize, 1276 &clippedSrcRect)) { 1277 this->drawTiledBitmap(bitmap, srcRect, clippedSrcRect, params, paint, flags, tileSize, 1278 doBicubic); 1279 } else { 1280 // take the simple case 1281 this->internalDrawBitmap(bitmap, srcRect, params, paint, flags, doBicubic); 1282 } 1283} 1284 1285// Break 'bitmap' into several tiles to draw it since it has already 1286// been determined to be too large to fit in VRAM 1287void SkGpuDevice::drawTiledBitmap(const SkBitmap& bitmap, 1288 const SkRect& srcRect, 1289 const SkIRect& clippedSrcIRect, 1290 const GrTextureParams& params, 1291 const SkPaint& paint, 1292 SkCanvas::DrawBitmapRectFlags flags, 1293 int tileSize, 1294 bool bicubic) { 1295 // The following pixel lock is technically redundant, but it is desirable 1296 // to lock outside of the tile loop to prevent redecoding the whole image 1297 // at each tile in cases where 'bitmap' holds an SkDiscardablePixelRef that 1298 // is larger than the limit of the discardable memory pool. 1299 SkAutoLockPixels alp(bitmap); 1300 SkRect clippedSrcRect = SkRect::Make(clippedSrcIRect); 1301 1302 int nx = bitmap.width() / tileSize; 1303 int ny = bitmap.height() / tileSize; 1304 for (int x = 0; x <= nx; x++) { 1305 for (int y = 0; y <= ny; y++) { 1306 SkRect tileR; 1307 tileR.set(SkIntToScalar(x * tileSize), 1308 SkIntToScalar(y * tileSize), 1309 SkIntToScalar((x + 1) * tileSize), 1310 SkIntToScalar((y + 1) * tileSize)); 1311 1312 if (!SkRect::Intersects(tileR, clippedSrcRect)) { 1313 continue; 1314 } 1315 1316 if (!tileR.intersect(srcRect)) { 1317 continue; 1318 } 1319 1320 SkBitmap tmpB; 1321 SkIRect iTileR; 1322 tileR.roundOut(&iTileR); 1323 SkPoint offset = SkPoint::Make(SkIntToScalar(iTileR.fLeft), 1324 SkIntToScalar(iTileR.fTop)); 1325 1326 // Adjust the context matrix to draw at the right x,y in device space 1327 SkMatrix tmpM; 1328 GrContext::AutoMatrix am; 1329 tmpM.setTranslate(offset.fX - srcRect.fLeft, offset.fY - srcRect.fTop); 1330 am.setPreConcat(fContext, tmpM); 1331 1332 if (SkPaint::kNone_FilterLevel != paint.getFilterLevel() || bicubic) { 1333 SkIRect iClampRect; 1334 1335 if (SkCanvas::kBleed_DrawBitmapRectFlag & flags) { 1336 // In bleed mode we want to always expand the tile on all edges 1337 // but stay within the bitmap bounds 1338 iClampRect = SkIRect::MakeWH(bitmap.width(), bitmap.height()); 1339 } else { 1340 // In texture-domain/clamp mode we only want to expand the 1341 // tile on edges interior to "srcRect" (i.e., we want to 1342 // not bleed across the original clamped edges) 1343 srcRect.roundOut(&iClampRect); 1344 } 1345 int outset = bicubic ? GrBicubicEffect::kFilterTexelPad : 1; 1346 clamped_outset_with_offset(&iTileR, outset, &offset, iClampRect); 1347 } 1348 1349 if (bitmap.extractSubset(&tmpB, iTileR)) { 1350 // now offset it to make it "local" to our tmp bitmap 1351 tileR.offset(-offset.fX, -offset.fY); 1352 1353 this->internalDrawBitmap(tmpB, tileR, params, paint, flags, bicubic); 1354 } 1355 } 1356 } 1357} 1358 1359static bool has_aligned_samples(const SkRect& srcRect, 1360 const SkRect& transformedRect) { 1361 // detect pixel disalignment 1362 if (SkScalarAbs(SkScalarRoundToScalar(transformedRect.left()) - 1363 transformedRect.left()) < COLOR_BLEED_TOLERANCE && 1364 SkScalarAbs(SkScalarRoundToScalar(transformedRect.top()) - 1365 transformedRect.top()) < COLOR_BLEED_TOLERANCE && 1366 SkScalarAbs(transformedRect.width() - srcRect.width()) < 1367 COLOR_BLEED_TOLERANCE && 1368 SkScalarAbs(transformedRect.height() - srcRect.height()) < 1369 COLOR_BLEED_TOLERANCE) { 1370 return true; 1371 } 1372 return false; 1373} 1374 1375static bool may_color_bleed(const SkRect& srcRect, 1376 const SkRect& transformedRect, 1377 const SkMatrix& m) { 1378 // Only gets called if has_aligned_samples returned false. 1379 // So we can assume that sampling is axis aligned but not texel aligned. 1380 SkASSERT(!has_aligned_samples(srcRect, transformedRect)); 1381 SkRect innerSrcRect(srcRect), innerTransformedRect, 1382 outerTransformedRect(transformedRect); 1383 innerSrcRect.inset(SK_ScalarHalf, SK_ScalarHalf); 1384 m.mapRect(&innerTransformedRect, innerSrcRect); 1385 1386 // The gap between outerTransformedRect and innerTransformedRect 1387 // represents the projection of the source border area, which is 1388 // problematic for color bleeding. We must check whether any 1389 // destination pixels sample the border area. 1390 outerTransformedRect.inset(COLOR_BLEED_TOLERANCE, COLOR_BLEED_TOLERANCE); 1391 innerTransformedRect.outset(COLOR_BLEED_TOLERANCE, COLOR_BLEED_TOLERANCE); 1392 SkIRect outer, inner; 1393 outerTransformedRect.round(&outer); 1394 innerTransformedRect.round(&inner); 1395 // If the inner and outer rects round to the same result, it means the 1396 // border does not overlap any pixel centers. Yay! 1397 return inner != outer; 1398} 1399 1400 1401/* 1402 * This is called by drawBitmap(), which has to handle images that may be too 1403 * large to be represented by a single texture. 1404 * 1405 * internalDrawBitmap assumes that the specified bitmap will fit in a texture 1406 * and that non-texture portion of the GrPaint has already been setup. 1407 */ 1408void SkGpuDevice::internalDrawBitmap(const SkBitmap& bitmap, 1409 const SkRect& srcRect, 1410 const GrTextureParams& params, 1411 const SkPaint& paint, 1412 SkCanvas::DrawBitmapRectFlags flags, 1413 bool bicubic) { 1414 SkASSERT(bitmap.width() <= fContext->getMaxTextureSize() && 1415 bitmap.height() <= fContext->getMaxTextureSize()); 1416 1417 GrTexture* texture; 1418 SkAutoCachedTexture act(this, bitmap, ¶ms, &texture); 1419 if (NULL == texture) { 1420 return; 1421 } 1422 1423 SkRect dstRect = {0, 0, srcRect.width(), srcRect.height() }; 1424 SkRect paintRect; 1425 SkScalar wInv = SkScalarInvert(SkIntToScalar(texture->width())); 1426 SkScalar hInv = SkScalarInvert(SkIntToScalar(texture->height())); 1427 paintRect.setLTRB(SkScalarMul(srcRect.fLeft, wInv), 1428 SkScalarMul(srcRect.fTop, hInv), 1429 SkScalarMul(srcRect.fRight, wInv), 1430 SkScalarMul(srcRect.fBottom, hInv)); 1431 1432 bool needsTextureDomain = false; 1433 if (!(flags & SkCanvas::kBleed_DrawBitmapRectFlag) && 1434 (bicubic || params.filterMode() != GrTextureParams::kNone_FilterMode)) { 1435 // Need texture domain if drawing a sub rect 1436 needsTextureDomain = srcRect.width() < bitmap.width() || 1437 srcRect.height() < bitmap.height(); 1438 if (!bicubic && needsTextureDomain && fContext->getMatrix().rectStaysRect()) { 1439 const SkMatrix& matrix = fContext->getMatrix(); 1440 // sampling is axis-aligned 1441 SkRect transformedRect; 1442 matrix.mapRect(&transformedRect, srcRect); 1443 1444 if (has_aligned_samples(srcRect, transformedRect)) { 1445 // We could also turn off filtering here (but we already did a cache lookup with 1446 // params). 1447 needsTextureDomain = false; 1448 } else { 1449 needsTextureDomain = may_color_bleed(srcRect, transformedRect, matrix); 1450 } 1451 } 1452 } 1453 1454 SkRect textureDomain = SkRect::MakeEmpty(); 1455 SkAutoTUnref<GrEffectRef> effect; 1456 if (needsTextureDomain) { 1457 // Use a constrained texture domain to avoid color bleeding 1458 SkScalar left, top, right, bottom; 1459 if (srcRect.width() > SK_Scalar1) { 1460 SkScalar border = SK_ScalarHalf / texture->width(); 1461 left = paintRect.left() + border; 1462 right = paintRect.right() - border; 1463 } else { 1464 left = right = SkScalarHalf(paintRect.left() + paintRect.right()); 1465 } 1466 if (srcRect.height() > SK_Scalar1) { 1467 SkScalar border = SK_ScalarHalf / texture->height(); 1468 top = paintRect.top() + border; 1469 bottom = paintRect.bottom() - border; 1470 } else { 1471 top = bottom = SkScalarHalf(paintRect.top() + paintRect.bottom()); 1472 } 1473 textureDomain.setLTRB(left, top, right, bottom); 1474 if (bicubic) { 1475 effect.reset(GrBicubicEffect::Create(texture, SkMatrix::I(), textureDomain)); 1476 } else { 1477 effect.reset(GrTextureDomainEffect::Create(texture, 1478 SkMatrix::I(), 1479 textureDomain, 1480 GrTextureDomain::kClamp_Mode, 1481 params.filterMode())); 1482 } 1483 } else if (bicubic) { 1484 SkASSERT(GrTextureParams::kNone_FilterMode == params.filterMode()); 1485 SkShader::TileMode tileModes[2] = { params.getTileModeX(), params.getTileModeY() }; 1486 effect.reset(GrBicubicEffect::Create(texture, SkMatrix::I(), tileModes)); 1487 } else { 1488 effect.reset(GrSimpleTextureEffect::Create(texture, SkMatrix::I(), params)); 1489 } 1490 1491 // Construct a GrPaint by setting the bitmap texture as the first effect and then configuring 1492 // the rest from the SkPaint. 1493 GrPaint grPaint; 1494 grPaint.addColorEffect(effect); 1495 bool alphaOnly = !(SkBitmap::kA8_Config == bitmap.config()); 1496 if (!skPaint2GrPaintNoShader(this, paint, alphaOnly, false, &grPaint)) { 1497 return; 1498 } 1499 1500 fContext->drawRectToRect(grPaint, dstRect, paintRect, NULL); 1501} 1502 1503static bool filter_texture(SkBaseDevice* device, GrContext* context, 1504 GrTexture* texture, const SkImageFilter* filter, 1505 int w, int h, const SkImageFilter::Context& ctx, 1506 SkBitmap* result, SkIPoint* offset) { 1507 SkASSERT(filter); 1508 SkDeviceImageFilterProxy proxy(device); 1509 1510 if (filter->canFilterImageGPU()) { 1511 // Save the render target and set it to NULL, so we don't accidentally draw to it in the 1512 // filter. Also set the clip wide open and the matrix to identity. 1513 GrContext::AutoWideOpenIdentityDraw awo(context, NULL); 1514 return filter->filterImageGPU(&proxy, wrap_texture(texture), ctx, result, offset); 1515 } else { 1516 return false; 1517 } 1518} 1519 1520void SkGpuDevice::drawSprite(const SkDraw& draw, const SkBitmap& bitmap, 1521 int left, int top, const SkPaint& paint) { 1522 // drawSprite is defined to be in device coords. 1523 CHECK_SHOULD_DRAW(draw, true); 1524 1525 SkAutoLockPixels alp(bitmap, !bitmap.getTexture()); 1526 if (!bitmap.getTexture() && !bitmap.readyToDraw()) { 1527 return; 1528 } 1529 1530 int w = bitmap.width(); 1531 int h = bitmap.height(); 1532 1533 GrTexture* texture; 1534 // draw sprite uses the default texture params 1535 SkAutoCachedTexture act(this, bitmap, NULL, &texture); 1536 1537 SkImageFilter* filter = paint.getImageFilter(); 1538 // This bitmap will own the filtered result as a texture. 1539 SkBitmap filteredBitmap; 1540 1541 if (NULL != filter) { 1542 SkIPoint offset = SkIPoint::Make(0, 0); 1543 SkMatrix matrix(*draw.fMatrix); 1544 matrix.postTranslate(SkIntToScalar(-left), SkIntToScalar(-top)); 1545 SkIRect clipBounds = SkIRect::MakeWH(bitmap.width(), bitmap.height()); 1546 SkImageFilter::Cache* cache = SkImageFilter::Cache::Create(); 1547 SkAutoUnref aur(cache); 1548 SkImageFilter::Context ctx(matrix, clipBounds, cache); 1549 if (filter_texture(this, fContext, texture, filter, w, h, ctx, &filteredBitmap, 1550 &offset)) { 1551 texture = (GrTexture*) filteredBitmap.getTexture(); 1552 w = filteredBitmap.width(); 1553 h = filteredBitmap.height(); 1554 left += offset.x(); 1555 top += offset.y(); 1556 } else { 1557 return; 1558 } 1559 } 1560 1561 GrPaint grPaint; 1562 grPaint.addColorTextureEffect(texture, SkMatrix::I()); 1563 1564 if(!skPaint2GrPaintNoShader(this, paint, true, false, &grPaint)) { 1565 return; 1566 } 1567 1568 fContext->drawRectToRect(grPaint, 1569 SkRect::MakeXYWH(SkIntToScalar(left), 1570 SkIntToScalar(top), 1571 SkIntToScalar(w), 1572 SkIntToScalar(h)), 1573 SkRect::MakeXYWH(0, 1574 0, 1575 SK_Scalar1 * w / texture->width(), 1576 SK_Scalar1 * h / texture->height())); 1577} 1578 1579void SkGpuDevice::drawBitmapRect(const SkDraw& origDraw, const SkBitmap& bitmap, 1580 const SkRect* src, const SkRect& dst, 1581 const SkPaint& paint, 1582 SkCanvas::DrawBitmapRectFlags flags) { 1583 SkMatrix matrix; 1584 SkRect bitmapBounds, tmpSrc; 1585 1586 bitmapBounds.set(0, 0, 1587 SkIntToScalar(bitmap.width()), 1588 SkIntToScalar(bitmap.height())); 1589 1590 // Compute matrix from the two rectangles 1591 if (NULL != src) { 1592 tmpSrc = *src; 1593 } else { 1594 tmpSrc = bitmapBounds; 1595 } 1596 1597 matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit); 1598 1599 // clip the tmpSrc to the bounds of the bitmap. No check needed if src==null. 1600 if (NULL != src) { 1601 if (!bitmapBounds.contains(tmpSrc)) { 1602 if (!tmpSrc.intersect(bitmapBounds)) { 1603 return; // nothing to draw 1604 } 1605 } 1606 } 1607 1608 SkRect tmpDst; 1609 matrix.mapRect(&tmpDst, tmpSrc); 1610 1611 SkTCopyOnFirstWrite<SkDraw> draw(origDraw); 1612 if (0 != tmpDst.fLeft || 0 != tmpDst.fTop) { 1613 // Translate so that tempDst's top left is at the origin. 1614 matrix = *origDraw.fMatrix; 1615 matrix.preTranslate(tmpDst.fLeft, tmpDst.fTop); 1616 draw.writable()->fMatrix = &matrix; 1617 } 1618 SkSize dstSize; 1619 dstSize.fWidth = tmpDst.width(); 1620 dstSize.fHeight = tmpDst.height(); 1621 1622 this->drawBitmapCommon(*draw, bitmap, &tmpSrc, &dstSize, paint, flags); 1623} 1624 1625void SkGpuDevice::drawDevice(const SkDraw& draw, SkBaseDevice* device, 1626 int x, int y, const SkPaint& paint) { 1627 // clear of the source device must occur before CHECK_SHOULD_DRAW 1628 SkGpuDevice* dev = static_cast<SkGpuDevice*>(device); 1629 if (dev->fNeedClear) { 1630 // TODO: could check here whether we really need to draw at all 1631 dev->clear(0x0); 1632 } 1633 1634 // drawDevice is defined to be in device coords. 1635 CHECK_SHOULD_DRAW(draw, true); 1636 1637 GrRenderTarget* devRT = dev->accessRenderTarget(); 1638 GrTexture* devTex; 1639 if (NULL == (devTex = devRT->asTexture())) { 1640 return; 1641 } 1642 1643 const SkBitmap& bm = dev->accessBitmap(false); 1644 int w = bm.width(); 1645 int h = bm.height(); 1646 1647 SkImageFilter* filter = paint.getImageFilter(); 1648 // This bitmap will own the filtered result as a texture. 1649 SkBitmap filteredBitmap; 1650 1651 if (NULL != filter) { 1652 SkIPoint offset = SkIPoint::Make(0, 0); 1653 SkMatrix matrix(*draw.fMatrix); 1654 matrix.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y)); 1655 SkIRect clipBounds = SkIRect::MakeWH(devTex->width(), devTex->height()); 1656 SkImageFilter::Cache* cache = SkImageFilter::Cache::Create(); 1657 SkAutoUnref aur(cache); 1658 SkImageFilter::Context ctx(matrix, clipBounds, cache); 1659 if (filter_texture(this, fContext, devTex, filter, w, h, ctx, &filteredBitmap, 1660 &offset)) { 1661 devTex = filteredBitmap.getTexture(); 1662 w = filteredBitmap.width(); 1663 h = filteredBitmap.height(); 1664 x += offset.fX; 1665 y += offset.fY; 1666 } else { 1667 return; 1668 } 1669 } 1670 1671 GrPaint grPaint; 1672 grPaint.addColorTextureEffect(devTex, SkMatrix::I()); 1673 1674 if (!skPaint2GrPaintNoShader(this, paint, true, false, &grPaint)) { 1675 return; 1676 } 1677 1678 SkRect dstRect = SkRect::MakeXYWH(SkIntToScalar(x), 1679 SkIntToScalar(y), 1680 SkIntToScalar(w), 1681 SkIntToScalar(h)); 1682 1683 // The device being drawn may not fill up its texture (e.g. saveLayer uses approximate 1684 // scratch texture). 1685 SkRect srcRect = SkRect::MakeWH(SK_Scalar1 * w / devTex->width(), 1686 SK_Scalar1 * h / devTex->height()); 1687 1688 fContext->drawRectToRect(grPaint, dstRect, srcRect); 1689} 1690 1691bool SkGpuDevice::canHandleImageFilter(const SkImageFilter* filter) { 1692 return filter->canFilterImageGPU(); 1693} 1694 1695bool SkGpuDevice::filterImage(const SkImageFilter* filter, const SkBitmap& src, 1696 const SkImageFilter::Context& ctx, 1697 SkBitmap* result, SkIPoint* offset) { 1698 // want explicitly our impl, so guard against a subclass of us overriding it 1699 if (!this->SkGpuDevice::canHandleImageFilter(filter)) { 1700 return false; 1701 } 1702 1703 SkAutoLockPixels alp(src, !src.getTexture()); 1704 if (!src.getTexture() && !src.readyToDraw()) { 1705 return false; 1706 } 1707 1708 GrTexture* texture; 1709 // We assume here that the filter will not attempt to tile the src. Otherwise, this cache lookup 1710 // must be pushed upstack. 1711 SkAutoCachedTexture act(this, src, NULL, &texture); 1712 1713 return filter_texture(this, fContext, texture, filter, src.width(), src.height(), ctx, 1714 result, offset); 1715} 1716 1717/////////////////////////////////////////////////////////////////////////////// 1718 1719// must be in SkCanvas::VertexMode order 1720static const GrPrimitiveType gVertexMode2PrimitiveType[] = { 1721 kTriangles_GrPrimitiveType, 1722 kTriangleStrip_GrPrimitiveType, 1723 kTriangleFan_GrPrimitiveType, 1724}; 1725 1726void SkGpuDevice::drawVertices(const SkDraw& draw, SkCanvas::VertexMode vmode, 1727 int vertexCount, const SkPoint vertices[], 1728 const SkPoint texs[], const SkColor colors[], 1729 SkXfermode* xmode, 1730 const uint16_t indices[], int indexCount, 1731 const SkPaint& paint) { 1732 CHECK_SHOULD_DRAW(draw, false); 1733 1734 GrPaint grPaint; 1735 // we ignore the shader if texs is null. 1736 if (NULL == texs) { 1737 if (!skPaint2GrPaintNoShader(this, paint, false, NULL == colors, &grPaint)) { 1738 return; 1739 } 1740 } else { 1741 if (!skPaint2GrPaintShader(this, paint, NULL == colors, &grPaint)) { 1742 return; 1743 } 1744 } 1745 1746 if (NULL != xmode && NULL != texs && NULL != colors) { 1747 if (!SkXfermode::IsMode(xmode, SkXfermode::kModulate_Mode)) { 1748 SkDebugf("Unsupported vertex-color/texture xfer mode.\n"); 1749#if 0 1750 return 1751#endif 1752 } 1753 } 1754 1755 SkAutoSTMalloc<128, GrColor> convertedColors(0); 1756 if (NULL != colors) { 1757 // need to convert byte order and from non-PM to PM 1758 convertedColors.reset(vertexCount); 1759 for (int i = 0; i < vertexCount; ++i) { 1760 convertedColors[i] = SkColor2GrColor(colors[i]); 1761 } 1762 colors = convertedColors.get(); 1763 } 1764 fContext->drawVertices(grPaint, 1765 gVertexMode2PrimitiveType[vmode], 1766 vertexCount, 1767 vertices, 1768 texs, 1769 colors, 1770 indices, 1771 indexCount); 1772} 1773 1774/////////////////////////////////////////////////////////////////////////////// 1775 1776void SkGpuDevice::drawText(const SkDraw& draw, const void* text, 1777 size_t byteLength, SkScalar x, SkScalar y, 1778 const SkPaint& paint) { 1779 CHECK_SHOULD_DRAW(draw, false); 1780 1781 if (fMainTextContext->canDraw(paint)) { 1782 GrPaint grPaint; 1783 if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) { 1784 return; 1785 } 1786 1787 SkDEBUGCODE(this->validate();) 1788 1789 fMainTextContext->drawText(grPaint, paint, (const char *)text, byteLength, x, y); 1790 } else if (fFallbackTextContext && fFallbackTextContext->canDraw(paint)) { 1791 GrPaint grPaint; 1792 if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) { 1793 return; 1794 } 1795 1796 SkDEBUGCODE(this->validate();) 1797 1798 fFallbackTextContext->drawText(grPaint, paint, (const char *)text, byteLength, x, y); 1799 } else { 1800 // this guy will just call our drawPath() 1801 draw.drawText_asPaths((const char*)text, byteLength, x, y, paint); 1802 } 1803} 1804 1805void SkGpuDevice::drawPosText(const SkDraw& draw, const void* text, 1806 size_t byteLength, const SkScalar pos[], 1807 SkScalar constY, int scalarsPerPos, 1808 const SkPaint& paint) { 1809 CHECK_SHOULD_DRAW(draw, false); 1810 1811 if (fMainTextContext->canDraw(paint)) { 1812 GrPaint grPaint; 1813 if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) { 1814 return; 1815 } 1816 1817 SkDEBUGCODE(this->validate();) 1818 1819 fMainTextContext->drawPosText(grPaint, paint, (const char *)text, byteLength, pos, 1820 constY, scalarsPerPos); 1821 } else if (fFallbackTextContext && fFallbackTextContext->canDraw(paint)) { 1822 GrPaint grPaint; 1823 if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) { 1824 return; 1825 } 1826 1827 SkDEBUGCODE(this->validate();) 1828 1829 fFallbackTextContext->drawPosText(grPaint, paint, (const char *)text, byteLength, pos, 1830 constY, scalarsPerPos); 1831 } else { 1832 draw.drawPosText_asPaths((const char*)text, byteLength, pos, constY, 1833 scalarsPerPos, paint); 1834 } 1835} 1836 1837void SkGpuDevice::drawTextOnPath(const SkDraw& draw, const void* text, 1838 size_t len, const SkPath& path, 1839 const SkMatrix* m, const SkPaint& paint) { 1840 CHECK_SHOULD_DRAW(draw, false); 1841 1842 SkASSERT(draw.fDevice == this); 1843 draw.drawTextOnPath((const char*)text, len, path, m, paint); 1844} 1845 1846/////////////////////////////////////////////////////////////////////////////// 1847 1848bool SkGpuDevice::filterTextFlags(const SkPaint& paint, TextFlags* flags) { 1849 if (!paint.isLCDRenderText()) { 1850 // we're cool with the paint as is 1851 return false; 1852 } 1853 1854 if (paint.getShader() || 1855 paint.getXfermode() || // unless its srcover 1856 paint.getMaskFilter() || 1857 paint.getRasterizer() || 1858 paint.getColorFilter() || 1859 paint.getPathEffect() || 1860 paint.isFakeBoldText() || 1861 paint.getStyle() != SkPaint::kFill_Style) { 1862 // turn off lcd 1863 flags->fFlags = paint.getFlags() & ~SkPaint::kLCDRenderText_Flag; 1864 flags->fHinting = paint.getHinting(); 1865 return true; 1866 } 1867 // we're cool with the paint as is 1868 return false; 1869} 1870 1871void SkGpuDevice::flush() { 1872 DO_DEFERRED_CLEAR(); 1873 fContext->resolveRenderTarget(fRenderTarget); 1874} 1875 1876/////////////////////////////////////////////////////////////////////////////// 1877 1878SkBaseDevice* SkGpuDevice::onCreateDevice(const SkImageInfo& info, Usage usage) { 1879 GrTextureDesc desc; 1880 desc.fConfig = fRenderTarget->config(); 1881 desc.fFlags = kRenderTarget_GrTextureFlagBit; 1882 desc.fWidth = info.width(); 1883 desc.fHeight = info.height(); 1884 desc.fSampleCnt = fRenderTarget->numSamples(); 1885 1886 SkAutoTUnref<GrTexture> texture; 1887 // Skia's convention is to only clear a device if it is non-opaque. 1888 unsigned flags = info.isOpaque() ? 0 : kNeedClear_Flag; 1889 1890#if CACHE_COMPATIBLE_DEVICE_TEXTURES 1891 // layers are never draw in repeat modes, so we can request an approx 1892 // match and ignore any padding. 1893 flags |= kCached_Flag; 1894 const GrContext::ScratchTexMatch match = (kSaveLayer_Usage == usage) ? 1895 GrContext::kApprox_ScratchTexMatch : 1896 GrContext::kExact_ScratchTexMatch; 1897 texture.reset(fContext->lockAndRefScratchTexture(desc, match)); 1898#else 1899 texture.reset(fContext->createUncachedTexture(desc, NULL, 0)); 1900#endif 1901 if (NULL != texture.get()) { 1902 return SkGpuDevice::Create(texture, flags); 1903 } else { 1904 GrPrintf("---- failed to create compatible device texture [%d %d]\n", 1905 info.width(), info.height()); 1906 return NULL; 1907 } 1908} 1909 1910SkSurface* SkGpuDevice::newSurface(const SkImageInfo& info) { 1911 return SkSurface::NewRenderTarget(fContext, info, fRenderTarget->numSamples()); 1912} 1913 1914void SkGpuDevice::EXPERIMENTAL_optimize(SkPicture* picture) { 1915 SkPicture::AccelData::Key key = GPUAccelData::ComputeAccelDataKey(); 1916 1917 SkAutoTUnref<GPUAccelData> data(SkNEW_ARGS(GPUAccelData, (key))); 1918 1919 picture->EXPERIMENTAL_addAccelData(data); 1920 1921 GatherGPUInfo(picture, data); 1922} 1923 1924static void wrap_texture(GrTexture* texture, int width, int height, SkBitmap* result) { 1925 SkImageInfo info = SkImageInfo::MakeN32Premul(width, height); 1926 result->setConfig(info); 1927 result->setPixelRef(SkNEW_ARGS(SkGrPixelRef, (info, texture)))->unref(); 1928} 1929 1930void SkGpuDevice::EXPERIMENTAL_purge(SkPicture* picture) { 1931 1932} 1933 1934bool SkGpuDevice::EXPERIMENTAL_drawPicture(SkCanvas* canvas, SkPicture* picture) { 1935 1936 SkPicture::AccelData::Key key = GPUAccelData::ComputeAccelDataKey(); 1937 1938 const SkPicture::AccelData* data = picture->EXPERIMENTAL_getAccelData(key); 1939 if (NULL == data) { 1940 return false; 1941 } 1942 1943 const GPUAccelData *gpuData = static_cast<const GPUAccelData*>(data); 1944 1945 SkAutoTArray<bool> pullForward(gpuData->numSaveLayers()); 1946 for (int i = 0; i < gpuData->numSaveLayers(); ++i) { 1947 pullForward[i] = false; 1948 } 1949 1950 SkRect clipBounds; 1951 if (!canvas->getClipBounds(&clipBounds)) { 1952 return true; 1953 } 1954 SkIRect query; 1955 clipBounds.roundOut(&query); 1956 1957 const SkPicture::OperationList& ops = picture->EXPERIMENTAL_getActiveOps(query); 1958 1959 // This code pre-renders the entire layer since it will be cached and potentially 1960 // reused with different clips (e.g., in different tiles). Because of this the 1961 // clip will not be limiting the size of the pre-rendered layer. kSaveLayerMaxSize 1962 // is used to limit which clips are pre-rendered. 1963 static const int kSaveLayerMaxSize = 256; 1964 1965 if (ops.valid()) { 1966 // In this case the picture has been generated with a BBH so we use 1967 // the BBH to limit the pre-rendering to just the layers needed to cover 1968 // the region being drawn 1969 for (int i = 0; i < ops.numOps(); ++i) { 1970 uint32_t offset = ops.offset(i); 1971 1972 // For now we're saving all the layers in the GPUAccelData so they 1973 // can be nested. Additionally, the nested layers appear before 1974 // their parent in the list. 1975 for (int j = 0 ; j < gpuData->numSaveLayers(); ++j) { 1976 const GPUAccelData::SaveLayerInfo& info = gpuData->saveLayerInfo(j); 1977 1978 if (pullForward[j]) { 1979 continue; // already pulling forward 1980 } 1981 1982 if (offset < info.fSaveLayerOpID || offset > info.fRestoreOpID) { 1983 continue; // the op isn't in this range 1984 } 1985 1986 // TODO: once this code is more stable unsuitable layers can 1987 // just be omitted during the optimization stage 1988 if (!info.fValid || 1989 kSaveLayerMaxSize < info.fSize.fWidth || 1990 kSaveLayerMaxSize < info.fSize.fHeight || 1991 info.fIsNested) { 1992 continue; // this layer is unsuitable 1993 } 1994 1995 pullForward[j] = true; 1996 } 1997 } 1998 } else { 1999 // In this case there is no BBH associated with the picture. Pre-render 2000 // all the layers that intersect the drawn region 2001 for (int j = 0; j < gpuData->numSaveLayers(); ++j) { 2002 const GPUAccelData::SaveLayerInfo& info = gpuData->saveLayerInfo(j); 2003 2004 SkIRect layerRect = SkIRect::MakeXYWH(info.fOffset.fX, 2005 info.fOffset.fY, 2006 info.fSize.fWidth, 2007 info.fSize.fHeight); 2008 2009 if (!SkIRect::Intersects(query, layerRect)) { 2010 continue; 2011 } 2012 2013 // TODO: once this code is more stable unsuitable layers can 2014 // just be omitted during the optimization stage 2015 if (!info.fValid || 2016 kSaveLayerMaxSize < info.fSize.fWidth || 2017 kSaveLayerMaxSize < info.fSize.fHeight || 2018 info.fIsNested) { 2019 continue; 2020 } 2021 2022 pullForward[j] = true; 2023 } 2024 } 2025 2026 SkPicturePlayback::PlaybackReplacements replacements; 2027 2028 for (int i = 0; i < gpuData->numSaveLayers(); ++i) { 2029 if (pullForward[i]) { 2030 GrCachedLayer* layer = fContext->getLayerCache()->findLayerOrCreate(picture, i); 2031 2032 const GPUAccelData::SaveLayerInfo& info = gpuData->saveLayerInfo(i); 2033 2034 if (NULL != picture->fPlayback) { 2035 SkPicturePlayback::PlaybackReplacements::ReplacementInfo* layerInfo = 2036 replacements.push(); 2037 layerInfo->fStart = info.fSaveLayerOpID; 2038 layerInfo->fStop = info.fRestoreOpID; 2039 layerInfo->fPos = info.fOffset; 2040 2041 GrTextureDesc desc; 2042 desc.fFlags = kRenderTarget_GrTextureFlagBit; 2043 desc.fWidth = info.fSize.fWidth; 2044 desc.fHeight = info.fSize.fHeight; 2045 desc.fConfig = kSkia8888_GrPixelConfig; 2046 // TODO: need to deal with sample count 2047 2048 bool bNeedsRendering = true; 2049 2050 // This just uses scratch textures and doesn't cache the texture. 2051 // This can yield a lot of re-rendering 2052 if (NULL == layer->getTexture()) { 2053 layer->setTexture(fContext->lockAndRefScratchTexture(desc, 2054 GrContext::kApprox_ScratchTexMatch)); 2055 if (NULL == layer->getTexture()) { 2056 continue; 2057 } 2058 } else { 2059 bNeedsRendering = false; 2060 } 2061 2062 layerInfo->fBM = SkNEW(SkBitmap); 2063 wrap_texture(layer->getTexture(), desc.fWidth, desc.fHeight, layerInfo->fBM); 2064 2065 SkASSERT(info.fPaint); 2066 layerInfo->fPaint = info.fPaint; 2067 2068 if (bNeedsRendering) { 2069 SkAutoTUnref<SkSurface> surface(SkSurface::NewRenderTargetDirect( 2070 layer->getTexture()->asRenderTarget())); 2071 2072 SkCanvas* canvas = surface->getCanvas(); 2073 2074 canvas->setMatrix(info.fCTM); 2075 canvas->clear(SK_ColorTRANSPARENT); 2076 2077 picture->fPlayback->setDrawLimits(info.fSaveLayerOpID, info.fRestoreOpID); 2078 picture->fPlayback->draw(*canvas, NULL); 2079 picture->fPlayback->setDrawLimits(0, 0); 2080 canvas->flush(); 2081 } 2082 } 2083 } 2084 } 2085 2086 // Playback using new layers 2087 picture->fPlayback->setReplacements(&replacements); 2088 picture->fPlayback->draw(*canvas, NULL); 2089 picture->fPlayback->setReplacements(NULL); 2090 2091 for (int i = 0; i < gpuData->numSaveLayers(); ++i) { 2092 GrCachedLayer* layer = fContext->getLayerCache()->findLayerOrCreate(picture, i); 2093 2094 if (NULL != layer->getTexture()) { 2095 fContext->unlockScratchTexture(layer->getTexture()); 2096 layer->setTexture(NULL); 2097 } 2098 } 2099 2100 return true; 2101} 2102