void GrRenderTarget::discard() { // go through context so that all necessary flushing occurs GrContext* context = this->getContext(); GrDrawContext* drawContext = context ? context->drawContext() : NULL; if (!drawContext) { return; } drawContext->discard(this); }
// Create a mask of 'devPath' and place the result in 'mask'. static GrTexture* create_mask_GPU(GrContext* context, const SkRect& maskRect, const SkPath& devPath, const GrStrokeInfo& strokeInfo, bool doAA, int sampleCnt) { GrSurfaceDesc desc; desc.fFlags = kRenderTarget_GrSurfaceFlag; desc.fWidth = SkScalarCeilToInt(maskRect.width()); desc.fHeight = SkScalarCeilToInt(maskRect.height()); desc.fSampleCnt = doAA ? sampleCnt : 0; // We actually only need A8, but it often isn't supported as a // render target so default to RGBA_8888 desc.fConfig = kRGBA_8888_GrPixelConfig; if (context->caps()->isConfigRenderable(kAlpha_8_GrPixelConfig, desc.fSampleCnt > 0)) { desc.fConfig = kAlpha_8_GrPixelConfig; } GrTexture* mask = context->textureProvider()->createApproxTexture(desc); if (nullptr == mask) { return nullptr; } SkRect clipRect = SkRect::MakeWH(maskRect.width(), maskRect.height()); GrDrawContext* drawContext = context->drawContext(); if (!drawContext) { return nullptr; } drawContext->clear(mask->asRenderTarget(), nullptr, 0x0, true); GrPaint tempPaint; tempPaint.setAntiAlias(doAA); tempPaint.setCoverageSetOpXPFactory(SkRegion::kReplace_Op); // setup new clip GrClip clip(clipRect); // Draw the mask into maskTexture with the path's top-left at the origin using tempPaint. SkMatrix translate; translate.setTranslate(-maskRect.fLeft, -maskRect.fTop); drawContext->drawPath(mask->asRenderTarget(), clip, tempPaint, translate, devPath, strokeInfo); return mask; }
static GrRenderTarget* prepare_rt_for_external_access(SkSurface_Gpu* surface, SkSurface::BackendHandleAccess access) { switch (access) { case SkSurface::kFlushRead_BackendHandleAccess: break; case SkSurface::kFlushWrite_BackendHandleAccess: case SkSurface::kDiscardWrite_BackendHandleAccess: // for now we don't special-case on Discard, but we may in the future. surface->notifyContentWillChange(SkSurface::kRetain_ContentChangeMode); break; } // Grab the render target *after* firing notifications, as it may get switched if CoW kicks in. surface->getDevice()->flush(); GrDrawContext* dc = surface->getDevice()->accessDrawContext(); return dc->accessRenderTarget(); }
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); }
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; }
void GrConfigConversionEffect::TestForPreservingPMConversions(GrContext* context, PMConversion* pmToUPMRule, PMConversion* upmToPMRule) { *pmToUPMRule = kNone_PMConversion; *upmToPMRule = kNone_PMConversion; SkAutoTMalloc<uint32_t> data(256 * 256 * 3); uint32_t* srcData = data.get(); uint32_t* firstRead = data.get() + 256 * 256; uint32_t* secondRead = data.get() + 2 * 256 * 256; // Fill with every possible premultiplied A, color channel value. There will be 256-y duplicate // values in row y. We set r,g, and b to the same value since they are handled identically. for (int y = 0; y < 256; ++y) { for (int x = 0; x < 256; ++x) { uint8_t* color = reinterpret_cast<uint8_t*>(&srcData[256*y + x]); color[3] = y; color[2] = SkTMin(x, y); color[1] = SkTMin(x, y); color[0] = SkTMin(x, y); } } GrSurfaceDesc desc; desc.fFlags = kRenderTarget_GrSurfaceFlag; desc.fWidth = 256; desc.fHeight = 256; desc.fConfig = kRGBA_8888_GrPixelConfig; SkAutoTUnref<GrTexture> readTex(context->textureProvider()->createTexture(desc, true, NULL, 0)); if (!readTex.get()) { return; } SkAutoTUnref<GrTexture> tempTex(context->textureProvider()->createTexture(desc, true, NULL, 0)); if (!tempTex.get()) { return; } desc.fFlags = kNone_GrSurfaceFlags; SkAutoTUnref<GrTexture> dataTex(context->textureProvider()->createTexture(desc, true, data, 0)); if (!dataTex.get()) { return; } static const PMConversion kConversionRules[][2] = { {kDivByAlpha_RoundDown_PMConversion, kMulByAlpha_RoundUp_PMConversion}, {kDivByAlpha_RoundUp_PMConversion, kMulByAlpha_RoundDown_PMConversion}, }; bool failed = true; for (size_t i = 0; i < SK_ARRAY_COUNT(kConversionRules) && failed; ++i) { *pmToUPMRule = kConversionRules[i][0]; *upmToPMRule = kConversionRules[i][1]; static const SkRect kDstRect = SkRect::MakeWH(SkIntToScalar(256), SkIntToScalar(256)); static const SkRect kSrcRect = SkRect::MakeWH(SK_Scalar1, SK_Scalar1); // We do a PM->UPM draw from dataTex to readTex and read the data. Then we do a UPM->PM draw // from readTex to tempTex followed by a PM->UPM draw to readTex and finally read the data. // We then verify that two reads produced the same values. GrPaint paint1; GrPaint paint2; GrPaint paint3; SkAutoTUnref<GrFragmentProcessor> pmToUPM1( SkNEW_ARGS(GrConfigConversionEffect, (paint1.getProcessorDataManager(), dataTex, false, *pmToUPMRule, SkMatrix::I()))); SkAutoTUnref<GrFragmentProcessor> upmToPM( SkNEW_ARGS(GrConfigConversionEffect, (paint2.getProcessorDataManager(), readTex, false, *upmToPMRule, SkMatrix::I()))); SkAutoTUnref<GrFragmentProcessor> pmToUPM2( SkNEW_ARGS(GrConfigConversionEffect, (paint3.getProcessorDataManager(), tempTex, false, *pmToUPMRule, SkMatrix::I()))); paint1.addColorProcessor(pmToUPM1); GrDrawContext* readDrawContext = context->drawContext(); if (!readDrawContext) { failed = true; break; } readDrawContext->drawNonAARectToRect(readTex->asRenderTarget(), GrClip::WideOpen(), paint1, SkMatrix::I(), kDstRect, kSrcRect); readTex->readPixels(0, 0, 256, 256, kRGBA_8888_GrPixelConfig, firstRead); paint2.addColorProcessor(upmToPM); GrDrawContext* tempDrawContext = context->drawContext(); if (!tempDrawContext) { failed = true; break; } tempDrawContext->drawNonAARectToRect(tempTex->asRenderTarget(), GrClip::WideOpen(), paint2, SkMatrix::I(), kDstRect, kSrcRect); paint3.addColorProcessor(pmToUPM2); readDrawContext = context->drawContext(); if (!readDrawContext) { failed = true; break; } readDrawContext->drawNonAARectToRect(readTex->asRenderTarget(), GrClip::WideOpen(), paint3, SkMatrix::I(), kDstRect, kSrcRect); readTex->readPixels(0, 0, 256, 256, kRGBA_8888_GrPixelConfig, secondRead); failed = false; for (int y = 0; y < 256 && !failed; ++y) { for (int x = 0; x <= y; ++x) { if (firstRead[256 * y + x] != secondRead[256 * y + x]) { failed = true; break; } } } } if (failed) { *pmToUPMRule = kNone_PMConversion; *upmToPMRule = kNone_PMConversion; } }
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; }
// creates a new texture that is the input texture scaled up. If optionalKey is valid it will be // set on the new texture. stretch controls whether the scaling is done using nearest or bilerp // filtering and the size to stretch the texture to. GrTexture* stretch_texture(GrTexture* inputTexture, const Stretch& stretch, SkPixelRef* pixelRef, const GrUniqueKey& optionalKey) { SkASSERT(Stretch::kNone_Type != stretch.fType); GrContext* context = inputTexture->getContext(); SkASSERT(context); const GrCaps* caps = context->caps(); // Either it's a cache miss or the original wasn't cached to begin with. GrSurfaceDesc rtDesc = inputTexture->desc(); rtDesc.fFlags = rtDesc.fFlags | kRenderTarget_GrSurfaceFlag; rtDesc.fWidth = stretch.fWidth; rtDesc.fHeight = stretch.fHeight; rtDesc.fConfig = GrMakePixelConfigUncompressed(rtDesc.fConfig); // If the config isn't renderable try converting to either A8 or an 32 bit config. Otherwise, // fail. if (!caps->isConfigRenderable(rtDesc.fConfig, false)) { if (GrPixelConfigIsAlphaOnly(rtDesc.fConfig)) { if (caps->isConfigRenderable(kAlpha_8_GrPixelConfig, false)) { rtDesc.fConfig = kAlpha_8_GrPixelConfig; } else if (caps->isConfigRenderable(kSkia8888_GrPixelConfig, false)) { rtDesc.fConfig = kSkia8888_GrPixelConfig; } else { return NULL; } } else if (kRGB_GrColorComponentFlags == (kRGB_GrColorComponentFlags & GrPixelConfigComponentMask(rtDesc.fConfig))) { if (caps->isConfigRenderable(kSkia8888_GrPixelConfig, false)) { rtDesc.fConfig = kSkia8888_GrPixelConfig; } else { return NULL; } } else { return NULL; } } GrTexture* stretched = create_texture_for_bmp(context, optionalKey, rtDesc, pixelRef, NULL, 0); if (!stretched) { return NULL; } GrPaint paint; // If filtering is not desired then we want to ensure all texels in the resampled image are // copies of texels from the original. GrTextureParams params(SkShader::kClamp_TileMode, Stretch::kBilerp_Type == stretch.fType ? GrTextureParams::kBilerp_FilterMode : GrTextureParams::kNone_FilterMode); paint.addColorTextureProcessor(inputTexture, SkMatrix::I(), params); SkRect rect = SkRect::MakeWH(SkIntToScalar(rtDesc.fWidth), SkIntToScalar(rtDesc.fHeight)); SkRect localRect = SkRect::MakeWH(1.f, 1.f); GrDrawContext* drawContext = context->drawContext(); if (!drawContext) { return NULL; } drawContext->drawNonAARectToRect(stretched->asRenderTarget(), GrClip::WideOpen(), paint, SkMatrix::I(), rect, localRect); return stretched; }
bool SkDisplacementMapEffect::filterImageGPU(Proxy* proxy, const SkBitmap& src, const Context& ctx, SkBitmap* result, SkIPoint* offset) const { SkBitmap colorBM = src; SkIPoint colorOffset = SkIPoint::Make(0, 0); if (getColorInput() && !getColorInput()->getInputResultGPU(proxy, src, ctx, &colorBM, &colorOffset)) { return false; } SkBitmap displacementBM = src; SkIPoint displacementOffset = SkIPoint::Make(0, 0); if (getDisplacementInput() && !getDisplacementInput()->getInputResultGPU(proxy, src, ctx, &displacementBM, &displacementOffset)) { return false; } SkIRect bounds; // Since GrDisplacementMapEffect does bounds checking on color pixel access, we don't need to // pad the color bitmap to bounds here. if (!this->applyCropRect(ctx, colorBM, colorOffset, &bounds)) { return false; } SkIRect displBounds; if (!this->applyCropRect(ctx, proxy, displacementBM, &displacementOffset, &displBounds, &displacementBM)) { return false; } if (!bounds.intersect(displBounds)) { return false; } GrTexture* color = colorBM.getTexture(); GrTexture* displacement = displacementBM.getTexture(); GrContext* context = color->getContext(); GrSurfaceDesc desc; desc.fFlags = kRenderTarget_GrSurfaceFlag; desc.fWidth = bounds.width(); desc.fHeight = bounds.height(); desc.fConfig = kSkia8888_GrPixelConfig; SkAutoTUnref<GrTexture> dst(context->textureProvider()->refScratchTexture(desc, GrTextureProvider::kApprox_ScratchTexMatch)); if (!dst) { return false; } SkVector scale = SkVector::Make(fScale, fScale); ctx.ctm().mapVectors(&scale, 1); GrPaint paint; SkMatrix offsetMatrix = GrCoordTransform::MakeDivByTextureWHMatrix(displacement); offsetMatrix.preTranslate(SkIntToScalar(colorOffset.fX - displacementOffset.fX), SkIntToScalar(colorOffset.fY - displacementOffset.fY)); paint.addColorProcessor( GrDisplacementMapEffect::Create(fXChannelSelector, fYChannelSelector, scale, displacement, offsetMatrix, color, colorBM.dimensions()))->unref(); SkIRect colorBounds = bounds; colorBounds.offset(-colorOffset); SkMatrix matrix; matrix.setTranslate(-SkIntToScalar(colorBounds.x()), -SkIntToScalar(colorBounds.y())); GrDrawContext* drawContext = context->drawContext(); if (!drawContext) { return false; } drawContext->drawRect(dst->asRenderTarget(), GrClip::WideOpen(), paint, matrix, SkRect::Make(colorBounds)); offset->fX = bounds.left(); offset->fY = bounds.top(); WrapTexture(dst, bounds.width(), bounds.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 NULL; } 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 NULL; } 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 NULL; } GrPaint paint; paint.setPorterDuffXPFactory(SkXfermode::kSrc_Mode); paint.addColorProcessor(GrYUVtoRGBEffect::Create(paint.getProcessorDataManager(), yTex, uTex, vTex, yuvSizes, colorSpace))->unref(); const SkRect rect = SkRect::MakeWH(SkIntToScalar(dstDesc.fWidth), SkIntToScalar(dstDesc.fHeight)); GrDrawContext* drawContext = ctx->drawContext(); drawContext->drawRect(dst->asRenderTarget(), GrClip::WideOpen(), paint, SkMatrix::I(), rect); ctx->flushSurfaceWrites(dst); return SkNEW_ARGS(SkImage_Gpu, (dstDesc.fWidth, dstDesc.fHeight, kNeedNewImageUniqueID, kOpaque_SkAlphaType, dst, 0, budgeted)); }
void SkMultiPictureDraw::draw(bool flush) { AutoMPDReset mpdreset(this); #ifdef FORCE_SINGLE_THREAD_DRAWING_FOR_TESTING for (int i = 0; i < fThreadSafeDrawData.count(); ++i) { fThreadSafeDrawData[i].draw(); } #else SkTaskGroup().batch(fThreadSafeDrawData.count(), [&](int i) { fThreadSafeDrawData[i].draw(); }); #endif // N.B. we could get going on any GPU work from this main thread while the CPU work runs. // But in practice, we've either got GPU work or CPU work, not both. const int count = fGPUDrawData.count(); if (0 == count) { return; } #if !defined(SK_IGNORE_GPU_LAYER_HOISTING) && SK_SUPPORT_GPU GrContext* context = fGPUDrawData[0].fCanvas->getGrContext(); SkASSERT(context); // Start by collecting all the layers that are going to be atlased and render // them (if necessary). Hoisting the free floating layers is deferred until // drawing the canvas that requires them. SkTDArray<GrHoistedLayer> atlasedNeedRendering, atlasedRecycled; GrLayerHoister::Begin(context); for (int i = 0; i < count; ++i) { const DrawData& data = fGPUDrawData[i]; // we only expect 1 context for all the canvases SkASSERT(data.fCanvas->getGrContext() == context); if (!data.fPaint && (kRGBA_8888_SkColorType == data.fCanvas->imageInfo().colorType() || kBGRA_8888_SkColorType == data.fCanvas->imageInfo().colorType())) { SkRect clipBounds; if (!data.fCanvas->getClipBounds(&clipBounds)) { continue; } SkMatrix initialMatrix = data.fCanvas->getTotalMatrix(); initialMatrix.preConcat(data.fMatrix); GrDrawContext* dc = data.fCanvas->internal_private_accessTopLayerDrawContext(); SkASSERT(dc); // TODO: sorting the cacheable layers from smallest to largest // would improve the packing and reduce the number of swaps // TODO: another optimization would be to make a first pass to // lock any required layer that is already in the atlas GrLayerHoister::FindLayersToAtlas(context, data.fPicture, initialMatrix, clipBounds, &atlasedNeedRendering, &atlasedRecycled, dc->numColorSamples()); } } GrLayerHoister::DrawLayersToAtlas(context, atlasedNeedRendering); SkTDArray<GrHoistedLayer> needRendering, recycled; #endif for (int i = 0; i < count; ++i) { const DrawData& data = fGPUDrawData[i]; SkCanvas* canvas = data.fCanvas; const SkPicture* picture = data.fPicture; #if !defined(SK_IGNORE_GPU_LAYER_HOISTING) && SK_SUPPORT_GPU if (!data.fPaint) { SkRect clipBounds; if (!canvas->getClipBounds(&clipBounds)) { continue; } SkAutoCanvasMatrixPaint acmp(canvas, &data.fMatrix, data.fPaint, picture->cullRect()); const SkMatrix initialMatrix = canvas->getTotalMatrix(); GrDrawContext* dc = data.fCanvas->internal_private_accessTopLayerDrawContext(); SkASSERT(dc); // Find the layers required by this canvas. It will return atlased // layers in the 'recycled' list since they have already been drawn. GrLayerHoister::FindLayersToHoist(context, picture, initialMatrix, clipBounds, &needRendering, &recycled, dc->numColorSamples()); GrLayerHoister::DrawLayers(context, needRendering); // Render the entire picture using new layers GrRecordReplaceDraw(picture, canvas, context->getLayerCache(), initialMatrix, nullptr); GrLayerHoister::UnlockLayers(context, needRendering); GrLayerHoister::UnlockLayers(context, recycled); needRendering.rewind(); recycled.rewind(); } else #endif { canvas->drawPicture(picture, &data.fMatrix, data.fPaint); } if (flush) { canvas->flush(); } } #if !defined(SK_IGNORE_GPU_LAYER_HOISTING) && SK_SUPPORT_GPU GrLayerHoister::UnlockLayers(context, atlasedNeedRendering); GrLayerHoister::UnlockLayers(context, atlasedRecycled); GrLayerHoister::End(context); #endif }