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