Home | History | Annotate | Download | only in tests
      1 /*
      2  * Copyright 2014 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 
     10 #include "../include/core/SkCanvas.h"
     11 #include "../include/core/SkPicture.h"
     12 #include "../include/core/SkStream.h"
     13 #include "../include/core/SkString.h"
     14 #include "../include/record/SkRecording.h"
     15 #include "../include/core/SkPictureRecorder.h"
     16 #include <cstring>
     17 
     18 // Verify that replay of a recording into a clipped canvas
     19 // produces the correct bitmap.
     20 // This arose from http://crbug.com/401593 which has
     21 // https://code.google.com/p/skia/issues/detail?id=1291 as its root cause.
     22 
     23 
     24 namespace {
     25 
     26 class Drawer {
     27  public:
     28     explicit Drawer()
     29             : fImageInfo(SkImageInfo::MakeN32Premul(200,100))
     30     {
     31         fCircleBM.allocPixels( SkImageInfo::MakeN32Premul(100,100) );
     32         SkCanvas canvas(fCircleBM);
     33         canvas.clear(0xffffffff);
     34         SkPaint circlePaint;
     35         circlePaint.setColor(0xff000000);
     36         canvas.drawCircle(50,50,50,circlePaint);
     37     }
     38 
     39     const SkImageInfo& imageInfo() const { return fImageInfo; }
     40 
     41     void draw(SkCanvas* canvas, const SkRect& clipRect, SkXfermode::Mode mode) const {
     42         SkPaint greenPaint;
     43         greenPaint.setColor(0xff008000);
     44         SkPaint blackPaint;
     45         blackPaint.setColor(0xff000000);
     46         SkPaint whitePaint;
     47         whitePaint.setColor(0xffffffff);
     48         SkPaint layerPaint;
     49         layerPaint.setColor(0xff000000);
     50         layerPaint.setXfermodeMode(mode);
     51         SkRect canvasRect(SkRect::MakeWH(SkIntToScalar(fImageInfo.width()),SkIntToScalar(fImageInfo.height())));
     52 
     53         canvas->clipRect(clipRect);
     54         canvas->clear(0xff000000);
     55 
     56         canvas->saveLayer(NULL,&blackPaint);
     57             canvas->drawRect(canvasRect,greenPaint);
     58             canvas->saveLayer(NULL,&layerPaint);
     59                 canvas->drawBitmapRect(fCircleBM,SkRect::MakeXYWH(20,20,60,60),&blackPaint);
     60             canvas->restore();
     61         canvas->restore();
     62     }
     63 
     64  private:
     65     const SkImageInfo fImageInfo;
     66     SkBitmap fCircleBM;
     67 };
     68 
     69 class RecordingStrategy {
     70  public:
     71     virtual ~RecordingStrategy() {}
     72     virtual void init(const SkImageInfo&) = 0;
     73     virtual const SkBitmap& recordAndReplay(const Drawer& drawer,
     74                                             const SkRect& intoClip,
     75                                             SkXfermode::Mode) = 0;
     76 };
     77 
     78 class BitmapBackedCanvasStrategy : public RecordingStrategy {
     79     // This version just draws into a bitmap-backed canvas.
     80  public:
     81     BitmapBackedCanvasStrategy() {}
     82 
     83     virtual void init(const SkImageInfo& imageInfo) {
     84         fBitmap.allocPixels(imageInfo);
     85     }
     86 
     87     virtual const SkBitmap& recordAndReplay(const Drawer& drawer,
     88                                             const SkRect& intoClip,
     89                                             SkXfermode::Mode mode) {
     90         SkCanvas canvas(fBitmap);
     91         canvas.clear(0xffffffff);
     92         // Note that the scene is drawn just into the clipped region!
     93         canvas.clipRect(intoClip);
     94         drawer.draw(&canvas, intoClip, mode); // Shouild be canvas-wide...
     95         return fBitmap;
     96     }
     97 
     98  private:
     99     SkBitmap fBitmap;
    100 };
    101 
    102 class DeprecatedRecorderStrategy : public RecordingStrategy {
    103     // This version draws the entire scene into an SkPictureRecorder,
    104     // using the deprecated recording backend.
    105     // Then it then replays the scene through a clip rectangle.
    106     // This backend proved to be buggy.
    107  public:
    108     DeprecatedRecorderStrategy() {}
    109 
    110     virtual void init(const SkImageInfo& imageInfo) {
    111         fBitmap.allocPixels(imageInfo);
    112         fWidth = imageInfo.width();
    113         fHeight= imageInfo.height();
    114     }
    115 
    116     virtual const SkBitmap& recordAndReplay(const Drawer& drawer,
    117                                             const SkRect& intoClip,
    118                                             SkXfermode::Mode mode) {
    119         SkTileGridFactory::TileGridInfo tileGridInfo = { {100,100}, {0,0}, {0,0} };
    120         SkTileGridFactory factory(tileGridInfo);
    121         SkPictureRecorder recorder;
    122         SkRect canvasRect(SkRect::MakeWH(SkIntToScalar(fWidth),SkIntToScalar(fHeight)));
    123         SkCanvas* canvas = recorder.DEPRECATED_beginRecording( SkIntToScalar(fWidth), SkIntToScalar(fHeight), &factory);
    124         drawer.draw(canvas, canvasRect, mode);
    125         SkAutoTDelete<SkPicture> picture(recorder.endRecording());
    126 
    127         SkCanvas replayCanvas(fBitmap);
    128         replayCanvas.clear(0xffffffff);
    129         replayCanvas.clipRect(intoClip);
    130         picture->playback(&replayCanvas);
    131 
    132         return fBitmap;
    133     }
    134 
    135  private:
    136     SkBitmap fBitmap;
    137     int fWidth;
    138     int fHeight;
    139 };
    140 
    141 class NewRecordingStrategy : public RecordingStrategy {
    142     // This version draws the entire scene into an SkPictureRecorder,
    143     // using the new recording backend.
    144     // Then it then replays the scene through a clip rectangle.
    145     // This backend proved to be buggy.
    146  public:
    147     NewRecordingStrategy() {}
    148 
    149     virtual void init(const SkImageInfo& imageInfo) {
    150         fBitmap.allocPixels(imageInfo);
    151         fWidth = imageInfo.width();
    152         fHeight= imageInfo.height();
    153     }
    154 
    155     virtual const SkBitmap& recordAndReplay(const Drawer& drawer,
    156                                             const SkRect& intoClip,
    157                                             SkXfermode::Mode mode) {
    158         SkTileGridFactory::TileGridInfo tileGridInfo = { {100,100}, {0,0}, {0,0} };
    159         SkTileGridFactory factory(tileGridInfo);
    160         SkPictureRecorder recorder;
    161         SkRect canvasRect(SkRect::MakeWH(SkIntToScalar(fWidth),SkIntToScalar(fHeight)));
    162         SkCanvas* canvas = recorder.EXPERIMENTAL_beginRecording( SkIntToScalar(fWidth), SkIntToScalar(fHeight), &factory);
    163 
    164         drawer.draw(canvas, canvasRect, mode);
    165         SkAutoTDelete<SkPicture> picture(recorder.endRecording());
    166 
    167         SkCanvas replayCanvas(fBitmap);
    168         replayCanvas.clear(0xffffffff);
    169         replayCanvas.clipRect(intoClip);
    170         picture->playback(&replayCanvas);
    171         return fBitmap;
    172     }
    173 
    174  private:
    175     SkBitmap fBitmap;
    176     int fWidth;
    177     int fHeight;
    178 };
    179 
    180 }
    181 
    182 
    183 
    184 DEF_TEST(SkRecordingAccuracyXfermode, reporter) {
    185 #define FINEGRAIN 0
    186 
    187     const Drawer drawer;
    188 
    189     BitmapBackedCanvasStrategy golden; // This is the expected result.
    190     DeprecatedRecorderStrategy deprecatedRecording;
    191     NewRecordingStrategy newRecording;
    192 
    193     golden.init(drawer.imageInfo());
    194     deprecatedRecording.init(drawer.imageInfo());
    195     newRecording.init(drawer.imageInfo());
    196 
    197 #if !FINEGRAIN
    198     unsigned numErrors = 0;
    199     SkString errors;
    200 #endif
    201 
    202     for (int iMode = 0; iMode < int(SkXfermode::kLastMode) ; iMode++ ) {
    203         const SkRect& clip = SkRect::MakeXYWH(100,0,100,100);
    204         SkXfermode::Mode mode = SkXfermode::Mode(iMode);
    205 
    206         const SkBitmap& goldenBM = golden.recordAndReplay(drawer, clip, mode);
    207         const SkBitmap& deprecatedBM = deprecatedRecording.recordAndReplay(drawer, clip, mode);
    208         const SkBitmap& newRecordingBM = newRecording.recordAndReplay(drawer, clip, mode);
    209 
    210         size_t pixelsSize = goldenBM.getSize();
    211         REPORTER_ASSERT( reporter, pixelsSize == deprecatedBM.getSize() );
    212         REPORTER_ASSERT( reporter, pixelsSize == newRecordingBM.getSize() );
    213 
    214         // The pixel arrays should match.
    215 #if FINEGRAIN
    216         REPORTER_ASSERT_MESSAGE( reporter,
    217                                  0==memcmp( goldenBM.getPixels(), deprecatedBM.getPixels(), pixelsSize ),
    218                                  "Tiled bitmap is wrong");
    219         REPORTER_ASSERT_MESSAGE( reporter,
    220                                  0==memcmp( goldenBM.getPixels(), recordingBM.getPixels(), pixelsSize ),
    221                                  "SkRecorder bitmap is wrong");
    222 #else
    223         if ( memcmp( goldenBM.getPixels(), deprecatedBM.getPixels(), pixelsSize ) ) {
    224             numErrors++;
    225             SkString str;
    226             str.printf("For SkXfermode %d %s:    Deprecated recorder bitmap is wrong\n", iMode, SkXfermode::ModeName(mode));
    227             errors.append(str);
    228         }
    229         if ( memcmp( goldenBM.getPixels(), newRecordingBM.getPixels(), pixelsSize ) ) {
    230             numErrors++;
    231             SkString str;
    232             str.printf("For SkXfermode %d %s:    SkPictureRecorder bitmap is wrong\n", iMode, SkXfermode::ModeName(mode));
    233             errors.append(str);
    234         }
    235 #endif
    236     }
    237 #if !FINEGRAIN
    238     REPORTER_ASSERT_MESSAGE( reporter, 0==numErrors, errors.c_str() );
    239 #endif
    240 }
    241