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 inputOffset = SkIPoint::Make(0, 0);
|
||||
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;
|
||||
} else {
|
||||
inputBounds = SkIRect::MakeXYWH(inputOffset.x(), inputOffset.y(),
|
||||
input->width(), input->height());
|
||||
}
|
||||
|
||||
SkIRect bounds;
|
||||
const SkIRect inputBounds = SkIRect::MakeXYWH(inputOffset.fX, inputOffset.fY,
|
||||
input->width(), input->height());
|
||||
if (!this->applyCropRect(ctx, inputBounds, &bounds)) {
|
||||
return nullptr;
|
||||
}
|
||||
@ -77,18 +83,27 @@ sk_sp<SkSpecialImage> SkColorFilterImageFilter::onFilterImage(SkSpecialImage* so
|
||||
SkCanvas* canvas = surf->getCanvas();
|
||||
SkASSERT(canvas);
|
||||
|
||||
// TODO: it seems like this clear shouldn't be necessary (see skbug.com/5075)
|
||||
canvas->clear(0x0);
|
||||
|
||||
SkPaint paint;
|
||||
|
||||
paint.setXfermodeMode(SkXfermode::kSrc_Mode);
|
||||
paint.setColorFilter(fColorFilter);
|
||||
|
||||
input->draw(canvas,
|
||||
SkIntToScalar(inputOffset.fX - bounds.fLeft),
|
||||
SkIntToScalar(inputOffset.fY - bounds.fTop),
|
||||
&paint);
|
||||
// TODO: it may not be necessary to clear or drawPaint inside the input bounds
|
||||
// (see skbug.com/5075)
|
||||
if (fColorFilter->affectsTransparentBlack()) {
|
||||
// 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->fY = bounds.fTop;
|
||||
|
@ -86,6 +86,36 @@ private:
|
||||
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) {
|
||||
SkScalar x = SkIntToScalar(width / 2);
|
||||
SkScalar y = SkIntToScalar(height / 2);
|
||||
@ -673,6 +703,40 @@ DEF_GPUTEST_FOR_NATIVE_CONTEXT(TestZeroBlurSigma_Gpu, reporter, context) {
|
||||
}
|
||||
#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) {
|
||||
// Check that all filters when drawn tiled (with subsequent clip rects) exactly
|
||||
// match the same filters drawn with a single full-canvas bitmap draw.
|
||||
|
Loading…
Reference in New Issue
Block a user