1/*
2 * Copyright 2013 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 "Test.h"
9#include "TestClassDef.h"
10#include "SkBitmapDevice.h"
11#include "SkCanvas.h"
12#include "SkCanvasStateUtils.h"
13#include "SkDrawFilter.h"
14#include "SkError.h"
15#include "SkPaint.h"
16#include "SkRect.h"
17#include "SkRRect.h"
18
19static void test_complex_layers(skiatest::Reporter* reporter) {
20    const int WIDTH = 400;
21    const int HEIGHT = 400;
22    const int SPACER = 10;
23
24    SkRect rect = SkRect::MakeXYWH(SkIntToScalar(SPACER), SkIntToScalar(SPACER),
25                                   SkIntToScalar(WIDTH-(2*SPACER)),
26                                   SkIntToScalar((HEIGHT-(2*SPACER)) / 7));
27
28    const SkBitmap::Config configs[] = { SkBitmap::kRGB_565_Config,
29                                         SkBitmap::kARGB_8888_Config
30    };
31    const int configCount = sizeof(configs) / sizeof(SkBitmap::Config);
32
33    const int layerAlpha[] = { 255, 255, 0 };
34    const SkCanvas::SaveFlags flags[] = { SkCanvas::kARGB_NoClipLayer_SaveFlag,
35                                          SkCanvas::kARGB_ClipLayer_SaveFlag,
36                                          SkCanvas::kARGB_NoClipLayer_SaveFlag
37    };
38    REPORTER_ASSERT(reporter, sizeof(layerAlpha) == sizeof(flags));
39    const int layerCombinations = sizeof(layerAlpha) / sizeof(int);
40
41    for (int i = 0; i < configCount; ++i) {
42        SkBitmap bitmaps[2];
43        for (int j = 0; j < 2; ++j) {
44            bitmaps[j].setConfig(configs[i], WIDTH, HEIGHT);
45            bitmaps[j].allocPixels();
46
47            SkCanvas canvas(bitmaps[j]);
48
49            canvas.drawColor(SK_ColorRED);
50
51            for (int k = 0; k < layerCombinations; ++k) {
52                // draw a rect within the layer's bounds and again outside the layer's bounds
53                canvas.saveLayerAlpha(&rect, layerAlpha[k], flags[k]);
54
55                SkCanvasState* state = NULL;
56                SkCanvas* tmpCanvas = NULL;
57                if (j) {
58                    state = SkCanvasStateUtils::CaptureCanvasState(&canvas);
59                    REPORTER_ASSERT(reporter, state);
60                    tmpCanvas = SkCanvasStateUtils::CreateFromCanvasState(state);
61                    REPORTER_ASSERT(reporter, tmpCanvas);
62                } else {
63                    tmpCanvas = SkRef(&canvas);
64                }
65
66                SkPaint bluePaint;
67                bluePaint.setColor(SK_ColorBLUE);
68                bluePaint.setStyle(SkPaint::kFill_Style);
69
70                tmpCanvas->drawRect(rect, bluePaint);
71                tmpCanvas->translate(0, rect.height() + SPACER);
72                tmpCanvas->drawRect(rect, bluePaint);
73
74                tmpCanvas->unref();
75                SkCanvasStateUtils::ReleaseCanvasState(state);
76
77                canvas.restore();
78
79                // translate the canvas for the next iteration
80                canvas.translate(0, 2*(rect.height() + SPACER));
81            }
82        }
83
84        // now we memcmp the two bitmaps
85        REPORTER_ASSERT(reporter, bitmaps[0].getSize() == bitmaps[1].getSize());
86        REPORTER_ASSERT(reporter, !memcmp(bitmaps[0].getPixels(),
87                                          bitmaps[1].getPixels(),
88                                          bitmaps[0].getSize()));
89    }
90}
91
92////////////////////////////////////////////////////////////////////////////////
93
94static void test_complex_clips(skiatest::Reporter* reporter) {
95
96    const int WIDTH = 400;
97    const int HEIGHT = 400;
98    const int SPACER = 10;
99
100    SkIRect layerRect = SkIRect::MakeWH(WIDTH, HEIGHT / 4);
101    layerRect.inset(2*SPACER, 2*SPACER);
102
103    SkIRect clipRect = layerRect;
104    clipRect.fRight = clipRect.fLeft + (clipRect.width() / 2) - (2*SPACER);
105    clipRect.outset(SPACER, SPACER);
106
107    SkIRect regionBounds = clipRect;
108    regionBounds.offset(clipRect.width() + (2*SPACER), 0);
109
110    SkIRect regionInterior = regionBounds;
111    regionInterior.inset(SPACER*3, SPACER*3);
112
113    SkRegion clipRegion;
114    clipRegion.setRect(regionBounds);
115    clipRegion.op(regionInterior, SkRegion::kDifference_Op);
116
117
118    const SkRegion::Op clipOps[] = { SkRegion::kIntersect_Op,
119                                     SkRegion::kIntersect_Op,
120                                     SkRegion::kReplace_Op,
121    };
122    const SkCanvas::SaveFlags flags[] = { SkCanvas::kARGB_NoClipLayer_SaveFlag,
123                                          SkCanvas::kARGB_ClipLayer_SaveFlag,
124                                          SkCanvas::kARGB_NoClipLayer_SaveFlag,
125    };
126    REPORTER_ASSERT(reporter, sizeof(clipOps) == sizeof(flags));
127    const int layerCombinations = sizeof(flags) / sizeof(SkCanvas::SaveFlags);
128
129    SkBitmap bitmaps[2];
130    for (int i = 0; i < 2; ++i) {
131        bitmaps[i].setConfig(SkBitmap::kARGB_8888_Config, WIDTH, HEIGHT);
132        bitmaps[i].allocPixels();
133
134        SkCanvas canvas(bitmaps[i]);
135
136        canvas.drawColor(SK_ColorRED);
137
138        SkRegion localRegion = clipRegion;
139
140        for (int j = 0; j < layerCombinations; ++j) {
141            SkRect layerBounds = SkRect::Make(layerRect);
142            canvas.saveLayerAlpha(&layerBounds, 128, flags[j]);
143
144            SkCanvasState* state = NULL;
145            SkCanvas* tmpCanvas = NULL;
146            if (i) {
147                state = SkCanvasStateUtils::CaptureCanvasState(&canvas);
148                REPORTER_ASSERT(reporter, state);
149                tmpCanvas = SkCanvasStateUtils::CreateFromCanvasState(state);
150                REPORTER_ASSERT(reporter, tmpCanvas);
151            } else {
152                tmpCanvas = SkRef(&canvas);
153            }
154
155            tmpCanvas->save();
156            tmpCanvas->clipRect(SkRect::Make(clipRect), clipOps[j]);
157            tmpCanvas->drawColor(SK_ColorBLUE);
158            tmpCanvas->restore();
159
160            tmpCanvas->clipRegion(localRegion, clipOps[j]);
161            tmpCanvas->drawColor(SK_ColorBLUE);
162
163            tmpCanvas->unref();
164            SkCanvasStateUtils::ReleaseCanvasState(state);
165
166            canvas.restore();
167
168            // translate the canvas and region for the next iteration
169            canvas.translate(0, SkIntToScalar(2*(layerRect.height() + (SPACER))));
170            localRegion.translate(0, 2*(layerRect.height() + SPACER));
171        }
172    }
173
174    // now we memcmp the two bitmaps
175    REPORTER_ASSERT(reporter, bitmaps[0].getSize() == bitmaps[1].getSize());
176    REPORTER_ASSERT(reporter, !memcmp(bitmaps[0].getPixels(),
177                                      bitmaps[1].getPixels(),
178                                      bitmaps[0].getSize()));
179}
180
181////////////////////////////////////////////////////////////////////////////////
182
183class TestDrawFilter : public SkDrawFilter {
184public:
185    virtual bool filter(SkPaint*, Type) SK_OVERRIDE { return true; }
186};
187
188static void test_draw_filters(skiatest::Reporter* reporter) {
189    TestDrawFilter drawFilter;
190    SkBitmapDevice device(SkBitmap::kARGB_8888_Config, 10, 10);
191    SkCanvas canvas(&device);
192
193    canvas.setDrawFilter(&drawFilter);
194
195    SkCanvasState* state = SkCanvasStateUtils::CaptureCanvasState(&canvas);
196    REPORTER_ASSERT(reporter, state);
197    SkCanvas* tmpCanvas = SkCanvasStateUtils::CreateFromCanvasState(state);
198    REPORTER_ASSERT(reporter, tmpCanvas);
199
200    REPORTER_ASSERT(reporter, NULL != canvas.getDrawFilter());
201    REPORTER_ASSERT(reporter, NULL == tmpCanvas->getDrawFilter());
202
203    tmpCanvas->unref();
204    SkCanvasStateUtils::ReleaseCanvasState(state);
205}
206
207////////////////////////////////////////////////////////////////////////////////
208
209// we need this function to prevent SkError from printing to stdout
210static void error_callback(SkError code, void* ctx) {}
211
212static void test_soft_clips(skiatest::Reporter* reporter) {
213    SkBitmapDevice device(SkBitmap::kARGB_8888_Config, 10, 10);
214    SkCanvas canvas(&device);
215
216    SkRRect roundRect;
217    roundRect.setOval(SkRect::MakeWH(5, 5));
218
219    canvas.clipRRect(roundRect, SkRegion::kIntersect_Op, true);
220
221    SkSetErrorCallback(error_callback, NULL);
222
223    SkCanvasState* state = SkCanvasStateUtils::CaptureCanvasState(&canvas);
224    REPORTER_ASSERT(reporter, !state);
225
226    REPORTER_ASSERT(reporter, kInvalidOperation_SkError == SkGetLastError());
227    SkClearLastError();
228}
229
230DEF_TEST(CanvasState, reporter) {
231    test_complex_layers(reporter);
232    test_complex_clips(reporter);
233    test_draw_filters(reporter);
234    test_soft_clips(reporter);
235}
236