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