Flutter Engine
The Flutter Engine
SimpleVias.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2023 Google LLC
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
8#include "gm/gm.h"
20#include "src/base/SkBase64.h"
22
23#include <sstream>
24#include <string>
25
26// Implements the "direct" via. It draws the GM directly on the surface under test without any
27// additional behaviors. Equivalent to running a GM with DM without using any vias.
29 // Run the GM.
30 SkString msg;
31 skiagm::GM::DrawResult result = gm->draw(surface->getCanvas(), &msg);
33 return {result, msg.c_str()};
34 }
35
36 // Extract a bitmap from the surface.
38 bitmap.allocPixelsFlags(surface->getCanvas()->imageInfo(), SkBitmap::kZeroPixels_AllocFlag);
39 if (!surface->readPixels(bitmap, 0, 0)) {
40 return {skiagm::DrawResult::kFail, "Could not read pixels from surface"};
41 }
42 return {result, msg.c_str(), bitmap};
43}
44
45// Encodes a bitmap as a base-64 image/png URI. In the presence of errors, the returned string will
46// contain a human-friendly error message. Otherwise the string will start with "data:image/png".
47//
48// Based on
49// https://skia.googlesource.com/skia/+/650c980daa72d887602e701db8f84072e26d4d48/tests/TestUtils.cpp#127.
50static std::string bitmap_to_base64_data_uri(const SkBitmap& bitmap) {
51 SkPixmap pm;
52 if (!bitmap.peekPixels(&pm)) {
53 return "peekPixels failed";
54 }
55
56 // We're going to embed this PNG in a data URI, so make it as small as possible.
59 options.fZLibLevel = 9;
60
62 if (!SkPngEncoder::Encode(&wStream, pm, options)) {
63 return "SkPngEncoder::Encode failed";
64 }
65
66 sk_sp<SkData> pngData = wStream.detachAsData();
67 size_t len = SkBase64::EncodedSize(pngData->size());
68
69 // The PNG can be almost arbitrarily large. We don't want to fill our logs with enormous URLs
70 // and should only output them on failure.
71 static const size_t kMaxBase64Length = 1024 * 1024;
72 if (len > kMaxBase64Length) {
73 return SkStringPrintf("Encoded image too large (%lu bytes)", len).c_str();
74 }
75
76 std::string out;
77 out.resize(len);
78 SkBase64::Encode(pngData->data(), pngData->size(), out.data());
79 return "data:image/png;base64," + out;
80}
81
82// Implements the "picture" and "picture_serialization" vias. The "serialize" argument determines
83// which of the two vias is used.
84//
85// The "picture" via is based on DM's ViaPicture class here:
86// https://skia.googlesource.com/skia/+/dcc56df202cca129edda3f6f8bae04ec306b264e/dm/DMSrcSink.cpp#2310.
87//
88// The "picture_serialization" via is based on DM's ViaSerialization class here:
89// https://skia.googlesource.com/skia/+/dcc56df202cca129edda3f6f8bae04ec306b264e/dm/DMSrcSink.cpp#2281.
90static GMOutput draw_via_picture(skiagm::GM* gm, SkSurface* surface, bool serialize) {
91 // Draw GM on a recording canvas.
92 SkPictureRecorder recorder;
93 SkCanvas* recordingCanvas =
94 recorder.beginRecording(gm->getISize().width(), gm->getISize().height());
95 SkString msg;
96 skiagm::DrawResult result = gm->draw(recordingCanvas, &msg);
98 return {result, msg.c_str()};
99 }
100
101 // Finish recording, and optionally serialize and then deserialize the resulting picture using
102 // the PNG encoder/decoder.
104 if (serialize) {
105 SkSerialProcs serialProcs = {.fImageProc = [](SkImage* img, void*) -> sk_sp<SkData> {
107 return SkPngEncoder::Encode(nullptr, img, SkPngEncoder::Options{});
108 }};
109 SkDeserialProcs deserialProcs = {.fImageDataProc = [](sk_sp<SkData> data,
110 std::optional<SkAlphaType>,
111 void* ctx) -> sk_sp<SkImage> {
112 SkCodec::Result decodeResult;
113 std::unique_ptr<SkCodec> codec = SkPngDecoder::Decode(data, &decodeResult);
114 SkASSERT(decodeResult == SkCodec::Result::kSuccess);
115 auto [image, getImageResult] = codec->getImage();
116 SkASSERT(getImageResult == SkCodec::Result::kSuccess);
117 return image;
118 }};
119 pic = SkPicture::MakeFromData(pic->serialize(&serialProcs).get(), &deserialProcs);
120 }
121
122 // Draw the recorded picture on the surface under test and extract it as a bitmap.
123 surface->getCanvas()->drawPicture(pic);
124 SkBitmap recordedBitmap;
125 recordedBitmap.allocPixelsFlags(surface->getCanvas()->imageInfo(),
127 if (!surface->readPixels(recordedBitmap, 0, 0)) {
128 return {skiagm::DrawResult::kFail, "Could not read recorded picture pixels from surface"};
129 }
130
131 // Draw GM on the surface under test and extract the reference bitmap.
132 result = gm->draw(surface->getCanvas(), &msg);
134 return {result, msg.c_str()};
135 }
136 SkBitmap referenceBitmap;
137 referenceBitmap.allocPixelsFlags(surface->getCanvas()->imageInfo(),
139 if (!surface->readPixels(referenceBitmap, 0, 0)) {
140 return {skiagm::DrawResult::kFail, "Could not read reference picture pixels from surface"};
141 }
142
143 // The recorded and reference bitmaps should be identical.
144 if (recordedBitmap.computeByteSize() != referenceBitmap.computeByteSize()) {
146 SkStringPrintf("Recorded and reference bitmap dimensions do not match: "
147 "expected byte size %lu, width %d and height %d; "
148 "got %lu, %d and %d",
149 referenceBitmap.computeByteSize(),
150 referenceBitmap.bounds().width(),
151 referenceBitmap.bounds().height(),
152 recordedBitmap.computeByteSize(),
153 recordedBitmap.bounds().width(),
154 recordedBitmap.bounds().height())
155 .c_str()};
156 }
157 if (0 != memcmp(recordedBitmap.getPixels(),
158 referenceBitmap.getPixels(),
159 referenceBitmap.computeByteSize())) {
161 SkStringPrintf("Recorded and reference bitmap pixels do not match.\n"
162 "Recorded image:\n%s\nReference image:\n%s",
163 bitmap_to_base64_data_uri(recordedBitmap).c_str(),
164 bitmap_to_base64_data_uri(referenceBitmap).c_str())
165 .c_str()};
166 }
167
168 return {result, msg.c_str(), referenceBitmap};
169}
170
171// This draw() implementation supports the "direct", "picture" and "picture_serialization" vias.
172//
173// The "direct" via draws the GM directly on the surface under test with no additional behaviors.
174// It is equivalent to running a GM with DM without using a via.
175//
176// The "picture" via tests that if we record a GM using an SkPictureRecorder, the bitmap produced
177// by drawing the recorded picture on the surface under test is the same as the bitmap obtained by
178// drawing the GM directly on the surface under test.
179//
180// The "picture_serialization" via is identical to the "picture" via, except that the recorded
181// picture is serialized and then deserialized before being drawn on the surface under test.
182GMOutput draw(skiagm::GM* gm, SkSurface* surface, std::string via) {
183 if (via == "direct") {
184 return draw_direct(gm, surface);
185 } else if (via == "picture") {
186 return draw_via_picture(gm, surface, /* serialize= */ false);
187 } else if (via == "picture_serialization") {
188 return draw_via_picture(gm, surface, /* serialize= */ true);
189 }
190 SK_ABORT("unknown --via flag value: %s", via.c_str());
191}
const char * options
GMOutput draw(skiagm::GM *gm, SkSurface *surface, std::string via)
Definition: SimpleVias.cpp:182
static std::string bitmap_to_base64_data_uri(const SkBitmap &bitmap)
Definition: SimpleVias.cpp:50
static GMOutput draw_direct(skiagm::GM *gm, SkSurface *surface)
Definition: SimpleVias.cpp:28
static GMOutput draw_via_picture(skiagm::GM *gm, SkSurface *surface, bool serialize)
Definition: SimpleVias.cpp:90
#define SK_ABORT(message,...)
Definition: SkAssert.h:70
#define SkASSERT_RELEASE(cond)
Definition: SkAssert.h:100
#define SkASSERT(cond)
Definition: SkAssert.h:116
SK_API SkString SkStringPrintf(const char *format,...) SK_PRINTF_LIKE(1
Creates a new string and writes into it using a printf()-style format.
size_t computeByteSize() const
Definition: SkBitmap.h:293
void * getPixels() const
Definition: SkBitmap.h:283
SkIRect bounds() const
Definition: SkBitmap.h:382
@ kZeroPixels_AllocFlag
zero pixel memory. No effect. This is the default.
Definition: SkBitmap.h:435
void allocPixelsFlags(const SkImageInfo &info, uint32_t flags)
Definition: SkBitmap.cpp:251
Result
Definition: SkCodec.h:76
const void * data() const
Definition: SkData.h:37
size_t size() const
Definition: SkData.h:30
sk_sp< SkData > detachAsData()
Definition: SkStream.cpp:707
virtual bool isTextureBacked() const =0
SkCanvas * beginRecording(const SkRect &bounds, sk_sp< SkBBoxHierarchy > bbh)
sk_sp< SkPicture > finishRecordingAsPicture()
sk_sp< SkData > serialize(const SkSerialProcs *procs=nullptr) const
Definition: SkPicture.cpp:249
static sk_sp< SkPicture > MakeFromData(const SkData *data, const SkDeserialProcs *procs=nullptr)
Definition: SkPicture.cpp:160
const char * c_str() const
Definition: SkString.h:133
T * get() const
Definition: SkRefCnt.h:303
Definition: gm.h:110
virtual SkISize getISize()=0
DrawResult draw(SkCanvas *canvas)
Definition: gm.h:140
@ kSuccess
Definition: embedder.h:73
VkSurfaceKHR surface
Definition: main.cc:49
GAsyncResult * result
SK_API std::unique_ptr< SkCodec > Decode(std::unique_ptr< SkStream >, SkCodec::Result *, SkCodecs::DecodeContext=nullptr)
SK_API bool Encode(SkWStream *dst, const SkPixmap &src, const Options &options)
sk_sp< const SkImage > image
Definition: SkRecords.h:269
Definition: bitmap.py:1
DrawResult
Definition: gm.h:104
Definition: Draw.h:18
static size_t EncodedSize(size_t srcDataLength)
Definition: SkBase64.h:40
static size_t Encode(const void *src, size_t length, void *dst, const char *encode=nullptr)
Definition: SkBase64.cpp:113
SkDeserialImageFromDataProc fImageDataProc
constexpr int32_t height() const
Definition: SkRect.h:165
constexpr int32_t width() const
Definition: SkRect.h:158
constexpr int32_t width() const
Definition: SkSize.h:36
constexpr int32_t height() const
Definition: SkSize.h:37
SkSerialImageProc fImageProc
Definition: SkSerialProcs.h:90
std::shared_ptr< const fml::Mapping > data
Definition: texture_gles.cc:63