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