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 MultiPictureDocument can be serialized and deserailzied without error.
8 * And that the pictures within it are re-created accurately
9 */
10
11 #include "include/core/SkCanvas.h"
12 #include "include/core/SkColorPriv.h"
13 #include "include/core/SkColorSpace.h"
14 #include "include/core/SkDocument.h"
15 #include "include/core/SkFont.h"
16 #include "include/core/SkImage.h"
17 #include "include/core/SkPicture.h"
18 #include "include/core/SkPictureRecorder.h"
19 #include "include/core/SkString.h"
20 #include "include/core/SkSurface.h"
21 #include "include/core/SkTextBlob.h"
22 #include "src/gpu/GrCaps.h"
23 #include "src/utils/SkMultiPictureDocument.h"
24 #include "tests/Test.h"
25 #include "tools/SkSharingProc.h"
26 #include "tools/ToolUtils.h"
27
28 // Covers rects, ovals, paths, images, text
draw_basic(SkCanvas * canvas,int seed,sk_sp<SkImage> image)29 static void draw_basic(SkCanvas* canvas, int seed, sk_sp<SkImage> image) {
30 canvas->drawColor(SK_ColorWHITE);
31
32 SkPaint paint;
33 paint.setStyle(SkPaint::kStroke_Style);
34 paint.setStrokeWidth(seed);
35 paint.setColor(SK_ColorRED);
36
37 SkRect rect = SkRect::MakeXYWH(50+seed, 50+seed, 4*seed, 60);
38 canvas->drawRect(rect, paint);
39
40 SkRRect oval;
41 oval.setOval(rect);
42 oval.offset(40, 60+seed);
43 paint.setColor(SK_ColorBLUE);
44 canvas->drawRRect(oval, paint);
45
46 paint.setColor(SK_ColorCYAN);
47 canvas->drawCircle(180, 50, 5*seed, paint);
48
49 rect.offset(80, 0);
50 paint.setColor(SK_ColorYELLOW);
51 canvas->drawRoundRect(rect, 10, 10, paint);
52
53 SkPath path;
54 path.cubicTo(768, 0, -512, 256, 256, 256);
55 paint.setColor(SK_ColorGREEN);
56 canvas->drawPath(path, paint);
57
58 canvas->drawImage(image, 128-seed, 128, SkSamplingOptions(), &paint);
59
60 if (seed % 2 == 0) {
61 SkRect rect2 = SkRect::MakeXYWH(0, 0, 40, 60);
62 canvas->drawImageRect(image, rect2, SkSamplingOptions(), &paint);
63 }
64
65 SkPaint paint2;
66 auto text = SkTextBlob::MakeFromString(
67 SkStringPrintf("Frame %d", seed).c_str(), SkFont(nullptr, 2+seed));
68 canvas->drawTextBlob(text.get(), 50, 25, paint2);
69 }
70
71 // Covers all of the above and drawing nested sub-pictures.
draw_advanced(SkCanvas * canvas,int seed,sk_sp<SkImage> image,sk_sp<SkPicture> sub)72 static void draw_advanced(SkCanvas* canvas, int seed, sk_sp<SkImage> image, sk_sp<SkPicture> sub) {
73 draw_basic(canvas, seed, image);
74
75 // Use subpicture twice in different places
76 canvas->drawPicture(sub);
77 canvas->save();
78 canvas->translate(seed, seed);
79 canvas->drawPicture(sub);
80 canvas->restore();
81 }
82
83 // Test serialization and deserialization of multi picture document
DEF_TEST(SkMultiPictureDocument_Serialize_and_deserialize,reporter)84 DEF_TEST(SkMultiPictureDocument_Serialize_and_deserialize, reporter) {
85 // Create the stream we will serialize into.
86 SkDynamicMemoryWStream stream;
87
88 // Create the image sharing proc.
89 SkSharingSerialContext ctx;
90 SkSerialProcs procs;
91 procs.fImageProc = SkSharingSerialContext::serializeImage;
92 procs.fImageCtx = &ctx;
93
94 // Create the multi picture document used for recording frames.
95 sk_sp<SkDocument> multipic = SkMakeMultiPictureDocument(&stream, &procs);
96
97 static const int NUM_FRAMES = 12;
98 static const int WIDTH = 256;
99 static const int HEIGHT = 256;
100
101 // Make an image to be used in a later step.
102 auto surface(SkSurface::MakeRasterN32Premul(100, 100));
103 surface->getCanvas()->clear(SK_ColorGREEN);
104 sk_sp<SkImage> image(surface->makeImageSnapshot());
105 REPORTER_ASSERT(reporter, image);
106
107 // Make a subpicture to be used in a later step
108 SkPictureRecorder pr;
109 SkCanvas* subCanvas = pr.beginRecording(100, 100);
110 draw_basic(subCanvas, 42, image);
111 sk_sp<SkPicture> sub = pr.finishRecordingAsPicture();
112
113 const SkImageInfo info = SkImageInfo::MakeN32Premul(WIDTH, HEIGHT);
114 std::vector<sk_sp<SkImage>> expectedImages;
115
116 for (int i=0; i<NUM_FRAMES; i++) {
117 SkCanvas* pictureCanvas = multipic->beginPage(WIDTH, HEIGHT);
118 draw_advanced(pictureCanvas, i, image, sub);
119 multipic->endPage();
120 // Also draw the picture to an image for later comparison
121 auto surf = SkSurface::MakeRaster(info);
122 draw_advanced(surf->getCanvas(), i, image, sub);
123 expectedImages.push_back(surf->makeImageSnapshot());
124 }
125 // Finalize
126 multipic->close();
127
128 // Confirm written data is at least as large as the magic word
129 std::unique_ptr<SkStreamAsset> writtenStream = stream.detachAsStream();
130 REPORTER_ASSERT(reporter, writtenStream->getLength() > 24,
131 "Written data length too short (%zu)", writtenStream->getLength());
132 // SkDebugf("Multi Frame file size = %zu\n", writtenStream->getLength());
133
134 // Set up deserialization
135 SkSharingDeserialContext deserialContext;
136 SkDeserialProcs dprocs;
137 dprocs.fImageProc = SkSharingDeserialContext::deserializeImage;
138 dprocs.fImageCtx = &deserialContext;
139
140 // Confirm data is a MultiPictureDocument
141 int frame_count = SkMultiPictureDocumentReadPageCount(writtenStream.get());
142 REPORTER_ASSERT(reporter, frame_count == NUM_FRAMES,
143 "Expected %d frames, got %d. \n 0 frames may indicate the written file was not a "
144 "MultiPictureDocument.", NUM_FRAMES, frame_count);
145
146 // Deserailize
147 std::vector<SkDocumentPage> frames(frame_count);
148 REPORTER_ASSERT(reporter,
149 SkMultiPictureDocumentRead(writtenStream.get(), frames.data(), frame_count, &dprocs),
150 "Failed while reading MultiPictureDocument");
151
152 // Examine each frame.
153 int i=0;
154 for (const auto& frame : frames) {
155 SkRect bounds = frame.fPicture->cullRect();
156 REPORTER_ASSERT(reporter, bounds.width() == WIDTH,
157 "Page width: expected (%d) got (%d)", WIDTH, (int)bounds.width());
158 REPORTER_ASSERT(reporter, bounds.height() == HEIGHT,
159 "Page height: expected (%d) got (%d)", HEIGHT, (int)bounds.height());
160
161 auto surf = SkSurface::MakeRaster(info);
162 surf->getCanvas()->drawPicture(frame.fPicture);
163 auto img = surf->makeImageSnapshot();
164 REPORTER_ASSERT(reporter, ToolUtils::equal_pixels(img.get(), expectedImages[i].get()));
165
166 i++;
167 }
168 }
169
170
171 #if SK_SUPPORT_GPU && defined(SK_BUILD_FOR_ANDROID) && __ANDROID_API__ >= 26
172
173 #include "include/gpu/GrDirectContext.h"
174 #include "src/gpu/GrAHardwareBufferUtils.h"
175 #include "src/gpu/GrDirectContextPriv.h"
176
177 #include <android/hardware_buffer.h>
178
179 static const int DEV_W = 16, DEV_H = 16;
180
get_src_color(int x,int y)181 static SkPMColor get_src_color(int x, int y) {
182 SkASSERT(x >= 0 && x < DEV_W);
183 SkASSERT(y >= 0 && y < DEV_H);
184
185 U8CPU r = x;
186 U8CPU g = y;
187 U8CPU b = 0xc;
188
189 U8CPU a = 0xff;
190 switch ((x+y) % 5) {
191 case 0:
192 a = 0xff;
193 break;
194 case 1:
195 a = 0x80;
196 break;
197 case 2:
198 a = 0xCC;
199 break;
200 case 4:
201 a = 0x01;
202 break;
203 case 3:
204 a = 0x00;
205 break;
206 }
207 a = 0xff;
208 return SkPremultiplyARGBInline(a, r, g, b);
209 }
210
make_src_bitmap()211 static SkBitmap make_src_bitmap() {
212 static SkBitmap bmp;
213 if (bmp.isNull()) {
214 bmp.allocN32Pixels(DEV_W, DEV_H);
215 intptr_t pixels = reinterpret_cast<intptr_t>(bmp.getPixels());
216 for (int y = 0; y < DEV_H; ++y) {
217 for (int x = 0; x < DEV_W; ++x) {
218 SkPMColor* pixel = reinterpret_cast<SkPMColor*>(
219 pixels + y * bmp.rowBytes() + x * bmp.bytesPerPixel());
220 *pixel = get_src_color(x, y);
221 }
222 }
223 }
224 return bmp;
225 }
226
cleanup_resources(AHardwareBuffer * buffer)227 static void cleanup_resources(AHardwareBuffer* buffer) {
228 if (buffer) {
229 AHardwareBuffer_release(buffer);
230 }
231 }
232
makeAHardwareBufferTestImage(skiatest::Reporter * reporter,GrDirectContext * context,AHardwareBuffer * buffer)233 static sk_sp<SkImage> makeAHardwareBufferTestImage(
234 skiatest::Reporter* reporter, GrDirectContext* context, AHardwareBuffer* buffer) {
235
236 const SkBitmap srcBitmap = make_src_bitmap();
237
238 AHardwareBuffer_Desc hwbDesc;
239 hwbDesc.width = DEV_W;
240 hwbDesc.height = DEV_H;
241 hwbDesc.layers = 1;
242 hwbDesc.usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN |
243 AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN |
244 AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE;
245 hwbDesc.format = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM;
246 // The following three are not used in the allocate
247 hwbDesc.stride = 0;
248 hwbDesc.rfu0= 0;
249 hwbDesc.rfu1= 0;
250
251 if (int error = AHardwareBuffer_allocate(&hwbDesc, &buffer)) {
252 ERRORF(reporter, "Failed to allocated hardware buffer, error: %d", error);
253 cleanup_resources(buffer);
254 return nullptr;
255 }
256
257 // Get actual desc for allocated buffer so we know the stride for uploading cpu data.
258 AHardwareBuffer_describe(buffer, &hwbDesc);
259
260 void* bufferAddr;
261 if (AHardwareBuffer_lock(buffer, AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN, -1, nullptr,
262 &bufferAddr)) {
263 ERRORF(reporter, "Failed to lock hardware buffer");
264 cleanup_resources(buffer);
265 return nullptr;
266 }
267
268 // fill buffer
269 int bbp = srcBitmap.bytesPerPixel();
270 uint32_t* src = (uint32_t*)srcBitmap.getPixels();
271 int nextLineStep = DEV_W;
272 uint32_t* dst = static_cast<uint32_t*>(bufferAddr);
273 for (int y = 0; y < DEV_H; ++y) {
274 memcpy(dst, src, DEV_W * bbp);
275 src += nextLineStep;
276 dst += hwbDesc.stride;
277 }
278 AHardwareBuffer_unlock(buffer, nullptr);
279
280 // Make SkImage from buffer in a way that mimics libs/hwui/AutoBackendTextureRelease
281 GrBackendFormat backendFormat =
282 GrAHardwareBufferUtils::GetBackendFormat(context, buffer, hwbDesc.format, false);
283 GrAHardwareBufferUtils::DeleteImageProc deleteProc;
284 GrAHardwareBufferUtils::UpdateImageProc updateProc;
285 GrAHardwareBufferUtils::TexImageCtx imageCtx;
286 GrBackendTexture texture = GrAHardwareBufferUtils::MakeBackendTexture(
287 context, buffer, hwbDesc.width, hwbDesc.height,
288 &deleteProc, // set by MakeBackendTexture
289 &updateProc, // set by MakeBackendTexture
290 &imageCtx, // set by MakeBackendTexture
291 false, // don't make protected image
292 backendFormat,
293 false // isRenderable
294 );
295 SkColorType colorType = GrAHardwareBufferUtils::GetSkColorTypeFromBufferFormat(hwbDesc.format);
296 sk_sp<SkImage> image = SkImage::MakeFromTexture(
297 context, texture, kTopLeft_GrSurfaceOrigin, colorType, kPremul_SkAlphaType,
298 SkColorSpace::MakeSRGB(),
299 nullptr, // no release proc
300 nullptr // context for release proc
301 );
302
303 REPORTER_ASSERT(reporter, image);
304 REPORTER_ASSERT(reporter, image->isTextureBacked());
305 return image;
306 }
307
308 // Test the onEndPage callback's intended use by processing an mskp containing AHardwareBuffer-backed SkImages
309 // Expected behavior is that the callback is called while the AHardwareBuffer is still valid and the
310 // images are copied so .close() can still access them.
311 // Confirm deserialized file contains images with correct data.
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkMultiPictureDocument_AHardwarebuffer,reporter,ctx_info)312 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkMultiPictureDocument_AHardwarebuffer,
313 reporter, ctx_info) {
314 auto context = ctx_info.directContext();
315 if (!context->priv().caps()->supportsAHardwareBufferImages()) {
316 return;
317 }
318
319 // Create the stream we will serialize into.
320 SkDynamicMemoryWStream stream;
321
322 // Create the image sharing proc.
323 SkSharingSerialContext ctx;
324 SkSerialProcs procs;
325 procs.fImageProc = SkSharingSerialContext::serializeImage;
326 procs.fImageCtx = &ctx;
327
328 // Create the multi picture document used for recording frames.
329 // Pass a lambda as the onEndPage callback that captures our sharing context
330 sk_sp<SkDocument> multipic = SkMakeMultiPictureDocument(&stream, &procs,
331 [sharingCtx = &ctx](const SkPicture* pic) {
332 SkSharingSerialContext::collectNonTextureImagesFromPicture(pic, sharingCtx);
333 });
334
335 static const int WIDTH = 256;
336 static const int HEIGHT = 256;
337
338 // Make an image to be used in a later step.
339 AHardwareBuffer* ahbuffer = nullptr;
340 sk_sp<SkImage> image = makeAHardwareBufferTestImage(reporter, context, ahbuffer);
341
342 const SkImageInfo info = SkImageInfo::MakeN32Premul(WIDTH, HEIGHT);
343 std::vector<sk_sp<SkImage>> expectedImages;
344
345 // Record single frame
346 SkCanvas* pictureCanvas = multipic->beginPage(WIDTH, HEIGHT);
347 draw_basic(pictureCanvas, 0, image);
348 multipic->endPage();
349 // Also draw the picture to an image for later comparison
350 auto surf = SkSurface::MakeRaster(info);
351 draw_basic(surf->getCanvas(), 0, image);
352 expectedImages.push_back(surf->makeImageSnapshot());
353
354 // Release Ahardwarebuffer. If the code under test has not copied it already,
355 // close() will fail.
356 // Note that this only works because we're doing one frame only. If this test were recording
357 // two or more frames, it would have change the buffer contents instead.
358 cleanup_resources(ahbuffer);
359
360 // Finalize
361 multipic->close();
362
363 // Confirm written data is at least as large as the magic word
364 std::unique_ptr<SkStreamAsset> writtenStream = stream.detachAsStream();
365 REPORTER_ASSERT(reporter, writtenStream->getLength() > 24,
366 "Written data length too short (%zu)", writtenStream->getLength());
367
368 // Set up deserialization
369 SkSharingDeserialContext deserialContext;
370 SkDeserialProcs dprocs;
371 dprocs.fImageProc = SkSharingDeserialContext::deserializeImage;
372 dprocs.fImageCtx = &deserialContext;
373
374 // Confirm data is a MultiPictureDocument
375 int frame_count = SkMultiPictureDocumentReadPageCount(writtenStream.get());
376 REPORTER_ASSERT(reporter, frame_count == 1,
377 "Expected 1 frame, got %d. \n 0 frames may indicate the written file was not a "
378 "MultiPictureDocument.", frame_count);
379
380 // Deserialize
381 std::vector<SkDocumentPage> frames(frame_count);
382 REPORTER_ASSERT(reporter,
383 SkMultiPictureDocumentRead(writtenStream.get(), frames.data(), frame_count, &dprocs),
384 "Failed while reading MultiPictureDocument");
385
386 // Examine frame.
387 SkRect bounds = frames[0].fPicture->cullRect();
388 REPORTER_ASSERT(reporter, bounds.width() == WIDTH,
389 "Page width: expected (%d) got (%d)", WIDTH, (int)bounds.width());
390 REPORTER_ASSERT(reporter, bounds.height() == HEIGHT,
391 "Page height: expected (%d) got (%d)", HEIGHT, (int)bounds.height());
392
393 auto surf2 = SkSurface::MakeRaster(info);
394 surf2->getCanvas()->drawPicture(frames[0].fPicture);
395 auto img = surf2->makeImageSnapshot();
396 REPORTER_ASSERT(reporter, ToolUtils::equal_pixels(img.get(), expectedImages[0].get()));
397 }
398
399 #endif // android compilation
400