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