bool SkImageFilter::canComputeFastBounds() const { if (this->affectsTransparentBlack()) { return false; } for (int i = 0; i < fInputCount; i++) { SkImageFilter* input = this->getInput(i); if (input && !input->canComputeFastBounds()) { return false; } } return true; }
void SkMergeImageFilter::toString(SkString* str) const { str->appendf("SkMergeImageFilter: ("); for (int i = 0; i < this->countInputs(); ++i) { SkImageFilter* filter = this->getInput(i); str->appendf("%d: (", i); filter->toString(str); str->appendf(")"); } str->append(")"); }
SkBitmap SkImageFilter::getInputResult(int index, Proxy* proxy, const SkBitmap& src, const SkMatrix& ctm, SkIPoint* loc) { SkASSERT(index < fInputCount); SkImageFilter* input = getInput(index); SkBitmap result; if (input && input->filterImage(proxy, src, ctm, &result, loc)) { return result; } else { return src; } }
bool SkImageFilter::canHandleComplexCTM() const { if (!this->onCanHandleComplexCTM()) { return false; } const int count = this->countInputs(); for (int i = 0; i < count; ++i) { SkImageFilter* input = this->getInput(i); if (input && !input->canHandleComplexCTM()) { return false; } } return true; }
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; }
void SkComposeImageFilter::toString(SkString* str) const { SkImageFilter* outer = getInput(0); SkImageFilter* inner = getInput(1); str->appendf("SkComposeImageFilter: ("); str->appendf("outer: "); outer->toString(str); str->appendf("inner: "); inner->toString(str); str->appendf(")"); }
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 (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 = SkIPoint::Make(0, 0); SkImageFilter* filter = getInput(i); if (filter) { if (!filter->filterImage(proxy, src, ctx, &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); } offset->fX = bounds.left(); offset->fY = bounds.top(); *result = dst->accessBitmap(false); return true; }
bool SkXfermodeImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& src, const SkMatrix& ctm, SkBitmap* dst, SkIPoint* offset) { SkBitmap background = src, foreground = src; SkImageFilter* backgroundInput = getInput(0); SkImageFilter* foregroundInput = getInput(1); SkIPoint backgroundOffset = SkIPoint::Make(0, 0); if (backgroundInput && !backgroundInput->filterImage(proxy, src, ctm, &background, &backgroundOffset)) { return false; } SkIPoint foregroundOffset = SkIPoint::Make(0, 0); if (foregroundInput && !foregroundInput->filterImage(proxy, src, ctm, &foreground, &foregroundOffset)) { return false; } SkIRect bounds, foregroundBounds; background.getBounds(&bounds); bounds.offset(backgroundOffset); foreground.getBounds(&foregroundBounds); foregroundBounds.offset(foregroundOffset); bounds.join(foregroundBounds); if (!applyCropRect(&bounds, ctm)) { return false; } SkAutoTUnref<SkBaseDevice> device(proxy->createDevice(bounds.width(), bounds.height())); if (NULL == device.get()) { return false; } SkCanvas canvas(device); canvas.translate(SkIntToScalar(-bounds.left()), SkIntToScalar(-bounds.top())); SkPaint paint; paint.setXfermodeMode(SkXfermode::kSrc_Mode); canvas.drawBitmap(background, SkIntToScalar(backgroundOffset.fX), SkIntToScalar(backgroundOffset.fY), &paint); paint.setXfermode(fMode); canvas.drawBitmap(foreground, SkIntToScalar(foregroundOffset.fX), SkIntToScalar(foregroundOffset.fY), &paint); canvas.clipRect(SkRect::Make(foregroundBounds), SkRegion::kDifference_Op); paint.setColor(SK_ColorTRANSPARENT); canvas.drawPaint(paint); *dst = device->accessBitmap(false); offset->fX += bounds.left(); offset->fY += bounds.top(); return true; }
sk_sp<SkSpecialImage> SkImageFilter::filterInput(int index, SkSpecialImage* src, const Context& ctx, SkIPoint* offset) const { SkImageFilter* input = this->getInput(index); if (!input) { return sk_sp<SkSpecialImage>(SkRef(src)); } sk_sp<SkSpecialImage> result(input->filterImage(src, this->mapContext(ctx), offset)); SkASSERT(!result || src->isTextureBacked() == result->isTextureBacked()); return result; }
SkRect SkImageFilter::computeFastBounds(const SkRect& src) const { if (0 == this->countInputs()) { return src; } SkRect combinedBounds = this->getInput(0) ? this->getInput(0)->computeFastBounds(src) : src; for (int i = 1; i < this->countInputs(); i++) { SkImageFilter* input = this->getInput(i); if (input) { combinedBounds.join(input->computeFastBounds(src)); } else { combinedBounds.join(src); } } return combinedBounds; }
bool SkImageFilter::filterInput(int index, Proxy* proxy, const SkBitmap& src, const Context& origCtx, SkBitmap* result, SkIPoint* offset, bool relaxSizeConstraint) const { SkImageFilter* input = this->getInput(index); if (!input) { return true; } SizeConstraint constraint = origCtx.sizeConstraint(); if (relaxSizeConstraint && (kExact_SizeConstraint == constraint)) { constraint = kApprox_SizeConstraint; } Context ctx(origCtx.ctm(), origCtx.clipBounds(), origCtx.cache(), constraint); return input->filterImage(proxy, src, this->mapContext(ctx), result, offset); }
bool SkComposeImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm, SkIRect* dst) { SkImageFilter* outer = getInput(0); SkImageFilter* inner = getInput(1); if (!outer && !inner) { return false; } if (!outer || !inner) { return (outer ? outer : inner)->filterBounds(src, ctm, dst); } SkIRect tmp; return inner->filterBounds(src, ctm, &tmp) && outer->filterBounds(tmp, ctm, dst); }
SkIRect SkImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm, MapDirection direction) const { if (this->countInputs() < 1) { return src; } SkIRect totalBounds; for (int i = 0; i < this->countInputs(); ++i) { SkImageFilter* filter = this->getInput(i); SkIRect rect = filter ? filter->filterBounds(src, ctm, direction) : src; if (0 == i) { totalBounds = rect; } else { totalBounds.join(rect); } } return totalBounds; }
bool SkComposeImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& src, const SkMatrix& ctm, SkBitmap* result, SkIPoint* offset) { SkImageFilter* outer = getInput(0); SkImageFilter* inner = getInput(1); if (!outer && !inner) { return false; } if (!outer || !inner) { return (outer ? outer : inner)->filterImage(proxy, src, ctm, result, offset); } SkBitmap tmp; return inner->filterImage(proxy, src, ctm, &tmp, offset) && outer->filterImage(proxy, tmp, ctm, result, offset); }
void SkImageFilter::computeFastBounds(const SkRect& src, SkRect* dst) const { if (0 == fInputCount) { *dst = src; return; } if (this->getInput(0)) { this->getInput(0)->computeFastBounds(src, dst); } else { *dst = src; } for (int i = 1; i < fInputCount; i++) { SkImageFilter* input = this->getInput(i); if (input) { SkRect bounds; input->computeFastBounds(src, &bounds); dst->join(bounds); } else { dst->join(src); } } }
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 SkImageFilter::filterInputGPU(int index, SkImageFilter::Proxy* proxy, const SkBitmap& src, const Context& origCtx, SkBitmap* result, SkIPoint* offset, bool relaxSizeConstraint) const { SkImageFilter* input = this->getInput(index); if (!input) { return true; } // Ensure that GrContext calls under filterImage and filterImageGPU below will see an identity // matrix with no clip and that the matrix, clip, and render target set before this function was // called are restored before we return to the caller. GrContext* context = src.getTexture()->getContext(); SizeConstraint constraint = origCtx.sizeConstraint(); if (relaxSizeConstraint && (kExact_SizeConstraint == constraint)) { constraint = kApprox_SizeConstraint; } Context ctx(origCtx.ctm(), origCtx.clipBounds(), origCtx.cache(), constraint); if (input->filterImage(proxy, src, this->mapContext(ctx), result, offset)) { if (!result->getTexture()) { const SkImageInfo info = result->info(); if (kUnknown_SkColorType == info.colorType()) { return false; } SkAutoTUnref<GrTexture> resultTex( GrRefCachedBitmapTexture(context, *result, GrTextureParams::ClampNoFilter())); if (!resultTex) { return false; } result->setPixelRef(new SkGrPixelRef(info, resultTex))->unref(); } return true; } else { return false; } }
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; }
virtual bool onFilterImage(Proxy* proxy, const SkBitmap& src, const Context& ctx, SkBitmap* dst, SkIPoint* offset) const override { SkBitmap source = src; SkImageFilter* input = getInput(0); SkIPoint srcOffset = SkIPoint::Make(0, 0); if (input && !input->filterImage(proxy, src, ctx, &source, &srcOffset)) { return false; } SkIRect bounds; if (!this->applyCropRect(ctx, proxy, source, &srcOffset, &bounds, &source)) { return false; } SkAutoTUnref<SkBaseDevice> device(proxy->createDevice(bounds.width(), bounds.height())); SkCanvas canvas(device); SkPaint paint; paint.setXfermodeMode(SkXfermode::kSrc_Mode); canvas.drawBitmap(source, fDX - bounds.left(), fDY - bounds.top(), &paint); *dst = device->accessBitmap(false); offset->fX += bounds.left(); offset->fY += bounds.top(); return true; }
bool SkBlendImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& src, const SkMatrix& ctm, SkBitmap* dst, SkIPoint* offset) { SkBitmap background, foreground = src; SkImageFilter* backgroundInput = getBackgroundInput(); SkImageFilter* foregroundInput = getForegroundInput(); SkASSERT(NULL != backgroundInput); if (!backgroundInput->filterImage(proxy, src, ctm, &background, offset)) { return false; } if (foregroundInput && !foregroundInput->filterImage(proxy, src, ctm, &foreground, offset)) { return false; } SkAutoLockPixels alp_foreground(foreground), alp_background(background); if (!foreground.getPixels() || !background.getPixels()) { return false; } dst->setConfig(background.config(), background.width(), background.height()); dst->allocPixels(); SkCanvas canvas(*dst); SkPaint paint; paint.setXfermodeMode(SkXfermode::kSrc_Mode); canvas.drawBitmap(background, 0, 0, &paint); // FEBlend's multiply mode is (1 - Sa) * Da + (1 - Da) * Sc + Sc * Dc // Skia's is just Sc * Dc. So we use a custom proc to implement FEBlend's // version. if (fMode == SkBlendImageFilter::kMultiply_Mode) { paint.setXfermode(new SkProcXfermode(multiply_proc))->unref(); } else { paint.setXfermodeMode(modeToXfermode(fMode)); } canvas.drawBitmap(foreground, 0, 0, &paint); return true; }
bool SkImageFilter::applyCropRect(const Context& ctx, Proxy* proxy, const SkBitmap& src, SkIPoint* srcOffset, SkIRect* bounds, SkBitmap* dst) const { SkIRect srcBounds; src.getBounds(&srcBounds); srcBounds.offset(*srcOffset); #ifdef SK_SUPPORT_SRC_BOUNDS_BLOAT_FOR_IMAGEFILTERS if (!fCropRect.applyTo(srcBounds, ctx, bounds)) { #else SkIRect dstBounds; this->onFilterNodeBounds(srcBounds, ctx.ctm(), &dstBounds, kForward_MapDirection); if (!fCropRect.applyTo(dstBounds, ctx, bounds)) { #endif return false; } if (srcBounds.contains(*bounds)) { *dst = src; return true; } else { SkAutoTUnref<SkBaseDevice> device(proxy->createDevice(bounds->width(), bounds->height())); if (!device) { return false; } SkCanvas canvas(device); canvas.clear(0x00000000); canvas.drawBitmap(src, srcOffset->x() - bounds->x(), srcOffset->y() - bounds->y()); *srcOffset = SkIPoint::Make(bounds->x(), bounds->y()); *dst = device->accessBitmap(false); return true; } } bool SkImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm, SkIRect* dst) const { if (fInputCount < 1) { *dst = src; return true; } SkIRect bounds, totalBounds; this->onFilterNodeBounds(src, ctm, &bounds, kReverse_MapDirection); for (int i = 0; i < fInputCount; ++i) { SkImageFilter* filter = this->getInput(i); SkIRect rect = bounds; if (filter && !filter->filterBounds(bounds, ctm, &rect)) { return false; } if (0 == i) { totalBounds = rect; } else { totalBounds.join(rect); } } // don't modify dst until now, so we don't accidentally change it in the // loop, but then return false on the next filter. *dst = totalBounds; return true; } void SkImageFilter::onFilterNodeBounds(const SkIRect& src, const SkMatrix&, SkIRect* dst, MapDirection) const { *dst = src; } SkImageFilter::Context SkImageFilter::mapContext(const Context& ctx) const { #ifdef SK_SUPPORT_SRC_BOUNDS_BLOAT_FOR_IMAGEFILTERS return ctx; #else SkIRect clipBounds; this->onFilterNodeBounds(ctx.clipBounds(), ctx.ctm(), &clipBounds, MapDirection::kReverse_MapDirection); return Context(ctx.ctm(), clipBounds, ctx.cache()); #endif } bool SkImageFilter::asFragmentProcessor(GrFragmentProcessor**, GrTexture*, const SkMatrix&, const SkIRect&) const { return false; } SkImageFilter* SkImageFilter::CreateMatrixFilter(const SkMatrix& matrix, SkFilterQuality filterQuality, SkImageFilter* input) { return SkMatrixImageFilter::Create(matrix, filterQuality, input); } SkImageFilter* SkImageFilter::newWithLocalMatrix(const SkMatrix& matrix) const { // SkLocalMatrixImageFilter takes SkImage* in its factory, but logically that parameter // is *always* treated as a const ptr. Hence the const-cast here. // return SkLocalMatrixImageFilter::Create(matrix, const_cast<SkImageFilter*>(this)); } #if SK_SUPPORT_GPU bool SkImageFilter::filterInputGPU(int index, SkImageFilter::Proxy* proxy, const SkBitmap& src, const Context& ctx, SkBitmap* result, SkIPoint* offset) const { SkImageFilter* input = this->getInput(index); if (!input) { return true; } // Ensure that GrContext calls under filterImage and filterImageGPU below will see an identity // matrix with no clip and that the matrix, clip, and render target set before this function was // called are restored before we return to the caller. GrContext* context = src.getTexture()->getContext(); if (input->filterImage(proxy, src, this->mapContext(ctx), result, offset)) { if (!result->getTexture()) { const SkImageInfo info = result->info(); if (kUnknown_SkColorType == info.colorType()) { return false; } SkAutoTUnref<GrTexture> resultTex( GrRefCachedBitmapTexture(context, *result, GrTextureParams::ClampNoFilter())); if (!resultTex) { return false; } result->setPixelRef(new SkGrPixelRef(info, resultTex))->unref(); } return true; } else { return false; } }
SkRect SkComposeImageFilter::computeFastBounds(const SkRect& src) const { SkImageFilter* outer = this->getInput(0); SkImageFilter* inner = this->getInput(1); return outer->computeFastBounds(inner->computeFastBounds(src)); }
bool SkImageFilter::applyCropRect(const Context& ctx, Proxy* proxy, const SkBitmap& src, SkIPoint* srcOffset, SkIRect* bounds, SkBitmap* dst) const { SkIRect srcBounds; src.getBounds(&srcBounds); srcBounds.offset(*srcOffset); #ifdef SK_SUPPORT_SRC_BOUNDS_BLOAT_FOR_IMAGEFILTERS if (!fCropRect.applyTo(srcBounds, ctx, bounds)) { #else SkIRect dstBounds; this->onFilterNodeBounds(srcBounds, ctx.ctm(), &dstBounds, kForward_MapDirection); if (!fCropRect.applyTo(dstBounds, ctx, bounds)) { #endif return false; } if (srcBounds.contains(*bounds)) { *dst = src; return true; } else { SkAutoTUnref<SkBaseDevice> device(proxy->createDevice(bounds->width(), bounds->height())); if (!device) { return false; } SkCanvas canvas(device); canvas.clear(0x00000000); canvas.drawBitmap(src, srcOffset->x() - bounds->x(), srcOffset->y() - bounds->y()); *srcOffset = SkIPoint::Make(bounds->x(), bounds->y()); *dst = device->accessBitmap(false); return true; } } bool SkImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm, SkIRect* dst) const { if (fInputCount < 1) { *dst = src; return true; } SkIRect bounds, totalBounds; this->onFilterNodeBounds(src, ctm, &bounds, kReverse_MapDirection); for (int i = 0; i < fInputCount; ++i) { SkImageFilter* filter = this->getInput(i); SkIRect rect = bounds; if (filter && !filter->filterBounds(bounds, ctm, &rect)) { return false; } if (0 == i) { totalBounds = rect; } else { totalBounds.join(rect); } } // don't modify dst until now, so we don't accidentally change it in the // loop, but then return false on the next filter. *dst = totalBounds; return true; } void SkImageFilter::onFilterNodeBounds(const SkIRect& src, const SkMatrix&, SkIRect* dst, MapDirection) const { *dst = src; } SkImageFilter::Context SkImageFilter::mapContext(const Context& ctx) const { #ifdef SK_SUPPORT_SRC_BOUNDS_BLOAT_FOR_IMAGEFILTERS return ctx; #else SkIRect clipBounds; this->onFilterNodeBounds(ctx.clipBounds(), ctx.ctm(), &clipBounds, MapDirection::kReverse_MapDirection); return Context(ctx.ctm(), clipBounds, ctx.cache(), ctx.sizeConstraint()); #endif } bool SkImageFilter::asFragmentProcessor(GrFragmentProcessor**, GrTexture*, const SkMatrix&, const SkIRect&) const { return false; } SkImageFilter* SkImageFilter::CreateMatrixFilter(const SkMatrix& matrix, SkFilterQuality filterQuality, SkImageFilter* input) { return SkMatrixImageFilter::Create(matrix, filterQuality, input); } SkImageFilter* SkImageFilter::newWithLocalMatrix(const SkMatrix& matrix) const { // SkLocalMatrixImageFilter takes SkImage* in its factory, but logically that parameter // is *always* treated as a const ptr. Hence the const-cast here. // return SkLocalMatrixImageFilter::Create(matrix, const_cast<SkImageFilter*>(this)); } #if SK_SUPPORT_GPU void SkImageFilter::WrapTexture(GrTexture* texture, int width, int height, SkBitmap* result) { SkImageInfo info = SkImageInfo::MakeN32Premul(width, height); result->setInfo(info); result->setPixelRef(new SkGrPixelRef(info, texture))->unref(); }
bool SkOffsetImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& source, const Context& ctx, SkBitmap* result, SkIPoint* offset) const { SkImageFilter* input = getInput(0); SkBitmap src = source; SkIPoint srcOffset = SkIPoint::Make(0, 0); #ifdef SK_DISABLE_OFFSETIMAGEFILTER_OPTIMIZATION if (false) { #else if (!cropRectIsSet()) { #endif if (input && !input->filterImage(proxy, source, ctx, &src, &srcOffset)) { return false; } SkVector vec; ctx.ctm().mapVectors(&vec, &fOffset, 1); offset->fX = srcOffset.fX + SkScalarRoundToInt(vec.fX); offset->fY = srcOffset.fY + SkScalarRoundToInt(vec.fY); *result = src; } else { if (input && !input->filterImage(proxy, source, ctx, &src, &srcOffset)) { return false; } SkIRect bounds; if (!this->applyCropRect(ctx, src, srcOffset, &bounds)) { return false; } SkAutoTUnref<SkBaseDevice> device(proxy->createDevice(bounds.width(), bounds.height())); if (NULL == device.get()) { return false; } SkCanvas canvas(device); SkPaint paint; paint.setXfermodeMode(SkXfermode::kSrc_Mode); canvas.translate(SkIntToScalar(srcOffset.fX - bounds.fLeft), SkIntToScalar(srcOffset.fY - bounds.fTop)); SkVector vec; ctx.ctm().mapVectors(&vec, &fOffset, 1); canvas.drawBitmap(src, vec.x(), vec.y(), &paint); *result = device->accessBitmap(false); offset->fX = bounds.fLeft; offset->fY = bounds.fTop; } return true; } void SkOffsetImageFilter::computeFastBounds(const SkRect& src, SkRect* dst) const { if (getInput(0)) { getInput(0)->computeFastBounds(src, dst); } else { *dst = src; } SkRect copy = *dst; dst->offset(fOffset.fX, fOffset.fY); dst->join(copy); } bool SkOffsetImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm, SkIRect* dst) const { SkVector vec; ctm.mapVectors(&vec, &fOffset, 1); SkIRect bounds = src; bounds.offset(-SkScalarCeilToInt(vec.fX), -SkScalarCeilToInt(vec.fY)); bounds.join(src); if (getInput(0)) { return getInput(0)->filterBounds(bounds, ctm, dst); } *dst = bounds; return true; }
void colorSpaceTest() { // Build filter tree RefPtr<ReferenceFilter> referenceFilter = ReferenceFilter::create(); // Add a dummy source graphic input RefPtr<FilterEffect> sourceEffect = referenceFilter->sourceGraphic(); sourceEffect->setOperatingColorSpace(ColorSpaceDeviceRGB); // Add a blur effect (with input : source) RefPtr<FilterEffect> blurEffect = FEGaussianBlur::create(referenceFilter.get(), 3.0f, 3.0f); blurEffect->setOperatingColorSpace(ColorSpaceLinearRGB); blurEffect->inputEffects().append(sourceEffect); // Add a blend effect (with inputs : blur, source) RefPtr<FilterEffect> blendEffect = FEBlend::create(referenceFilter.get(), FEBLEND_MODE_NORMAL); blendEffect->setOperatingColorSpace(ColorSpaceDeviceRGB); FilterEffectVector& blendInputs = blendEffect->inputEffects(); blendInputs.reserveCapacity(2); blendInputs.append(sourceEffect); blendInputs.append(blurEffect); // Add a merge effect (with inputs : blur, blend) RefPtr<FilterEffect> mergeEffect = FEMerge::create(referenceFilter.get()); mergeEffect->setOperatingColorSpace(ColorSpaceLinearRGB); FilterEffectVector& mergeInputs = mergeEffect->inputEffects(); mergeInputs.reserveCapacity(2); mergeInputs.append(blurEffect); mergeInputs.append(blendEffect); referenceFilter->setLastEffect(mergeEffect); // Get SkImageFilter resulting tree SkiaImageFilterBuilder builder; RefPtr<SkImageFilter> filter = builder.build(referenceFilter->lastEffect(), ColorSpaceDeviceRGB); // Let's check that the resulting tree looks like this : // ColorSpace (Linear->Device) : CS (L->D) // | // Merge (L) // | | // | CS (D->L) // | | // | Blend (D) // | / | // | CS (L->D) | // | / | // Blur (L) | // \ | // CS (D->L) | // \ | // Source Graphic (D) EXPECT_EQ(filter->countInputs(), 1); // Should be CS (L->D) SkImageFilter* child = filter->getInput(0); // Should be Merge EXPECT_EQ(child->asColorFilter(0), false); EXPECT_EQ(child->countInputs(), 2); child = child->getInput(1); // Should be CS (D->L) EXPECT_EQ(child->asColorFilter(0), true); EXPECT_EQ(child->countInputs(), 1); child = child->getInput(0); // Should be Blend EXPECT_EQ(child->asColorFilter(0), false); EXPECT_EQ(child->countInputs(), 2); child = child->getInput(0); // Should be CS (L->D) EXPECT_EQ(child->asColorFilter(0), true); EXPECT_EQ(child->countInputs(), 1); child = child->getInput(0); // Should be Blur EXPECT_EQ(child->asColorFilter(0), false); EXPECT_EQ(child->countInputs(), 1); child = child->getInput(0); // Should be CS (D->L) EXPECT_EQ(child->asColorFilter(0), true); EXPECT_EQ(child->countInputs(), 1); }