Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
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
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;
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.
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.
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();
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
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
354 sk_sp<SkDocument> multipic = SkMultiPictureDocument::Make(&stream, &procs,
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
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)
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)
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 static SkString SkStringPrintf()
Definition SkString.h:287
#define DEF_TEST(name, reporter)
Definition Test.h:312
#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)
void restore()
Definition SkCanvas.cpp:465
void translate(SkScalar dx, SkScalar dy)
void drawColor(SkColor color, SkBlendMode mode=SkBlendMode::kSrcOver)
Definition SkCanvas.h:1182
void drawRRect(const SkRRect &rrect, const SkPaint &paint)
void drawRoundRect(const SkRect &rect, SkScalar rx, SkScalar ry, const SkPaint &paint)
void drawImageRect(const SkImage *, const SkRect &src, const SkRect &dst, const SkSamplingOptions &, const SkPaint *, SrcRectConstraint)
int save()
Definition SkCanvas.cpp:451
void drawPath(const SkPath &path, const SkPaint &paint)
void drawPicture(const SkPicture *picture)
Definition SkCanvas.h:1961
void drawTextBlob(const SkTextBlob *blob, SkScalar x, SkScalar y, const SkPaint &paint)
void drawImage(const SkImage *image, SkScalar left, SkScalar top)
Definition SkCanvas.h:1528
void drawCircle(SkScalar cx, SkScalar cy, SkScalar radius, const SkPaint &paint)
static sk_sp< SkColorSpace > MakeSRGB()
virtual bool isTextureBacked() const =0
@ kStroke_Style
set to stroke geometry
Definition SkPaint.h:194
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
VkSurfaceKHR surface
Definition main.cc:49
sk_sp< SkImage > image
Definition examples.cpp:29
double frame
Definition examples.cpp:31
static bool b
struct MyStruct a[10]
static const uint8_t buffer[]
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_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)
dst
Definition cp.py:12
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
SkSerialImageProc fImageProc
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)