1 /*
2  * Copyright 2016 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "include/core/SkBitmap.h"
9 #include "include/core/SkBlendMode.h"
10 #include "include/core/SkCanvas.h"
11 #include "include/core/SkColor.h"
12 #include "include/core/SkData.h"
13 #include "include/core/SkImageFilter.h"
14 #include "include/core/SkImageInfo.h"
15 #include "include/core/SkPaint.h"
16 #include "include/core/SkPoint.h"
17 #include "include/core/SkRect.h"
18 #include "include/core/SkRefCnt.h"
19 #include "include/core/SkScalar.h"
20 #include "include/core/SkShader.h"
21 #include "include/core/SkString.h"
22 #include "include/core/SkSurface.h"
23 #include "include/core/SkTileMode.h"
24 #include "include/core/SkTypes.h"
25 #include "include/effects/SkGradientShader.h"
26 #include "include/effects/SkImageFilters.h"
27 #include "include/effects/SkRuntimeEffect.h"
28 #include "include/gpu/GpuTypes.h"
29 #include "include/gpu/GrDirectContext.h"
30 #include "src/effects/imagefilters/SkRuntimeImageFilter.h"
31 #include "tests/CtsEnforcement.h"
32 #include "tests/Test.h"
33 
34 #include <vector>
35 
36 struct GrContextOptions;
37 
test_unscaled(skiatest::Reporter * reporter)38 static void test_unscaled(skiatest::Reporter* reporter) {
39     static const int kWidth = 10;
40     static const int kHeight = 10;
41 
42     SkIRect ir = SkIRect::MakeWH(kWidth, kHeight);
43 
44     SkBitmap filterResult, paintResult;
45 
46     filterResult.allocN32Pixels(kWidth, kHeight);
47     SkCanvas canvasFilter(filterResult);
48     canvasFilter.clear(0x00000000);
49 
50     paintResult.allocN32Pixels(kWidth, kHeight);
51     SkCanvas canvasPaint(paintResult);
52     canvasPaint.clear(0x00000000);
53 
54     SkPoint center = SkPoint::Make(SkIntToScalar(5), SkIntToScalar(5));
55     SkColor colors[] = {SK_ColorBLUE, SK_ColorRED, SK_ColorGREEN};
56     SkScalar pos[] = {0, SK_ScalarHalf, SK_Scalar1};
57     SkScalar radius = SkIntToScalar(5);
58 
59     sk_sp<SkShader> gradient = SkGradientShader::MakeRadial(
60             center, radius, colors, pos, std::size(colors), SkTileMode::kClamp);
61 
62     // Test using the image filter
63     {
64         SkPaint paint;
65         paint.setImageFilter(SkImageFilters::Shader(gradient, &ir));
66         canvasFilter.drawRect(SkRect::Make(ir), paint);
67     }
68 
69     // Test using the paint directly
70     {
71         SkPaint paint;
72         paint.setShader(gradient);
73         canvasPaint.drawRect(SkRect::Make(ir), paint);
74     }
75 
76     // Assert that both paths yielded the same result
77     for (int y = 0; y < kHeight; ++y) {
78         const SkPMColor* filterPtr = filterResult.getAddr32(0, y);
79         const SkPMColor* paintPtr = paintResult.getAddr32(0, y);
80         for (int x = 0; x < kWidth; ++x, ++filterPtr, ++paintPtr) {
81             REPORTER_ASSERT(reporter, *filterPtr == *paintPtr);
82         }
83     }
84 }
85 
test_scaled(skiatest::Reporter * reporter)86 static void test_scaled(skiatest::Reporter* reporter) {
87     static const int kWidth = 10;
88     static const int kHeight = 10;
89 
90     SkIRect ir = SkIRect::MakeWH(kWidth, kHeight);
91 
92     SkBitmap filterResult, paintResult;
93 
94     filterResult.allocN32Pixels(kWidth, kHeight);
95     SkCanvas canvasFilter(filterResult);
96     canvasFilter.clear(0x00000000);
97 
98     paintResult.allocN32Pixels(kWidth, kHeight);
99     SkCanvas canvasPaint(paintResult);
100     canvasPaint.clear(0x00000000);
101 
102     SkPoint center = SkPoint::Make(SkIntToScalar(5), SkIntToScalar(5));
103     SkColor colors[] = {SK_ColorBLUE, SK_ColorRED, SK_ColorGREEN};
104     SkScalar pos[] = {0, SK_ScalarHalf, SK_Scalar1};
105     SkScalar radius = SkIntToScalar(5);
106 
107     sk_sp<SkShader> gradient = SkGradientShader::MakeRadial(
108         center, radius, colors, pos, std::size(colors), SkTileMode::kClamp);
109 
110     // Test using the image filter
111     {
112         SkPaint paint;
113         paint.setImageFilter(SkImageFilters::Shader(gradient, &ir));
114         canvasFilter.scale(SkIntToScalar(2), SkIntToScalar(2));
115         canvasFilter.drawRect(SkRect::Make(ir), paint);
116     }
117 
118     // Test using the paint directly
119     {
120         SkPaint paint;
121         paint.setShader(gradient);
122         canvasPaint.scale(SkIntToScalar(2), SkIntToScalar(2));
123         canvasPaint.drawRect(SkRect::Make(ir), paint);
124     }
125 
126     // Assert that both paths yielded the same result
127     for (int y = 0; y < kHeight; ++y) {
128         const SkPMColor* filterPtr = filterResult.getAddr32(0, y);
129         const SkPMColor* paintPtr = paintResult.getAddr32(0, y);
130         for (int x = 0; x < kWidth; ++x, ++filterPtr, ++paintPtr) {
131             REPORTER_ASSERT(reporter, *filterPtr == *paintPtr);
132         }
133     }
134 }
135 
DEF_TEST(ShaderImageFilter,reporter)136 DEF_TEST(ShaderImageFilter, reporter) {
137     test_unscaled(reporter);
138     test_scaled(reporter);
139 }
140 
test_runtime_shader(skiatest::Reporter * r,SkSurface * surface)141 static void test_runtime_shader(skiatest::Reporter* r, SkSurface* surface) {
142     sk_sp<SkRuntimeEffect> effect = SkRuntimeEffect::MakeForShader(SkString(R"(
143         uniform shader child;
144         vec4 main(vec2 coord) {
145             return child.eval(coord) * 0.5;
146         }
147     )"))
148                                             .effect;
149     SkRuntimeShaderBuilder builder(effect);
150 
151     // create a red image filter to feed as input into the SkImageFilters::RuntimeShader
152     sk_sp<SkImageFilter> input = SkImageFilters::Shader(SkShaders::Color(SK_ColorRED));
153 
154     // Create the different variations of SkImageFilters::RuntimeShader
155     // All 3 variations should produce the same pixel output
156     std::vector<sk_sp<SkImageFilter>> filters = {
157             SkMakeRuntimeImageFilter(effect, /*uniforms=*/nullptr, input),
158             SkImageFilters::RuntimeShader(builder, /*childShaderName=*/"", input),
159             SkImageFilters::RuntimeShader(builder, /*childShaderName=*/"child", input)};
160 
161     for (auto&& filter : filters) {
162         auto canvas = surface->getCanvas();
163 
164         // clear to transparent
165         SkPaint paint;
166         paint.setColor(SK_ColorTRANSPARENT);
167         paint.setBlendMode(SkBlendMode::kSrc);
168         canvas->drawPaint(paint);
169 
170         SkPaint filterPaint;
171         // the green color will be ignored by the filter within the runtime shader
172         filterPaint.setColor(SK_ColorGREEN);
173         filterPaint.setImageFilter(filter);
174         canvas->saveLayer(nullptr, &filterPaint);
175         // the blue color will be ignored by the filter because the input to the image filter is not
176         // null
177         canvas->drawColor(SK_ColorBLUE);
178         canvas->restore();
179 
180         // This is expected to read back the half transparent red pixel produced by the image filter
181         SkBitmap bitmap;
182         REPORTER_ASSERT(r, bitmap.tryAllocPixels(surface->imageInfo()));
183         REPORTER_ASSERT(r,
184                         surface->readPixels(bitmap.info(),
185                                             bitmap.getPixels(),
186                                             bitmap.rowBytes(),
187                                             /*srcX=*/0,
188                                             /*srcY=*/0));
189         SkColor color = bitmap.getColor(/*x=*/0, /*y=*/0);
190 
191         // check alpha with a small tolerance
192         SkAlpha alpha = SkColorGetA(color);
193         REPORTER_ASSERT(r, alpha >= 127 && alpha <= 129, "Expected: %d Actual: %d", 128, alpha);
194 
195         // check each color channel
196         color = SkColorSetA(color, 255);
197         REPORTER_ASSERT(r, SK_ColorRED == color, "Expected: %08x Actual: %08x", SK_ColorRED, color);
198     }
199 }
200 
DEF_TEST(SkRuntimeShaderImageFilter_CPU,r)201 DEF_TEST(SkRuntimeShaderImageFilter_CPU, r) {
202     const SkImageInfo info = SkImageInfo::MakeN32Premul(/*width=*/1, /*height=*/1);
203     sk_sp<SkSurface> surface(SkSurface::MakeRaster(info));
204     test_runtime_shader(r, surface.get());
205 }
206 
DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(SkRuntimeShaderImageFilter_GPU,r,ctxInfo,CtsEnforcement::kApiLevel_T)207 DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(SkRuntimeShaderImageFilter_GPU,
208                                        r,
209                                        ctxInfo,
210                                        CtsEnforcement::kApiLevel_T) {
211     const SkImageInfo info = SkImageInfo::MakeN32Premul(/*width=*/1, /*height=*/1);
212     sk_sp<SkSurface> surface(
213             SkSurface::MakeRenderTarget(ctxInfo.directContext(), skgpu::Budgeted::kNo, info));
214     test_runtime_shader(r, surface.get());
215 }
216