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 "SkBitmap.h"
9#include "SkBitmapDevice.h"
10#include "SkCanvas.h"
11#include "SkDraw.h"
12#include "SkLayerDrawLooper.h"
13#include "SkMatrix.h"
14#include "SkPaint.h"
15#include "SkRect.h"
16#include "SkRefCnt.h"
17#include "SkScalar.h"
18#include "SkSmallAllocator.h"
19#include "SkXfermode.h"
20#include "Test.h"
21
22static SkBitmap make_bm(int w, int h) {
23    SkBitmap bm;
24    bm.allocN32Pixels(w, h);
25    return bm;
26}
27
28class FakeDevice : public SkBitmapDevice {
29public:
30    FakeDevice() : SkBitmapDevice(make_bm(100, 100)) { }
31
32    virtual void drawRect(const SkDraw& draw, const SkRect& r,
33                          const SkPaint& paint) SK_OVERRIDE {
34        fLastMatrix = *draw.fMatrix;
35        this->INHERITED::drawRect(draw, r, paint);
36    }
37
38    SkMatrix fLastMatrix;
39
40private:
41    typedef SkBitmapDevice INHERITED;
42};
43
44static void test_frontToBack(skiatest::Reporter* reporter) {
45    SkLayerDrawLooper::Builder looperBuilder;
46    SkLayerDrawLooper::LayerInfo layerInfo;
47
48    // Add the front layer, with the defaults.
49    (void)looperBuilder.addLayer(layerInfo);
50
51    // Add the back layer, with some layer info set.
52    layerInfo.fOffset.set(10.0f, 20.0f);
53    layerInfo.fPaintBits |= SkLayerDrawLooper::kXfermode_Bit;
54    SkPaint* layerPaint = looperBuilder.addLayer(layerInfo);
55    layerPaint->setXfermodeMode(SkXfermode::kSrc_Mode);
56
57    FakeDevice device;
58    SkCanvas canvas(&device);
59    SkPaint paint;
60    SkAutoTUnref<SkLayerDrawLooper> looper(looperBuilder.detachLooper());
61    SkSmallAllocator<1, 32> allocator;
62    void* buffer = allocator.reserveT<SkDrawLooper::Context>(looper->contextSize());
63    SkDrawLooper::Context* context = looper->createContext(&canvas, buffer);
64
65    // The back layer should come first.
66    REPORTER_ASSERT(reporter, context->next(&canvas, &paint));
67    REPORTER_ASSERT(reporter, SkXfermode::IsMode(paint.getXfermode(), SkXfermode::kSrc_Mode));
68    canvas.drawRect(SkRect::MakeWH(50.0f, 50.0f), paint);
69    REPORTER_ASSERT(reporter, 10.0f == device.fLastMatrix.getTranslateX());
70    REPORTER_ASSERT(reporter, 20.0f == device.fLastMatrix.getTranslateY());
71    paint.reset();
72
73    // Then the front layer.
74    REPORTER_ASSERT(reporter, context->next(&canvas, &paint));
75    REPORTER_ASSERT(reporter, SkXfermode::IsMode(paint.getXfermode(), SkXfermode::kSrcOver_Mode));
76    canvas.drawRect(SkRect::MakeWH(50.0f, 50.0f), paint);
77    REPORTER_ASSERT(reporter, 0.0f == device.fLastMatrix.getTranslateX());
78    REPORTER_ASSERT(reporter, 0.0f == device.fLastMatrix.getTranslateY());
79
80    // Only two layers were added, so that should be the end.
81    REPORTER_ASSERT(reporter, !context->next(&canvas, &paint));
82}
83
84static void test_backToFront(skiatest::Reporter* reporter) {
85    SkLayerDrawLooper::Builder looperBuilder;
86    SkLayerDrawLooper::LayerInfo layerInfo;
87
88    // Add the back layer, with the defaults.
89    (void)looperBuilder.addLayerOnTop(layerInfo);
90
91    // Add the front layer, with some layer info set.
92    layerInfo.fOffset.set(10.0f, 20.0f);
93    layerInfo.fPaintBits |= SkLayerDrawLooper::kXfermode_Bit;
94    SkPaint* layerPaint = looperBuilder.addLayerOnTop(layerInfo);
95    layerPaint->setXfermodeMode(SkXfermode::kSrc_Mode);
96
97    FakeDevice device;
98    SkCanvas canvas(&device);
99    SkPaint paint;
100    SkAutoTUnref<SkLayerDrawLooper> looper(looperBuilder.detachLooper());
101    SkSmallAllocator<1, 32> allocator;
102    void* buffer = allocator.reserveT<SkDrawLooper::Context>(looper->contextSize());
103    SkDrawLooper::Context* context = looper->createContext(&canvas, buffer);
104
105    // The back layer should come first.
106    REPORTER_ASSERT(reporter, context->next(&canvas, &paint));
107    REPORTER_ASSERT(reporter, SkXfermode::IsMode(paint.getXfermode(), SkXfermode::kSrcOver_Mode));
108    canvas.drawRect(SkRect::MakeWH(50.0f, 50.0f), paint);
109    REPORTER_ASSERT(reporter, 0.0f == device.fLastMatrix.getTranslateX());
110    REPORTER_ASSERT(reporter, 0.0f == device.fLastMatrix.getTranslateY());
111    paint.reset();
112
113    // Then the front layer.
114    REPORTER_ASSERT(reporter, context->next(&canvas, &paint));
115    REPORTER_ASSERT(reporter, SkXfermode::IsMode(paint.getXfermode(), SkXfermode::kSrc_Mode));
116    canvas.drawRect(SkRect::MakeWH(50.0f, 50.0f), paint);
117    REPORTER_ASSERT(reporter, 10.0f == device.fLastMatrix.getTranslateX());
118    REPORTER_ASSERT(reporter, 20.0f == device.fLastMatrix.getTranslateY());
119
120    // Only two layers were added, so that should be the end.
121    REPORTER_ASSERT(reporter, !context->next(&canvas, &paint));
122}
123
124static void test_mixed(skiatest::Reporter* reporter) {
125    SkLayerDrawLooper::Builder looperBuilder;
126    SkLayerDrawLooper::LayerInfo layerInfo;
127
128    // Add the back layer, with the defaults.
129    (void)looperBuilder.addLayer(layerInfo);
130
131    // Add the front layer, with some layer info set.
132    layerInfo.fOffset.set(10.0f, 20.0f);
133    layerInfo.fPaintBits |= SkLayerDrawLooper::kXfermode_Bit;
134    SkPaint* layerPaint = looperBuilder.addLayerOnTop(layerInfo);
135    layerPaint->setXfermodeMode(SkXfermode::kSrc_Mode);
136
137    FakeDevice device;
138    SkCanvas canvas(&device);
139    SkPaint paint;
140    SkAutoTUnref<SkLayerDrawLooper> looper(looperBuilder.detachLooper());
141    SkSmallAllocator<1, 32> allocator;
142    void* buffer = allocator.reserveT<SkDrawLooper::Context>(looper->contextSize());
143    SkDrawLooper::Context* context = looper->createContext(&canvas, buffer);
144
145    // The back layer should come first.
146    REPORTER_ASSERT(reporter, context->next(&canvas, &paint));
147    REPORTER_ASSERT(reporter, SkXfermode::IsMode(paint.getXfermode(), SkXfermode::kSrcOver_Mode));
148    canvas.drawRect(SkRect::MakeWH(50.0f, 50.0f), paint);
149    REPORTER_ASSERT(reporter, 0.0f == device.fLastMatrix.getTranslateX());
150    REPORTER_ASSERT(reporter, 0.0f == device.fLastMatrix.getTranslateY());
151    paint.reset();
152
153    // Then the front layer.
154    REPORTER_ASSERT(reporter, context->next(&canvas, &paint));
155    REPORTER_ASSERT(reporter, SkXfermode::IsMode(paint.getXfermode(), SkXfermode::kSrc_Mode));
156    canvas.drawRect(SkRect::MakeWH(50.0f, 50.0f), paint);
157    REPORTER_ASSERT(reporter, 10.0f == device.fLastMatrix.getTranslateX());
158    REPORTER_ASSERT(reporter, 20.0f == device.fLastMatrix.getTranslateY());
159
160    // Only two layers were added, so that should be the end.
161    REPORTER_ASSERT(reporter, !context->next(&canvas, &paint));
162}
163
164DEF_TEST(LayerDrawLooper, reporter) {
165    test_frontToBack(reporter);
166    test_backToFront(reporter);
167    test_mixed(reporter);
168}
169