Fix failed filter followed by an affects-transparent-black filter.
When an upstream filter returns null, either through failure or clipping, a downstream affects-transparent-black filter should still produce non-transparent pixels. This patch fixes SkColorFilterImageFilter. Note: this will affect the results of the imagefilterscropexpand GM. BUG=skia: GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1844593002 Review URL: https://codereview.chromium.org/1844593002
This commit is contained in:
parent
3b37545bc5
commit
6a93fa1a45
@ -57,13 +57,19 @@ sk_sp<SkSpecialImage> SkColorFilterImageFilter::onFilterImage(SkSpecialImage* so
|
|||||||
SkIPoint* offset) const {
|
SkIPoint* offset) const {
|
||||||
SkIPoint inputOffset = SkIPoint::Make(0, 0);
|
SkIPoint inputOffset = SkIPoint::Make(0, 0);
|
||||||
sk_sp<SkSpecialImage> input(this->filterInput(0, source, ctx, &inputOffset));
|
sk_sp<SkSpecialImage> input(this->filterInput(0, source, ctx, &inputOffset));
|
||||||
if (!input) {
|
|
||||||
|
SkIRect inputBounds;
|
||||||
|
if (fColorFilter->affectsTransparentBlack()) {
|
||||||
|
// If the color filter affects transparent black, the bounds are the entire clip.
|
||||||
|
inputBounds = ctx.clipBounds();
|
||||||
|
} else if (!input) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
} else {
|
||||||
|
inputBounds = SkIRect::MakeXYWH(inputOffset.x(), inputOffset.y(),
|
||||||
|
input->width(), input->height());
|
||||||
}
|
}
|
||||||
|
|
||||||
SkIRect bounds;
|
SkIRect bounds;
|
||||||
const SkIRect inputBounds = SkIRect::MakeXYWH(inputOffset.fX, inputOffset.fY,
|
|
||||||
input->width(), input->height());
|
|
||||||
if (!this->applyCropRect(ctx, inputBounds, &bounds)) {
|
if (!this->applyCropRect(ctx, inputBounds, &bounds)) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
@ -77,18 +83,27 @@ sk_sp<SkSpecialImage> SkColorFilterImageFilter::onFilterImage(SkSpecialImage* so
|
|||||||
SkCanvas* canvas = surf->getCanvas();
|
SkCanvas* canvas = surf->getCanvas();
|
||||||
SkASSERT(canvas);
|
SkASSERT(canvas);
|
||||||
|
|
||||||
// TODO: it seems like this clear shouldn't be necessary (see skbug.com/5075)
|
|
||||||
canvas->clear(0x0);
|
|
||||||
|
|
||||||
SkPaint paint;
|
SkPaint paint;
|
||||||
|
|
||||||
paint.setXfermodeMode(SkXfermode::kSrc_Mode);
|
paint.setXfermodeMode(SkXfermode::kSrc_Mode);
|
||||||
paint.setColorFilter(fColorFilter);
|
paint.setColorFilter(fColorFilter);
|
||||||
|
|
||||||
input->draw(canvas,
|
// TODO: it may not be necessary to clear or drawPaint inside the input bounds
|
||||||
SkIntToScalar(inputOffset.fX - bounds.fLeft),
|
// (see skbug.com/5075)
|
||||||
SkIntToScalar(inputOffset.fY - bounds.fTop),
|
if (fColorFilter->affectsTransparentBlack()) {
|
||||||
&paint);
|
// The subsequent input->draw() call may not fill the entire canvas. For filters which
|
||||||
|
// affect transparent black, ensure that the filter is applied everywhere.
|
||||||
|
canvas->drawPaint(paint);
|
||||||
|
} else {
|
||||||
|
canvas->clear(0x0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input) {
|
||||||
|
input->draw(canvas,
|
||||||
|
SkIntToScalar(inputOffset.fX - bounds.fLeft),
|
||||||
|
SkIntToScalar(inputOffset.fY - bounds.fTop),
|
||||||
|
&paint);
|
||||||
|
}
|
||||||
|
|
||||||
offset->fX = bounds.fLeft;
|
offset->fX = bounds.fLeft;
|
||||||
offset->fY = bounds.fTop;
|
offset->fY = bounds.fTop;
|
||||||
|
@ -86,6 +86,36 @@ private:
|
|||||||
typedef SkImageFilter INHERITED;
|
typedef SkImageFilter INHERITED;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class FailImageFilter : public SkImageFilter {
|
||||||
|
public:
|
||||||
|
FailImageFilter() : SkImageFilter(0, nullptr) {
|
||||||
|
}
|
||||||
|
|
||||||
|
sk_sp<SkSpecialImage> onFilterImage(SkSpecialImage* source,
|
||||||
|
const Context& ctx,
|
||||||
|
SkIPoint* offset) const override {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
SK_TO_STRING_OVERRIDE()
|
||||||
|
SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(FailImageFilter)
|
||||||
|
|
||||||
|
private:
|
||||||
|
typedef SkImageFilter INHERITED;
|
||||||
|
};
|
||||||
|
|
||||||
|
sk_sp<SkFlattenable> FailImageFilter::CreateProc(SkReadBuffer& buffer) {
|
||||||
|
SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 0);
|
||||||
|
return sk_sp<SkFlattenable>(new FailImageFilter());
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef SK_IGNORE_TO_STRING
|
||||||
|
void FailImageFilter::toString(SkString* str) const {
|
||||||
|
str->appendf("FailImageFilter: (");
|
||||||
|
str->append(")");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void draw_gradient_circle(SkCanvas* canvas, int width, int height) {
|
void draw_gradient_circle(SkCanvas* canvas, int width, int height) {
|
||||||
SkScalar x = SkIntToScalar(width / 2);
|
SkScalar x = SkIntToScalar(width / 2);
|
||||||
SkScalar y = SkIntToScalar(height / 2);
|
SkScalar y = SkIntToScalar(height / 2);
|
||||||
@ -673,6 +703,40 @@ DEF_GPUTEST_FOR_NATIVE_CONTEXT(TestZeroBlurSigma_Gpu, reporter, context) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
// Tests that, even when an upstream filter has returned null (due to failure or clipping), a
|
||||||
|
// downstream filter that affects transparent black still does so even with a nullptr input.
|
||||||
|
static void test_fail_affects_transparent_black(SkImageFilter::Proxy* proxy,
|
||||||
|
skiatest::Reporter* reporter,
|
||||||
|
GrContext* context) {
|
||||||
|
sk_sp<FailImageFilter> failFilter(new FailImageFilter());
|
||||||
|
sk_sp<SkSpecialImage> source(create_empty_special_image(context, proxy, 5));
|
||||||
|
SkImageFilter::Context ctx(SkMatrix::I(), SkIRect::MakeXYWH(0, 0, 1, 1), nullptr);
|
||||||
|
sk_sp<SkColorFilter> green(SkColorFilter::MakeModeFilter(SK_ColorGREEN, SkXfermode::kSrc_Mode));
|
||||||
|
SkASSERT(green->affectsTransparentBlack());
|
||||||
|
sk_sp<SkImageFilter> greenFilter(SkColorFilterImageFilter::Create(green.get(),
|
||||||
|
failFilter.get()));
|
||||||
|
SkIPoint offset;
|
||||||
|
sk_sp<SkSpecialImage> result(greenFilter->filterImage(source.get(), ctx, &offset));
|
||||||
|
REPORTER_ASSERT(reporter, nullptr != result.get());
|
||||||
|
if (result.get()) {
|
||||||
|
SkBitmap resultBM;
|
||||||
|
TestingSpecialImageAccess::GetROPixels(result.get(), &resultBM);
|
||||||
|
SkAutoLockPixels lock(resultBM);
|
||||||
|
REPORTER_ASSERT(reporter, *resultBM.getAddr32(0, 0) == SK_ColorGREEN);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DEF_TEST(ImageFilterFailAffectsTransparentBlack, reporter) {
|
||||||
|
run_raster_test(reporter, 100, test_fail_affects_transparent_black);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if SK_SUPPORT_GPU
|
||||||
|
DEF_GPUTEST_FOR_NATIVE_CONTEXT(ImageFilterFailAffectsTransparentBlack_Gpu, reporter, context) {
|
||||||
|
run_gpu_test(reporter, context, 100, test_fail_affects_transparent_black);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
DEF_TEST(ImageFilterDrawTiled, reporter) {
|
DEF_TEST(ImageFilterDrawTiled, reporter) {
|
||||||
// Check that all filters when drawn tiled (with subsequent clip rects) exactly
|
// Check that all filters when drawn tiled (with subsequent clip rects) exactly
|
||||||
// match the same filters drawn with a single full-canvas bitmap draw.
|
// match the same filters drawn with a single full-canvas bitmap draw.
|
||||||
|
Loading…
Reference in New Issue
Block a user