RecordingXfermodeTest.cpp 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  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. #include "tests/Test.h"
  8. #include "include/core/SkBitmap.h"
  9. #include "include/core/SkCanvas.h"
  10. #include "include/core/SkPicture.h"
  11. #include "include/core/SkPictureRecorder.h"
  12. #include "include/core/SkStream.h"
  13. #include "include/core/SkString.h"
  14. #include "src/core/SkBlendModePriv.h"
  15. #include <cstring>
  16. // Verify that replay of a recording into a clipped canvas
  17. // produces the correct bitmap.
  18. // This arose from http://crbug.com/401593 which has
  19. // https://code.google.com/p/skia/issues/detail?id=1291 as its root cause.
  20. namespace {
  21. class Drawer {
  22. public:
  23. explicit Drawer() : fImageInfo(SkImageInfo::MakeN32Premul(200, 100)) {
  24. fCircleBM.allocPixels(SkImageInfo::MakeN32Premul(100, 100));
  25. SkCanvas canvas(fCircleBM);
  26. canvas.clear(0xffffffff);
  27. SkPaint circlePaint;
  28. circlePaint.setColor(0xff000000);
  29. canvas.drawCircle(50, 50, 50, circlePaint);
  30. }
  31. const SkImageInfo& imageInfo() const { return fImageInfo; }
  32. void draw(SkCanvas* canvas, const SkRect& clipRect, SkBlendMode mode) const {
  33. SkPaint greenPaint;
  34. greenPaint.setColor(0xff008000);
  35. SkPaint blackPaint;
  36. blackPaint.setColor(0xff000000);
  37. SkPaint whitePaint;
  38. whitePaint.setColor(0xffffffff);
  39. SkPaint layerPaint;
  40. layerPaint.setColor(0xff000000);
  41. layerPaint.setBlendMode(mode);
  42. SkRect canvasRect(SkRect::MakeWH(SkIntToScalar(fImageInfo.width()),
  43. SkIntToScalar(fImageInfo.height())));
  44. canvas->clipRect(clipRect);
  45. canvas->clear(0xff000000);
  46. canvas->saveLayer(nullptr, &blackPaint);
  47. canvas->drawRect(canvasRect, greenPaint);
  48. canvas->saveLayer(nullptr, &layerPaint);
  49. canvas->drawBitmapRect(fCircleBM, SkRect::MakeXYWH(20,20,60,60), &blackPaint);
  50. canvas->restore();
  51. canvas->restore();
  52. }
  53. private:
  54. const SkImageInfo fImageInfo;
  55. SkBitmap fCircleBM;
  56. };
  57. class RecordingStrategy {
  58. public:
  59. virtual ~RecordingStrategy() {}
  60. virtual const SkBitmap& recordAndReplay(const Drawer& drawer,
  61. const SkRect& intoClip,
  62. SkBlendMode) = 0;
  63. };
  64. class BitmapBackedCanvasStrategy : public RecordingStrategy {
  65. // This version just draws into a bitmap-backed canvas.
  66. public:
  67. BitmapBackedCanvasStrategy(const SkImageInfo& imageInfo) {
  68. fBitmap.allocPixels(imageInfo);
  69. }
  70. const SkBitmap& recordAndReplay(const Drawer& drawer, const SkRect& intoClip,
  71. SkBlendMode mode) override {
  72. SkCanvas canvas(fBitmap);
  73. canvas.clear(0xffffffff);
  74. // Note that the scene is drawn just into the clipped region!
  75. canvas.clipRect(intoClip);
  76. drawer.draw(&canvas, intoClip, mode); // Shouild be canvas-wide...
  77. return fBitmap;
  78. }
  79. private:
  80. SkBitmap fBitmap;
  81. };
  82. class PictureStrategy : public RecordingStrategy {
  83. // This version draws the entire scene into an SkPictureRecorder.
  84. // Then it then replays the scene through a clip rectangle.
  85. // This backend proved to be buggy.
  86. public:
  87. PictureStrategy(const SkImageInfo& imageInfo) {
  88. fBitmap.allocPixels(imageInfo);
  89. fWidth = imageInfo.width();
  90. fHeight = imageInfo.height();
  91. }
  92. const SkBitmap& recordAndReplay(const Drawer& drawer, const SkRect& intoClip,
  93. SkBlendMode mode) override {
  94. SkRTreeFactory factory;
  95. SkPictureRecorder recorder;
  96. SkRect canvasRect(SkRect::MakeWH(SkIntToScalar(fWidth),SkIntToScalar(fHeight)));
  97. SkCanvas* canvas = recorder.beginRecording(SkIntToScalar(fWidth),
  98. SkIntToScalar(fHeight),
  99. &factory);
  100. drawer.draw(canvas, canvasRect, mode);
  101. sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
  102. SkCanvas replayCanvas(fBitmap);
  103. replayCanvas.clear(0xffffffff);
  104. replayCanvas.clipRect(intoClip);
  105. picture->playback(&replayCanvas);
  106. return fBitmap;
  107. }
  108. private:
  109. SkBitmap fBitmap;
  110. int fWidth;
  111. int fHeight;
  112. };
  113. } // namespace
  114. DEF_TEST(SkRecordingAccuracyXfermode, reporter) {
  115. #define FINEGRAIN 0
  116. const Drawer drawer;
  117. BitmapBackedCanvasStrategy golden(drawer.imageInfo());
  118. PictureStrategy picture(drawer.imageInfo());
  119. #if !FINEGRAIN
  120. unsigned numErrors = 0;
  121. SkString errors;
  122. #endif
  123. for (int iMode = 0; iMode < int(SkBlendMode::kLastMode); iMode++) {
  124. const SkRect& clip = SkRect::MakeXYWH(100, 0, 100, 100);
  125. SkBlendMode mode = SkBlendMode(iMode);
  126. const SkBitmap& goldenBM = golden.recordAndReplay(drawer, clip, mode);
  127. const SkBitmap& pictureBM = picture.recordAndReplay(drawer, clip, mode);
  128. size_t pixelsSize = goldenBM.computeByteSize();
  129. REPORTER_ASSERT(reporter, pixelsSize == pictureBM.computeByteSize());
  130. // The pixel arrays should match.
  131. #if FINEGRAIN
  132. REPORTER_ASSERT(reporter,
  133. 0 == memcmp(goldenBM.getPixels(), pictureBM.getPixels(), pixelsSize));
  134. #else
  135. if (memcmp(goldenBM.getPixels(), pictureBM.getPixels(), pixelsSize)) {
  136. numErrors++;
  137. errors.appendf("For SkXfermode %d %s: SkPictureRecorder bitmap is wrong\n",
  138. iMode, SkBlendMode_Name(mode));
  139. }
  140. #endif
  141. }
  142. #if !FINEGRAIN
  143. REPORTER_ASSERT(reporter, 0 == numErrors, errors.c_str());
  144. #endif
  145. }