GrContext.cpp revision d8b5faca043100d7a1e4594b4d10e462532af390
1 2/* 3 * Copyright 2011 Google Inc. 4 * 5 * Use of this source code is governed by a BSD-style license that can be 6 * found in the LICENSE file. 7 */ 8 9 10#include "GrContext.h" 11 12#include "effects/GrConvolutionEffect.h" 13#include "effects/GrSingleTextureEffect.h" 14#include "effects/GrConfigConversionEffect.h" 15 16#include "GrBufferAllocPool.h" 17#include "GrGpu.h" 18#include "GrIndexBuffer.h" 19#include "GrInOrderDrawBuffer.h" 20#include "GrPathRenderer.h" 21#include "GrPathUtils.h" 22#include "GrResourceCache.h" 23#include "GrSoftwarePathRenderer.h" 24#include "GrStencilBuffer.h" 25#include "GrTextStrike.h" 26#include "SkTLazy.h" 27#include "SkTLS.h" 28#include "SkTrace.h" 29 30SK_DEFINE_INST_COUNT(GrContext) 31SK_DEFINE_INST_COUNT(GrDrawState) 32 33// It can be useful to set this to kNo_BufferedDraw to test whether a bug is caused by using the 34// InOrderDrawBuffer, to compare performance of using/not using InOrderDrawBuffer, or to make 35// debugging easier. 36#define DEFAULT_BUFFERING (GR_DISABLE_DRAW_BUFFERING ? kNo_BufferedDraw : kYes_BufferedDraw) 37 38#define MAX_BLUR_SIGMA 4.0f 39 40// When we're using coverage AA but the blend is incompatible (given gpu 41// limitations) should we disable AA or draw wrong? 42#define DISABLE_COVERAGE_AA_FOR_BLEND 1 43 44#if GR_DEBUG 45 // change this to a 1 to see notifications when partial coverage fails 46 #define GR_DEBUG_PARTIAL_COVERAGE_CHECK 0 47#else 48 #define GR_DEBUG_PARTIAL_COVERAGE_CHECK 0 49#endif 50 51static const size_t MAX_TEXTURE_CACHE_COUNT = 2048; 52static const size_t MAX_TEXTURE_CACHE_BYTES = GR_DEFAULT_TEXTURE_CACHE_MB_LIMIT * 1024 * 1024; 53 54static const size_t DRAW_BUFFER_VBPOOL_BUFFER_SIZE = 1 << 15; 55static const int DRAW_BUFFER_VBPOOL_PREALLOC_BUFFERS = 4; 56 57static const size_t DRAW_BUFFER_IBPOOL_BUFFER_SIZE = 1 << 11; 58static const int DRAW_BUFFER_IBPOOL_PREALLOC_BUFFERS = 4; 59 60#define ASSERT_OWNED_RESOURCE(R) GrAssert(!(R) || (R)->getContext() == this) 61 62GrContext* GrContext::Create(GrBackend backend, GrBackendContext context) { 63 GrContext* ctx = NULL; 64 GrGpu* fGpu = GrGpu::Create(backend, context); 65 if (NULL != fGpu) { 66 ctx = SkNEW_ARGS(GrContext, (fGpu)); 67 fGpu->unref(); 68 } 69 return ctx; 70} 71 72namespace { 73void* CreateThreadInstanceCount() { 74 return SkNEW_ARGS(int, (0)); 75} 76void DeleteThreadInstanceCount(void* v) { 77 delete reinterpret_cast<int*>(v); 78} 79#define THREAD_INSTANCE_COUNT \ 80 (*reinterpret_cast<int*>(SkTLS::Get(CreateThreadInstanceCount, \ 81 DeleteThreadInstanceCount))) 82 83} 84 85int GrContext::GetThreadInstanceCount() { 86 return THREAD_INSTANCE_COUNT; 87} 88 89GrContext::~GrContext() { 90 for (int i = 0; i < fCleanUpData.count(); ++i) { 91 (*fCleanUpData[i].fFunc)(this, fCleanUpData[i].fInfo); 92 } 93 94 this->flush(); 95 96 // Since the gpu can hold scratch textures, give it a chance to let go 97 // of them before freeing the texture cache 98 fGpu->purgeResources(); 99 100 delete fTextureCache; 101 fTextureCache = NULL; 102 delete fFontCache; 103 delete fDrawBuffer; 104 delete fDrawBufferVBAllocPool; 105 delete fDrawBufferIBAllocPool; 106 107 fAARectRenderer->unref(); 108 109 fGpu->unref(); 110 GrSafeUnref(fPathRendererChain); 111 GrSafeUnref(fSoftwarePathRenderer); 112 fDrawState->unref(); 113 114 --THREAD_INSTANCE_COUNT; 115} 116 117void GrContext::contextLost() { 118 contextDestroyed(); 119 this->setupDrawBuffer(); 120} 121 122void GrContext::contextDestroyed() { 123 // abandon first to so destructors 124 // don't try to free the resources in the API. 125 fGpu->abandonResources(); 126 127 // a path renderer may be holding onto resources that 128 // are now unusable 129 GrSafeSetNull(fPathRendererChain); 130 GrSafeSetNull(fSoftwarePathRenderer); 131 132 delete fDrawBuffer; 133 fDrawBuffer = NULL; 134 135 delete fDrawBufferVBAllocPool; 136 fDrawBufferVBAllocPool = NULL; 137 138 delete fDrawBufferIBAllocPool; 139 fDrawBufferIBAllocPool = NULL; 140 141 fAARectRenderer->reset(); 142 143 fTextureCache->purgeAllUnlocked(); 144 fFontCache->freeAll(); 145 fGpu->markContextDirty(); 146} 147 148void GrContext::resetContext() { 149 fGpu->markContextDirty(); 150} 151 152void GrContext::freeGpuResources() { 153 this->flush(); 154 155 fGpu->purgeResources(); 156 157 fAARectRenderer->reset(); 158 159 fTextureCache->purgeAllUnlocked(); 160 fFontCache->freeAll(); 161 // a path renderer may be holding onto resources 162 GrSafeSetNull(fPathRendererChain); 163 GrSafeSetNull(fSoftwarePathRenderer); 164} 165 166size_t GrContext::getGpuTextureCacheBytes() const { 167 return fTextureCache->getCachedResourceBytes(); 168} 169 170//////////////////////////////////////////////////////////////////////////////// 171 172namespace { 173 174void scale_rect(SkRect* rect, float xScale, float yScale) { 175 rect->fLeft = SkScalarMul(rect->fLeft, SkFloatToScalar(xScale)); 176 rect->fTop = SkScalarMul(rect->fTop, SkFloatToScalar(yScale)); 177 rect->fRight = SkScalarMul(rect->fRight, SkFloatToScalar(xScale)); 178 rect->fBottom = SkScalarMul(rect->fBottom, SkFloatToScalar(yScale)); 179} 180 181float adjust_sigma(float sigma, int *scaleFactor, int *radius) { 182 *scaleFactor = 1; 183 while (sigma > MAX_BLUR_SIGMA) { 184 *scaleFactor *= 2; 185 sigma *= 0.5f; 186 } 187 *radius = static_cast<int>(ceilf(sigma * 3.0f)); 188 GrAssert(*radius <= GrConvolutionEffect::kMaxKernelRadius); 189 return sigma; 190} 191 192void convolve_gaussian(GrDrawTarget* target, 193 GrTexture* texture, 194 const SkRect& rect, 195 float sigma, 196 int radius, 197 Gr1DKernelEffect::Direction direction) { 198 GrRenderTarget* rt = target->drawState()->getRenderTarget(); 199 GrDrawTarget::AutoStateRestore asr(target, GrDrawTarget::kReset_ASRInit); 200 GrDrawState* drawState = target->drawState(); 201 drawState->setRenderTarget(rt); 202 GrMatrix sampleM; 203 sampleM.setIDiv(texture->width(), texture->height()); 204 SkAutoTUnref<GrConvolutionEffect> conv(SkNEW_ARGS(GrConvolutionEffect, 205 (texture, direction, radius, 206 sigma))); 207 drawState->stage(0)->setEffect(conv, sampleM); 208 target->drawSimpleRect(rect, NULL); 209} 210 211} 212 213 214GrTexture* GrContext::findTexture(const GrCacheKey& key) { 215 return static_cast<GrTexture*>(fTextureCache->find(key.key())); 216} 217 218GrTexture* GrContext::findTexture(const GrTextureDesc& desc, 219 const GrCacheData& cacheData, 220 const GrTextureParams* params) { 221 GrResourceKey resourceKey = GrTexture::ComputeKey(fGpu, params, desc, cacheData, false); 222 GrResource* resource = fTextureCache->find(resourceKey); 223 return static_cast<GrTexture*>(resource); 224} 225 226bool GrContext::isTextureInCache(const GrTextureDesc& desc, 227 const GrCacheData& cacheData, 228 const GrTextureParams* params) const { 229 GrResourceKey resourceKey = GrTexture::ComputeKey(fGpu, params, desc, cacheData, false); 230 return fTextureCache->hasKey(resourceKey); 231} 232 233void GrContext::addStencilBuffer(GrStencilBuffer* sb) { 234 ASSERT_OWNED_RESOURCE(sb); 235 236 GrResourceKey resourceKey = GrStencilBuffer::ComputeKey(sb->width(), 237 sb->height(), 238 sb->numSamples()); 239 fTextureCache->addResource(resourceKey, sb); 240} 241 242GrStencilBuffer* GrContext::findStencilBuffer(int width, int height, 243 int sampleCnt) { 244 GrResourceKey resourceKey = GrStencilBuffer::ComputeKey(width, 245 height, 246 sampleCnt); 247 GrResource* resource = fTextureCache->find(resourceKey); 248 return static_cast<GrStencilBuffer*>(resource); 249} 250 251static void stretchImage(void* dst, 252 int dstW, 253 int dstH, 254 void* src, 255 int srcW, 256 int srcH, 257 int bpp) { 258 GrFixed dx = (srcW << 16) / dstW; 259 GrFixed dy = (srcH << 16) / dstH; 260 261 GrFixed y = dy >> 1; 262 263 int dstXLimit = dstW*bpp; 264 for (int j = 0; j < dstH; ++j) { 265 GrFixed x = dx >> 1; 266 void* srcRow = (uint8_t*)src + (y>>16)*srcW*bpp; 267 void* dstRow = (uint8_t*)dst + j*dstW*bpp; 268 for (int i = 0; i < dstXLimit; i += bpp) { 269 memcpy((uint8_t*) dstRow + i, 270 (uint8_t*) srcRow + (x>>16)*bpp, 271 bpp); 272 x += dx; 273 } 274 y += dy; 275 } 276} 277 278// The desired texture is NPOT and tiled but that isn't supported by 279// the current hardware. Resize the texture to be a POT 280GrTexture* GrContext::createResizedTexture(const GrTextureDesc& desc, 281 const GrCacheData& cacheData, 282 void* srcData, 283 size_t rowBytes, 284 bool needsFiltering) { 285 GrTexture* clampedTexture = this->findTexture(desc, cacheData, NULL); 286 if (NULL == clampedTexture) { 287 clampedTexture = this->createTexture(NULL, desc, cacheData, srcData, rowBytes); 288 289 GrAssert(NULL != clampedTexture); 290 if (NULL == clampedTexture) { 291 return NULL; 292 } 293 } 294 295 clampedTexture->ref(); 296 297 GrTextureDesc rtDesc = desc; 298 rtDesc.fFlags = rtDesc.fFlags | 299 kRenderTarget_GrTextureFlagBit | 300 kNoStencil_GrTextureFlagBit; 301 rtDesc.fWidth = GrNextPow2(GrMax(desc.fWidth, 64)); 302 rtDesc.fHeight = GrNextPow2(GrMax(desc.fHeight, 64)); 303 304 GrTexture* texture = fGpu->createTexture(rtDesc, NULL, 0); 305 306 if (NULL != texture) { 307 GrDrawTarget::AutoStateRestore asr(fGpu, GrDrawTarget::kReset_ASRInit); 308 GrDrawState* drawState = fGpu->drawState(); 309 drawState->setRenderTarget(texture->asRenderTarget()); 310 311 // if filtering is not desired then we want to ensure all 312 // texels in the resampled image are copies of texels from 313 // the original. 314 GrTextureParams params(SkShader::kClamp_TileMode, needsFiltering); 315 drawState->createTextureEffect(0, clampedTexture, GrMatrix::I(), params); 316 317 static const GrVertexLayout layout = 318 GrDrawTarget::StageTexCoordVertexLayoutBit(0,0); 319 GrDrawTarget::AutoReleaseGeometry arg(fGpu, layout, 4, 0); 320 321 if (arg.succeeded()) { 322 GrPoint* verts = (GrPoint*) arg.vertices(); 323 verts[0].setIRectFan(0, 0, 324 texture->width(), 325 texture->height(), 326 2*sizeof(GrPoint)); 327 verts[1].setIRectFan(0, 0, 1, 1, 2*sizeof(GrPoint)); 328 fGpu->drawNonIndexed(kTriangleFan_GrPrimitiveType, 329 0, 4); 330 } 331 texture->releaseRenderTarget(); 332 } else { 333 // TODO: Our CPU stretch doesn't filter. But we create separate 334 // stretched textures when the texture params is either filtered or 335 // not. Either implement filtered stretch blit on CPU or just create 336 // one when FBO case fails. 337 338 rtDesc.fFlags = kNone_GrTextureFlags; 339 // no longer need to clamp at min RT size. 340 rtDesc.fWidth = GrNextPow2(desc.fWidth); 341 rtDesc.fHeight = GrNextPow2(desc.fHeight); 342 int bpp = GrBytesPerPixel(desc.fConfig); 343 SkAutoSMalloc<128*128*4> stretchedPixels(bpp * rtDesc.fWidth * rtDesc.fHeight); 344 stretchImage(stretchedPixels.get(), rtDesc.fWidth, rtDesc.fHeight, 345 srcData, desc.fWidth, desc.fHeight, bpp); 346 347 size_t stretchedRowBytes = rtDesc.fWidth * bpp; 348 349 GrTexture* texture = fGpu->createTexture(rtDesc, stretchedPixels.get(), stretchedRowBytes); 350 GrAssert(NULL != texture); 351 } 352 353 clampedTexture->unref(); 354 return texture; 355} 356 357GrTexture* GrContext::createTexture( 358 const GrTextureParams* params, 359 const GrTextureDesc& desc, 360 const GrCacheData& cacheData, 361 void* srcData, 362 size_t rowBytes) { 363 SK_TRACE_EVENT0("GrContext::createAndLockTexture"); 364 365#if GR_DUMP_TEXTURE_UPLOAD 366 GrPrintf("GrContext::createAndLockTexture [%d %d]\n", desc.fWidth, desc.fHeight); 367#endif 368 369 GrResourceKey resourceKey = GrTexture::ComputeKey(fGpu, params, desc, cacheData, false); 370 371 SkAutoTUnref<GrTexture> texture; 372 if (GrTexture::NeedsResizing(resourceKey)) { 373 texture.reset(this->createResizedTexture(desc, cacheData, 374 srcData, rowBytes, 375 GrTexture::NeedsFiltering(resourceKey))); 376 } else { 377 texture.reset(fGpu->createTexture(desc, srcData, rowBytes)); 378 } 379 380 if (NULL != texture) { 381 fTextureCache->addResource(resourceKey, texture); 382 } 383 384 return texture; 385} 386 387GrTexture* GrContext::lockScratchTexture(const GrTextureDesc& inDesc, 388 ScratchTexMatch match) { 389 GrTextureDesc desc = inDesc; 390 GrCacheData cacheData(GrCacheData::kScratch_CacheID); 391 392 GrAssert((desc.fFlags & kRenderTarget_GrTextureFlagBit) || 393 !(desc.fFlags & kNoStencil_GrTextureFlagBit)); 394 395 if (kExact_ScratchTexMatch != match) { 396 // bin by pow2 with a reasonable min 397 static const int MIN_SIZE = 256; 398 desc.fWidth = GrMax(MIN_SIZE, GrNextPow2(desc.fWidth)); 399 desc.fHeight = GrMax(MIN_SIZE, GrNextPow2(desc.fHeight)); 400 } 401 402 GrResource* resource = NULL; 403 int origWidth = desc.fWidth; 404 int origHeight = desc.fHeight; 405 bool doubledW = false; 406 bool doubledH = false; 407 408 do { 409 GrResourceKey key = GrTexture::ComputeKey(fGpu, NULL, desc, cacheData, true); 410 // Ensure we have exclusive access to the texture so future 'find' calls don't return it 411 resource = fTextureCache->find(key, GrResourceCache::kHide_OwnershipFlag); 412 // if we miss, relax the fit of the flags... 413 // then try doubling width... then height. 414 if (NULL != resource || kExact_ScratchTexMatch == match) { 415 break; 416 } 417 // We no longer try to reuse textures that were previously used as render targets in 418 // situations where no RT is needed; doing otherwise can confuse the video driver and 419 // cause significant performance problems in some cases. 420 if (desc.fFlags & kNoStencil_GrTextureFlagBit) { 421 desc.fFlags = desc.fFlags & ~kNoStencil_GrTextureFlagBit; 422 } else if (!doubledW) { 423 desc.fFlags = inDesc.fFlags; 424 desc.fWidth *= 2; 425 doubledW = true; 426 } else if (!doubledH) { 427 desc.fFlags = inDesc.fFlags; 428 desc.fWidth = origWidth; 429 desc.fHeight *= 2; 430 doubledH = true; 431 } else { 432 break; 433 } 434 435 } while (true); 436 437 if (NULL == resource) { 438 desc.fFlags = inDesc.fFlags; 439 desc.fWidth = origWidth; 440 desc.fHeight = origHeight; 441 SkAutoTUnref<GrTexture> texture(fGpu->createTexture(desc, NULL, 0)); 442 if (NULL != texture) { 443 GrResourceKey key = GrTexture::ComputeKey(fGpu, NULL, 444 texture->desc(), 445 cacheData, 446 true); 447 // Make the resource exclusive so future 'find' calls don't return it 448 fTextureCache->addResource(key, texture, GrResourceCache::kHide_OwnershipFlag); 449 resource = texture; 450 } 451 } 452 453 return static_cast<GrTexture*>(resource); 454} 455 456void GrContext::addExistingTextureToCache(GrTexture* texture) { 457 458 if (NULL == texture) { 459 return; 460 } 461 462 // This texture should already have a cache entry since it was once 463 // attached 464 GrAssert(NULL != texture->getCacheEntry()); 465 466 // Conceptually, the cache entry is going to assume responsibility 467 // for the creation ref. 468 GrAssert(1 == texture->getRefCnt()); 469 470 // Since this texture came from an AutoScratchTexture it should 471 // still be in the exclusive pile 472 fTextureCache->makeNonExclusive(texture->getCacheEntry()); 473 474 this->purgeCache(); 475} 476 477 478void GrContext::unlockScratchTexture(GrTexture* texture) { 479 ASSERT_OWNED_RESOURCE(texture); 480 GrAssert(NULL != texture->getCacheEntry()); 481 482 // If this is a scratch texture we detached it from the cache 483 // while it was locked (to avoid two callers simultaneously getting 484 // the same texture). 485 if (GrTexture::IsScratchTexture(texture->getCacheEntry()->key())) { 486 fTextureCache->makeNonExclusive(texture->getCacheEntry()); 487 } 488 489 this->purgeCache(); 490} 491 492void GrContext::purgeCache() { 493 if (NULL != fTextureCache) { 494 fTextureCache->purgeAsNeeded(); 495 } 496} 497 498GrTexture* GrContext::createUncachedTexture(const GrTextureDesc& descIn, 499 void* srcData, 500 size_t rowBytes) { 501 GrTextureDesc descCopy = descIn; 502 return fGpu->createTexture(descCopy, srcData, rowBytes); 503} 504 505void GrContext::getTextureCacheLimits(int* maxTextures, 506 size_t* maxTextureBytes) const { 507 fTextureCache->getLimits(maxTextures, maxTextureBytes); 508} 509 510void GrContext::setTextureCacheLimits(int maxTextures, size_t maxTextureBytes) { 511 fTextureCache->setLimits(maxTextures, maxTextureBytes); 512} 513 514int GrContext::getMaxTextureSize() const { 515 return fGpu->getCaps().maxTextureSize(); 516} 517 518int GrContext::getMaxRenderTargetSize() const { 519 return fGpu->getCaps().maxRenderTargetSize(); 520} 521 522/////////////////////////////////////////////////////////////////////////////// 523 524GrTexture* GrContext::wrapBackendTexture(const GrBackendTextureDesc& desc) { 525 return fGpu->wrapBackendTexture(desc); 526} 527 528GrRenderTarget* GrContext::wrapBackendRenderTarget(const GrBackendRenderTargetDesc& desc) { 529 return fGpu->wrapBackendRenderTarget(desc); 530} 531 532/////////////////////////////////////////////////////////////////////////////// 533 534bool GrContext::supportsIndex8PixelConfig(const GrTextureParams* params, 535 int width, int height) const { 536 const GrDrawTarget::Caps& caps = fGpu->getCaps(); 537 if (!caps.eightBitPaletteSupport()) { 538 return false; 539 } 540 541 bool isPow2 = GrIsPow2(width) && GrIsPow2(height); 542 543 if (!isPow2) { 544 bool tiled = NULL != params && params->isTiled(); 545 if (tiled && !caps.npotTextureTileSupport()) { 546 return false; 547 } 548 } 549 return true; 550} 551 552//////////////////////////////////////////////////////////////////////////////// 553 554const GrClipData* GrContext::getClip() const { 555 return fGpu->getClip(); 556} 557 558void GrContext::setClip(const GrClipData* clipData) { 559 fGpu->setClip(clipData); 560 561 fDrawState->setState(GrDrawState::kClip_StateBit, 562 clipData && clipData->fClipStack && !clipData->fClipStack->isWideOpen()); 563} 564 565//////////////////////////////////////////////////////////////////////////////// 566 567void GrContext::clear(const GrIRect* rect, 568 const GrColor color, 569 GrRenderTarget* target) { 570 this->prepareToDraw(NULL, DEFAULT_BUFFERING)->clear(rect, color, target); 571} 572 573void GrContext::drawPaint(const GrPaint& origPaint) { 574 // set rect to be big enough to fill the space, but not super-huge, so we 575 // don't overflow fixed-point implementations 576 GrRect r; 577 r.setLTRB(0, 0, 578 GrIntToScalar(getRenderTarget()->width()), 579 GrIntToScalar(getRenderTarget()->height())); 580 GrMatrix inverse; 581 SkTCopyOnFirstWrite<GrPaint> paint(origPaint); 582 AutoMatrix am; 583 584 // We attempt to map r by the inverse matrix and draw that. mapRect will 585 // map the four corners and bound them with a new rect. This will not 586 // produce a correct result for some perspective matrices. 587 if (!this->getMatrix().hasPerspective()) { 588 if (!fDrawState->getViewInverse(&inverse)) { 589 GrPrintf("Could not invert matrix\n"); 590 return; 591 } 592 inverse.mapRect(&r); 593 } else { 594 if (!am.setIdentity(this, paint.writable())) { 595 GrPrintf("Could not invert matrix\n"); 596 return; 597 } 598 } 599 // by definition this fills the entire clip, no need for AA 600 if (paint->isAntiAlias()) { 601 paint.writable()->setAntiAlias(false); 602 } 603 this->drawRect(*paint, r); 604} 605 606//////////////////////////////////////////////////////////////////////////////// 607 608namespace { 609inline bool disable_coverage_aa_for_blend(GrDrawTarget* target) { 610 return DISABLE_COVERAGE_AA_FOR_BLEND && !target->canApplyCoverage(); 611} 612} 613 614//////////////////////////////////////////////////////////////////////////////// 615 616/* create a triangle strip that strokes the specified triangle. There are 8 617 unique vertices, but we repreat the last 2 to close up. Alternatively we 618 could use an indices array, and then only send 8 verts, but not sure that 619 would be faster. 620 */ 621static void setStrokeRectStrip(GrPoint verts[10], GrRect rect, 622 GrScalar width) { 623 const GrScalar rad = GrScalarHalf(width); 624 rect.sort(); 625 626 verts[0].set(rect.fLeft + rad, rect.fTop + rad); 627 verts[1].set(rect.fLeft - rad, rect.fTop - rad); 628 verts[2].set(rect.fRight - rad, rect.fTop + rad); 629 verts[3].set(rect.fRight + rad, rect.fTop - rad); 630 verts[4].set(rect.fRight - rad, rect.fBottom - rad); 631 verts[5].set(rect.fRight + rad, rect.fBottom + rad); 632 verts[6].set(rect.fLeft + rad, rect.fBottom - rad); 633 verts[7].set(rect.fLeft - rad, rect.fBottom + rad); 634 verts[8] = verts[0]; 635 verts[9] = verts[1]; 636} 637 638/** 639 * Returns true if the rects edges are integer-aligned. 640 */ 641static bool isIRect(const GrRect& r) { 642 return GrScalarIsInt(r.fLeft) && GrScalarIsInt(r.fTop) && 643 GrScalarIsInt(r.fRight) && GrScalarIsInt(r.fBottom); 644} 645 646static bool apply_aa_to_rect(GrDrawTarget* target, 647 const GrRect& rect, 648 GrScalar width, 649 const GrMatrix* matrix, 650 GrMatrix* combinedMatrix, 651 GrRect* devRect, 652 bool* useVertexCoverage) { 653 // we use a simple coverage ramp to do aa on axis-aligned rects 654 // we check if the rect will be axis-aligned, and the rect won't land on 655 // integer coords. 656 657 // we are keeping around the "tweak the alpha" trick because 658 // it is our only hope for the fixed-pipe implementation. 659 // In a shader implementation we can give a separate coverage input 660 // TODO: remove this ugliness when we drop the fixed-pipe impl 661 *useVertexCoverage = false; 662 if (!target->canTweakAlphaForCoverage()) { 663 if (disable_coverage_aa_for_blend(target)) { 664#if GR_DEBUG 665 //GrPrintf("Turning off AA to correctly apply blend.\n"); 666#endif 667 return false; 668 } else { 669 *useVertexCoverage = true; 670 } 671 } 672 const GrDrawState& drawState = target->getDrawState(); 673 if (drawState.getRenderTarget()->isMultisampled()) { 674 return false; 675 } 676 677 if (0 == width && target->willUseHWAALines()) { 678 return false; 679 } 680 681 if (!drawState.getViewMatrix().preservesAxisAlignment()) { 682 return false; 683 } 684 685 if (NULL != matrix && 686 !matrix->preservesAxisAlignment()) { 687 return false; 688 } 689 690 *combinedMatrix = drawState.getViewMatrix(); 691 if (NULL != matrix) { 692 combinedMatrix->preConcat(*matrix); 693 GrAssert(combinedMatrix->preservesAxisAlignment()); 694 } 695 696 combinedMatrix->mapRect(devRect, rect); 697 devRect->sort(); 698 699 if (width < 0) { 700 return !isIRect(*devRect); 701 } else { 702 return true; 703 } 704} 705 706void GrContext::drawRect(const GrPaint& paint, 707 const GrRect& rect, 708 GrScalar width, 709 const GrMatrix* matrix) { 710 SK_TRACE_EVENT0("GrContext::drawRect"); 711 712 GrDrawTarget* target = this->prepareToDraw(&paint, DEFAULT_BUFFERING); 713 GrDrawState::AutoStageDisable atr(fDrawState); 714 715 GrRect devRect = rect; 716 GrMatrix combinedMatrix; 717 bool useVertexCoverage; 718 bool needAA = paint.isAntiAlias() && 719 !this->getRenderTarget()->isMultisampled(); 720 bool doAA = needAA && apply_aa_to_rect(target, rect, width, matrix, 721 &combinedMatrix, &devRect, 722 &useVertexCoverage); 723 724 if (doAA) { 725 GrDrawState::AutoDeviceCoordDraw adcd(target->drawState()); 726 if (!adcd.succeeded()) { 727 return; 728 } 729 if (width >= 0) { 730 GrVec strokeSize;; 731 if (width > 0) { 732 strokeSize.set(width, width); 733 combinedMatrix.mapVectors(&strokeSize, 1); 734 strokeSize.setAbs(strokeSize); 735 } else { 736 strokeSize.set(GR_Scalar1, GR_Scalar1); 737 } 738 fAARectRenderer->strokeAARect(this->getGpu(), target, devRect, 739 strokeSize, useVertexCoverage); 740 } else { 741 fAARectRenderer->fillAARect(this->getGpu(), target, 742 devRect, useVertexCoverage); 743 } 744 return; 745 } 746 747 if (width >= 0) { 748 // TODO: consider making static vertex buffers for these cases. 749 // Hairline could be done by just adding closing vertex to 750 // unitSquareVertexBuffer() 751 752 static const int worstCaseVertCount = 10; 753 GrDrawTarget::AutoReleaseGeometry geo(target, 0, worstCaseVertCount, 0); 754 755 if (!geo.succeeded()) { 756 GrPrintf("Failed to get space for vertices!\n"); 757 return; 758 } 759 760 GrPrimitiveType primType; 761 int vertCount; 762 GrPoint* vertex = geo.positions(); 763 764 if (width > 0) { 765 vertCount = 10; 766 primType = kTriangleStrip_GrPrimitiveType; 767 setStrokeRectStrip(vertex, rect, width); 768 } else { 769 // hairline 770 vertCount = 5; 771 primType = kLineStrip_GrPrimitiveType; 772 vertex[0].set(rect.fLeft, rect.fTop); 773 vertex[1].set(rect.fRight, rect.fTop); 774 vertex[2].set(rect.fRight, rect.fBottom); 775 vertex[3].set(rect.fLeft, rect.fBottom); 776 vertex[4].set(rect.fLeft, rect.fTop); 777 } 778 779 GrDrawState::AutoViewMatrixRestore avmr; 780 if (NULL != matrix) { 781 GrDrawState* drawState = target->drawState(); 782 avmr.set(drawState, *matrix); 783 } 784 785 target->drawNonIndexed(primType, 0, vertCount); 786 } else { 787#if GR_STATIC_RECT_VB 788 const GrVertexBuffer* sqVB = fGpu->getUnitSquareVertexBuffer(); 789 if (NULL == sqVB) { 790 GrPrintf("Failed to create static rect vb.\n"); 791 return; 792 } 793 target->setVertexSourceToBuffer(0, sqVB); 794 GrDrawState* drawState = target->drawState(); 795 GrMatrix m; 796 m.setAll(rect.width(), 0, rect.fLeft, 797 0, rect.height(), rect.fTop, 798 0, 0, GrMatrix::I()[8]); 799 800 if (NULL != matrix) { 801 m.postConcat(*matrix); 802 } 803 GrDrawState::AutoViewMatrixRestore avmr(drawState, m); 804 805 target->drawNonIndexed(kTriangleFan_GrPrimitiveType, 0, 4); 806#else 807 target->drawSimpleRect(rect, matrix); 808#endif 809 } 810} 811 812void GrContext::drawRectToRect(const GrPaint& paint, 813 const GrRect& dstRect, 814 const GrRect& srcRect, 815 const GrMatrix* dstMatrix, 816 const GrMatrix* srcMatrix) { 817 SK_TRACE_EVENT0("GrContext::drawRectToRect"); 818 819 // srcRect refers to paint's first color stage 820 if (!paint.isColorStageEnabled(0)) { 821 drawRect(paint, dstRect, -1, dstMatrix); 822 return; 823 } 824 825 GrDrawTarget* target = this->prepareToDraw(&paint, DEFAULT_BUFFERING); 826 827#if GR_STATIC_RECT_VB 828 GrDrawState::AutoStageDisable atr(fDrawState); 829 GrDrawState* drawState = target->drawState(); 830 831 GrMatrix m; 832 833 m.setAll(dstRect.width(), 0, dstRect.fLeft, 834 0, dstRect.height(), dstRect.fTop, 835 0, 0, GrMatrix::I()[8]); 836 if (NULL != dstMatrix) { 837 m.postConcat(*dstMatrix); 838 } 839 840 // The first color stage's coords come from srcRect rather than applying a matrix to dstRect. 841 // We explicitly compute a matrix for that stage below, no need to adjust here. 842 static const uint32_t kExplicitCoordMask = 1 << GrPaint::kFirstColorStage; 843 GrDrawState::AutoViewMatrixRestore avmr(drawState, m, kExplicitCoordMask); 844 845 m.setAll(srcRect.width(), 0, srcRect.fLeft, 846 0, srcRect.height(), srcRect.fTop, 847 0, 0, GrMatrix::I()[8]); 848 if (NULL != srcMatrix) { 849 m.postConcat(*srcMatrix); 850 } 851 852 drawState->stage(GrPaint::kFirstColorStage)->preConcatCoordChange(m); 853 854 const GrVertexBuffer* sqVB = fGpu->getUnitSquareVertexBuffer(); 855 if (NULL == sqVB) { 856 GrPrintf("Failed to create static rect vb.\n"); 857 return; 858 } 859 target->setVertexSourceToBuffer(0, sqVB); 860 target->drawNonIndexed(kTriangleFan_GrPrimitiveType, 0, 4); 861#else 862 GrDrawState::AutoStageDisable atr(fDrawState); 863 864 const GrRect* srcRects[GrDrawState::kNumStages] = {NULL}; 865 const GrMatrix* srcMatrices[GrDrawState::kNumStages] = {NULL}; 866 srcRects[0] = &srcRect; 867 srcMatrices[0] = srcMatrix; 868 869 target->drawRect(dstRect, dstMatrix, srcRects, srcMatrices); 870#endif 871} 872 873void GrContext::drawVertices(const GrPaint& paint, 874 GrPrimitiveType primitiveType, 875 int vertexCount, 876 const GrPoint positions[], 877 const GrPoint texCoords[], 878 const GrColor colors[], 879 const uint16_t indices[], 880 int indexCount) { 881 SK_TRACE_EVENT0("GrContext::drawVertices"); 882 883 GrDrawTarget::AutoReleaseGeometry geo; 884 885 GrDrawTarget* target = this->prepareToDraw(&paint, DEFAULT_BUFFERING); 886 GrDrawState::AutoStageDisable atr(fDrawState); 887 888 GrVertexLayout layout = 0; 889 if (NULL != texCoords) { 890 layout |= GrDrawTarget::StageTexCoordVertexLayoutBit(0, 0); 891 } 892 if (NULL != colors) { 893 layout |= GrDrawTarget::kColor_VertexLayoutBit; 894 } 895 int vertexSize = GrDrawTarget::VertexSize(layout); 896 897 if (sizeof(GrPoint) != vertexSize) { 898 if (!geo.set(target, layout, vertexCount, 0)) { 899 GrPrintf("Failed to get space for vertices!\n"); 900 return; 901 } 902 int texOffsets[GrDrawState::kMaxTexCoords]; 903 int colorOffset; 904 GrDrawTarget::VertexSizeAndOffsetsByIdx(layout, 905 texOffsets, 906 &colorOffset, 907 NULL, 908 NULL); 909 void* curVertex = geo.vertices(); 910 911 for (int i = 0; i < vertexCount; ++i) { 912 *((GrPoint*)curVertex) = positions[i]; 913 914 if (texOffsets[0] > 0) { 915 *(GrPoint*)((intptr_t)curVertex + texOffsets[0]) = texCoords[i]; 916 } 917 if (colorOffset > 0) { 918 *(GrColor*)((intptr_t)curVertex + colorOffset) = colors[i]; 919 } 920 curVertex = (void*)((intptr_t)curVertex + vertexSize); 921 } 922 } else { 923 target->setVertexSourceToArray(layout, positions, vertexCount); 924 } 925 926 // we don't currently apply offscreen AA to this path. Need improved 927 // management of GrDrawTarget's geometry to avoid copying points per-tile. 928 929 if (NULL != indices) { 930 target->setIndexSourceToArray(indices, indexCount); 931 target->drawIndexed(primitiveType, 0, 0, vertexCount, indexCount); 932 } else { 933 target->drawNonIndexed(primitiveType, 0, vertexCount); 934 } 935} 936 937/////////////////////////////////////////////////////////////////////////////// 938namespace { 939 940struct CircleVertex { 941 GrPoint fPos; 942 GrPoint fCenter; 943 GrScalar fOuterRadius; 944 GrScalar fInnerRadius; 945}; 946 947/* Returns true if will map a circle to another circle. This can be true 948 * if the matrix only includes square-scale, rotation, translation. 949 */ 950inline bool isSimilarityTransformation(const SkMatrix& matrix, 951 SkScalar tol = SK_ScalarNearlyZero) { 952 if (matrix.isIdentity() || matrix.getType() == SkMatrix::kTranslate_Mask) { 953 return true; 954 } 955 if (matrix.hasPerspective()) { 956 return false; 957 } 958 959 SkScalar mx = matrix.get(SkMatrix::kMScaleX); 960 SkScalar sx = matrix.get(SkMatrix::kMSkewX); 961 SkScalar my = matrix.get(SkMatrix::kMScaleY); 962 SkScalar sy = matrix.get(SkMatrix::kMSkewY); 963 964 if (mx == 0 && sx == 0 && my == 0 && sy == 0) { 965 return false; 966 } 967 968 // it has scales or skews, but it could also be rotation, check it out. 969 SkVector vec[2]; 970 vec[0].set(mx, sx); 971 vec[1].set(sy, my); 972 973 return SkScalarNearlyZero(vec[0].dot(vec[1]), SkScalarSquare(tol)) && 974 SkScalarNearlyEqual(vec[0].lengthSqd(), vec[1].lengthSqd(), 975 SkScalarSquare(tol)); 976} 977 978} 979 980// TODO: strokeWidth can't be larger than zero right now. 981// It will be fixed when drawPath() can handle strokes. 982void GrContext::drawOval(const GrPaint& paint, 983 const GrRect& rect, 984 SkScalar strokeWidth) { 985 GrAssert(strokeWidth <= 0); 986 if (!isSimilarityTransformation(this->getMatrix()) || 987 !paint.isAntiAlias() || 988 rect.height() != rect.width()) { 989 SkPath path; 990 path.addOval(rect); 991 GrPathFill fill = (strokeWidth == 0) ? 992 kHairLine_GrPathFill : kWinding_GrPathFill; 993 this->internalDrawPath(paint, path, fill); 994 return; 995 } 996 997 GrDrawTarget* target = this->prepareToDraw(&paint, DEFAULT_BUFFERING); 998 999 GrDrawState* drawState = target->drawState(); 1000 GrDrawState::AutoStageDisable atr(fDrawState); 1001 const GrMatrix vm = drawState->getViewMatrix(); 1002 1003 const GrRenderTarget* rt = drawState->getRenderTarget(); 1004 if (NULL == rt) { 1005 return; 1006 } 1007 1008 GrDrawState::AutoDeviceCoordDraw adcd(drawState); 1009 if (!adcd.succeeded()) { 1010 return; 1011 } 1012 1013 GrVertexLayout layout = GrDrawTarget::kEdge_VertexLayoutBit; 1014 GrAssert(sizeof(CircleVertex) == GrDrawTarget::VertexSize(layout)); 1015 1016 GrPoint center = GrPoint::Make(rect.centerX(), rect.centerY()); 1017 GrScalar radius = SkScalarHalf(rect.width()); 1018 1019 vm.mapPoints(¢er, 1); 1020 radius = vm.mapRadius(radius); 1021 1022 GrScalar outerRadius = radius; 1023 GrScalar innerRadius = 0; 1024 SkScalar halfWidth = 0; 1025 if (strokeWidth == 0) { 1026 halfWidth = SkScalarHalf(SK_Scalar1); 1027 1028 outerRadius += halfWidth; 1029 innerRadius = SkMaxScalar(0, radius - halfWidth); 1030 } 1031 1032 GrDrawTarget::AutoReleaseGeometry geo(target, layout, 4, 0); 1033 if (!geo.succeeded()) { 1034 GrPrintf("Failed to get space for vertices!\n"); 1035 return; 1036 } 1037 1038 CircleVertex* verts = reinterpret_cast<CircleVertex*>(geo.vertices()); 1039 1040 // The fragment shader will extend the radius out half a pixel 1041 // to antialias. Expand the drawn rect here so all the pixels 1042 // will be captured. 1043 SkScalar L = center.fX - outerRadius - SkFloatToScalar(0.5f); 1044 SkScalar R = center.fX + outerRadius + SkFloatToScalar(0.5f); 1045 SkScalar T = center.fY - outerRadius - SkFloatToScalar(0.5f); 1046 SkScalar B = center.fY + outerRadius + SkFloatToScalar(0.5f); 1047 1048 verts[0].fPos = SkPoint::Make(L, T); 1049 verts[1].fPos = SkPoint::Make(R, T); 1050 verts[2].fPos = SkPoint::Make(L, B); 1051 verts[3].fPos = SkPoint::Make(R, B); 1052 1053 for (int i = 0; i < 4; ++i) { 1054 verts[i].fCenter = center; 1055 verts[i].fOuterRadius = outerRadius; 1056 verts[i].fInnerRadius = innerRadius; 1057 } 1058 1059 drawState->setVertexEdgeType(GrDrawState::kCircle_EdgeType); 1060 target->drawNonIndexed(kTriangleStrip_GrPrimitiveType, 0, 4); 1061} 1062 1063void GrContext::drawPath(const GrPaint& paint, const SkPath& path, GrPathFill fill) { 1064 1065 if (path.isEmpty()) { 1066 if (GrIsFillInverted(fill)) { 1067 this->drawPaint(paint); 1068 } 1069 return; 1070 } 1071 1072 SkRect ovalRect; 1073 if (!GrIsFillInverted(fill) && path.isOval(&ovalRect)) { 1074 SkScalar width = (fill == kHairLine_GrPathFill) ? 0 : -SK_Scalar1; 1075 this->drawOval(paint, ovalRect, width); 1076 return; 1077 } 1078 1079 this->internalDrawPath(paint, path, fill); 1080} 1081 1082void GrContext::internalDrawPath(const GrPaint& paint, const SkPath& path, GrPathFill fill) { 1083 1084 // Note that below we may sw-rasterize the path into a scratch texture. 1085 // Scratch textures can be recycled after they are returned to the texture 1086 // cache. This presents a potential hazard for buffered drawing. However, 1087 // the writePixels that uploads to the scratch will perform a flush so we're 1088 // OK. 1089 GrDrawTarget* target = this->prepareToDraw(&paint, DEFAULT_BUFFERING); 1090 GrDrawState::AutoStageDisable atr(fDrawState); 1091 1092 bool prAA = paint.isAntiAlias() && !this->getRenderTarget()->isMultisampled(); 1093 1094 // An Assumption here is that path renderer would use some form of tweaking 1095 // the src color (either the input alpha or in the frag shader) to implement 1096 // aa. If we have some future driver-mojo path AA that can do the right 1097 // thing WRT to the blend then we'll need some query on the PR. 1098 if (disable_coverage_aa_for_blend(target)) { 1099#if GR_DEBUG 1100 //GrPrintf("Turning off AA to correctly apply blend.\n"); 1101#endif 1102 prAA = false; 1103 } 1104 1105 GrPathRenderer* pr = this->getPathRenderer(path, fill, target, prAA, true); 1106 if (NULL == pr) { 1107#if GR_DEBUG 1108 GrPrintf("Unable to find path renderer compatible with path.\n"); 1109#endif 1110 return; 1111 } 1112 1113 pr->drawPath(path, fill, target, prAA); 1114} 1115 1116//////////////////////////////////////////////////////////////////////////////// 1117 1118void GrContext::flush(int flagsBitfield) { 1119 if (kDiscard_FlushBit & flagsBitfield) { 1120 fDrawBuffer->reset(); 1121 } else { 1122 this->flushDrawBuffer(); 1123 } 1124 if (kForceCurrentRenderTarget_FlushBit & flagsBitfield) { 1125 fGpu->forceRenderTargetFlush(); 1126 } 1127} 1128 1129void GrContext::flushDrawBuffer() { 1130 if (fDrawBuffer) { 1131 // With addition of the AA clip path, flushing the draw buffer can 1132 // result in the generation of an AA clip mask. During this 1133 // process the SW path renderer may be invoked which recusively 1134 // calls this method (via internalWriteTexturePixels) creating 1135 // infinite recursion 1136 GrInOrderDrawBuffer* temp = fDrawBuffer; 1137 fDrawBuffer = NULL; 1138 1139 temp->flushTo(fGpu); 1140 1141 fDrawBuffer = temp; 1142 } 1143} 1144 1145void GrContext::writeTexturePixels(GrTexture* texture, 1146 int left, int top, int width, int height, 1147 GrPixelConfig config, const void* buffer, size_t rowBytes, 1148 uint32_t flags) { 1149 SK_TRACE_EVENT0("GrContext::writeTexturePixels"); 1150 ASSERT_OWNED_RESOURCE(texture); 1151 1152 // TODO: use scratch texture to perform conversion 1153 if (kUnpremul_PixelOpsFlag & flags) { 1154 return; 1155 } 1156 if (!(kDontFlush_PixelOpsFlag & flags)) { 1157 this->flush(); 1158 } 1159 1160 fGpu->writeTexturePixels(texture, left, top, width, height, 1161 config, buffer, rowBytes); 1162} 1163 1164bool GrContext::readTexturePixels(GrTexture* texture, 1165 int left, int top, int width, int height, 1166 GrPixelConfig config, void* buffer, size_t rowBytes, 1167 uint32_t flags) { 1168 SK_TRACE_EVENT0("GrContext::readTexturePixels"); 1169 ASSERT_OWNED_RESOURCE(texture); 1170 1171 // TODO: code read pixels for textures that aren't also rendertargets 1172 GrRenderTarget* target = texture->asRenderTarget(); 1173 if (NULL != target) { 1174 return this->readRenderTargetPixels(target, 1175 left, top, width, height, 1176 config, buffer, rowBytes, 1177 flags); 1178 } else { 1179 return false; 1180 } 1181} 1182 1183#include "SkConfig8888.h" 1184 1185namespace { 1186/** 1187 * Converts a GrPixelConfig to a SkCanvas::Config8888. Only byte-per-channel 1188 * formats are representable as Config8888 and so the function returns false 1189 * if the GrPixelConfig has no equivalent Config8888. 1190 */ 1191bool grconfig_to_config8888(GrPixelConfig config, 1192 bool unpremul, 1193 SkCanvas::Config8888* config8888) { 1194 switch (config) { 1195 case kRGBA_8888_GrPixelConfig: 1196 if (unpremul) { 1197 *config8888 = SkCanvas::kRGBA_Unpremul_Config8888; 1198 } else { 1199 *config8888 = SkCanvas::kRGBA_Premul_Config8888; 1200 } 1201 return true; 1202 case kBGRA_8888_GrPixelConfig: 1203 if (unpremul) { 1204 *config8888 = SkCanvas::kBGRA_Unpremul_Config8888; 1205 } else { 1206 *config8888 = SkCanvas::kBGRA_Premul_Config8888; 1207 } 1208 return true; 1209 default: 1210 return false; 1211 } 1212} 1213 1214// It returns a configuration with where the byte position of the R & B components are swapped in 1215// relation to the input config. This should only be called with the result of 1216// grconfig_to_config8888 as it will fail for other configs. 1217SkCanvas::Config8888 swap_config8888_red_and_blue(SkCanvas::Config8888 config8888) { 1218 switch (config8888) { 1219 case SkCanvas::kBGRA_Premul_Config8888: 1220 return SkCanvas::kRGBA_Premul_Config8888; 1221 case SkCanvas::kBGRA_Unpremul_Config8888: 1222 return SkCanvas::kRGBA_Unpremul_Config8888; 1223 case SkCanvas::kRGBA_Premul_Config8888: 1224 return SkCanvas::kBGRA_Premul_Config8888; 1225 case SkCanvas::kRGBA_Unpremul_Config8888: 1226 return SkCanvas::kBGRA_Unpremul_Config8888; 1227 default: 1228 GrCrash("Unexpected input"); 1229 return SkCanvas::kBGRA_Unpremul_Config8888;; 1230 } 1231} 1232} 1233 1234bool GrContext::readRenderTargetPixels(GrRenderTarget* target, 1235 int left, int top, int width, int height, 1236 GrPixelConfig config, void* buffer, size_t rowBytes, 1237 uint32_t flags) { 1238 SK_TRACE_EVENT0("GrContext::readRenderTargetPixels"); 1239 ASSERT_OWNED_RESOURCE(target); 1240 1241 if (NULL == target) { 1242 target = fDrawState->getRenderTarget(); 1243 if (NULL == target) { 1244 return false; 1245 } 1246 } 1247 1248 if (!(kDontFlush_PixelOpsFlag & flags)) { 1249 this->flush(); 1250 } 1251 1252 // Determine which conversions have to be applied: flipY, swapRAnd, and/or unpremul. 1253 1254 // If fGpu->readPixels would incur a y-flip cost then we will read the pixels upside down. We'll 1255 // either do the flipY by drawing into a scratch with a matrix or on the cpu after the read. 1256 bool flipY = fGpu->readPixelsWillPayForYFlip(target, left, top, 1257 width, height, config, 1258 rowBytes); 1259 bool swapRAndB = fGpu->preferredReadPixelsConfig(config) == GrPixelConfigSwapRAndB(config); 1260 1261 bool unpremul = SkToBool(kUnpremul_PixelOpsFlag & flags); 1262 1263 // flipY will get set to false when it is handled below using a scratch. However, in that case 1264 // we still want to do the read upside down. 1265 bool readUpsideDown = flipY; 1266 1267 if (unpremul && kRGBA_8888_GrPixelConfig != config && kBGRA_8888_GrPixelConfig != config) { 1268 // The unpremul flag is only allowed for these two configs. 1269 return false; 1270 } 1271 1272 GrPixelConfig readConfig; 1273 if (swapRAndB) { 1274 readConfig = GrPixelConfigSwapRAndB(config); 1275 GrAssert(kUnknown_GrPixelConfig != config); 1276 } else { 1277 readConfig = config; 1278 } 1279 1280 // If the src is a texture and we would have to do conversions after read pixels, we instead 1281 // do the conversions by drawing the src to a scratch texture. If we handle any of the 1282 // conversions in the draw we set the corresponding bool to false so that we don't reapply it 1283 // on the read back pixels. 1284 GrTexture* src = target->asTexture(); 1285 GrAutoScratchTexture ast; 1286 if (NULL != src && (swapRAndB || unpremul || flipY)) { 1287 // Make the scratch a render target because we don't have a robust readTexturePixels as of 1288 // yet. It calls this function. 1289 GrTextureDesc desc; 1290 desc.fFlags = kRenderTarget_GrTextureFlagBit; 1291 desc.fWidth = width; 1292 desc.fHeight = height; 1293 desc.fConfig = readConfig; 1294 1295 // When a full readback is faster than a partial we could always make the scratch exactly 1296 // match the passed rect. However, if we see many different size rectangles we will trash 1297 // our texture cache and pay the cost of creating and destroying many textures. So, we only 1298 // request an exact match when the caller is reading an entire RT. 1299 ScratchTexMatch match = kApprox_ScratchTexMatch; 1300 if (0 == left && 1301 0 == top && 1302 target->width() == width && 1303 target->height() == height && 1304 fGpu->fullReadPixelsIsFasterThanPartial()) { 1305 match = kExact_ScratchTexMatch; 1306 } 1307 ast.set(this, desc, match); 1308 GrTexture* texture = ast.texture(); 1309 if (texture) { 1310 GrEffectStage stage; 1311 // compute a matrix to perform the draw 1312 GrMatrix textureMatrix; 1313 if (flipY) { 1314 textureMatrix.setTranslate(SK_Scalar1 * left, 1315 SK_Scalar1 * (top + height)); 1316 textureMatrix.set(GrMatrix::kMScaleY, -GR_Scalar1); 1317 } else { 1318 textureMatrix.setTranslate(SK_Scalar1 *left, SK_Scalar1 *top); 1319 } 1320 textureMatrix.postIDiv(src->width(), src->height()); 1321 1322 bool effectInstalled = false; 1323 if (unpremul) { 1324 if (this->installPMToUPMEffect(src, swapRAndB, textureMatrix, &stage)) { 1325 effectInstalled = true; 1326 unpremul = false; // we no longer need to do this on CPU after the readback. 1327 } 1328 } 1329 // If we failed to create a PM->UPM effect and have no other conversions to perform then 1330 // there is no longer any point to using the scratch. 1331 if (effectInstalled || flipY || swapRAndB) { 1332 if (!effectInstalled) { 1333 SkAssertResult(GrConfigConversionEffect::InstallEffect( 1334 src, 1335 swapRAndB, 1336 GrConfigConversionEffect::kNone_PMConversion, 1337 textureMatrix, 1338 &stage)); 1339 } 1340 swapRAndB = false; // we will handle the swap in the draw. 1341 flipY = false; // we already incorporated the y flip in the matrix 1342 1343 GrDrawTarget::AutoStateRestore asr(fGpu, GrDrawTarget::kReset_ASRInit); 1344 GrDrawState* drawState = fGpu->drawState(); 1345 *drawState->stage(0) = stage; 1346 1347 drawState->setRenderTarget(texture->asRenderTarget()); 1348 GrRect rect = GrRect::MakeWH(GrIntToScalar(width), GrIntToScalar(height)); 1349 fGpu->drawSimpleRect(rect, NULL); 1350 // we want to read back from the scratch's origin 1351 left = 0; 1352 top = 0; 1353 target = texture->asRenderTarget(); 1354 } 1355 } 1356 } 1357 if (!fGpu->readPixels(target, 1358 left, top, width, height, 1359 readConfig, buffer, rowBytes, readUpsideDown)) { 1360 return false; 1361 } 1362 // Perform any conversions we weren't able to perform using a scratch texture. 1363 if (unpremul || swapRAndB || flipY) { 1364 // These are initialized to suppress a warning 1365 SkCanvas::Config8888 srcC8888 = SkCanvas::kNative_Premul_Config8888; 1366 SkCanvas::Config8888 dstC8888 = SkCanvas::kNative_Premul_Config8888; 1367 1368 bool c8888IsValid = grconfig_to_config8888(config, false, &srcC8888); 1369 grconfig_to_config8888(config, unpremul, &dstC8888); 1370 1371 if (swapRAndB) { 1372 GrAssert(c8888IsValid); // we should only do r/b swap on 8888 configs 1373 srcC8888 = swap_config8888_red_and_blue(srcC8888); 1374 } 1375 if (flipY) { 1376 size_t tightRB = width * GrBytesPerPixel(config); 1377 if (0 == rowBytes) { 1378 rowBytes = tightRB; 1379 } 1380 SkAutoSTMalloc<256, uint8_t> tempRow(tightRB); 1381 intptr_t top = reinterpret_cast<intptr_t>(buffer); 1382 intptr_t bot = top + (height - 1) * rowBytes; 1383 while (top < bot) { 1384 uint32_t* t = reinterpret_cast<uint32_t*>(top); 1385 uint32_t* b = reinterpret_cast<uint32_t*>(bot); 1386 uint32_t* temp = reinterpret_cast<uint32_t*>(tempRow.get()); 1387 memcpy(temp, t, tightRB); 1388 if (c8888IsValid) { 1389 SkConvertConfig8888Pixels(t, tightRB, dstC8888, 1390 b, tightRB, srcC8888, 1391 width, 1); 1392 SkConvertConfig8888Pixels(b, tightRB, dstC8888, 1393 temp, tightRB, srcC8888, 1394 width, 1); 1395 } else { 1396 memcpy(t, b, tightRB); 1397 memcpy(b, temp, tightRB); 1398 } 1399 top += rowBytes; 1400 bot -= rowBytes; 1401 } 1402 // The above loop does nothing on the middle row when height is odd. 1403 if (top == bot && c8888IsValid && dstC8888 != srcC8888) { 1404 uint32_t* mid = reinterpret_cast<uint32_t*>(top); 1405 SkConvertConfig8888Pixels(mid, tightRB, dstC8888, mid, tightRB, srcC8888, width, 1); 1406 } 1407 } else { 1408 // if we aren't flipping Y then we have no reason to be here other than doing 1409 // conversions for 8888 (r/b swap or upm). 1410 GrAssert(c8888IsValid); 1411 uint32_t* b32 = reinterpret_cast<uint32_t*>(buffer); 1412 SkConvertConfig8888Pixels(b32, rowBytes, dstC8888, 1413 b32, rowBytes, srcC8888, 1414 width, height); 1415 } 1416 } 1417 return true; 1418} 1419 1420void GrContext::resolveRenderTarget(GrRenderTarget* target) { 1421 GrAssert(target); 1422 ASSERT_OWNED_RESOURCE(target); 1423 // In the future we may track whether there are any pending draws to this 1424 // target. We don't today so we always perform a flush. We don't promise 1425 // this to our clients, though. 1426 this->flush(); 1427 fGpu->resolveRenderTarget(target); 1428} 1429 1430void GrContext::copyTexture(GrTexture* src, GrRenderTarget* dst) { 1431 if (NULL == src || NULL == dst) { 1432 return; 1433 } 1434 ASSERT_OWNED_RESOURCE(src); 1435 1436 // Writes pending to the source texture are not tracked, so a flush 1437 // is required to ensure that the copy captures the most recent contents 1438 // of the source texture. See similar behaviour in 1439 // GrContext::resolveRenderTarget. 1440 this->flush(); 1441 1442 GrDrawTarget::AutoStateRestore asr(fGpu, GrDrawTarget::kReset_ASRInit); 1443 GrDrawState* drawState = fGpu->drawState(); 1444 drawState->setRenderTarget(dst); 1445 GrMatrix sampleM; 1446 sampleM.setIDiv(src->width(), src->height()); 1447 drawState->createTextureEffect(0, src, sampleM); 1448 SkRect rect = SkRect::MakeXYWH(0, 0, 1449 SK_Scalar1 * src->width(), 1450 SK_Scalar1 * src->height()); 1451 fGpu->drawSimpleRect(rect, NULL); 1452} 1453 1454void GrContext::writeRenderTargetPixels(GrRenderTarget* target, 1455 int left, int top, int width, int height, 1456 GrPixelConfig config, 1457 const void* buffer, 1458 size_t rowBytes, 1459 uint32_t flags) { 1460 SK_TRACE_EVENT0("GrContext::writeRenderTargetPixels"); 1461 ASSERT_OWNED_RESOURCE(target); 1462 1463 if (NULL == target) { 1464 target = fDrawState->getRenderTarget(); 1465 if (NULL == target) { 1466 return; 1467 } 1468 } 1469 1470 // TODO: when underlying api has a direct way to do this we should use it (e.g. glDrawPixels on 1471 // desktop GL). 1472 1473 // We will always call some form of writeTexturePixels and we will pass our flags on to it. 1474 // Thus, we don't perform a flush here since that call will do it (if the kNoFlush flag isn't 1475 // set.) 1476 1477 // If the RT is also a texture and we don't have to premultiply then take the texture path. 1478 // We expect to be at least as fast or faster since it doesn't use an intermediate texture as 1479 // we do below. 1480 1481#if !GR_MAC_BUILD 1482 // At least some drivers on the Mac get confused when glTexImage2D is called on a texture 1483 // attached to an FBO. The FBO still sees the old image. TODO: determine what OS versions and/or 1484 // HW is affected. 1485 if (NULL != target->asTexture() && !(kUnpremul_PixelOpsFlag & flags)) { 1486 this->writeTexturePixels(target->asTexture(), 1487 left, top, width, height, 1488 config, buffer, rowBytes, flags); 1489 return; 1490 } 1491#endif 1492 1493 bool swapRAndB = (fGpu->preferredReadPixelsConfig(config) == GrPixelConfigSwapRAndB(config)); 1494 1495 GrPixelConfig textureConfig; 1496 if (swapRAndB) { 1497 textureConfig = GrPixelConfigSwapRAndB(config); 1498 } else { 1499 textureConfig = config; 1500 } 1501 1502 GrTextureDesc desc; 1503 desc.fWidth = width; 1504 desc.fHeight = height; 1505 desc.fConfig = textureConfig; 1506 GrAutoScratchTexture ast(this, desc); 1507 GrTexture* texture = ast.texture(); 1508 if (NULL == texture) { 1509 return; 1510 } 1511 1512 GrEffectStage stage; 1513 GrMatrix textureMatrix; 1514 textureMatrix.setIDiv(texture->width(), texture->height()); 1515 1516 // allocate a tmp buffer and sw convert the pixels to premul 1517 SkAutoSTMalloc<128 * 128, uint32_t> tmpPixels(0); 1518 1519 bool effectInstalled = false; 1520 if (kUnpremul_PixelOpsFlag & flags) { 1521 if (kRGBA_8888_GrPixelConfig != config && kBGRA_8888_GrPixelConfig != config) { 1522 return; 1523 } 1524 effectInstalled = this->installUPMToPMEffect(texture, 1525 swapRAndB, 1526 textureMatrix, 1527 &stage); 1528 if (!effectInstalled) { 1529 SkCanvas::Config8888 srcConfig8888, dstConfig8888; 1530 GR_DEBUGCODE(bool success = ) 1531 grconfig_to_config8888(config, true, &srcConfig8888); 1532 GrAssert(success); 1533 GR_DEBUGCODE(success = ) 1534 grconfig_to_config8888(config, false, &dstConfig8888); 1535 GrAssert(success); 1536 const uint32_t* src = reinterpret_cast<const uint32_t*>(buffer); 1537 tmpPixels.reset(width * height); 1538 SkConvertConfig8888Pixels(tmpPixels.get(), 4 * width, dstConfig8888, 1539 src, rowBytes, srcConfig8888, 1540 width, height); 1541 buffer = tmpPixels.get(); 1542 rowBytes = 4 * width; 1543 } 1544 } 1545 if (!effectInstalled) { 1546 SkAssertResult(GrConfigConversionEffect::InstallEffect( 1547 texture, 1548 swapRAndB, 1549 GrConfigConversionEffect::kNone_PMConversion, 1550 textureMatrix, 1551 &stage)); 1552 } 1553 1554 this->writeTexturePixels(texture, 1555 0, 0, width, height, 1556 textureConfig, buffer, rowBytes, 1557 flags & ~kUnpremul_PixelOpsFlag); 1558 1559 GrDrawTarget::AutoStateRestore asr(fGpu, GrDrawTarget::kReset_ASRInit); 1560 GrDrawState* drawState = fGpu->drawState(); 1561 *drawState->stage(0) = stage; 1562 1563 GrMatrix matrix; 1564 matrix.setTranslate(GrIntToScalar(left), GrIntToScalar(top)); 1565 drawState->setViewMatrix(matrix); 1566 drawState->setRenderTarget(target); 1567 1568 fGpu->drawSimpleRect(GrRect::MakeWH(SkIntToScalar(width), SkIntToScalar(height)), NULL); 1569} 1570//////////////////////////////////////////////////////////////////////////////// 1571 1572GrDrawTarget* GrContext::prepareToDraw(const GrPaint* paint, BufferedDraw buffered) { 1573 if (kNo_BufferedDraw == buffered && kYes_BufferedDraw == fLastDrawWasBuffered) { 1574 this->flushDrawBuffer(); 1575 fLastDrawWasBuffered = kNo_BufferedDraw; 1576 } 1577 if (NULL != paint) { 1578 GrAssert(fDrawState->stagesDisabled()); 1579 fDrawState->setFromPaint(*paint); 1580#if GR_DEBUG_PARTIAL_COVERAGE_CHECK 1581 if ((paint->hasMask() || 0xff != paint->fCoverage) && 1582 !fGpu->canApplyCoverage()) { 1583 GrPrintf("Partial pixel coverage will be incorrectly blended.\n"); 1584 } 1585#endif 1586 } 1587 if (kYes_BufferedDraw == buffered) { 1588 fDrawBuffer->setClip(fGpu->getClip()); 1589 fLastDrawWasBuffered = kYes_BufferedDraw; 1590 return fDrawBuffer; 1591 } else { 1592 GrAssert(kNo_BufferedDraw == buffered); 1593 return fGpu; 1594 } 1595} 1596 1597/* 1598 * This method finds a path renderer that can draw the specified path on 1599 * the provided target. 1600 * Due to its expense, the software path renderer has split out so it can 1601 * can be individually allowed/disallowed via the "allowSW" boolean. 1602 */ 1603GrPathRenderer* GrContext::getPathRenderer(const SkPath& path, 1604 GrPathFill fill, 1605 const GrDrawTarget* target, 1606 bool antiAlias, 1607 bool allowSW) { 1608 if (NULL == fPathRendererChain) { 1609 fPathRendererChain = 1610 SkNEW_ARGS(GrPathRendererChain, 1611 (this, GrPathRendererChain::kNone_UsageFlag)); 1612 } 1613 1614 GrPathRenderer* pr = fPathRendererChain->getPathRenderer(path, fill, 1615 target, 1616 antiAlias); 1617 1618 if (NULL == pr && allowSW) { 1619 if (NULL == fSoftwarePathRenderer) { 1620 fSoftwarePathRenderer = SkNEW_ARGS(GrSoftwarePathRenderer, (this)); 1621 } 1622 1623 pr = fSoftwarePathRenderer; 1624 } 1625 1626 return pr; 1627} 1628 1629//////////////////////////////////////////////////////////////////////////////// 1630 1631void GrContext::setRenderTarget(GrRenderTarget* target) { 1632 ASSERT_OWNED_RESOURCE(target); 1633 fDrawState->setRenderTarget(target); 1634} 1635 1636GrRenderTarget* GrContext::getRenderTarget() { 1637 return fDrawState->getRenderTarget(); 1638} 1639 1640const GrRenderTarget* GrContext::getRenderTarget() const { 1641 return fDrawState->getRenderTarget(); 1642} 1643 1644bool GrContext::isConfigRenderable(GrPixelConfig config) const { 1645 return fGpu->isConfigRenderable(config); 1646} 1647 1648const GrMatrix& GrContext::getMatrix() const { 1649 return fDrawState->getViewMatrix(); 1650} 1651 1652void GrContext::setMatrix(const GrMatrix& m) { 1653 fDrawState->setViewMatrix(m); 1654} 1655 1656void GrContext::setIdentityMatrix() { 1657 fDrawState->viewMatrix()->reset(); 1658} 1659 1660void GrContext::concatMatrix(const GrMatrix& m) const { 1661 fDrawState->preConcatViewMatrix(m); 1662} 1663 1664static inline intptr_t setOrClear(intptr_t bits, int shift, intptr_t pred) { 1665 intptr_t mask = 1 << shift; 1666 if (pred) { 1667 bits |= mask; 1668 } else { 1669 bits &= ~mask; 1670 } 1671 return bits; 1672} 1673 1674GrContext::GrContext(GrGpu* gpu) { 1675 ++THREAD_INSTANCE_COUNT; 1676 1677 fGpu = gpu; 1678 fGpu->ref(); 1679 fGpu->setContext(this); 1680 1681 fDrawState = SkNEW(GrDrawState); 1682 fGpu->setDrawState(fDrawState); 1683 1684 fPathRendererChain = NULL; 1685 fSoftwarePathRenderer = NULL; 1686 1687 fTextureCache = SkNEW_ARGS(GrResourceCache, 1688 (MAX_TEXTURE_CACHE_COUNT, 1689 MAX_TEXTURE_CACHE_BYTES)); 1690 fFontCache = SkNEW_ARGS(GrFontCache, (fGpu)); 1691 1692 fLastDrawWasBuffered = kNo_BufferedDraw; 1693 1694 fDrawBuffer = NULL; 1695 fDrawBufferVBAllocPool = NULL; 1696 fDrawBufferIBAllocPool = NULL; 1697 1698 fAARectRenderer = SkNEW(GrAARectRenderer); 1699 1700 fDidTestPMConversions = false; 1701 1702 this->setupDrawBuffer(); 1703} 1704 1705void GrContext::setupDrawBuffer() { 1706 1707 GrAssert(NULL == fDrawBuffer); 1708 GrAssert(NULL == fDrawBufferVBAllocPool); 1709 GrAssert(NULL == fDrawBufferIBAllocPool); 1710 1711 fDrawBufferVBAllocPool = 1712 SkNEW_ARGS(GrVertexBufferAllocPool, (fGpu, false, 1713 DRAW_BUFFER_VBPOOL_BUFFER_SIZE, 1714 DRAW_BUFFER_VBPOOL_PREALLOC_BUFFERS)); 1715 fDrawBufferIBAllocPool = 1716 SkNEW_ARGS(GrIndexBufferAllocPool, (fGpu, false, 1717 DRAW_BUFFER_IBPOOL_BUFFER_SIZE, 1718 DRAW_BUFFER_IBPOOL_PREALLOC_BUFFERS)); 1719 1720 fDrawBuffer = SkNEW_ARGS(GrInOrderDrawBuffer, (fGpu, 1721 fDrawBufferVBAllocPool, 1722 fDrawBufferIBAllocPool)); 1723 1724 fDrawBuffer->setQuadIndexBuffer(this->getQuadIndexBuffer()); 1725 if (fDrawBuffer) { 1726 fDrawBuffer->setAutoFlushTarget(fGpu); 1727 fDrawBuffer->setDrawState(fDrawState); 1728 } 1729} 1730 1731GrDrawTarget* GrContext::getTextTarget(const GrPaint& paint) { 1732 return prepareToDraw(&paint, DEFAULT_BUFFERING); 1733} 1734 1735const GrIndexBuffer* GrContext::getQuadIndexBuffer() const { 1736 return fGpu->getQuadIndexBuffer(); 1737} 1738 1739namespace { 1740void test_pm_conversions(GrContext* ctx, int* pmToUPMValue, int* upmToPMValue) { 1741 GrConfigConversionEffect::PMConversion pmToUPM; 1742 GrConfigConversionEffect::PMConversion upmToPM; 1743 GrConfigConversionEffect::TestForPreservingPMConversions(ctx, &pmToUPM, &upmToPM); 1744 *pmToUPMValue = pmToUPM; 1745 *upmToPMValue = upmToPM; 1746} 1747} 1748 1749bool GrContext::installPMToUPMEffect(GrTexture* texture, 1750 bool swapRAndB, 1751 const GrMatrix& matrix, 1752 GrEffectStage* stage) { 1753 if (!fDidTestPMConversions) { 1754 test_pm_conversions(this, &fPMToUPMConversion, &fUPMToPMConversion); 1755 fDidTestPMConversions = true; 1756 } 1757 GrConfigConversionEffect::PMConversion pmToUPM = 1758 static_cast<GrConfigConversionEffect::PMConversion>(fPMToUPMConversion); 1759 if (GrConfigConversionEffect::kNone_PMConversion != pmToUPM) { 1760 GrConfigConversionEffect::InstallEffect(texture, swapRAndB, pmToUPM, matrix, stage); 1761 return true; 1762 } else { 1763 return false; 1764 } 1765} 1766 1767bool GrContext::installUPMToPMEffect(GrTexture* texture, 1768 bool swapRAndB, 1769 const GrMatrix& matrix, 1770 GrEffectStage* stage) { 1771 if (!fDidTestPMConversions) { 1772 test_pm_conversions(this, &fPMToUPMConversion, &fUPMToPMConversion); 1773 fDidTestPMConversions = true; 1774 } 1775 GrConfigConversionEffect::PMConversion upmToPM = 1776 static_cast<GrConfigConversionEffect::PMConversion>(fUPMToPMConversion); 1777 if (GrConfigConversionEffect::kNone_PMConversion != upmToPM) { 1778 GrConfigConversionEffect::InstallEffect(texture, swapRAndB, upmToPM, matrix, stage); 1779 return true; 1780 } else { 1781 return false; 1782 } 1783} 1784 1785GrTexture* GrContext::gaussianBlur(GrTexture* srcTexture, 1786 bool canClobberSrc, 1787 const SkRect& rect, 1788 float sigmaX, float sigmaY) { 1789 ASSERT_OWNED_RESOURCE(srcTexture); 1790 1791 AutoRenderTarget art(this); 1792 1793 AutoMatrix am; 1794 am.setIdentity(this); 1795 1796 SkIRect clearRect; 1797 int scaleFactorX, radiusX; 1798 int scaleFactorY, radiusY; 1799 sigmaX = adjust_sigma(sigmaX, &scaleFactorX, &radiusX); 1800 sigmaY = adjust_sigma(sigmaY, &scaleFactorY, &radiusY); 1801 1802 SkRect srcRect(rect); 1803 scale_rect(&srcRect, 1.0f / scaleFactorX, 1.0f / scaleFactorY); 1804 srcRect.roundOut(); 1805 scale_rect(&srcRect, static_cast<float>(scaleFactorX), 1806 static_cast<float>(scaleFactorY)); 1807 1808 AutoClip acs(this, srcRect); 1809 1810 GrAssert(kBGRA_8888_GrPixelConfig == srcTexture->config() || 1811 kRGBA_8888_GrPixelConfig == srcTexture->config() || 1812 kAlpha_8_GrPixelConfig == srcTexture->config()); 1813 1814 GrTextureDesc desc; 1815 desc.fFlags = kRenderTarget_GrTextureFlagBit | kNoStencil_GrTextureFlagBit; 1816 desc.fWidth = SkScalarFloorToInt(srcRect.width()); 1817 desc.fHeight = SkScalarFloorToInt(srcRect.height()); 1818 desc.fConfig = srcTexture->config(); 1819 1820 GrAutoScratchTexture temp1, temp2; 1821 GrTexture* dstTexture = temp1.set(this, desc); 1822 GrTexture* tempTexture = canClobberSrc ? srcTexture : temp2.set(this, desc); 1823 if (NULL == dstTexture || NULL == tempTexture) { 1824 return NULL; 1825 } 1826 1827 GrPaint paint; 1828 paint.reset(); 1829 1830 for (int i = 1; i < scaleFactorX || i < scaleFactorY; i *= 2) { 1831 GrMatrix matrix; 1832 matrix.setIDiv(srcTexture->width(), srcTexture->height()); 1833 this->setRenderTarget(dstTexture->asRenderTarget()); 1834 SkRect dstRect(srcRect); 1835 scale_rect(&dstRect, i < scaleFactorX ? 0.5f : 1.0f, 1836 i < scaleFactorY ? 0.5f : 1.0f); 1837 1838 paint.colorStage(0)->setEffect(SkNEW_ARGS(GrSingleTextureEffect, 1839 (srcTexture, matrix, true)))->unref(); 1840 this->drawRectToRect(paint, dstRect, srcRect); 1841 srcRect = dstRect; 1842 srcTexture = dstTexture; 1843 SkTSwap(dstTexture, tempTexture); 1844 } 1845 1846 SkIRect srcIRect; 1847 srcRect.roundOut(&srcIRect); 1848 1849 if (sigmaX > 0.0f) { 1850 if (scaleFactorX > 1) { 1851 // Clear out a radius to the right of the srcRect to prevent the 1852 // X convolution from reading garbage. 1853 clearRect = SkIRect::MakeXYWH(srcIRect.fRight, srcIRect.fTop, 1854 radiusX, srcIRect.height()); 1855 this->clear(&clearRect, 0x0); 1856 } 1857 1858 this->setRenderTarget(dstTexture->asRenderTarget()); 1859 GrDrawTarget* target = this->prepareToDraw(NULL, DEFAULT_BUFFERING); 1860 convolve_gaussian(target, srcTexture, srcRect, sigmaX, radiusX, 1861 Gr1DKernelEffect::kX_Direction); 1862 srcTexture = dstTexture; 1863 SkTSwap(dstTexture, tempTexture); 1864 } 1865 1866 if (sigmaY > 0.0f) { 1867 if (scaleFactorY > 1 || sigmaX > 0.0f) { 1868 // Clear out a radius below the srcRect to prevent the Y 1869 // convolution from reading garbage. 1870 clearRect = SkIRect::MakeXYWH(srcIRect.fLeft, srcIRect.fBottom, 1871 srcIRect.width(), radiusY); 1872 this->clear(&clearRect, 0x0); 1873 } 1874 1875 this->setRenderTarget(dstTexture->asRenderTarget()); 1876 GrDrawTarget* target = this->prepareToDraw(NULL, DEFAULT_BUFFERING); 1877 convolve_gaussian(target, srcTexture, srcRect, sigmaY, radiusY, 1878 Gr1DKernelEffect::kY_Direction); 1879 srcTexture = dstTexture; 1880 SkTSwap(dstTexture, tempTexture); 1881 } 1882 1883 if (scaleFactorX > 1 || scaleFactorY > 1) { 1884 // Clear one pixel to the right and below, to accommodate bilinear 1885 // upsampling. 1886 clearRect = SkIRect::MakeXYWH(srcIRect.fLeft, srcIRect.fBottom, 1887 srcIRect.width() + 1, 1); 1888 this->clear(&clearRect, 0x0); 1889 clearRect = SkIRect::MakeXYWH(srcIRect.fRight, srcIRect.fTop, 1890 1, srcIRect.height()); 1891 this->clear(&clearRect, 0x0); 1892 GrMatrix matrix; 1893 // FIXME: This should be mitchell, not bilinear. 1894 matrix.setIDiv(srcTexture->width(), srcTexture->height()); 1895 this->setRenderTarget(dstTexture->asRenderTarget()); 1896 paint.colorStage(0)->setEffect(SkNEW_ARGS(GrSingleTextureEffect,(srcTexture, 1897 matrix, true)))->unref(); 1898 SkRect dstRect(srcRect); 1899 scale_rect(&dstRect, (float) scaleFactorX, (float) scaleFactorY); 1900 this->drawRectToRect(paint, dstRect, srcRect); 1901 srcRect = dstRect; 1902 srcTexture = dstTexture; 1903 SkTSwap(dstTexture, tempTexture); 1904 } 1905 if (srcTexture == temp1.texture()) { 1906 return temp1.detach(); 1907 } else if (srcTexture == temp2.texture()) { 1908 return temp2.detach(); 1909 } else { 1910 srcTexture->ref(); 1911 return srcTexture; 1912 } 1913} 1914 1915/////////////////////////////////////////////////////////////////////////////// 1916#if GR_CACHE_STATS 1917void GrContext::printCacheStats() const { 1918 fTextureCache->printStats(); 1919} 1920#endif 1921