static void convolve_gaussian_1d(GrDrawContext* drawContext, GrRenderTarget* rt, const GrClip& clip, const SkRect& srcRect, const SkRect& dstRect, GrTexture* texture, Gr1DKernelEffect::Direction direction, int radius, float sigma, bool useBounds, float bounds[2]) { GrPaint paint; SkAutoTUnref<GrFragmentProcessor> conv(GrConvolutionEffect::CreateGaussian( paint.getProcessorDataManager(), texture, direction, radius, sigma, useBounds, bounds)); paint.addColorProcessor(conv); drawContext->drawNonAARectToRect(rt, clip, paint, SkMatrix::I(), dstRect, srcRect); }
static void convolve_gaussian_2d(GrDrawContext* drawContext, GrRenderTarget* rt, const GrClip& clip, const SkRect& srcRect, const SkRect& dstRect, GrTexture* texture, int radiusX, int radiusY, SkScalar sigmaX, SkScalar sigmaY, bool useBounds, SkIRect bounds) { SkISize size = SkISize::Make(2 * radiusX + 1, 2 * radiusY + 1); SkIPoint kernelOffset = SkIPoint::Make(radiusX, radiusY); GrPaint paint; SkAutoTUnref<GrFragmentProcessor> conv(GrMatrixConvolutionEffect::CreateGaussian( paint.getProcessorDataManager(), texture, bounds, size, 1.0, 0.0, kernelOffset, useBounds ? GrTextureDomain::kClamp_Mode : GrTextureDomain::kIgnore_Mode, true, sigmaX, sigmaY)); paint.addColorProcessor(conv); drawContext->drawNonAARectToRect(rt, clip, paint, SkMatrix::I(), dstRect, srcRect); }
const GrFragmentProcessor* GrPerlinNoiseEffect::TestCreate(GrProcessorTestData* d) { int numOctaves = d->fRandom->nextRangeU(2, 10); bool stitchTiles = d->fRandom->nextBool(); SkScalar seed = SkIntToScalar(d->fRandom->nextU()); SkISize tileSize = SkISize::Make(d->fRandom->nextRangeU(4, 4096), d->fRandom->nextRangeU(4, 4096)); SkScalar baseFrequencyX = d->fRandom->nextRangeScalar(0.01f, 0.99f); SkScalar baseFrequencyY = d->fRandom->nextRangeScalar(0.01f, 0.99f); SkAutoTUnref<SkShader> shader(d->fRandom->nextBool() ? SkPerlinNoiseShader::CreateFractalNoise(baseFrequencyX, baseFrequencyY, numOctaves, seed, stitchTiles ? &tileSize : nullptr) : SkPerlinNoiseShader::CreateTurbulence(baseFrequencyX, baseFrequencyY, numOctaves, seed, stitchTiles ? &tileSize : nullptr)); GrPaint grPaint; return shader->asFragmentProcessor(d->fContext, GrTest::TestMatrix(d->fRandom), nullptr, kNone_SkFilterQuality, grPaint.getProcessorDataManager()); }
GrTexture* GaussianBlur(GrContext* context, GrTexture* srcTexture, bool canClobberSrc, const SkRect& rect, bool cropToRect, float sigmaX, float sigmaY) { SkASSERT(context); SkIRect clearRect; int scaleFactorX, radiusX; int scaleFactorY, radiusY; int maxTextureSize = context->caps()->maxTextureSize(); sigmaX = adjust_sigma(sigmaX, maxTextureSize, &scaleFactorX, &radiusX); sigmaY = adjust_sigma(sigmaY, maxTextureSize, &scaleFactorY, &radiusY); SkRect srcRect(rect); scale_rect(&srcRect, 1.0f / scaleFactorX, 1.0f / scaleFactorY); srcRect.roundOut(&srcRect); scale_rect(&srcRect, static_cast<float>(scaleFactorX), static_cast<float>(scaleFactorY)); // setup new clip GrClip clip(SkRect::MakeWH(srcRect.width(), srcRect.height())); SkASSERT(kBGRA_8888_GrPixelConfig == srcTexture->config() || kRGBA_8888_GrPixelConfig == srcTexture->config() || kAlpha_8_GrPixelConfig == srcTexture->config()); GrSurfaceDesc desc; desc.fFlags = kRenderTarget_GrSurfaceFlag; desc.fWidth = SkScalarFloorToInt(srcRect.width()); desc.fHeight = SkScalarFloorToInt(srcRect.height()); desc.fConfig = srcTexture->config(); GrTexture* dstTexture; GrTexture* tempTexture; SkAutoTUnref<GrTexture> temp1, temp2; temp1.reset(context->textureProvider()->refScratchTexture( desc, GrTextureProvider::kApprox_ScratchTexMatch)); dstTexture = temp1.get(); if (canClobberSrc) { tempTexture = srcTexture; } else { temp2.reset(context->textureProvider()->refScratchTexture( desc, GrTextureProvider::kApprox_ScratchTexMatch)); tempTexture = temp2.get(); } if (NULL == dstTexture || NULL == tempTexture) { return NULL; } GrDrawContext* drawContext = context->drawContext(); if (!drawContext) { return NULL; } for (int i = 1; i < scaleFactorX || i < scaleFactorY; i *= 2) { GrPaint paint; SkMatrix matrix; matrix.setIDiv(srcTexture->width(), srcTexture->height()); SkRect dstRect(srcRect); if (cropToRect && i == 1) { dstRect.offset(-dstRect.fLeft, -dstRect.fTop); SkRect domain; matrix.mapRect(&domain, rect); domain.inset(i < scaleFactorX ? SK_ScalarHalf / srcTexture->width() : 0.0f, i < scaleFactorY ? SK_ScalarHalf / srcTexture->height() : 0.0f); SkAutoTUnref<GrFragmentProcessor> fp( GrTextureDomainEffect::Create( paint.getProcessorDataManager(), srcTexture, matrix, domain, GrTextureDomain::kDecal_Mode, GrTextureParams::kBilerp_FilterMode)); paint.addColorProcessor(fp); } else { GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kBilerp_FilterMode); paint.addColorTextureProcessor(srcTexture, matrix, params); } scale_rect(&dstRect, i < scaleFactorX ? 0.5f : 1.0f, i < scaleFactorY ? 0.5f : 1.0f); drawContext->drawNonAARectToRect(dstTexture->asRenderTarget(), clip, paint, SkMatrix::I(), dstRect, srcRect); srcRect = dstRect; srcTexture = dstTexture; SkTSwap(dstTexture, tempTexture); } const SkIRect srcIRect = srcRect.roundOut(); // For really small blurs(Certainly no wider than 5x5 on desktop gpus) it is faster to just // launch a single non separable kernel vs two launches if (sigmaX > 0.0f && sigmaY > 0 && (2 * radiusX + 1) * (2 * radiusY + 1) <= MAX_KERNEL_SIZE) { // We shouldn't be scaling because this is a small size blur SkASSERT((scaleFactorX == scaleFactorY) == 1); SkRect dstRect = SkRect::MakeWH(srcRect.width(), srcRect.height()); convolve_gaussian_2d(drawContext, dstTexture->asRenderTarget(), clip, srcRect, dstRect, srcTexture, radiusX, radiusY, sigmaX, sigmaY, cropToRect, srcIRect); srcTexture = dstTexture; srcRect = dstRect; SkTSwap(dstTexture, tempTexture); } else { if (sigmaX > 0.0f) { if (scaleFactorX > 1) { // Clear out a radius to the right of the srcRect to prevent the // X convolution from reading garbage. clearRect = SkIRect::MakeXYWH(srcIRect.fRight, srcIRect.fTop, radiusX, srcIRect.height()); drawContext->clear(srcTexture->asRenderTarget(), &clearRect, 0x0, false); } SkRect dstRect = SkRect::MakeWH(srcRect.width(), srcRect.height()); convolve_gaussian(drawContext, dstTexture->asRenderTarget(), clip, srcRect, dstRect, srcTexture, Gr1DKernelEffect::kX_Direction, radiusX, sigmaX, cropToRect); srcTexture = dstTexture; srcRect = dstRect; SkTSwap(dstTexture, tempTexture); } if (sigmaY > 0.0f) { if (scaleFactorY > 1 || sigmaX > 0.0f) { // Clear out a radius below the srcRect to prevent the Y // convolution from reading garbage. clearRect = SkIRect::MakeXYWH(srcIRect.fLeft, srcIRect.fBottom, srcIRect.width(), radiusY); drawContext->clear(srcTexture->asRenderTarget(), &clearRect, 0x0, false); } SkRect dstRect = SkRect::MakeWH(srcRect.width(), srcRect.height()); convolve_gaussian(drawContext, dstTexture->asRenderTarget(), clip, srcRect, dstRect, srcTexture, Gr1DKernelEffect::kY_Direction, radiusY, sigmaY, cropToRect); srcTexture = dstTexture; srcRect = dstRect; SkTSwap(dstTexture, tempTexture); } } if (scaleFactorX > 1 || scaleFactorY > 1) { // Clear one pixel to the right and below, to accommodate bilinear // upsampling. clearRect = SkIRect::MakeXYWH(srcIRect.fLeft, srcIRect.fBottom, srcIRect.width() + 1, 1); drawContext->clear(srcTexture->asRenderTarget(), &clearRect, 0x0, false); clearRect = SkIRect::MakeXYWH(srcIRect.fRight, srcIRect.fTop, 1, srcIRect.height()); drawContext->clear(srcTexture->asRenderTarget(), &clearRect, 0x0, false); SkMatrix matrix; matrix.setIDiv(srcTexture->width(), srcTexture->height()); GrPaint paint; // FIXME: this should be mitchell, not bilinear. GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kBilerp_FilterMode); paint.addColorTextureProcessor(srcTexture, matrix, params); SkRect dstRect(srcRect); scale_rect(&dstRect, (float) scaleFactorX, (float) scaleFactorY); drawContext->drawNonAARectToRect(dstTexture->asRenderTarget(), clip, paint, SkMatrix::I(), dstRect, srcRect); srcRect = dstRect; srcTexture = dstTexture; SkTSwap(dstTexture, tempTexture); } return SkRef(srcTexture); }
static void test_getConstantColorComponents(skiatest::Reporter* reporter, GrContext* grContext) { struct GetConstantComponentTestCase { // "Shape drawn with" uint32_t inputComponents; // "rgb of", "red of", "alpha of", ... GrColor inputColor; // "[color]" SkColor filterColor; // "with filter color [color]" SkXfermode::Mode filterMode; // "in mode [mode]" // "produces" uint32_t outputComponents; // "rgb of", "red of", "alpha of", ... GrColor outputColor; // "[color]" }; // Shorthands. enum { kR = kR_GrColorComponentFlag, kG = kG_GrColorComponentFlag, kB = kB_GrColorComponentFlag, kA = kA_GrColorComponentFlag, kRGB = kRGB_GrColorComponentFlags, kRGBA = kRGBA_GrColorComponentFlags }; // Note: below, SkColors are non-premultiplied, where as GrColors are premultiplied. const SkColor c1 = SkColorSetARGB(200, 200, 200, 200); const SkColor c2 = SkColorSetARGB(60, 60, 60, 60); const GrColor gr_c1 = SkColor2GrColor(c1); const GrColor gr_c2 = SkColor2GrColor(c2); const GrColor gr_black = GrColorPackA4(0); const GrColor gr_white = GrColorPackA4(255); const GrColor gr_whiteTrans = GrColorPackA4(128); GetConstantComponentTestCase filterTests[] = { // A color filtered with Clear produces black. { kRGBA, gr_white, SK_ColorBLACK, SkXfermode::kClear_Mode, kRGBA, gr_black }, { kRGBA, gr_c1, SK_ColorWHITE, SkXfermode::kClear_Mode, kRGBA, gr_black }, { kR, gr_white, c1, SkXfermode::kClear_Mode, kRGBA, gr_black }, // A color filtered with a color in mode Src, produces the filter color. { kRGBA, gr_c2, c1, SkXfermode::kSrc_Mode, kRGBA, gr_c1 }, { kA, gr_c1, c1, SkXfermode::kSrc_Mode, kRGBA, gr_c1 }, // A color filtered with SrcOver produces a color. { kRGBA, gr_whiteTrans, SkColorSetARGB(128, 200, 200, 200), SkXfermode::kSrcOver_Mode, kRGBA, GrColorPackRGBA(164, 164, 164, 192)}, // An unknown color with known alpha filtered with SrcOver produces an unknown color with known alpha. { kA , gr_whiteTrans, SkColorSetARGB(128, 200, 200, 200), SkXfermode::kSrcOver_Mode, kA , GrColorPackRGBA(0, 0, 0, 192)}, // A color with unknown alpha filtered with SrcOver produces a color with unknown alpha. { kRGB , gr_whiteTrans, SkColorSetARGB(128, 200, 200, 200), SkXfermode::kSrcOver_Mode, kRGB, GrColorPackRGBA(164, 164, 164, 0)}, // A color filtered with DstOver produces a color. { kRGBA, gr_whiteTrans, SkColorSetARGB(128, 200, 200, 200), SkXfermode::kDstOver_Mode, kRGBA, GrColorPackRGBA(178, 178, 178, 192)}, // An unknown color with known alpha filtered with DstOver produces an unknown color with known alpha. { kA , gr_whiteTrans, SkColorSetARGB(128, 200, 200, 200), SkXfermode::kDstOver_Mode, kA , GrColorPackRGBA(0, 0, 0, 192)}, // A color with unknown alpha filtered with DstOver produces an unknown color. { kRGB , gr_whiteTrans, SkColorSetARGB(128, 200, 200, 200), SkXfermode::kDstOver_Mode, 0 , gr_black}, // An unknown color with known alpha and red component filtered with Multiply produces an unknown color with known red and alpha. { kR|kA , gr_whiteTrans, SkColorSetARGB(128, 200, 200, 200), SkXfermode::kModulate_Mode, kR|kA, GrColorPackRGBA(50, 0, 0, 64) } }; GrPaint paint; for (size_t i = 0; i < SK_ARRAY_COUNT(filterTests); ++i) { const GetConstantComponentTestCase& test = filterTests[i]; SkAutoTUnref<SkColorFilter> cf(SkColorFilter::CreateModeFilter(test.filterColor, test.filterMode)); SkTDArray<const GrFragmentProcessor*> array; bool hasFrag = cf->asFragmentProcessors(grContext, paint.getProcessorDataManager(), &array); REPORTER_ASSERT(reporter, hasFrag); REPORTER_ASSERT(reporter, 1 == array.count()); GrInvariantOutput inout(test.inputColor, static_cast<GrColorComponentFlags>(test.inputComponents), false); array[0]->computeInvariantOutput(&inout); REPORTER_ASSERT(reporter, filterColor(inout.color(), inout.validFlags()) == test.outputColor); REPORTER_ASSERT(reporter, test.outputComponents == inout.validFlags()); array[0]->unref(); } }
bool SkXfermodeImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, const Context& ctx, SkBitmap* result, SkIPoint* offset) const { SkBitmap background = src; SkIPoint backgroundOffset = SkIPoint::Make(0, 0); if (this->getInput(0) && !this->getInput(0)->getInputResultGPU(proxy, src, ctx, &background, &backgroundOffset)) { return this->onFilterImage(proxy, src, ctx, result, offset); } GrTexture* backgroundTex = background.getTexture(); if (NULL == backgroundTex) { SkASSERT(false); return false; } SkBitmap foreground = src; SkIPoint foregroundOffset = SkIPoint::Make(0, 0); if (this->getInput(1) && !this->getInput(1)->getInputResultGPU(proxy, src, ctx, &foreground, &foregroundOffset)) { return this->onFilterImage(proxy, src, ctx, result, offset); } GrTexture* foregroundTex = foreground.getTexture(); GrContext* context = foregroundTex->getContext(); GrFragmentProcessor* xferProcessor = NULL; GrSurfaceDesc desc; desc.fFlags = kRenderTarget_GrSurfaceFlag; desc.fWidth = src.width(); desc.fHeight = src.height(); desc.fConfig = kSkia8888_GrPixelConfig; SkAutoTUnref<GrTexture> dst(context->textureProvider()->refScratchTexture( desc, GrTextureProvider::kApprox_ScratchTexMatch)); if (!dst) { return false; } GrPaint paint; if (!fMode || !fMode->asFragmentProcessor(&xferProcessor, paint.getProcessorDataManager(), backgroundTex)) { // canFilterImageGPU() should've taken care of this SkASSERT(false); return false; } SkMatrix foregroundMatrix = GrCoordTransform::MakeDivByTextureWHMatrix(foregroundTex); foregroundMatrix.preTranslate(SkIntToScalar(backgroundOffset.fX-foregroundOffset.fX), SkIntToScalar(backgroundOffset.fY-foregroundOffset.fY)); SkRect srcRect; src.getBounds(&srcRect); SkAutoTUnref<GrFragmentProcessor> foregroundDomain(GrTextureDomainEffect::Create( paint.getProcessorDataManager(), foregroundTex, foregroundMatrix, GrTextureDomain::MakeTexelDomain(foregroundTex, foreground.bounds()), GrTextureDomain::kDecal_Mode, GrTextureParams::kNone_FilterMode) ); paint.addColorProcessor(foregroundDomain.get()); paint.addColorProcessor(xferProcessor)->unref(); GrDrawContext* drawContext = context->drawContext(); if (!drawContext) { return false; } drawContext->drawRect(dst->asRenderTarget(), GrClip::WideOpen(), paint, SkMatrix::I(), srcRect); offset->fX = backgroundOffset.fX; offset->fY = backgroundOffset.fY; WrapTexture(dst, src.width(), src.height(), result); return true; }
SkImage* SkImage::NewFromYUVTexturesCopy(GrContext* ctx , SkYUVColorSpace colorSpace, const GrBackendObject yuvTextureHandles[3], const SkISize yuvSizes[3], GrSurfaceOrigin origin) { const SkSurface::Budgeted budgeted = SkSurface::kYes_Budgeted; if (yuvSizes[0].fWidth <= 0 || yuvSizes[0].fHeight <= 0 || yuvSizes[1].fWidth <= 0 || yuvSizes[1].fHeight <= 0 || yuvSizes[2].fWidth <= 0 || yuvSizes[2].fHeight <= 0) { return nullptr; } static const GrPixelConfig kConfig = kAlpha_8_GrPixelConfig; GrBackendTextureDesc yDesc; yDesc.fConfig = kConfig; yDesc.fOrigin = origin; yDesc.fSampleCnt = 0; yDesc.fTextureHandle = yuvTextureHandles[0]; yDesc.fWidth = yuvSizes[0].fWidth; yDesc.fHeight = yuvSizes[0].fHeight; GrBackendTextureDesc uDesc; uDesc.fConfig = kConfig; uDesc.fOrigin = origin; uDesc.fSampleCnt = 0; uDesc.fTextureHandle = yuvTextureHandles[1]; uDesc.fWidth = yuvSizes[1].fWidth; uDesc.fHeight = yuvSizes[1].fHeight; GrBackendTextureDesc vDesc; vDesc.fConfig = kConfig; vDesc.fOrigin = origin; vDesc.fSampleCnt = 0; vDesc.fTextureHandle = yuvTextureHandles[2]; vDesc.fWidth = yuvSizes[2].fWidth; vDesc.fHeight = yuvSizes[2].fHeight; SkAutoTUnref<GrTexture> yTex(ctx->textureProvider()->wrapBackendTexture( yDesc, kBorrow_GrWrapOwnership)); SkAutoTUnref<GrTexture> uTex(ctx->textureProvider()->wrapBackendTexture( uDesc, kBorrow_GrWrapOwnership)); SkAutoTUnref<GrTexture> vTex(ctx->textureProvider()->wrapBackendTexture( vDesc, kBorrow_GrWrapOwnership)); if (!yTex || !uTex || !vTex) { return nullptr; } GrSurfaceDesc dstDesc; // Needs to be a render target in order to draw to it for the yuv->rgb conversion. dstDesc.fFlags = kRenderTarget_GrSurfaceFlag; dstDesc.fOrigin = origin; dstDesc.fWidth = yuvSizes[0].fWidth; dstDesc.fHeight = yuvSizes[0].fHeight; dstDesc.fConfig = kRGBA_8888_GrPixelConfig; dstDesc.fSampleCnt = 0; SkAutoTUnref<GrTexture> dst(ctx->textureProvider()->createTexture(dstDesc, true)); if (!dst) { return nullptr; } GrPaint paint; paint.setPorterDuffXPFactory(SkXfermode::kSrc_Mode); paint.addColorFragmentProcessor(GrYUVtoRGBEffect::Create(paint.getProcessorDataManager(), yTex, uTex, vTex, yuvSizes, colorSpace))->unref(); const SkRect rect = SkRect::MakeWH(SkIntToScalar(dstDesc.fWidth), SkIntToScalar(dstDesc.fHeight)); SkAutoTUnref<GrDrawContext> drawContext(ctx->drawContext()); if (!drawContext) { return nullptr; } drawContext->drawRect(dst->asRenderTarget(), GrClip::WideOpen(), paint, SkMatrix::I(), rect); ctx->flushSurfaceWrites(dst); return new SkImage_Gpu(dstDesc.fWidth, dstDesc.fHeight, kNeedNewImageUniqueID, kOpaque_SkAlphaType, dst, budgeted); }
static GrTexture* load_yuv_texture(GrContext* ctx, const GrUniqueKey& optionalKey, const SkBitmap& bm, const GrSurfaceDesc& desc) { // Subsets are not supported, the whole pixelRef is loaded when using YUV decoding SkPixelRef* pixelRef = bm.pixelRef(); if ((NULL == pixelRef) || (pixelRef->info().width() != bm.info().width()) || (pixelRef->info().height() != bm.info().height())) { return NULL; } const bool useCache = optionalKey.isValid(); SkYUVPlanesCache::Info yuvInfo; SkAutoTUnref<SkCachedData> cachedData; SkAutoMalloc storage; if (useCache) { cachedData.reset(SkYUVPlanesCache::FindAndRef(pixelRef->getGenerationID(), &yuvInfo)); } void* planes[3]; if (cachedData.get()) { planes[0] = (void*)cachedData->data(); planes[1] = (uint8_t*)planes[0] + yuvInfo.fSizeInMemory[0]; planes[2] = (uint8_t*)planes[1] + yuvInfo.fSizeInMemory[1]; } else { // Fetch yuv plane sizes for memory allocation. Here, width and height can be // rounded up to JPEG block size and be larger than the image's width and height. if (!pixelRef->getYUV8Planes(yuvInfo.fSize, NULL, NULL, NULL)) { return NULL; } // Allocate the memory for YUV size_t totalSize(0); for (int i = 0; i < 3; ++i) { yuvInfo.fRowBytes[i] = yuvInfo.fSize[i].fWidth; yuvInfo.fSizeInMemory[i] = yuvInfo.fRowBytes[i] * yuvInfo.fSize[i].fHeight; totalSize += yuvInfo.fSizeInMemory[i]; } if (useCache) { cachedData.reset(SkResourceCache::NewCachedData(totalSize)); planes[0] = cachedData->writable_data(); } else { storage.reset(totalSize); planes[0] = storage.get(); } planes[1] = (uint8_t*)planes[0] + yuvInfo.fSizeInMemory[0]; planes[2] = (uint8_t*)planes[1] + yuvInfo.fSizeInMemory[1]; // Get the YUV planes and update plane sizes to actual image size if (!pixelRef->getYUV8Planes(yuvInfo.fSize, planes, yuvInfo.fRowBytes, &yuvInfo.fColorSpace)) { return NULL; } if (useCache) { // Decoding is done, cache the resulting YUV planes SkYUVPlanesCache::Add(pixelRef->getGenerationID(), cachedData, &yuvInfo); } } GrSurfaceDesc yuvDesc; yuvDesc.fConfig = kAlpha_8_GrPixelConfig; SkAutoTUnref<GrTexture> yuvTextures[3]; for (int i = 0; i < 3; ++i) { yuvDesc.fWidth = yuvInfo.fSize[i].fWidth; yuvDesc.fHeight = yuvInfo.fSize[i].fHeight; bool needsExactTexture = (yuvDesc.fWidth != yuvInfo.fSize[0].fWidth) || (yuvDesc.fHeight != yuvInfo.fSize[0].fHeight); if (needsExactTexture) { yuvTextures[i].reset(ctx->textureProvider()->createTexture(yuvDesc, true)); } else { yuvTextures[i].reset(ctx->textureProvider()->createApproxTexture(yuvDesc)); } if (!yuvTextures[i] || !yuvTextures[i]->writePixels(0, 0, yuvDesc.fWidth, yuvDesc.fHeight, yuvDesc.fConfig, planes[i], yuvInfo.fRowBytes[i])) { return NULL; } } GrSurfaceDesc rtDesc = desc; rtDesc.fFlags = rtDesc.fFlags | kRenderTarget_GrSurfaceFlag; GrTexture* result = create_texture_for_bmp(ctx, optionalKey, rtDesc, pixelRef, NULL, 0); if (!result) { return NULL; } GrRenderTarget* renderTarget = result->asRenderTarget(); SkASSERT(renderTarget); GrPaint paint; SkAutoTUnref<GrFragmentProcessor> yuvToRgbProcessor(GrYUVtoRGBEffect::Create(paint.getProcessorDataManager(), yuvTextures[0], yuvTextures[1], yuvTextures[2], yuvInfo.fSize, yuvInfo.fColorSpace)); paint.addColorProcessor(yuvToRgbProcessor); SkRect r = SkRect::MakeWH(SkIntToScalar(yuvInfo.fSize[0].fWidth), SkIntToScalar(yuvInfo.fSize[0].fHeight)); GrDrawContext* drawContext = ctx->drawContext(); if (!drawContext) { return NULL; } drawContext->drawRect(renderTarget, GrClip::WideOpen(), paint, SkMatrix::I(), r); return result; }