• 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 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