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