Flutter Engine
The Flutter Engine
MultiPictureDocumentTest.cpp
Go to the documentation of this file.
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
14#include "include/core/SkFont.h"
18#include "include/core/SkPath.h"
22#include "include/core/SkRect.h"
32#include "tests/Test.h"
33#include "tools/SkSharingProc.h"
34#include "tools/ToolUtils.h"
36
37#include <memory>
38#include <vector>
39
40// Covers rects, ovals, paths, images, text
41static void draw_basic(SkCanvas* canvas, int seed, sk_sp<SkImage> image) {
42 canvas->drawColor(SK_ColorWHITE);
43
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
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
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;
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.
85static 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
97DEF_TEST(SkMultiPictureDocument_Serialize_and_deserialize, reporter) {
98 // Create the stream we will serialize into.
100
101 // Create the image sharing proc.
103 SkSerialProcs procs;
105 procs.fImageCtx = &ctx;
106
107 // Create the multi picture document used for recording frames.
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.
116 surface->getCanvas()->clear(SK_ColorGREEN);
117 sk_sp<SkImage> image(surface->makeImageSnapshot());
119
120 // Make a subpicture to be used in a later step
122 SkCanvas* subCanvas = pr.beginRecording(100, 100);
123 draw_basic(subCanvas, 42, image);
125
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;
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);
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();
170 "Page width: expected (%d) got (%d)", WIDTH, (int)bounds.width());
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
196
198
199static const int DEV_W = 16, DEV_H = 16;
200
201static 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
231static SkBitmap make_src_bitmap() {
232 static SkBitmap bmp;
233 if (bmp.isNull()) {
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
247static void cleanup_resources(AHardwareBuffer* buffer) {
248 if (buffer) {
249 AHardwareBuffer_release(buffer);
250 }
251}
252
253static sk_sp<SkImage> makeAHardwareBufferTestImage(
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);
317 texture,
319 colorType,
322 deleteProc,
323 imageCtx);
324
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.
334DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(SkMultiPictureDocument_AHardwarebuffer,
335 reporter,
336 ctx_info,
338 auto context = ctx_info.directContext();
339 if (!context->priv().caps()->supportsAHardwareBufferImages()) {
340 return;
341 }
342
343 // Create the stream we will serialize into.
345
346 // Create the image sharing proc.
348 SkSerialProcs procs;
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
355 [sharingCtx = &ctx](const SkPicture* pic) {
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
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;
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);
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();
413 "Page width: expected (%d) got (%d)", WIDTH, (int)bounds.width());
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
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition: DM.cpp:213
static const int DEV_H
static const int DEV_W
reporter
Definition: FontMgrTest.cpp:39
struct AHardwareBuffer AHardwareBuffer
@ kTopLeft_GrSurfaceOrigin
Definition: GrTypes.h:148
static void draw_advanced(SkCanvas *canvas, int seed, sk_sp< SkImage > image, sk_sp< SkPicture > sub)
DEF_TEST(SkMultiPictureDocument_Serialize_and_deserialize, reporter)
static void draw_basic(SkCanvas *canvas, int seed, sk_sp< SkImage > image)
static SkPMColor get_src_color(int x, int y)
@ kPremul_SkAlphaType
pixel components are premultiplied by alpha
Definition: SkAlphaType.h:29
#define SkASSERT(cond)
Definition: SkAssert.h:116
unsigned U8CPU
Definition: SkCPUTypes.h:18
static SkPMColor SkPremultiplyARGBInline(U8CPU a, U8CPU r, U8CPU g, U8CPU b)
Definition: SkColorPriv.h:126
SkColorType
Definition: SkColorType.h:19
constexpr SkColor SK_ColorYELLOW
Definition: SkColor.h:139
constexpr SkColor SK_ColorCYAN
Definition: SkColor.h:143
uint32_t SkPMColor
Definition: SkColor.h:205
constexpr SkColor SK_ColorBLUE
Definition: SkColor.h:135
constexpr SkColor SK_ColorRED
Definition: SkColor.h:126
constexpr SkColor SK_ColorGREEN
Definition: SkColor.h:131
constexpr SkColor SK_ColorWHITE
Definition: SkColor.h:122
static SkColorType colorType(AImageDecoder *decoder, const AImageDecoderHeaderInfo *headerInfo)
SK_API SkString SkStringPrintf(const char *format,...) SK_PRINTF_LIKE(1
Creates a new string and writes into it using a printf()-style format.
#define REPORTER_ASSERT(r, cond,...)
Definition: Test.h:286
#define ERRORF(r,...)
Definition: Test.h:293
#define DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(name, reporter, context_info, ctsEnforcement)
Definition: Test.h:434
#define WIDTH
#define HEIGHT
const GrCaps * caps() const
bool supportsAHardwareBufferImages() const
Definition: GrCaps.h:397
GrDirectContextPriv priv()
size_t rowBytes() const
Definition: SkBitmap.h:238
bool isNull() const
Definition: SkBitmap.h:219
void * getPixels() const
Definition: SkBitmap.h:283
int bytesPerPixel() const
Definition: SkBitmap.h:187
void allocN32Pixels(int width, int height, bool isOpaque=false)
Definition: SkBitmap.cpp:232
void drawRect(const SkRect &rect, const SkPaint &paint)
Definition: SkCanvas.cpp:1673
void restore()
Definition: SkCanvas.cpp:461
void translate(SkScalar dx, SkScalar dy)
Definition: SkCanvas.cpp:1278
void drawColor(SkColor color, SkBlendMode mode=SkBlendMode::kSrcOver)
Definition: SkCanvas.h:1182
void drawRRect(const SkRRect &rrect, const SkPaint &paint)
Definition: SkCanvas.cpp:1705
void drawRoundRect(const SkRect &rect, SkScalar rx, SkScalar ry, const SkPaint &paint)
Definition: SkCanvas.cpp:2717
void drawImageRect(const SkImage *, const SkRect &src, const SkRect &dst, const SkSamplingOptions &, const SkPaint *, SrcRectConstraint)
Definition: SkCanvas.cpp:2333
int save()
Definition: SkCanvas.cpp:447
void drawPath(const SkPath &path, const SkPaint &paint)
Definition: SkCanvas.cpp:1747
void drawPicture(const SkPicture *picture)
Definition: SkCanvas.h:1961
void drawTextBlob(const SkTextBlob *blob, SkScalar x, SkScalar y, const SkPaint &paint)
Definition: SkCanvas.cpp:2484
void drawImage(const SkImage *image, SkScalar left, SkScalar top)
Definition: SkCanvas.h:1528
void drawCircle(SkScalar cx, SkScalar cy, SkScalar radius, const SkPaint &paint)
Definition: SkCanvas.cpp:2707
static sk_sp< SkColorSpace > MakeSRGB()
Definition: SkFont.h:35
virtual bool isTextureBacked() const =0
@ kStroke_Style
set to stroke geometry
Definition: SkPaint.h:194
Definition: SkPath.h:59
SkCanvas * beginRecording(const SkRect &bounds, sk_sp< SkBBoxHierarchy > bbh)
sk_sp< SkPicture > finishRecordingAsPicture()
static sk_sp< SkTextBlob > MakeFromString(const char *string, const SkFont &font, SkTextEncoding encoding=SkTextEncoding::kUTF8)
Definition: SkTextBlob.h:115
const Paint & paint
Definition: color_source.cc:38
VkSurfaceKHR surface
Definition: main.cc:49
double frame
Definition: examples.cpp:31
static bool b
struct MyStruct a[10]
const uint8_t uint32_t uint32_t GError ** error
std::u16string text
FlTexture * texture
double y
double x
SK_API sk_sp< SkImage > BorrowTextureFrom(GrRecordingContext *context, const GrBackendTexture &backendTexture, GrSurfaceOrigin origin, SkColorType colorType, SkAlphaType alphaType, sk_sp< SkColorSpace > colorSpace, TextureReleaseProc textureReleaseProc=nullptr, ReleaseContext releaseContext=nullptr)
SK_API sk_sp< SkDocument > Make(SkWStream *dst, const SkSerialProcs *=nullptr, std::function< void(const SkPicture *)> onEndPage=nullptr)
SK_API bool Read(SkStreamSeekable *src, SkDocumentPage *dstArray, int dstArrayCount, const SkDeserialProcs *=nullptr)
SK_API int ReadPageCount(SkStreamSeekable *src)
Optional< SkRect > bounds
Definition: SkRecords.h:189
sk_sp< const SkImage > image
Definition: SkRecords.h:269
SkRect oval
Definition: SkRecords.h:249
sk_sp< SkBlender > blender SkRect rect
Definition: SkRecords.h:350
SK_API sk_sp< SkSurface > Raster(const SkImageInfo &imageInfo, size_t rowBytes, const SkSurfaceProps *surfaceProps)
SkFont DefaultFont()
bool equal_pixels(const SkPixmap &a, const SkPixmap &b)
Definition: ToolUtils.cpp:456
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
Definition: switches.h:57
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace buffer
Definition: switches.h:126
font
Font Metadata and Metrics.
dst
Definition: cp.py:12
SkSamplingOptions(SkFilterMode::kLinear))
SkDeserialImageProc fImageProc
static SkImageInfo MakeN32Premul(int width, int height)
static constexpr SkRect MakeXYWH(float x, float y, float w, float h)
Definition: SkRect.h:659
void offset(float dx, float dy)
Definition: SkRect.h:1016
void * fImageCtx
Definition: SkSerialProcs.h:91
SkSerialImageProc fImageProc
Definition: SkSerialProcs.h:90
static sk_sp< SkImage > deserializeImage(const void *data, size_t length, void *ctx)
static sk_sp< SkData > serializeImage(SkImage *img, void *ctx)
static void collectNonTextureImagesFromPicture(const SkPicture *pic, SkSharingSerialContext *sharingCtx)