2013-07-24 22:19:24 +00:00
|
|
|
/*
|
|
|
|
* Copyright 2013 Google Inc.
|
|
|
|
*
|
|
|
|
* Use of this source code is governed by a BSD-style license that can be
|
|
|
|
* found in the LICENSE file.
|
|
|
|
*/
|
|
|
|
|
Fixed issues found by fuzzer
Last week, the fuzzer found a few numerical issue with filters and I had written some fixes for them. Here are the fixes with some unit tests.
For senorblanco : So I figured out what was asserting when we'd get a 0 width "result" in SkBicubicImageFilter::onFilterImage(). Basically, if the "result" SkBitmap object calls SkBitmap::setConfig() with "width" and/or "height" set to 0, then the SkBitmap object will call SkBitmap::reset(), making the SkBitmap object's config invalid. At this point, calling SkBitmap::getAddr32() will assert, even without attempting to dereference the data pointer, because the SkBitmap's config is invalid. If height is valid, but width is 0, then this call to SkBitmap::getAddr32() happens directly in SkBicubicImageFilter::onFilterImage() a few lines lower and asserts right away.
BUG=
R=senorblanco@google.com, senorblanco@chromium.org, bsalomon@google.com
Author: sugoi@chromium.org
Review URL: https://chromiumcodereview.appspot.com/23533042
git-svn-id: http://skia.googlecode.com/svn/trunk@11249 2bbb7eff-a529-9590-31e7-b0007b416f81
2013-09-13 12:40:02 +00:00
|
|
|
#include "SkBitmap.h"
|
|
|
|
#include "SkBitmapDevice.h"
|
2014-01-24 20:56:26 +00:00
|
|
|
#include "SkBlurImageFilter.h"
|
Fixed issues found by fuzzer
Last week, the fuzzer found a few numerical issue with filters and I had written some fixes for them. Here are the fixes with some unit tests.
For senorblanco : So I figured out what was asserting when we'd get a 0 width "result" in SkBicubicImageFilter::onFilterImage(). Basically, if the "result" SkBitmap object calls SkBitmap::setConfig() with "width" and/or "height" set to 0, then the SkBitmap object will call SkBitmap::reset(), making the SkBitmap object's config invalid. At this point, calling SkBitmap::getAddr32() will assert, even without attempting to dereference the data pointer, because the SkBitmap's config is invalid. If height is valid, but width is 0, then this call to SkBitmap::getAddr32() happens directly in SkBicubicImageFilter::onFilterImage() a few lines lower and asserts right away.
BUG=
R=senorblanco@google.com, senorblanco@chromium.org, bsalomon@google.com
Author: sugoi@chromium.org
Review URL: https://chromiumcodereview.appspot.com/23533042
git-svn-id: http://skia.googlecode.com/svn/trunk@11249 2bbb7eff-a529-9590-31e7-b0007b416f81
2013-09-13 12:40:02 +00:00
|
|
|
#include "SkCanvas.h"
|
2013-07-24 22:19:24 +00:00
|
|
|
#include "SkColorFilterImageFilter.h"
|
2014-01-24 20:56:26 +00:00
|
|
|
#include "SkColorMatrixFilter.h"
|
2015-02-13 17:05:47 +00:00
|
|
|
#include "SkComposeImageFilter.h"
|
Make SkImageFilter crop rects relative to the primitive origin, instead of relative to their parent's crop rect. This is required by SVG semantics, and is more sane anyway.
To do this, this patch changes the "offset/loc" parameter in filterImage() / onFilterImage() from an inout-param to an out-param only, so that the calling filter can know how much the input filter wants its result offset (and doesn't include the original primitive position). This offset can then be applied to the current filter's crop rect. (I've renamed the parameter "offset" in all cases to make this clear.) This makes the call sites in SkCanvas/SkGpuDevice responsible for applying the resulting offset to the primitive's position, which is actually a fairly small change.
This change also fixes SkTileImageFilter and SkOffsetImageFilter to correctly handle an input offset, which they weren't before. This required modifying the GM's, since they assumed the broken behaviour.
NOTE: this will require rebaselining the imagefiltersgraph test, since it has a new test case.
NOTE: this will "break" the Blink layout tests css3/filters/effect-reference-subregion-chained-hw.html and css3/filters/effect-reference-subregion-hw.html, but it actually makes them give correct results. It should be suppressed on the skia roll, and I'll rebaseline it.
R=reed@google.com
Review URL: https://codereview.chromium.org/112803004
git-svn-id: http://skia.googlecode.com/svn/trunk@12895 2bbb7eff-a529-9590-31e7-b0007b416f81
2014-01-03 21:48:22 +00:00
|
|
|
#include "SkDisplacementMapEffect.h"
|
|
|
|
#include "SkDropShadowImageFilter.h"
|
2014-05-30 20:50:56 +00:00
|
|
|
#include "SkFlattenableSerialization.h"
|
2014-04-29 15:20:39 +00:00
|
|
|
#include "SkGradientShader.h"
|
2015-09-15 18:26:13 +00:00
|
|
|
#include "SkImage.h"
|
2015-09-14 20:31:18 +00:00
|
|
|
#include "SkImageSource.h"
|
Fixed issues found by fuzzer
Last week, the fuzzer found a few numerical issue with filters and I had written some fixes for them. Here are the fixes with some unit tests.
For senorblanco : So I figured out what was asserting when we'd get a 0 width "result" in SkBicubicImageFilter::onFilterImage(). Basically, if the "result" SkBitmap object calls SkBitmap::setConfig() with "width" and/or "height" set to 0, then the SkBitmap object will call SkBitmap::reset(), making the SkBitmap object's config invalid. At this point, calling SkBitmap::getAddr32() will assert, even without attempting to dereference the data pointer, because the SkBitmap's config is invalid. If height is valid, but width is 0, then this call to SkBitmap::getAddr32() happens directly in SkBicubicImageFilter::onFilterImage() a few lines lower and asserts right away.
BUG=
R=senorblanco@google.com, senorblanco@chromium.org, bsalomon@google.com
Author: sugoi@chromium.org
Review URL: https://chromiumcodereview.appspot.com/23533042
git-svn-id: http://skia.googlecode.com/svn/trunk@11249 2bbb7eff-a529-9590-31e7-b0007b416f81
2013-09-13 12:40:02 +00:00
|
|
|
#include "SkLightingImageFilter.h"
|
2014-01-24 20:56:26 +00:00
|
|
|
#include "SkMatrixConvolutionImageFilter.h"
|
Make SkImageFilter crop rects relative to the primitive origin, instead of relative to their parent's crop rect. This is required by SVG semantics, and is more sane anyway.
To do this, this patch changes the "offset/loc" parameter in filterImage() / onFilterImage() from an inout-param to an out-param only, so that the calling filter can know how much the input filter wants its result offset (and doesn't include the original primitive position). This offset can then be applied to the current filter's crop rect. (I've renamed the parameter "offset" in all cases to make this clear.) This makes the call sites in SkCanvas/SkGpuDevice responsible for applying the resulting offset to the primitive's position, which is actually a fairly small change.
This change also fixes SkTileImageFilter and SkOffsetImageFilter to correctly handle an input offset, which they weren't before. This required modifying the GM's, since they assumed the broken behaviour.
NOTE: this will require rebaselining the imagefiltersgraph test, since it has a new test case.
NOTE: this will "break" the Blink layout tests css3/filters/effect-reference-subregion-chained-hw.html and css3/filters/effect-reference-subregion-hw.html, but it actually makes them give correct results. It should be suppressed on the skia roll, and I'll rebaseline it.
R=reed@google.com
Review URL: https://codereview.chromium.org/112803004
git-svn-id: http://skia.googlecode.com/svn/trunk@12895 2bbb7eff-a529-9590-31e7-b0007b416f81
2014-01-03 21:48:22 +00:00
|
|
|
#include "SkMergeImageFilter.h"
|
|
|
|
#include "SkMorphologyImageFilter.h"
|
|
|
|
#include "SkOffsetImageFilter.h"
|
2014-10-29 19:36:32 +00:00
|
|
|
#include "SkPerlinNoiseShader.h"
|
2014-02-05 22:36:31 +00:00
|
|
|
#include "SkPicture.h"
|
2014-05-30 20:36:15 +00:00
|
|
|
#include "SkPictureImageFilter.h"
|
2014-04-18 18:04:41 +00:00
|
|
|
#include "SkPictureRecorder.h"
|
2015-07-13 20:16:44 +00:00
|
|
|
#include "SkPoint3.h"
|
2014-08-19 13:27:53 +00:00
|
|
|
#include "SkReadBuffer.h"
|
2014-01-24 20:56:26 +00:00
|
|
|
#include "SkRect.h"
|
2014-10-29 19:36:32 +00:00
|
|
|
#include "SkRectShaderImageFilter.h"
|
2015-09-14 20:31:18 +00:00
|
|
|
#include "SkSurface.h"
|
2015-08-20 18:10:41 +00:00
|
|
|
#include "SkTableColorFilter.h"
|
Make SkImageFilter crop rects relative to the primitive origin, instead of relative to their parent's crop rect. This is required by SVG semantics, and is more sane anyway.
To do this, this patch changes the "offset/loc" parameter in filterImage() / onFilterImage() from an inout-param to an out-param only, so that the calling filter can know how much the input filter wants its result offset (and doesn't include the original primitive position). This offset can then be applied to the current filter's crop rect. (I've renamed the parameter "offset" in all cases to make this clear.) This makes the call sites in SkCanvas/SkGpuDevice responsible for applying the resulting offset to the primitive's position, which is actually a fairly small change.
This change also fixes SkTileImageFilter and SkOffsetImageFilter to correctly handle an input offset, which they weren't before. This required modifying the GM's, since they assumed the broken behaviour.
NOTE: this will require rebaselining the imagefiltersgraph test, since it has a new test case.
NOTE: this will "break" the Blink layout tests css3/filters/effect-reference-subregion-chained-hw.html and css3/filters/effect-reference-subregion-hw.html, but it actually makes them give correct results. It should be suppressed on the skia roll, and I'll rebaseline it.
R=reed@google.com
Review URL: https://codereview.chromium.org/112803004
git-svn-id: http://skia.googlecode.com/svn/trunk@12895 2bbb7eff-a529-9590-31e7-b0007b416f81
2014-01-03 21:48:22 +00:00
|
|
|
#include "SkTileImageFilter.h"
|
|
|
|
#include "SkXfermodeImageFilter.h"
|
2014-01-24 20:56:26 +00:00
|
|
|
#include "Test.h"
|
2013-07-24 22:19:24 +00:00
|
|
|
|
2014-02-03 22:22:16 +00:00
|
|
|
#if SK_SUPPORT_GPU
|
2015-12-01 12:35:26 +00:00
|
|
|
#include "GrContext.h"
|
2014-02-03 22:22:16 +00:00
|
|
|
#include "SkGpuDevice.h"
|
|
|
|
#endif
|
|
|
|
|
2013-12-18 22:15:12 +00:00
|
|
|
static const int kBitmapSize = 4;
|
|
|
|
|
2014-02-05 22:36:31 +00:00
|
|
|
namespace {
|
|
|
|
|
|
|
|
class MatrixTestImageFilter : public SkImageFilter {
|
|
|
|
public:
|
|
|
|
MatrixTestImageFilter(skiatest::Reporter* reporter, const SkMatrix& expectedMatrix)
|
2015-08-27 14:41:13 +00:00
|
|
|
: SkImageFilter(0, nullptr), fReporter(reporter), fExpectedMatrix(expectedMatrix) {
|
2014-02-05 22:36:31 +00:00
|
|
|
}
|
|
|
|
|
2014-03-14 15:44:01 +00:00
|
|
|
virtual bool onFilterImage(Proxy*, const SkBitmap& src, const Context& ctx,
|
2015-03-26 01:17:31 +00:00
|
|
|
SkBitmap* result, SkIPoint* offset) const override {
|
2014-03-14 15:44:01 +00:00
|
|
|
REPORTER_ASSERT(fReporter, ctx.ctm() == fExpectedMatrix);
|
2014-02-05 22:36:31 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-12-19 21:49:15 +00:00
|
|
|
SK_TO_STRING_OVERRIDE()
|
2014-02-05 22:36:31 +00:00
|
|
|
SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(MatrixTestImageFilter)
|
|
|
|
|
|
|
|
protected:
|
2015-03-26 01:17:31 +00:00
|
|
|
void flatten(SkWriteBuffer& buffer) const override {
|
2014-08-21 14:59:51 +00:00
|
|
|
this->INHERITED::flatten(buffer);
|
2014-02-05 22:36:31 +00:00
|
|
|
buffer.writeFunctionPtr(fReporter);
|
|
|
|
buffer.writeMatrix(fExpectedMatrix);
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
skiatest::Reporter* fReporter;
|
|
|
|
SkMatrix fExpectedMatrix;
|
2014-12-01 19:47:08 +00:00
|
|
|
|
2014-08-21 14:59:51 +00:00
|
|
|
typedef SkImageFilter INHERITED;
|
2014-02-05 22:36:31 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2014-08-21 14:59:51 +00:00
|
|
|
SkFlattenable* MatrixTestImageFilter::CreateProc(SkReadBuffer& buffer) {
|
|
|
|
SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 1);
|
|
|
|
skiatest::Reporter* reporter = (skiatest::Reporter*)buffer.readFunctionPtr();
|
|
|
|
SkMatrix matrix;
|
|
|
|
buffer.readMatrix(&matrix);
|
2015-08-26 20:07:48 +00:00
|
|
|
return new MatrixTestImageFilter(reporter, matrix);
|
2014-08-21 14:59:51 +00:00
|
|
|
}
|
|
|
|
|
2014-12-19 21:49:15 +00:00
|
|
|
#ifndef SK_IGNORE_TO_STRING
|
|
|
|
void MatrixTestImageFilter::toString(SkString* str) const {
|
|
|
|
str->appendf("MatrixTestImageFilter: (");
|
|
|
|
str->append(")");
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2015-09-15 18:26:13 +00:00
|
|
|
static SkImage* make_small_image() {
|
|
|
|
SkAutoTUnref<SkSurface> surface(SkSurface::NewRasterN32Premul(kBitmapSize, kBitmapSize));
|
|
|
|
SkCanvas* canvas = surface->getCanvas();
|
|
|
|
canvas->clear(0x00000000);
|
2013-12-18 22:15:12 +00:00
|
|
|
SkPaint darkPaint;
|
|
|
|
darkPaint.setColor(0xFF804020);
|
|
|
|
SkPaint lightPaint;
|
|
|
|
lightPaint.setColor(0xFF244484);
|
|
|
|
const int i = kBitmapSize / 4;
|
|
|
|
for (int y = 0; y < kBitmapSize; y += i) {
|
|
|
|
for (int x = 0; x < kBitmapSize; x += i) {
|
2015-09-15 18:26:13 +00:00
|
|
|
canvas->save();
|
|
|
|
canvas->translate(SkIntToScalar(x), SkIntToScalar(y));
|
|
|
|
canvas->drawRect(SkRect::MakeXYWH(0, 0,
|
2013-12-18 22:15:12 +00:00
|
|
|
SkIntToScalar(i),
|
|
|
|
SkIntToScalar(i)), darkPaint);
|
2015-09-15 18:26:13 +00:00
|
|
|
canvas->drawRect(SkRect::MakeXYWH(SkIntToScalar(i),
|
2013-12-18 22:15:12 +00:00
|
|
|
0,
|
|
|
|
SkIntToScalar(i),
|
|
|
|
SkIntToScalar(i)), lightPaint);
|
2015-09-15 18:26:13 +00:00
|
|
|
canvas->drawRect(SkRect::MakeXYWH(0,
|
2013-12-18 22:15:12 +00:00
|
|
|
SkIntToScalar(i),
|
|
|
|
SkIntToScalar(i),
|
|
|
|
SkIntToScalar(i)), lightPaint);
|
2015-09-15 18:26:13 +00:00
|
|
|
canvas->drawRect(SkRect::MakeXYWH(SkIntToScalar(i),
|
2013-12-18 22:15:12 +00:00
|
|
|
SkIntToScalar(i),
|
|
|
|
SkIntToScalar(i),
|
|
|
|
SkIntToScalar(i)), darkPaint);
|
2015-09-15 18:26:13 +00:00
|
|
|
canvas->restore();
|
Fixed issues found by fuzzer
Last week, the fuzzer found a few numerical issue with filters and I had written some fixes for them. Here are the fixes with some unit tests.
For senorblanco : So I figured out what was asserting when we'd get a 0 width "result" in SkBicubicImageFilter::onFilterImage(). Basically, if the "result" SkBitmap object calls SkBitmap::setConfig() with "width" and/or "height" set to 0, then the SkBitmap object will call SkBitmap::reset(), making the SkBitmap object's config invalid. At this point, calling SkBitmap::getAddr32() will assert, even without attempting to dereference the data pointer, because the SkBitmap's config is invalid. If height is valid, but width is 0, then this call to SkBitmap::getAddr32() happens directly in SkBicubicImageFilter::onFilterImage() a few lines lower and asserts right away.
BUG=
R=senorblanco@google.com, senorblanco@chromium.org, bsalomon@google.com
Author: sugoi@chromium.org
Review URL: https://chromiumcodereview.appspot.com/23533042
git-svn-id: http://skia.googlecode.com/svn/trunk@11249 2bbb7eff-a529-9590-31e7-b0007b416f81
2013-09-13 12:40:02 +00:00
|
|
|
}
|
|
|
|
}
|
2015-09-15 18:26:13 +00:00
|
|
|
|
|
|
|
return surface->newImageSnapshot();
|
2013-12-18 22:15:12 +00:00
|
|
|
}
|
|
|
|
|
2015-08-27 14:41:13 +00:00
|
|
|
static SkImageFilter* make_scale(float amount, SkImageFilter* input = nullptr) {
|
2013-12-18 22:15:12 +00:00
|
|
|
SkScalar s = amount;
|
|
|
|
SkScalar matrix[20] = { s, 0, 0, 0, 0,
|
|
|
|
0, s, 0, 0, 0,
|
|
|
|
0, 0, s, 0, 0,
|
|
|
|
0, 0, 0, s, 0 };
|
2014-02-21 18:46:30 +00:00
|
|
|
SkAutoTUnref<SkColorFilter> filter(SkColorMatrixFilter::Create(matrix));
|
2013-12-18 22:15:12 +00:00
|
|
|
return SkColorFilterImageFilter::Create(filter, input);
|
|
|
|
}
|
|
|
|
|
2015-03-08 11:42:52 +00:00
|
|
|
static SkImageFilter* make_grayscale(SkImageFilter* input, const SkImageFilter::CropRect* cropRect) {
|
2013-12-18 22:15:12 +00:00
|
|
|
SkScalar matrix[20];
|
|
|
|
memset(matrix, 0, 20 * sizeof(SkScalar));
|
|
|
|
matrix[0] = matrix[5] = matrix[10] = 0.2126f;
|
|
|
|
matrix[1] = matrix[6] = matrix[11] = 0.7152f;
|
|
|
|
matrix[2] = matrix[7] = matrix[12] = 0.0722f;
|
|
|
|
matrix[18] = 1.0f;
|
2014-02-21 18:46:30 +00:00
|
|
|
SkAutoTUnref<SkColorFilter> filter(SkColorMatrixFilter::Create(matrix));
|
2013-12-18 22:15:12 +00:00
|
|
|
return SkColorFilterImageFilter::Create(filter, input, cropRect);
|
|
|
|
}
|
|
|
|
|
2015-03-08 11:42:52 +00:00
|
|
|
static SkImageFilter* make_blue(SkImageFilter* input, const SkImageFilter::CropRect* cropRect) {
|
|
|
|
SkAutoTUnref<SkColorFilter> filter(SkColorFilter::CreateModeFilter(SK_ColorBLUE,
|
|
|
|
SkXfermode::kSrcIn_Mode));
|
|
|
|
return SkColorFilterImageFilter::Create(filter, input, cropRect);
|
|
|
|
}
|
|
|
|
|
2013-12-18 22:15:12 +00:00
|
|
|
DEF_TEST(ImageFilter, reporter) {
|
|
|
|
{
|
2015-03-08 11:42:52 +00:00
|
|
|
// Check that two non-clipping color-matrice-filters concatenate into a single filter.
|
2013-12-18 22:15:12 +00:00
|
|
|
SkAutoTUnref<SkImageFilter> halfBrightness(make_scale(0.5f));
|
|
|
|
SkAutoTUnref<SkImageFilter> quarterBrightness(make_scale(0.5f, halfBrightness));
|
2015-08-27 14:41:13 +00:00
|
|
|
REPORTER_ASSERT(reporter, nullptr == quarterBrightness->getInput(0));
|
2015-03-08 11:42:52 +00:00
|
|
|
SkColorFilter* cf;
|
|
|
|
REPORTER_ASSERT(reporter, quarterBrightness->asColorFilter(&cf));
|
2015-08-27 14:41:13 +00:00
|
|
|
REPORTER_ASSERT(reporter, cf->asColorMatrix(nullptr));
|
2015-03-08 11:42:52 +00:00
|
|
|
cf->unref();
|
2013-07-24 22:19:24 +00:00
|
|
|
}
|
|
|
|
|
2013-12-18 22:15:12 +00:00
|
|
|
{
|
2015-03-08 11:42:52 +00:00
|
|
|
// Check that a clipping color-matrice-filter followed by a color-matrice-filters
|
|
|
|
// concatenates into a single filter, but not a matrixfilter (due to clamping).
|
2013-12-18 22:15:12 +00:00
|
|
|
SkAutoTUnref<SkImageFilter> doubleBrightness(make_scale(2.0f));
|
|
|
|
SkAutoTUnref<SkImageFilter> halfBrightness(make_scale(0.5f, doubleBrightness));
|
2015-08-27 14:41:13 +00:00
|
|
|
REPORTER_ASSERT(reporter, nullptr == halfBrightness->getInput(0));
|
2015-03-08 11:42:52 +00:00
|
|
|
SkColorFilter* cf;
|
|
|
|
REPORTER_ASSERT(reporter, halfBrightness->asColorFilter(&cf));
|
2015-08-27 14:41:13 +00:00
|
|
|
REPORTER_ASSERT(reporter, !cf->asColorMatrix(nullptr));
|
2015-03-08 11:42:52 +00:00
|
|
|
cf->unref();
|
2013-07-24 22:19:24 +00:00
|
|
|
}
|
|
|
|
|
2013-12-18 22:15:12 +00:00
|
|
|
{
|
|
|
|
// Check that a color filter image filter without a crop rect can be
|
|
|
|
// expressed as a color filter.
|
2015-08-27 14:41:13 +00:00
|
|
|
SkAutoTUnref<SkImageFilter> gray(make_grayscale(nullptr, nullptr));
|
|
|
|
REPORTER_ASSERT(reporter, true == gray->asColorFilter(nullptr));
|
2015-03-08 11:42:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
// Check that a colorfilterimage filter without a crop rect but with an input
|
|
|
|
// that is another colorfilterimage can be expressed as a colorfilter (composed).
|
2015-08-27 14:41:13 +00:00
|
|
|
SkAutoTUnref<SkImageFilter> mode(make_blue(nullptr, nullptr));
|
|
|
|
SkAutoTUnref<SkImageFilter> gray(make_grayscale(mode, nullptr));
|
|
|
|
REPORTER_ASSERT(reporter, true == gray->asColorFilter(nullptr));
|
2013-07-24 22:19:24 +00:00
|
|
|
}
|
2015-03-05 22:47:29 +00:00
|
|
|
|
2015-03-08 11:42:52 +00:00
|
|
|
{
|
|
|
|
// Test that if we exceed the limit of what ComposeColorFilter can combine, we still
|
|
|
|
// can build the DAG and won't assert if we call asColorFilter.
|
2015-08-27 14:41:13 +00:00
|
|
|
SkAutoTUnref<SkImageFilter> filter(make_blue(nullptr, nullptr));
|
2015-03-08 11:42:52 +00:00
|
|
|
const int kWayTooManyForComposeColorFilter = 100;
|
|
|
|
for (int i = 0; i < kWayTooManyForComposeColorFilter; ++i) {
|
2015-08-27 14:41:13 +00:00
|
|
|
filter.reset(make_blue(filter, nullptr));
|
2015-03-08 11:42:52 +00:00
|
|
|
// the first few of these will succeed, but after we hit the internal limit,
|
|
|
|
// it will then return false.
|
2015-08-27 14:41:13 +00:00
|
|
|
(void)filter->asColorFilter(nullptr);
|
2015-03-08 11:42:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-12-18 22:15:12 +00:00
|
|
|
{
|
|
|
|
// Check that a color filter image filter with a crop rect cannot
|
|
|
|
// be expressed as a color filter.
|
|
|
|
SkImageFilter::CropRect cropRect(SkRect::MakeXYWH(0, 0, 100, 100));
|
2015-08-27 14:41:13 +00:00
|
|
|
SkAutoTUnref<SkImageFilter> grayWithCrop(make_grayscale(nullptr, &cropRect));
|
|
|
|
REPORTER_ASSERT(reporter, false == grayWithCrop->asColorFilter(nullptr));
|
2013-12-18 22:15:12 +00:00
|
|
|
}
|
2013-07-24 22:19:24 +00:00
|
|
|
|
2014-07-03 18:13:09 +00:00
|
|
|
{
|
|
|
|
// Check that two non-commutative matrices are concatenated in
|
|
|
|
// the correct order.
|
|
|
|
SkScalar blueToRedMatrix[20] = { 0 };
|
|
|
|
blueToRedMatrix[2] = blueToRedMatrix[18] = SK_Scalar1;
|
|
|
|
SkScalar redToGreenMatrix[20] = { 0 };
|
|
|
|
redToGreenMatrix[5] = redToGreenMatrix[18] = SK_Scalar1;
|
|
|
|
SkAutoTUnref<SkColorFilter> blueToRed(SkColorMatrixFilter::Create(blueToRedMatrix));
|
|
|
|
SkAutoTUnref<SkImageFilter> filter1(SkColorFilterImageFilter::Create(blueToRed.get()));
|
|
|
|
SkAutoTUnref<SkColorFilter> redToGreen(SkColorMatrixFilter::Create(redToGreenMatrix));
|
|
|
|
SkAutoTUnref<SkImageFilter> filter2(SkColorFilterImageFilter::Create(redToGreen.get(), filter1.get()));
|
|
|
|
|
|
|
|
SkBitmap result;
|
|
|
|
result.allocN32Pixels(kBitmapSize, kBitmapSize);
|
|
|
|
|
|
|
|
SkPaint paint;
|
|
|
|
paint.setColor(SK_ColorBLUE);
|
|
|
|
paint.setImageFilter(filter2.get());
|
|
|
|
SkCanvas canvas(result);
|
|
|
|
canvas.clear(0x0);
|
|
|
|
SkRect rect = SkRect::Make(SkIRect::MakeWH(kBitmapSize, kBitmapSize));
|
|
|
|
canvas.drawRect(rect, paint);
|
|
|
|
uint32_t pixel = *result.getAddr32(0, 0);
|
|
|
|
// The result here should be green, since we have effectively shifted blue to green.
|
|
|
|
REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN);
|
|
|
|
}
|
|
|
|
|
2013-12-18 22:15:12 +00:00
|
|
|
{
|
|
|
|
// Tests pass by not asserting
|
2015-09-15 18:26:13 +00:00
|
|
|
SkAutoTUnref<SkImage> image(make_small_image());
|
|
|
|
SkBitmap result;
|
2014-02-13 14:41:43 +00:00
|
|
|
result.allocN32Pixels(kBitmapSize, kBitmapSize);
|
2013-07-24 22:19:24 +00:00
|
|
|
|
|
|
|
{
|
2013-12-18 22:15:12 +00:00
|
|
|
// This tests for :
|
|
|
|
// 1 ) location at (0,0,1)
|
2015-07-13 20:16:44 +00:00
|
|
|
SkPoint3 location = SkPoint3::Make(0, 0, SK_Scalar1);
|
2013-12-18 22:15:12 +00:00
|
|
|
// 2 ) location and target at same value
|
2015-07-13 20:16:44 +00:00
|
|
|
SkPoint3 target = SkPoint3::Make(location.fX, location.fY, location.fZ);
|
2013-12-18 22:15:12 +00:00
|
|
|
// 3 ) large negative specular exponent value
|
|
|
|
SkScalar specularExponent = -1000;
|
|
|
|
|
2015-09-15 18:26:13 +00:00
|
|
|
SkAutoTUnref<SkImageFilter> bmSrc(SkImageSource::Create(image));
|
2013-12-18 22:15:12 +00:00
|
|
|
SkPaint paint;
|
|
|
|
paint.setImageFilter(SkLightingImageFilter::CreateSpotLitSpecular(
|
|
|
|
location, target, specularExponent, 180,
|
|
|
|
0xFFFFFFFF, SK_Scalar1, SK_Scalar1, SK_Scalar1,
|
|
|
|
bmSrc))->unref();
|
|
|
|
SkCanvas canvas(result);
|
|
|
|
SkRect r = SkRect::MakeWH(SkIntToScalar(kBitmapSize),
|
|
|
|
SkIntToScalar(kBitmapSize));
|
|
|
|
canvas.drawRect(r, paint);
|
2013-07-24 22:19:24 +00:00
|
|
|
}
|
|
|
|
}
|
2014-02-03 22:22:16 +00:00
|
|
|
}
|
Make SkImageFilter crop rects relative to the primitive origin, instead of relative to their parent's crop rect. This is required by SVG semantics, and is more sane anyway.
To do this, this patch changes the "offset/loc" parameter in filterImage() / onFilterImage() from an inout-param to an out-param only, so that the calling filter can know how much the input filter wants its result offset (and doesn't include the original primitive position). This offset can then be applied to the current filter's crop rect. (I've renamed the parameter "offset" in all cases to make this clear.) This makes the call sites in SkCanvas/SkGpuDevice responsible for applying the resulting offset to the primitive's position, which is actually a fairly small change.
This change also fixes SkTileImageFilter and SkOffsetImageFilter to correctly handle an input offset, which they weren't before. This required modifying the GM's, since they assumed the broken behaviour.
NOTE: this will require rebaselining the imagefiltersgraph test, since it has a new test case.
NOTE: this will "break" the Blink layout tests css3/filters/effect-reference-subregion-chained-hw.html and css3/filters/effect-reference-subregion-hw.html, but it actually makes them give correct results. It should be suppressed on the skia roll, and I'll rebaseline it.
R=reed@google.com
Review URL: https://codereview.chromium.org/112803004
git-svn-id: http://skia.googlecode.com/svn/trunk@12895 2bbb7eff-a529-9590-31e7-b0007b416f81
2014-01-03 21:48:22 +00:00
|
|
|
|
2015-06-22 16:46:59 +00:00
|
|
|
static void test_crop_rects(SkImageFilter::Proxy* proxy, skiatest::Reporter* reporter) {
|
2014-02-03 22:22:16 +00:00
|
|
|
// Check that all filters offset to their absolute crop rect,
|
|
|
|
// unaffected by the input crop rect.
|
|
|
|
// Tests pass by not asserting.
|
|
|
|
SkBitmap bitmap;
|
2014-02-13 14:41:43 +00:00
|
|
|
bitmap.allocN32Pixels(100, 100);
|
2014-02-03 22:22:16 +00:00
|
|
|
bitmap.eraseARGB(0, 0, 0, 0);
|
|
|
|
|
|
|
|
SkImageFilter::CropRect inputCropRect(SkRect::MakeXYWH(8, 13, 80, 80));
|
|
|
|
SkImageFilter::CropRect cropRect(SkRect::MakeXYWH(20, 30, 60, 60));
|
2015-08-27 14:41:13 +00:00
|
|
|
SkAutoTUnref<SkImageFilter> input(make_grayscale(nullptr, &inputCropRect));
|
2014-02-03 22:22:16 +00:00
|
|
|
|
|
|
|
SkAutoTUnref<SkColorFilter> cf(SkColorFilter::CreateModeFilter(SK_ColorRED, SkXfermode::kSrcIn_Mode));
|
2015-07-13 20:16:44 +00:00
|
|
|
SkPoint3 location = SkPoint3::Make(0, 0, SK_Scalar1);
|
2014-02-03 22:22:16 +00:00
|
|
|
SkScalar kernel[9] = {
|
|
|
|
SkIntToScalar( 1), SkIntToScalar( 1), SkIntToScalar( 1),
|
|
|
|
SkIntToScalar( 1), SkIntToScalar(-7), SkIntToScalar( 1),
|
|
|
|
SkIntToScalar( 1), SkIntToScalar( 1), SkIntToScalar( 1),
|
|
|
|
};
|
|
|
|
SkISize kernelSize = SkISize::Make(3, 3);
|
|
|
|
SkScalar gain = SK_Scalar1, bias = 0;
|
|
|
|
|
|
|
|
SkImageFilter* filters[] = {
|
|
|
|
SkColorFilterImageFilter::Create(cf.get(), input.get(), &cropRect),
|
2014-03-10 10:51:58 +00:00
|
|
|
SkDisplacementMapEffect::Create(SkDisplacementMapEffect::kR_ChannelSelectorType,
|
|
|
|
SkDisplacementMapEffect::kB_ChannelSelectorType,
|
|
|
|
40.0f, input.get(), input.get(), &cropRect),
|
|
|
|
SkBlurImageFilter::Create(SK_Scalar1, SK_Scalar1, input.get(), &cropRect),
|
2014-10-23 20:59:52 +00:00
|
|
|
SkDropShadowImageFilter::Create(SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1,
|
|
|
|
SK_ColorGREEN, SkDropShadowImageFilter::kDrawShadowAndForeground_ShadowMode,
|
2015-03-18 19:11:33 +00:00
|
|
|
input.get(), &cropRect),
|
2014-02-03 22:22:16 +00:00
|
|
|
SkLightingImageFilter::CreatePointLitDiffuse(location, SK_ColorGREEN, 0, 0, input.get(), &cropRect),
|
|
|
|
SkLightingImageFilter::CreatePointLitSpecular(location, SK_ColorGREEN, 0, 0, 0, input.get(), &cropRect),
|
2014-03-10 10:51:58 +00:00
|
|
|
SkMatrixConvolutionImageFilter::Create(kernelSize, kernel, gain, bias, SkIPoint::Make(1, 1), SkMatrixConvolutionImageFilter::kRepeat_TileMode, false, input.get(), &cropRect),
|
|
|
|
SkMergeImageFilter::Create(input.get(), input.get(), SkXfermode::kSrcOver_Mode, &cropRect),
|
|
|
|
SkOffsetImageFilter::Create(SK_Scalar1, SK_Scalar1, input.get(), &cropRect),
|
|
|
|
SkOffsetImageFilter::Create(SK_Scalar1, SK_Scalar1, input.get(), &cropRect),
|
|
|
|
SkDilateImageFilter::Create(3, 2, input.get(), &cropRect),
|
|
|
|
SkErodeImageFilter::Create(2, 3, input.get(), &cropRect),
|
|
|
|
SkTileImageFilter::Create(inputCropRect.rect(), cropRect.rect(), input.get()),
|
|
|
|
SkXfermodeImageFilter::Create(SkXfermode::Create(SkXfermode::kSrcOver_Mode), input.get(), input.get(), &cropRect),
|
2014-02-03 22:22:16 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
for (size_t i = 0; i < SK_ARRAY_COUNT(filters); ++i) {
|
|
|
|
SkImageFilter* filter = filters[i];
|
|
|
|
SkBitmap result;
|
|
|
|
SkIPoint offset;
|
|
|
|
SkString str;
|
2014-02-04 00:28:46 +00:00
|
|
|
str.printf("filter %d", static_cast<int>(i));
|
2015-11-25 18:32:18 +00:00
|
|
|
SkImageFilter::Context ctx(SkMatrix::I(), SkIRect::MakeWH(100, 100), nullptr, SkImageFilter::kApprox_SizeConstraint);
|
2015-06-22 16:46:59 +00:00
|
|
|
REPORTER_ASSERT_MESSAGE(reporter, filter->filterImage(proxy, bitmap, ctx,
|
Implement intra-frame cacheing in image filters.
When image filters are processed within Skia, they simply do
a blind recursion. This has the side-effect of turning the
DAG into a tree. I.e., nodes visited more than once during
the traversal will be processed more than once.
This change implements a very simple cacheing scheme: a
cache is created before traversing the DAG, and handed
into the processing traversal. Before recursing into a child
in SkImageFilter::filterImage(), the cache is checked for a
hit, and early-out is performed. Otherwise, the node is
processed, and its result bitmap and location (offset) are
cached, but only if it contains two or more children and
thus will be visited again during the traversal.
Currently, the child count is approximated with the
refcount. This is good enough in most cases (and exactly
correct for the Chrome use case). We could add an exact
child count to the image filter, but this will require
violating the immutability of image filters slightly in
order to bump the child count as nodes are connected. I
leave it up to the reviewer to decide which is better.
R=reed@google.com
Author: senorblanco@chromium.org
Review URL: https://codereview.chromium.org/230653005
git-svn-id: http://skia.googlecode.com/svn/trunk@14160 2bbb7eff-a529-9590-31e7-b0007b416f81
2014-04-11 18:57:00 +00:00
|
|
|
&result, &offset), str.c_str());
|
2014-02-03 22:22:16 +00:00
|
|
|
REPORTER_ASSERT_MESSAGE(reporter, offset.fX == 20 && offset.fY == 30, str.c_str());
|
|
|
|
}
|
Make SkImageFilter crop rects relative to the primitive origin, instead of relative to their parent's crop rect. This is required by SVG semantics, and is more sane anyway.
To do this, this patch changes the "offset/loc" parameter in filterImage() / onFilterImage() from an inout-param to an out-param only, so that the calling filter can know how much the input filter wants its result offset (and doesn't include the original primitive position). This offset can then be applied to the current filter's crop rect. (I've renamed the parameter "offset" in all cases to make this clear.) This makes the call sites in SkCanvas/SkGpuDevice responsible for applying the resulting offset to the primitive's position, which is actually a fairly small change.
This change also fixes SkTileImageFilter and SkOffsetImageFilter to correctly handle an input offset, which they weren't before. This required modifying the GM's, since they assumed the broken behaviour.
NOTE: this will require rebaselining the imagefiltersgraph test, since it has a new test case.
NOTE: this will "break" the Blink layout tests css3/filters/effect-reference-subregion-chained-hw.html and css3/filters/effect-reference-subregion-hw.html, but it actually makes them give correct results. It should be suppressed on the skia roll, and I'll rebaseline it.
R=reed@google.com
Review URL: https://codereview.chromium.org/112803004
git-svn-id: http://skia.googlecode.com/svn/trunk@12895 2bbb7eff-a529-9590-31e7-b0007b416f81
2014-01-03 21:48:22 +00:00
|
|
|
|
2014-02-03 22:22:16 +00:00
|
|
|
for (size_t i = 0; i < SK_ARRAY_COUNT(filters); ++i) {
|
|
|
|
SkSafeUnref(filters[i]);
|
Make SkImageFilter crop rects relative to the primitive origin, instead of relative to their parent's crop rect. This is required by SVG semantics, and is more sane anyway.
To do this, this patch changes the "offset/loc" parameter in filterImage() / onFilterImage() from an inout-param to an out-param only, so that the calling filter can know how much the input filter wants its result offset (and doesn't include the original primitive position). This offset can then be applied to the current filter's crop rect. (I've renamed the parameter "offset" in all cases to make this clear.) This makes the call sites in SkCanvas/SkGpuDevice responsible for applying the resulting offset to the primitive's position, which is actually a fairly small change.
This change also fixes SkTileImageFilter and SkOffsetImageFilter to correctly handle an input offset, which they weren't before. This required modifying the GM's, since they assumed the broken behaviour.
NOTE: this will require rebaselining the imagefiltersgraph test, since it has a new test case.
NOTE: this will "break" the Blink layout tests css3/filters/effect-reference-subregion-chained-hw.html and css3/filters/effect-reference-subregion-hw.html, but it actually makes them give correct results. It should be suppressed on the skia roll, and I'll rebaseline it.
R=reed@google.com
Review URL: https://codereview.chromium.org/112803004
git-svn-id: http://skia.googlecode.com/svn/trunk@12895 2bbb7eff-a529-9590-31e7-b0007b416f81
2014-01-03 21:48:22 +00:00
|
|
|
}
|
2013-12-18 22:15:12 +00:00
|
|
|
}
|
2014-02-03 22:22:16 +00:00
|
|
|
|
2014-04-29 15:20:39 +00:00
|
|
|
static SkBitmap make_gradient_circle(int width, int height) {
|
|
|
|
SkBitmap bitmap;
|
|
|
|
SkScalar x = SkIntToScalar(width / 2);
|
|
|
|
SkScalar y = SkIntToScalar(height / 2);
|
|
|
|
SkScalar radius = SkMinScalar(x, y) * 0.8f;
|
|
|
|
bitmap.allocN32Pixels(width, height);
|
|
|
|
SkCanvas canvas(bitmap);
|
|
|
|
canvas.clear(0x00000000);
|
|
|
|
SkColor colors[2];
|
|
|
|
colors[0] = SK_ColorWHITE;
|
|
|
|
colors[1] = SK_ColorBLACK;
|
|
|
|
SkAutoTUnref<SkShader> shader(
|
2015-08-27 14:41:13 +00:00
|
|
|
SkGradientShader::CreateRadial(SkPoint::Make(x, y), radius, colors, nullptr, 2,
|
2014-04-29 15:20:39 +00:00
|
|
|
SkShader::kClamp_TileMode)
|
|
|
|
);
|
|
|
|
SkPaint paint;
|
|
|
|
paint.setShader(shader);
|
|
|
|
canvas.drawCircle(x, y, radius, paint);
|
|
|
|
return bitmap;
|
|
|
|
}
|
|
|
|
|
2015-06-22 16:46:59 +00:00
|
|
|
static void test_negative_blur_sigma(SkImageFilter::Proxy* proxy, skiatest::Reporter* reporter) {
|
2014-09-09 16:15:04 +00:00
|
|
|
// Check that SkBlurImageFilter will accept a negative sigma, either in
|
|
|
|
// the given arguments or after CTM application.
|
2015-10-06 21:05:32 +00:00
|
|
|
const int width = 32, height = 32;
|
|
|
|
const SkScalar five = SkIntToScalar(5);
|
2014-09-09 16:15:04 +00:00
|
|
|
|
2015-10-06 21:05:32 +00:00
|
|
|
SkAutoTUnref<SkImageFilter> positiveFilter(SkBlurImageFilter::Create(five, five));
|
|
|
|
SkAutoTUnref<SkImageFilter> negativeFilter(SkBlurImageFilter::Create(-five, five));
|
2014-09-09 16:15:04 +00:00
|
|
|
|
|
|
|
SkBitmap gradient = make_gradient_circle(width, height);
|
|
|
|
SkBitmap positiveResult1, negativeResult1;
|
|
|
|
SkBitmap positiveResult2, negativeResult2;
|
|
|
|
SkIPoint offset;
|
2015-11-25 18:32:18 +00:00
|
|
|
SkImageFilter::Context ctx(SkMatrix::I(), SkIRect::MakeWH(32, 32), nullptr, SkImageFilter::kApprox_SizeConstraint);
|
|
|
|
REPORTER_ASSERT(reporter, positiveFilter->filterImage(proxy, gradient, ctx, &positiveResult1, &offset));
|
|
|
|
REPORTER_ASSERT(reporter, negativeFilter->filterImage(proxy, gradient, ctx, &negativeResult1, &offset));
|
2014-09-09 16:15:04 +00:00
|
|
|
SkMatrix negativeScale;
|
|
|
|
negativeScale.setScale(-SK_Scalar1, SK_Scalar1);
|
2015-11-25 18:32:18 +00:00
|
|
|
SkImageFilter::Context negativeCTX(negativeScale, SkIRect::MakeWH(32, 32), nullptr, SkImageFilter::kApprox_SizeConstraint);
|
|
|
|
REPORTER_ASSERT(reporter, positiveFilter->filterImage(proxy, gradient, negativeCTX, &negativeResult2, &offset));
|
|
|
|
REPORTER_ASSERT(reporter, negativeFilter->filterImage(proxy, gradient, negativeCTX, &positiveResult2, &offset));
|
2014-09-09 16:15:04 +00:00
|
|
|
SkAutoLockPixels lockP1(positiveResult1);
|
|
|
|
SkAutoLockPixels lockP2(positiveResult2);
|
|
|
|
SkAutoLockPixels lockN1(negativeResult1);
|
|
|
|
SkAutoLockPixels lockN2(negativeResult2);
|
|
|
|
for (int y = 0; y < height; y++) {
|
|
|
|
int diffs = memcmp(positiveResult1.getAddr32(0, y), negativeResult1.getAddr32(0, y), positiveResult1.rowBytes());
|
|
|
|
REPORTER_ASSERT(reporter, !diffs);
|
|
|
|
if (diffs) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
diffs = memcmp(positiveResult1.getAddr32(0, y), negativeResult2.getAddr32(0, y), positiveResult1.rowBytes());
|
|
|
|
REPORTER_ASSERT(reporter, !diffs);
|
|
|
|
if (diffs) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
diffs = memcmp(positiveResult1.getAddr32(0, y), positiveResult2.getAddr32(0, y), positiveResult1.rowBytes());
|
|
|
|
REPORTER_ASSERT(reporter, !diffs);
|
|
|
|
if (diffs) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
DEF_TEST(TestNegativeBlurSigma, reporter) {
|
2015-06-22 16:46:59 +00:00
|
|
|
const SkImageInfo info = SkImageInfo::MakeN32Premul(100, 100);
|
|
|
|
const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
|
|
|
|
|
|
|
|
SkAutoTUnref<SkBaseDevice> device(SkBitmapDevice::Create(info, props));
|
2015-10-12 18:30:02 +00:00
|
|
|
SkImageFilter::DeviceProxy proxy(device);
|
2015-06-22 16:46:59 +00:00
|
|
|
|
|
|
|
test_negative_blur_sigma(&proxy, reporter);
|
2014-09-09 16:15:04 +00:00
|
|
|
}
|
|
|
|
|
2014-04-29 15:20:39 +00:00
|
|
|
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.
|
|
|
|
// Tests pass by not asserting.
|
|
|
|
|
|
|
|
SkAutoTUnref<SkColorFilter> cf(SkColorFilter::CreateModeFilter(SK_ColorRED, SkXfermode::kSrcIn_Mode));
|
2015-07-13 20:16:44 +00:00
|
|
|
SkPoint3 location = SkPoint3::Make(0, 0, SK_Scalar1);
|
2014-04-29 15:20:39 +00:00
|
|
|
SkScalar kernel[9] = {
|
|
|
|
SkIntToScalar( 1), SkIntToScalar( 1), SkIntToScalar( 1),
|
|
|
|
SkIntToScalar( 1), SkIntToScalar(-7), SkIntToScalar( 1),
|
|
|
|
SkIntToScalar( 1), SkIntToScalar( 1), SkIntToScalar( 1),
|
|
|
|
};
|
2015-10-06 21:05:32 +00:00
|
|
|
const SkISize kernelSize = SkISize::Make(3, 3);
|
|
|
|
const SkScalar gain = SK_Scalar1, bias = 0;
|
|
|
|
const SkScalar five = SkIntToScalar(5);
|
2014-04-29 15:20:39 +00:00
|
|
|
|
2015-09-15 18:26:13 +00:00
|
|
|
SkAutoTUnref<SkImage> gradientImage(SkImage::NewFromBitmap(make_gradient_circle(64, 64)));
|
|
|
|
SkAutoTUnref<SkImageFilter> gradientSource(SkImageSource::Create(gradientImage));
|
2014-05-28 19:29:25 +00:00
|
|
|
SkAutoTUnref<SkImageFilter> blur(SkBlurImageFilter::Create(five, five));
|
2014-05-07 20:56:08 +00:00
|
|
|
SkMatrix matrix;
|
2014-05-28 19:29:25 +00:00
|
|
|
|
2014-05-07 20:56:08 +00:00
|
|
|
matrix.setTranslate(SK_Scalar1, SK_Scalar1);
|
|
|
|
matrix.postRotate(SkIntToScalar(45), SK_Scalar1, SK_Scalar1);
|
2014-04-29 15:20:39 +00:00
|
|
|
|
2014-05-30 20:36:15 +00:00
|
|
|
SkRTreeFactory factory;
|
|
|
|
SkPictureRecorder recorder;
|
|
|
|
SkCanvas* recordingCanvas = recorder.beginRecording(64, 64, &factory, 0);
|
|
|
|
|
|
|
|
SkPaint greenPaint;
|
|
|
|
greenPaint.setColor(SK_ColorGREEN);
|
|
|
|
recordingCanvas->drawRect(SkRect::Make(SkIRect::MakeXYWH(10, 10, 30, 20)), greenPaint);
|
|
|
|
SkAutoTUnref<SkPicture> picture(recorder.endRecording());
|
|
|
|
SkAutoTUnref<SkImageFilter> pictureFilter(SkPictureImageFilter::Create(picture.get()));
|
2014-10-29 19:36:32 +00:00
|
|
|
SkAutoTUnref<SkShader> shader(SkPerlinNoiseShader::CreateTurbulence(SK_Scalar1, SK_Scalar1, 1, 0));
|
|
|
|
|
|
|
|
SkAutoTUnref<SkImageFilter> rectShaderFilter(SkRectShaderImageFilter::Create(shader.get()));
|
2014-05-30 20:36:15 +00:00
|
|
|
|
2015-05-20 07:52:17 +00:00
|
|
|
SkAutoTUnref<SkShader> greenColorShader(SkShader::CreateColorShader(SK_ColorGREEN));
|
|
|
|
SkImageFilter::CropRect leftSideCropRect(SkRect::MakeXYWH(0, 0, 32, 64));
|
|
|
|
SkAutoTUnref<SkImageFilter> rectShaderFilterLeft(SkRectShaderImageFilter::Create(greenColorShader.get(), &leftSideCropRect));
|
|
|
|
SkImageFilter::CropRect rightSideCropRect(SkRect::MakeXYWH(32, 0, 32, 64));
|
|
|
|
SkAutoTUnref<SkImageFilter> rectShaderFilterRight(SkRectShaderImageFilter::Create(greenColorShader.get(), &rightSideCropRect));
|
|
|
|
|
2014-04-29 15:20:39 +00:00
|
|
|
struct {
|
|
|
|
const char* fName;
|
|
|
|
SkImageFilter* fFilter;
|
|
|
|
} filters[] = {
|
|
|
|
{ "color filter", SkColorFilterImageFilter::Create(cf.get()) },
|
|
|
|
{ "displacement map", SkDisplacementMapEffect::Create(
|
|
|
|
SkDisplacementMapEffect::kR_ChannelSelectorType,
|
|
|
|
SkDisplacementMapEffect::kB_ChannelSelectorType,
|
2015-09-15 18:26:13 +00:00
|
|
|
20.0f, gradientSource.get()) },
|
2014-04-29 15:20:39 +00:00
|
|
|
{ "blur", SkBlurImageFilter::Create(SK_Scalar1, SK_Scalar1) },
|
|
|
|
{ "drop shadow", SkDropShadowImageFilter::Create(
|
2014-10-23 20:59:52 +00:00
|
|
|
SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_ColorGREEN,
|
|
|
|
SkDropShadowImageFilter::kDrawShadowAndForeground_ShadowMode) },
|
2014-04-29 15:20:39 +00:00
|
|
|
{ "diffuse lighting", SkLightingImageFilter::CreatePointLitDiffuse(
|
|
|
|
location, SK_ColorGREEN, 0, 0) },
|
|
|
|
{ "specular lighting",
|
|
|
|
SkLightingImageFilter::CreatePointLitSpecular(location, SK_ColorGREEN, 0, 0, 0) },
|
|
|
|
{ "matrix convolution",
|
|
|
|
SkMatrixConvolutionImageFilter::Create(
|
|
|
|
kernelSize, kernel, gain, bias, SkIPoint::Make(1, 1),
|
|
|
|
SkMatrixConvolutionImageFilter::kRepeat_TileMode, false) },
|
2015-08-27 14:41:13 +00:00
|
|
|
{ "merge", SkMergeImageFilter::Create(nullptr, nullptr, SkXfermode::kSrcOver_Mode) },
|
2015-05-20 07:52:17 +00:00
|
|
|
{ "merge with disjoint inputs", SkMergeImageFilter::Create(
|
|
|
|
rectShaderFilterLeft, rectShaderFilterRight, SkXfermode::kSrcOver_Mode) },
|
2014-04-29 15:20:39 +00:00
|
|
|
{ "offset", SkOffsetImageFilter::Create(SK_Scalar1, SK_Scalar1) },
|
|
|
|
{ "dilate", SkDilateImageFilter::Create(3, 2) },
|
|
|
|
{ "erode", SkErodeImageFilter::Create(2, 3) },
|
|
|
|
{ "tile", SkTileImageFilter::Create(SkRect::MakeXYWH(0, 0, 50, 50),
|
2015-08-27 14:41:13 +00:00
|
|
|
SkRect::MakeXYWH(0, 0, 100, 100), nullptr) },
|
2015-03-20 13:38:17 +00:00
|
|
|
{ "matrix", SkImageFilter::CreateMatrixFilter(matrix, kLow_SkFilterQuality) },
|
2014-05-28 19:29:25 +00:00
|
|
|
{ "blur and offset", SkOffsetImageFilter::Create(five, five, blur.get()) },
|
2014-05-30 20:36:15 +00:00
|
|
|
{ "picture and blur", SkBlurImageFilter::Create(five, five, pictureFilter.get()) },
|
2014-10-29 19:36:32 +00:00
|
|
|
{ "rect shader and blur", SkBlurImageFilter::Create(five, five, rectShaderFilter.get()) },
|
2014-04-29 15:20:39 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
SkBitmap untiledResult, tiledResult;
|
2015-10-06 21:05:32 +00:00
|
|
|
const int width = 64, height = 64;
|
2014-04-29 15:20:39 +00:00
|
|
|
untiledResult.allocN32Pixels(width, height);
|
|
|
|
tiledResult.allocN32Pixels(width, height);
|
|
|
|
SkCanvas tiledCanvas(tiledResult);
|
|
|
|
SkCanvas untiledCanvas(untiledResult);
|
2014-05-07 20:00:04 +00:00
|
|
|
int tileSize = 8;
|
2014-04-29 15:20:39 +00:00
|
|
|
|
2014-05-07 20:00:04 +00:00
|
|
|
for (int scale = 1; scale <= 2; ++scale) {
|
|
|
|
for (size_t i = 0; i < SK_ARRAY_COUNT(filters); ++i) {
|
|
|
|
tiledCanvas.clear(0);
|
|
|
|
untiledCanvas.clear(0);
|
|
|
|
SkPaint paint;
|
|
|
|
paint.setImageFilter(filters[i].fFilter);
|
|
|
|
paint.setTextSize(SkIntToScalar(height));
|
|
|
|
paint.setColor(SK_ColorWHITE);
|
|
|
|
SkString str;
|
|
|
|
const char* text = "ABC";
|
|
|
|
SkScalar ypos = SkIntToScalar(height);
|
|
|
|
untiledCanvas.save();
|
|
|
|
untiledCanvas.scale(SkIntToScalar(scale), SkIntToScalar(scale));
|
|
|
|
untiledCanvas.drawText(text, strlen(text), 0, ypos, paint);
|
|
|
|
untiledCanvas.restore();
|
|
|
|
for (int y = 0; y < height; y += tileSize) {
|
|
|
|
for (int x = 0; x < width; x += tileSize) {
|
|
|
|
tiledCanvas.save();
|
|
|
|
tiledCanvas.clipRect(SkRect::Make(SkIRect::MakeXYWH(x, y, tileSize, tileSize)));
|
|
|
|
tiledCanvas.scale(SkIntToScalar(scale), SkIntToScalar(scale));
|
|
|
|
tiledCanvas.drawText(text, strlen(text), 0, ypos, paint);
|
|
|
|
tiledCanvas.restore();
|
|
|
|
}
|
2014-04-29 15:20:39 +00:00
|
|
|
}
|
2014-05-07 20:00:04 +00:00
|
|
|
untiledCanvas.flush();
|
|
|
|
tiledCanvas.flush();
|
|
|
|
for (int y = 0; y < height; y++) {
|
|
|
|
int diffs = memcmp(untiledResult.getAddr32(0, y), tiledResult.getAddr32(0, y), untiledResult.rowBytes());
|
|
|
|
REPORTER_ASSERT_MESSAGE(reporter, !diffs, filters[i].fName);
|
|
|
|
if (diffs) {
|
|
|
|
break;
|
|
|
|
}
|
2014-04-29 15:20:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (size_t i = 0; i < SK_ARRAY_COUNT(filters); ++i) {
|
|
|
|
SkSafeUnref(filters[i].fFilter);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-12-01 19:47:08 +00:00
|
|
|
static void draw_saveLayer_picture(int width, int height, int tileSize,
|
2014-08-29 15:03:56 +00:00
|
|
|
SkBBHFactory* factory, SkBitmap* result) {
|
2014-08-22 16:06:34 +00:00
|
|
|
|
|
|
|
SkMatrix matrix;
|
|
|
|
matrix.setTranslate(SkIntToScalar(50), 0);
|
|
|
|
|
|
|
|
SkAutoTUnref<SkColorFilter> cf(SkColorFilter::CreateModeFilter(SK_ColorWHITE, SkXfermode::kSrc_Mode));
|
|
|
|
SkAutoTUnref<SkImageFilter> cfif(SkColorFilterImageFilter::Create(cf.get()));
|
2015-03-20 13:38:17 +00:00
|
|
|
SkAutoTUnref<SkImageFilter> imageFilter(SkImageFilter::CreateMatrixFilter(matrix, kNone_SkFilterQuality, cfif.get()));
|
2014-08-22 16:06:34 +00:00
|
|
|
|
|
|
|
SkPaint paint;
|
|
|
|
paint.setImageFilter(imageFilter.get());
|
|
|
|
SkPictureRecorder recorder;
|
|
|
|
SkRect bounds = SkRect::Make(SkIRect::MakeXYWH(0, 0, 50, 50));
|
2014-12-01 19:47:08 +00:00
|
|
|
SkCanvas* recordingCanvas = recorder.beginRecording(SkIntToScalar(width),
|
|
|
|
SkIntToScalar(height),
|
2014-08-29 15:03:56 +00:00
|
|
|
factory, 0);
|
2014-08-22 16:06:34 +00:00
|
|
|
recordingCanvas->translate(-55, 0);
|
|
|
|
recordingCanvas->saveLayer(&bounds, &paint);
|
|
|
|
recordingCanvas->restore();
|
|
|
|
SkAutoTUnref<SkPicture> picture1(recorder.endRecording());
|
|
|
|
|
|
|
|
result->allocN32Pixels(width, height);
|
|
|
|
SkCanvas canvas(*result);
|
|
|
|
canvas.clear(0);
|
|
|
|
canvas.clipRect(SkRect::Make(SkIRect::MakeWH(tileSize, tileSize)));
|
|
|
|
canvas.drawPicture(picture1.get());
|
|
|
|
}
|
|
|
|
|
|
|
|
DEF_TEST(ImageFilterDrawMatrixBBH, reporter) {
|
|
|
|
// Check that matrix filter when drawn tiled with BBH exactly
|
|
|
|
// matches the same thing drawn without BBH.
|
|
|
|
// Tests pass by not asserting.
|
|
|
|
|
|
|
|
const int width = 200, height = 200;
|
|
|
|
const int tileSize = 100;
|
|
|
|
SkBitmap result1, result2;
|
|
|
|
SkRTreeFactory factory;
|
|
|
|
|
2014-08-29 15:03:56 +00:00
|
|
|
draw_saveLayer_picture(width, height, tileSize, &factory, &result1);
|
2015-08-27 14:41:13 +00:00
|
|
|
draw_saveLayer_picture(width, height, tileSize, nullptr, &result2);
|
2014-08-22 16:06:34 +00:00
|
|
|
|
|
|
|
for (int y = 0; y < height; y++) {
|
|
|
|
int diffs = memcmp(result1.getAddr32(0, y), result2.getAddr32(0, y), result1.rowBytes());
|
|
|
|
REPORTER_ASSERT(reporter, !diffs);
|
|
|
|
if (diffs) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-08-27 14:41:13 +00:00
|
|
|
static SkImageFilter* makeBlur(SkImageFilter* input = nullptr) {
|
2014-08-25 19:46:58 +00:00
|
|
|
return SkBlurImageFilter::Create(SK_Scalar1, SK_Scalar1, input);
|
|
|
|
}
|
|
|
|
|
2015-08-27 14:41:13 +00:00
|
|
|
static SkImageFilter* makeDropShadow(SkImageFilter* input = nullptr) {
|
2014-08-25 19:46:58 +00:00
|
|
|
return SkDropShadowImageFilter::Create(
|
|
|
|
SkIntToScalar(100), SkIntToScalar(100),
|
|
|
|
SkIntToScalar(10), SkIntToScalar(10),
|
2014-10-23 20:59:52 +00:00
|
|
|
SK_ColorBLUE, SkDropShadowImageFilter::kDrawShadowAndForeground_ShadowMode,
|
2015-08-27 14:41:13 +00:00
|
|
|
input, nullptr);
|
2014-08-25 19:46:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
DEF_TEST(ImageFilterBlurThenShadowBounds, reporter) {
|
|
|
|
SkAutoTUnref<SkImageFilter> filter1(makeBlur());
|
|
|
|
SkAutoTUnref<SkImageFilter> filter2(makeDropShadow(filter1.get()));
|
|
|
|
|
|
|
|
SkIRect bounds = SkIRect::MakeXYWH(0, 0, 100, 100);
|
|
|
|
SkIRect expectedBounds = SkIRect::MakeXYWH(-133, -133, 236, 236);
|
|
|
|
filter2->filterBounds(bounds, SkMatrix::I(), &bounds);
|
|
|
|
|
|
|
|
REPORTER_ASSERT(reporter, bounds == expectedBounds);
|
|
|
|
}
|
|
|
|
|
|
|
|
DEF_TEST(ImageFilterShadowThenBlurBounds, reporter) {
|
|
|
|
SkAutoTUnref<SkImageFilter> filter1(makeDropShadow());
|
|
|
|
SkAutoTUnref<SkImageFilter> filter2(makeBlur(filter1.get()));
|
|
|
|
|
|
|
|
SkIRect bounds = SkIRect::MakeXYWH(0, 0, 100, 100);
|
|
|
|
SkIRect expectedBounds = SkIRect::MakeXYWH(-133, -133, 236, 236);
|
|
|
|
filter2->filterBounds(bounds, SkMatrix::I(), &bounds);
|
|
|
|
|
|
|
|
REPORTER_ASSERT(reporter, bounds == expectedBounds);
|
|
|
|
}
|
|
|
|
|
|
|
|
DEF_TEST(ImageFilterDilateThenBlurBounds, reporter) {
|
|
|
|
SkAutoTUnref<SkImageFilter> filter1(SkDilateImageFilter::Create(2, 2));
|
|
|
|
SkAutoTUnref<SkImageFilter> filter2(makeDropShadow(filter1.get()));
|
|
|
|
|
|
|
|
SkIRect bounds = SkIRect::MakeXYWH(0, 0, 100, 100);
|
|
|
|
SkIRect expectedBounds = SkIRect::MakeXYWH(-132, -132, 234, 234);
|
|
|
|
filter2->filterBounds(bounds, SkMatrix::I(), &bounds);
|
|
|
|
|
|
|
|
REPORTER_ASSERT(reporter, bounds == expectedBounds);
|
|
|
|
}
|
|
|
|
|
2015-02-13 17:05:47 +00:00
|
|
|
DEF_TEST(ImageFilterComposedBlurFastBounds, reporter) {
|
|
|
|
SkAutoTUnref<SkImageFilter> filter1(makeBlur());
|
|
|
|
SkAutoTUnref<SkImageFilter> filter2(makeBlur());
|
|
|
|
SkAutoTUnref<SkImageFilter> composedFilter(SkComposeImageFilter::Create(filter1.get(), filter2.get()));
|
|
|
|
|
|
|
|
SkRect boundsSrc = SkRect::MakeWH(SkIntToScalar(100), SkIntToScalar(100));
|
|
|
|
SkRect expectedBounds = SkRect::MakeXYWH(
|
|
|
|
SkIntToScalar(-6), SkIntToScalar(-6), SkIntToScalar(112), SkIntToScalar(112));
|
|
|
|
SkRect boundsDst = SkRect::MakeEmpty();
|
|
|
|
composedFilter->computeFastBounds(boundsSrc, &boundsDst);
|
|
|
|
|
|
|
|
REPORTER_ASSERT(reporter, boundsDst == expectedBounds);
|
|
|
|
}
|
|
|
|
|
2015-11-25 15:06:55 +00:00
|
|
|
DEF_TEST(ImageFilterMergeResultSize, reporter) {
|
|
|
|
SkBitmap greenBM;
|
|
|
|
greenBM.allocN32Pixels(20, 20);
|
|
|
|
greenBM.eraseColor(SK_ColorGREEN);
|
|
|
|
SkAutoTUnref<SkImage> greenImage(SkImage::NewFromBitmap(greenBM));
|
|
|
|
SkAutoTUnref<SkImageFilter> source(SkImageSource::Create(greenImage.get()));
|
|
|
|
SkAutoTUnref<SkImageFilter> merge(SkMergeImageFilter::Create(source.get(), source.get()));
|
|
|
|
|
|
|
|
SkBitmap bitmap;
|
|
|
|
bitmap.allocN32Pixels(1, 1);
|
|
|
|
bitmap.eraseColor(0);
|
|
|
|
const SkImageInfo info = SkImageInfo::MakeN32Premul(100, 100);
|
|
|
|
const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
|
|
|
|
SkAutoTUnref<SkBaseDevice> device(SkBitmapDevice::Create(info, props));
|
|
|
|
SkImageFilter::DeviceProxy proxy(device);
|
|
|
|
SkImageFilter::Context ctx(SkMatrix::I(), SkIRect::MakeXYWH(0, 0, 100, 100), nullptr,
|
|
|
|
SkImageFilter::kApprox_SizeConstraint);
|
|
|
|
SkBitmap result;
|
|
|
|
SkIPoint offset;
|
|
|
|
REPORTER_ASSERT(reporter, merge->filterImage(&proxy, bitmap, ctx, &result, &offset));
|
|
|
|
REPORTER_ASSERT(reporter, result.width() == 20 && result.height() == 20);
|
|
|
|
}
|
|
|
|
|
2014-08-29 15:03:56 +00:00
|
|
|
static void draw_blurred_rect(SkCanvas* canvas) {
|
2014-07-14 17:19:54 +00:00
|
|
|
SkAutoTUnref<SkImageFilter> filter(SkBlurImageFilter::Create(SkIntToScalar(8), 0));
|
|
|
|
SkPaint filterPaint;
|
|
|
|
filterPaint.setColor(SK_ColorWHITE);
|
|
|
|
filterPaint.setImageFilter(filter);
|
2015-08-27 14:41:13 +00:00
|
|
|
canvas->saveLayer(nullptr, &filterPaint);
|
2014-07-14 17:19:54 +00:00
|
|
|
SkPaint whitePaint;
|
|
|
|
whitePaint.setColor(SK_ColorWHITE);
|
|
|
|
canvas->drawRect(SkRect::Make(SkIRect::MakeWH(4, 4)), whitePaint);
|
|
|
|
canvas->restore();
|
|
|
|
}
|
|
|
|
|
2014-08-29 15:03:56 +00:00
|
|
|
static void draw_picture_clipped(SkCanvas* canvas, const SkRect& clipRect, const SkPicture* picture) {
|
2014-07-14 17:19:54 +00:00
|
|
|
canvas->save();
|
|
|
|
canvas->clipRect(clipRect);
|
|
|
|
canvas->drawPicture(picture);
|
|
|
|
canvas->restore();
|
|
|
|
}
|
|
|
|
|
|
|
|
DEF_TEST(ImageFilterDrawTiledBlurRTree, reporter) {
|
|
|
|
// Check that the blur filter when recorded with RTree acceleration,
|
|
|
|
// and drawn tiled (with subsequent clip rects) exactly
|
|
|
|
// matches the same filter drawn with without RTree acceleration.
|
|
|
|
// This tests that the "bleed" from the blur into the otherwise-blank
|
|
|
|
// tiles is correctly rendered.
|
|
|
|
// Tests pass by not asserting.
|
|
|
|
|
|
|
|
int width = 16, height = 8;
|
|
|
|
SkBitmap result1, result2;
|
|
|
|
result1.allocN32Pixels(width, height);
|
|
|
|
result2.allocN32Pixels(width, height);
|
|
|
|
SkCanvas canvas1(result1);
|
|
|
|
SkCanvas canvas2(result2);
|
|
|
|
int tileSize = 8;
|
|
|
|
|
|
|
|
canvas1.clear(0);
|
|
|
|
canvas2.clear(0);
|
|
|
|
|
|
|
|
SkRTreeFactory factory;
|
|
|
|
|
|
|
|
SkPictureRecorder recorder1, recorder2;
|
|
|
|
// The only difference between these two pictures is that one has RTree aceleration.
|
2014-12-01 19:47:08 +00:00
|
|
|
SkCanvas* recordingCanvas1 = recorder1.beginRecording(SkIntToScalar(width),
|
|
|
|
SkIntToScalar(height),
|
2015-08-27 14:41:13 +00:00
|
|
|
nullptr, 0);
|
2014-12-01 19:47:08 +00:00
|
|
|
SkCanvas* recordingCanvas2 = recorder2.beginRecording(SkIntToScalar(width),
|
|
|
|
SkIntToScalar(height),
|
2014-08-29 15:03:56 +00:00
|
|
|
&factory, 0);
|
|
|
|
draw_blurred_rect(recordingCanvas1);
|
|
|
|
draw_blurred_rect(recordingCanvas2);
|
2014-07-14 17:19:54 +00:00
|
|
|
SkAutoTUnref<SkPicture> picture1(recorder1.endRecording());
|
|
|
|
SkAutoTUnref<SkPicture> picture2(recorder2.endRecording());
|
|
|
|
for (int y = 0; y < height; y += tileSize) {
|
|
|
|
for (int x = 0; x < width; x += tileSize) {
|
|
|
|
SkRect tileRect = SkRect::Make(SkIRect::MakeXYWH(x, y, tileSize, tileSize));
|
2014-08-29 15:03:56 +00:00
|
|
|
draw_picture_clipped(&canvas1, tileRect, picture1);
|
|
|
|
draw_picture_clipped(&canvas2, tileRect, picture2);
|
2014-07-14 17:19:54 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
for (int y = 0; y < height; y++) {
|
|
|
|
int diffs = memcmp(result1.getAddr32(0, y), result2.getAddr32(0, y), result1.rowBytes());
|
|
|
|
REPORTER_ASSERT(reporter, !diffs);
|
|
|
|
if (diffs) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-01 14:03:41 +00:00
|
|
|
DEF_TEST(ImageFilterMatrixConvolution, reporter) {
|
|
|
|
// Check that a 1x3 filter does not cause a spurious assert.
|
|
|
|
SkScalar kernel[3] = {
|
|
|
|
SkIntToScalar( 1), SkIntToScalar( 1), SkIntToScalar( 1),
|
|
|
|
};
|
|
|
|
SkISize kernelSize = SkISize::Make(1, 3);
|
|
|
|
SkScalar gain = SK_Scalar1, bias = 0;
|
|
|
|
SkIPoint kernelOffset = SkIPoint::Make(0, 0);
|
|
|
|
|
|
|
|
SkAutoTUnref<SkImageFilter> filter(
|
|
|
|
SkMatrixConvolutionImageFilter::Create(
|
|
|
|
kernelSize, kernel, gain, bias, kernelOffset,
|
|
|
|
SkMatrixConvolutionImageFilter::kRepeat_TileMode, false));
|
|
|
|
|
|
|
|
SkBitmap result;
|
|
|
|
int width = 16, height = 16;
|
|
|
|
result.allocN32Pixels(width, height);
|
|
|
|
SkCanvas canvas(result);
|
|
|
|
canvas.clear(0);
|
|
|
|
|
|
|
|
SkPaint paint;
|
|
|
|
paint.setImageFilter(filter);
|
|
|
|
SkRect rect = SkRect::Make(SkIRect::MakeWH(width, height));
|
|
|
|
canvas.drawRect(rect, paint);
|
|
|
|
}
|
|
|
|
|
2014-05-02 19:13:11 +00:00
|
|
|
DEF_TEST(ImageFilterMatrixConvolutionBorder, reporter) {
|
|
|
|
// Check that a filter with borders outside the target bounds
|
|
|
|
// does not crash.
|
|
|
|
SkScalar kernel[3] = {
|
|
|
|
0, 0, 0,
|
|
|
|
};
|
|
|
|
SkISize kernelSize = SkISize::Make(3, 1);
|
|
|
|
SkScalar gain = SK_Scalar1, bias = 0;
|
|
|
|
SkIPoint kernelOffset = SkIPoint::Make(2, 0);
|
|
|
|
|
|
|
|
SkAutoTUnref<SkImageFilter> filter(
|
|
|
|
SkMatrixConvolutionImageFilter::Create(
|
|
|
|
kernelSize, kernel, gain, bias, kernelOffset,
|
|
|
|
SkMatrixConvolutionImageFilter::kClamp_TileMode, true));
|
|
|
|
|
|
|
|
SkBitmap result;
|
|
|
|
|
|
|
|
int width = 10, height = 10;
|
|
|
|
result.allocN32Pixels(width, height);
|
|
|
|
SkCanvas canvas(result);
|
|
|
|
canvas.clear(0);
|
|
|
|
|
|
|
|
SkPaint filterPaint;
|
|
|
|
filterPaint.setImageFilter(filter);
|
|
|
|
SkRect bounds = SkRect::MakeWH(1, 10);
|
|
|
|
SkRect rect = SkRect::Make(SkIRect::MakeWH(width, height));
|
|
|
|
SkPaint rectPaint;
|
|
|
|
canvas.saveLayer(&bounds, &filterPaint);
|
|
|
|
canvas.drawRect(rect, rectPaint);
|
|
|
|
canvas.restore();
|
|
|
|
}
|
|
|
|
|
2014-02-03 22:22:16 +00:00
|
|
|
DEF_TEST(ImageFilterCropRect, reporter) {
|
2015-06-22 16:46:59 +00:00
|
|
|
const SkImageInfo info = SkImageInfo::MakeN32Premul(100, 100);
|
|
|
|
const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
|
|
|
|
|
|
|
|
SkAutoTUnref<SkBaseDevice> device(SkBitmapDevice::Create(info, props));
|
2015-10-12 18:30:02 +00:00
|
|
|
SkImageFilter::DeviceProxy proxy(device);
|
2015-06-22 16:46:59 +00:00
|
|
|
|
|
|
|
test_crop_rects(&proxy, reporter);
|
2014-02-03 22:22:16 +00:00
|
|
|
}
|
|
|
|
|
2014-06-24 13:50:39 +00:00
|
|
|
DEF_TEST(ImageFilterMatrix, reporter) {
|
2014-02-05 22:36:31 +00:00
|
|
|
SkBitmap temp;
|
2014-02-13 14:41:43 +00:00
|
|
|
temp.allocN32Pixels(100, 100);
|
2015-06-22 16:46:59 +00:00
|
|
|
SkCanvas canvas(temp);
|
2014-02-05 22:36:31 +00:00
|
|
|
canvas.scale(SkIntToScalar(2), SkIntToScalar(2));
|
|
|
|
|
|
|
|
SkMatrix expectedMatrix = canvas.getTotalMatrix();
|
|
|
|
|
2014-04-17 23:35:06 +00:00
|
|
|
SkRTreeFactory factory;
|
|
|
|
SkPictureRecorder recorder;
|
|
|
|
SkCanvas* recordingCanvas = recorder.beginRecording(100, 100, &factory, 0);
|
2014-02-05 22:36:31 +00:00
|
|
|
|
|
|
|
SkPaint paint;
|
|
|
|
SkAutoTUnref<MatrixTestImageFilter> imageFilter(
|
|
|
|
new MatrixTestImageFilter(reporter, expectedMatrix));
|
|
|
|
paint.setImageFilter(imageFilter.get());
|
2015-08-27 14:41:13 +00:00
|
|
|
recordingCanvas->saveLayer(nullptr, &paint);
|
2014-02-05 22:36:31 +00:00
|
|
|
SkPaint solidPaint;
|
|
|
|
solidPaint.setColor(0xFFFFFFFF);
|
|
|
|
recordingCanvas->save();
|
|
|
|
recordingCanvas->scale(SkIntToScalar(10), SkIntToScalar(10));
|
|
|
|
recordingCanvas->drawRect(SkRect::Make(SkIRect::MakeWH(100, 100)), solidPaint);
|
|
|
|
recordingCanvas->restore(); // scale
|
|
|
|
recordingCanvas->restore(); // saveLayer
|
2014-04-13 19:09:42 +00:00
|
|
|
SkAutoTUnref<SkPicture> picture(recorder.endRecording());
|
2014-02-05 22:36:31 +00:00
|
|
|
|
2014-06-04 12:40:44 +00:00
|
|
|
canvas.drawPicture(picture);
|
2014-02-05 22:36:31 +00:00
|
|
|
}
|
|
|
|
|
2014-07-30 21:49:31 +00:00
|
|
|
DEF_TEST(ImageFilterCrossProcessPictureImageFilter, reporter) {
|
2014-05-30 20:50:56 +00:00
|
|
|
SkRTreeFactory factory;
|
|
|
|
SkPictureRecorder recorder;
|
|
|
|
SkCanvas* recordingCanvas = recorder.beginRecording(1, 1, &factory, 0);
|
|
|
|
|
|
|
|
// Create an SkPicture which simply draws a green 1x1 rectangle.
|
|
|
|
SkPaint greenPaint;
|
|
|
|
greenPaint.setColor(SK_ColorGREEN);
|
|
|
|
recordingCanvas->drawRect(SkRect::Make(SkIRect::MakeWH(1, 1)), greenPaint);
|
|
|
|
SkAutoTUnref<SkPicture> picture(recorder.endRecording());
|
|
|
|
|
|
|
|
// Wrap that SkPicture in an SkPictureImageFilter.
|
|
|
|
SkAutoTUnref<SkImageFilter> imageFilter(
|
|
|
|
SkPictureImageFilter::Create(picture.get()));
|
|
|
|
|
|
|
|
// Check that SkPictureImageFilter successfully serializes its contained
|
|
|
|
// SkPicture when not in cross-process mode.
|
|
|
|
SkPaint paint;
|
|
|
|
paint.setImageFilter(imageFilter.get());
|
|
|
|
SkPictureRecorder outerRecorder;
|
|
|
|
SkCanvas* outerCanvas = outerRecorder.beginRecording(1, 1, &factory, 0);
|
|
|
|
SkPaint redPaintWithFilter;
|
|
|
|
redPaintWithFilter.setColor(SK_ColorRED);
|
|
|
|
redPaintWithFilter.setImageFilter(imageFilter.get());
|
|
|
|
outerCanvas->drawRect(SkRect::Make(SkIRect::MakeWH(1, 1)), redPaintWithFilter);
|
|
|
|
SkAutoTUnref<SkPicture> outerPicture(outerRecorder.endRecording());
|
|
|
|
|
|
|
|
SkBitmap bitmap;
|
|
|
|
bitmap.allocN32Pixels(1, 1);
|
2015-06-22 16:46:59 +00:00
|
|
|
SkCanvas canvas(bitmap);
|
2014-05-30 20:50:56 +00:00
|
|
|
|
|
|
|
// The result here should be green, since the filter replaces the primitive's red interior.
|
|
|
|
canvas.clear(0x0);
|
2014-06-04 12:40:44 +00:00
|
|
|
canvas.drawPicture(outerPicture);
|
2014-05-30 20:50:56 +00:00
|
|
|
uint32_t pixel = *bitmap.getAddr32(0, 0);
|
|
|
|
REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN);
|
|
|
|
|
|
|
|
// Check that, for now, SkPictureImageFilter does not serialize or
|
|
|
|
// deserialize its contained picture when the filter is serialized
|
|
|
|
// cross-process. Do this by "laundering" it through SkValidatingReadBuffer.
|
|
|
|
SkAutoTUnref<SkData> data(SkValidatingSerializeFlattenable(imageFilter.get()));
|
|
|
|
SkAutoTUnref<SkFlattenable> flattenable(SkValidatingDeserializeFlattenable(
|
|
|
|
data->data(), data->size(), SkImageFilter::GetFlattenableType()));
|
|
|
|
SkImageFilter* unflattenedFilter = static_cast<SkImageFilter*>(flattenable.get());
|
|
|
|
|
|
|
|
redPaintWithFilter.setImageFilter(unflattenedFilter);
|
|
|
|
SkPictureRecorder crossProcessRecorder;
|
|
|
|
SkCanvas* crossProcessCanvas = crossProcessRecorder.beginRecording(1, 1, &factory, 0);
|
|
|
|
crossProcessCanvas->drawRect(SkRect::Make(SkIRect::MakeWH(1, 1)), redPaintWithFilter);
|
|
|
|
SkAutoTUnref<SkPicture> crossProcessPicture(crossProcessRecorder.endRecording());
|
|
|
|
|
|
|
|
canvas.clear(0x0);
|
2014-06-04 12:40:44 +00:00
|
|
|
canvas.drawPicture(crossProcessPicture);
|
2014-05-30 20:50:56 +00:00
|
|
|
pixel = *bitmap.getAddr32(0, 0);
|
2015-06-16 16:28:37 +00:00
|
|
|
// If the security precautions are enabled, the result here should not be green, since the
|
|
|
|
// filter draws nothing.
|
|
|
|
REPORTER_ASSERT(reporter, SkPicture::PictureIOSecurityPrecautionsEnabled()
|
|
|
|
? pixel != SK_ColorGREEN : pixel == SK_ColorGREEN);
|
2014-05-30 20:50:56 +00:00
|
|
|
}
|
|
|
|
|
2014-07-30 21:49:31 +00:00
|
|
|
DEF_TEST(ImageFilterClippedPictureImageFilter, reporter) {
|
|
|
|
SkRTreeFactory factory;
|
|
|
|
SkPictureRecorder recorder;
|
|
|
|
SkCanvas* recordingCanvas = recorder.beginRecording(1, 1, &factory, 0);
|
|
|
|
|
|
|
|
// Create an SkPicture which simply draws a green 1x1 rectangle.
|
|
|
|
SkPaint greenPaint;
|
|
|
|
greenPaint.setColor(SK_ColorGREEN);
|
|
|
|
recordingCanvas->drawRect(SkRect::Make(SkIRect::MakeWH(1, 1)), greenPaint);
|
|
|
|
SkAutoTUnref<SkPicture> picture(recorder.endRecording());
|
|
|
|
|
2015-06-22 16:46:59 +00:00
|
|
|
SkAutoTUnref<SkImageFilter> imageFilter(SkPictureImageFilter::Create(picture.get()));
|
2014-07-30 21:49:31 +00:00
|
|
|
|
|
|
|
SkBitmap result;
|
|
|
|
SkIPoint offset;
|
2015-10-22 20:20:20 +00:00
|
|
|
SkImageFilter::Context ctx(SkMatrix::I(), SkIRect::MakeXYWH(1, 1, 1, 1), nullptr, SkImageFilter::kApprox_SizeConstraint);
|
2014-07-30 21:49:31 +00:00
|
|
|
SkBitmap bitmap;
|
|
|
|
bitmap.allocN32Pixels(2, 2);
|
2015-06-22 19:06:08 +00:00
|
|
|
const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
|
|
|
|
SkBitmapDevice device(bitmap, props);
|
2015-10-12 18:30:02 +00:00
|
|
|
SkImageFilter::DeviceProxy proxy(&device);
|
2014-07-30 21:49:31 +00:00
|
|
|
REPORTER_ASSERT(reporter, !imageFilter->filterImage(&proxy, bitmap, ctx, &result, &offset));
|
|
|
|
}
|
|
|
|
|
2014-06-24 13:50:39 +00:00
|
|
|
DEF_TEST(ImageFilterEmptySaveLayer, reporter) {
|
2014-05-06 22:52:55 +00:00
|
|
|
// Even when there's an empty saveLayer()/restore(), ensure that an image
|
|
|
|
// filter or color filter which affects transparent black still draws.
|
|
|
|
|
|
|
|
SkBitmap bitmap;
|
|
|
|
bitmap.allocN32Pixels(10, 10);
|
2015-06-22 16:46:59 +00:00
|
|
|
SkCanvas canvas(bitmap);
|
2014-05-06 22:52:55 +00:00
|
|
|
|
|
|
|
SkRTreeFactory factory;
|
|
|
|
SkPictureRecorder recorder;
|
|
|
|
|
|
|
|
SkAutoTUnref<SkColorFilter> green(
|
|
|
|
SkColorFilter::CreateModeFilter(SK_ColorGREEN, SkXfermode::kSrc_Mode));
|
2015-10-12 18:10:10 +00:00
|
|
|
SkAutoTUnref<SkImageFilter> imageFilter(
|
2014-05-06 22:52:55 +00:00
|
|
|
SkColorFilterImageFilter::Create(green.get()));
|
|
|
|
SkPaint imageFilterPaint;
|
|
|
|
imageFilterPaint.setImageFilter(imageFilter.get());
|
|
|
|
SkPaint colorFilterPaint;
|
|
|
|
colorFilterPaint.setColorFilter(green.get());
|
|
|
|
|
|
|
|
SkRect bounds = SkRect::MakeWH(10, 10);
|
|
|
|
|
|
|
|
SkCanvas* recordingCanvas = recorder.beginRecording(10, 10, &factory, 0);
|
|
|
|
recordingCanvas->saveLayer(&bounds, &imageFilterPaint);
|
|
|
|
recordingCanvas->restore();
|
|
|
|
SkAutoTUnref<SkPicture> picture(recorder.endRecording());
|
|
|
|
|
|
|
|
canvas.clear(0);
|
2014-06-04 12:40:44 +00:00
|
|
|
canvas.drawPicture(picture);
|
2014-05-06 22:52:55 +00:00
|
|
|
uint32_t pixel = *bitmap.getAddr32(0, 0);
|
|
|
|
REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN);
|
|
|
|
|
|
|
|
recordingCanvas = recorder.beginRecording(10, 10, &factory, 0);
|
2015-08-27 14:41:13 +00:00
|
|
|
recordingCanvas->saveLayer(nullptr, &imageFilterPaint);
|
2014-05-06 22:52:55 +00:00
|
|
|
recordingCanvas->restore();
|
|
|
|
SkAutoTUnref<SkPicture> picture2(recorder.endRecording());
|
|
|
|
|
|
|
|
canvas.clear(0);
|
2014-06-04 12:40:44 +00:00
|
|
|
canvas.drawPicture(picture2);
|
2014-05-06 22:52:55 +00:00
|
|
|
pixel = *bitmap.getAddr32(0, 0);
|
|
|
|
REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN);
|
|
|
|
|
|
|
|
recordingCanvas = recorder.beginRecording(10, 10, &factory, 0);
|
|
|
|
recordingCanvas->saveLayer(&bounds, &colorFilterPaint);
|
|
|
|
recordingCanvas->restore();
|
|
|
|
SkAutoTUnref<SkPicture> picture3(recorder.endRecording());
|
|
|
|
|
|
|
|
canvas.clear(0);
|
2014-06-04 12:40:44 +00:00
|
|
|
canvas.drawPicture(picture3);
|
2014-05-06 22:52:55 +00:00
|
|
|
pixel = *bitmap.getAddr32(0, 0);
|
|
|
|
REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN);
|
|
|
|
}
|
|
|
|
|
2015-06-22 16:46:59 +00:00
|
|
|
static void test_huge_blur(SkCanvas* canvas, skiatest::Reporter* reporter) {
|
2014-03-24 20:50:59 +00:00
|
|
|
SkBitmap bitmap;
|
|
|
|
bitmap.allocN32Pixels(100, 100);
|
|
|
|
bitmap.eraseARGB(0, 0, 0, 0);
|
|
|
|
|
|
|
|
// Check that a blur with an insane radius does not crash or assert.
|
|
|
|
SkAutoTUnref<SkImageFilter> blur(SkBlurImageFilter::Create(SkIntToScalar(1<<30), SkIntToScalar(1<<30)));
|
|
|
|
|
|
|
|
SkPaint paint;
|
|
|
|
paint.setImageFilter(blur);
|
2015-06-22 16:46:59 +00:00
|
|
|
canvas->drawSprite(bitmap, 0, 0, &paint);
|
2014-03-24 20:50:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
DEF_TEST(HugeBlurImageFilter, reporter) {
|
|
|
|
SkBitmap temp;
|
|
|
|
temp.allocN32Pixels(100, 100);
|
2015-06-22 16:46:59 +00:00
|
|
|
SkCanvas canvas(temp);
|
|
|
|
test_huge_blur(&canvas, reporter);
|
2014-03-24 20:50:59 +00:00
|
|
|
}
|
|
|
|
|
2014-09-29 14:57:20 +00:00
|
|
|
DEF_TEST(MatrixConvolutionSanityTest, reporter) {
|
|
|
|
SkScalar kernel[1] = { 0 };
|
|
|
|
SkScalar gain = SK_Scalar1, bias = 0;
|
|
|
|
SkIPoint kernelOffset = SkIPoint::Make(1, 1);
|
|
|
|
|
2015-08-27 14:41:13 +00:00
|
|
|
// Check that an enormous (non-allocatable) kernel gives a nullptr filter.
|
2014-09-29 14:57:20 +00:00
|
|
|
SkAutoTUnref<SkImageFilter> conv(SkMatrixConvolutionImageFilter::Create(
|
|
|
|
SkISize::Make(1<<30, 1<<30),
|
|
|
|
kernel,
|
|
|
|
gain,
|
|
|
|
bias,
|
|
|
|
kernelOffset,
|
|
|
|
SkMatrixConvolutionImageFilter::kRepeat_TileMode,
|
|
|
|
false));
|
|
|
|
|
2015-08-27 14:41:13 +00:00
|
|
|
REPORTER_ASSERT(reporter, nullptr == conv.get());
|
2014-09-29 14:57:20 +00:00
|
|
|
|
2015-08-27 14:41:13 +00:00
|
|
|
// Check that a nullptr kernel gives a nullptr filter.
|
2014-09-29 14:57:20 +00:00
|
|
|
conv.reset(SkMatrixConvolutionImageFilter::Create(
|
|
|
|
SkISize::Make(1, 1),
|
2015-08-27 14:41:13 +00:00
|
|
|
nullptr,
|
2014-09-29 14:57:20 +00:00
|
|
|
gain,
|
|
|
|
bias,
|
|
|
|
kernelOffset,
|
|
|
|
SkMatrixConvolutionImageFilter::kRepeat_TileMode,
|
|
|
|
false));
|
|
|
|
|
2015-08-27 14:41:13 +00:00
|
|
|
REPORTER_ASSERT(reporter, nullptr == conv.get());
|
2014-09-29 14:57:20 +00:00
|
|
|
|
2015-08-27 14:41:13 +00:00
|
|
|
// Check that a kernel width < 1 gives a nullptr filter.
|
2014-09-29 14:57:20 +00:00
|
|
|
conv.reset(SkMatrixConvolutionImageFilter::Create(
|
|
|
|
SkISize::Make(0, 1),
|
|
|
|
kernel,
|
|
|
|
gain,
|
|
|
|
bias,
|
|
|
|
kernelOffset,
|
|
|
|
SkMatrixConvolutionImageFilter::kRepeat_TileMode,
|
|
|
|
false));
|
|
|
|
|
2015-08-27 14:41:13 +00:00
|
|
|
REPORTER_ASSERT(reporter, nullptr == conv.get());
|
2014-09-29 14:57:20 +00:00
|
|
|
|
2015-08-27 14:41:13 +00:00
|
|
|
// Check that kernel height < 1 gives a nullptr filter.
|
2014-09-29 14:57:20 +00:00
|
|
|
conv.reset(SkMatrixConvolutionImageFilter::Create(
|
|
|
|
SkISize::Make(1, -1),
|
|
|
|
kernel,
|
|
|
|
gain,
|
|
|
|
bias,
|
|
|
|
kernelOffset,
|
|
|
|
SkMatrixConvolutionImageFilter::kRepeat_TileMode,
|
|
|
|
false));
|
|
|
|
|
2015-08-27 14:41:13 +00:00
|
|
|
REPORTER_ASSERT(reporter, nullptr == conv.get());
|
2014-09-29 14:57:20 +00:00
|
|
|
}
|
|
|
|
|
2015-06-22 16:46:59 +00:00
|
|
|
static void test_xfermode_cropped_input(SkCanvas* canvas, skiatest::Reporter* reporter) {
|
|
|
|
canvas->clear(0);
|
Fix SkXfermodeImageFilter when an input is cropped out.
If one of inputs to SkXfermodeImageFilter draws nothing, either due to
it being cropped out upstream, or within the filter itself, the filter
should still draw the other input, since otherwise the result will be incorrect.
For the GPU path, since we can't detect this case in
canFilterImageGPU() without recursing, we'll just drop to
the generic path if either input is empty, since we can't use the effect in that case anyway.
While we're at it, let's drop to the generic path if the
xfermode can't be expressed as an effect, since the code
here was doing a 2-pass render in that case anyway, which
is equivalent to what the (xfermode == NULL) case was doing
anyway.
R=bsalomon@google.com, sugoi@chromium.org
Review URL: https://codereview.chromium.org/220723007
git-svn-id: http://skia.googlecode.com/svn/trunk@14016 2bbb7eff-a529-9590-31e7-b0007b416f81
2014-04-01 19:15:23 +00:00
|
|
|
|
|
|
|
SkBitmap bitmap;
|
|
|
|
bitmap.allocN32Pixels(1, 1);
|
|
|
|
bitmap.eraseARGB(255, 255, 255, 255);
|
|
|
|
|
|
|
|
SkAutoTUnref<SkColorFilter> green(
|
|
|
|
SkColorFilter::CreateModeFilter(SK_ColorGREEN, SkXfermode::kSrcIn_Mode));
|
2015-10-12 18:10:10 +00:00
|
|
|
SkAutoTUnref<SkImageFilter> greenFilter(SkColorFilterImageFilter::Create(green.get()));
|
Fix SkXfermodeImageFilter when an input is cropped out.
If one of inputs to SkXfermodeImageFilter draws nothing, either due to
it being cropped out upstream, or within the filter itself, the filter
should still draw the other input, since otherwise the result will be incorrect.
For the GPU path, since we can't detect this case in
canFilterImageGPU() without recursing, we'll just drop to
the generic path if either input is empty, since we can't use the effect in that case anyway.
While we're at it, let's drop to the generic path if the
xfermode can't be expressed as an effect, since the code
here was doing a 2-pass render in that case anyway, which
is equivalent to what the (xfermode == NULL) case was doing
anyway.
R=bsalomon@google.com, sugoi@chromium.org
Review URL: https://codereview.chromium.org/220723007
git-svn-id: http://skia.googlecode.com/svn/trunk@14016 2bbb7eff-a529-9590-31e7-b0007b416f81
2014-04-01 19:15:23 +00:00
|
|
|
SkImageFilter::CropRect cropRect(SkRect::MakeEmpty());
|
2015-10-12 18:10:10 +00:00
|
|
|
SkAutoTUnref<SkImageFilter> croppedOut(
|
2015-08-27 14:41:13 +00:00
|
|
|
SkColorFilterImageFilter::Create(green.get(), nullptr, &cropRect));
|
Fix SkXfermodeImageFilter when an input is cropped out.
If one of inputs to SkXfermodeImageFilter draws nothing, either due to
it being cropped out upstream, or within the filter itself, the filter
should still draw the other input, since otherwise the result will be incorrect.
For the GPU path, since we can't detect this case in
canFilterImageGPU() without recursing, we'll just drop to
the generic path if either input is empty, since we can't use the effect in that case anyway.
While we're at it, let's drop to the generic path if the
xfermode can't be expressed as an effect, since the code
here was doing a 2-pass render in that case anyway, which
is equivalent to what the (xfermode == NULL) case was doing
anyway.
R=bsalomon@google.com, sugoi@chromium.org
Review URL: https://codereview.chromium.org/220723007
git-svn-id: http://skia.googlecode.com/svn/trunk@14016 2bbb7eff-a529-9590-31e7-b0007b416f81
2014-04-01 19:15:23 +00:00
|
|
|
|
|
|
|
// Check that an xfermode image filter whose input has been cropped out still draws the other
|
|
|
|
// input. Also check that drawing with both inputs cropped out doesn't cause a GPU warning.
|
|
|
|
SkXfermode* mode = SkXfermode::Create(SkXfermode::kSrcOver_Mode);
|
|
|
|
SkAutoTUnref<SkImageFilter> xfermodeNoFg(
|
|
|
|
SkXfermodeImageFilter::Create(mode, greenFilter, croppedOut));
|
|
|
|
SkAutoTUnref<SkImageFilter> xfermodeNoBg(
|
|
|
|
SkXfermodeImageFilter::Create(mode, croppedOut, greenFilter));
|
|
|
|
SkAutoTUnref<SkImageFilter> xfermodeNoFgNoBg(
|
|
|
|
SkXfermodeImageFilter::Create(mode, croppedOut, croppedOut));
|
|
|
|
|
|
|
|
SkPaint paint;
|
|
|
|
paint.setImageFilter(xfermodeNoFg);
|
2015-06-22 16:46:59 +00:00
|
|
|
canvas->drawSprite(bitmap, 0, 0, &paint);
|
Fix SkXfermodeImageFilter when an input is cropped out.
If one of inputs to SkXfermodeImageFilter draws nothing, either due to
it being cropped out upstream, or within the filter itself, the filter
should still draw the other input, since otherwise the result will be incorrect.
For the GPU path, since we can't detect this case in
canFilterImageGPU() without recursing, we'll just drop to
the generic path if either input is empty, since we can't use the effect in that case anyway.
While we're at it, let's drop to the generic path if the
xfermode can't be expressed as an effect, since the code
here was doing a 2-pass render in that case anyway, which
is equivalent to what the (xfermode == NULL) case was doing
anyway.
R=bsalomon@google.com, sugoi@chromium.org
Review URL: https://codereview.chromium.org/220723007
git-svn-id: http://skia.googlecode.com/svn/trunk@14016 2bbb7eff-a529-9590-31e7-b0007b416f81
2014-04-01 19:15:23 +00:00
|
|
|
|
|
|
|
uint32_t pixel;
|
2015-03-06 15:16:00 +00:00
|
|
|
SkImageInfo info = SkImageInfo::Make(1, 1, kBGRA_8888_SkColorType, kUnpremul_SkAlphaType);
|
2015-06-22 16:46:59 +00:00
|
|
|
canvas->readPixels(info, &pixel, 4, 0, 0);
|
Fix SkXfermodeImageFilter when an input is cropped out.
If one of inputs to SkXfermodeImageFilter draws nothing, either due to
it being cropped out upstream, or within the filter itself, the filter
should still draw the other input, since otherwise the result will be incorrect.
For the GPU path, since we can't detect this case in
canFilterImageGPU() without recursing, we'll just drop to
the generic path if either input is empty, since we can't use the effect in that case anyway.
While we're at it, let's drop to the generic path if the
xfermode can't be expressed as an effect, since the code
here was doing a 2-pass render in that case anyway, which
is equivalent to what the (xfermode == NULL) case was doing
anyway.
R=bsalomon@google.com, sugoi@chromium.org
Review URL: https://codereview.chromium.org/220723007
git-svn-id: http://skia.googlecode.com/svn/trunk@14016 2bbb7eff-a529-9590-31e7-b0007b416f81
2014-04-01 19:15:23 +00:00
|
|
|
REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN);
|
|
|
|
|
|
|
|
paint.setImageFilter(xfermodeNoBg);
|
2015-06-22 16:46:59 +00:00
|
|
|
canvas->drawSprite(bitmap, 0, 0, &paint);
|
|
|
|
canvas->readPixels(info, &pixel, 4, 0, 0);
|
Fix SkXfermodeImageFilter when an input is cropped out.
If one of inputs to SkXfermodeImageFilter draws nothing, either due to
it being cropped out upstream, or within the filter itself, the filter
should still draw the other input, since otherwise the result will be incorrect.
For the GPU path, since we can't detect this case in
canFilterImageGPU() without recursing, we'll just drop to
the generic path if either input is empty, since we can't use the effect in that case anyway.
While we're at it, let's drop to the generic path if the
xfermode can't be expressed as an effect, since the code
here was doing a 2-pass render in that case anyway, which
is equivalent to what the (xfermode == NULL) case was doing
anyway.
R=bsalomon@google.com, sugoi@chromium.org
Review URL: https://codereview.chromium.org/220723007
git-svn-id: http://skia.googlecode.com/svn/trunk@14016 2bbb7eff-a529-9590-31e7-b0007b416f81
2014-04-01 19:15:23 +00:00
|
|
|
REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN);
|
|
|
|
|
|
|
|
paint.setImageFilter(xfermodeNoFgNoBg);
|
2015-06-22 16:46:59 +00:00
|
|
|
canvas->drawSprite(bitmap, 0, 0, &paint);
|
|
|
|
canvas->readPixels(info, &pixel, 4, 0, 0);
|
Fix SkXfermodeImageFilter when an input is cropped out.
If one of inputs to SkXfermodeImageFilter draws nothing, either due to
it being cropped out upstream, or within the filter itself, the filter
should still draw the other input, since otherwise the result will be incorrect.
For the GPU path, since we can't detect this case in
canFilterImageGPU() without recursing, we'll just drop to
the generic path if either input is empty, since we can't use the effect in that case anyway.
While we're at it, let's drop to the generic path if the
xfermode can't be expressed as an effect, since the code
here was doing a 2-pass render in that case anyway, which
is equivalent to what the (xfermode == NULL) case was doing
anyway.
R=bsalomon@google.com, sugoi@chromium.org
Review URL: https://codereview.chromium.org/220723007
git-svn-id: http://skia.googlecode.com/svn/trunk@14016 2bbb7eff-a529-9590-31e7-b0007b416f81
2014-04-01 19:15:23 +00:00
|
|
|
REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN);
|
|
|
|
}
|
|
|
|
|
2014-04-02 19:20:05 +00:00
|
|
|
DEF_TEST(ImageFilterNestedSaveLayer, reporter) {
|
|
|
|
SkBitmap temp;
|
|
|
|
temp.allocN32Pixels(50, 50);
|
2015-06-22 16:46:59 +00:00
|
|
|
SkCanvas canvas(temp);
|
2014-04-02 19:20:05 +00:00
|
|
|
canvas.clear(0x0);
|
|
|
|
|
|
|
|
SkBitmap bitmap;
|
|
|
|
bitmap.allocN32Pixels(10, 10);
|
|
|
|
bitmap.eraseColor(SK_ColorGREEN);
|
|
|
|
|
|
|
|
SkMatrix matrix;
|
|
|
|
matrix.setScale(SkIntToScalar(2), SkIntToScalar(2));
|
|
|
|
matrix.postTranslate(SkIntToScalar(-20), SkIntToScalar(-20));
|
|
|
|
SkAutoTUnref<SkImageFilter> matrixFilter(
|
2015-03-20 13:38:17 +00:00
|
|
|
SkImageFilter::CreateMatrixFilter(matrix, kLow_SkFilterQuality));
|
2014-04-02 19:20:05 +00:00
|
|
|
|
|
|
|
// Test that saveLayer() with a filter nested inside another saveLayer() applies the
|
|
|
|
// correct offset to the filter matrix.
|
|
|
|
SkRect bounds1 = SkRect::MakeXYWH(10, 10, 30, 30);
|
2015-08-27 14:41:13 +00:00
|
|
|
canvas.saveLayer(&bounds1, nullptr);
|
2014-04-02 19:20:05 +00:00
|
|
|
SkPaint filterPaint;
|
|
|
|
filterPaint.setImageFilter(matrixFilter);
|
|
|
|
SkRect bounds2 = SkRect::MakeXYWH(20, 20, 10, 10);
|
|
|
|
canvas.saveLayer(&bounds2, &filterPaint);
|
|
|
|
SkPaint greenPaint;
|
|
|
|
greenPaint.setColor(SK_ColorGREEN);
|
|
|
|
canvas.drawRect(bounds2, greenPaint);
|
|
|
|
canvas.restore();
|
|
|
|
canvas.restore();
|
|
|
|
SkPaint strokePaint;
|
|
|
|
strokePaint.setStyle(SkPaint::kStroke_Style);
|
|
|
|
strokePaint.setColor(SK_ColorRED);
|
|
|
|
|
2015-03-06 15:16:00 +00:00
|
|
|
SkImageInfo info = SkImageInfo::Make(1, 1, kBGRA_8888_SkColorType, kUnpremul_SkAlphaType);
|
2014-04-02 19:20:05 +00:00
|
|
|
uint32_t pixel;
|
|
|
|
canvas.readPixels(info, &pixel, 4, 25, 25);
|
|
|
|
REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN);
|
|
|
|
|
|
|
|
// Test that drawSprite() with a filter nested inside a saveLayer() applies the
|
|
|
|
// correct offset to the filter matrix.
|
|
|
|
canvas.clear(0x0);
|
|
|
|
canvas.readPixels(info, &pixel, 4, 25, 25);
|
2015-08-27 14:41:13 +00:00
|
|
|
canvas.saveLayer(&bounds1, nullptr);
|
2014-04-02 19:20:05 +00:00
|
|
|
canvas.drawSprite(bitmap, 20, 20, &filterPaint);
|
|
|
|
canvas.restore();
|
|
|
|
|
|
|
|
canvas.readPixels(info, &pixel, 4, 25, 25);
|
|
|
|
REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN);
|
|
|
|
}
|
|
|
|
|
Fix SkXfermodeImageFilter when an input is cropped out.
If one of inputs to SkXfermodeImageFilter draws nothing, either due to
it being cropped out upstream, or within the filter itself, the filter
should still draw the other input, since otherwise the result will be incorrect.
For the GPU path, since we can't detect this case in
canFilterImageGPU() without recursing, we'll just drop to
the generic path if either input is empty, since we can't use the effect in that case anyway.
While we're at it, let's drop to the generic path if the
xfermode can't be expressed as an effect, since the code
here was doing a 2-pass render in that case anyway, which
is equivalent to what the (xfermode == NULL) case was doing
anyway.
R=bsalomon@google.com, sugoi@chromium.org
Review URL: https://codereview.chromium.org/220723007
git-svn-id: http://skia.googlecode.com/svn/trunk@14016 2bbb7eff-a529-9590-31e7-b0007b416f81
2014-04-01 19:15:23 +00:00
|
|
|
DEF_TEST(XfermodeImageFilterCroppedInput, reporter) {
|
|
|
|
SkBitmap temp;
|
|
|
|
temp.allocN32Pixels(100, 100);
|
2015-06-22 16:46:59 +00:00
|
|
|
SkCanvas canvas(temp);
|
|
|
|
test_xfermode_cropped_input(&canvas, reporter);
|
Fix SkXfermodeImageFilter when an input is cropped out.
If one of inputs to SkXfermodeImageFilter draws nothing, either due to
it being cropped out upstream, or within the filter itself, the filter
should still draw the other input, since otherwise the result will be incorrect.
For the GPU path, since we can't detect this case in
canFilterImageGPU() without recursing, we'll just drop to
the generic path if either input is empty, since we can't use the effect in that case anyway.
While we're at it, let's drop to the generic path if the
xfermode can't be expressed as an effect, since the code
here was doing a 2-pass render in that case anyway, which
is equivalent to what the (xfermode == NULL) case was doing
anyway.
R=bsalomon@google.com, sugoi@chromium.org
Review URL: https://codereview.chromium.org/220723007
git-svn-id: http://skia.googlecode.com/svn/trunk@14016 2bbb7eff-a529-9590-31e7-b0007b416f81
2014-04-01 19:15:23 +00:00
|
|
|
}
|
2014-03-24 20:50:59 +00:00
|
|
|
|
2015-02-13 17:05:47 +00:00
|
|
|
DEF_TEST(ComposedImageFilterOffset, reporter) {
|
|
|
|
SkBitmap bitmap;
|
|
|
|
bitmap.allocN32Pixels(100, 100);
|
|
|
|
bitmap.eraseARGB(0, 0, 0, 0);
|
2015-06-22 19:06:08 +00:00
|
|
|
const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
|
|
|
|
SkBitmapDevice device(bitmap, props);
|
2015-10-12 18:30:02 +00:00
|
|
|
SkImageFilter::DeviceProxy proxy(&device);
|
2015-02-13 17:05:47 +00:00
|
|
|
|
|
|
|
SkImageFilter::CropRect cropRect(SkRect::MakeXYWH(1, 0, 20, 20));
|
2015-08-27 14:41:13 +00:00
|
|
|
SkAutoTUnref<SkImageFilter> offsetFilter(SkOffsetImageFilter::Create(0, 0, nullptr, &cropRect));
|
2015-02-13 18:15:46 +00:00
|
|
|
SkAutoTUnref<SkImageFilter> blurFilter(makeBlur());
|
|
|
|
SkAutoTUnref<SkImageFilter> composedFilter(SkComposeImageFilter::Create(blurFilter, offsetFilter.get()));
|
2015-02-13 17:05:47 +00:00
|
|
|
SkBitmap result;
|
|
|
|
SkIPoint offset;
|
2015-11-25 18:32:18 +00:00
|
|
|
SkImageFilter::Context ctx(SkMatrix::I(), SkIRect::MakeWH(100, 100), nullptr, SkImageFilter::kApprox_SizeConstraint);
|
2015-02-13 17:05:47 +00:00
|
|
|
REPORTER_ASSERT(reporter, composedFilter->filterImage(&proxy, bitmap, ctx, &result, &offset));
|
|
|
|
REPORTER_ASSERT(reporter, offset.fX == 1 && offset.fY == 0);
|
|
|
|
}
|
|
|
|
|
2015-07-13 17:27:05 +00:00
|
|
|
DEF_TEST(PartialCropRect, reporter) {
|
|
|
|
SkBitmap bitmap;
|
|
|
|
bitmap.allocN32Pixels(100, 100);
|
|
|
|
bitmap.eraseARGB(0, 0, 0, 0);
|
|
|
|
const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
|
|
|
|
SkBitmapDevice device(bitmap, props);
|
2015-10-12 18:30:02 +00:00
|
|
|
SkImageFilter::DeviceProxy proxy(&device);
|
2015-07-13 17:27:05 +00:00
|
|
|
|
|
|
|
SkImageFilter::CropRect cropRect(SkRect::MakeXYWH(100, 0, 20, 30),
|
2015-07-16 22:19:11 +00:00
|
|
|
SkImageFilter::CropRect::kHasWidth_CropEdge | SkImageFilter::CropRect::kHasHeight_CropEdge);
|
2015-08-27 14:41:13 +00:00
|
|
|
SkAutoTUnref<SkImageFilter> filter(make_grayscale(nullptr, &cropRect));
|
2015-07-13 17:27:05 +00:00
|
|
|
SkBitmap result;
|
|
|
|
SkIPoint offset;
|
2015-11-25 18:32:18 +00:00
|
|
|
SkImageFilter::Context ctx(SkMatrix::I(), SkIRect::MakeWH(100, 100), nullptr, SkImageFilter::kApprox_SizeConstraint);
|
2015-07-13 17:27:05 +00:00
|
|
|
REPORTER_ASSERT(reporter, filter->filterImage(&proxy, bitmap, ctx, &result, &offset));
|
|
|
|
REPORTER_ASSERT(reporter, offset.fX == 0);
|
|
|
|
REPORTER_ASSERT(reporter, offset.fY == 0);
|
|
|
|
REPORTER_ASSERT(reporter, result.width() == 20);
|
|
|
|
REPORTER_ASSERT(reporter, result.height() == 30);
|
|
|
|
}
|
|
|
|
|
2015-08-20 18:10:41 +00:00
|
|
|
DEF_TEST(ImageFilterCanComputeFastBounds, reporter) {
|
|
|
|
|
|
|
|
SkPoint3 location = SkPoint3::Make(0, 0, SK_Scalar1);
|
|
|
|
SkAutoTUnref<SkImageFilter> lighting(SkLightingImageFilter::CreatePointLitDiffuse(
|
|
|
|
location, SK_ColorGREEN, 0, 0));
|
|
|
|
REPORTER_ASSERT(reporter, !lighting->canComputeFastBounds());
|
|
|
|
|
|
|
|
SkAutoTUnref<SkImageFilter> gray(make_grayscale(nullptr, nullptr));
|
|
|
|
REPORTER_ASSERT(reporter, gray->canComputeFastBounds());
|
|
|
|
{
|
|
|
|
SkColorFilter* grayCF;
|
|
|
|
REPORTER_ASSERT(reporter, gray->asAColorFilter(&grayCF));
|
|
|
|
REPORTER_ASSERT(reporter, !grayCF->affectsTransparentBlack());
|
|
|
|
grayCF->unref();
|
|
|
|
}
|
|
|
|
REPORTER_ASSERT(reporter, gray->canComputeFastBounds());
|
|
|
|
|
|
|
|
SkAutoTUnref<SkImageFilter> grayBlur(SkBlurImageFilter::Create(SK_Scalar1, SK_Scalar1, gray.get()));
|
|
|
|
REPORTER_ASSERT(reporter, grayBlur->canComputeFastBounds());
|
|
|
|
|
|
|
|
SkScalar greenMatrix[20] = { 0, 0, 0, 0, 0,
|
|
|
|
0, 0, 0, 0, 1,
|
|
|
|
0, 0, 0, 0, 0,
|
|
|
|
0, 0, 0, 0, 1 };
|
|
|
|
SkAutoTUnref<SkColorFilter> greenCF(SkColorMatrixFilter::Create(greenMatrix));
|
|
|
|
SkAutoTUnref<SkImageFilter> green(SkColorFilterImageFilter::Create(greenCF));
|
|
|
|
|
|
|
|
REPORTER_ASSERT(reporter, greenCF->affectsTransparentBlack());
|
|
|
|
REPORTER_ASSERT(reporter, !green->canComputeFastBounds());
|
|
|
|
|
|
|
|
SkAutoTUnref<SkImageFilter> greenBlur(SkBlurImageFilter::Create(SK_Scalar1, SK_Scalar1, green.get()));
|
|
|
|
REPORTER_ASSERT(reporter, !greenBlur->canComputeFastBounds());
|
|
|
|
|
|
|
|
uint8_t allOne[256], identity[256];
|
|
|
|
for (int i = 0; i < 256; ++i) {
|
|
|
|
identity[i] = i;
|
|
|
|
allOne[i] = 255;
|
|
|
|
}
|
|
|
|
|
|
|
|
SkAutoTUnref<SkColorFilter> identityCF(
|
|
|
|
SkTableColorFilter::CreateARGB(identity, identity, identity, allOne));
|
|
|
|
SkAutoTUnref<SkImageFilter> identityFilter(SkColorFilterImageFilter::Create(identityCF.get()));
|
|
|
|
REPORTER_ASSERT(reporter, !identityCF->affectsTransparentBlack());
|
|
|
|
REPORTER_ASSERT(reporter, identityFilter->canComputeFastBounds());
|
|
|
|
|
|
|
|
SkAutoTUnref<SkColorFilter> forceOpaqueCF(
|
|
|
|
SkTableColorFilter::CreateARGB(allOne, identity, identity, identity));
|
|
|
|
SkAutoTUnref<SkImageFilter> forceOpaque(SkColorFilterImageFilter::Create(forceOpaqueCF.get()));
|
|
|
|
REPORTER_ASSERT(reporter, forceOpaqueCF->affectsTransparentBlack());
|
|
|
|
REPORTER_ASSERT(reporter, !forceOpaque->canComputeFastBounds());
|
|
|
|
}
|
|
|
|
|
2015-09-14 20:31:18 +00:00
|
|
|
// Verify that SkImageSource survives serialization
|
|
|
|
DEF_TEST(ImageFilterImageSourceSerialization, reporter) {
|
|
|
|
SkAutoTUnref<SkSurface> surface(SkSurface::NewRasterN32Premul(10, 10));
|
|
|
|
surface->getCanvas()->clear(SK_ColorGREEN);
|
|
|
|
SkAutoTUnref<SkImage> image(surface->newImageSnapshot());
|
|
|
|
SkAutoTUnref<SkImageFilter> filter(SkImageSource::Create(image));
|
|
|
|
|
|
|
|
SkAutoTUnref<SkData> data(SkValidatingSerializeFlattenable(filter));
|
|
|
|
SkAutoTUnref<SkFlattenable> flattenable(SkValidatingDeserializeFlattenable(
|
|
|
|
data->data(), data->size(), SkImageFilter::GetFlattenableType()));
|
|
|
|
SkImageFilter* unflattenedFilter = static_cast<SkImageFilter*>(flattenable.get());
|
|
|
|
REPORTER_ASSERT(reporter, unflattenedFilter);
|
|
|
|
|
|
|
|
SkBitmap bm;
|
|
|
|
bm.allocN32Pixels(10, 10);
|
2015-09-15 13:56:23 +00:00
|
|
|
bm.eraseColor(SK_ColorBLUE);
|
2015-09-14 20:31:18 +00:00
|
|
|
SkPaint paint;
|
|
|
|
paint.setColor(SK_ColorRED);
|
|
|
|
paint.setImageFilter(unflattenedFilter);
|
|
|
|
|
|
|
|
SkCanvas canvas(bm);
|
|
|
|
canvas.drawRect(SkRect::MakeWH(10, 10), paint);
|
|
|
|
REPORTER_ASSERT(reporter, *bm.getAddr32(0, 0) == SkPreMultiplyColor(SK_ColorGREEN));
|
|
|
|
}
|
|
|
|
|
2014-02-03 22:36:39 +00:00
|
|
|
#if SK_SUPPORT_GPU
|
2014-09-22 14:29:03 +00:00
|
|
|
|
2015-12-01 12:35:26 +00:00
|
|
|
DEF_GPUTEST_FOR_NATIVE_CONTEXT(ImageFilterCropRect_Gpu, reporter, context) {
|
2015-06-22 19:06:08 +00:00
|
|
|
const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
|
|
|
|
|
2014-02-16 00:59:25 +00:00
|
|
|
SkAutoTUnref<SkGpuDevice> device(SkGpuDevice::Create(context,
|
2015-01-16 15:32:33 +00:00
|
|
|
SkSurface::kNo_Budgeted,
|
2014-02-16 00:59:25 +00:00
|
|
|
SkImageInfo::MakeN32Premul(100, 100),
|
2015-01-16 15:32:33 +00:00
|
|
|
0,
|
2015-06-23 21:38:48 +00:00
|
|
|
&props,
|
|
|
|
SkGpuDevice::kUninit_InitContents));
|
2015-10-12 18:30:02 +00:00
|
|
|
SkImageFilter::DeviceProxy proxy(device);
|
2015-06-22 16:46:59 +00:00
|
|
|
|
|
|
|
test_crop_rects(&proxy, reporter);
|
2014-02-03 22:22:16 +00:00
|
|
|
}
|
2014-03-24 20:50:59 +00:00
|
|
|
|
2015-12-01 12:35:26 +00:00
|
|
|
DEF_GPUTEST_FOR_NATIVE_CONTEXT(HugeBlurImageFilter_Gpu, reporter, context) {
|
2015-06-22 19:06:08 +00:00
|
|
|
const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
|
|
|
|
|
2014-03-24 20:50:59 +00:00
|
|
|
SkAutoTUnref<SkGpuDevice> device(SkGpuDevice::Create(context,
|
2015-01-16 15:32:33 +00:00
|
|
|
SkSurface::kNo_Budgeted,
|
2014-03-24 20:50:59 +00:00
|
|
|
SkImageInfo::MakeN32Premul(100, 100),
|
2015-01-16 15:32:33 +00:00
|
|
|
0,
|
2015-06-23 21:38:48 +00:00
|
|
|
&props,
|
|
|
|
SkGpuDevice::kUninit_InitContents));
|
2015-06-22 16:46:59 +00:00
|
|
|
SkCanvas canvas(device);
|
|
|
|
|
|
|
|
test_huge_blur(&canvas, reporter);
|
2014-03-24 20:50:59 +00:00
|
|
|
}
|
Fix SkXfermodeImageFilter when an input is cropped out.
If one of inputs to SkXfermodeImageFilter draws nothing, either due to
it being cropped out upstream, or within the filter itself, the filter
should still draw the other input, since otherwise the result will be incorrect.
For the GPU path, since we can't detect this case in
canFilterImageGPU() without recursing, we'll just drop to
the generic path if either input is empty, since we can't use the effect in that case anyway.
While we're at it, let's drop to the generic path if the
xfermode can't be expressed as an effect, since the code
here was doing a 2-pass render in that case anyway, which
is equivalent to what the (xfermode == NULL) case was doing
anyway.
R=bsalomon@google.com, sugoi@chromium.org
Review URL: https://codereview.chromium.org/220723007
git-svn-id: http://skia.googlecode.com/svn/trunk@14016 2bbb7eff-a529-9590-31e7-b0007b416f81
2014-04-01 19:15:23 +00:00
|
|
|
|
2015-12-01 12:35:26 +00:00
|
|
|
DEF_GPUTEST_FOR_NATIVE_CONTEXT(XfermodeImageFilterCroppedInput_Gpu, reporter, context) {
|
2015-06-22 19:06:08 +00:00
|
|
|
const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
|
|
|
|
|
Fix SkXfermodeImageFilter when an input is cropped out.
If one of inputs to SkXfermodeImageFilter draws nothing, either due to
it being cropped out upstream, or within the filter itself, the filter
should still draw the other input, since otherwise the result will be incorrect.
For the GPU path, since we can't detect this case in
canFilterImageGPU() without recursing, we'll just drop to
the generic path if either input is empty, since we can't use the effect in that case anyway.
While we're at it, let's drop to the generic path if the
xfermode can't be expressed as an effect, since the code
here was doing a 2-pass render in that case anyway, which
is equivalent to what the (xfermode == NULL) case was doing
anyway.
R=bsalomon@google.com, sugoi@chromium.org
Review URL: https://codereview.chromium.org/220723007
git-svn-id: http://skia.googlecode.com/svn/trunk@14016 2bbb7eff-a529-9590-31e7-b0007b416f81
2014-04-01 19:15:23 +00:00
|
|
|
SkAutoTUnref<SkGpuDevice> device(SkGpuDevice::Create(context,
|
2015-01-16 15:32:33 +00:00
|
|
|
SkSurface::kNo_Budgeted,
|
Fix SkXfermodeImageFilter when an input is cropped out.
If one of inputs to SkXfermodeImageFilter draws nothing, either due to
it being cropped out upstream, or within the filter itself, the filter
should still draw the other input, since otherwise the result will be incorrect.
For the GPU path, since we can't detect this case in
canFilterImageGPU() without recursing, we'll just drop to
the generic path if either input is empty, since we can't use the effect in that case anyway.
While we're at it, let's drop to the generic path if the
xfermode can't be expressed as an effect, since the code
here was doing a 2-pass render in that case anyway, which
is equivalent to what the (xfermode == NULL) case was doing
anyway.
R=bsalomon@google.com, sugoi@chromium.org
Review URL: https://codereview.chromium.org/220723007
git-svn-id: http://skia.googlecode.com/svn/trunk@14016 2bbb7eff-a529-9590-31e7-b0007b416f81
2014-04-01 19:15:23 +00:00
|
|
|
SkImageInfo::MakeN32Premul(1, 1),
|
2015-01-16 15:32:33 +00:00
|
|
|
0,
|
2015-06-23 21:38:48 +00:00
|
|
|
&props,
|
|
|
|
SkGpuDevice::kUninit_InitContents));
|
2015-06-22 16:46:59 +00:00
|
|
|
SkCanvas canvas(device);
|
|
|
|
|
|
|
|
test_xfermode_cropped_input(&canvas, reporter);
|
Fix SkXfermodeImageFilter when an input is cropped out.
If one of inputs to SkXfermodeImageFilter draws nothing, either due to
it being cropped out upstream, or within the filter itself, the filter
should still draw the other input, since otherwise the result will be incorrect.
For the GPU path, since we can't detect this case in
canFilterImageGPU() without recursing, we'll just drop to
the generic path if either input is empty, since we can't use the effect in that case anyway.
While we're at it, let's drop to the generic path if the
xfermode can't be expressed as an effect, since the code
here was doing a 2-pass render in that case anyway, which
is equivalent to what the (xfermode == NULL) case was doing
anyway.
R=bsalomon@google.com, sugoi@chromium.org
Review URL: https://codereview.chromium.org/220723007
git-svn-id: http://skia.googlecode.com/svn/trunk@14016 2bbb7eff-a529-9590-31e7-b0007b416f81
2014-04-01 19:15:23 +00:00
|
|
|
}
|
2014-09-09 16:15:04 +00:00
|
|
|
|
2015-12-01 12:35:26 +00:00
|
|
|
DEF_GPUTEST_FOR_NATIVE_CONTEXT(TestNegativeBlurSigma_Gpu, reporter, context) {
|
2015-06-22 19:06:08 +00:00
|
|
|
const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
|
|
|
|
|
2014-09-09 16:15:04 +00:00
|
|
|
SkAutoTUnref<SkGpuDevice> device(SkGpuDevice::Create(context,
|
2015-01-16 15:32:33 +00:00
|
|
|
SkSurface::kNo_Budgeted,
|
2014-09-09 16:15:04 +00:00
|
|
|
SkImageInfo::MakeN32Premul(1, 1),
|
2015-01-16 15:32:33 +00:00
|
|
|
0,
|
2015-06-23 21:38:48 +00:00
|
|
|
&props,
|
|
|
|
SkGpuDevice::kUninit_InitContents));
|
2015-10-12 18:30:02 +00:00
|
|
|
SkImageFilter::DeviceProxy proxy(device);
|
2015-06-22 16:46:59 +00:00
|
|
|
|
|
|
|
test_negative_blur_sigma(&proxy, reporter);
|
2014-09-09 16:15:04 +00:00
|
|
|
}
|
2014-02-03 22:36:39 +00:00
|
|
|
#endif
|