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