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