MultiSkpTest.cpp 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  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. * This test confirms that a multi skp can be serialize and deserailzied without error.
  8. */
  9. #include "include/core/SkDocument.h"
  10. #include "include/core/SkPicture.h"
  11. #include "include/core/SkSurface.h"
  12. #include "include/core/SkString.h"
  13. #include "include/core/SkTextBlob.h"
  14. #include "include/core/SkFont.h"
  15. #include "src/core/SkRecord.h"
  16. #include "src/core/SkRecorder.h"
  17. #include "src/utils/SkMultiPictureDocument.h"
  18. #include "tools/SkSharingProc.h"
  19. #include "tests/Test.h"
  20. namespace {
  21. class RecordVisitor {
  22. // An SkRecord visitor that remembers the name of the last visited command.
  23. public:
  24. SkString name;
  25. explicit RecordVisitor() {}
  26. template <typename T>
  27. void operator()(const T& command) {
  28. name = SkString(NameOf(command));
  29. }
  30. SkString lastCommandName() {
  31. return name;
  32. }
  33. private:
  34. template <typename T>
  35. static const char* NameOf(const T&) {
  36. #define CASE(U) case SkRecords::U##_Type: return #U;
  37. switch (T::kType) { SK_RECORD_TYPES(CASE) }
  38. #undef CASE
  39. return "Unknown T";
  40. }
  41. };
  42. } // namespace
  43. // Compare record tested with record expected. Assert op sequence is the same (comparing types)
  44. // frame_num is only used for error message.
  45. static void compareRecords(const SkRecord& tested, const SkRecord& expected,
  46. int frame_num, skiatest::Reporter* reporter) {
  47. REPORTER_ASSERT(reporter, tested.count() == expected.count(),
  48. "Found %d commands in frame %d, expected %d", tested.count(), frame_num, expected.count());
  49. RecordVisitor rv;
  50. for (int i = 0; i < tested.count(); i++) {
  51. tested.visit(i, rv);
  52. const SkString testCommandName = rv.lastCommandName();
  53. expected.visit(i, rv);
  54. const SkString expectedCommandName = rv.lastCommandName();
  55. REPORTER_ASSERT(reporter, testCommandName == expectedCommandName,
  56. "Unexpected command type '%s' in frame %d, op %d. Expected '%s'",
  57. testCommandName.c_str(), frame_num, i, expectedCommandName.c_str());
  58. }
  59. }
  60. static void draw_something(SkCanvas* canvas, int seed, sk_sp<SkImage> image) {
  61. canvas->drawColor(SK_ColorWHITE);
  62. SkPaint paint;
  63. paint.setStyle(SkPaint::kStroke_Style);
  64. paint.setStrokeWidth(seed);
  65. paint.setColor(SK_ColorRED);
  66. SkRect rect = SkRect::MakeXYWH(50+seed, 50+seed, 4*seed, 60);
  67. canvas->drawRect(rect, paint);
  68. SkRRect oval;
  69. oval.setOval(rect);
  70. oval.offset(40, 60+seed);
  71. paint.setColor(SK_ColorBLUE);
  72. canvas->drawRRect(oval, paint);
  73. paint.setColor(SK_ColorCYAN);
  74. canvas->drawCircle(180, 50, 5*seed, paint);
  75. rect.offset(80, 0);
  76. paint.setColor(SK_ColorYELLOW);
  77. canvas->drawRoundRect(rect, 10, 10, paint);
  78. SkPath path;
  79. path.cubicTo(768, 0, -512, 256, 256, 256);
  80. paint.setColor(SK_ColorGREEN);
  81. canvas->drawPath(path, paint);
  82. canvas->drawImage(image, 128-seed, 128, &paint);
  83. if (seed % 2 == 0) {
  84. SkRect rect2 = SkRect::MakeXYWH(0, 0, 40, 60);
  85. canvas->drawImageRect(image, rect2, &paint);
  86. }
  87. SkPaint paint2;
  88. auto text = SkTextBlob::MakeFromString(
  89. SkStringPrintf("Frame %d", seed).c_str(), SkFont(nullptr, 2+seed));
  90. canvas->drawTextBlob(text.get(), 50, 25, paint2);
  91. }
  92. // Test serialization and deserialization of multi skp.
  93. DEF_TEST(Serialize_and_deserialize_multi_skp, reporter) {
  94. // Create the stream we will serialize into.
  95. SkDynamicMemoryWStream stream;
  96. // Create the image sharing proc.
  97. SkSharingSerialContext ctx;
  98. SkSerialProcs procs;
  99. procs.fImageProc = SkSharingSerialContext::serializeImage;
  100. procs.fImageCtx = &ctx;
  101. // Create the mulit picture document used for recording frames.
  102. sk_sp<SkDocument> multipic = SkMakeMultiPictureDocument(&stream, &procs);
  103. static const int NUM_FRAMES = 12;
  104. static const int WIDTH = 256;
  105. static const int HEIGHT = 256;
  106. // Make an image to be used in a later step.
  107. auto surface(SkSurface::MakeRasterN32Premul(100, 100));
  108. surface->getCanvas()->clear(SK_ColorGREEN);
  109. sk_sp<SkImage> image(surface->makeImageSnapshot());
  110. REPORTER_ASSERT(reporter, image);
  111. // Create frames, recording them to multipic.
  112. SkRecord expectedRecords[NUM_FRAMES];
  113. for (int i=0; i<NUM_FRAMES; i++) {
  114. SkCanvas* pictureCanvas = multipic->beginPage(WIDTH, HEIGHT);
  115. draw_something(pictureCanvas, i, image);
  116. multipic->endPage();
  117. // Also record the same commands to separate SkRecords for later comparison
  118. SkRecorder canvas(&expectedRecords[i], WIDTH, HEIGHT);
  119. draw_something(&canvas, i, image);
  120. }
  121. // Finalize
  122. multipic->close();
  123. // Confirm written data is at least as large as the magic word
  124. std::unique_ptr<SkStreamAsset> writtenStream = stream.detachAsStream();
  125. REPORTER_ASSERT(reporter, writtenStream->getLength() > 24,
  126. "Written data length too short (%d)", writtenStream->getLength());
  127. SkDebugf("Multi Frame file size = %d\n", writtenStream->getLength());
  128. // Set up deserialization
  129. SkSharingDeserialContext deserialContext;
  130. SkDeserialProcs dprocs;
  131. dprocs.fImageProc = SkSharingDeserialContext::deserializeImage;
  132. dprocs.fImageCtx = &deserialContext;
  133. // Confirm data is a MultiPictureDocument
  134. int frame_count = SkMultiPictureDocumentReadPageCount(writtenStream.get());
  135. REPORTER_ASSERT(reporter, frame_count == NUM_FRAMES,
  136. "Expected %d frames, got %d. \n 0 frames may indicate the written file was not a "
  137. "MultiPictureDocument.", NUM_FRAMES, frame_count);
  138. // Deserailize
  139. std::vector<SkDocumentPage> frames(frame_count);
  140. REPORTER_ASSERT(reporter,
  141. SkMultiPictureDocumentRead(writtenStream.get(), frames.data(), frame_count, &dprocs),
  142. "Failed while reading MultiPictureDocument");
  143. // Examine each frame.
  144. SkRecorder resultRecorder(nullptr, 1, 1);
  145. int i=0;
  146. for (const auto& frame : frames) {
  147. SkRect bounds = frame.fPicture->cullRect();
  148. REPORTER_ASSERT(reporter, bounds.width() == WIDTH,
  149. "Page width: expected (%d) got (%d)", WIDTH, bounds.width());
  150. REPORTER_ASSERT(reporter, bounds.height() == HEIGHT,
  151. "Page height: expected (%d) got (%d)", HEIGHT, bounds.height());
  152. // confirm contents of picture match what we drew.
  153. // There are several ways of doing this, an ideal comparison would not break in the same
  154. // way at the same time as the code under test (no serialization), and would involve only
  155. // minimal transformation of frame.fPicture, minimizing the chance that a detected fault lies
  156. // in the test itself. The comparions also would not be an overly sensitive change detector,
  157. // so that it doesn't break every time someone submits code (no golden file)
  158. // Extract the SkRecord from the deserialized picture using playback (instead of a mess of
  159. // friend classes to grab the private record inside frame.fPicture
  160. SkRecord record;
  161. // This picture mode is necessary so that we record the command contents of frame.fPicture
  162. // not just a 'DrawPicture' command.
  163. resultRecorder.reset(&record, bounds, SkRecorder::Playback_DrawPictureMode, nullptr);
  164. frame.fPicture->playback(&resultRecorder);
  165. // Compare the record to the expected one
  166. compareRecords(record, expectedRecords[i], i, reporter);
  167. i++;
  168. }
  169. }