blob: bd8e2fa223d43ffc50e2fd6ef1cbff0332489757 [file] [log] [blame]
Nathaniel Nifong0426c382019-06-21 11:09:19 -04001/*
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 *
Nathaniel Nifong7ee3f932019-09-25 14:52:44 -04007 * This test confirms that a MultiPictureDocument can be serialized and deserailzied without error.
8 * And that the pictures within it are re-created accurately
Nathaniel Nifong0426c382019-06-21 11:09:19 -04009 */
10
Mike Reed47d795a2020-08-24 13:21:51 -040011#include "include/core/SkCanvas.h"
Brian Salomon5392c942021-03-30 16:14:37 -040012#include "include/core/SkColorPriv.h"
Nathaniel Nifong4a565682021-01-05 10:34:48 -050013#include "include/core/SkColorSpace.h"
Nathaniel Nifong0426c382019-06-21 11:09:19 -040014#include "include/core/SkDocument.h"
Nathaniel Nifong0426c382019-06-21 11:09:19 -040015#include "include/core/SkFont.h"
Nathaniel Nifong4a565682021-01-05 10:34:48 -050016#include "include/core/SkImage.h"
Mike Klein52337de2019-07-25 09:00:52 -050017#include "include/core/SkPicture.h"
Nathaniel Nifong7ee3f932019-09-25 14:52:44 -040018#include "include/core/SkPictureRecorder.h"
Mike Klein52337de2019-07-25 09:00:52 -050019#include "include/core/SkString.h"
20#include "include/core/SkSurface.h"
21#include "include/core/SkTextBlob.h"
Brian Salomon5392c942021-03-30 16:14:37 -040022#include "src/gpu/GrCaps.h"
Nathaniel Nifong0426c382019-06-21 11:09:19 -040023#include "src/utils/SkMultiPictureDocument.h"
Nathaniel Nifong0426c382019-06-21 11:09:19 -040024#include "tests/Test.h"
Mike Klein52337de2019-07-25 09:00:52 -050025#include "tools/SkSharingProc.h"
Mike Reed47d795a2020-08-24 13:21:51 -040026#include "tools/ToolUtils.h"
Nathaniel Nifong0426c382019-06-21 11:09:19 -040027
Nathaniel Nifong7ee3f932019-09-25 14:52:44 -040028// Covers rects, ovals, paths, images, text
29static void draw_basic(SkCanvas* canvas, int seed, sk_sp<SkImage> image) {
Nathaniel Nifong0426c382019-06-21 11:09:19 -040030 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
Mike Reed039f1362021-01-27 21:21:08 -050058 canvas->drawImage(image, 128-seed, 128, SkSamplingOptions(), &paint);
Nathaniel Nifong0426c382019-06-21 11:09:19 -040059
60 if (seed % 2 == 0) {
61 SkRect rect2 = SkRect::MakeXYWH(0, 0, 40, 60);
Mike Reed039f1362021-01-27 21:21:08 -050062 canvas->drawImageRect(image, rect2, SkSamplingOptions(), &paint);
Nathaniel Nifong0426c382019-06-21 11:09:19 -040063 }
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
Nathaniel Nifong7ee3f932019-09-25 14:52:44 -040071// Covers all of the above and drawing nested sub-pictures.
72static 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
Nathaniel Nifong4a565682021-01-05 10:34:48 -050084DEF_TEST(SkMultiPictureDocument_Serialize_and_deserialize, reporter) {
Nathaniel Nifong0426c382019-06-21 11:09:19 -040085 // 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
Nathaniel Nifong7ee3f932019-09-25 14:52:44 -040094 // Create the multi picture document used for recording frames.
Nathaniel Nifong0426c382019-06-21 11:09:19 -040095 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
Nathaniel Nifong7ee3f932019-09-25 14:52:44 -0400107 // 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
Mike Reed47d795a2020-08-24 13:21:51 -0400113 const SkImageInfo info = SkImageInfo::MakeN32Premul(WIDTH, HEIGHT);
Nathaniel Nifong4a565682021-01-05 10:34:48 -0500114 std::vector<sk_sp<SkImage>> expectedImages;
Mike Reed47d795a2020-08-24 13:21:51 -0400115
Nathaniel Nifong0426c382019-06-21 11:09:19 -0400116 for (int i=0; i<NUM_FRAMES; i++) {
117 SkCanvas* pictureCanvas = multipic->beginPage(WIDTH, HEIGHT);
Nathaniel Nifong7ee3f932019-09-25 14:52:44 -0400118 draw_advanced(pictureCanvas, i, image, sub);
Nathaniel Nifong0426c382019-06-21 11:09:19 -0400119 multipic->endPage();
Nathaniel Nifong4a565682021-01-05 10:34:48 -0500120 // Also draw the picture to an image for later comparison
Mike Reed47d795a2020-08-24 13:21:51 -0400121 auto surf = SkSurface::MakeRaster(info);
122 draw_advanced(surf->getCanvas(), i, image, sub);
Nathaniel Nifong4a565682021-01-05 10:34:48 -0500123 expectedImages.push_back(surf->makeImageSnapshot());
Nathaniel Nifong0426c382019-06-21 11:09:19 -0400124 }
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,
Adlai Holler684838f2020-05-12 10:41:04 -0400131 "Written data length too short (%zu)", writtenStream->getLength());
132 // SkDebugf("Multi Frame file size = %zu\n", writtenStream->getLength());
Nathaniel Nifong0426c382019-06-21 11:09:19 -0400133
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.
Nathaniel Nifong0426c382019-06-21 11:09:19 -0400153 int i=0;
154 for (const auto& frame : frames) {
155 SkRect bounds = frame.fPicture->cullRect();
156 REPORTER_ASSERT(reporter, bounds.width() == WIDTH,
Adlai Holler684838f2020-05-12 10:41:04 -0400157 "Page width: expected (%d) got (%d)", WIDTH, (int)bounds.width());
Nathaniel Nifong0426c382019-06-21 11:09:19 -0400158 REPORTER_ASSERT(reporter, bounds.height() == HEIGHT,
Adlai Holler684838f2020-05-12 10:41:04 -0400159 "Page height: expected (%d) got (%d)", HEIGHT, (int)bounds.height());
Nathaniel Nifong0426c382019-06-21 11:09:19 -0400160
Mike Reed47d795a2020-08-24 13:21:51 -0400161 auto surf = SkSurface::MakeRaster(info);
162 surf->getCanvas()->drawPicture(frame.fPicture);
163 auto img = surf->makeImageSnapshot();
Nathaniel Nifong4a565682021-01-05 10:34:48 -0500164 REPORTER_ASSERT(reporter, ToolUtils::equal_pixels(img.get(), expectedImages[i].get()));
Mike Reed47d795a2020-08-24 13:21:51 -0400165
Nathaniel Nifong0426c382019-06-21 11:09:19 -0400166 i++;
167 }
168}
Nathaniel Nifong4a565682021-01-05 10:34:48 -0500169
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
179static const int DEV_W = 16, DEV_H = 16;
180
181static 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
211static 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
227static void cleanup_resources(AHardwareBuffer* buffer) {
228 if (buffer) {
229 AHardwareBuffer_release(buffer);
230 }
231}
232
233static 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.
312DEF_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