static void convolve_gaussian_2d(GrDrawContext* drawContext, const GrClip& clip, const SkIRect& dstRect, const SkIPoint& srcOffset, GrTexture* texture, int radiusX, int radiusY, SkScalar sigmaX, SkScalar sigmaY, const SkIRect* srcBounds) { SkMatrix localMatrix = SkMatrix::MakeTrans(-SkIntToScalar(srcOffset.x()), -SkIntToScalar(srcOffset.y())); SkISize size = SkISize::Make(2 * radiusX + 1, 2 * radiusY + 1); SkIPoint kernelOffset = SkIPoint::Make(radiusX, radiusY); GrPaint paint; paint.setGammaCorrect(drawContext->isGammaCorrect()); SkIRect bounds = srcBounds ? *srcBounds : SkIRect::EmptyIRect(); sk_sp<GrFragmentProcessor> conv(GrMatrixConvolutionEffect::MakeGaussian( texture, bounds, size, 1.0, 0.0, kernelOffset, srcBounds ? GrTextureDomain::kDecal_Mode : GrTextureDomain::kIgnore_Mode, true, sigmaX, sigmaY)); paint.addColorFragmentProcessor(std::move(conv)); paint.setPorterDuffXPFactory(SkBlendMode::kSrc); drawContext->fillRectWithLocalMatrix(clip, paint, SkMatrix::I(), SkRect::Make(dstRect), localMatrix); }
void SkBaseDevice::drawSpriteWithFilter(const SkDraw& draw, const SkBitmap& bitmap, int x, int y, const SkPaint& paint) { SkImageFilter* filter = paint.getImageFilter(); SkASSERT(filter); if (!this->canHandleImageFilter(filter)) { SkImageFilter::DeviceProxy proxy(this); SkIPoint offset = SkIPoint::Make(0, 0); SkMatrix matrix = *draw.fMatrix; matrix.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y)); const SkIRect clipBounds = draw.fClip->getBounds().makeOffset(-x, -y); SkAutoTUnref<SkImageFilter::Cache> cache(this->getImageFilterCache()); SkImageFilter::Context ctx(matrix, clipBounds, cache.get()); sk_sp<SkSpecialImage> srcImg(SkSpecialImage::internal_fromBM(&proxy, bitmap, &this->surfaceProps())); if (!srcImg) { return; // something disastrous happened } sk_sp<SkSpecialImage> resultImg(filter->filterImage(srcImg.get(), ctx, &offset)); if (resultImg) { SkPaint tmpUnfiltered(paint); tmpUnfiltered.setImageFilter(nullptr); SkBitmap resultBM; if (resultImg->internal_getBM(&resultBM)) { // TODO: add drawSprite(SkSpecialImage) to SkDevice? (see skbug.com/5073) this->drawSprite(draw, resultBM, x + offset.x(), y + offset.y(), tmpUnfiltered); } } } else { this->drawSprite(draw, bitmap, x, y, paint); } }
GrMatrixConvolutionEffect::GrMatrixConvolutionEffect(sk_sp<GrTextureProxy> proxy, const SkIRect& bounds, const SkISize& kernelSize, const SkScalar* kernel, SkScalar gain, SkScalar bias, const SkIPoint& kernelOffset, GrTextureDomain::Mode tileMode, bool convolveAlpha) // To advertise either the modulation or opaqueness optimizations we'd have to examine the // parameters. : INHERITED(kGrMatrixConvolutionEffect_ClassID, kNone_OptimizationFlags) , fCoordTransform(proxy.get()) , fDomain(proxy.get(), GrTextureDomain::MakeTexelDomainForMode(bounds, tileMode), tileMode) , fTextureSampler(std::move(proxy)) , fKernelSize(kernelSize) , fGain(SkScalarToFloat(gain)) , fBias(SkScalarToFloat(bias) / 255.0f) , fConvolveAlpha(convolveAlpha) { this->addCoordTransform(&fCoordTransform); this->addTextureSampler(&fTextureSampler); for (int i = 0; i < kernelSize.width() * kernelSize.height(); i++) { fKernel[i] = SkScalarToFloat(kernel[i]); } fKernelOffset[0] = static_cast<float>(kernelOffset.x()); fKernelOffset[1] = static_cast<float>(kernelOffset.y()); }
void SkBitmapDevice::drawSpecial(const SkDraw& draw, SkSpecialImage* srcImg, int x, int y, const SkPaint& paint) { SkASSERT(!srcImg->isTextureBacked()); SkBitmap resultBM; SkImageFilter* filter = paint.getImageFilter(); if (filter) { SkIPoint offset = SkIPoint::Make(0, 0); SkMatrix matrix = *draw.fMatrix; matrix.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y)); const SkIRect clipBounds = draw.fRC->getBounds().makeOffset(-x, -y); SkAutoTUnref<SkImageFilterCache> cache(this->getImageFilterCache()); SkImageFilter::OutputProperties outputProperties(fBitmap.colorSpace()); SkImageFilter::Context ctx(matrix, clipBounds, cache.get(), outputProperties); sk_sp<SkSpecialImage> resultImg(filter->filterImage(srcImg, ctx, &offset)); if (resultImg) { SkPaint tmpUnfiltered(paint); tmpUnfiltered.setImageFilter(nullptr); if (resultImg->getROPixels(&resultBM)) { this->drawSprite(draw, resultBM, x + offset.x(), y + offset.y(), tmpUnfiltered); } } } else { if (srcImg->getROPixels(&resultBM)) { this->drawSprite(draw, resultBM, x, y, paint); } } }
sk_sp<GrTextureProxy> SkImageGenerator::generateTexture(GrContext* ctx, const SkImageInfo& info, const SkIPoint& origin) { SkIRect srcRect = SkIRect::MakeXYWH(origin.x(), origin.y(), info.width(), info.height()); if (!SkIRect::MakeWH(fInfo.width(), fInfo.height()).contains(srcRect)) { return nullptr; } return this->onGenerateTexture(ctx, info, origin); }
sk_sp<SkImage> SkImage_Lazy::onMakeSubset(const SkIRect& subset) const { SkASSERT(fInfo.bounds().contains(subset)); SkASSERT(fInfo.bounds() != subset); const SkIRect generatorSubset = subset.makeOffset(fOrigin.x(), fOrigin.y()); Validator validator(fSharedGenerator, &generatorSubset, fInfo.refColorSpace()); return validator ? sk_sp<SkImage>(new SkImage_Lazy(&validator)) : nullptr; }
const SkGlyph& SkGlyphCache::getGlyphMetrics(SkGlyphID glyphID, SkPoint position) { if (!fIsSubpixel) { return this->getGlyphIDMetrics(glyphID); } else { SkIPoint lookupPosition = SkGlyphCacheCommon::SubpixelLookup(fAxisAlignment, position); return this->getGlyphIDMetrics(glyphID, lookupPosition.x(), lookupPosition.y()); } }
bool SkMergeImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& src, const Context& ctx, SkBitmap* result, SkIPoint* offset) const { if (countInputs() < 1) { return false; } SkIRect bounds; if (!this->applyCropRect(ctx, src, SkIPoint::Make(0, 0), &bounds)) { return false; } const int x0 = bounds.left(); const int y0 = bounds.top(); SkAutoTUnref<SkBaseDevice> dst(proxy->createDevice(bounds.width(), bounds.height())); if (nullptr == dst) { return false; } SkCanvas canvas(dst); SkPaint paint; bool didProduceResult = false; int inputCount = countInputs(); for (int i = 0; i < inputCount; ++i) { SkBitmap tmp; const SkBitmap* srcPtr; SkIPoint pos = SkIPoint::Make(0, 0); SkImageFilter* filter = getInput(i); if (filter) { if (!filter->filterImage(proxy, src, ctx, &tmp, &pos)) { continue; } srcPtr = &tmp; } else { srcPtr = &src; } if (fModes) { paint.setXfermodeMode((SkXfermode::Mode)fModes[i]); } else { paint.setXfermode(nullptr); } canvas.drawSprite(*srcPtr, pos.x() - x0, pos.y() - y0, &paint); didProduceResult = true; } if (!didProduceResult) return false; offset->fX = bounds.left(); offset->fY = bounds.top(); *result = dst->accessBitmap(false); return true; }
sk_sp<SkSpecialImage> SkDropShadowImageFilter::onFilterImage(SkSpecialImage* source, const Context& ctx, SkIPoint* offset) const { SkIPoint inputOffset = SkIPoint::Make(0, 0); sk_sp<SkSpecialImage> input(this->filterInput(0, source, ctx, &inputOffset)); if (!input) { return nullptr; } const SkIRect inputBounds = SkIRect::MakeXYWH(inputOffset.x(), inputOffset.y(), input->width(), input->height()); SkIRect bounds; if (!this->applyCropRect(ctx, inputBounds, &bounds)) { return nullptr; } const SkImageInfo info = SkImageInfo::MakeN32(bounds.width(), bounds.height(), kPremul_SkAlphaType); sk_sp<SkSpecialSurface> surf(source->makeSurface(info)); if (!surf) { return nullptr; } SkCanvas* canvas = surf->getCanvas(); SkASSERT(canvas); canvas->clear(0x0); SkVector sigma = SkVector::Make(fSigmaX, fSigmaY); ctx.ctm().mapVectors(&sigma, 1); sigma.fX = SkMaxScalar(0, sigma.fX); sigma.fY = SkMaxScalar(0, sigma.fY); SkAutoTUnref<SkImageFilter> blurFilter(SkBlurImageFilter::Create(sigma.fX, sigma.fY)); SkPaint paint; paint.setImageFilter(blurFilter.get()); paint.setColorFilter(SkColorFilter::MakeModeFilter(fColor, SkXfermode::kSrcIn_Mode)); paint.setXfermodeMode(SkXfermode::kSrcOver_Mode); SkVector offsetVec = SkVector::Make(fDx, fDy); ctx.ctm().mapVectors(&offsetVec, 1); canvas->translate(SkIntToScalar(inputOffset.fX - bounds.fLeft), SkIntToScalar(inputOffset.fY - bounds.fTop)); input->draw(canvas, offsetVec.fX, offsetVec.fY, &paint); if (fShadowMode == kDrawShadowAndForeground_ShadowMode) { input->draw(canvas, 0, 0, nullptr); } offset->fX = bounds.fLeft; offset->fY = bounds.fTop; return surf->makeImageSnapshot(); }
void onDraw(SkCanvas* canvas) override { auto cf = SkColorFilter::MakeModeFilter(SK_ColorGREEN, SkXfermode::kSrc_Mode); sk_sp<SkImageFilter> filters[] = { SkColorFilterImageFilter::Make(std::move(cf), nullptr), SkBlurImageFilter::Make(2.0f, 2.0f, nullptr), SkDropShadowImageFilter::Make( 10.0f, 5.0f, 3.0f, 3.0f, SK_ColorBLUE, SkDropShadowImageFilter::kDrawShadowAndForeground_ShadowMode, nullptr), }; SkIRect clipBounds[] { { -20, -20, 100, 100 }, { 0, 0, 75, 75 }, { 20, 20, 100, 100 }, { -20, -20, 50, 50 }, { 20, 20, 50, 50 }, }; SkImageInfo info = SkImageInfo::MakeN32(100, 100, kPremul_SkAlphaType); SkScalar MARGIN = SkIntToScalar(40); SkScalar DX = info.width() + MARGIN; SkScalar DY = info.height() + MARGIN; canvas->translate(MARGIN, MARGIN); sk_sp<SkSurface> surface = canvas->makeSurface(info); if (!surface) { surface = SkSurface::MakeRaster(info); } sk_tool_utils::draw_checkerboard(surface->getCanvas()); sk_sp<SkImage> source = surface->makeImageSnapshot(); for (auto clipBound : clipBounds) { canvas->save(); for (size_t i = 0; i < SK_ARRAY_COUNT(filters); ++i) { SkIRect subset = SkIRect::MakeXYWH(25, 25, 50, 50); SkIRect outSubset; SkIPoint offset; sk_sp<SkImage> result = source->makeWithFilter(filters[i].get(), subset, clipBound, &outSubset, &offset); SkASSERT(result); SkASSERT(source->isTextureBacked() == result->isTextureBacked()); result = result->makeSubset(outSubset); canvas->drawImage(result.get(), SkIntToScalar(offset.fX), SkIntToScalar(offset.fY)); show_bounds(canvas, SkIRect::MakeXYWH(offset.x(), offset.y(), outSubset.width(), outSubset.height()), clipBound); canvas->translate(DX, 0); } canvas->restore(); canvas->translate(0, DY); } }
bool SkMergeImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& src, const SkMatrix& ctm, SkBitmap* result, SkIPoint* loc) { if (countInputs() < 1) { return false; } const SkIRect srcBounds = SkIRect::MakeXYWH(loc->x(), loc->y(), src.width(), src.height()); SkIRect bounds; if (!this->filterBounds(srcBounds, ctm, &bounds)) { return false; } const int x0 = bounds.left(); const int y0 = bounds.top(); SkAutoTUnref<SkDevice> dst(proxy->createDevice(bounds.width(), bounds.height())); if (NULL == dst) { return false; } SkCanvas canvas(dst); SkPaint paint; int inputCount = countInputs(); for (int i = 0; i < inputCount; ++i) { SkBitmap tmp; const SkBitmap* srcPtr; SkIPoint pos = *loc; SkImageFilter* filter = getInput(i); if (filter) { if (!filter->filterImage(proxy, src, ctm, &tmp, &pos)) { return false; } srcPtr = &tmp; } else { srcPtr = &src; } if (fModes) { paint.setXfermodeMode((SkXfermode::Mode)fModes[i]); } else { paint.setXfermode(NULL); } canvas.drawSprite(*srcPtr, pos.x() - x0, pos.y() - y0, &paint); } loc->set(bounds.left(), bounds.top()); *result = dst->accessBitmap(false); return true; }
bool SkImage_Lazy::lockAsBitmap(SkBitmap* bitmap, SkImage::CachingHint chint, CachedFormat format, const SkImageInfo& info, SkTransferFunctionBehavior behavior) const { if (this->lockAsBitmapOnlyIfAlreadyCached(bitmap, format)) { return true; } uint32_t uniqueID = this->getUniqueID(format); SkBitmap tmpBitmap; SkBitmapCache::RecPtr cacheRec; SkPixmap pmap; if (SkImage::kAllow_CachingHint == chint) { auto desc = SkBitmapCacheDesc::Make(uniqueID, info.width(), info.height()); cacheRec = SkBitmapCache::Alloc(desc, info, &pmap); if (!cacheRec) { return false; } } else { if (!tmpBitmap.tryAllocPixels(info)) { return false; } if (!tmpBitmap.peekPixels(&pmap)) { return false; } } ScopedGenerator generator(fSharedGenerator); if (!generate_pixels(generator, pmap, fOrigin.x(), fOrigin.y(), behavior)) { return false; } if (cacheRec) { SkBitmapCache::Add(std::move(cacheRec), bitmap); SkASSERT(bitmap->getPixels()); // we're locked SkASSERT(bitmap->isImmutable()); SkASSERT(bitmap->getGenerationID() == uniqueID); this->notifyAddedToCache(); } else { *bitmap = tmpBitmap; bitmap->pixelRef()->setImmutableWithID(uniqueID); } check_output_bitmap(*bitmap, uniqueID); return true; }
sk_sp<SkImage> SkImage_Lazy::onMakeColorSpace(sk_sp<SkColorSpace> target, SkColorType targetColorType, SkTransferFunctionBehavior premulBehavior) const { SkAutoExclusive autoAquire(fOnMakeColorSpaceMutex); if (target && fOnMakeColorSpaceTarget && SkColorSpace::Equals(target.get(), fOnMakeColorSpaceTarget.get())) { return fOnMakeColorSpaceResult; } const SkIRect generatorSubset = SkIRect::MakeXYWH(fOrigin.x(), fOrigin.y(), fInfo.width(), fInfo.height()); Validator validator(fSharedGenerator, &generatorSubset, target); sk_sp<SkImage> result = validator ? sk_sp<SkImage>(new SkImage_Lazy(&validator)) : nullptr; if (result) { fOnMakeColorSpaceTarget = target; fOnMakeColorSpaceResult = result; } return result; }
static void convolve_gaussian_1d(GrDrawContext* drawContext, const GrClip& clip, const SkIRect& dstRect, const SkIPoint& srcOffset, GrTexture* texture, Gr1DKernelEffect::Direction direction, int radius, float sigma, bool useBounds, float bounds[2]) { GrPaint paint; paint.setGammaCorrect(drawContext->isGammaCorrect()); sk_sp<GrFragmentProcessor> conv(GrConvolutionEffect::MakeGaussian( texture, direction, radius, sigma, useBounds, bounds)); paint.addColorFragmentProcessor(std::move(conv)); paint.setPorterDuffXPFactory(SkBlendMode::kSrc); SkMatrix localMatrix = SkMatrix::MakeTrans(-SkIntToScalar(srcOffset.x()), -SkIntToScalar(srcOffset.y())); drawContext->fillRectWithLocalMatrix(clip, paint, SkMatrix::I(), SkRect::Make(dstRect), localMatrix); }
void SkBaseDevice::drawBitmapAsSprite(const SkDraw& draw, const SkBitmap& bitmap, int x, int y, const SkPaint& paint) { SkImageFilter* filter = paint.getImageFilter(); if (filter && !this->canHandleImageFilter(filter)) { SkImageFilter::DeviceProxy proxy(this); SkBitmap dst; SkIPoint offset = SkIPoint::Make(0, 0); SkMatrix matrix = *draw.fMatrix; matrix.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y)); const SkIRect clipBounds = draw.fClip->getBounds().makeOffset(-x, -y); SkAutoTUnref<SkImageFilter::Cache> cache(this->getImageFilterCache()); SkImageFilter::Context ctx(matrix, clipBounds, cache.get()); if (filter->filterImageDeprecated(&proxy, bitmap, ctx, &dst, &offset)) { SkPaint tmpUnfiltered(paint); tmpUnfiltered.setImageFilter(nullptr); this->drawSprite(draw, dst, x + offset.x(), y + offset.y(), tmpUnfiltered); } } else { this->drawSprite(draw, bitmap, x, y, paint); } }
bool SkInstallDiscardablePixelRef(SkImageGenerator* generator, const SkIRect* subset, SkBitmap* dst, SkDiscardableMemory::Factory* factory) { SkAutoTDelete<SkImageGenerator> autoGenerator(generator); if (NULL == autoGenerator.get()) { return false; } SkImageInfo prInfo = autoGenerator->getInfo(); if (prInfo.isEmpty()) { return false; } SkIPoint origin = SkIPoint::Make(0, 0); SkImageInfo bmInfo = prInfo; if (subset) { const SkIRect prBounds = SkIRect::MakeWH(prInfo.width(), prInfo.height()); if (subset->isEmpty() || !prBounds.contains(*subset)) { return false; } bmInfo = prInfo.makeWH(subset->width(), subset->height()); origin.set(subset->x(), subset->y()); } // must compute our desired rowBytes w.r.t. the pixelRef's dimensions, not ours, which may be // smaller. if (!dst->setInfo(bmInfo, prInfo.minRowBytes())) { return false; } // Since dst->setInfo() may have changed/fixed-up info, we check from the bitmap SkASSERT(dst->info().colorType() != kUnknown_SkColorType); if (dst->empty()) { // Use a normal pixelref. return dst->tryAllocPixels(); } SkAutoTUnref<SkDiscardablePixelRef> ref( new SkDiscardablePixelRef(prInfo, autoGenerator.detach(), dst->rowBytes(), factory)); dst->setPixelRef(ref, origin.x(), origin.y()); return true; }
bool SkPictureImageGenerator::onGenerateScaledPixels(const SkISize& scaledSize, const SkIPoint& scaledOrigin, const SkPixmap& scaledPixels) { int w = scaledSize.width(); int h = scaledSize.height(); const SkScalar scaleX = SkIntToScalar(w) / this->getInfo().width(); const SkScalar scaleY = SkIntToScalar(h) / this->getInfo().height(); SkMatrix matrix = SkMatrix::MakeScale(scaleX, scaleY); matrix.postTranslate(-SkIntToScalar(scaledOrigin.x()), -SkIntToScalar(scaledOrigin.y())); SkBitmap bitmap; if (!bitmap.installPixels(scaledPixels)) { return false; } bitmap.eraseColor(SK_ColorTRANSPARENT); SkCanvas canvas(bitmap, SkSurfaceProps(0, kUnknown_SkPixelGeometry)); matrix.preConcat(fMatrix); canvas.drawPicture(fPicture, &matrix, fPaint.getMaybeNull()); return true; }
GrMatrixConvolutionEffect::GrMatrixConvolutionEffect(GrTexture* texture, const SkIRect& bounds, const SkISize& kernelSize, const SkScalar* kernel, SkScalar gain, SkScalar bias, const SkIPoint& kernelOffset, GrTextureDomain::Mode tileMode, bool convolveAlpha) : INHERITED(texture, GrCoordTransform::MakeDivByTextureWHMatrix(texture)), fKernelSize(kernelSize), fGain(SkScalarToFloat(gain)), fBias(SkScalarToFloat(bias) / 255.0f), fConvolveAlpha(convolveAlpha), fDomain(GrTextureDomain::MakeTexelDomainForMode(texture, bounds, tileMode), tileMode) { this->initClassID<GrMatrixConvolutionEffect>(); for (int i = 0; i < kernelSize.width() * kernelSize.height(); i++) { fKernel[i] = SkScalarToFloat(kernel[i]); } fKernelOffset[0] = static_cast<float>(kernelOffset.x()); fKernelOffset[1] = static_cast<float>(kernelOffset.y()); }
sk_sp<GrTextureProxy> SkPictureImageGenerator::onGenerateTexture(GrContext* ctx, const SkImageInfo& info, const SkIPoint& origin) { SkASSERT(ctx); // // TODO: respect the usage, by possibly creating a different (pow2) surface // sk_sp<SkSurface> surface(SkSurface::MakeRenderTarget(ctx, SkBudgeted::kYes, info)); if (!surface) { return nullptr; } SkMatrix matrix = fMatrix; matrix.postTranslate(-origin.x(), -origin.y()); surface->getCanvas()->clear(0); // does NewRenderTarget promise to do this for us? surface->getCanvas()->drawPicture(fPicture.get(), &matrix, fPaint.getMaybeNull()); sk_sp<SkImage> image(surface->makeImageSnapshot()); if (!image) { return nullptr; } return as_IB(image)->asTextureProxyRef(); }
bool SkComposeImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& src, const Context& ctx, SkBitmap* result, SkIPoint* offset) const { SkImageFilter* outer = getInput(0); SkImageFilter* inner = getInput(1); SkBitmap tmp; SkIPoint innerOffset = SkIPoint::Make(0, 0); SkIPoint outerOffset = SkIPoint::Make(0, 0); if (!inner->filterImage(proxy, src, ctx, &tmp, &innerOffset)) return false; SkMatrix outerMatrix(ctx.ctm()); outerMatrix.postTranslate(SkIntToScalar(-innerOffset.x()), SkIntToScalar(-innerOffset.y())); Context outerContext(outerMatrix, ctx.clipBounds(), ctx.cache()); if (!outer->filterImage(proxy, tmp, outerContext, result, &outerOffset)) { return false; } *offset = innerOffset + outerOffset; return true; }
bool SkXfermodeImageFilter::filterImageGPUDeprecated(Proxy* proxy, const SkBitmap& src, const Context& ctx, SkBitmap* result, SkIPoint* offset) const { GrContext* context = nullptr; SkBitmap background = src; SkIPoint backgroundOffset = SkIPoint::Make(0, 0); if (!this->filterInputGPUDeprecated(0, proxy, src, ctx, &background, &backgroundOffset)) { background.reset(); } GrTexture* backgroundTex = background.getTexture(); if (backgroundTex) { context = backgroundTex->getContext(); } SkBitmap foreground = src; SkIPoint foregroundOffset = SkIPoint::Make(0, 0); if (!this->filterInputGPUDeprecated(1, proxy, src, ctx, &foreground, &foregroundOffset)) { foreground.reset(); } GrTexture* foregroundTex = foreground.getTexture(); if (foregroundTex) { context = foregroundTex->getContext(); } if (!context) { return false; } SkIRect bounds = background.bounds().makeOffset(backgroundOffset.x(), backgroundOffset.y()); bounds.join(foreground.bounds().makeOffset(foregroundOffset.x(), foregroundOffset.y())); if (bounds.isEmpty()) { return false; } GrSurfaceDesc desc; desc.fFlags = kRenderTarget_GrSurfaceFlag; desc.fWidth = bounds.width(); desc.fHeight = bounds.height(); desc.fConfig = kSkia8888_GrPixelConfig; SkAutoTUnref<GrTexture> dst(context->textureProvider()->createApproxTexture(desc)); if (!dst) { return false; } GrPaint paint; SkAutoTUnref<const GrFragmentProcessor> bgFP; if (backgroundTex) { SkMatrix backgroundMatrix; backgroundMatrix.setIDiv(backgroundTex->width(), backgroundTex->height()); backgroundMatrix.preTranslate(SkIntToScalar(-backgroundOffset.fX), SkIntToScalar(-backgroundOffset.fY)); bgFP.reset(GrTextureDomainEffect::Create( backgroundTex, backgroundMatrix, GrTextureDomain::MakeTexelDomain(backgroundTex, background.bounds()), GrTextureDomain::kDecal_Mode, GrTextureParams::kNone_FilterMode)); } else { bgFP.reset(GrConstColorProcessor::Create(GrColor_TRANSPARENT_BLACK, GrConstColorProcessor::kIgnore_InputMode)); } if (foregroundTex) { SkMatrix foregroundMatrix; foregroundMatrix.setIDiv(foregroundTex->width(), foregroundTex->height()); foregroundMatrix.preTranslate(SkIntToScalar(-foregroundOffset.fX), SkIntToScalar(-foregroundOffset.fY)); SkAutoTUnref<const GrFragmentProcessor> foregroundFP; foregroundFP.reset(GrTextureDomainEffect::Create( foregroundTex, foregroundMatrix, GrTextureDomain::MakeTexelDomain(foregroundTex, foreground.bounds()), GrTextureDomain::kDecal_Mode, GrTextureParams::kNone_FilterMode)); paint.addColorFragmentProcessor(foregroundFP.get()); // A null fMode is interpreted to mean kSrcOver_Mode (to match raster). SkAutoTUnref<SkXfermode> mode(SkSafeRef(fMode.get())); if (!mode) { // It would be awesome to use SkXfermode::Create here but it knows better // than us and won't return a kSrcOver_Mode SkXfermode. That means we // have to get one the hard way. struct ProcCoeff rec; rec.fProc = SkXfermode::GetProc(SkXfermode::kSrcOver_Mode); SkXfermode::ModeAsCoeff(SkXfermode::kSrcOver_Mode, &rec.fSC, &rec.fDC); mode.reset(new SkProcCoeffXfermode(rec, SkXfermode::kSrcOver_Mode)); } SkAutoTUnref<const GrFragmentProcessor> xferFP(mode->getFragmentProcessorForImageFilter(bgFP)); // A null 'xferFP' here means kSrc_Mode was used in which case we can just proceed if (xferFP) { paint.addColorFragmentProcessor(xferFP); } } else { paint.addColorFragmentProcessor(bgFP); } paint.setPorterDuffXPFactory(SkXfermode::kSrc_Mode); SkAutoTUnref<GrDrawContext> drawContext(context->drawContext(dst->asRenderTarget())); if (!drawContext) { return false; } SkMatrix matrix; matrix.setTranslate(SkIntToScalar(-bounds.left()), SkIntToScalar(-bounds.top())); drawContext->drawRect(GrClip::WideOpen(), paint, matrix, SkRect::Make(bounds)); offset->fX = bounds.left(); offset->fY = bounds.top(); GrWrapTextureInBitmap(dst, bounds.width(), bounds.height(), false, result); return true; }
static void draw_nine_clipped(const SkMask& mask, const SkIRect& outerR, const SkIPoint& center, bool fillCenter, const SkIRect& clipR, SkBlitter* blitter) { int cx = center.x(); int cy = center.y(); SkMask m; // top-left m.fBounds = mask.fBounds; m.fBounds.fRight = cx; m.fBounds.fBottom = cy; if (m.fBounds.width() > 0 && m.fBounds.height() > 0) { extractMaskSubset(mask, &m); m.fBounds.offsetTo(outerR.left(), outerR.top()); blitClippedMask(blitter, m, m.fBounds, clipR); } // top-right m.fBounds = mask.fBounds; m.fBounds.fLeft = cx + 1; m.fBounds.fBottom = cy; if (m.fBounds.width() > 0 && m.fBounds.height() > 0) { extractMaskSubset(mask, &m); m.fBounds.offsetTo(outerR.right() - m.fBounds.width(), outerR.top()); blitClippedMask(blitter, m, m.fBounds, clipR); } // bottom-left m.fBounds = mask.fBounds; m.fBounds.fRight = cx; m.fBounds.fTop = cy + 1; if (m.fBounds.width() > 0 && m.fBounds.height() > 0) { extractMaskSubset(mask, &m); m.fBounds.offsetTo(outerR.left(), outerR.bottom() - m.fBounds.height()); blitClippedMask(blitter, m, m.fBounds, clipR); } // bottom-right m.fBounds = mask.fBounds; m.fBounds.fLeft = cx + 1; m.fBounds.fTop = cy + 1; if (m.fBounds.width() > 0 && m.fBounds.height() > 0) { extractMaskSubset(mask, &m); m.fBounds.offsetTo(outerR.right() - m.fBounds.width(), outerR.bottom() - m.fBounds.height()); blitClippedMask(blitter, m, m.fBounds, clipR); } SkIRect innerR; innerR.set(outerR.left() + cx - mask.fBounds.left(), outerR.top() + cy - mask.fBounds.top(), outerR.right() + (cx + 1 - mask.fBounds.right()), outerR.bottom() + (cy + 1 - mask.fBounds.bottom())); if (fillCenter) { blitClippedRect(blitter, innerR, clipR); } const int innerW = innerR.width(); size_t storageSize = (innerW + 1) * (sizeof(int16_t) + sizeof(uint8_t)); SkAutoSMalloc<4*1024> storage(storageSize); int16_t* runs = (int16_t*)storage.get(); uint8_t* alpha = (uint8_t*)(runs + innerW + 1); SkIRect r; // top r.set(innerR.left(), outerR.top(), innerR.right(), innerR.top()); if (r.intersect(clipR)) { int startY = SkMax32(0, r.top() - outerR.top()); int stopY = startY + r.height(); int width = r.width(); for (int y = startY; y < stopY; ++y) { runs[0] = width; runs[width] = 0; alpha[0] = *mask.getAddr8(cx, mask.fBounds.top() + y); blitter->blitAntiH(r.left(), outerR.top() + y, alpha, runs); } } // bottom r.set(innerR.left(), innerR.bottom(), innerR.right(), outerR.bottom()); if (r.intersect(clipR)) { int startY = outerR.bottom() - r.bottom(); int stopY = startY + r.height(); int width = r.width(); for (int y = startY; y < stopY; ++y) { runs[0] = width; runs[width] = 0; alpha[0] = *mask.getAddr8(cx, mask.fBounds.bottom() - y - 1); blitter->blitAntiH(r.left(), outerR.bottom() - y - 1, alpha, runs); } } // left r.set(outerR.left(), innerR.top(), innerR.left(), innerR.bottom()); if (r.intersect(clipR)) { int startX = r.left() - outerR.left(); int stopX = startX + r.width(); int height = r.height(); for (int x = startX; x < stopX; ++x) { blitter->blitV(outerR.left() + x, r.top(), height, *mask.getAddr8(mask.fBounds.left() + x, mask.fBounds.top() + cy)); } } // right r.set(innerR.right(), innerR.top(), outerR.right(), innerR.bottom()); if (r.intersect(clipR)) { int startX = outerR.right() - r.right(); int stopX = startX + r.width(); int height = r.height(); for (int x = startX; x < stopX; ++x) { blitter->blitV(outerR.right() - x - 1, r.top(), height, *mask.getAddr8(mask.fBounds.right() - x - 1, mask.fBounds.top() + cy)); } } }
sk_sp<SkSpecialImage> SkDisplacementMapEffect::onFilterImage(SkSpecialImage* source, const Context& ctx, SkIPoint* offset) const { SkIPoint colorOffset = SkIPoint::Make(0, 0); sk_sp<SkSpecialImage> color(this->filterInput(1, source, ctx, &colorOffset)); if (!color) { return nullptr; } SkIPoint displOffset = SkIPoint::Make(0, 0); sk_sp<SkSpecialImage> displ(this->filterInput(0, source, ctx, &displOffset)); if (!displ) { return nullptr; } const SkIRect srcBounds = SkIRect::MakeXYWH(colorOffset.x(), colorOffset.y(), color->width(), color->height()); // Both paths do bounds checking on color pixel access, we don't need to // pad the color bitmap to bounds here. SkIRect bounds; if (!this->applyCropRect(ctx, srcBounds, &bounds)) { return nullptr; } SkIRect displBounds; displ = this->applyCropRect(ctx, displ.get(), &displOffset, &displBounds); if (!displ) { return nullptr; } if (!bounds.intersect(displBounds)) { return nullptr; } const SkIRect colorBounds = bounds.makeOffset(-colorOffset.x(), -colorOffset.y()); SkVector scale = SkVector::Make(fScale, fScale); ctx.ctm().mapVectors(&scale, 1); #if SK_SUPPORT_GPU if (source->isTextureBacked()) { GrContext* context = source->getContext(); sk_sp<GrTexture> colorTexture(color->asTextureRef(context)); sk_sp<GrTexture> displTexture(displ->asTextureRef(context)); if (!colorTexture || !displTexture) { return nullptr; } GrSurfaceDesc desc; desc.fFlags = kRenderTarget_GrSurfaceFlag; desc.fWidth = bounds.width(); desc.fHeight = bounds.height(); desc.fConfig = kSkia8888_GrPixelConfig; SkAutoTUnref<GrTexture> dst(context->textureProvider()->createApproxTexture(desc)); if (!dst) { return nullptr; } GrPaint paint; SkMatrix offsetMatrix = GrCoordTransform::MakeDivByTextureWHMatrix(displTexture.get()); offsetMatrix.preTranslate(SkIntToScalar(colorOffset.fX - displOffset.fX), SkIntToScalar(colorOffset.fY - displOffset.fY)); paint.addColorFragmentProcessor( GrDisplacementMapEffect::Create(fXChannelSelector, fYChannelSelector, scale, displTexture.get(), offsetMatrix, colorTexture.get(), SkISize::Make(color->width(), color->height())))->unref(); paint.setPorterDuffXPFactory(SkXfermode::kSrc_Mode); SkMatrix matrix; matrix.setTranslate(-SkIntToScalar(colorBounds.x()), -SkIntToScalar(colorBounds.y())); SkAutoTUnref<GrDrawContext> drawContext(context->drawContext(dst->asRenderTarget())); if (!drawContext) { return nullptr; } drawContext->drawRect(GrClip::WideOpen(), paint, matrix, SkRect::Make(colorBounds)); offset->fX = bounds.left(); offset->fY = bounds.top(); return SkSpecialImage::MakeFromGpu(SkIRect::MakeWH(bounds.width(), bounds.height()), kNeedNewImageUniqueID_SpecialImage, dst); } #endif SkBitmap colorBM, displBM; if (!color->getROPixels(&colorBM) || !displ->getROPixels(&displBM)) { return nullptr; } if ((colorBM.colorType() != kN32_SkColorType) || (displBM.colorType() != kN32_SkColorType)) { return nullptr; } SkAutoLockPixels colorLock(colorBM), displLock(displBM); if (!colorBM.getPixels() || !displBM.getPixels()) { return nullptr; } SkImageInfo info = SkImageInfo::MakeN32(bounds.width(), bounds.height(), colorBM.alphaType()); SkBitmap dst; if (!dst.tryAllocPixels(info)) { return nullptr; } SkAutoLockPixels dstLock(dst); computeDisplacement(fXChannelSelector, fYChannelSelector, scale, &dst, displBM, colorOffset - displOffset, colorBM, colorBounds); offset->fX = bounds.left(); offset->fY = bounds.top(); return SkSpecialImage::MakeFromRaster(SkIRect::MakeWH(bounds.width(), bounds.height()), dst); }
bool SkBlurImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& source, const Context& ctx, SkBitmap* dst, SkIPoint* offset) const { SkBitmap src = source; SkIPoint srcOffset = SkIPoint::Make(0, 0); if (!this->filterInput(0, proxy, source, ctx, &src, &srcOffset)) { return false; } if (src.colorType() != kN32_SkColorType) { return false; } SkIRect srcBounds, dstBounds; if (!this->applyCropRect(this->mapContext(ctx), src, srcOffset, &dstBounds, &srcBounds)) { return false; } if (!srcBounds.intersect(dstBounds)) { return false; } SkAutoLockPixels alp(src); if (!src.getPixels()) { return false; } SkAutoTUnref<SkBaseDevice> device(proxy->createDevice(dstBounds.width(), dstBounds.height())); if (!device) { return false; } *dst = device->accessBitmap(false); SkAutoLockPixels alp_dst(*dst); SkVector sigma = mapSigma(fSigma, ctx.ctm()); int kernelSizeX, kernelSizeX3, lowOffsetX, highOffsetX; int kernelSizeY, kernelSizeY3, lowOffsetY, highOffsetY; getBox3Params(sigma.x(), &kernelSizeX, &kernelSizeX3, &lowOffsetX, &highOffsetX); getBox3Params(sigma.y(), &kernelSizeY, &kernelSizeY3, &lowOffsetY, &highOffsetY); if (kernelSizeX < 0 || kernelSizeY < 0) { return false; } if (kernelSizeX == 0 && kernelSizeY == 0) { src.copyTo(dst, dst->colorType()); offset->fX = dstBounds.x() + srcOffset.x(); offset->fY = dstBounds.y() + srcOffset.y(); return true; } SkAutoTUnref<SkBaseDevice> tempDevice(proxy->createDevice(dst->width(), dst->height())); if (!tempDevice) { return false; } SkBitmap temp = tempDevice->accessBitmap(false); SkAutoLockPixels alpTemp(temp); offset->fX = dstBounds.fLeft; offset->fY = dstBounds.fTop; SkPMColor* t = temp.getAddr32(0, 0); SkPMColor* d = dst->getAddr32(0, 0); int w = dstBounds.width(), h = dstBounds.height(); const SkPMColor* s = src.getAddr32(srcBounds.x() - srcOffset.x(), srcBounds.y() - srcOffset.y()); srcBounds.offset(-dstBounds.x(), -dstBounds.y()); dstBounds.offset(-dstBounds.x(), -dstBounds.y()); SkIRect srcBoundsT = SkIRect::MakeLTRB(srcBounds.top(), srcBounds.left(), srcBounds.bottom(), srcBounds.right()); SkIRect dstBoundsT = SkIRect::MakeWH(dstBounds.height(), dstBounds.width()); int sw = src.rowBytesAsPixels(); /** * * In order to make memory accesses cache-friendly, we reorder the passes to * use contiguous memory reads wherever possible. * * For example, the 6 passes of the X-and-Y blur case are rewritten as * follows. Instead of 3 passes in X and 3 passes in Y, we perform * 2 passes in X, 1 pass in X transposed to Y on write, 2 passes in X, * then 1 pass in X transposed to Y on write. * * +----+ +----+ +----+ +---+ +---+ +---+ +----+ * + AB + ----> | AB | ----> | AB | -----> | A | ----> | A | ----> | A | -----> | AB | * +----+ blurX +----+ blurX +----+ blurXY | B | blurX | B | blurX | B | blurXY +----+ * +---+ +---+ +---+ * * In this way, two of the y-blurs become x-blurs applied to transposed * images, and all memory reads are contiguous. */ if (kernelSizeX > 0 && kernelSizeY > 0) { SkOpts::box_blur_xx(s, sw, srcBounds, t, kernelSizeX, lowOffsetX, highOffsetX, w, h); SkOpts::box_blur_xx(t, w, dstBounds, d, kernelSizeX, highOffsetX, lowOffsetX, w, h); SkOpts::box_blur_xy(d, w, dstBounds, t, kernelSizeX3, highOffsetX, highOffsetX, w, h); SkOpts::box_blur_xx(t, h, dstBoundsT, d, kernelSizeY, lowOffsetY, highOffsetY, h, w); SkOpts::box_blur_xx(d, h, dstBoundsT, t, kernelSizeY, highOffsetY, lowOffsetY, h, w); SkOpts::box_blur_xy(t, h, dstBoundsT, d, kernelSizeY3, highOffsetY, highOffsetY, h, w); } else if (kernelSizeX > 0) { SkOpts::box_blur_xx(s, sw, srcBounds, d, kernelSizeX, lowOffsetX, highOffsetX, w, h); SkOpts::box_blur_xx(d, w, dstBounds, t, kernelSizeX, highOffsetX, lowOffsetX, w, h); SkOpts::box_blur_xx(t, w, dstBounds, d, kernelSizeX3, highOffsetX, highOffsetX, w, h); } else if (kernelSizeY > 0) { SkOpts::box_blur_yx(s, sw, srcBoundsT, d, kernelSizeY, lowOffsetY, highOffsetY, h, w); SkOpts::box_blur_xx(d, h, dstBoundsT, t, kernelSizeY, highOffsetY, lowOffsetY, h, w); SkOpts::box_blur_xy(t, h, dstBoundsT, d, kernelSizeY3, highOffsetY, highOffsetY, h, w); } return true; }
sk_sp<SkSpecialImage> SkBlurImageFilter::onFilterImage(SkSpecialImage* source, const Context& ctx, SkIPoint* offset) const { SkIPoint inputOffset = SkIPoint::Make(0, 0); sk_sp<SkSpecialImage> input(this->filterInput(0, source, ctx, &inputOffset)); if (!input) { return nullptr; } SkIRect inputBounds = SkIRect::MakeXYWH(inputOffset.fX, inputOffset.fY, input->width(), input->height()); SkIRect dstBounds; if (!this->applyCropRect(this->mapContext(ctx), inputBounds, &dstBounds)) { return nullptr; } if (!inputBounds.intersect(dstBounds)) { return nullptr; } const SkVector sigma = map_sigma(fSigma, ctx.ctm()); #if SK_SUPPORT_GPU if (input->peekTexture() && input->peekTexture()->getContext()) { if (0 == sigma.x() && 0 == sigma.y()) { offset->fX = inputBounds.x(); offset->fY = inputBounds.y(); return input->makeSubset(inputBounds.makeOffset(-inputOffset.x(), -inputOffset.y())); } GrTexture* inputTexture = input->peekTexture(); offset->fX = dstBounds.fLeft; offset->fY = dstBounds.fTop; inputBounds.offset(-inputOffset); dstBounds.offset(-inputOffset); SkRect inputBoundsF(SkRect::Make(inputBounds)); SkAutoTUnref<GrTexture> tex(SkGpuBlurUtils::GaussianBlur(inputTexture->getContext(), inputTexture, false, source->props().allowSRGBInputs(), SkRect::Make(dstBounds), &inputBoundsF, sigma.x(), sigma.y())); if (!tex) { return nullptr; } return SkSpecialImage::MakeFromGpu(source->internal_getProxy(), SkIRect::MakeWH(dstBounds.width(), dstBounds.height()), kNeedNewImageUniqueID_SpecialImage, tex, &source->props()); } #endif int kernelSizeX, kernelSizeX3, lowOffsetX, highOffsetX; int kernelSizeY, kernelSizeY3, lowOffsetY, highOffsetY; get_box3_params(sigma.x(), &kernelSizeX, &kernelSizeX3, &lowOffsetX, &highOffsetX); get_box3_params(sigma.y(), &kernelSizeY, &kernelSizeY3, &lowOffsetY, &highOffsetY); if (kernelSizeX < 0 || kernelSizeY < 0) { return nullptr; } if (kernelSizeX == 0 && kernelSizeY == 0) { offset->fX = inputBounds.x(); offset->fY = inputBounds.y(); return input->makeSubset(inputBounds.makeOffset(-inputOffset.x(), -inputOffset.y())); } SkPixmap inputPixmap; if (!input->peekPixels(&inputPixmap)) { return nullptr; } if (inputPixmap.colorType() != kN32_SkColorType) { return nullptr; } SkImageInfo info = SkImageInfo::Make(dstBounds.width(), dstBounds.height(), inputPixmap.colorType(), inputPixmap.alphaType()); SkBitmap tmp, dst; if (!tmp.tryAllocPixels(info) || !dst.tryAllocPixels(info)) { return nullptr; } SkAutoLockPixels tmpLock(tmp), dstLock(dst); offset->fX = dstBounds.fLeft; offset->fY = dstBounds.fTop; SkPMColor* t = tmp.getAddr32(0, 0); SkPMColor* d = dst.getAddr32(0, 0); int w = dstBounds.width(), h = dstBounds.height(); const SkPMColor* s = inputPixmap.addr32(inputBounds.x() - inputOffset.x(), inputBounds.y() - inputOffset.y()); inputBounds.offset(-dstBounds.x(), -dstBounds.y()); dstBounds.offset(-dstBounds.x(), -dstBounds.y()); SkIRect inputBoundsT = SkIRect::MakeLTRB(inputBounds.top(), inputBounds.left(), inputBounds.bottom(), inputBounds.right()); SkIRect dstBoundsT = SkIRect::MakeWH(dstBounds.height(), dstBounds.width()); int sw = int(inputPixmap.rowBytes() >> 2); /** * * In order to make memory accesses cache-friendly, we reorder the passes to * use contiguous memory reads wherever possible. * * For example, the 6 passes of the X-and-Y blur case are rewritten as * follows. Instead of 3 passes in X and 3 passes in Y, we perform * 2 passes in X, 1 pass in X transposed to Y on write, 2 passes in X, * then 1 pass in X transposed to Y on write. * * +----+ +----+ +----+ +---+ +---+ +---+ +----+ * + AB + ----> | AB | ----> | AB | -----> | A | ----> | A | ----> | A | -----> | AB | * +----+ blurX +----+ blurX +----+ blurXY | B | blurX | B | blurX | B | blurXY +----+ * +---+ +---+ +---+ * * In this way, two of the y-blurs become x-blurs applied to transposed * images, and all memory reads are contiguous. */ if (kernelSizeX > 0 && kernelSizeY > 0) { SkOpts::box_blur_xx(s, sw, inputBounds, t, kernelSizeX, lowOffsetX, highOffsetX, w, h); SkOpts::box_blur_xx(t, w, dstBounds, d, kernelSizeX, highOffsetX, lowOffsetX, w, h); SkOpts::box_blur_xy(d, w, dstBounds, t, kernelSizeX3, highOffsetX, highOffsetX, w, h); SkOpts::box_blur_xx(t, h, dstBoundsT, d, kernelSizeY, lowOffsetY, highOffsetY, h, w); SkOpts::box_blur_xx(d, h, dstBoundsT, t, kernelSizeY, highOffsetY, lowOffsetY, h, w); SkOpts::box_blur_xy(t, h, dstBoundsT, d, kernelSizeY3, highOffsetY, highOffsetY, h, w); } else if (kernelSizeX > 0) { SkOpts::box_blur_xx(s, sw, inputBounds, d, kernelSizeX, lowOffsetX, highOffsetX, w, h); SkOpts::box_blur_xx(d, w, dstBounds, t, kernelSizeX, highOffsetX, lowOffsetX, w, h); SkOpts::box_blur_xx(t, w, dstBounds, d, kernelSizeX3, highOffsetX, highOffsetX, w, h); } else if (kernelSizeY > 0) { SkOpts::box_blur_yx(s, sw, inputBoundsT, d, kernelSizeY, lowOffsetY, highOffsetY, h, w); SkOpts::box_blur_xx(d, h, dstBoundsT, t, kernelSizeY, highOffsetY, lowOffsetY, h, w); SkOpts::box_blur_xy(t, h, dstBoundsT, d, kernelSizeY3, highOffsetY, highOffsetY, h, w); } return SkSpecialImage::MakeFromRaster(source->internal_getProxy(), SkIRect::MakeWH(dstBounds.width(), dstBounds.height()), dst, &source->props()); }
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->filterInputGPU(0, proxy, src, ctx, &background, &backgroundOffset)) { return false; } GrTexture* backgroundTex = background.getTexture(); if (nullptr == backgroundTex) { SkASSERT(false); return false; } SkBitmap foreground = src; SkIPoint foregroundOffset = SkIPoint::Make(0, 0); if (!this->filterInputGPU(1, proxy, src, ctx, &foreground, &foregroundOffset)) { return false; } GrTexture* foregroundTex = foreground.getTexture(); GrContext* context = foregroundTex->getContext(); SkIRect bounds = background.bounds().makeOffset(backgroundOffset.x(), backgroundOffset.y()); bounds.join(foreground.bounds().makeOffset(foregroundOffset.x(), foregroundOffset.y())); if (bounds.isEmpty()) { return false; } const GrFragmentProcessor* xferFP = nullptr; GrSurfaceDesc desc; desc.fFlags = kRenderTarget_GrSurfaceFlag; desc.fWidth = bounds.width(); desc.fHeight = bounds.height(); desc.fConfig = kSkia8888_GrPixelConfig; SkAutoTUnref<GrTexture> dst(context->textureProvider()->createApproxTexture(desc)); if (!dst) { return false; } GrPaint paint; SkMatrix backgroundMatrix; backgroundMatrix.setIDiv(backgroundTex->width(), backgroundTex->height()); backgroundMatrix.preTranslate(SkIntToScalar(-backgroundOffset.fX), SkIntToScalar(-backgroundOffset.fY)); SkAutoTUnref<const GrFragmentProcessor> bgFP(GrTextureDomainEffect::Create( backgroundTex, backgroundMatrix, GrTextureDomain::MakeTexelDomain(backgroundTex, background.bounds()), GrTextureDomain::kDecal_Mode, GrTextureParams::kNone_FilterMode) ); if (!fMode || !fMode->asFragmentProcessor(&xferFP, bgFP)) { // canFilterImageGPU() should've taken care of this SkASSERT(false); return false; } SkMatrix foregroundMatrix; foregroundMatrix.setIDiv(foregroundTex->width(), foregroundTex->height()); foregroundMatrix.preTranslate(SkIntToScalar(-foregroundOffset.fX), SkIntToScalar(-foregroundOffset.fY)); SkAutoTUnref<const GrFragmentProcessor> foregroundFP(GrTextureDomainEffect::Create( foregroundTex, foregroundMatrix, GrTextureDomain::MakeTexelDomain(foregroundTex, foreground.bounds()), GrTextureDomain::kDecal_Mode, GrTextureParams::kNone_FilterMode) ); paint.addColorFragmentProcessor(foregroundFP.get()); if (xferFP) { paint.addColorFragmentProcessor(xferFP)->unref(); } paint.setPorterDuffXPFactory(SkXfermode::kSrc_Mode); SkAutoTUnref<GrDrawContext> drawContext(context->drawContext(dst->asRenderTarget())); if (!drawContext) { return false; } SkMatrix matrix; matrix.setTranslate(SkIntToScalar(-bounds.left()), SkIntToScalar(-bounds.top())); drawContext->drawRect(GrClip::WideOpen(), paint, matrix, SkRect::Make(bounds)); offset->fX = bounds.left(); offset->fY = bounds.top(); GrWrapTextureInBitmap(dst, bounds.width(), bounds.height(), false, result); return true; }
sk_sp<SkSpecialImage> SkXfermodeImageFilter::onFilterImage(SkSpecialImage* source, const Context& ctx, SkIPoint* offset) const { SkIPoint backgroundOffset = SkIPoint::Make(0, 0); sk_sp<SkSpecialImage> background(this->filterInput(0, source, ctx, &backgroundOffset)); SkIPoint foregroundOffset = SkIPoint::Make(0, 0); sk_sp<SkSpecialImage> foreground(this->filterInput(1, source, ctx, &foregroundOffset)); SkIRect foregroundBounds = SkIRect::EmptyIRect(); if (foreground) { foregroundBounds = SkIRect::MakeXYWH(foregroundOffset.x(), foregroundOffset.y(), foreground->width(), foreground->height()); } SkIRect srcBounds = SkIRect::EmptyIRect(); if (background) { srcBounds = SkIRect::MakeXYWH(backgroundOffset.x(), backgroundOffset.y(), background->width(), background->height()); } srcBounds.join(foregroundBounds); if (srcBounds.isEmpty()) { return nullptr; } SkIRect bounds; if (!this->applyCropRect(ctx, srcBounds, &bounds)) { return nullptr; } offset->fX = bounds.left(); offset->fY = bounds.top(); #if SK_SUPPORT_GPU if (source->isTextureBacked()) { return this->filterImageGPU(source, background, backgroundOffset, foreground, foregroundOffset, bounds); } #endif const SkImageInfo info = SkImageInfo::MakeN32(bounds.width(), bounds.height(), kPremul_SkAlphaType); sk_sp<SkSpecialSurface> surf(source->makeSurface(info)); if (!surf) { return nullptr; } SkCanvas* canvas = surf->getCanvas(); SkASSERT(canvas); canvas->clear(0x0); // can't count on background to fully clear the background canvas->translate(SkIntToScalar(-bounds.left()), SkIntToScalar(-bounds.top())); SkPaint paint; paint.setXfermodeMode(SkXfermode::kSrc_Mode); if (background) { background->draw(canvas, SkIntToScalar(backgroundOffset.fX), SkIntToScalar(backgroundOffset.fY), &paint); } paint.setXfermode(fMode); if (foreground) { foreground->draw(canvas, SkIntToScalar(foregroundOffset.fX), SkIntToScalar(foregroundOffset.fY), &paint); } canvas->clipRect(SkRect::Make(foregroundBounds), SkRegion::kDifference_Op); paint.setColor(SK_ColorTRANSPARENT); canvas->drawPaint(paint); return surf->makeImageSnapshot(); }
void SkDiffContext::outputRecords(SkWStream& stream, bool useJSONP) { DiffRecord* currentRecord = fRecords; if (useJSONP) { stream.writeText("var SkPDiffRecords = {\n"); } else { stream.writeText("{\n"); } stream.writeText(" \"records\": [\n"); while (NULL != currentRecord) { stream.writeText(" {\n"); stream.writeText(" \"baselinePath\": \""); stream.writeText(currentRecord->fBaselinePath.c_str()); stream.writeText("\",\n"); stream.writeText(" \"testPath\": \""); stream.writeText(currentRecord->fTestPath.c_str()); stream.writeText("\",\n"); stream.writeText(" \"diffs\": [\n"); for (int diffIndex = 0; diffIndex < currentRecord->fDiffs.count(); diffIndex++) { DiffData& data = currentRecord->fDiffs[diffIndex]; stream.writeText(" {\n"); stream.writeText(" \"differName\": \""); stream.writeText(data.fDiffName); stream.writeText("\",\n"); stream.writeText(" \"result\": "); stream.writeScalarAsText(data.fResult); stream.writeText(",\n"); stream.writeText(" \"pointsOfInterest\": [\n"); for (int poiIndex = 0; poiIndex < data.fPointsOfInterest.count(); poiIndex++) { SkIPoint poi = data.fPointsOfInterest[poiIndex]; stream.writeText(" ["); stream.writeDecAsText(poi.x()); stream.writeText(","); stream.writeDecAsText(poi.y()); stream.writeText("]"); // JSON does not allow trailing commas if (poiIndex + 1 < data.fPointsOfInterest.count()) { stream.writeText(","); } stream.writeText("\n"); } stream.writeText(" ]\n"); stream.writeText(" }"); // JSON does not allow trailing commas if (diffIndex + 1 < currentRecord->fDiffs.count()) { stream.writeText(","); } stream.writeText(" \n"); } stream.writeText(" ]\n"); stream.writeText(" }"); // JSON does not allow trailing commas if (NULL != currentRecord->fNext) { stream.writeText(","); } stream.writeText("\n"); currentRecord = currentRecord->fNext; } stream.writeText(" ]\n"); if (useJSONP) { stream.writeText("};\n"); } else { stream.writeText("}\n"); } }
sk_sp<SkSpecialImage> SkTileImageFilter::onFilterImage(SkSpecialImage* source, const Context& ctx, SkIPoint* offset) const { SkIPoint inputOffset = SkIPoint::Make(0, 0); sk_sp<SkSpecialImage> input(this->filterInput(0, source, ctx, &inputOffset)); if (!input) { return nullptr; } SkRect dstRect; ctx.ctm().mapRect(&dstRect, fDstRect); if (!dstRect.intersect(SkRect::Make(ctx.clipBounds()))) { return nullptr; } const SkIRect dstIRect = dstRect.roundOut(); if (!fSrcRect.width() || !fSrcRect.height() || !dstIRect.width() || !dstIRect.height()) { return nullptr; } SkRect srcRect; ctx.ctm().mapRect(&srcRect, fSrcRect); SkIRect srcIRect; srcRect.roundOut(&srcIRect); srcIRect.offset(-inputOffset); const SkIRect inputBounds = SkIRect::MakeWH(input->width(), input->height()); if (!SkIRect::Intersects(srcIRect, inputBounds)) { return nullptr; } // We create an SkImage here b.c. it needs to be a tight fit for the tiling sk_sp<SkImage> subset; if (inputBounds.contains(srcIRect)) { subset = input->makeTightSubset(srcIRect); if (!subset) { return nullptr; } } else { sk_sp<SkSurface> surf(input->makeTightSurface(ctx.outputProperties(), srcIRect.size())); if (!surf) { return nullptr; } SkCanvas* canvas = surf->getCanvas(); SkASSERT(canvas); SkPaint paint; paint.setBlendMode(SkBlendMode::kSrc); input->draw(canvas, SkIntToScalar(inputOffset.x()), SkIntToScalar(inputOffset.y()), &paint); subset = surf->makeImageSnapshot(); } SkASSERT(subset->width() == srcIRect.width()); SkASSERT(subset->height() == srcIRect.height()); sk_sp<SkSpecialSurface> surf(source->makeSurface(ctx.outputProperties(), dstIRect.size())); if (!surf) { return nullptr; } SkCanvas* canvas = surf->getCanvas(); SkASSERT(canvas); SkPaint paint; paint.setBlendMode(SkBlendMode::kSrc); paint.setShader(subset->makeShader(SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode)); canvas->translate(-dstRect.fLeft, -dstRect.fTop); canvas->drawRect(dstRect, paint); offset->fX = dstIRect.fLeft; offset->fY = dstIRect.fTop; return surf->makeImageSnapshot(); }
bool SkBlurMask::BoxBlur(SkMask* dst, const SkMask& src, SkScalar sigma, SkBlurStyle style, SkBlurQuality quality, SkIPoint* margin, bool force_quality) { if (src.fFormat != SkMask::kA8_Format) { return false; } SkIPoint border; #ifdef SK_SUPPORT_LEGACY_MASK_BLUR auto get_adjusted_radii = [](SkScalar passRadius, int *loRadius, int *hiRadius) { *loRadius = *hiRadius = SkScalarCeilToInt(passRadius); if (SkIntToScalar(*hiRadius) - passRadius > 0.5f) { *loRadius = *hiRadius - 1; } }; // Force high quality off for small radii (performance) if (!force_quality && sigma <= SkIntToScalar(2)) { quality = kLow_SkBlurQuality; } SkScalar passRadius; if (kHigh_SkBlurQuality == quality) { // For the high quality path the 3 pass box blur kernel width is // 6*rad+1 while the full Gaussian width is 6*sigma. passRadius = sigma - (1 / 6.0f); } else { // For the low quality path we only attempt to cover 3*sigma of the // Gaussian blur area (1.5*sigma on each side). The single pass box // blur's kernel size is 2*rad+1. passRadius = 1.5f * sigma - 0.5f; } // highQuality: use three box blur passes as a cheap way // to approximate a Gaussian blur int passCount = (kHigh_SkBlurQuality == quality) ? 3 : 1; int rx = SkScalarCeilToInt(passRadius); int outerWeight = 255 - SkScalarRoundToInt((SkIntToScalar(rx) - passRadius) * 255); SkASSERT(rx >= 0); SkASSERT((unsigned)outerWeight <= 255); if (rx <= 0) { return false; } int ry = rx; // only do square blur for now int padx = passCount * rx; int pady = passCount * ry; border = {padx, pady}; dst->fBounds.set(src.fBounds.fLeft - padx, src.fBounds.fTop - pady, src.fBounds.fRight + padx, src.fBounds.fBottom + pady); dst->fRowBytes = dst->fBounds.width(); dst->fFormat = SkMask::kA8_Format; dst->fImage = nullptr; if (src.fImage) { size_t dstSize = dst->computeImageSize(); if (0 == dstSize) { return false; // too big to allocate, abort } int sw = src.fBounds.width(); int sh = src.fBounds.height(); const uint8_t* sp = src.fImage; uint8_t* dp = SkMask::AllocImage(dstSize); SkAutoTCallVProc<uint8_t, SkMask_FreeImage> autoCall(dp); // build the blurry destination SkAutoTMalloc<uint8_t> tmpBuffer(dstSize); uint8_t* tp = tmpBuffer.get(); int w = sw, h = sh; if (outerWeight == 255) { int loRadius, hiRadius; get_adjusted_radii(passRadius, &loRadius, &hiRadius); if (kHigh_SkBlurQuality == quality) { // Do three X blurs, with a transpose on the final one. w = boxBlur<false>(sp, src.fRowBytes, tp, loRadius, hiRadius, w, h); w = boxBlur<false>(tp, w, dp, hiRadius, loRadius, w, h); w = boxBlur<true>(dp, w, tp, hiRadius, hiRadius, w, h); // Do three Y blurs, with a transpose on the final one. h = boxBlur<false>(tp, h, dp, loRadius, hiRadius, h, w); h = boxBlur<false>(dp, h, tp, hiRadius, loRadius, h, w); h = boxBlur<true>(tp, h, dp, hiRadius, hiRadius, h, w); } else { w = boxBlur<true>(sp, src.fRowBytes, tp, rx, rx, w, h); h = boxBlur<true>(tp, h, dp, ry, ry, h, w); } } else { if (kHigh_SkBlurQuality == quality) { // Do three X blurs, with a transpose on the final one. w = boxBlurInterp<false>(sp, src.fRowBytes, tp, rx, w, h, outerWeight); w = boxBlurInterp<false>(tp, w, dp, rx, w, h, outerWeight); w = boxBlurInterp<true>(dp, w, tp, rx, w, h, outerWeight); // Do three Y blurs, with a transpose on the final one. h = boxBlurInterp<false>(tp, h, dp, ry, h, w, outerWeight); h = boxBlurInterp<false>(dp, h, tp, ry, h, w, outerWeight); h = boxBlurInterp<true>(tp, h, dp, ry, h, w, outerWeight); } else { w = boxBlurInterp<true>(sp, src.fRowBytes, tp, rx, w, h, outerWeight); h = boxBlurInterp<true>(tp, h, dp, ry, h, w, outerWeight); } } dst->fImage = autoCall.release(); } #else SkMaskBlurFilter blurFilter{sigma, sigma}; border = blurFilter.blur(src, dst); #endif // SK_SUPPORT_LEGACY_MASK_BLUR if (src.fImage != nullptr) { // if need be, alloc the "real" dst (same size as src) and copy/merge // the blur into it (applying the src) if (style == kInner_SkBlurStyle) { // now we allocate the "real" dst, mirror the size of src size_t srcSize = src.computeImageSize(); if (0 == srcSize) { return false; // too big to allocate, abort } auto blur = dst->fImage; dst->fImage = SkMask::AllocImage(srcSize); auto blurStart = &blur[border.x() + border.y() * dst->fRowBytes]; merge_src_with_blur(dst->fImage, src.fRowBytes, src.fImage, src.fRowBytes, blurStart, dst->fRowBytes, src.fBounds.width(), src.fBounds.height()); SkMask::FreeImage(blur); } else if (style != kNormal_SkBlurStyle) { auto dstStart = &dst->fImage[border.x() + border.y() * dst->fRowBytes]; clamp_with_orig(dstStart, dst->fRowBytes, src.fImage, src.fRowBytes, src.fBounds.width(), src.fBounds.height(), style); } } if (style == kInner_SkBlurStyle) { dst->fBounds = src.fBounds; // restore trimmed bounds dst->fRowBytes = src.fRowBytes; } if (margin != nullptr) { *margin = border; } return true; }