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