xref: /aosp_15_r20/external/skia/tests/RecordingXfermodeTest.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker  * Copyright 2014 Google Inc.
3*c8dee2aaSAndroid Build Coastguard Worker  *
4*c8dee2aaSAndroid Build Coastguard Worker  * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker  * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker  */
7*c8dee2aaSAndroid Build Coastguard Worker 
8*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkBBHFactory.h"
9*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkBitmap.h"
10*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkBlendMode.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkCanvas.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkImage.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkImageInfo.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPaint.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPicture.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPictureRecorder.h"
17*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkRect.h"
18*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkRefCnt.h"
19*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkSamplingOptions.h"
20*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkScalar.h"
21*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkString.h"
22*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkSurface.h"
23*c8dee2aaSAndroid Build Coastguard Worker #include "tests/Test.h"
24*c8dee2aaSAndroid Build Coastguard Worker 
25*c8dee2aaSAndroid Build Coastguard Worker #include <cstring>
26*c8dee2aaSAndroid Build Coastguard Worker 
27*c8dee2aaSAndroid Build Coastguard Worker // Verify that replay of a recording into a clipped canvas
28*c8dee2aaSAndroid Build Coastguard Worker // produces the correct bitmap.
29*c8dee2aaSAndroid Build Coastguard Worker // This arose from http://crbug.com/401593 which has
30*c8dee2aaSAndroid Build Coastguard Worker // https://code.google.com/p/skia/issues/detail?id=1291 as its root cause.
31*c8dee2aaSAndroid Build Coastguard Worker 
32*c8dee2aaSAndroid Build Coastguard Worker namespace {
33*c8dee2aaSAndroid Build Coastguard Worker 
34*c8dee2aaSAndroid Build Coastguard Worker class Drawer {
35*c8dee2aaSAndroid Build Coastguard Worker  public:
Drawer()36*c8dee2aaSAndroid Build Coastguard Worker     explicit Drawer() : fImageInfo(SkImageInfo::MakeN32Premul(200, 100)) {
37*c8dee2aaSAndroid Build Coastguard Worker         auto surf = SkSurfaces::Raster(SkImageInfo::MakeN32Premul(100, 100));
38*c8dee2aaSAndroid Build Coastguard Worker         surf->getCanvas()->clear(0xffffffff);
39*c8dee2aaSAndroid Build Coastguard Worker         SkPaint circlePaint;
40*c8dee2aaSAndroid Build Coastguard Worker         circlePaint.setColor(0xff000000);
41*c8dee2aaSAndroid Build Coastguard Worker         surf->getCanvas()->drawCircle(50, 50, 50, circlePaint);
42*c8dee2aaSAndroid Build Coastguard Worker         fCircleImage = surf->makeImageSnapshot();
43*c8dee2aaSAndroid Build Coastguard Worker     }
44*c8dee2aaSAndroid Build Coastguard Worker 
imageInfo() const45*c8dee2aaSAndroid Build Coastguard Worker     const SkImageInfo& imageInfo() const { return fImageInfo; }
46*c8dee2aaSAndroid Build Coastguard Worker 
draw(SkCanvas * canvas,const SkRect & clipRect,SkBlendMode mode) const47*c8dee2aaSAndroid Build Coastguard Worker     void draw(SkCanvas* canvas, const SkRect& clipRect, SkBlendMode mode) const {
48*c8dee2aaSAndroid Build Coastguard Worker         SkPaint greenPaint;
49*c8dee2aaSAndroid Build Coastguard Worker         greenPaint.setColor(0xff008000);
50*c8dee2aaSAndroid Build Coastguard Worker         SkPaint blackPaint;
51*c8dee2aaSAndroid Build Coastguard Worker         blackPaint.setColor(0xff000000);
52*c8dee2aaSAndroid Build Coastguard Worker         SkPaint whitePaint;
53*c8dee2aaSAndroid Build Coastguard Worker         whitePaint.setColor(0xffffffff);
54*c8dee2aaSAndroid Build Coastguard Worker         SkPaint layerPaint;
55*c8dee2aaSAndroid Build Coastguard Worker         layerPaint.setColor(0xff000000);
56*c8dee2aaSAndroid Build Coastguard Worker         layerPaint.setBlendMode(mode);
57*c8dee2aaSAndroid Build Coastguard Worker         SkRect canvasRect(SkRect::MakeWH(SkIntToScalar(fImageInfo.width()),
58*c8dee2aaSAndroid Build Coastguard Worker                                          SkIntToScalar(fImageInfo.height())));
59*c8dee2aaSAndroid Build Coastguard Worker 
60*c8dee2aaSAndroid Build Coastguard Worker         canvas->clipRect(clipRect);
61*c8dee2aaSAndroid Build Coastguard Worker         canvas->clear(0xff000000);
62*c8dee2aaSAndroid Build Coastguard Worker 
63*c8dee2aaSAndroid Build Coastguard Worker         canvas->saveLayer(nullptr, &blackPaint);
64*c8dee2aaSAndroid Build Coastguard Worker             canvas->drawRect(canvasRect, greenPaint);
65*c8dee2aaSAndroid Build Coastguard Worker             canvas->saveLayer(nullptr, &layerPaint);
66*c8dee2aaSAndroid Build Coastguard Worker                 canvas->drawImageRect(fCircleImage, SkRect::MakeXYWH(20,20,60,60),
67*c8dee2aaSAndroid Build Coastguard Worker                                       SkSamplingOptions(), &blackPaint);
68*c8dee2aaSAndroid Build Coastguard Worker             canvas->restore();
69*c8dee2aaSAndroid Build Coastguard Worker         canvas->restore();
70*c8dee2aaSAndroid Build Coastguard Worker     }
71*c8dee2aaSAndroid Build Coastguard Worker 
72*c8dee2aaSAndroid Build Coastguard Worker  private:
73*c8dee2aaSAndroid Build Coastguard Worker     const SkImageInfo fImageInfo;
74*c8dee2aaSAndroid Build Coastguard Worker     sk_sp<SkImage> fCircleImage;
75*c8dee2aaSAndroid Build Coastguard Worker };
76*c8dee2aaSAndroid Build Coastguard Worker 
77*c8dee2aaSAndroid Build Coastguard Worker class RecordingStrategy {
78*c8dee2aaSAndroid Build Coastguard Worker  public:
~RecordingStrategy()79*c8dee2aaSAndroid Build Coastguard Worker     virtual ~RecordingStrategy() {}
80*c8dee2aaSAndroid Build Coastguard Worker     virtual const SkBitmap& recordAndReplay(const Drawer& drawer,
81*c8dee2aaSAndroid Build Coastguard Worker                                             const SkRect& intoClip,
82*c8dee2aaSAndroid Build Coastguard Worker                                             SkBlendMode) = 0;
83*c8dee2aaSAndroid Build Coastguard Worker };
84*c8dee2aaSAndroid Build Coastguard Worker 
85*c8dee2aaSAndroid Build Coastguard Worker class BitmapBackedCanvasStrategy : public RecordingStrategy {
86*c8dee2aaSAndroid Build Coastguard Worker     // This version just draws into a bitmap-backed canvas.
87*c8dee2aaSAndroid Build Coastguard Worker  public:
BitmapBackedCanvasStrategy(const SkImageInfo & imageInfo)88*c8dee2aaSAndroid Build Coastguard Worker     BitmapBackedCanvasStrategy(const SkImageInfo& imageInfo) {
89*c8dee2aaSAndroid Build Coastguard Worker         fBitmap.allocPixels(imageInfo);
90*c8dee2aaSAndroid Build Coastguard Worker     }
91*c8dee2aaSAndroid Build Coastguard Worker 
recordAndReplay(const Drawer & drawer,const SkRect & intoClip,SkBlendMode mode)92*c8dee2aaSAndroid Build Coastguard Worker     const SkBitmap& recordAndReplay(const Drawer& drawer, const SkRect& intoClip,
93*c8dee2aaSAndroid Build Coastguard Worker                                     SkBlendMode mode) override {
94*c8dee2aaSAndroid Build Coastguard Worker         SkCanvas canvas(fBitmap);
95*c8dee2aaSAndroid Build Coastguard Worker         canvas.clear(0xffffffff);
96*c8dee2aaSAndroid Build Coastguard Worker         // Note that the scene is drawn just into the clipped region!
97*c8dee2aaSAndroid Build Coastguard Worker         canvas.clipRect(intoClip);
98*c8dee2aaSAndroid Build Coastguard Worker         drawer.draw(&canvas, intoClip, mode); // Shouild be canvas-wide...
99*c8dee2aaSAndroid Build Coastguard Worker         return fBitmap;
100*c8dee2aaSAndroid Build Coastguard Worker     }
101*c8dee2aaSAndroid Build Coastguard Worker 
102*c8dee2aaSAndroid Build Coastguard Worker  private:
103*c8dee2aaSAndroid Build Coastguard Worker     SkBitmap fBitmap;
104*c8dee2aaSAndroid Build Coastguard Worker };
105*c8dee2aaSAndroid Build Coastguard Worker 
106*c8dee2aaSAndroid Build Coastguard Worker class PictureStrategy : public RecordingStrategy {
107*c8dee2aaSAndroid Build Coastguard Worker     // This version draws the entire scene into an SkPictureRecorder.
108*c8dee2aaSAndroid Build Coastguard Worker     // Then it then replays the scene through a clip rectangle.
109*c8dee2aaSAndroid Build Coastguard Worker     // This backend proved to be buggy.
110*c8dee2aaSAndroid Build Coastguard Worker  public:
PictureStrategy(const SkImageInfo & imageInfo)111*c8dee2aaSAndroid Build Coastguard Worker     PictureStrategy(const SkImageInfo& imageInfo) {
112*c8dee2aaSAndroid Build Coastguard Worker         fBitmap.allocPixels(imageInfo);
113*c8dee2aaSAndroid Build Coastguard Worker         fWidth  = imageInfo.width();
114*c8dee2aaSAndroid Build Coastguard Worker         fHeight = imageInfo.height();
115*c8dee2aaSAndroid Build Coastguard Worker     }
116*c8dee2aaSAndroid Build Coastguard Worker 
recordAndReplay(const Drawer & drawer,const SkRect & intoClip,SkBlendMode mode)117*c8dee2aaSAndroid Build Coastguard Worker     const SkBitmap& recordAndReplay(const Drawer& drawer, const SkRect& intoClip,
118*c8dee2aaSAndroid Build Coastguard Worker                                     SkBlendMode mode) override {
119*c8dee2aaSAndroid Build Coastguard Worker         SkRTreeFactory factory;
120*c8dee2aaSAndroid Build Coastguard Worker         SkPictureRecorder recorder;
121*c8dee2aaSAndroid Build Coastguard Worker         SkRect canvasRect(SkRect::MakeWH(SkIntToScalar(fWidth),SkIntToScalar(fHeight)));
122*c8dee2aaSAndroid Build Coastguard Worker         SkCanvas* canvas = recorder.beginRecording(SkIntToScalar(fWidth),
123*c8dee2aaSAndroid Build Coastguard Worker                                                    SkIntToScalar(fHeight),
124*c8dee2aaSAndroid Build Coastguard Worker                                                    &factory);
125*c8dee2aaSAndroid Build Coastguard Worker         drawer.draw(canvas, canvasRect, mode);
126*c8dee2aaSAndroid Build Coastguard Worker         sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
127*c8dee2aaSAndroid Build Coastguard Worker 
128*c8dee2aaSAndroid Build Coastguard Worker         SkCanvas replayCanvas(fBitmap);
129*c8dee2aaSAndroid Build Coastguard Worker         replayCanvas.clear(0xffffffff);
130*c8dee2aaSAndroid Build Coastguard Worker         replayCanvas.clipRect(intoClip);
131*c8dee2aaSAndroid Build Coastguard Worker         picture->playback(&replayCanvas);
132*c8dee2aaSAndroid Build Coastguard Worker         return fBitmap;
133*c8dee2aaSAndroid Build Coastguard Worker     }
134*c8dee2aaSAndroid Build Coastguard Worker 
135*c8dee2aaSAndroid Build Coastguard Worker  private:
136*c8dee2aaSAndroid Build Coastguard Worker     SkBitmap fBitmap;
137*c8dee2aaSAndroid Build Coastguard Worker     int fWidth;
138*c8dee2aaSAndroid Build Coastguard Worker     int fHeight;
139*c8dee2aaSAndroid Build Coastguard Worker };
140*c8dee2aaSAndroid Build Coastguard Worker 
141*c8dee2aaSAndroid Build Coastguard Worker } // namespace
142*c8dee2aaSAndroid Build Coastguard Worker 
143*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(SkRecordingAccuracyXfermode,reporter)144*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(SkRecordingAccuracyXfermode, reporter) {
145*c8dee2aaSAndroid Build Coastguard Worker #define FINEGRAIN 0
146*c8dee2aaSAndroid Build Coastguard Worker     const Drawer drawer;
147*c8dee2aaSAndroid Build Coastguard Worker 
148*c8dee2aaSAndroid Build Coastguard Worker     BitmapBackedCanvasStrategy golden(drawer.imageInfo());
149*c8dee2aaSAndroid Build Coastguard Worker     PictureStrategy picture(drawer.imageInfo());
150*c8dee2aaSAndroid Build Coastguard Worker 
151*c8dee2aaSAndroid Build Coastguard Worker #if !FINEGRAIN
152*c8dee2aaSAndroid Build Coastguard Worker     unsigned numErrors = 0;
153*c8dee2aaSAndroid Build Coastguard Worker     SkString errors;
154*c8dee2aaSAndroid Build Coastguard Worker #endif
155*c8dee2aaSAndroid Build Coastguard Worker 
156*c8dee2aaSAndroid Build Coastguard Worker     for (int iMode = 0; iMode < kSkBlendModeCount; iMode++) {
157*c8dee2aaSAndroid Build Coastguard Worker         const SkRect& clip = SkRect::MakeXYWH(100, 0, 100, 100);
158*c8dee2aaSAndroid Build Coastguard Worker         SkBlendMode mode = SkBlendMode(iMode);
159*c8dee2aaSAndroid Build Coastguard Worker 
160*c8dee2aaSAndroid Build Coastguard Worker         const SkBitmap& goldenBM = golden.recordAndReplay(drawer, clip, mode);
161*c8dee2aaSAndroid Build Coastguard Worker         const SkBitmap& pictureBM = picture.recordAndReplay(drawer, clip, mode);
162*c8dee2aaSAndroid Build Coastguard Worker 
163*c8dee2aaSAndroid Build Coastguard Worker         size_t pixelsSize = goldenBM.computeByteSize();
164*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(reporter, pixelsSize == pictureBM.computeByteSize());
165*c8dee2aaSAndroid Build Coastguard Worker 
166*c8dee2aaSAndroid Build Coastguard Worker         // The pixel arrays should match.
167*c8dee2aaSAndroid Build Coastguard Worker #if FINEGRAIN
168*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(reporter,
169*c8dee2aaSAndroid Build Coastguard Worker                         0 == memcmp(goldenBM.getPixels(), pictureBM.getPixels(), pixelsSize));
170*c8dee2aaSAndroid Build Coastguard Worker #else
171*c8dee2aaSAndroid Build Coastguard Worker         if (0 != memcmp(goldenBM.getPixels(), pictureBM.getPixels(), pixelsSize)) {
172*c8dee2aaSAndroid Build Coastguard Worker             numErrors++;
173*c8dee2aaSAndroid Build Coastguard Worker             errors.appendf("For SkBlendMode %d %s:    SkPictureRecorder bitmap is wrong\n",
174*c8dee2aaSAndroid Build Coastguard Worker                            iMode, SkBlendMode_Name(mode));
175*c8dee2aaSAndroid Build Coastguard Worker         }
176*c8dee2aaSAndroid Build Coastguard Worker #endif
177*c8dee2aaSAndroid Build Coastguard Worker     }
178*c8dee2aaSAndroid Build Coastguard Worker 
179*c8dee2aaSAndroid Build Coastguard Worker #if !FINEGRAIN
180*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter, 0 == numErrors, "%s", errors.c_str());
181*c8dee2aaSAndroid Build Coastguard Worker #endif
182*c8dee2aaSAndroid Build Coastguard Worker }
183