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.
|
|
|
|
*/
|
|
|
|
|
2019-04-23 17:05:21 +00:00
|
|
|
#include "include/core/SkBitmap.h"
|
|
|
|
#include "include/core/SkCanvas.h"
|
|
|
|
#include "include/core/SkImage.h"
|
|
|
|
#include "include/core/SkPicture.h"
|
|
|
|
#include "include/core/SkPictureRecorder.h"
|
|
|
|
#include "include/core/SkPoint3.h"
|
|
|
|
#include "include/core/SkRect.h"
|
|
|
|
#include "include/core/SkSurface.h"
|
|
|
|
#include "include/effects/SkColorMatrixFilter.h"
|
|
|
|
#include "include/effects/SkGradientShader.h"
|
2019-08-05 14:41:10 +00:00
|
|
|
#include "include/effects/SkImageFilters.h"
|
2019-04-23 17:05:21 +00:00
|
|
|
#include "include/effects/SkPerlinNoiseShader.h"
|
|
|
|
#include "include/effects/SkTableColorFilter.h"
|
2020-07-06 14:56:46 +00:00
|
|
|
#include "include/gpu/GrDirectContext.h"
|
2020-06-24 20:56:33 +00:00
|
|
|
#include "src/core/SkColorFilterBase.h"
|
2019-08-02 13:57:04 +00:00
|
|
|
#include "src/core/SkImageFilter_Base.h"
|
2019-04-23 17:05:21 +00:00
|
|
|
#include "src/core/SkReadBuffer.h"
|
|
|
|
#include "src/core/SkSpecialImage.h"
|
|
|
|
#include "src/core/SkSpecialSurface.h"
|
2020-07-06 14:56:46 +00:00
|
|
|
#include "src/gpu/GrCaps.h"
|
|
|
|
#include "src/gpu/GrRecordingContextPriv.h"
|
2019-04-23 17:05:21 +00:00
|
|
|
#include "tests/Test.h"
|
|
|
|
#include "tools/Resources.h"
|
|
|
|
#include "tools/ToolUtils.h"
|
2013-07-24 22:19:24 +00:00
|
|
|
|
2013-12-18 22:15:12 +00:00
|
|
|
static const int kBitmapSize = 4;
|
|
|
|
|
2014-02-05 22:36:31 +00:00
|
|
|
namespace {
|
|
|
|
|
2019-08-02 13:57:04 +00:00
|
|
|
class MatrixTestImageFilter : public SkImageFilter_Base {
|
2014-02-05 22:36:31 +00:00
|
|
|
public:
|
2016-04-04 12:05:11 +00:00
|
|
|
static sk_sp<SkImageFilter> Make(skiatest::Reporter* reporter,
|
|
|
|
const SkMatrix& expectedMatrix) {
|
|
|
|
return sk_sp<SkImageFilter>(new MatrixTestImageFilter(reporter, expectedMatrix));
|
2014-02-05 22:36:31 +00:00
|
|
|
}
|
|
|
|
|
2016-04-04 12:05:11 +00:00
|
|
|
protected:
|
2019-08-14 18:35:42 +00:00
|
|
|
sk_sp<SkSpecialImage> onFilterImage(const Context& ctx, SkIPoint* offset) const override {
|
2014-03-14 15:44:01 +00:00
|
|
|
REPORTER_ASSERT(fReporter, ctx.ctm() == fExpectedMatrix);
|
2016-04-04 19:07:47 +00:00
|
|
|
offset->fX = offset->fY = 0;
|
2019-08-14 18:35:42 +00:00
|
|
|
return sk_ref_sp<SkSpecialImage>(ctx.sourceImage());
|
2014-02-05 22:36:31 +00:00
|
|
|
}
|
|
|
|
|
2015-03-26 01:17:31 +00:00
|
|
|
void flatten(SkWriteBuffer& buffer) const override {
|
2016-05-02 14:51:08 +00:00
|
|
|
SkDEBUGFAIL("Should never get here");
|
2014-02-05 22:36:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
2018-10-18 21:27:16 +00:00
|
|
|
SK_FLATTENABLE_HOOKS(MatrixTestImageFilter)
|
|
|
|
|
2016-04-04 12:05:11 +00:00
|
|
|
MatrixTestImageFilter(skiatest::Reporter* reporter, const SkMatrix& expectedMatrix)
|
|
|
|
: INHERITED(nullptr, 0, nullptr)
|
|
|
|
, fReporter(reporter)
|
|
|
|
, fExpectedMatrix(expectedMatrix) {
|
|
|
|
}
|
|
|
|
|
2014-02-05 22:36:31 +00:00
|
|
|
skiatest::Reporter* fReporter;
|
|
|
|
SkMatrix fExpectedMatrix;
|
2014-12-01 19:47:08 +00:00
|
|
|
|
2020-09-03 02:42:33 +00:00
|
|
|
using INHERITED = SkImageFilter_Base;
|
2014-02-05 22:36:31 +00:00
|
|
|
};
|
|
|
|
|
2019-08-02 13:57:04 +00:00
|
|
|
class FailImageFilter : public SkImageFilter_Base {
|
2016-04-05 11:43:45 +00:00
|
|
|
public:
|
2019-08-02 13:57:04 +00:00
|
|
|
FailImageFilter() : INHERITED(nullptr, 0, nullptr) { }
|
2016-04-05 11:43:45 +00:00
|
|
|
|
2019-08-14 18:35:42 +00:00
|
|
|
sk_sp<SkSpecialImage> onFilterImage(const Context& ctx, SkIPoint* offset) const override {
|
2016-04-05 11:43:45 +00:00
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2018-10-18 21:27:16 +00:00
|
|
|
SK_FLATTENABLE_HOOKS(FailImageFilter)
|
2016-04-05 11:43:45 +00:00
|
|
|
|
|
|
|
private:
|
2020-09-03 02:42:33 +00:00
|
|
|
using INHERITED = SkImageFilter_Base;
|
2016-04-05 11:43:45 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
sk_sp<SkFlattenable> FailImageFilter::CreateProc(SkReadBuffer& buffer) {
|
|
|
|
SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 0);
|
|
|
|
return sk_sp<SkFlattenable>(new FailImageFilter());
|
|
|
|
}
|
|
|
|
|
2016-03-23 20:44:26 +00:00
|
|
|
void draw_gradient_circle(SkCanvas* canvas, int width, int height) {
|
|
|
|
SkScalar x = SkIntToScalar(width / 2);
|
|
|
|
SkScalar y = SkIntToScalar(height / 2);
|
2020-02-05 18:34:09 +00:00
|
|
|
SkScalar radius = std::min(x, y) * 0.8f;
|
2016-03-23 20:44:26 +00:00
|
|
|
canvas->clear(0x00000000);
|
|
|
|
SkColor colors[2];
|
|
|
|
colors[0] = SK_ColorWHITE;
|
|
|
|
colors[1] = SK_ColorBLACK;
|
|
|
|
sk_sp<SkShader> shader(
|
|
|
|
SkGradientShader::MakeRadial(SkPoint::Make(x, y), radius, colors, nullptr, 2,
|
2019-04-03 14:27:45 +00:00
|
|
|
SkTileMode::kClamp)
|
2016-03-23 20:44:26 +00:00
|
|
|
);
|
|
|
|
SkPaint paint;
|
|
|
|
paint.setShader(shader);
|
|
|
|
canvas->drawCircle(x, y, radius, paint);
|
|
|
|
}
|
|
|
|
|
|
|
|
SkBitmap make_gradient_circle(int width, int height) {
|
|
|
|
SkBitmap bitmap;
|
|
|
|
bitmap.allocN32Pixels(width, height);
|
|
|
|
SkCanvas canvas(bitmap);
|
|
|
|
draw_gradient_circle(&canvas, width, height);
|
|
|
|
return bitmap;
|
|
|
|
}
|
|
|
|
|
|
|
|
class FilterList {
|
|
|
|
public:
|
2019-08-05 14:41:10 +00:00
|
|
|
FilterList(sk_sp<SkImageFilter> input, const SkIRect* cropRect = nullptr) {
|
|
|
|
static const SkScalar kBlurSigma = SkIntToScalar(5);
|
|
|
|
|
2016-03-23 20:44:26 +00:00
|
|
|
SkPoint3 location = SkPoint3::Make(0, 0, SK_Scalar1);
|
2016-04-04 11:31:25 +00:00
|
|
|
{
|
2019-08-05 14:41:10 +00:00
|
|
|
sk_sp<SkColorFilter> cf(SkColorFilters::Blend(SK_ColorRED, SkBlendMode::kSrcIn));
|
2016-03-23 20:44:26 +00:00
|
|
|
|
2016-04-04 11:31:25 +00:00
|
|
|
this->addFilter("color filter",
|
2019-08-05 14:41:10 +00:00
|
|
|
SkImageFilters::ColorFilter(std::move(cf), input, cropRect));
|
2016-04-04 11:31:25 +00:00
|
|
|
}
|
|
|
|
{
|
2020-12-23 16:50:36 +00:00
|
|
|
sk_sp<SkImage> gradientImage(make_gradient_circle(64, 64).asImage());
|
2019-08-05 14:41:10 +00:00
|
|
|
sk_sp<SkImageFilter> gradientSource(SkImageFilters::Image(std::move(gradientImage)));
|
2016-04-04 11:31:25 +00:00
|
|
|
|
2016-11-14 19:17:16 +00:00
|
|
|
this->addFilter("displacement map",
|
2019-08-05 14:41:10 +00:00
|
|
|
SkImageFilters::DisplacementMap(SkColorChannel::kR, SkColorChannel::kB, 20.0f,
|
|
|
|
std::move(gradientSource), input, cropRect));
|
2016-04-04 11:31:25 +00:00
|
|
|
}
|
2019-08-05 14:41:10 +00:00
|
|
|
this->addFilter("blur", SkImageFilters::Blur(SK_Scalar1, SK_Scalar1, input, cropRect));
|
|
|
|
this->addFilter("drop shadow", SkImageFilters::DropShadow(
|
|
|
|
SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_ColorGREEN, input, cropRect));
|
2016-04-08 23:28:09 +00:00
|
|
|
this->addFilter("diffuse lighting",
|
2019-08-05 14:41:10 +00:00
|
|
|
SkImageFilters::PointLitDiffuse(location, SK_ColorGREEN, 0, 0, input, cropRect));
|
2016-03-23 20:44:26 +00:00
|
|
|
this->addFilter("specular lighting",
|
2019-08-05 14:41:10 +00:00
|
|
|
SkImageFilters::PointLitSpecular(location, SK_ColorGREEN, 0, 0, 0, input,
|
|
|
|
cropRect));
|
2016-04-08 23:28:09 +00:00
|
|
|
{
|
|
|
|
SkScalar kernel[9] = {
|
|
|
|
SkIntToScalar(1), SkIntToScalar(1), SkIntToScalar(1),
|
|
|
|
SkIntToScalar(1), SkIntToScalar(-7), SkIntToScalar(1),
|
|
|
|
SkIntToScalar(1), SkIntToScalar(1), SkIntToScalar(1),
|
|
|
|
};
|
|
|
|
const SkISize kernelSize = SkISize::Make(3, 3);
|
|
|
|
const SkScalar gain = SK_Scalar1, bias = 0;
|
|
|
|
|
2018-05-17 15:17:39 +00:00
|
|
|
// This filter needs a saveLayer bc it is in repeat mode
|
2016-04-08 23:28:09 +00:00
|
|
|
this->addFilter("matrix convolution",
|
2019-08-05 14:41:10 +00:00
|
|
|
SkImageFilters::MatrixConvolution(
|
|
|
|
kernelSize, kernel, gain, bias, SkIPoint::Make(1, 1),
|
|
|
|
SkTileMode::kRepeat, false, input, cropRect),
|
2018-05-17 15:17:39 +00:00
|
|
|
true);
|
2016-04-08 23:28:09 +00:00
|
|
|
}
|
2019-08-05 14:41:10 +00:00
|
|
|
this->addFilter("merge", SkImageFilters::Merge(input, input, cropRect));
|
2016-04-08 23:28:09 +00:00
|
|
|
|
2016-04-04 11:31:25 +00:00
|
|
|
{
|
Add SkImageFilters::Shader in place of Paint factory
SkImageFilters::Paint did not use every slot of the SkPaint, with only
its color, alpha, color filter, and shader having a meaningful effect on
the image filter result. It was always blended into a transparent dst,
so blend mode wasn't very relevant, and it was always filled to whatever
required geometry, so stroke style, path effect, and mask filters were
ignored or not well specified.
Color, alpha, and color filter can all be combined into an SkShader, so
a more constrained SkImageFilters::Shader provides the same useful
capabilities without as many surprises.
SkImageFilters::Paint still exists, but is deprecated to be removed
once I've confirmed clients aren't depending on it.
Bug: skia:9310
Change-Id: I11a82bda1a5d440726cf4e2b5bfaae4929568679
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/323680
Reviewed-by: Kevin Lubick <kjlubick@google.com>
Commit-Queue: Michael Ludwig <michaelludwig@google.com>
2020-10-07 19:27:20 +00:00
|
|
|
sk_sp<SkShader> greenColorShader = SkShaders::Color(SK_ColorGREEN);
|
2016-04-04 11:31:25 +00:00
|
|
|
|
2019-08-05 14:41:10 +00:00
|
|
|
SkIRect leftSideCropRect = SkIRect::MakeXYWH(0, 0, 32, 64);
|
Add SkImageFilters::Shader in place of Paint factory
SkImageFilters::Paint did not use every slot of the SkPaint, with only
its color, alpha, color filter, and shader having a meaningful effect on
the image filter result. It was always blended into a transparent dst,
so blend mode wasn't very relevant, and it was always filled to whatever
required geometry, so stroke style, path effect, and mask filters were
ignored or not well specified.
Color, alpha, and color filter can all be combined into an SkShader, so
a more constrained SkImageFilters::Shader provides the same useful
capabilities without as many surprises.
SkImageFilters::Paint still exists, but is deprecated to be removed
once I've confirmed clients aren't depending on it.
Bug: skia:9310
Change-Id: I11a82bda1a5d440726cf4e2b5bfaae4929568679
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/323680
Reviewed-by: Kevin Lubick <kjlubick@google.com>
Commit-Queue: Michael Ludwig <michaelludwig@google.com>
2020-10-07 19:27:20 +00:00
|
|
|
sk_sp<SkImageFilter> shaderFilterLeft(SkImageFilters::Shader(greenColorShader,
|
|
|
|
&leftSideCropRect));
|
2019-08-05 14:41:10 +00:00
|
|
|
SkIRect rightSideCropRect = SkIRect::MakeXYWH(32, 0, 32, 64);
|
Add SkImageFilters::Shader in place of Paint factory
SkImageFilters::Paint did not use every slot of the SkPaint, with only
its color, alpha, color filter, and shader having a meaningful effect on
the image filter result. It was always blended into a transparent dst,
so blend mode wasn't very relevant, and it was always filled to whatever
required geometry, so stroke style, path effect, and mask filters were
ignored or not well specified.
Color, alpha, and color filter can all be combined into an SkShader, so
a more constrained SkImageFilters::Shader provides the same useful
capabilities without as many surprises.
SkImageFilters::Paint still exists, but is deprecated to be removed
once I've confirmed clients aren't depending on it.
Bug: skia:9310
Change-Id: I11a82bda1a5d440726cf4e2b5bfaae4929568679
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/323680
Reviewed-by: Kevin Lubick <kjlubick@google.com>
Commit-Queue: Michael Ludwig <michaelludwig@google.com>
2020-10-07 19:27:20 +00:00
|
|
|
sk_sp<SkImageFilter> shaderFilterRight(SkImageFilters::Shader(greenColorShader,
|
|
|
|
&rightSideCropRect));
|
2016-04-04 11:31:25 +00:00
|
|
|
|
|
|
|
|
2019-08-05 14:41:10 +00:00
|
|
|
this->addFilter("merge with disjoint inputs", SkImageFilters::Merge(
|
Add SkImageFilters::Shader in place of Paint factory
SkImageFilters::Paint did not use every slot of the SkPaint, with only
its color, alpha, color filter, and shader having a meaningful effect on
the image filter result. It was always blended into a transparent dst,
so blend mode wasn't very relevant, and it was always filled to whatever
required geometry, so stroke style, path effect, and mask filters were
ignored or not well specified.
Color, alpha, and color filter can all be combined into an SkShader, so
a more constrained SkImageFilters::Shader provides the same useful
capabilities without as many surprises.
SkImageFilters::Paint still exists, but is deprecated to be removed
once I've confirmed clients aren't depending on it.
Bug: skia:9310
Change-Id: I11a82bda1a5d440726cf4e2b5bfaae4929568679
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/323680
Reviewed-by: Kevin Lubick <kjlubick@google.com>
Commit-Queue: Michael Ludwig <michaelludwig@google.com>
2020-10-07 19:27:20 +00:00
|
|
|
std::move(shaderFilterLeft), std::move(shaderFilterRight), cropRect));
|
2016-04-04 11:31:25 +00:00
|
|
|
}
|
|
|
|
|
2019-08-05 14:41:10 +00:00
|
|
|
this->addFilter("offset", SkImageFilters::Offset(SK_Scalar1, SK_Scalar1, input, cropRect));
|
|
|
|
this->addFilter("dilate", SkImageFilters::Dilate(3, 2, input, cropRect));
|
|
|
|
this->addFilter("erode", SkImageFilters::Erode(2, 3, input, cropRect));
|
|
|
|
this->addFilter("tile", SkImageFilters::Tile(SkRect::MakeXYWH(0, 0, 50, 50),
|
|
|
|
cropRect ? SkRect::Make(*cropRect)
|
|
|
|
: SkRect::MakeXYWH(0, 0, 100, 100),
|
|
|
|
input));
|
2016-04-08 23:28:09 +00:00
|
|
|
|
2016-03-23 20:44:26 +00:00
|
|
|
if (!cropRect) {
|
2016-04-08 23:28:09 +00:00
|
|
|
SkMatrix matrix;
|
|
|
|
|
|
|
|
matrix.setTranslate(SK_Scalar1, SK_Scalar1);
|
|
|
|
matrix.postRotate(SkIntToScalar(45), SK_Scalar1, SK_Scalar1);
|
|
|
|
|
2016-04-05 22:09:00 +00:00
|
|
|
this->addFilter("matrix",
|
2021-02-01 18:07:32 +00:00
|
|
|
SkImageFilters::MatrixTransform(matrix,
|
|
|
|
SkSamplingOptions(SkFilterMode::kLinear),
|
|
|
|
input));
|
2016-03-23 20:44:26 +00:00
|
|
|
}
|
2016-04-04 11:31:25 +00:00
|
|
|
{
|
2019-08-05 14:41:10 +00:00
|
|
|
sk_sp<SkImageFilter> blur(SkImageFilters::Blur(kBlurSigma, kBlurSigma, input));
|
2016-04-04 11:31:25 +00:00
|
|
|
|
2019-08-05 14:41:10 +00:00
|
|
|
this->addFilter("blur and offset", SkImageFilters::Offset(
|
|
|
|
kBlurSigma, kBlurSigma, std::move(blur), cropRect));
|
2016-04-04 11:31:25 +00:00
|
|
|
}
|
|
|
|
{
|
|
|
|
SkPictureRecorder recorder;
|
2017-04-10 13:50:25 +00:00
|
|
|
SkCanvas* recordingCanvas = recorder.beginRecording(64, 64);
|
2016-04-04 11:31:25 +00:00
|
|
|
|
|
|
|
SkPaint greenPaint;
|
|
|
|
greenPaint.setColor(SK_ColorGREEN);
|
|
|
|
recordingCanvas->drawRect(SkRect::Make(SkIRect::MakeXYWH(10, 10, 30, 20)), greenPaint);
|
|
|
|
sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
|
2019-08-05 14:41:10 +00:00
|
|
|
sk_sp<SkImageFilter> pictureFilter(SkImageFilters::Picture(std::move(picture)));
|
2016-04-04 11:31:25 +00:00
|
|
|
|
2019-08-05 14:41:10 +00:00
|
|
|
this->addFilter("picture and blur", SkImageFilters::Blur(
|
|
|
|
kBlurSigma, kBlurSigma, std::move(pictureFilter), cropRect));
|
2016-04-04 11:31:25 +00:00
|
|
|
}
|
|
|
|
{
|
Add SkImageFilters::Shader in place of Paint factory
SkImageFilters::Paint did not use every slot of the SkPaint, with only
its color, alpha, color filter, and shader having a meaningful effect on
the image filter result. It was always blended into a transparent dst,
so blend mode wasn't very relevant, and it was always filled to whatever
required geometry, so stroke style, path effect, and mask filters were
ignored or not well specified.
Color, alpha, and color filter can all be combined into an SkShader, so
a more constrained SkImageFilters::Shader provides the same useful
capabilities without as many surprises.
SkImageFilters::Paint still exists, but is deprecated to be removed
once I've confirmed clients aren't depending on it.
Bug: skia:9310
Change-Id: I11a82bda1a5d440726cf4e2b5bfaae4929568679
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/323680
Reviewed-by: Kevin Lubick <kjlubick@google.com>
Commit-Queue: Michael Ludwig <michaelludwig@google.com>
2020-10-07 19:27:20 +00:00
|
|
|
sk_sp<SkImageFilter> paintFilter(SkImageFilters::Shader(
|
|
|
|
SkPerlinNoiseShader::MakeTurbulence(SK_Scalar1, SK_Scalar1, 1, 0)));
|
2016-04-04 11:31:25 +00:00
|
|
|
|
2019-08-05 14:41:10 +00:00
|
|
|
this->addFilter("paint and blur", SkImageFilters::Blur(
|
|
|
|
kBlurSigma, kBlurSigma, std::move(paintFilter), cropRect));
|
2016-04-04 11:31:25 +00:00
|
|
|
}
|
2020-10-09 14:45:07 +00:00
|
|
|
this->addFilter("blend", SkImageFilters::Blend(
|
2019-08-05 14:41:10 +00:00
|
|
|
SkBlendMode::kSrc, input, input, cropRect));
|
2016-03-23 20:44:26 +00:00
|
|
|
}
|
|
|
|
int count() const { return fFilters.count(); }
|
|
|
|
SkImageFilter* getFilter(int index) const { return fFilters[index].fFilter.get(); }
|
|
|
|
const char* getName(int index) const { return fFilters[index].fName; }
|
2018-05-17 15:17:39 +00:00
|
|
|
bool needsSaveLayer(int index) const { return fFilters[index].fNeedsSaveLayer; }
|
2016-03-23 20:44:26 +00:00
|
|
|
private:
|
|
|
|
struct Filter {
|
2018-05-17 15:17:39 +00:00
|
|
|
Filter() : fName(nullptr), fNeedsSaveLayer(false) {}
|
|
|
|
Filter(const char* name, sk_sp<SkImageFilter> filter, bool needsSaveLayer)
|
2016-04-08 23:28:09 +00:00
|
|
|
: fName(name)
|
2018-05-17 15:17:39 +00:00
|
|
|
, fFilter(std::move(filter))
|
|
|
|
, fNeedsSaveLayer(needsSaveLayer) {
|
2016-04-08 23:28:09 +00:00
|
|
|
}
|
2016-03-23 20:44:26 +00:00
|
|
|
const char* fName;
|
|
|
|
sk_sp<SkImageFilter> fFilter;
|
2018-05-17 15:17:39 +00:00
|
|
|
bool fNeedsSaveLayer;
|
2016-03-23 20:44:26 +00:00
|
|
|
};
|
2018-05-17 15:17:39 +00:00
|
|
|
void addFilter(const char* name, sk_sp<SkImageFilter> filter, bool needsSaveLayer = false) {
|
|
|
|
fFilters.push_back(Filter(name, std::move(filter), needsSaveLayer));
|
2016-03-23 20:44:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
SkTArray<Filter> fFilters;
|
|
|
|
};
|
|
|
|
|
2019-08-02 13:57:04 +00:00
|
|
|
class FixedBoundsImageFilter : public SkImageFilter_Base {
|
2017-08-25 23:27:04 +00:00
|
|
|
public:
|
|
|
|
FixedBoundsImageFilter(const SkIRect& bounds)
|
2019-08-02 13:57:04 +00:00
|
|
|
: INHERITED(nullptr, 0, nullptr), fBounds(bounds) {}
|
2017-08-25 23:27:04 +00:00
|
|
|
|
|
|
|
private:
|
|
|
|
Factory getFactory() const override { return nullptr; }
|
2018-10-18 21:27:16 +00:00
|
|
|
const char* getTypeName() const override { return nullptr; }
|
2017-08-25 23:27:04 +00:00
|
|
|
|
2019-08-14 18:35:42 +00:00
|
|
|
sk_sp<SkSpecialImage> onFilterImage(const Context&, SkIPoint* offset) const override {
|
2017-08-25 23:27:04 +00:00
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2018-05-17 15:17:39 +00:00
|
|
|
SkIRect onFilterBounds(const SkIRect&, const SkMatrix&,
|
|
|
|
MapDirection, const SkIRect*) const override {
|
2017-08-25 23:27:04 +00:00
|
|
|
return fBounds;
|
|
|
|
}
|
|
|
|
|
|
|
|
SkIRect fBounds;
|
2019-08-02 13:57:04 +00:00
|
|
|
|
2020-09-03 02:42:33 +00:00
|
|
|
using INHERITED = SkImageFilter_Base;
|
2017-08-25 23:27:04 +00:00
|
|
|
};
|
2020-08-06 18:11:56 +00:00
|
|
|
} // namespace
|
2014-02-05 22:36:31 +00:00
|
|
|
|
2016-04-03 16:11:13 +00:00
|
|
|
sk_sp<SkFlattenable> MatrixTestImageFilter::CreateProc(SkReadBuffer& buffer) {
|
2016-05-02 14:51:08 +00:00
|
|
|
SkDEBUGFAIL("Should never get here");
|
|
|
|
return nullptr;
|
2014-08-21 14:59:51 +00:00
|
|
|
}
|
|
|
|
|
2016-03-17 17:51:11 +00:00
|
|
|
static sk_sp<SkImage> make_small_image() {
|
2016-03-24 01:59:25 +00:00
|
|
|
auto surface(SkSurface::MakeRasterN32Premul(kBitmapSize, kBitmapSize));
|
2015-09-15 18:26:13 +00:00
|
|
|
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);
|
2019-08-05 14:41:10 +00:00
|
|
|
const int kRectSize = kBitmapSize / 4;
|
|
|
|
static_assert(kBitmapSize % 4 == 0, "bitmap size not multiple of 4");
|
|
|
|
|
|
|
|
for (int y = 0; y < kBitmapSize; y += kRectSize) {
|
|
|
|
for (int x = 0; x < kBitmapSize; x += kRectSize) {
|
2015-09-15 18:26:13 +00:00
|
|
|
canvas->save();
|
|
|
|
canvas->translate(SkIntToScalar(x), SkIntToScalar(y));
|
2019-08-05 14:41:10 +00:00
|
|
|
canvas->drawRect(
|
|
|
|
SkRect::MakeXYWH(0, 0, kRectSize, kRectSize), darkPaint);
|
|
|
|
canvas->drawRect(
|
|
|
|
SkRect::MakeXYWH(kRectSize, 0, kRectSize, kRectSize), lightPaint);
|
|
|
|
canvas->drawRect(
|
|
|
|
SkRect::MakeXYWH(0, kRectSize, kRectSize, kRectSize), lightPaint);
|
|
|
|
canvas->drawRect(
|
|
|
|
SkRect::MakeXYWH(kRectSize, kRectSize, kRectSize, kRectSize), 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
|
|
|
|
2016-03-17 17:51:11 +00:00
|
|
|
return surface->makeImageSnapshot();
|
2013-12-18 22:15:12 +00:00
|
|
|
}
|
|
|
|
|
2016-04-05 18:50:42 +00:00
|
|
|
static sk_sp<SkImageFilter> make_scale(float amount, sk_sp<SkImageFilter> input) {
|
2019-04-30 16:18:54 +00:00
|
|
|
float s = amount;
|
|
|
|
float matrix[20] = { s, 0, 0, 0, 0,
|
|
|
|
0, s, 0, 0, 0,
|
|
|
|
0, 0, s, 0, 0,
|
|
|
|
0, 0, 0, s, 0 };
|
|
|
|
sk_sp<SkColorFilter> filter(SkColorFilters::Matrix(matrix));
|
2019-08-05 14:41:10 +00:00
|
|
|
return SkImageFilters::ColorFilter(std::move(filter), std::move(input));
|
2013-12-18 22:15:12 +00:00
|
|
|
}
|
|
|
|
|
2016-04-05 18:50:42 +00:00
|
|
|
static sk_sp<SkImageFilter> make_grayscale(sk_sp<SkImageFilter> input,
|
2019-08-05 14:41:10 +00:00
|
|
|
const SkIRect* cropRect) {
|
2019-04-30 16:18:54 +00:00
|
|
|
float matrix[20];
|
|
|
|
memset(matrix, 0, 20 * sizeof(float));
|
2013-12-18 22:15:12 +00:00
|
|
|
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;
|
2019-04-30 16:18:54 +00:00
|
|
|
sk_sp<SkColorFilter> filter(SkColorFilters::Matrix(matrix));
|
2019-08-05 14:41:10 +00:00
|
|
|
return SkImageFilters::ColorFilter(std::move(filter), std::move(input), cropRect);
|
2013-12-18 22:15:12 +00:00
|
|
|
}
|
|
|
|
|
2019-08-05 14:41:10 +00:00
|
|
|
static sk_sp<SkImageFilter> make_blue(sk_sp<SkImageFilter> input, const SkIRect* cropRect) {
|
2019-04-08 20:23:20 +00:00
|
|
|
sk_sp<SkColorFilter> filter(SkColorFilters::Blend(SK_ColorBLUE, SkBlendMode::kSrcIn));
|
2019-08-05 14:41:10 +00:00
|
|
|
return SkImageFilters::ColorFilter(std::move(filter), std::move(input), cropRect);
|
2015-03-08 11:42:52 +00:00
|
|
|
}
|
|
|
|
|
2020-07-21 20:50:06 +00:00
|
|
|
static sk_sp<SkSpecialSurface> create_empty_special_surface(GrRecordingContext* rContext,
|
|
|
|
int widthHeight) {
|
|
|
|
if (rContext) {
|
|
|
|
return SkSpecialSurface::MakeRenderTarget(rContext, widthHeight, widthHeight,
|
2021-04-19 23:27:09 +00:00
|
|
|
GrColorType::kRGBA_8888, nullptr,
|
|
|
|
SkSurfaceProps());
|
2018-05-31 18:27:17 +00:00
|
|
|
} else {
|
2016-03-07 20:45:14 +00:00
|
|
|
const SkImageInfo info = SkImageInfo::MakeN32(widthHeight, widthHeight,
|
|
|
|
kOpaque_SkAlphaType);
|
2021-04-19 23:27:09 +00:00
|
|
|
return SkSpecialSurface::MakeRaster(info, SkSurfaceProps());
|
2016-03-07 20:45:14 +00:00
|
|
|
}
|
2016-03-16 23:15:53 +00:00
|
|
|
}
|
|
|
|
|
2020-07-21 20:50:06 +00:00
|
|
|
static sk_sp<SkSurface> create_surface(GrRecordingContext* rContext, int width, int height) {
|
2016-05-19 21:50:29 +00:00
|
|
|
const SkImageInfo info = SkImageInfo::MakeN32(width, height, kOpaque_SkAlphaType);
|
2020-07-21 20:50:06 +00:00
|
|
|
if (rContext) {
|
|
|
|
return SkSurface::MakeRenderTarget(rContext, SkBudgeted::kNo, info);
|
2018-05-31 18:27:17 +00:00
|
|
|
} else {
|
2016-05-19 21:50:29 +00:00
|
|
|
return SkSurface::MakeRaster(info);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-21 20:50:06 +00:00
|
|
|
static sk_sp<SkSpecialImage> create_empty_special_image(GrRecordingContext* rContext,
|
|
|
|
int widthHeight) {
|
|
|
|
sk_sp<SkSpecialSurface> surf(create_empty_special_surface(rContext, widthHeight));
|
2016-03-07 20:45:14 +00:00
|
|
|
|
|
|
|
SkASSERT(surf);
|
|
|
|
|
|
|
|
SkCanvas* canvas = surf->getCanvas();
|
|
|
|
SkASSERT(canvas);
|
|
|
|
|
|
|
|
canvas->clear(0x0);
|
|
|
|
|
2016-03-17 21:31:39 +00:00
|
|
|
return surf->makeImageSnapshot();
|
2016-03-07 20:45:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-12-18 22:15:12 +00:00
|
|
|
DEF_TEST(ImageFilter, reporter) {
|
|
|
|
{
|
2019-03-12 15:01:28 +00:00
|
|
|
// Check that a color matrix filter followed by a color matrix filter
|
|
|
|
// concatenates into a single filter.
|
2016-04-05 18:50:42 +00:00
|
|
|
sk_sp<SkImageFilter> doubleBrightness(make_scale(2.0f, nullptr));
|
|
|
|
sk_sp<SkImageFilter> halfBrightness(make_scale(0.5f, std::move(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));
|
|
|
|
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.
|
2016-04-05 18:50:42 +00:00
|
|
|
sk_sp<SkImageFilter> gray(make_grayscale(nullptr, nullptr));
|
2015-08-27 14:41:13 +00:00
|
|
|
REPORTER_ASSERT(reporter, true == gray->asColorFilter(nullptr));
|
2015-03-08 11:42:52 +00:00
|
|
|
}
|
2016-02-07 20:23:10 +00:00
|
|
|
|
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).
|
2016-04-05 18:50:42 +00:00
|
|
|
sk_sp<SkImageFilter> mode(make_blue(nullptr, nullptr));
|
|
|
|
sk_sp<SkImageFilter> gray(make_grayscale(std::move(mode), nullptr));
|
2015-08-27 14:41:13 +00:00
|
|
|
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.
|
2016-04-05 18:50:42 +00:00
|
|
|
sk_sp<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) {
|
2016-04-05 18:50:42 +00:00
|
|
|
filter = 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.
|
2019-08-05 14:41:10 +00:00
|
|
|
SkIRect cropRect = SkIRect::MakeWH(100, 100);
|
2016-04-05 18:50:42 +00:00
|
|
|
sk_sp<SkImageFilter> grayWithCrop(make_grayscale(nullptr, &cropRect));
|
2015-08-27 14:41:13 +00:00
|
|
|
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.
|
2019-04-30 16:18:54 +00:00
|
|
|
float blueToRedMatrix[20] = { 0 };
|
|
|
|
blueToRedMatrix[2] = blueToRedMatrix[18] = 1;
|
|
|
|
float redToGreenMatrix[20] = { 0 };
|
|
|
|
redToGreenMatrix[5] = redToGreenMatrix[18] = 1;
|
|
|
|
sk_sp<SkColorFilter> blueToRed(SkColorFilters::Matrix(blueToRedMatrix));
|
2019-08-05 14:41:10 +00:00
|
|
|
sk_sp<SkImageFilter> filter1(SkImageFilters::ColorFilter(std::move(blueToRed), nullptr));
|
2019-04-30 16:18:54 +00:00
|
|
|
sk_sp<SkColorFilter> redToGreen(SkColorFilters::Matrix(redToGreenMatrix));
|
2019-08-05 14:41:10 +00:00
|
|
|
sk_sp<SkImageFilter> filter2(SkImageFilters::ColorFilter(std::move(redToGreen),
|
|
|
|
std::move(filter1)));
|
2014-07-03 18:13:09 +00:00
|
|
|
|
|
|
|
SkBitmap result;
|
|
|
|
result.allocN32Pixels(kBitmapSize, kBitmapSize);
|
|
|
|
|
|
|
|
SkPaint paint;
|
|
|
|
paint.setColor(SK_ColorBLUE);
|
2016-04-05 18:50:42 +00:00
|
|
|
paint.setImageFilter(std::move(filter2));
|
2014-07-03 18:13:09 +00:00
|
|
|
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
|
2016-03-17 17:51:11 +00:00
|
|
|
sk_sp<SkImage> image(make_small_image());
|
2015-09-15 18:26:13 +00:00
|
|
|
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;
|
|
|
|
|
2019-08-05 14:41:10 +00:00
|
|
|
sk_sp<SkImageFilter> bmSrc(SkImageFilters::Image(std::move(image)));
|
2013-12-18 22:15:12 +00:00
|
|
|
SkPaint paint;
|
2019-08-05 14:41:10 +00:00
|
|
|
paint.setImageFilter(SkImageFilters::SpotLitSpecular(
|
2013-12-18 22:15:12 +00:00
|
|
|
location, target, specularExponent, 180,
|
|
|
|
0xFFFFFFFF, SK_Scalar1, SK_Scalar1, SK_Scalar1,
|
2016-04-08 23:28:09 +00:00
|
|
|
std::move(bmSrc)));
|
2013-12-18 22:15:12 +00:00
|
|
|
SkCanvas canvas(result);
|
2019-08-05 14:41:10 +00:00
|
|
|
SkRect r = SkRect::MakeIWH(kBitmapSize, kBitmapSize);
|
2013-12-18 22:15:12 +00:00
|
|
|
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
|
|
|
|
2020-07-21 20:50:06 +00:00
|
|
|
static void test_cropRects(skiatest::Reporter* reporter, GrRecordingContext* rContext) {
|
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.
|
2020-07-21 20:50:06 +00:00
|
|
|
sk_sp<SkSpecialImage> srcImg(create_empty_special_image(rContext, 100));
|
2016-03-07 20:45:14 +00:00
|
|
|
SkASSERT(srcImg);
|
2014-02-03 22:22:16 +00:00
|
|
|
|
2019-08-05 14:41:10 +00:00
|
|
|
SkIRect inputCropRect = SkIRect::MakeXYWH(8, 13, 80, 80);
|
|
|
|
SkIRect cropRect = SkIRect::MakeXYWH(20, 30, 60, 60);
|
2016-04-05 16:09:36 +00:00
|
|
|
sk_sp<SkImageFilter> input(make_grayscale(nullptr, &inputCropRect));
|
2014-02-03 22:22:16 +00:00
|
|
|
|
2016-04-05 16:09:36 +00:00
|
|
|
FilterList filters(input, &cropRect);
|
2014-02-03 22:22:16 +00:00
|
|
|
|
2016-03-23 20:44:26 +00:00
|
|
|
for (int i = 0; i < filters.count(); ++i) {
|
|
|
|
SkImageFilter* filter = filters.getFilter(i);
|
2014-02-03 22:22:16 +00:00
|
|
|
SkIPoint offset;
|
2019-08-02 13:57:04 +00:00
|
|
|
SkImageFilter_Base::Context ctx(SkMatrix::I(), SkIRect::MakeWH(100, 100), nullptr,
|
2019-08-14 18:35:42 +00:00
|
|
|
kN32_SkColorType, nullptr, srcImg.get());
|
2019-08-26 14:52:15 +00:00
|
|
|
sk_sp<SkSpecialImage> resultImg(as_IFB(filter)->filterImage(ctx).imageAndOffset(&offset));
|
2018-01-29 14:50:47 +00:00
|
|
|
REPORTER_ASSERT(reporter, resultImg, filters.getName(i));
|
|
|
|
REPORTER_ASSERT(reporter, offset.fX == 20 && offset.fY == 30, filters.getName(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
|
|
|
|
2020-08-27 16:44:07 +00:00
|
|
|
static bool special_image_to_bitmap(GrDirectContext* dContext, const SkSpecialImage* src,
|
|
|
|
SkBitmap* dst) {
|
2020-06-22 18:51:22 +00:00
|
|
|
sk_sp<SkImage> img = src->asImage();
|
|
|
|
if (!img) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!dst->tryAllocN32Pixels(src->width(), src->height())) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-08-27 16:44:07 +00:00
|
|
|
return img->readPixels(dContext, dst->pixmap(), src->subset().fLeft, src->subset().fTop);
|
2020-06-22 18:51:22 +00:00
|
|
|
}
|
|
|
|
|
2020-07-21 20:50:06 +00:00
|
|
|
static void test_negative_blur_sigma(skiatest::Reporter* reporter,
|
2020-08-27 16:44:07 +00:00
|
|
|
GrDirectContext* dContext) {
|
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.
|
2019-08-05 14:41:10 +00:00
|
|
|
static const int kWidth = 32, kHeight = 32;
|
|
|
|
static const SkScalar kBlurSigma = SkIntToScalar(5);
|
2014-09-09 16:15:04 +00:00
|
|
|
|
2019-08-05 14:41:10 +00:00
|
|
|
sk_sp<SkImageFilter> positiveFilter(SkImageFilters::Blur(kBlurSigma, kBlurSigma, nullptr));
|
|
|
|
sk_sp<SkImageFilter> negativeFilter(SkImageFilters::Blur(-kBlurSigma, kBlurSigma, nullptr));
|
2014-09-09 16:15:04 +00:00
|
|
|
|
2020-12-23 16:50:36 +00:00
|
|
|
sk_sp<SkImage> gradient = make_gradient_circle(kWidth, kHeight).asImage();
|
2020-06-24 13:57:59 +00:00
|
|
|
sk_sp<SkSpecialImage> imgSrc(
|
2021-04-19 23:27:09 +00:00
|
|
|
SkSpecialImage::MakeFromImage(dContext, SkIRect::MakeWH(kWidth, kHeight), gradient,
|
|
|
|
SkSurfaceProps()));
|
2016-03-07 20:45:14 +00:00
|
|
|
|
2014-09-09 16:15:04 +00:00
|
|
|
SkIPoint offset;
|
2019-08-14 18:35:15 +00:00
|
|
|
SkImageFilter_Base::Context ctx(SkMatrix::I(), SkIRect::MakeWH(32, 32), nullptr,
|
2019-08-14 18:35:42 +00:00
|
|
|
kN32_SkColorType, nullptr, imgSrc.get());
|
2016-03-07 20:45:14 +00:00
|
|
|
|
2019-08-02 13:57:04 +00:00
|
|
|
sk_sp<SkSpecialImage> positiveResult1(
|
2019-08-26 14:52:15 +00:00
|
|
|
as_IFB(positiveFilter)->filterImage(ctx).imageAndOffset(&offset));
|
2016-03-07 20:45:14 +00:00
|
|
|
REPORTER_ASSERT(reporter, positiveResult1);
|
|
|
|
|
2019-08-02 13:57:04 +00:00
|
|
|
sk_sp<SkSpecialImage> negativeResult1(
|
2019-08-26 14:52:15 +00:00
|
|
|
as_IFB(negativeFilter)->filterImage(ctx).imageAndOffset(&offset));
|
2016-03-07 20:45:14 +00:00
|
|
|
REPORTER_ASSERT(reporter, negativeResult1);
|
|
|
|
|
2014-09-09 16:15:04 +00:00
|
|
|
SkMatrix negativeScale;
|
|
|
|
negativeScale.setScale(-SK_Scalar1, SK_Scalar1);
|
2019-08-02 13:57:04 +00:00
|
|
|
SkImageFilter_Base::Context negativeCTX(negativeScale, SkIRect::MakeWH(32, 32), nullptr,
|
2019-08-14 18:35:42 +00:00
|
|
|
kN32_SkColorType, nullptr, imgSrc.get());
|
2016-03-07 20:45:14 +00:00
|
|
|
|
2019-08-02 13:57:04 +00:00
|
|
|
sk_sp<SkSpecialImage> negativeResult2(
|
2019-08-26 14:52:15 +00:00
|
|
|
as_IFB(positiveFilter)->filterImage(negativeCTX).imageAndOffset(&offset));
|
2016-03-07 20:45:14 +00:00
|
|
|
REPORTER_ASSERT(reporter, negativeResult2);
|
|
|
|
|
2019-08-02 13:57:04 +00:00
|
|
|
sk_sp<SkSpecialImage> positiveResult2(
|
2019-08-26 14:52:15 +00:00
|
|
|
as_IFB(negativeFilter)->filterImage(negativeCTX).imageAndOffset(&offset));
|
2016-03-07 20:45:14 +00:00
|
|
|
REPORTER_ASSERT(reporter, positiveResult2);
|
|
|
|
|
|
|
|
|
|
|
|
SkBitmap positiveResultBM1, positiveResultBM2;
|
|
|
|
SkBitmap negativeResultBM1, negativeResultBM2;
|
|
|
|
|
2020-08-27 16:44:07 +00:00
|
|
|
REPORTER_ASSERT(reporter, special_image_to_bitmap(dContext, positiveResult1.get(),
|
|
|
|
&positiveResultBM1));
|
|
|
|
REPORTER_ASSERT(reporter, special_image_to_bitmap(dContext, positiveResult2.get(),
|
|
|
|
&positiveResultBM2));
|
|
|
|
REPORTER_ASSERT(reporter, special_image_to_bitmap(dContext, negativeResult1.get(),
|
|
|
|
&negativeResultBM1));
|
|
|
|
REPORTER_ASSERT(reporter, special_image_to_bitmap(dContext, negativeResult2.get(),
|
|
|
|
&negativeResultBM2));
|
2016-03-07 20:45:14 +00:00
|
|
|
|
2019-08-05 14:41:10 +00:00
|
|
|
for (int y = 0; y < kHeight; y++) {
|
2016-03-07 20:45:14 +00:00
|
|
|
int diffs = memcmp(positiveResultBM1.getAddr32(0, y),
|
|
|
|
negativeResultBM1.getAddr32(0, y),
|
|
|
|
positiveResultBM1.rowBytes());
|
2014-09-09 16:15:04 +00:00
|
|
|
REPORTER_ASSERT(reporter, !diffs);
|
|
|
|
if (diffs) {
|
|
|
|
break;
|
|
|
|
}
|
2016-03-07 20:45:14 +00:00
|
|
|
diffs = memcmp(positiveResultBM1.getAddr32(0, y),
|
|
|
|
negativeResultBM2.getAddr32(0, y),
|
|
|
|
positiveResultBM1.rowBytes());
|
2014-09-09 16:15:04 +00:00
|
|
|
REPORTER_ASSERT(reporter, !diffs);
|
|
|
|
if (diffs) {
|
|
|
|
break;
|
|
|
|
}
|
2016-03-07 20:45:14 +00:00
|
|
|
diffs = memcmp(positiveResultBM1.getAddr32(0, y),
|
|
|
|
positiveResultBM2.getAddr32(0, y),
|
|
|
|
positiveResultBM1.rowBytes());
|
2014-09-09 16:15:04 +00:00
|
|
|
REPORTER_ASSERT(reporter, !diffs);
|
|
|
|
if (diffs) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-11 18:58:39 +00:00
|
|
|
DEF_TEST(ImageFilterNegativeBlurSigma, reporter) {
|
2016-04-20 18:48:36 +00:00
|
|
|
test_negative_blur_sigma(reporter, nullptr);
|
2016-03-07 20:45:14 +00:00
|
|
|
}
|
|
|
|
|
2016-04-12 16:59:58 +00:00
|
|
|
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ImageFilterNegativeBlurSigma_Gpu, reporter, ctxInfo) {
|
2020-07-06 14:56:46 +00:00
|
|
|
test_negative_blur_sigma(reporter, ctxInfo.directContext());
|
2014-09-09 16:15:04 +00:00
|
|
|
}
|
|
|
|
|
2020-07-22 13:35:49 +00:00
|
|
|
static void test_morphology_radius_with_mirror_ctm(skiatest::Reporter* reporter,
|
2020-08-27 16:44:07 +00:00
|
|
|
GrDirectContext* dContext) {
|
2019-10-08 12:21:14 +00:00
|
|
|
// Check that SkMorphologyImageFilter maps the radius correctly when the
|
|
|
|
// CTM contains a mirroring transform.
|
|
|
|
static const int kWidth = 32, kHeight = 32;
|
|
|
|
static const int kRadius = 8;
|
|
|
|
|
|
|
|
sk_sp<SkImageFilter> filter(SkImageFilters::Dilate(kRadius, kRadius, nullptr));
|
|
|
|
|
|
|
|
SkBitmap bitmap;
|
|
|
|
bitmap.allocN32Pixels(kWidth, kHeight);
|
|
|
|
SkCanvas canvas(bitmap);
|
|
|
|
canvas.clear(SK_ColorTRANSPARENT);
|
|
|
|
SkPaint paint;
|
|
|
|
paint.setColor(SK_ColorWHITE);
|
|
|
|
canvas.drawRect(SkRect::MakeXYWH(kWidth / 4, kHeight / 4, kWidth / 2, kHeight / 2),
|
|
|
|
paint);
|
2020-12-23 16:50:36 +00:00
|
|
|
sk_sp<SkImage> image = bitmap.asImage();
|
2020-06-24 13:57:59 +00:00
|
|
|
sk_sp<SkSpecialImage> imgSrc(
|
2021-04-19 23:27:09 +00:00
|
|
|
SkSpecialImage::MakeFromImage(dContext, SkIRect::MakeWH(kWidth, kHeight), image,
|
|
|
|
SkSurfaceProps()));
|
2019-10-08 12:21:14 +00:00
|
|
|
|
|
|
|
SkIPoint offset;
|
|
|
|
SkImageFilter_Base::Context ctx(SkMatrix::I(), SkIRect::MakeWH(32, 32), nullptr,
|
|
|
|
kN32_SkColorType, nullptr, imgSrc.get());
|
|
|
|
|
|
|
|
sk_sp<SkSpecialImage> normalResult(
|
|
|
|
as_IFB(filter)->filterImage(ctx).imageAndOffset(&offset));
|
|
|
|
REPORTER_ASSERT(reporter, normalResult);
|
|
|
|
|
|
|
|
SkMatrix mirrorX;
|
|
|
|
mirrorX.setTranslate(0, SkIntToScalar(32));
|
|
|
|
mirrorX.preScale(SK_Scalar1, -SK_Scalar1);
|
|
|
|
SkImageFilter_Base::Context mirrorXCTX(mirrorX, SkIRect::MakeWH(32, 32), nullptr,
|
|
|
|
kN32_SkColorType, nullptr, imgSrc.get());
|
|
|
|
|
|
|
|
sk_sp<SkSpecialImage> mirrorXResult(
|
|
|
|
as_IFB(filter)->filterImage(mirrorXCTX).imageAndOffset(&offset));
|
|
|
|
REPORTER_ASSERT(reporter, mirrorXResult);
|
|
|
|
|
|
|
|
SkMatrix mirrorY;
|
|
|
|
mirrorY.setTranslate(SkIntToScalar(32), 0);
|
|
|
|
mirrorY.preScale(-SK_Scalar1, SK_Scalar1);
|
|
|
|
SkImageFilter_Base::Context mirrorYCTX(mirrorY, SkIRect::MakeWH(32, 32), nullptr,
|
|
|
|
kN32_SkColorType, nullptr, imgSrc.get());
|
|
|
|
|
|
|
|
sk_sp<SkSpecialImage> mirrorYResult(
|
|
|
|
as_IFB(filter)->filterImage(mirrorYCTX).imageAndOffset(&offset));
|
|
|
|
REPORTER_ASSERT(reporter, mirrorYResult);
|
|
|
|
|
|
|
|
SkBitmap normalResultBM, mirrorXResultBM, mirrorYResultBM;
|
|
|
|
|
2020-08-27 16:44:07 +00:00
|
|
|
REPORTER_ASSERT(reporter, special_image_to_bitmap(dContext, normalResult.get(),
|
|
|
|
&normalResultBM));
|
|
|
|
REPORTER_ASSERT(reporter, special_image_to_bitmap(dContext, mirrorXResult.get(),
|
|
|
|
&mirrorXResultBM));
|
|
|
|
REPORTER_ASSERT(reporter, special_image_to_bitmap(dContext, mirrorYResult.get(),
|
|
|
|
&mirrorYResultBM));
|
2019-10-08 12:21:14 +00:00
|
|
|
|
|
|
|
for (int y = 0; y < kHeight; y++) {
|
|
|
|
int diffs = memcmp(normalResultBM.getAddr32(0, y),
|
|
|
|
mirrorXResultBM.getAddr32(0, y),
|
|
|
|
normalResultBM.rowBytes());
|
|
|
|
REPORTER_ASSERT(reporter, !diffs);
|
|
|
|
if (diffs) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
diffs = memcmp(normalResultBM.getAddr32(0, y),
|
|
|
|
mirrorYResultBM.getAddr32(0, y),
|
|
|
|
normalResultBM.rowBytes());
|
|
|
|
REPORTER_ASSERT(reporter, !diffs);
|
|
|
|
if (diffs) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
DEF_TEST(MorphologyFilterRadiusWithMirrorCTM, reporter) {
|
|
|
|
test_morphology_radius_with_mirror_ctm(reporter, nullptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(MorphologyFilterRadiusWithMirrorCTM_Gpu, reporter, ctxInfo) {
|
2020-07-06 14:56:46 +00:00
|
|
|
test_morphology_radius_with_mirror_ctm(reporter, ctxInfo.directContext());
|
2019-10-08 12:21:14 +00:00
|
|
|
}
|
|
|
|
|
2020-08-27 16:44:07 +00:00
|
|
|
static void test_zero_blur_sigma(skiatest::Reporter* reporter, GrDirectContext* dContext) {
|
2016-03-16 23:15:53 +00:00
|
|
|
// Check that SkBlurImageFilter with a zero sigma and a non-zero srcOffset works correctly.
|
2019-08-05 14:41:10 +00:00
|
|
|
SkIRect cropRect = SkIRect::MakeXYWH(5, 0, 5, 10);
|
|
|
|
sk_sp<SkImageFilter> input(SkImageFilters::Offset(0, 0, nullptr, &cropRect));
|
|
|
|
sk_sp<SkImageFilter> filter(SkImageFilters::Blur(0, 0, std::move(input), &cropRect));
|
2016-03-16 23:15:53 +00:00
|
|
|
|
2020-08-27 16:44:07 +00:00
|
|
|
sk_sp<SkSpecialSurface> surf(create_empty_special_surface(dContext, 10));
|
2016-03-16 23:15:53 +00:00
|
|
|
surf->getCanvas()->clear(SK_ColorGREEN);
|
2016-03-17 21:31:39 +00:00
|
|
|
sk_sp<SkSpecialImage> image(surf->makeImageSnapshot());
|
2016-03-16 23:15:53 +00:00
|
|
|
|
|
|
|
SkIPoint offset;
|
2019-08-14 18:35:15 +00:00
|
|
|
SkImageFilter_Base::Context ctx(SkMatrix::I(), SkIRect::MakeWH(32, 32), nullptr,
|
2019-08-14 18:35:42 +00:00
|
|
|
kN32_SkColorType, nullptr, image.get());
|
2016-03-16 23:15:53 +00:00
|
|
|
|
2019-08-26 14:52:15 +00:00
|
|
|
sk_sp<SkSpecialImage> result(as_IFB(filter)->filterImage(ctx).imageAndOffset(&offset));
|
2016-03-16 23:15:53 +00:00
|
|
|
REPORTER_ASSERT(reporter, offset.fX == 5 && offset.fY == 0);
|
|
|
|
REPORTER_ASSERT(reporter, result);
|
|
|
|
REPORTER_ASSERT(reporter, result->width() == 5 && result->height() == 10);
|
|
|
|
|
|
|
|
SkBitmap resultBM;
|
|
|
|
|
2020-08-27 16:44:07 +00:00
|
|
|
REPORTER_ASSERT(reporter, special_image_to_bitmap(dContext, result.get(), &resultBM));
|
2016-03-16 23:15:53 +00:00
|
|
|
|
|
|
|
for (int y = 0; y < resultBM.height(); y++) {
|
|
|
|
for (int x = 0; x < resultBM.width(); x++) {
|
|
|
|
bool diff = *resultBM.getAddr32(x, y) != SK_ColorGREEN;
|
|
|
|
REPORTER_ASSERT(reporter, !diff);
|
|
|
|
if (diff) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-11 18:58:39 +00:00
|
|
|
DEF_TEST(ImageFilterZeroBlurSigma, reporter) {
|
2016-04-20 18:48:36 +00:00
|
|
|
test_zero_blur_sigma(reporter, nullptr);
|
2016-03-16 23:15:53 +00:00
|
|
|
}
|
|
|
|
|
2016-04-12 16:59:58 +00:00
|
|
|
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ImageFilterZeroBlurSigma_Gpu, reporter, ctxInfo) {
|
2020-07-06 14:56:46 +00:00
|
|
|
test_zero_blur_sigma(reporter, ctxInfo.directContext());
|
2016-03-16 23:15:53 +00:00
|
|
|
}
|
|
|
|
|
2016-04-05 11:43:45 +00:00
|
|
|
|
|
|
|
// Tests that, even when an upstream filter has returned null (due to failure or clipping), a
|
|
|
|
// downstream filter that affects transparent black still does so even with a nullptr input.
|
2020-07-21 20:50:06 +00:00
|
|
|
static void test_fail_affects_transparent_black(skiatest::Reporter* reporter,
|
2020-08-27 16:44:07 +00:00
|
|
|
GrDirectContext* dContext) {
|
2016-04-05 11:43:45 +00:00
|
|
|
sk_sp<FailImageFilter> failFilter(new FailImageFilter());
|
2020-08-27 16:44:07 +00:00
|
|
|
sk_sp<SkSpecialImage> source(create_empty_special_image(dContext, 5));
|
2019-08-02 13:57:04 +00:00
|
|
|
SkImageFilter_Base::Context ctx(SkMatrix::I(), SkIRect::MakeXYWH(0, 0, 1, 1), nullptr,
|
2019-08-14 18:35:42 +00:00
|
|
|
kN32_SkColorType, nullptr, source.get());
|
2019-04-08 20:23:20 +00:00
|
|
|
sk_sp<SkColorFilter> green(SkColorFilters::Blend(SK_ColorGREEN, SkBlendMode::kSrc));
|
2020-06-24 20:56:33 +00:00
|
|
|
SkASSERT(as_CFB(green)->affectsTransparentBlack());
|
2019-08-05 14:41:10 +00:00
|
|
|
sk_sp<SkImageFilter> greenFilter(SkImageFilters::ColorFilter(std::move(green),
|
|
|
|
std::move(failFilter)));
|
2016-04-05 11:43:45 +00:00
|
|
|
SkIPoint offset;
|
2019-08-26 14:52:15 +00:00
|
|
|
sk_sp<SkSpecialImage> result(as_IFB(greenFilter)->filterImage(ctx).imageAndOffset(&offset));
|
2016-04-05 11:43:45 +00:00
|
|
|
REPORTER_ASSERT(reporter, nullptr != result.get());
|
2020-08-16 12:48:02 +00:00
|
|
|
if (result) {
|
2016-04-05 11:43:45 +00:00
|
|
|
SkBitmap resultBM;
|
2020-08-27 16:44:07 +00:00
|
|
|
REPORTER_ASSERT(reporter, special_image_to_bitmap(dContext, result.get(), &resultBM));
|
2016-04-05 11:43:45 +00:00
|
|
|
REPORTER_ASSERT(reporter, *resultBM.getAddr32(0, 0) == SK_ColorGREEN);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
DEF_TEST(ImageFilterFailAffectsTransparentBlack, reporter) {
|
2016-04-20 18:48:36 +00:00
|
|
|
test_fail_affects_transparent_black(reporter, nullptr);
|
2016-04-05 11:43:45 +00:00
|
|
|
}
|
|
|
|
|
2016-04-12 16:59:58 +00:00
|
|
|
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ImageFilterFailAffectsTransparentBlack_Gpu, reporter, ctxInfo) {
|
2020-07-06 14:56:46 +00:00
|
|
|
test_fail_affects_transparent_black(reporter, ctxInfo.directContext());
|
2016-04-05 11:43:45 +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.
|
|
|
|
|
2016-04-05 16:09:36 +00:00
|
|
|
FilterList filters(nullptr);
|
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);
|
2018-05-17 15:17:39 +00:00
|
|
|
const int tileSize = 8;
|
|
|
|
|
|
|
|
SkPaint textPaint;
|
|
|
|
textPaint.setColor(SK_ColorWHITE);
|
2019-03-20 16:12:10 +00:00
|
|
|
SkFont font(ToolUtils::create_portable_typeface(), height);
|
2018-05-17 15:17:39 +00:00
|
|
|
|
|
|
|
const char* text = "ABC";
|
|
|
|
const SkScalar yPos = SkIntToScalar(height);
|
2014-04-29 15:20:39 +00:00
|
|
|
|
2014-05-07 20:00:04 +00:00
|
|
|
for (int scale = 1; scale <= 2; ++scale) {
|
2016-03-23 20:44:26 +00:00
|
|
|
for (int i = 0; i < filters.count(); ++i) {
|
2018-05-17 15:17:39 +00:00
|
|
|
SkPaint combinedPaint;
|
|
|
|
combinedPaint.setColor(SK_ColorWHITE);
|
|
|
|
combinedPaint.setImageFilter(sk_ref_sp(filters.getFilter(i)));
|
|
|
|
|
|
|
|
untiledCanvas.clear(SK_ColorTRANSPARENT);
|
2014-05-07 20:00:04 +00:00
|
|
|
untiledCanvas.save();
|
|
|
|
untiledCanvas.scale(SkIntToScalar(scale), SkIntToScalar(scale));
|
2019-01-07 16:01:57 +00:00
|
|
|
untiledCanvas.drawString(text, 0, yPos, font, combinedPaint);
|
2014-05-07 20:00:04 +00:00
|
|
|
untiledCanvas.restore();
|
2018-05-17 15:17:39 +00:00
|
|
|
|
|
|
|
tiledCanvas.clear(SK_ColorTRANSPARENT);
|
2014-05-07 20:00:04 +00:00
|
|
|
for (int y = 0; y < height; y += tileSize) {
|
|
|
|
for (int x = 0; x < width; x += tileSize) {
|
|
|
|
tiledCanvas.save();
|
2018-05-17 15:17:39 +00:00
|
|
|
const SkRect clipRect = SkRect::MakeXYWH(x, y, tileSize, tileSize);
|
|
|
|
tiledCanvas.clipRect(clipRect);
|
|
|
|
if (filters.needsSaveLayer(i)) {
|
2019-08-05 14:41:10 +00:00
|
|
|
const SkRect layerBounds = SkRect::MakeIWH(width, height);
|
2018-05-17 15:17:39 +00:00
|
|
|
tiledCanvas.saveLayer(&layerBounds, &combinedPaint);
|
|
|
|
tiledCanvas.scale(SkIntToScalar(scale), SkIntToScalar(scale));
|
2019-01-07 16:01:57 +00:00
|
|
|
tiledCanvas.drawString(text, 0, yPos, font, textPaint);
|
2018-05-17 15:17:39 +00:00
|
|
|
tiledCanvas.restore();
|
|
|
|
} else {
|
|
|
|
tiledCanvas.scale(SkIntToScalar(scale), SkIntToScalar(scale));
|
2019-01-07 16:01:57 +00:00
|
|
|
tiledCanvas.drawString(text, 0, yPos, font, combinedPaint);
|
2018-05-17 15:17:39 +00:00
|
|
|
}
|
|
|
|
|
2014-05-07 20:00:04 +00:00
|
|
|
tiledCanvas.restore();
|
|
|
|
}
|
2014-04-29 15:20:39 +00:00
|
|
|
}
|
2018-05-17 15:17:39 +00:00
|
|
|
|
2019-03-20 16:12:10 +00:00
|
|
|
if (!ToolUtils::equal_pixels(untiledResult, tiledResult)) {
|
2018-01-29 14:50:47 +00:00
|
|
|
REPORTER_ASSERT(reporter, false, filters.getName(i));
|
2017-08-08 19:48:54 +00:00
|
|
|
break;
|
2014-04-29 15:20:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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);
|
|
|
|
|
2019-04-08 20:23:20 +00:00
|
|
|
sk_sp<SkColorFilter> cf(SkColorFilters::Blend(SK_ColorWHITE, SkBlendMode::kSrc));
|
2019-08-05 14:41:10 +00:00
|
|
|
sk_sp<SkImageFilter> cfif(SkImageFilters::ColorFilter(std::move(cf), nullptr));
|
2021-02-01 18:07:32 +00:00
|
|
|
sk_sp<SkImageFilter> imageFilter(SkImageFilters::MatrixTransform(matrix,
|
|
|
|
SkSamplingOptions(),
|
2016-04-05 22:09:00 +00:00
|
|
|
std::move(cfif)));
|
2014-08-22 16:06:34 +00:00
|
|
|
|
|
|
|
SkPaint paint;
|
2016-04-05 18:50:42 +00:00
|
|
|
paint.setImageFilter(std::move(imageFilter));
|
2014-08-22 16:06:34 +00:00
|
|
|
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),
|
2020-08-21 17:42:01 +00:00
|
|
|
factory);
|
2014-08-22 16:06:34 +00:00
|
|
|
recordingCanvas->translate(-55, 0);
|
|
|
|
recordingCanvas->saveLayer(&bounds, &paint);
|
|
|
|
recordingCanvas->restore();
|
2016-03-18 14:25:55 +00:00
|
|
|
sk_sp<SkPicture> picture1(recorder.finishRecordingAsPicture());
|
2014-08-22 16:06:34 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-04 11:31:25 +00:00
|
|
|
static sk_sp<SkImageFilter> make_blur(sk_sp<SkImageFilter> input) {
|
2019-08-05 14:41:10 +00:00
|
|
|
return SkImageFilters::Blur(SK_Scalar1, SK_Scalar1, std::move(input));
|
2014-08-25 19:46:58 +00:00
|
|
|
}
|
|
|
|
|
2016-04-04 11:31:25 +00:00
|
|
|
static sk_sp<SkImageFilter> make_drop_shadow(sk_sp<SkImageFilter> input) {
|
2019-08-05 14:41:10 +00:00
|
|
|
return SkImageFilters::DropShadow(100, 100, 10, 10, SK_ColorBLUE, std::move(input));
|
2014-08-25 19:46:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
DEF_TEST(ImageFilterBlurThenShadowBounds, reporter) {
|
2016-04-04 11:31:25 +00:00
|
|
|
sk_sp<SkImageFilter> filter1(make_blur(nullptr));
|
|
|
|
sk_sp<SkImageFilter> filter2(make_drop_shadow(std::move(filter1)));
|
2014-08-25 19:46:58 +00:00
|
|
|
|
|
|
|
SkIRect bounds = SkIRect::MakeXYWH(0, 0, 100, 100);
|
2017-10-17 22:27:23 +00:00
|
|
|
SkIRect expectedBounds = SkIRect::MakeXYWH(-133, -133, 236, 236);
|
2018-05-17 15:17:39 +00:00
|
|
|
bounds = filter2->filterBounds(bounds, SkMatrix::I(),
|
|
|
|
SkImageFilter::kReverse_MapDirection, &bounds);
|
2014-08-25 19:46:58 +00:00
|
|
|
|
|
|
|
REPORTER_ASSERT(reporter, bounds == expectedBounds);
|
|
|
|
}
|
|
|
|
|
|
|
|
DEF_TEST(ImageFilterShadowThenBlurBounds, reporter) {
|
2016-04-04 11:31:25 +00:00
|
|
|
sk_sp<SkImageFilter> filter1(make_drop_shadow(nullptr));
|
|
|
|
sk_sp<SkImageFilter> filter2(make_blur(std::move(filter1)));
|
2014-08-25 19:46:58 +00:00
|
|
|
|
|
|
|
SkIRect bounds = SkIRect::MakeXYWH(0, 0, 100, 100);
|
2017-10-17 22:27:23 +00:00
|
|
|
SkIRect expectedBounds = SkIRect::MakeXYWH(-133, -133, 236, 236);
|
2018-05-17 15:17:39 +00:00
|
|
|
bounds = filter2->filterBounds(bounds, SkMatrix::I(),
|
|
|
|
SkImageFilter::kReverse_MapDirection, &bounds);
|
2014-08-25 19:46:58 +00:00
|
|
|
|
|
|
|
REPORTER_ASSERT(reporter, bounds == expectedBounds);
|
|
|
|
}
|
|
|
|
|
|
|
|
DEF_TEST(ImageFilterDilateThenBlurBounds, reporter) {
|
2019-08-05 14:41:10 +00:00
|
|
|
sk_sp<SkImageFilter> filter1(SkImageFilters::Dilate(2, 2, nullptr));
|
2016-04-04 11:31:25 +00:00
|
|
|
sk_sp<SkImageFilter> filter2(make_drop_shadow(std::move(filter1)));
|
2014-08-25 19:46:58 +00:00
|
|
|
|
|
|
|
SkIRect bounds = SkIRect::MakeXYWH(0, 0, 100, 100);
|
|
|
|
SkIRect expectedBounds = SkIRect::MakeXYWH(-132, -132, 234, 234);
|
2018-05-17 15:17:39 +00:00
|
|
|
bounds = filter2->filterBounds(bounds, SkMatrix::I(),
|
|
|
|
SkImageFilter::kReverse_MapDirection, &bounds);
|
2014-08-25 19:46:58 +00:00
|
|
|
|
|
|
|
REPORTER_ASSERT(reporter, bounds == expectedBounds);
|
|
|
|
}
|
|
|
|
|
2016-07-11 21:07:59 +00:00
|
|
|
DEF_TEST(ImageFilterScaledBlurRadius, reporter) {
|
|
|
|
// Each blur should spread 3*sigma, so 3 for the blur and 30 for the shadow
|
|
|
|
// (before the CTM). Bounds should be computed correctly in the presence of
|
|
|
|
// a (possibly negative) scale.
|
|
|
|
sk_sp<SkImageFilter> blur(make_blur(nullptr));
|
|
|
|
sk_sp<SkImageFilter> dropShadow(make_drop_shadow(nullptr));
|
|
|
|
{
|
|
|
|
// Uniform scale by 2.
|
|
|
|
SkMatrix scaleMatrix;
|
|
|
|
scaleMatrix.setScale(2, 2);
|
|
|
|
SkIRect bounds = SkIRect::MakeLTRB(0, 0, 200, 200);
|
|
|
|
|
2017-10-17 22:27:23 +00:00
|
|
|
SkIRect expectedBlurBounds = SkIRect::MakeLTRB(-6, -6, 206, 206);
|
2016-07-11 21:07:59 +00:00
|
|
|
SkIRect blurBounds = blur->filterBounds(
|
2019-08-05 14:41:10 +00:00
|
|
|
bounds, scaleMatrix, SkImageFilter::kForward_MapDirection, nullptr);
|
2016-07-11 21:07:59 +00:00
|
|
|
REPORTER_ASSERT(reporter, blurBounds == expectedBlurBounds);
|
|
|
|
SkIRect reverseBlurBounds = blur->filterBounds(
|
2019-08-05 14:41:10 +00:00
|
|
|
bounds, scaleMatrix, SkImageFilter::kReverse_MapDirection, &bounds);
|
2016-07-11 21:07:59 +00:00
|
|
|
REPORTER_ASSERT(reporter, reverseBlurBounds == expectedBlurBounds);
|
|
|
|
|
|
|
|
SkIRect expectedShadowBounds = SkIRect::MakeLTRB(0, 0, 460, 460);
|
|
|
|
SkIRect shadowBounds = dropShadow->filterBounds(
|
2019-08-05 14:41:10 +00:00
|
|
|
bounds, scaleMatrix, SkImageFilter::kForward_MapDirection, nullptr);
|
2016-07-11 21:07:59 +00:00
|
|
|
REPORTER_ASSERT(reporter, shadowBounds == expectedShadowBounds);
|
|
|
|
SkIRect expectedReverseShadowBounds =
|
2019-08-05 14:41:10 +00:00
|
|
|
SkIRect::MakeLTRB(-260, -260, 200, 200);
|
2016-07-11 21:07:59 +00:00
|
|
|
SkIRect reverseShadowBounds = dropShadow->filterBounds(
|
2019-08-05 14:41:10 +00:00
|
|
|
bounds, scaleMatrix, SkImageFilter::kReverse_MapDirection, &bounds);
|
|
|
|
REPORTER_ASSERT(reporter, reverseShadowBounds == expectedReverseShadowBounds);
|
2016-07-11 21:07:59 +00:00
|
|
|
}
|
|
|
|
{
|
|
|
|
// Vertical flip.
|
|
|
|
SkMatrix scaleMatrix;
|
|
|
|
scaleMatrix.setScale(1, -1);
|
|
|
|
SkIRect bounds = SkIRect::MakeLTRB(0, -100, 100, 0);
|
|
|
|
|
2017-10-17 22:27:23 +00:00
|
|
|
SkIRect expectedBlurBounds = SkIRect::MakeLTRB(-3, -103, 103, 3);
|
2016-07-11 21:07:59 +00:00
|
|
|
SkIRect blurBounds = blur->filterBounds(
|
2019-08-05 14:41:10 +00:00
|
|
|
bounds, scaleMatrix, SkImageFilter::kForward_MapDirection, nullptr);
|
2016-07-11 21:07:59 +00:00
|
|
|
REPORTER_ASSERT(reporter, blurBounds == expectedBlurBounds);
|
|
|
|
SkIRect reverseBlurBounds = blur->filterBounds(
|
2019-08-05 14:41:10 +00:00
|
|
|
bounds, scaleMatrix, SkImageFilter::kReverse_MapDirection, &bounds);
|
2016-07-11 21:07:59 +00:00
|
|
|
REPORTER_ASSERT(reporter, reverseBlurBounds == expectedBlurBounds);
|
|
|
|
|
|
|
|
SkIRect expectedShadowBounds = SkIRect::MakeLTRB(0, -230, 230, 0);
|
|
|
|
SkIRect shadowBounds = dropShadow->filterBounds(
|
2019-08-05 14:41:10 +00:00
|
|
|
bounds, scaleMatrix, SkImageFilter::kForward_MapDirection, nullptr);
|
2016-07-11 21:07:59 +00:00
|
|
|
REPORTER_ASSERT(reporter, shadowBounds == expectedShadowBounds);
|
|
|
|
SkIRect expectedReverseShadowBounds =
|
2019-08-05 14:41:10 +00:00
|
|
|
SkIRect::MakeLTRB(-130, -100, 100, 130);
|
2016-07-11 21:07:59 +00:00
|
|
|
SkIRect reverseShadowBounds = dropShadow->filterBounds(
|
2019-08-05 14:41:10 +00:00
|
|
|
bounds, scaleMatrix, SkImageFilter::kReverse_MapDirection, &bounds);
|
|
|
|
REPORTER_ASSERT(reporter, reverseShadowBounds == expectedReverseShadowBounds);
|
2016-07-11 21:07:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-13 17:05:47 +00:00
|
|
|
DEF_TEST(ImageFilterComposedBlurFastBounds, reporter) {
|
2016-04-04 11:31:25 +00:00
|
|
|
sk_sp<SkImageFilter> filter1(make_blur(nullptr));
|
|
|
|
sk_sp<SkImageFilter> filter2(make_blur(nullptr));
|
2019-08-05 14:41:10 +00:00
|
|
|
sk_sp<SkImageFilter> composedFilter(SkImageFilters::Compose(std::move(filter1),
|
|
|
|
std::move(filter2)));
|
2015-02-13 17:05:47 +00:00
|
|
|
|
2019-08-05 14:41:10 +00:00
|
|
|
SkRect boundsSrc = SkRect::MakeIWH(100, 100);
|
|
|
|
SkRect expectedBounds = SkRect::MakeXYWH(-6, -6, 112, 112);
|
2016-03-21 21:51:59 +00:00
|
|
|
SkRect boundsDst = composedFilter->computeFastBounds(boundsSrc);
|
2015-02-13 17:05:47 +00:00
|
|
|
|
|
|
|
REPORTER_ASSERT(reporter, boundsDst == expectedBounds);
|
|
|
|
}
|
|
|
|
|
2016-03-17 19:24:23 +00:00
|
|
|
DEF_TEST(ImageFilterUnionBounds, reporter) {
|
2019-08-05 14:41:10 +00:00
|
|
|
sk_sp<SkImageFilter> offset(SkImageFilters::Offset(50, 0, nullptr));
|
2016-03-17 19:24:23 +00:00
|
|
|
// Regardless of which order they appear in, the image filter bounds should
|
|
|
|
// be combined correctly.
|
|
|
|
{
|
2020-10-09 14:45:07 +00:00
|
|
|
sk_sp<SkImageFilter> composite(SkImageFilters::Blend(SkBlendMode::kSrcOver, offset));
|
2019-08-05 14:41:10 +00:00
|
|
|
SkRect bounds = SkRect::MakeIWH(100, 100);
|
2016-03-17 19:24:23 +00:00
|
|
|
// Intentionally aliasing here, as that's what the real callers do.
|
2016-03-21 21:51:59 +00:00
|
|
|
bounds = composite->computeFastBounds(bounds);
|
2019-08-05 14:41:10 +00:00
|
|
|
REPORTER_ASSERT(reporter, bounds == SkRect::MakeIWH(150, 100));
|
2016-03-17 19:24:23 +00:00
|
|
|
}
|
|
|
|
{
|
2020-10-09 14:45:07 +00:00
|
|
|
sk_sp<SkImageFilter> composite(SkImageFilters::Blend(SkBlendMode::kSrcOver, nullptr,
|
|
|
|
offset, nullptr));
|
2019-08-05 14:41:10 +00:00
|
|
|
SkRect bounds = SkRect::MakeIWH(100, 100);
|
2016-03-17 19:24:23 +00:00
|
|
|
// Intentionally aliasing here, as that's what the real callers do.
|
2016-03-21 21:51:59 +00:00
|
|
|
bounds = composite->computeFastBounds(bounds);
|
2019-08-05 14:41:10 +00:00
|
|
|
REPORTER_ASSERT(reporter, bounds == SkRect::MakeIWH(150, 100));
|
2016-03-17 19:24:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-21 20:50:06 +00:00
|
|
|
static void test_imagefilter_merge_result_size(skiatest::Reporter* reporter,
|
|
|
|
GrRecordingContext* rContext) {
|
2015-11-25 15:06:55 +00:00
|
|
|
SkBitmap greenBM;
|
|
|
|
greenBM.allocN32Pixels(20, 20);
|
|
|
|
greenBM.eraseColor(SK_ColorGREEN);
|
2020-12-23 16:50:36 +00:00
|
|
|
sk_sp<SkImage> greenImage(greenBM.asImage());
|
2019-08-05 14:41:10 +00:00
|
|
|
sk_sp<SkImageFilter> source(SkImageFilters::Image(std::move(greenImage)));
|
|
|
|
sk_sp<SkImageFilter> merge(SkImageFilters::Merge(source, source));
|
2015-11-25 15:06:55 +00:00
|
|
|
|
2020-07-21 20:50:06 +00:00
|
|
|
sk_sp<SkSpecialImage> srcImg(create_empty_special_image(rContext, 1));
|
2016-03-07 20:45:14 +00:00
|
|
|
|
2019-08-02 13:57:04 +00:00
|
|
|
SkImageFilter_Base::Context ctx(SkMatrix::I(), SkIRect::MakeXYWH(0, 0, 100, 100), nullptr,
|
2019-08-14 18:35:42 +00:00
|
|
|
kN32_SkColorType, nullptr, srcImg.get());
|
2015-11-25 15:06:55 +00:00
|
|
|
SkIPoint offset;
|
2016-03-07 20:45:14 +00:00
|
|
|
|
2019-08-26 14:52:15 +00:00
|
|
|
sk_sp<SkSpecialImage> resultImg(as_IFB(merge)->filterImage(ctx).imageAndOffset(&offset));
|
2016-03-07 20:45:14 +00:00
|
|
|
REPORTER_ASSERT(reporter, resultImg);
|
|
|
|
|
|
|
|
REPORTER_ASSERT(reporter, resultImg->width() == 20 && resultImg->height() == 20);
|
|
|
|
}
|
|
|
|
|
|
|
|
DEF_TEST(ImageFilterMergeResultSize, reporter) {
|
2016-04-20 18:48:36 +00:00
|
|
|
test_imagefilter_merge_result_size(reporter, nullptr);
|
2016-03-07 20:45:14 +00:00
|
|
|
}
|
|
|
|
|
2016-06-28 15:07:26 +00:00
|
|
|
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ImageFilterMergeResultSize_Gpu, reporter, ctxInfo) {
|
2020-07-06 14:56:46 +00:00
|
|
|
test_imagefilter_merge_result_size(reporter, ctxInfo.directContext());
|
2015-11-25 15:06:55 +00:00
|
|
|
}
|
|
|
|
|
2014-08-29 15:03:56 +00:00
|
|
|
static void draw_blurred_rect(SkCanvas* canvas) {
|
2014-07-14 17:19:54 +00:00
|
|
|
SkPaint filterPaint;
|
|
|
|
filterPaint.setColor(SK_ColorWHITE);
|
2019-08-05 14:41:10 +00:00
|
|
|
filterPaint.setImageFilter(SkImageFilters::Blur(SkIntToScalar(8), 0, nullptr));
|
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.
|
2020-08-21 17:42:01 +00:00
|
|
|
SkCanvas* recordingCanvas1 = recorder1.beginRecording(width, height);
|
|
|
|
SkCanvas* recordingCanvas2 = recorder2.beginRecording(width, height, &factory);
|
2019-08-05 14:41:10 +00:00
|
|
|
|
2014-08-29 15:03:56 +00:00
|
|
|
draw_blurred_rect(recordingCanvas1);
|
|
|
|
draw_blurred_rect(recordingCanvas2);
|
2016-03-18 14:25:55 +00:00
|
|
|
sk_sp<SkPicture> picture1(recorder1.finishRecordingAsPicture());
|
|
|
|
sk_sp<SkPicture> picture2(recorder2.finishRecordingAsPicture());
|
2014-07-14 17:19:54 +00:00
|
|
|
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));
|
2016-03-18 14:25:55 +00:00
|
|
|
draw_picture_clipped(&canvas1, tileRect, picture1.get());
|
|
|
|
draw_picture_clipped(&canvas2, tileRect, picture2.get());
|
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);
|
|
|
|
|
2019-08-05 14:41:10 +00:00
|
|
|
sk_sp<SkImageFilter> filter(SkImageFilters::MatrixConvolution(
|
|
|
|
kernelSize, kernel, gain, bias, kernelOffset, SkTileMode::kRepeat, false, nullptr));
|
2014-05-01 14:03:41 +00:00
|
|
|
|
|
|
|
SkBitmap result;
|
|
|
|
int width = 16, height = 16;
|
|
|
|
result.allocN32Pixels(width, height);
|
|
|
|
SkCanvas canvas(result);
|
|
|
|
canvas.clear(0);
|
|
|
|
|
|
|
|
SkPaint paint;
|
2016-04-08 15:01:20 +00:00
|
|
|
paint.setImageFilter(std::move(filter));
|
2014-05-01 14:03:41 +00:00
|
|
|
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);
|
|
|
|
|
2019-08-05 14:41:10 +00:00
|
|
|
sk_sp<SkImageFilter> filter(SkImageFilters::MatrixConvolution(
|
|
|
|
kernelSize, kernel, gain, bias, kernelOffset, SkTileMode::kClamp, true, nullptr));
|
2014-05-02 19:13:11 +00:00
|
|
|
|
|
|
|
SkBitmap result;
|
|
|
|
|
|
|
|
int width = 10, height = 10;
|
|
|
|
result.allocN32Pixels(width, height);
|
|
|
|
SkCanvas canvas(result);
|
|
|
|
canvas.clear(0);
|
|
|
|
|
|
|
|
SkPaint filterPaint;
|
2016-04-08 15:01:20 +00:00
|
|
|
filterPaint.setImageFilter(std::move(filter));
|
2019-08-05 14:41:10 +00:00
|
|
|
SkRect bounds = SkRect::MakeIWH(1, 10);
|
2014-05-02 19:13:11 +00:00
|
|
|
SkRect rect = SkRect::Make(SkIRect::MakeWH(width, height));
|
|
|
|
SkPaint rectPaint;
|
|
|
|
canvas.saveLayer(&bounds, &filterPaint);
|
|
|
|
canvas.drawRect(rect, rectPaint);
|
|
|
|
canvas.restore();
|
|
|
|
}
|
|
|
|
|
2020-07-21 20:50:06 +00:00
|
|
|
static void test_big_kernel(skiatest::Reporter* reporter, GrRecordingContext* rContext) {
|
2016-04-13 11:54:36 +00:00
|
|
|
// Check that a kernel that is too big for the GPU still works
|
|
|
|
SkScalar identityKernel[49] = {
|
|
|
|
0, 0, 0, 0, 0, 0, 0,
|
|
|
|
0, 0, 0, 0, 0, 0, 0,
|
|
|
|
0, 0, 0, 0, 0, 0, 0,
|
|
|
|
0, 0, 0, 1, 0, 0, 0,
|
|
|
|
0, 0, 0, 0, 0, 0, 0,
|
|
|
|
0, 0, 0, 0, 0, 0, 0,
|
|
|
|
0, 0, 0, 0, 0, 0, 0
|
|
|
|
};
|
|
|
|
SkISize kernelSize = SkISize::Make(7, 7);
|
|
|
|
SkScalar gain = SK_Scalar1, bias = 0;
|
|
|
|
SkIPoint kernelOffset = SkIPoint::Make(0, 0);
|
|
|
|
|
2019-08-05 14:41:10 +00:00
|
|
|
sk_sp<SkImageFilter> filter(SkImageFilters::MatrixConvolution(
|
|
|
|
kernelSize, identityKernel, gain, bias, kernelOffset,
|
|
|
|
SkTileMode::kClamp, true, nullptr));
|
2016-04-13 11:54:36 +00:00
|
|
|
|
2020-07-21 20:50:06 +00:00
|
|
|
sk_sp<SkSpecialImage> srcImg(create_empty_special_image(rContext, 100));
|
2016-04-13 11:54:36 +00:00
|
|
|
SkASSERT(srcImg);
|
|
|
|
|
|
|
|
SkIPoint offset;
|
2019-08-02 13:57:04 +00:00
|
|
|
SkImageFilter_Base::Context ctx(SkMatrix::I(), SkIRect::MakeWH(100, 100), nullptr,
|
2019-08-14 18:35:42 +00:00
|
|
|
kN32_SkColorType, nullptr, srcImg.get());
|
2019-08-26 14:52:15 +00:00
|
|
|
sk_sp<SkSpecialImage> resultImg(as_IFB(filter)->filterImage(ctx).imageAndOffset(&offset));
|
2016-04-13 11:54:36 +00:00
|
|
|
REPORTER_ASSERT(reporter, resultImg);
|
2020-07-21 20:50:06 +00:00
|
|
|
REPORTER_ASSERT(reporter, SkToBool(rContext) == resultImg->isTextureBacked());
|
2016-04-13 11:54:36 +00:00
|
|
|
REPORTER_ASSERT(reporter, resultImg->width() == 100 && resultImg->height() == 100);
|
|
|
|
REPORTER_ASSERT(reporter, offset.fX == 0 && offset.fY == 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
DEF_TEST(ImageFilterMatrixConvolutionBigKernel, reporter) {
|
2016-04-20 18:48:36 +00:00
|
|
|
test_big_kernel(reporter, nullptr);
|
2016-04-13 11:54:36 +00:00
|
|
|
}
|
|
|
|
|
2016-06-28 15:07:26 +00:00
|
|
|
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ImageFilterMatrixConvolutionBigKernel_Gpu,
|
|
|
|
reporter, ctxInfo) {
|
2020-07-06 14:56:46 +00:00
|
|
|
test_big_kernel(reporter, ctxInfo.directContext());
|
2016-04-13 11:54:36 +00:00
|
|
|
}
|
|
|
|
|
2014-02-03 22:22:16 +00:00
|
|
|
DEF_TEST(ImageFilterCropRect, reporter) {
|
2019-08-05 14:41:10 +00:00
|
|
|
test_cropRects(reporter, nullptr);
|
2016-03-07 20:45:14 +00:00
|
|
|
}
|
2015-06-22 16:46:59 +00:00
|
|
|
|
2016-04-12 16:59:58 +00:00
|
|
|
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ImageFilterCropRect_Gpu, reporter, ctxInfo) {
|
2020-07-06 14:56:46 +00:00
|
|
|
test_cropRects(reporter, ctxInfo.directContext());
|
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;
|
2020-08-21 17:42:01 +00:00
|
|
|
SkCanvas* recordingCanvas = recorder.beginRecording(100, 100, &factory);
|
2014-02-05 22:36:31 +00:00
|
|
|
|
|
|
|
SkPaint paint;
|
2016-04-04 12:05:11 +00:00
|
|
|
paint.setImageFilter(MatrixTestImageFilter::Make(reporter, expectedMatrix));
|
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
|
|
|
|
|
2016-03-18 14:25:55 +00:00
|
|
|
canvas.drawPicture(recorder.finishRecordingAsPicture());
|
2014-02-05 22:36:31 +00:00
|
|
|
}
|
|
|
|
|
2020-07-21 20:50:06 +00:00
|
|
|
static void test_clipped_picture_imagefilter(skiatest::Reporter* reporter,
|
|
|
|
GrRecordingContext* rContext) {
|
2016-03-18 14:25:55 +00:00
|
|
|
sk_sp<SkPicture> picture;
|
2014-07-30 21:49:31 +00:00
|
|
|
|
2016-03-07 20:45:14 +00:00
|
|
|
{
|
|
|
|
SkRTreeFactory factory;
|
|
|
|
SkPictureRecorder recorder;
|
2020-08-21 17:42:01 +00:00
|
|
|
SkCanvas* recordingCanvas = recorder.beginRecording(1, 1, &factory);
|
2016-03-07 20:45:14 +00:00
|
|
|
|
|
|
|
// 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);
|
2016-03-18 14:25:55 +00:00
|
|
|
picture = recorder.finishRecordingAsPicture();
|
2016-03-07 20:45:14 +00:00
|
|
|
}
|
|
|
|
|
2020-07-21 20:50:06 +00:00
|
|
|
sk_sp<SkSpecialImage> srcImg(create_empty_special_image(rContext, 2));
|
2014-07-30 21:49:31 +00:00
|
|
|
|
2019-08-05 14:41:10 +00:00
|
|
|
sk_sp<SkImageFilter> imageFilter(SkImageFilters::Picture(picture));
|
2014-07-30 21:49:31 +00:00
|
|
|
|
|
|
|
SkIPoint offset;
|
2019-08-02 13:57:04 +00:00
|
|
|
SkImageFilter_Base::Context ctx(SkMatrix::I(), SkIRect::MakeXYWH(1, 1, 1, 1), nullptr,
|
2019-08-14 18:35:42 +00:00
|
|
|
kN32_SkColorType, nullptr, srcImg.get());
|
2016-03-07 20:45:14 +00:00
|
|
|
|
2019-08-26 14:52:15 +00:00
|
|
|
sk_sp<SkSpecialImage> resultImage(
|
|
|
|
as_IFB(imageFilter)->filterImage(ctx).imageAndOffset(&offset));
|
2016-03-07 20:45:14 +00:00
|
|
|
REPORTER_ASSERT(reporter, !resultImage);
|
|
|
|
}
|
|
|
|
|
|
|
|
DEF_TEST(ImageFilterClippedPictureImageFilter, reporter) {
|
2016-04-20 18:48:36 +00:00
|
|
|
test_clipped_picture_imagefilter(reporter, nullptr);
|
2016-03-07 20:45:14 +00:00
|
|
|
}
|
|
|
|
|
2016-04-12 16:59:58 +00:00
|
|
|
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ImageFilterClippedPictureImageFilter_Gpu, reporter, ctxInfo) {
|
2020-07-06 14:56:46 +00:00
|
|
|
test_clipped_picture_imagefilter(reporter, ctxInfo.directContext());
|
2014-07-30 21:49:31 +00:00
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
|
2019-08-26 14:52:15 +00:00
|
|
|
sk_sp<SkColorFilter> green(SkColorFilters::Blend(SK_ColorGREEN, SkBlendMode::kSrc));
|
2019-08-05 14:41:10 +00:00
|
|
|
sk_sp<SkImageFilter> imageFilter(SkImageFilters::ColorFilter(green, nullptr));
|
2014-05-06 22:52:55 +00:00
|
|
|
SkPaint imageFilterPaint;
|
2016-04-05 18:50:42 +00:00
|
|
|
imageFilterPaint.setImageFilter(std::move(imageFilter));
|
2014-05-06 22:52:55 +00:00
|
|
|
SkPaint colorFilterPaint;
|
2016-03-22 17:17:23 +00:00
|
|
|
colorFilterPaint.setColorFilter(green);
|
2014-05-06 22:52:55 +00:00
|
|
|
|
2019-08-05 14:41:10 +00:00
|
|
|
SkRect bounds = SkRect::MakeIWH(10, 10);
|
2014-05-06 22:52:55 +00:00
|
|
|
|
2020-08-21 17:42:01 +00:00
|
|
|
SkCanvas* recordingCanvas = recorder.beginRecording(10, 10, &factory);
|
2014-05-06 22:52:55 +00:00
|
|
|
recordingCanvas->saveLayer(&bounds, &imageFilterPaint);
|
|
|
|
recordingCanvas->restore();
|
2016-03-18 14:25:55 +00:00
|
|
|
sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
|
2014-05-06 22:52:55 +00:00
|
|
|
|
|
|
|
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);
|
|
|
|
|
2020-08-21 17:42:01 +00:00
|
|
|
recordingCanvas = recorder.beginRecording(10, 10, &factory);
|
2015-08-27 14:41:13 +00:00
|
|
|
recordingCanvas->saveLayer(nullptr, &imageFilterPaint);
|
2014-05-06 22:52:55 +00:00
|
|
|
recordingCanvas->restore();
|
2016-03-18 14:25:55 +00:00
|
|
|
sk_sp<SkPicture> picture2(recorder.finishRecordingAsPicture());
|
2014-05-06 22:52:55 +00:00
|
|
|
|
|
|
|
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);
|
|
|
|
|
2020-08-21 17:42:01 +00:00
|
|
|
recordingCanvas = recorder.beginRecording(10, 10, &factory);
|
2014-05-06 22:52:55 +00:00
|
|
|
recordingCanvas->saveLayer(&bounds, &colorFilterPaint);
|
|
|
|
recordingCanvas->restore();
|
2016-03-18 14:25:55 +00:00
|
|
|
sk_sp<SkPicture> picture3(recorder.finishRecordingAsPicture());
|
2014-05-06 22:52:55 +00:00
|
|
|
|
|
|
|
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);
|
|
|
|
|
2020-07-31 18:09:06 +00:00
|
|
|
// Check that a blur with a very large radius does not crash or assert.
|
2014-03-24 20:50:59 +00:00
|
|
|
SkPaint paint;
|
2019-08-05 14:41:10 +00:00
|
|
|
paint.setImageFilter(SkImageFilters::Blur(SkIntToScalar(1<<30), SkIntToScalar(1<<30), nullptr));
|
2021-01-24 16:39:58 +00:00
|
|
|
canvas->drawImage(bitmap.asImage(), 0, 0, SkSamplingOptions(), &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
|
|
|
}
|
|
|
|
|
2020-07-31 18:09:06 +00:00
|
|
|
DEF_TEST(ImageFilterMatrixConvolutionTest, reporter) {
|
2014-09-29 14:57:20 +00:00
|
|
|
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.
|
2019-08-05 14:41:10 +00:00
|
|
|
sk_sp<SkImageFilter> conv(SkImageFilters::MatrixConvolution(
|
|
|
|
SkISize::Make(1<<30, 1<<30), kernel, gain, bias, kernelOffset,
|
|
|
|
SkTileMode::kRepeat, false, nullptr));
|
2014-09-29 14:57:20 +00:00
|
|
|
|
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.
|
2019-08-05 14:41:10 +00:00
|
|
|
conv = SkImageFilters::MatrixConvolution(
|
|
|
|
SkISize::Make(1, 1), nullptr, gain, bias, kernelOffset,
|
|
|
|
SkTileMode::kRepeat, false, nullptr);
|
2014-09-29 14:57:20 +00:00
|
|
|
|
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.
|
2019-08-05 14:41:10 +00:00
|
|
|
conv = SkImageFilters::MatrixConvolution(
|
|
|
|
SkISize::Make(0, 1), kernel, gain, bias, kernelOffset,
|
|
|
|
SkTileMode::kRepeat, false, nullptr);
|
2014-09-29 14:57:20 +00:00
|
|
|
|
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.
|
2019-08-05 14:41:10 +00:00
|
|
|
conv = SkImageFilters::MatrixConvolution(
|
|
|
|
SkISize::Make(1, -1), kernel, gain, bias, kernelOffset,
|
|
|
|
SkTileMode::kRepeat, false, nullptr);
|
2014-09-29 14:57:20 +00:00
|
|
|
|
2015-08-27 14:41:13 +00:00
|
|
|
REPORTER_ASSERT(reporter, nullptr == conv.get());
|
2014-09-29 14:57:20 +00:00
|
|
|
}
|
|
|
|
|
2017-07-21 18:24:29 +00:00
|
|
|
static void test_xfermode_cropped_input(SkSurface* surf, skiatest::Reporter* reporter) {
|
|
|
|
auto canvas = surf->getCanvas();
|
2015-06-22 16:46:59 +00:00
|
|
|
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);
|
|
|
|
|
2019-04-08 20:23:20 +00:00
|
|
|
sk_sp<SkColorFilter> green(SkColorFilters::Blend(SK_ColorGREEN, SkBlendMode::kSrcIn));
|
2019-08-05 14:41:10 +00:00
|
|
|
sk_sp<SkImageFilter> greenFilter(SkImageFilters::ColorFilter(green, nullptr));
|
|
|
|
SkIRect cropRect = SkIRect::MakeEmpty();
|
|
|
|
sk_sp<SkImageFilter> croppedOut(SkImageFilters::ColorFilter(green, 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
|
|
|
|
2020-10-09 14:45:07 +00:00
|
|
|
// Check that an blend image filter whose input has been cropped out still draws the other
|
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
|
|
|
// input. Also check that drawing with both inputs cropped out doesn't cause a GPU warning.
|
2016-10-06 00:33:02 +00:00
|
|
|
SkBlendMode mode = SkBlendMode::kSrcOver;
|
2020-10-09 14:45:07 +00:00
|
|
|
sk_sp<SkImageFilter> xfermodeNoFg(SkImageFilters::Blend(
|
2019-08-05 14:41:10 +00:00
|
|
|
mode, greenFilter, croppedOut, nullptr));
|
2020-10-09 14:45:07 +00:00
|
|
|
sk_sp<SkImageFilter> xfermodeNoBg(SkImageFilters::Blend(
|
2019-08-05 14:41:10 +00:00
|
|
|
mode, croppedOut, greenFilter, nullptr));
|
2020-10-09 14:45:07 +00:00
|
|
|
sk_sp<SkImageFilter> xfermodeNoFgNoBg(SkImageFilters::Blend(
|
2019-08-05 14:41:10 +00:00
|
|
|
mode, croppedOut, croppedOut, nullptr));
|
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
|
|
|
|
|
|
|
SkPaint paint;
|
2016-04-05 19:48:34 +00:00
|
|
|
paint.setImageFilter(std::move(xfermodeNoFg));
|
2021-01-25 00:49:21 +00:00
|
|
|
canvas->drawImage(bitmap.asImage(), 0, 0, SkSamplingOptions(), &paint); // drawSprite
|
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);
|
2017-07-21 18:24:29 +00:00
|
|
|
surf->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);
|
|
|
|
|
2016-04-05 19:48:34 +00:00
|
|
|
paint.setImageFilter(std::move(xfermodeNoBg));
|
2021-01-25 00:49:21 +00:00
|
|
|
canvas->drawImage(bitmap.asImage(), 0, 0, SkSamplingOptions(), &paint); // drawSprite
|
2017-07-21 18:24:29 +00:00
|
|
|
surf->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);
|
|
|
|
|
2016-04-05 19:48:34 +00:00
|
|
|
paint.setImageFilter(std::move(xfermodeNoFgNoBg));
|
2021-01-25 00:49:21 +00:00
|
|
|
canvas->drawImage(bitmap.asImage(), 0, 0, SkSamplingOptions(), &paint); // drawSprite
|
2017-07-21 18:24:29 +00:00
|
|
|
surf->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));
|
2016-04-05 22:09:00 +00:00
|
|
|
sk_sp<SkImageFilter> matrixFilter(
|
2021-02-01 18:07:32 +00:00
|
|
|
SkImageFilters::MatrixTransform(matrix, SkSamplingOptions(SkFilterMode::kLinear), nullptr));
|
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;
|
2016-04-05 22:09:00 +00:00
|
|
|
filterPaint.setImageFilter(std::move(matrixFilter));
|
2014-04-02 19:20:05 +00:00
|
|
|
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;
|
2017-07-21 18:24:29 +00:00
|
|
|
temp.readPixels(info, &pixel, 4, 25, 25);
|
2014-04-02 19:20:05 +00:00
|
|
|
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);
|
2017-07-21 18:24:29 +00:00
|
|
|
temp.readPixels(info, &pixel, 4, 25, 25);
|
2015-08-27 14:41:13 +00:00
|
|
|
canvas.saveLayer(&bounds1, nullptr);
|
2021-01-25 00:49:21 +00:00
|
|
|
canvas.drawImage(bitmap.asImage(), 20, 20, SkSamplingOptions(), &filterPaint); // drawSprite
|
2014-04-02 19:20:05 +00:00
|
|
|
canvas.restore();
|
|
|
|
|
2017-07-21 18:24:29 +00:00
|
|
|
temp.readPixels(info, &pixel, 4, 25, 25);
|
2014-04-02 19:20:05 +00:00
|
|
|
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) {
|
2017-07-21 18:24:29 +00:00
|
|
|
test_xfermode_cropped_input(SkSurface::MakeRasterN32Premul(100, 100).get(), 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
|
|
|
|
2020-07-21 20:50:06 +00:00
|
|
|
static void test_composed_imagefilter_offset(skiatest::Reporter* reporter,
|
|
|
|
GrRecordingContext* rContext) {
|
|
|
|
sk_sp<SkSpecialImage> srcImg(create_empty_special_image(rContext, 100));
|
2015-02-13 17:05:47 +00:00
|
|
|
|
2019-08-05 14:41:10 +00:00
|
|
|
SkIRect cropRect = SkIRect::MakeXYWH(1, 0, 20, 20);
|
|
|
|
sk_sp<SkImageFilter> offsetFilter(SkImageFilters::Offset(0, 0, nullptr, &cropRect));
|
|
|
|
sk_sp<SkImageFilter> blurFilter(SkImageFilters::Blur(SK_Scalar1, SK_Scalar1,
|
2016-04-04 11:31:25 +00:00
|
|
|
nullptr, &cropRect));
|
2019-08-05 14:41:10 +00:00
|
|
|
sk_sp<SkImageFilter> composedFilter(SkImageFilters::Compose(std::move(blurFilter),
|
|
|
|
std::move(offsetFilter)));
|
2015-02-13 17:05:47 +00:00
|
|
|
SkIPoint offset;
|
2019-08-02 13:57:04 +00:00
|
|
|
SkImageFilter_Base::Context ctx(SkMatrix::I(), SkIRect::MakeWH(100, 100), nullptr,
|
2019-08-14 18:35:42 +00:00
|
|
|
kN32_SkColorType, nullptr, srcImg.get());
|
2016-03-07 20:45:14 +00:00
|
|
|
|
2019-08-02 13:57:04 +00:00
|
|
|
sk_sp<SkSpecialImage> resultImg(
|
2019-08-26 14:52:15 +00:00
|
|
|
as_IFB(composedFilter)->filterImage(ctx).imageAndOffset(&offset));
|
2016-03-07 20:45:14 +00:00
|
|
|
REPORTER_ASSERT(reporter, resultImg);
|
2015-02-13 17:05:47 +00:00
|
|
|
REPORTER_ASSERT(reporter, offset.fX == 1 && offset.fY == 0);
|
|
|
|
}
|
|
|
|
|
2016-03-07 20:45:14 +00:00
|
|
|
DEF_TEST(ComposedImageFilterOffset, reporter) {
|
2016-04-20 18:48:36 +00:00
|
|
|
test_composed_imagefilter_offset(reporter, nullptr);
|
2016-03-07 20:45:14 +00:00
|
|
|
}
|
|
|
|
|
2016-04-12 16:59:58 +00:00
|
|
|
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ComposedImageFilterOffset_Gpu, reporter, ctxInfo) {
|
2020-07-06 14:56:46 +00:00
|
|
|
test_composed_imagefilter_offset(reporter, ctxInfo.directContext());
|
2016-03-07 20:45:14 +00:00
|
|
|
}
|
|
|
|
|
2020-07-21 20:50:06 +00:00
|
|
|
static void test_composed_imagefilter_bounds(skiatest::Reporter* reporter,
|
2020-08-27 16:44:07 +00:00
|
|
|
GrDirectContext* dContext) {
|
2016-03-21 15:38:58 +00:00
|
|
|
// The bounds passed to the inner filter must be filtered by the outer
|
|
|
|
// filter, so that the inner filter produces the pixels that the outer
|
|
|
|
// filter requires as input. This matters if the outer filter moves pixels.
|
|
|
|
// Here, accounting for the outer offset is necessary so that the green
|
|
|
|
// pixels of the picture are not clipped.
|
|
|
|
|
|
|
|
SkPictureRecorder recorder;
|
2019-08-05 14:41:10 +00:00
|
|
|
SkCanvas* recordingCanvas = recorder.beginRecording(SkRect::MakeIWH(200, 100));
|
2016-03-21 15:38:58 +00:00
|
|
|
recordingCanvas->clipRect(SkRect::MakeXYWH(100, 0, 100, 100));
|
|
|
|
recordingCanvas->clear(SK_ColorGREEN);
|
2016-03-30 19:32:58 +00:00
|
|
|
sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
|
2019-08-05 14:41:10 +00:00
|
|
|
sk_sp<SkImageFilter> pictureFilter(SkImageFilters::Picture(picture));
|
|
|
|
SkIRect cropRect = SkIRect::MakeWH(100, 100);
|
|
|
|
sk_sp<SkImageFilter> offsetFilter(SkImageFilters::Offset(-100, 0, nullptr, &cropRect));
|
|
|
|
sk_sp<SkImageFilter> composedFilter(SkImageFilters::Compose(std::move(offsetFilter),
|
|
|
|
std::move(pictureFilter)));
|
2016-03-21 15:38:58 +00:00
|
|
|
|
2020-08-27 16:44:07 +00:00
|
|
|
sk_sp<SkSpecialImage> sourceImage(create_empty_special_image(dContext, 100));
|
2019-08-02 13:57:04 +00:00
|
|
|
SkImageFilter_Base::Context ctx(SkMatrix::I(), SkIRect::MakeWH(100, 100), nullptr,
|
2019-08-14 18:35:42 +00:00
|
|
|
kN32_SkColorType, nullptr, sourceImage.get());
|
2016-03-21 15:38:58 +00:00
|
|
|
SkIPoint offset;
|
2019-08-02 13:57:04 +00:00
|
|
|
sk_sp<SkSpecialImage> result(
|
2019-08-26 14:52:15 +00:00
|
|
|
as_IFB(composedFilter)->filterImage(ctx).imageAndOffset(&offset));
|
2016-03-21 15:38:58 +00:00
|
|
|
REPORTER_ASSERT(reporter, offset.isZero());
|
|
|
|
REPORTER_ASSERT(reporter, result);
|
|
|
|
REPORTER_ASSERT(reporter, result->subset().size() == SkISize::Make(100, 100));
|
|
|
|
|
|
|
|
SkBitmap resultBM;
|
2020-08-27 16:44:07 +00:00
|
|
|
REPORTER_ASSERT(reporter, special_image_to_bitmap(dContext, result.get(), &resultBM));
|
2016-03-21 15:38:58 +00:00
|
|
|
REPORTER_ASSERT(reporter, resultBM.getColor(50, 50) == SK_ColorGREEN);
|
|
|
|
}
|
|
|
|
|
|
|
|
DEF_TEST(ComposedImageFilterBounds, reporter) {
|
2016-04-20 18:48:36 +00:00
|
|
|
test_composed_imagefilter_bounds(reporter, nullptr);
|
2016-03-21 15:38:58 +00:00
|
|
|
}
|
|
|
|
|
2016-06-28 15:07:26 +00:00
|
|
|
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ComposedImageFilterBounds_Gpu, reporter, ctxInfo) {
|
2020-07-06 14:56:46 +00:00
|
|
|
test_composed_imagefilter_bounds(reporter, ctxInfo.directContext());
|
2016-03-21 15:38:58 +00:00
|
|
|
}
|
|
|
|
|
2015-08-20 18:10:41 +00:00
|
|
|
DEF_TEST(ImageFilterCanComputeFastBounds, reporter) {
|
|
|
|
|
2016-04-08 23:28:09 +00:00
|
|
|
{
|
|
|
|
SkPoint3 location = SkPoint3::Make(0, 0, SK_Scalar1);
|
2019-08-05 14:41:10 +00:00
|
|
|
sk_sp<SkImageFilter> lighting(SkImageFilters::PointLitDiffuse(
|
|
|
|
location, SK_ColorGREEN, 0, 0, nullptr));
|
2016-04-08 23:28:09 +00:00
|
|
|
REPORTER_ASSERT(reporter, !lighting->canComputeFastBounds());
|
|
|
|
}
|
2015-08-20 18:10:41 +00:00
|
|
|
|
|
|
|
{
|
2016-04-04 11:31:25 +00:00
|
|
|
sk_sp<SkImageFilter> gray(make_grayscale(nullptr, nullptr));
|
|
|
|
REPORTER_ASSERT(reporter, gray->canComputeFastBounds());
|
|
|
|
{
|
|
|
|
SkColorFilter* grayCF;
|
|
|
|
REPORTER_ASSERT(reporter, gray->asAColorFilter(&grayCF));
|
2020-06-24 20:56:33 +00:00
|
|
|
REPORTER_ASSERT(reporter, !as_CFB(grayCF)->affectsTransparentBlack());
|
2016-04-04 11:31:25 +00:00
|
|
|
grayCF->unref();
|
|
|
|
}
|
|
|
|
REPORTER_ASSERT(reporter, gray->canComputeFastBounds());
|
2015-08-20 18:10:41 +00:00
|
|
|
|
2019-08-05 14:41:10 +00:00
|
|
|
sk_sp<SkImageFilter> grayBlur(SkImageFilters::Blur(
|
|
|
|
SK_Scalar1, SK_Scalar1, std::move(gray)));
|
2016-04-04 11:31:25 +00:00
|
|
|
REPORTER_ASSERT(reporter, grayBlur->canComputeFastBounds());
|
|
|
|
}
|
2015-08-20 18:10:41 +00:00
|
|
|
|
2016-04-04 11:31:25 +00:00
|
|
|
{
|
2019-04-30 16:18:54 +00:00
|
|
|
float greenMatrix[20] = { 0, 0, 0, 0, 0,
|
|
|
|
0, 0, 0, 0, 1.0f/255,
|
|
|
|
0, 0, 0, 0, 0,
|
|
|
|
0, 0, 0, 0, 1.0f/255
|
|
|
|
};
|
|
|
|
sk_sp<SkColorFilter> greenCF(SkColorFilters::Matrix(greenMatrix));
|
2019-08-05 14:41:10 +00:00
|
|
|
sk_sp<SkImageFilter> green(SkImageFilters::ColorFilter(greenCF, nullptr));
|
2016-04-04 11:31:25 +00:00
|
|
|
|
2020-06-26 13:05:10 +00:00
|
|
|
REPORTER_ASSERT(reporter, as_CFB(greenCF)->affectsTransparentBlack());
|
2016-04-04 11:31:25 +00:00
|
|
|
REPORTER_ASSERT(reporter, !green->canComputeFastBounds());
|
|
|
|
|
2019-08-05 14:41:10 +00:00
|
|
|
sk_sp<SkImageFilter> greenBlur(SkImageFilters::Blur(SK_Scalar1, SK_Scalar1,
|
2016-04-04 11:31:25 +00:00
|
|
|
std::move(green)));
|
|
|
|
REPORTER_ASSERT(reporter, !greenBlur->canComputeFastBounds());
|
|
|
|
}
|
2015-08-20 18:10:41 +00:00
|
|
|
|
|
|
|
uint8_t allOne[256], identity[256];
|
|
|
|
for (int i = 0; i < 256; ++i) {
|
|
|
|
identity[i] = i;
|
|
|
|
allOne[i] = 255;
|
|
|
|
}
|
|
|
|
|
2016-04-05 18:50:42 +00:00
|
|
|
sk_sp<SkColorFilter> identityCF(SkTableColorFilter::MakeARGB(identity, identity,
|
|
|
|
identity, allOne));
|
2019-08-05 14:41:10 +00:00
|
|
|
sk_sp<SkImageFilter> identityFilter(SkImageFilters::ColorFilter(identityCF, nullptr));
|
2020-06-24 20:56:33 +00:00
|
|
|
REPORTER_ASSERT(reporter, !as_CFB(identityCF)->affectsTransparentBlack());
|
2015-08-20 18:10:41 +00:00
|
|
|
REPORTER_ASSERT(reporter, identityFilter->canComputeFastBounds());
|
|
|
|
|
2016-04-05 18:50:42 +00:00
|
|
|
sk_sp<SkColorFilter> forceOpaqueCF(SkTableColorFilter::MakeARGB(allOne, identity,
|
|
|
|
identity, identity));
|
2019-08-05 14:41:10 +00:00
|
|
|
sk_sp<SkImageFilter> forceOpaque(SkImageFilters::ColorFilter(forceOpaqueCF, nullptr));
|
2020-06-24 20:56:33 +00:00
|
|
|
REPORTER_ASSERT(reporter, as_CFB(forceOpaqueCF)->affectsTransparentBlack());
|
2015-08-20 18:10:41 +00:00
|
|
|
REPORTER_ASSERT(reporter, !forceOpaque->canComputeFastBounds());
|
|
|
|
}
|
|
|
|
|
2015-09-14 20:31:18 +00:00
|
|
|
// Verify that SkImageSource survives serialization
|
|
|
|
DEF_TEST(ImageFilterImageSourceSerialization, reporter) {
|
2016-03-24 01:59:25 +00:00
|
|
|
auto surface(SkSurface::MakeRasterN32Premul(10, 10));
|
2015-09-14 20:31:18 +00:00
|
|
|
surface->getCanvas()->clear(SK_ColorGREEN);
|
2016-03-17 17:51:11 +00:00
|
|
|
sk_sp<SkImage> image(surface->makeImageSnapshot());
|
2019-08-05 14:41:10 +00:00
|
|
|
sk_sp<SkImageFilter> filter(SkImageFilters::Image(std::move(image)));
|
2015-09-14 20:31:18 +00:00
|
|
|
|
2018-01-23 16:57:30 +00:00
|
|
|
sk_sp<SkData> data(filter->serialize());
|
|
|
|
sk_sp<SkImageFilter> unflattenedFilter = SkImageFilter::Deserialize(data->data(), data->size());
|
2015-09-14 20:31:18 +00:00
|
|
|
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);
|
2019-08-05 14:41:10 +00:00
|
|
|
canvas.drawRect(SkRect::MakeIWH(10, 10), paint);
|
2015-09-14 20:31:18 +00:00
|
|
|
REPORTER_ASSERT(reporter, *bm.getAddr32(0, 0) == SkPreMultiplyColor(SK_ColorGREEN));
|
|
|
|
}
|
|
|
|
|
2017-09-28 18:33:57 +00:00
|
|
|
DEF_TEST(ImageFilterImageSourceUninitialized, r) {
|
|
|
|
sk_sp<SkData> data(GetResourceAsData("crbug769134.fil"));
|
|
|
|
if (!data) {
|
|
|
|
return;
|
|
|
|
}
|
2018-01-23 16:57:30 +00:00
|
|
|
sk_sp<SkImageFilter> unflattenedFilter = SkImageFilter::Deserialize(data->data(), data->size());
|
2017-09-28 18:33:57 +00:00
|
|
|
// This will fail. More importantly, msan will verify that we did not
|
|
|
|
// compare against uninitialized memory.
|
|
|
|
REPORTER_ASSERT(r, !unflattenedFilter);
|
|
|
|
}
|
|
|
|
|
2016-01-05 16:39:28 +00:00
|
|
|
static void test_large_blur_input(skiatest::Reporter* reporter, SkCanvas* canvas) {
|
|
|
|
SkBitmap largeBmp;
|
|
|
|
int largeW = 5000;
|
|
|
|
int largeH = 5000;
|
|
|
|
// If we're GPU-backed make the bitmap too large to be converted into a texture.
|
2020-07-06 14:56:46 +00:00
|
|
|
if (auto ctx = canvas->recordingContext()) {
|
2019-02-04 18:26:26 +00:00
|
|
|
largeW = ctx->priv().caps()->maxTextureSize() + 1;
|
2016-01-05 16:39:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
largeBmp.allocN32Pixels(largeW, largeH);
|
2016-02-07 20:23:10 +00:00
|
|
|
largeBmp.eraseColor(0);
|
2016-01-05 16:39:28 +00:00
|
|
|
if (!largeBmp.getPixels()) {
|
|
|
|
ERRORF(reporter, "Failed to allocate large bmp.");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-12-23 16:50:36 +00:00
|
|
|
sk_sp<SkImage> largeImage(largeBmp.asImage());
|
2016-01-05 16:39:28 +00:00
|
|
|
if (!largeImage) {
|
|
|
|
ERRORF(reporter, "Failed to create large image.");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-08-05 14:41:10 +00:00
|
|
|
sk_sp<SkImageFilter> largeSource(SkImageFilters::Image(std::move(largeImage)));
|
2016-01-05 16:39:28 +00:00
|
|
|
if (!largeSource) {
|
|
|
|
ERRORF(reporter, "Failed to create large SkImageSource.");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-08-05 14:41:10 +00:00
|
|
|
sk_sp<SkImageFilter> blur(SkImageFilters::Blur(10.f, 10.f, std::move(largeSource)));
|
2016-01-05 16:39:28 +00:00
|
|
|
if (!blur) {
|
|
|
|
ERRORF(reporter, "Failed to create SkBlurImageFilter.");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
SkPaint paint;
|
2016-04-01 16:28:51 +00:00
|
|
|
paint.setImageFilter(std::move(blur));
|
2016-01-05 16:39:28 +00:00
|
|
|
|
|
|
|
// This should not crash (http://crbug.com/570479).
|
|
|
|
canvas->drawRect(SkRect::MakeIWH(largeW, largeH), paint);
|
|
|
|
}
|
|
|
|
|
2016-04-11 18:58:39 +00:00
|
|
|
DEF_TEST(ImageFilterBlurLargeImage, reporter) {
|
2016-03-24 01:59:25 +00:00
|
|
|
auto surface(SkSurface::MakeRaster(SkImageInfo::MakeN32Premul(100, 100)));
|
2016-01-05 16:39:28 +00:00
|
|
|
test_large_blur_input(reporter, surface->getCanvas());
|
|
|
|
}
|
|
|
|
|
2020-07-21 20:50:06 +00:00
|
|
|
static void test_make_with_filter(skiatest::Reporter* reporter, GrRecordingContext* rContext) {
|
|
|
|
sk_sp<SkSurface> surface(create_surface(rContext, 192, 128));
|
2016-05-19 21:50:29 +00:00
|
|
|
surface->getCanvas()->clear(SK_ColorRED);
|
|
|
|
SkPaint bluePaint;
|
|
|
|
bluePaint.setColor(SK_ColorBLUE);
|
|
|
|
SkIRect subset = SkIRect::MakeXYWH(25, 20, 50, 50);
|
|
|
|
surface->getCanvas()->drawRect(SkRect::Make(subset), bluePaint);
|
|
|
|
sk_sp<SkImage> sourceImage = surface->makeImageSnapshot();
|
|
|
|
|
|
|
|
sk_sp<SkImageFilter> filter = make_grayscale(nullptr, nullptr);
|
|
|
|
SkIRect clipBounds = SkIRect::MakeXYWH(30, 35, 100, 100);
|
|
|
|
SkIRect outSubset;
|
|
|
|
SkIPoint offset;
|
|
|
|
sk_sp<SkImage> result;
|
|
|
|
|
2020-07-21 20:50:06 +00:00
|
|
|
result = sourceImage->makeWithFilter(rContext, nullptr, subset, clipBounds,
|
|
|
|
&outSubset, &offset);
|
2016-05-19 21:50:29 +00:00
|
|
|
REPORTER_ASSERT(reporter, !result);
|
|
|
|
|
2020-07-21 20:50:06 +00:00
|
|
|
result = sourceImage->makeWithFilter(rContext, filter.get(), subset, clipBounds,
|
|
|
|
nullptr, &offset);
|
2016-05-19 21:50:29 +00:00
|
|
|
REPORTER_ASSERT(reporter, !result);
|
|
|
|
|
2020-07-21 20:50:06 +00:00
|
|
|
result = sourceImage->makeWithFilter(rContext, filter.get(), subset, clipBounds,
|
|
|
|
&outSubset, nullptr);
|
2016-05-19 21:50:29 +00:00
|
|
|
REPORTER_ASSERT(reporter, !result);
|
|
|
|
|
|
|
|
SkIRect bigSubset = SkIRect::MakeXYWH(-10000, -10000, 20000, 20000);
|
2020-07-21 20:50:06 +00:00
|
|
|
result = sourceImage->makeWithFilter(rContext, filter.get(), bigSubset, clipBounds,
|
|
|
|
&outSubset, &offset);
|
2016-05-19 21:50:29 +00:00
|
|
|
REPORTER_ASSERT(reporter, !result);
|
|
|
|
|
|
|
|
SkIRect empty = SkIRect::MakeEmpty();
|
2020-07-21 20:50:06 +00:00
|
|
|
result = sourceImage->makeWithFilter(rContext, filter.get(), empty, clipBounds,
|
|
|
|
&outSubset, &offset);
|
2016-05-19 21:50:29 +00:00
|
|
|
REPORTER_ASSERT(reporter, !result);
|
|
|
|
|
2020-07-21 20:50:06 +00:00
|
|
|
result = sourceImage->makeWithFilter(rContext, filter.get(), subset, empty,
|
|
|
|
&outSubset, &offset);
|
2016-05-19 21:50:29 +00:00
|
|
|
REPORTER_ASSERT(reporter, !result);
|
|
|
|
|
|
|
|
SkIRect leftField = SkIRect::MakeXYWH(-1000, 0, 100, 100);
|
2020-07-21 20:50:06 +00:00
|
|
|
result = sourceImage->makeWithFilter(rContext, filter.get(), subset, leftField,
|
|
|
|
&outSubset, &offset);
|
2016-05-19 21:50:29 +00:00
|
|
|
REPORTER_ASSERT(reporter, !result);
|
|
|
|
|
2020-07-21 20:50:06 +00:00
|
|
|
result = sourceImage->makeWithFilter(rContext, filter.get(), subset, clipBounds,
|
|
|
|
&outSubset, &offset);
|
2016-05-19 21:50:29 +00:00
|
|
|
|
|
|
|
REPORTER_ASSERT(reporter, result);
|
|
|
|
REPORTER_ASSERT(reporter, result->bounds().contains(outSubset));
|
|
|
|
SkIRect destRect = SkIRect::MakeXYWH(offset.x(), offset.y(),
|
|
|
|
outSubset.width(), outSubset.height());
|
|
|
|
REPORTER_ASSERT(reporter, clipBounds.contains(destRect));
|
2017-02-18 21:58:09 +00:00
|
|
|
|
|
|
|
// In GPU-mode, this case creates a special image with a backing size that differs from
|
|
|
|
// the content size
|
|
|
|
{
|
|
|
|
clipBounds.setXYWH(0, 0, 170, 100);
|
|
|
|
subset.setXYWH(0, 0, 160, 90);
|
|
|
|
|
2020-10-09 14:45:07 +00:00
|
|
|
filter = SkImageFilters::Blend(SkBlendMode::kSrc, nullptr);
|
2020-07-21 20:50:06 +00:00
|
|
|
result = sourceImage->makeWithFilter(rContext, filter.get(), subset, clipBounds,
|
|
|
|
&outSubset, &offset);
|
2017-02-18 21:58:09 +00:00
|
|
|
REPORTER_ASSERT(reporter, result);
|
|
|
|
}
|
2016-05-19 21:50:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
DEF_TEST(ImageFilterMakeWithFilter, reporter) {
|
|
|
|
test_make_with_filter(reporter, nullptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ImageFilterMakeWithFilter_Gpu, reporter, ctxInfo) {
|
2020-07-06 14:56:46 +00:00
|
|
|
test_make_with_filter(reporter, ctxInfo.directContext());
|
2016-05-19 21:50:29 +00:00
|
|
|
}
|
2014-09-22 14:29:03 +00:00
|
|
|
|
2016-04-12 16:59:58 +00:00
|
|
|
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ImageFilterHugeBlur_Gpu, reporter, ctxInfo) {
|
2015-06-22 19:06:08 +00:00
|
|
|
|
2020-07-06 14:56:46 +00:00
|
|
|
sk_sp<SkSurface> surf(SkSurface::MakeRenderTarget(ctxInfo.directContext(),
|
2016-04-20 18:48:36 +00:00
|
|
|
SkBudgeted::kNo,
|
|
|
|
SkImageInfo::MakeN32Premul(100, 100)));
|
2015-06-22 16:46:59 +00:00
|
|
|
|
2016-04-20 18:48:36 +00:00
|
|
|
|
|
|
|
SkCanvas* canvas = surf->getCanvas();
|
|
|
|
|
|
|
|
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
|
|
|
|
2016-06-28 15:07:26 +00:00
|
|
|
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(XfermodeImageFilterCroppedInput_Gpu, reporter, ctxInfo) {
|
2017-07-05 21:01:48 +00:00
|
|
|
sk_sp<SkSurface> surf(SkSurface::MakeRenderTarget(
|
2020-07-06 14:56:46 +00:00
|
|
|
ctxInfo.directContext(),
|
2017-07-05 21:01:48 +00:00
|
|
|
SkBudgeted::kNo,
|
|
|
|
SkImageInfo::Make(1, 1, kRGBA_8888_SkColorType, kPremul_SkAlphaType)));
|
2016-04-20 18:48:36 +00:00
|
|
|
|
2017-07-21 18:24:29 +00:00
|
|
|
test_xfermode_cropped_input(surf.get(), 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
|
|
|
|
2016-06-28 15:07:26 +00:00
|
|
|
DEF_GPUTEST_FOR_ALL_CONTEXTS(ImageFilterBlurLargeImage_Gpu, reporter, ctxInfo) {
|
2017-07-05 21:01:48 +00:00
|
|
|
auto surface(SkSurface::MakeRenderTarget(
|
2020-07-06 14:56:46 +00:00
|
|
|
ctxInfo.directContext(), SkBudgeted::kYes,
|
2017-07-05 21:01:48 +00:00
|
|
|
SkImageInfo::Make(100, 100, kRGBA_8888_SkColorType, kPremul_SkAlphaType)));
|
2016-01-05 16:39:28 +00:00
|
|
|
test_large_blur_input(reporter, surface->getCanvas());
|
|
|
|
}
|
2016-04-23 22:19:07 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Test that colorfilterimagefilter does not require its CTM to be decomposed when it has more
|
|
|
|
* than just scale/translate, but that other filters do.
|
|
|
|
*/
|
2016-04-25 16:25:15 +00:00
|
|
|
DEF_TEST(ImageFilterComplexCTM, reporter) {
|
2016-04-23 22:19:07 +00:00
|
|
|
// just need a colorfilter to exercise the corresponding imagefilter
|
2019-04-08 20:23:20 +00:00
|
|
|
sk_sp<SkColorFilter> cf = SkColorFilters::Blend(SK_ColorRED, SkBlendMode::kSrcATop);
|
2019-08-05 14:41:10 +00:00
|
|
|
sk_sp<SkImageFilter> cfif = SkImageFilters::ColorFilter(cf, nullptr); // can handle
|
|
|
|
sk_sp<SkImageFilter> blif = SkImageFilters::Blur(3, 3, nullptr); // cannot handle
|
2016-04-23 22:19:07 +00:00
|
|
|
|
|
|
|
struct {
|
|
|
|
sk_sp<SkImageFilter> fFilter;
|
|
|
|
bool fExpectCanHandle;
|
|
|
|
} recs[] = {
|
2019-08-05 14:41:10 +00:00
|
|
|
{ cfif, true },
|
|
|
|
{ SkImageFilters::ColorFilter(cf, cfif), true },
|
|
|
|
{ SkImageFilters::Merge(cfif, cfif), true },
|
|
|
|
{ SkImageFilters::Compose(cfif, cfif), true },
|
|
|
|
|
|
|
|
{ blif, false },
|
|
|
|
{ SkImageFilters::Blur(3, 3, cfif), false },
|
|
|
|
{ SkImageFilters::ColorFilter(cf, blif), false },
|
|
|
|
{ SkImageFilters::Merge(cfif, blif), false },
|
|
|
|
{ SkImageFilters::Compose(blif, cfif), false },
|
2016-04-23 22:19:07 +00:00
|
|
|
};
|
2016-11-14 19:17:16 +00:00
|
|
|
|
2016-04-23 22:19:07 +00:00
|
|
|
for (const auto& rec : recs) {
|
2019-08-02 13:57:04 +00:00
|
|
|
const bool canHandle = as_IFB(rec.fFilter)->canHandleComplexCTM();
|
2016-04-23 22:19:07 +00:00
|
|
|
REPORTER_ASSERT(reporter, canHandle == rec.fExpectCanHandle);
|
|
|
|
}
|
|
|
|
}
|
2017-07-06 16:48:15 +00:00
|
|
|
|
2017-09-25 17:26:40 +00:00
|
|
|
// Test SkXfermodeImageFilter::filterBounds with different blending modes.
|
2017-08-25 23:27:04 +00:00
|
|
|
DEF_TEST(XfermodeImageFilterBounds, reporter) {
|
|
|
|
SkIRect background_rect = SkIRect::MakeXYWH(0, 0, 100, 100);
|
|
|
|
SkIRect foreground_rect = SkIRect::MakeXYWH(50, 50, 100, 100);
|
|
|
|
sk_sp<SkImageFilter> background(new FixedBoundsImageFilter(background_rect));
|
|
|
|
sk_sp<SkImageFilter> foreground(new FixedBoundsImageFilter(foreground_rect));
|
|
|
|
|
|
|
|
const int kModeCount = static_cast<int>(SkBlendMode::kLastMode) + 1;
|
|
|
|
SkIRect expectedBounds[kModeCount];
|
|
|
|
// Expect union of input rects by default.
|
|
|
|
for (int i = 0; i < kModeCount; ++i) {
|
|
|
|
expectedBounds[i] = background_rect;
|
|
|
|
expectedBounds[i].join(foreground_rect);
|
|
|
|
}
|
|
|
|
|
|
|
|
SkIRect intersection = background_rect;
|
|
|
|
intersection.intersect(foreground_rect);
|
|
|
|
expectedBounds[static_cast<int>(SkBlendMode::kClear)] = SkIRect::MakeEmpty();
|
|
|
|
expectedBounds[static_cast<int>(SkBlendMode::kSrc)] = foreground_rect;
|
|
|
|
expectedBounds[static_cast<int>(SkBlendMode::kDst)] = background_rect;
|
|
|
|
expectedBounds[static_cast<int>(SkBlendMode::kSrcIn)] = intersection;
|
|
|
|
expectedBounds[static_cast<int>(SkBlendMode::kDstIn)] = intersection;
|
|
|
|
expectedBounds[static_cast<int>(SkBlendMode::kSrcATop)] = background_rect;
|
|
|
|
expectedBounds[static_cast<int>(SkBlendMode::kDstATop)] = foreground_rect;
|
|
|
|
|
|
|
|
// The value of this variable doesn't matter because we use inputs with fixed bounds.
|
|
|
|
SkIRect src = SkIRect::MakeXYWH(11, 22, 33, 44);
|
|
|
|
for (int i = 0; i < kModeCount; ++i) {
|
2020-10-09 14:45:07 +00:00
|
|
|
sk_sp<SkImageFilter> xfermode(SkImageFilters::Blend(static_cast<SkBlendMode>(i),
|
|
|
|
background, foreground, nullptr));
|
2018-05-17 15:17:39 +00:00
|
|
|
auto bounds = xfermode->filterBounds(src, SkMatrix::I(),
|
|
|
|
SkImageFilter::kForward_MapDirection, nullptr);
|
2017-08-25 23:27:04 +00:00
|
|
|
REPORTER_ASSERT(reporter, bounds == expectedBounds[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Test empty intersection.
|
|
|
|
sk_sp<SkImageFilter> background2(new FixedBoundsImageFilter(SkIRect::MakeXYWH(0, 0, 20, 20)));
|
|
|
|
sk_sp<SkImageFilter> foreground2(new FixedBoundsImageFilter(SkIRect::MakeXYWH(40, 40, 50, 50)));
|
2020-10-09 14:45:07 +00:00
|
|
|
sk_sp<SkImageFilter> xfermode(SkImageFilters::Blend(
|
2017-08-25 23:27:04 +00:00
|
|
|
SkBlendMode::kSrcIn, std::move(background2), std::move(foreground2), nullptr));
|
2018-05-17 15:17:39 +00:00
|
|
|
auto bounds = xfermode->filterBounds(src, SkMatrix::I(),
|
|
|
|
SkImageFilter::kForward_MapDirection, nullptr);
|
2017-08-25 23:27:04 +00:00
|
|
|
REPORTER_ASSERT(reporter, bounds.isEmpty());
|
|
|
|
}
|
|
|
|
|
2017-10-26 11:54:16 +00:00
|
|
|
DEF_TEST(OffsetImageFilterBounds, reporter) {
|
|
|
|
SkIRect src = SkIRect::MakeXYWH(0, 0, 100, 100);
|
2019-08-05 14:41:10 +00:00
|
|
|
sk_sp<SkImageFilter> offset(SkImageFilters::Offset(-50.5f, -50.5f, nullptr));
|
2017-10-26 11:54:16 +00:00
|
|
|
|
|
|
|
SkIRect expectedForward = SkIRect::MakeXYWH(-50, -50, 100, 100);
|
|
|
|
SkIRect boundsForward = offset->filterBounds(src, SkMatrix::I(),
|
2018-05-17 15:17:39 +00:00
|
|
|
SkImageFilter::kForward_MapDirection, nullptr);
|
2017-10-26 11:54:16 +00:00
|
|
|
REPORTER_ASSERT(reporter, boundsForward == expectedForward);
|
|
|
|
|
|
|
|
SkIRect expectedReverse = SkIRect::MakeXYWH(50, 50, 100, 100);
|
|
|
|
SkIRect boundsReverse = offset->filterBounds(src, SkMatrix::I(),
|
2018-05-17 15:17:39 +00:00
|
|
|
SkImageFilter::kReverse_MapDirection, &src);
|
2017-10-26 11:54:16 +00:00
|
|
|
REPORTER_ASSERT(reporter, boundsReverse == expectedReverse);
|
|
|
|
}
|
|
|
|
|
2017-08-25 23:27:04 +00:00
|
|
|
static void test_arithmetic_bounds(skiatest::Reporter* reporter, float k1, float k2, float k3,
|
|
|
|
float k4, sk_sp<SkImageFilter> background,
|
|
|
|
sk_sp<SkImageFilter> foreground,
|
2019-08-05 14:41:10 +00:00
|
|
|
const SkIRect* crop, const SkIRect& expected) {
|
2017-08-25 23:27:04 +00:00
|
|
|
sk_sp<SkImageFilter> arithmetic(
|
2019-08-05 14:41:10 +00:00
|
|
|
SkImageFilters::Arithmetic(k1, k2, k3, k4, false, background, foreground, crop));
|
2017-08-25 23:27:04 +00:00
|
|
|
// The value of the input rect doesn't matter because we use inputs with fixed bounds.
|
|
|
|
SkIRect bounds = arithmetic->filterBounds(SkIRect::MakeXYWH(11, 22, 33, 44), SkMatrix::I(),
|
2018-05-17 15:17:39 +00:00
|
|
|
SkImageFilter::kForward_MapDirection, nullptr);
|
2017-08-25 23:27:04 +00:00
|
|
|
REPORTER_ASSERT(reporter, expected == bounds);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void test_arithmetic_combinations(skiatest::Reporter* reporter, float v) {
|
2019-08-05 14:41:10 +00:00
|
|
|
SkIRect bgRect = SkIRect::MakeXYWH(0, 0, 100, 100);
|
|
|
|
SkIRect fgRect = SkIRect::MakeXYWH(50, 50, 100, 100);
|
|
|
|
sk_sp<SkImageFilter> background(new FixedBoundsImageFilter(bgRect));
|
|
|
|
sk_sp<SkImageFilter> foreground(new FixedBoundsImageFilter(fgRect));
|
2017-08-25 23:27:04 +00:00
|
|
|
|
2019-08-05 14:41:10 +00:00
|
|
|
SkIRect unionRect = bgRect;
|
|
|
|
unionRect.join(fgRect);
|
|
|
|
SkIRect intersection = bgRect;
|
|
|
|
intersection.intersect(fgRect);
|
2017-08-25 23:27:04 +00:00
|
|
|
|
|
|
|
test_arithmetic_bounds(reporter, 0, 0, 0, 0, background, foreground, nullptr,
|
|
|
|
SkIRect::MakeEmpty());
|
2019-08-05 14:41:10 +00:00
|
|
|
test_arithmetic_bounds(reporter, 0, 0, 0, v, background, foreground, nullptr, unionRect);
|
|
|
|
test_arithmetic_bounds(reporter, 0, 0, v, 0, background, foreground, nullptr, bgRect);
|
|
|
|
test_arithmetic_bounds(reporter, 0, 0, v, v, background, foreground, nullptr, unionRect);
|
|
|
|
test_arithmetic_bounds(reporter, 0, v, 0, 0, background, foreground, nullptr, fgRect);
|
|
|
|
test_arithmetic_bounds(reporter, 0, v, 0, v, background, foreground, nullptr, unionRect);
|
|
|
|
test_arithmetic_bounds(reporter, 0, v, v, 0, background, foreground, nullptr, unionRect);
|
|
|
|
test_arithmetic_bounds(reporter, 0, v, v, v, background, foreground, nullptr, unionRect);
|
2017-08-25 23:27:04 +00:00
|
|
|
test_arithmetic_bounds(reporter, v, 0, 0, 0, background, foreground, nullptr, intersection);
|
2019-08-05 14:41:10 +00:00
|
|
|
test_arithmetic_bounds(reporter, v, 0, 0, v, background, foreground, nullptr, unionRect);
|
|
|
|
test_arithmetic_bounds(reporter, v, 0, v, 0, background, foreground, nullptr, bgRect);
|
|
|
|
test_arithmetic_bounds(reporter, v, 0, v, v, background, foreground, nullptr, unionRect);
|
|
|
|
test_arithmetic_bounds(reporter, v, v, 0, 0, background, foreground, nullptr, fgRect);
|
|
|
|
test_arithmetic_bounds(reporter, v, v, 0, v, background, foreground, nullptr, unionRect);
|
|
|
|
test_arithmetic_bounds(reporter, v, v, v, 0, background, foreground, nullptr, unionRect);
|
|
|
|
test_arithmetic_bounds(reporter, v, v, v, v, background, foreground, nullptr, unionRect);
|
|
|
|
|
|
|
|
// Test with crop. When k4 is non-zero, the result is expected to be cropRect
|
2017-08-25 23:27:04 +00:00
|
|
|
// regardless of inputs because the filter affects the whole crop area.
|
2019-08-05 14:41:10 +00:00
|
|
|
SkIRect cropRect = SkIRect::MakeXYWH(-111, -222, 333, 444);
|
|
|
|
test_arithmetic_bounds(reporter, 0, 0, 0, 0, background, foreground, &cropRect,
|
2017-08-25 23:27:04 +00:00
|
|
|
SkIRect::MakeEmpty());
|
2019-08-05 14:41:10 +00:00
|
|
|
test_arithmetic_bounds(reporter, 0, 0, 0, v, background, foreground, &cropRect, cropRect);
|
|
|
|
test_arithmetic_bounds(reporter, 0, 0, v, 0, background, foreground, &cropRect, bgRect);
|
|
|
|
test_arithmetic_bounds(reporter, 0, 0, v, v, background, foreground, &cropRect, cropRect);
|
|
|
|
test_arithmetic_bounds(reporter, 0, v, 0, 0, background, foreground, &cropRect, fgRect);
|
|
|
|
test_arithmetic_bounds(reporter, 0, v, 0, v, background, foreground, &cropRect, cropRect);
|
|
|
|
test_arithmetic_bounds(reporter, 0, v, v, 0, background, foreground, &cropRect, unionRect);
|
|
|
|
test_arithmetic_bounds(reporter, 0, v, v, v, background, foreground, &cropRect, cropRect);
|
|
|
|
test_arithmetic_bounds(reporter, v, 0, 0, 0, background, foreground, &cropRect, intersection);
|
|
|
|
test_arithmetic_bounds(reporter, v, 0, 0, v, background, foreground, &cropRect, cropRect);
|
|
|
|
test_arithmetic_bounds(reporter, v, 0, v, 0, background, foreground, &cropRect, bgRect);
|
|
|
|
test_arithmetic_bounds(reporter, v, 0, v, v, background, foreground, &cropRect, cropRect);
|
|
|
|
test_arithmetic_bounds(reporter, v, v, 0, 0, background, foreground, &cropRect, fgRect);
|
|
|
|
test_arithmetic_bounds(reporter, v, v, 0, v, background, foreground, &cropRect, cropRect);
|
|
|
|
test_arithmetic_bounds(reporter, v, v, v, 0, background, foreground, &cropRect, unionRect);
|
|
|
|
test_arithmetic_bounds(reporter, v, v, v, v, background, foreground, &cropRect, cropRect);
|
2017-08-25 23:27:04 +00:00
|
|
|
}
|
|
|
|
|
2017-09-25 17:26:40 +00:00
|
|
|
// Test SkArithmeticImageFilter::filterBounds with different blending modes.
|
2017-08-25 23:27:04 +00:00
|
|
|
DEF_TEST(ArithmeticImageFilterBounds, reporter) {
|
|
|
|
test_arithmetic_combinations(reporter, 1);
|
|
|
|
test_arithmetic_combinations(reporter, 0.5);
|
|
|
|
}
|
2017-09-25 17:26:40 +00:00
|
|
|
|
2020-09-25 13:48:01 +00:00
|
|
|
// Test SkDisplacementMapEffect::filterBounds.
|
|
|
|
DEF_TEST(DisplacementMapBounds, reporter) {
|
|
|
|
SkIRect floodBounds(SkIRect::MakeXYWH(20, 30, 10, 10));
|
Add SkImageFilters::Shader in place of Paint factory
SkImageFilters::Paint did not use every slot of the SkPaint, with only
its color, alpha, color filter, and shader having a meaningful effect on
the image filter result. It was always blended into a transparent dst,
so blend mode wasn't very relevant, and it was always filled to whatever
required geometry, so stroke style, path effect, and mask filters were
ignored or not well specified.
Color, alpha, and color filter can all be combined into an SkShader, so
a more constrained SkImageFilters::Shader provides the same useful
capabilities without as many surprises.
SkImageFilters::Paint still exists, but is deprecated to be removed
once I've confirmed clients aren't depending on it.
Bug: skia:9310
Change-Id: I11a82bda1a5d440726cf4e2b5bfaae4929568679
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/323680
Reviewed-by: Kevin Lubick <kjlubick@google.com>
Commit-Queue: Michael Ludwig <michaelludwig@google.com>
2020-10-07 19:27:20 +00:00
|
|
|
sk_sp<SkImageFilter> flood(SkImageFilters::Shader(SkShaders::Color(SK_ColorGREEN),
|
|
|
|
&floodBounds));
|
2020-09-25 13:48:01 +00:00
|
|
|
SkIRect tilingBounds(SkIRect::MakeXYWH(0, 0, 200, 100));
|
|
|
|
sk_sp<SkImageFilter> tiling(SkImageFilters::Tile(SkRect::Make(floodBounds),
|
|
|
|
SkRect::Make(tilingBounds),
|
|
|
|
flood));
|
|
|
|
sk_sp<SkImageFilter> displace(SkImageFilters::DisplacementMap(SkColorChannel::kR,
|
|
|
|
SkColorChannel::kB,
|
|
|
|
20.0f, nullptr, tiling));
|
|
|
|
SkIRect input(SkIRect::MakeXYWH(20, 30, 40, 50));
|
|
|
|
// Expected: union(floodBounds, outset(input, 10))
|
|
|
|
SkIRect expected(SkIRect::MakeXYWH(10, 20, 60, 70));
|
|
|
|
REPORTER_ASSERT(reporter,
|
|
|
|
expected == displace->filterBounds(input, SkMatrix::I(),
|
|
|
|
SkImageFilter::kReverse_MapDirection));
|
|
|
|
}
|
|
|
|
|
2017-09-25 17:26:40 +00:00
|
|
|
// Test SkImageSource::filterBounds.
|
|
|
|
DEF_TEST(ImageSourceBounds, reporter) {
|
2020-12-23 16:50:36 +00:00
|
|
|
sk_sp<SkImage> image(make_gradient_circle(64, 64).asImage());
|
2017-09-25 17:26:40 +00:00
|
|
|
// Default src and dst rects.
|
2019-08-05 14:41:10 +00:00
|
|
|
sk_sp<SkImageFilter> source1(SkImageFilters::Image(image));
|
2017-09-25 17:26:40 +00:00
|
|
|
SkIRect imageBounds = SkIRect::MakeWH(64, 64);
|
|
|
|
SkIRect input(SkIRect::MakeXYWH(10, 20, 30, 40));
|
|
|
|
REPORTER_ASSERT(reporter,
|
|
|
|
imageBounds == source1->filterBounds(input, SkMatrix::I(),
|
2018-05-17 15:17:39 +00:00
|
|
|
SkImageFilter::kForward_MapDirection,
|
|
|
|
nullptr));
|
2017-09-25 17:26:40 +00:00
|
|
|
REPORTER_ASSERT(reporter,
|
|
|
|
input == source1->filterBounds(input, SkMatrix::I(),
|
2018-05-17 15:17:39 +00:00
|
|
|
SkImageFilter::kReverse_MapDirection, &input));
|
2020-05-21 16:11:27 +00:00
|
|
|
SkMatrix scale(SkMatrix::Scale(2, 2));
|
2017-09-25 17:26:40 +00:00
|
|
|
SkIRect scaledBounds = SkIRect::MakeWH(128, 128);
|
|
|
|
REPORTER_ASSERT(reporter,
|
|
|
|
scaledBounds == source1->filterBounds(input, scale,
|
2018-05-17 15:17:39 +00:00
|
|
|
SkImageFilter::kForward_MapDirection,
|
|
|
|
nullptr));
|
|
|
|
REPORTER_ASSERT(reporter, input == source1->filterBounds(input, scale,
|
|
|
|
SkImageFilter::kReverse_MapDirection,
|
|
|
|
&input));
|
2017-09-25 17:26:40 +00:00
|
|
|
|
|
|
|
// Specified src and dst rects.
|
|
|
|
SkRect src(SkRect::MakeXYWH(0.5, 0.5, 100.5, 100.5));
|
|
|
|
SkRect dst(SkRect::MakeXYWH(-10.5, -10.5, 120.5, 120.5));
|
2021-02-14 02:04:58 +00:00
|
|
|
sk_sp<SkImageFilter> source2(SkImageFilters::Image(image, src, dst,
|
|
|
|
SkSamplingOptions(SkFilterMode::kLinear,
|
|
|
|
SkMipmapMode::kLinear)));
|
2017-09-25 17:26:40 +00:00
|
|
|
REPORTER_ASSERT(reporter,
|
|
|
|
dst.roundOut() == source2->filterBounds(input, SkMatrix::I(),
|
2018-05-17 15:17:39 +00:00
|
|
|
SkImageFilter::kForward_MapDirection,
|
|
|
|
nullptr));
|
2017-09-25 17:26:40 +00:00
|
|
|
REPORTER_ASSERT(reporter,
|
|
|
|
input == source2->filterBounds(input, SkMatrix::I(),
|
2018-05-17 15:17:39 +00:00
|
|
|
SkImageFilter::kReverse_MapDirection, &input));
|
2017-09-25 17:26:40 +00:00
|
|
|
scale.mapRect(&dst);
|
|
|
|
scale.mapRect(&src);
|
|
|
|
REPORTER_ASSERT(reporter,
|
|
|
|
dst.roundOut() == source2->filterBounds(input, scale,
|
2018-05-17 15:17:39 +00:00
|
|
|
SkImageFilter::kForward_MapDirection,
|
|
|
|
nullptr));
|
|
|
|
REPORTER_ASSERT(reporter, input == source2->filterBounds(input, scale,
|
|
|
|
SkImageFilter::kReverse_MapDirection,
|
|
|
|
&input));
|
2017-09-25 17:26:40 +00:00
|
|
|
}
|
2018-05-17 15:17:39 +00:00
|
|
|
|
2020-09-25 10:32:42 +00:00
|
|
|
// Test SkPictureImageFilter::filterBounds.
|
|
|
|
DEF_TEST(PictureImageSourceBounds, reporter) {
|
|
|
|
SkPictureRecorder recorder;
|
|
|
|
SkCanvas* recordingCanvas = recorder.beginRecording(64, 64);
|
|
|
|
|
|
|
|
SkPaint greenPaint;
|
|
|
|
greenPaint.setColor(SK_ColorGREEN);
|
|
|
|
recordingCanvas->drawRect(SkRect::Make(SkIRect::MakeXYWH(10, 10, 30, 20)), greenPaint);
|
|
|
|
sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
|
|
|
|
|
|
|
|
// Default target rect.
|
|
|
|
sk_sp<SkImageFilter> source1(SkImageFilters::Picture(picture));
|
|
|
|
SkIRect pictureBounds = SkIRect::MakeWH(64, 64);
|
|
|
|
SkIRect input(SkIRect::MakeXYWH(10, 20, 30, 40));
|
|
|
|
REPORTER_ASSERT(reporter,
|
|
|
|
pictureBounds == source1->filterBounds(input, SkMatrix::I(),
|
|
|
|
SkImageFilter::kForward_MapDirection,
|
|
|
|
nullptr));
|
|
|
|
REPORTER_ASSERT(reporter,
|
|
|
|
input == source1->filterBounds(input, SkMatrix::I(),
|
|
|
|
SkImageFilter::kReverse_MapDirection, &input));
|
|
|
|
SkMatrix scale(SkMatrix::Scale(2, 2));
|
|
|
|
SkIRect scaledPictureBounds = SkIRect::MakeWH(128, 128);
|
|
|
|
REPORTER_ASSERT(reporter,
|
|
|
|
scaledPictureBounds == source1->filterBounds(input, scale,
|
|
|
|
SkImageFilter::kForward_MapDirection,
|
|
|
|
nullptr));
|
|
|
|
REPORTER_ASSERT(reporter, input == source1->filterBounds(input, scale,
|
|
|
|
SkImageFilter::kReverse_MapDirection,
|
|
|
|
&input));
|
|
|
|
|
|
|
|
// Specified target rect.
|
|
|
|
SkRect targetRect(SkRect::MakeXYWH(9.5, 9.5, 31, 21));
|
|
|
|
sk_sp<SkImageFilter> source2(SkImageFilters::Picture(picture, targetRect));
|
|
|
|
REPORTER_ASSERT(reporter,
|
|
|
|
targetRect.roundOut() == source2->filterBounds(input, SkMatrix::I(),
|
|
|
|
SkImageFilter::kForward_MapDirection,
|
|
|
|
nullptr));
|
|
|
|
REPORTER_ASSERT(reporter,
|
|
|
|
input == source2->filterBounds(input, SkMatrix::I(),
|
|
|
|
SkImageFilter::kReverse_MapDirection, &input));
|
|
|
|
scale.mapRect(&targetRect);
|
|
|
|
REPORTER_ASSERT(reporter,
|
|
|
|
targetRect.roundOut() == source2->filterBounds(input, scale,
|
|
|
|
SkImageFilter::kForward_MapDirection,
|
|
|
|
nullptr));
|
|
|
|
REPORTER_ASSERT(reporter, input == source2->filterBounds(input, scale,
|
|
|
|
SkImageFilter::kReverse_MapDirection,
|
|
|
|
&input));
|
|
|
|
}
|