1 /*
2  * Copyright 2011 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 "gm/gm.h"
9 #include "include/core/SkBitmap.h"
10 #include "include/core/SkBlendMode.h"
11 #include "include/core/SkCanvas.h"
12 #include "include/core/SkColor.h"
13 #include "include/core/SkColorFilter.h"
14 #include "include/core/SkImageFilter.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/SkSize.h"
22 #include "include/core/SkString.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/SkTableColorFilter.h"
28 
29 #include <math.h>
30 #include <utility>
31 
make_shader0(int w,int h)32 static sk_sp<SkShader> make_shader0(int w, int h) {
33     SkPoint pts[] = { {0, 0}, {SkIntToScalar(w), SkIntToScalar(h)} };
34     SkColor colors[] = {
35         SK_ColorBLACK, SK_ColorGREEN, SK_ColorCYAN,
36         SK_ColorRED, 0, SK_ColorBLUE, SK_ColorWHITE
37     };
38     return SkGradientShader::MakeLinear(pts, colors, nullptr, SK_ARRAY_COUNT(colors),
39                                         SkTileMode::kClamp);
40 }
make_bm0(SkBitmap * bm)41 static void make_bm0(SkBitmap* bm) {
42     int W = 120;
43     int H = 120;
44     bm->allocN32Pixels(W, H);
45     bm->eraseColor(SK_ColorTRANSPARENT);
46 
47     SkCanvas canvas(*bm);
48     SkPaint paint;
49     paint.setShader(make_shader0(W, H));
50     canvas.drawPaint(paint);
51 }
make_shader1(int w,int h)52 static sk_sp<SkShader> make_shader1(int w, int h) {
53     SkScalar cx = SkIntToScalar(w)/2;
54     SkScalar cy = SkIntToScalar(h)/2;
55     SkColor colors[] = {
56         SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE,
57     };
58     return SkGradientShader::MakeRadial(SkPoint::Make(cx, cy), cx, colors, nullptr,
59                                         SK_ARRAY_COUNT(colors), SkTileMode::kClamp);
60 }
make_bm1(SkBitmap * bm)61 static void make_bm1(SkBitmap* bm) {
62     int W = 120;
63     int H = 120;
64     SkScalar cx = SkIntToScalar(W)/2;
65     SkScalar cy = SkIntToScalar(H)/2;
66     bm->allocN32Pixels(W, H);
67     bm->eraseColor(SK_ColorTRANSPARENT);
68 
69     SkCanvas canvas(*bm);
70     SkPaint paint;
71     paint.setShader(make_shader1(W, H));
72     paint.setAntiAlias(true);
73     canvas.drawCircle(cx, cy, cx, paint);
74 }
75 
make_table0(uint8_t table[])76 static void make_table0(uint8_t table[]) {
77     for (int i = 0; i < 256; ++i) {
78         int n = i >> 5;
79         table[i] = (n << 5) | (n << 2) | (n >> 1);
80     }
81 }
make_table1(uint8_t table[])82 static void make_table1(uint8_t table[]) {
83     for (int i = 0; i < 256; ++i) {
84         table[i] = i * i / 255;
85     }
86 }
make_table2(uint8_t table[])87 static void make_table2(uint8_t table[]) {
88     for (int i = 0; i < 256; ++i) {
89         float fi = i / 255.0f;
90         table[i] = static_cast<uint8_t>(sqrtf(fi) * 255);
91     }
92 }
93 
make_null_cf()94 static sk_sp<SkColorFilter> make_null_cf() {
95     return nullptr;
96 }
97 
make_cf0()98 static sk_sp<SkColorFilter> make_cf0() {
99     uint8_t table[256]; make_table0(table);
100     return SkTableColorFilter::Make(table);
101 }
make_cf1()102 static sk_sp<SkColorFilter> make_cf1() {
103     uint8_t table[256]; make_table1(table);
104     return SkTableColorFilter::Make(table);
105 }
make_cf2()106 static sk_sp<SkColorFilter> make_cf2() {
107     uint8_t table[256]; make_table2(table);
108     return SkTableColorFilter::Make(table);
109 }
make_cf3()110 static sk_sp<SkColorFilter> make_cf3() {
111     uint8_t table0[256]; make_table0(table0);
112     uint8_t table1[256]; make_table1(table1);
113     uint8_t table2[256]; make_table2(table2);
114     return SkTableColorFilter::MakeARGB(nullptr, table0, table1, table2);
115 }
116 
117 class TableColorFilterGM : public skiagm::GM {
118 public:
TableColorFilterGM()119     TableColorFilterGM() {}
120 
121 protected:
onShortName()122     SkString onShortName() override {
123         return SkString("tablecolorfilter");
124     }
125 
onISize()126     SkISize onISize() override {
127         return {700, 1650};
128     }
129 
onDraw(SkCanvas * canvas)130     void onDraw(SkCanvas* canvas) override {
131         canvas->drawColor(0xFFDDDDDD);
132         canvas->translate(20, 20);
133 
134         static sk_sp<SkColorFilter> (*gColorFilterMakers[])() = {
135             make_null_cf, make_cf0, make_cf1, make_cf2, make_cf3
136         };
137         static void (*gBitmapMakers[])(SkBitmap*) = { make_bm0, make_bm1 };
138 
139         // This test will be done once for each bitmap with the results stacked vertically.
140         // For a single bitmap the resulting image will be the following:
141         //  - A first line with the original bitmap, followed by the image drawn once
142         //  with each of the N color filters
143         //  - N lines of the bitmap drawn N times, this will cover all N*N combinations of
144         //  pair of color filters in order to test the collapsing of consecutive table
145         //  color filters.
146         //
147         //  Here is a graphical representation of the result for 2 bitmaps and 2 filters
148         //  with the number corresponding to the number of filters the bitmap goes through:
149         //
150         //  --bitmap1
151         //  011
152         //  22
153         //  22
154         //  --bitmap2
155         //  011
156         //  22
157         //  22
158 
159         SkScalar x = 0, y = 0;
160         for (size_t bitmapMaker = 0; bitmapMaker < SK_ARRAY_COUNT(gBitmapMakers); ++bitmapMaker) {
161             SkBitmap bm;
162             gBitmapMakers[bitmapMaker](&bm);
163 
164             SkScalar xOffset = SkScalar(bm.width() * 9 / 8);
165             SkScalar yOffset = SkScalar(bm.height() * 9 / 8);
166 
167             // Draw the first element of the first line
168             x = 0;
169             SkPaint paint;
170             canvas->drawBitmap(bm, x, y, &paint);
171 
172             // Draws the rest of the first line for this bitmap
173             // each draw being at xOffset of the previous one
174             for (unsigned i = 1; i < SK_ARRAY_COUNT(gColorFilterMakers); ++i) {
175                 x += xOffset;
176                 paint.setColorFilter(gColorFilterMakers[i]());
177                 canvas->drawBitmap(bm, x, y, &paint);
178             }
179 
180             paint.setColorFilter(nullptr);
181 
182             for (unsigned i = 0; i < SK_ARRAY_COUNT(gColorFilterMakers); ++i) {
183                 sk_sp<SkColorFilter> colorFilter1(gColorFilterMakers[i]());
184                 sk_sp<SkImageFilter> imageFilter1(SkImageFilters::ColorFilter(
185                         std::move(colorFilter1), nullptr));
186 
187                 // Move down to the next line and draw it
188                 // each draw being at xOffset of the previous one
189                 y += yOffset;
190                 x = 0;
191                 for (unsigned j = 1; j < SK_ARRAY_COUNT(gColorFilterMakers); ++j) {
192                     sk_sp<SkColorFilter> colorFilter2(gColorFilterMakers[j]());
193                     sk_sp<SkImageFilter> imageFilter2(SkImageFilters::ColorFilter(
194                             std::move(colorFilter2), imageFilter1, nullptr));
195                     paint.setImageFilter(std::move(imageFilter2));
196                     canvas->drawBitmap(bm, x, y, &paint);
197                     x += xOffset;
198                 }
199             }
200 
201             // Move down one line to the beginning of the block for next bitmap
202             y += yOffset;
203         }
204     }
205 
206 private:
207     using INHERITED = GM;
208 };
209 DEF_GM( return new TableColorFilterGM; )
210 
211 //////////////////////////////////////////////////////////////////////////////
212 
213 class ComposeColorFilterGM : public skiagm::GM {
214     enum {
215         COLOR_COUNT = 3,
216         MODE_COUNT = 4,
217     };
218     const SkColor*      fColors;
219     const SkBlendMode*  fModes;
220     const char*         fName;
221 
222 public:
ComposeColorFilterGM(const SkColor colors[],const SkBlendMode modes[],const char * name)223     ComposeColorFilterGM(const SkColor colors[], const SkBlendMode modes[], const char* name)
224         : fColors(colors), fModes(modes), fName(name) {}
225 
226 private:
onShortName()227     SkString onShortName() override { return SkString(fName); }
228 
onISize()229     SkISize onISize() override { return {790, 790}; }
230 
onDraw(SkCanvas * canvas)231     void onDraw(SkCanvas* canvas) override {
232         SkBitmap bm;
233         make_bm1(&bm);
234 
235         canvas->drawColor(0xFFDDDDDD);
236 
237         const int MODES = MODE_COUNT * COLOR_COUNT;
238         sk_sp<SkColorFilter> filters[MODES];
239         int index = 0;
240         for (int i = 0; i < MODE_COUNT; ++i) {
241             for (int j = 0; j < COLOR_COUNT; ++j) {
242                 filters[index++] = SkColorFilters::Blend(fColors[j], fModes[i]);
243             }
244         }
245 
246         SkPaint paint;
247         paint.setShader(make_shader1(50, 50));
248         SkRect r = SkRect::MakeWH(50, 50);
249         const SkScalar spacer = 10;
250 
251         canvas->translate(spacer, spacer);
252 
253         canvas->drawRect(r, paint); // orig
254 
255         for (int i = 0; i < MODES; ++i) {
256             paint.setColorFilter(filters[i]);
257 
258             canvas->save();
259             canvas->translate((i + 1) * (r.width() + spacer), 0);
260             canvas->drawRect(r, paint);
261             canvas->restore();
262 
263             canvas->save();
264             canvas->translate(0, (i + 1) * (r.width() + spacer));
265             canvas->drawRect(r, paint);
266             canvas->restore();
267         }
268 
269         canvas->translate(r.width() + spacer, r.width() + spacer);
270 
271         for (int y = 0; y < MODES; ++y) {
272             canvas->save();
273             for (int x = 0; x < MODES; ++x) {
274                 paint.setColorFilter(filters[y]->makeComposed(filters[x]));
275                 canvas->drawRect(r, paint);
276                 canvas->translate(r.width() + spacer, 0);
277             }
278             canvas->restore();
279             canvas->translate(0, r.height() + spacer);
280         }
281     }
282 };
283 
284 const SkColor gColors0[] = { SK_ColorCYAN, SK_ColorMAGENTA, SK_ColorYELLOW };
285 const SkBlendMode gModes0[] = {
286     SkBlendMode::kOverlay,
287     SkBlendMode::kDarken,
288     SkBlendMode::kColorBurn,
289     SkBlendMode::kExclusion,
290 };
291 DEF_GM( return new ComposeColorFilterGM(gColors0, gModes0, "colorcomposefilter_wacky"); )
292 
293 const SkColor gColors1[] = { 0x80FF0000, 0x8000FF00, 0x800000FF };
294 const SkBlendMode gModes1[] = {
295     SkBlendMode::kSrcOver,
296     SkBlendMode::kXor,
297     SkBlendMode::kDstOut,
298     SkBlendMode::kSrcATop,
299 };
300 DEF_GM( return new ComposeColorFilterGM(gColors1, gModes1, "colorcomposefilter_alpha"); )
301