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.h"
9#include "sk_tool_utils.h"
10#include "SkArithmeticImageFilter.h"
11#include "SkCanvas.h"
12#include "SkColorPriv.h"
13#include "SkGradientShader.h"
14#include "SkImage.h"
15#include "SkImageSource.h"
16#include "SkShader.h"
17#include "SkSurface.h"
18
19#define WW  100
20#define HH  32
21
22static sk_sp<SkImage> make_src() {
23    sk_sp<SkSurface> surface(SkSurface::MakeRasterN32Premul(WW, HH));
24    SkCanvas* canvas = surface->getCanvas();
25
26    SkPaint paint;
27    SkPoint pts[] = { {0, 0}, {SkIntToScalar(WW), SkIntToScalar(HH)} };
28    SkColor colors[] = {
29        SK_ColorTRANSPARENT, SK_ColorGREEN, SK_ColorCYAN,
30        SK_ColorRED, SK_ColorMAGENTA, SK_ColorWHITE,
31    };
32    paint.setShader(SkGradientShader::MakeLinear(pts, colors, nullptr, SK_ARRAY_COUNT(colors),
33                                                 SkShader::kClamp_TileMode));
34    canvas->drawPaint(paint);
35    return surface->makeImageSnapshot();
36}
37
38static sk_sp<SkImage> make_dst() {
39    sk_sp<SkSurface> surface(SkSurface::MakeRasterN32Premul(WW, HH));
40    SkCanvas* canvas = surface->getCanvas();
41
42    SkPaint paint;
43    SkPoint pts[] = { {0, SkIntToScalar(HH)}, {SkIntToScalar(WW), 0} };
44    SkColor colors[] = {
45        SK_ColorBLUE, SK_ColorYELLOW, SK_ColorBLACK, SK_ColorGREEN,
46        sk_tool_utils::color_to_565(SK_ColorGRAY)
47    };
48    paint.setShader(SkGradientShader::MakeLinear(pts, colors, nullptr, SK_ARRAY_COUNT(colors),
49                                                 SkShader::kClamp_TileMode));
50    canvas->drawPaint(paint);
51    return surface->makeImageSnapshot();
52}
53
54static void show_k_text(SkCanvas* canvas, SkScalar x, SkScalar y, const SkScalar k[]) {
55    SkPaint paint;
56    paint.setTextSize(SkIntToScalar(24));
57    paint.setAntiAlias(true);
58    sk_tool_utils::set_portable_typeface(&paint);
59    for (int i = 0; i < 4; ++i) {
60        SkString str;
61        str.appendScalar(k[i]);
62        SkScalar width = paint.measureText(str.c_str(), str.size());
63        canvas->drawText(str.c_str(), str.size(), x, y + paint.getTextSize(), paint);
64        x += width + SkIntToScalar(10);
65    }
66}
67
68class ArithmodeGM : public skiagm::GM {
69public:
70    ArithmodeGM () {}
71
72protected:
73
74    virtual SkString onShortName() {
75        return SkString("arithmode");
76    }
77
78    virtual SkISize onISize() { return SkISize::Make(640, 572); }
79
80    virtual void onDraw(SkCanvas* canvas) {
81        sk_sp<SkImage> src = make_src();
82        sk_sp<SkImage> dst = make_dst();
83        sk_sp<SkImageFilter> srcFilter = SkImageSource::Make(src);
84        sk_sp<SkImageFilter> dstFilter = SkImageSource::Make(dst);
85
86        constexpr SkScalar one = SK_Scalar1;
87        constexpr SkScalar K[] = {
88            0, 0, 0, 0,
89            0, 0, 0, one,
90            0, one, 0, 0,
91            0, 0, one, 0,
92            0, one, one, 0,
93            0, one, -one, 0,
94            0, one/2, one/2, 0,
95            0, one/2, one/2, one/4,
96            0, one/2, one/2, -one/4,
97            one/4, one/2, one/2, 0,
98            -one/4, one/2, one/2, 0,
99        };
100
101        const SkScalar* k = K;
102        const SkScalar* stop = k + SK_ARRAY_COUNT(K);
103        const SkRect rect = SkRect::MakeWH(WW, HH);
104        SkScalar gap = SkIntToScalar(WW + 20);
105        while (k < stop) {
106            {
107                SkAutoCanvasRestore acr(canvas, true);
108                canvas->drawImage(src, 0, 0);
109                canvas->translate(gap, 0);
110                canvas->drawImage(dst, 0, 0);
111                canvas->translate(gap, 0);
112                SkPaint paint;
113                paint.setImageFilter(SkArithmeticImageFilter::Make(k[0], k[1], k[2], k[3], true,
114                                                                   dstFilter, srcFilter, nullptr));
115                canvas->saveLayer(&rect, &paint);
116                canvas->restore();
117
118                canvas->translate(gap, 0);
119                show_k_text(canvas, 0, 0, k);
120            }
121
122            k += 4;
123            canvas->translate(0, HH + 12);
124        }
125
126        // Draw two special cases to test enforcePMColor. In these cases, we
127        // draw the dst bitmap twice, the first time it is halved and inverted,
128        // leading to invalid premultiplied colors. If we enforcePMColor, these
129        // invalid values should be clamped, and will not contribute to the
130        // second draw.
131        for (int i = 0; i < 2; i++) {
132            const bool enforcePMColor = (i == 0);
133
134            {
135                SkAutoCanvasRestore acr(canvas, true);
136                canvas->translate(gap, 0);
137                canvas->drawImage(dst, 0, 0);
138                canvas->translate(gap, 0);
139
140                sk_sp<SkImageFilter> bg =
141                        SkArithmeticImageFilter::Make(0, 0, -one / 2, 1, enforcePMColor, dstFilter);
142                SkPaint p;
143                p.setImageFilter(SkArithmeticImageFilter::Make(0, one / 2, -one, 1, true,
144                                                               std::move(bg), dstFilter, nullptr));
145                canvas->saveLayer(&rect, &p);
146                canvas->restore();
147                canvas->translate(gap, 0);
148
149                // Label
150                SkPaint paint;
151                paint.setTextSize(SkIntToScalar(24));
152                paint.setAntiAlias(true);
153                sk_tool_utils::set_portable_typeface(&paint);
154                SkString str(enforcePMColor ? "enforcePM" : "no enforcePM");
155                canvas->drawText(str.c_str(), str.size(), 0, paint.getTextSize(), paint);
156            }
157            canvas->translate(0, HH + 12);
158        }
159    }
160
161private:
162    typedef GM INHERITED;
163};
164
165///////////////////////////////////////////////////////////////////////////////
166
167DEF_GM( return new ArithmodeGM; )
168