1327f905d2cb0d37c302d651d8f2b17ea56368467dneto/*
2327f905d2cb0d37c302d651d8f2b17ea56368467dneto * Copyright 2014 Google Inc.
3327f905d2cb0d37c302d651d8f2b17ea56368467dneto *
4327f905d2cb0d37c302d651d8f2b17ea56368467dneto * Use of this source code is governed by a BSD-style license that can be
5327f905d2cb0d37c302d651d8f2b17ea56368467dneto * found in the LICENSE file.
6327f905d2cb0d37c302d651d8f2b17ea56368467dneto */
7327f905d2cb0d37c302d651d8f2b17ea56368467dneto
8327f905d2cb0d37c302d651d8f2b17ea56368467dneto#include "Test.h"
9327f905d2cb0d37c302d651d8f2b17ea56368467dneto
10327f905d2cb0d37c302d651d8f2b17ea56368467dneto#include "../include/core/SkCanvas.h"
11327f905d2cb0d37c302d651d8f2b17ea56368467dneto#include "../include/core/SkPicture.h"
12327f905d2cb0d37c302d651d8f2b17ea56368467dneto#include "../include/core/SkStream.h"
13327f905d2cb0d37c302d651d8f2b17ea56368467dneto#include "../include/core/SkString.h"
14327f905d2cb0d37c302d651d8f2b17ea56368467dneto#include "../include/core/SkPictureRecorder.h"
15327f905d2cb0d37c302d651d8f2b17ea56368467dneto#include <cstring>
16327f905d2cb0d37c302d651d8f2b17ea56368467dneto
17327f905d2cb0d37c302d651d8f2b17ea56368467dneto// Verify that replay of a recording into a clipped canvas
18327f905d2cb0d37c302d651d8f2b17ea56368467dneto// produces the correct bitmap.
19327f905d2cb0d37c302d651d8f2b17ea56368467dneto// This arose from http://crbug.com/401593 which has
20327f905d2cb0d37c302d651d8f2b17ea56368467dneto// https://code.google.com/p/skia/issues/detail?id=1291 as its root cause.
21327f905d2cb0d37c302d651d8f2b17ea56368467dneto
22327f905d2cb0d37c302d651d8f2b17ea56368467dnetonamespace {
23327f905d2cb0d37c302d651d8f2b17ea56368467dneto
24327f905d2cb0d37c302d651d8f2b17ea56368467dnetoclass Drawer {
25327f905d2cb0d37c302d651d8f2b17ea56368467dneto public:
2646616af01b412ea984a516fda1ed8ec08e689f29mtklein    explicit Drawer() : fImageInfo(SkImageInfo::MakeN32Premul(200, 100)) {
2746616af01b412ea984a516fda1ed8ec08e689f29mtklein        fCircleBM.allocPixels(SkImageInfo::MakeN32Premul(100, 100));
28327f905d2cb0d37c302d651d8f2b17ea56368467dneto        SkCanvas canvas(fCircleBM);
29327f905d2cb0d37c302d651d8f2b17ea56368467dneto        canvas.clear(0xffffffff);
30327f905d2cb0d37c302d651d8f2b17ea56368467dneto        SkPaint circlePaint;
31327f905d2cb0d37c302d651d8f2b17ea56368467dneto        circlePaint.setColor(0xff000000);
3246616af01b412ea984a516fda1ed8ec08e689f29mtklein        canvas.drawCircle(50, 50, 50, circlePaint);
33327f905d2cb0d37c302d651d8f2b17ea56368467dneto    }
34327f905d2cb0d37c302d651d8f2b17ea56368467dneto
35327f905d2cb0d37c302d651d8f2b17ea56368467dneto    const SkImageInfo& imageInfo() const { return fImageInfo; }
36327f905d2cb0d37c302d651d8f2b17ea56368467dneto
37327f905d2cb0d37c302d651d8f2b17ea56368467dneto    void draw(SkCanvas* canvas, const SkRect& clipRect, SkXfermode::Mode mode) const {
38327f905d2cb0d37c302d651d8f2b17ea56368467dneto        SkPaint greenPaint;
39327f905d2cb0d37c302d651d8f2b17ea56368467dneto        greenPaint.setColor(0xff008000);
40327f905d2cb0d37c302d651d8f2b17ea56368467dneto        SkPaint blackPaint;
41327f905d2cb0d37c302d651d8f2b17ea56368467dneto        blackPaint.setColor(0xff000000);
42327f905d2cb0d37c302d651d8f2b17ea56368467dneto        SkPaint whitePaint;
43327f905d2cb0d37c302d651d8f2b17ea56368467dneto        whitePaint.setColor(0xffffffff);
44327f905d2cb0d37c302d651d8f2b17ea56368467dneto        SkPaint layerPaint;
45327f905d2cb0d37c302d651d8f2b17ea56368467dneto        layerPaint.setColor(0xff000000);
46327f905d2cb0d37c302d651d8f2b17ea56368467dneto        layerPaint.setXfermodeMode(mode);
4746616af01b412ea984a516fda1ed8ec08e689f29mtklein        SkRect canvasRect(SkRect::MakeWH(SkIntToScalar(fImageInfo.width()),
4846616af01b412ea984a516fda1ed8ec08e689f29mtklein                                         SkIntToScalar(fImageInfo.height())));
49327f905d2cb0d37c302d651d8f2b17ea56368467dneto
50327f905d2cb0d37c302d651d8f2b17ea56368467dneto        canvas->clipRect(clipRect);
51327f905d2cb0d37c302d651d8f2b17ea56368467dneto        canvas->clear(0xff000000);
52327f905d2cb0d37c302d651d8f2b17ea56368467dneto
5346616af01b412ea984a516fda1ed8ec08e689f29mtklein        canvas->saveLayer(NULL, &blackPaint);
5446616af01b412ea984a516fda1ed8ec08e689f29mtklein            canvas->drawRect(canvasRect, greenPaint);
5546616af01b412ea984a516fda1ed8ec08e689f29mtklein            canvas->saveLayer(NULL, &layerPaint);
5646616af01b412ea984a516fda1ed8ec08e689f29mtklein                canvas->drawBitmapRect(fCircleBM, SkRect::MakeXYWH(20,20,60,60), &blackPaint);
57327f905d2cb0d37c302d651d8f2b17ea56368467dneto            canvas->restore();
58327f905d2cb0d37c302d651d8f2b17ea56368467dneto        canvas->restore();
59327f905d2cb0d37c302d651d8f2b17ea56368467dneto    }
60327f905d2cb0d37c302d651d8f2b17ea56368467dneto
61327f905d2cb0d37c302d651d8f2b17ea56368467dneto private:
62327f905d2cb0d37c302d651d8f2b17ea56368467dneto    const SkImageInfo fImageInfo;
63327f905d2cb0d37c302d651d8f2b17ea56368467dneto    SkBitmap fCircleBM;
64327f905d2cb0d37c302d651d8f2b17ea56368467dneto};
65327f905d2cb0d37c302d651d8f2b17ea56368467dneto
66327f905d2cb0d37c302d651d8f2b17ea56368467dnetoclass RecordingStrategy {
67327f905d2cb0d37c302d651d8f2b17ea56368467dneto public:
68327f905d2cb0d37c302d651d8f2b17ea56368467dneto    virtual ~RecordingStrategy() {}
69327f905d2cb0d37c302d651d8f2b17ea56368467dneto    virtual const SkBitmap& recordAndReplay(const Drawer& drawer,
70327f905d2cb0d37c302d651d8f2b17ea56368467dneto                                            const SkRect& intoClip,
71327f905d2cb0d37c302d651d8f2b17ea56368467dneto                                            SkXfermode::Mode) = 0;
72327f905d2cb0d37c302d651d8f2b17ea56368467dneto};
73327f905d2cb0d37c302d651d8f2b17ea56368467dneto
74327f905d2cb0d37c302d651d8f2b17ea56368467dnetoclass BitmapBackedCanvasStrategy : public RecordingStrategy {
75327f905d2cb0d37c302d651d8f2b17ea56368467dneto    // This version just draws into a bitmap-backed canvas.
76327f905d2cb0d37c302d651d8f2b17ea56368467dneto public:
7746616af01b412ea984a516fda1ed8ec08e689f29mtklein    BitmapBackedCanvasStrategy(const SkImageInfo& imageInfo) {
78327f905d2cb0d37c302d651d8f2b17ea56368467dneto        fBitmap.allocPixels(imageInfo);
79327f905d2cb0d37c302d651d8f2b17ea56368467dneto    }
80327f905d2cb0d37c302d651d8f2b17ea56368467dneto
81327f905d2cb0d37c302d651d8f2b17ea56368467dneto    virtual const SkBitmap& recordAndReplay(const Drawer& drawer,
82327f905d2cb0d37c302d651d8f2b17ea56368467dneto                                            const SkRect& intoClip,
83327f905d2cb0d37c302d651d8f2b17ea56368467dneto                                            SkXfermode::Mode mode) {
84327f905d2cb0d37c302d651d8f2b17ea56368467dneto        SkCanvas canvas(fBitmap);
85327f905d2cb0d37c302d651d8f2b17ea56368467dneto        canvas.clear(0xffffffff);
86327f905d2cb0d37c302d651d8f2b17ea56368467dneto        // Note that the scene is drawn just into the clipped region!
87327f905d2cb0d37c302d651d8f2b17ea56368467dneto        canvas.clipRect(intoClip);
88327f905d2cb0d37c302d651d8f2b17ea56368467dneto        drawer.draw(&canvas, intoClip, mode); // Shouild be canvas-wide...
89327f905d2cb0d37c302d651d8f2b17ea56368467dneto        return fBitmap;
90327f905d2cb0d37c302d651d8f2b17ea56368467dneto    }
91327f905d2cb0d37c302d651d8f2b17ea56368467dneto
92327f905d2cb0d37c302d651d8f2b17ea56368467dneto private:
93327f905d2cb0d37c302d651d8f2b17ea56368467dneto    SkBitmap fBitmap;
94327f905d2cb0d37c302d651d8f2b17ea56368467dneto};
95327f905d2cb0d37c302d651d8f2b17ea56368467dneto
9646616af01b412ea984a516fda1ed8ec08e689f29mtkleinclass PictureStrategy : public RecordingStrategy {
9746616af01b412ea984a516fda1ed8ec08e689f29mtklein    // This version draws the entire scene into an SkPictureRecorder.
98327f905d2cb0d37c302d651d8f2b17ea56368467dneto    // Then it then replays the scene through a clip rectangle.
99327f905d2cb0d37c302d651d8f2b17ea56368467dneto    // This backend proved to be buggy.
100327f905d2cb0d37c302d651d8f2b17ea56368467dneto public:
10146616af01b412ea984a516fda1ed8ec08e689f29mtklein    PictureStrategy(const SkImageInfo& imageInfo) {
102327f905d2cb0d37c302d651d8f2b17ea56368467dneto        fBitmap.allocPixels(imageInfo);
10346616af01b412ea984a516fda1ed8ec08e689f29mtklein        fWidth  = imageInfo.width();
10446616af01b412ea984a516fda1ed8ec08e689f29mtklein        fHeight = imageInfo.height();
105327f905d2cb0d37c302d651d8f2b17ea56368467dneto    }
106327f905d2cb0d37c302d651d8f2b17ea56368467dneto
107327f905d2cb0d37c302d651d8f2b17ea56368467dneto    virtual const SkBitmap& recordAndReplay(const Drawer& drawer,
108327f905d2cb0d37c302d651d8f2b17ea56368467dneto                                            const SkRect& intoClip,
109327f905d2cb0d37c302d651d8f2b17ea56368467dneto                                            SkXfermode::Mode mode) {
110703dd2ed187b9788c5bb0f2d313f2d07695603d6mtklein        SkRTreeFactory factory;
111327f905d2cb0d37c302d651d8f2b17ea56368467dneto        SkPictureRecorder recorder;
112327f905d2cb0d37c302d651d8f2b17ea56368467dneto        SkRect canvasRect(SkRect::MakeWH(SkIntToScalar(fWidth),SkIntToScalar(fHeight)));
11346616af01b412ea984a516fda1ed8ec08e689f29mtklein        SkCanvas* canvas = recorder.beginRecording(SkIntToScalar(fWidth),
11446616af01b412ea984a516fda1ed8ec08e689f29mtklein                                                   SkIntToScalar(fHeight),
11546616af01b412ea984a516fda1ed8ec08e689f29mtklein                                                   &factory);
116327f905d2cb0d37c302d651d8f2b17ea56368467dneto        drawer.draw(canvas, canvasRect, mode);
11790d0ff013bbd8e5295d1517d41cb408e9d9f4d93reed        SkAutoTUnref<SkPicture> picture(recorder.endRecording());
118327f905d2cb0d37c302d651d8f2b17ea56368467dneto
119327f905d2cb0d37c302d651d8f2b17ea56368467dneto        SkCanvas replayCanvas(fBitmap);
120327f905d2cb0d37c302d651d8f2b17ea56368467dneto        replayCanvas.clear(0xffffffff);
121327f905d2cb0d37c302d651d8f2b17ea56368467dneto        replayCanvas.clipRect(intoClip);
122327f905d2cb0d37c302d651d8f2b17ea56368467dneto        picture->playback(&replayCanvas);
123327f905d2cb0d37c302d651d8f2b17ea56368467dneto        return fBitmap;
124327f905d2cb0d37c302d651d8f2b17ea56368467dneto    }
125327f905d2cb0d37c302d651d8f2b17ea56368467dneto
126327f905d2cb0d37c302d651d8f2b17ea56368467dneto private:
127327f905d2cb0d37c302d651d8f2b17ea56368467dneto    SkBitmap fBitmap;
128327f905d2cb0d37c302d651d8f2b17ea56368467dneto    int fWidth;
129327f905d2cb0d37c302d651d8f2b17ea56368467dneto    int fHeight;
130327f905d2cb0d37c302d651d8f2b17ea56368467dneto};
131327f905d2cb0d37c302d651d8f2b17ea56368467dneto
13246616af01b412ea984a516fda1ed8ec08e689f29mtklein} // namespace
133327f905d2cb0d37c302d651d8f2b17ea56368467dneto
134327f905d2cb0d37c302d651d8f2b17ea56368467dneto
135327f905d2cb0d37c302d651d8f2b17ea56368467dnetoDEF_TEST(SkRecordingAccuracyXfermode, reporter) {
136327f905d2cb0d37c302d651d8f2b17ea56368467dneto#define FINEGRAIN 0
137327f905d2cb0d37c302d651d8f2b17ea56368467dneto    const Drawer drawer;
138327f905d2cb0d37c302d651d8f2b17ea56368467dneto
13946616af01b412ea984a516fda1ed8ec08e689f29mtklein    BitmapBackedCanvasStrategy golden(drawer.imageInfo());
14046616af01b412ea984a516fda1ed8ec08e689f29mtklein    PictureStrategy picture(drawer.imageInfo());
141327f905d2cb0d37c302d651d8f2b17ea56368467dneto
142327f905d2cb0d37c302d651d8f2b17ea56368467dneto#if !FINEGRAIN
143327f905d2cb0d37c302d651d8f2b17ea56368467dneto    unsigned numErrors = 0;
144327f905d2cb0d37c302d651d8f2b17ea56368467dneto    SkString errors;
145327f905d2cb0d37c302d651d8f2b17ea56368467dneto#endif
146327f905d2cb0d37c302d651d8f2b17ea56368467dneto
14746616af01b412ea984a516fda1ed8ec08e689f29mtklein    for (int iMode = 0; iMode < int(SkXfermode::kLastMode); iMode++) {
14846616af01b412ea984a516fda1ed8ec08e689f29mtklein        const SkRect& clip = SkRect::MakeXYWH(100, 0, 100, 100);
149327f905d2cb0d37c302d651d8f2b17ea56368467dneto        SkXfermode::Mode mode = SkXfermode::Mode(iMode);
150327f905d2cb0d37c302d651d8f2b17ea56368467dneto
151327f905d2cb0d37c302d651d8f2b17ea56368467dneto        const SkBitmap& goldenBM = golden.recordAndReplay(drawer, clip, mode);
15246616af01b412ea984a516fda1ed8ec08e689f29mtklein        const SkBitmap& pictureBM = picture.recordAndReplay(drawer, clip, mode);
153327f905d2cb0d37c302d651d8f2b17ea56368467dneto
154327f905d2cb0d37c302d651d8f2b17ea56368467dneto        size_t pixelsSize = goldenBM.getSize();
15546616af01b412ea984a516fda1ed8ec08e689f29mtklein        REPORTER_ASSERT(reporter, pixelsSize == pictureBM.getSize());
156327f905d2cb0d37c302d651d8f2b17ea56368467dneto
157327f905d2cb0d37c302d651d8f2b17ea56368467dneto        // The pixel arrays should match.
158327f905d2cb0d37c302d651d8f2b17ea56368467dneto#if FINEGRAIN
15946616af01b412ea984a516fda1ed8ec08e689f29mtklein        REPORTER_ASSERT(reporter,
16046616af01b412ea984a516fda1ed8ec08e689f29mtklein                        0 == memcmp(goldenBM.getPixels(), pictureBM.getPixels(), pixelsSize));
161327f905d2cb0d37c302d651d8f2b17ea56368467dneto#else
16246616af01b412ea984a516fda1ed8ec08e689f29mtklein        if (memcmp(goldenBM.getPixels(), pictureBM.getPixels(), pixelsSize)) {
163327f905d2cb0d37c302d651d8f2b17ea56368467dneto            numErrors++;
16446616af01b412ea984a516fda1ed8ec08e689f29mtklein            errors.appendf("For SkXfermode %d %s:    SkPictureRecorder bitmap is wrong\n",
16546616af01b412ea984a516fda1ed8ec08e689f29mtklein                           iMode, SkXfermode::ModeName(mode));
166327f905d2cb0d37c302d651d8f2b17ea56368467dneto        }
167327f905d2cb0d37c302d651d8f2b17ea56368467dneto#endif
168327f905d2cb0d37c302d651d8f2b17ea56368467dneto    }
16946616af01b412ea984a516fda1ed8ec08e689f29mtklein
170327f905d2cb0d37c302d651d8f2b17ea56368467dneto#if !FINEGRAIN
17146616af01b412ea984a516fda1ed8ec08e689f29mtklein    REPORTER_ASSERT_MESSAGE(reporter, 0 == numErrors, errors.c_str());
172327f905d2cb0d37c302d651d8f2b17ea56368467dneto#endif
173327f905d2cb0d37c302d651d8f2b17ea56368467dneto}
174