Flutter Engine
The Flutter Engine
YUVTest.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
11#include "include/core/SkData.h"
15#include "include/core/SkSize.h"
23#include "tests/Test.h"
24#include "tools/Resources.h"
25
26#include <cmath>
27#include <cstdint>
28#include <memory>
29#include <utility>
30
32 const char path[],
33 const SkYUVAInfo* expectedInfo) {
34 std::unique_ptr<SkStream> stream(GetResourceAsStream(path));
35 if (!stream) {
36 return;
37 }
38 std::unique_ptr<SkCodec> codec(SkCodec::MakeFromStream(std::move(stream)));
40 if (!codec) {
41 return;
42 }
43
44 // Test queryYUBAInfo()
45 SkYUVAPixmapInfo yuvaPixmapInfo;
46
47 static constexpr auto kAllTypes = SkYUVAPixmapInfo::SupportedDataTypes::All();
48 static constexpr auto kNoTypes = SkYUVAPixmapInfo::SupportedDataTypes();
49
50 // SkYUVAInfo param is required to be non-null.
51 bool success = codec->queryYUVAInfo(kAllTypes, nullptr);
52 REPORTER_ASSERT(reporter, !success);
53 // Fails when there is no support for YUVA planes.
54 success = codec->queryYUVAInfo(kNoTypes, &yuvaPixmapInfo);
55 REPORTER_ASSERT(reporter, !success);
56
57 success = codec->queryYUVAInfo(kAllTypes, &yuvaPixmapInfo);
58 REPORTER_ASSERT(reporter, SkToBool(expectedInfo) == success);
59 if (!success) {
60 return;
61 }
62 REPORTER_ASSERT(reporter, *expectedInfo == yuvaPixmapInfo.yuvaInfo());
63
64 int numPlanes = yuvaPixmapInfo.numPlanes();
66 for (int i = 0; i < numPlanes; ++i) {
67 const SkImageInfo& planeInfo = yuvaPixmapInfo.planeInfo(i);
68 SkColorType planeCT = planeInfo.colorType();
69 REPORTER_ASSERT(reporter, !planeInfo.isEmpty());
71 REPORTER_ASSERT(reporter, planeInfo.validRowBytes(yuvaPixmapInfo.rowBytes(i)));
72 // Currently all planes must share a data type, gettable as SkYUVAPixmapInfo::dataType().
73 auto [numChannels, planeDataType] = SkYUVAPixmapInfo::NumChannelsAndDataType(planeCT);
74 REPORTER_ASSERT(reporter, planeDataType == yuvaPixmapInfo.dataType());
75 }
76 for (int i = numPlanes; i < SkYUVAInfo::kMaxPlanes; ++i) {
77 const SkImageInfo& planeInfo = yuvaPixmapInfo.planeInfo(i);
80 REPORTER_ASSERT(reporter, yuvaPixmapInfo.rowBytes(i) == 0);
81 }
82
83 // Allocate the memory for the YUV decode.
84 auto pixmaps = SkYUVAPixmaps::Allocate(yuvaPixmapInfo);
85 REPORTER_ASSERT(reporter, pixmaps.isValid());
86
87 for (int i = 0; i < SkYUVAPixmaps::kMaxPlanes; ++i) {
88 REPORTER_ASSERT(reporter, pixmaps.plane(i).info() == yuvaPixmapInfo.planeInfo(i));
89 }
90 for (int i = numPlanes; i < SkYUVAInfo::kMaxPlanes; ++i) {
91 REPORTER_ASSERT(reporter, pixmaps.plane(i).rowBytes() == 0);
92 }
93
94 // Test getYUVAPlanes()
95 REPORTER_ASSERT(reporter, SkCodec::kSuccess == codec->getYUVAPlanes(pixmaps));
96}
97
98DEF_TEST(Jpeg_YUV_Codec, r) {
99 auto setExpectations = [](SkISize dims, SkYUVAInfo::Subsampling subsampling) {
100 return SkYUVAInfo(dims,
102 subsampling,
107 };
108
109 SkYUVAInfo expectations = setExpectations({128, 128}, SkYUVAInfo::Subsampling::k420);
110 codec_yuv(r, "images/color_wheel.jpg", &expectations);
111
112 // H2V2
113 expectations = setExpectations({512, 512}, SkYUVAInfo::Subsampling::k420);
114 codec_yuv(r, "images/mandrill_512_q075.jpg", &expectations);
115
116 // H1V1
117 expectations = setExpectations({512, 512}, SkYUVAInfo::Subsampling::k444);
118 codec_yuv(r, "images/mandrill_h1v1.jpg", &expectations);
119
120 // H2V1
121 expectations = setExpectations({512, 512}, SkYUVAInfo::Subsampling::k422);
122 codec_yuv(r, "images/mandrill_h2v1.jpg", &expectations);
123
124 // Non-power of two dimensions
125 expectations = setExpectations({439, 154}, SkYUVAInfo::Subsampling::k420);
126 codec_yuv(r, "images/cropped_mandrill.jpg", &expectations);
127
128 expectations = setExpectations({8, 8}, SkYUVAInfo::Subsampling::k420);
129 codec_yuv(r, "images/randPixels.jpg", &expectations);
130
131 // Progressive images
132 expectations = setExpectations({512, 512}, SkYUVAInfo::Subsampling::k444);
133 codec_yuv(r, "images/brickwork-texture.jpg", &expectations);
134 codec_yuv(r, "images/brickwork_normal-map.jpg", &expectations);
135
136 // A CMYK encoded image should fail.
137 codec_yuv(r, "images/CMYK.jpg", nullptr);
138 // A grayscale encoded image should fail.
139 codec_yuv(r, "images/grayscale.jpg", nullptr);
140 // A PNG should fail.
141 codec_yuv(r, "images/arrow.png", nullptr);
142}
143
144SkYUVAPixmaps decode_yuva(skiatest::Reporter* r, std::unique_ptr<SkStream> stream) {
145 static constexpr auto kAllTypes = SkYUVAPixmapInfo::SupportedDataTypes::All();
147
148 std::unique_ptr<SkCodec> codec(SkCodec::MakeFromStream(std::move(stream)));
149 REPORTER_ASSERT(r, codec);
150
151 SkYUVAPixmapInfo yuvaPixmapInfo;
152 REPORTER_ASSERT(r, codec->queryYUVAInfo(kAllTypes, &yuvaPixmapInfo));
153 result = SkYUVAPixmaps::Allocate(yuvaPixmapInfo);
154 REPORTER_ASSERT(r, result.isValid());
155 REPORTER_ASSERT(r, SkCodec::kSuccess == codec->getYUVAPlanes(result));
156 return result;
157}
158
160 REPORTER_ASSERT(r, a.yuvaInfo() == b.yuvaInfo());
161 REPORTER_ASSERT(r, a.numPlanes() == b.numPlanes());
162 for (int plane = 0; plane < a.numPlanes(); ++plane) {
163 const SkPixmap& aPlane = a.plane(plane);
164 const SkPixmap& bPlane = b.plane(plane);
165 REPORTER_ASSERT(r, aPlane.computeByteSize() == bPlane.computeByteSize());
166 const uint8_t* aData = reinterpret_cast<const uint8_t*>(aPlane.addr());
167 const uint8_t* bData = reinterpret_cast<const uint8_t*>(bPlane.addr());
168 for (int row = 0; row < aPlane.height(); ++row) {
169 for (int col = 0; col < aPlane.width() * aPlane.info().bytesPerPixel(); ++col) {
170 int32_t aByte = aData[col];
171 int32_t bByte = bData[col];
172 // Allow at most one bit of difference.
173 REPORTER_ASSERT(r, std::abs(aByte - bByte) <= 1);
174 }
175 aData += aPlane.rowBytes();
176 bData += bPlane.rowBytes();
177 }
178 }
179}
180
181DEF_TEST(Jpeg_YUV_Encode, r) {
182 const char* paths[] = {
183 "images/color_wheel.jpg",
184 "images/mandrill_512_q075.jpg",
185 "images/mandrill_h1v1.jpg",
186 "images/mandrill_h2v1.jpg",
187 "images/cropped_mandrill.jpg",
188 "images/randPixels.jpg",
189 };
190 for (const auto* path : paths) {
191 SkYUVAPixmaps decoded;
192 {
193 std::unique_ptr<SkStream> stream(GetResourceAsStream(path));
194 decoded = decode_yuva(r, std::move(stream));
195 }
196
197 SkYUVAPixmaps roundtrip;
198 {
200 SkDynamicMemoryWStream encodeStream;
201 REPORTER_ASSERT(r, SkJpegEncoder::Encode(&encodeStream, decoded, nullptr, options));
202 auto encodedData = encodeStream.detachAsData();
203 roundtrip = decode_yuva(r, SkMemoryStream::Make(encodedData));
204 }
205
206 verify_same(r, decoded, roundtrip);
207 }
208}
209
210// Be sure that the two matrices are inverses of each other
211// (i.e. rgb2yuv and yuv2rgb
213 const SkYUVColorSpace spaces[] = {
219 };
220
221 // Not sure what the theoretical precision we can hope for is, so pick a big value that
222 // passes (when I think we're correct).
223 const float tolerance = 1.0f/(1 << 18);
224
225 for (auto cs : spaces) {
227 y2rm = SkColorMatrix::YUVtoRGB(cs);
228 r2ym.postConcat(y2rm);
229
230 float tmp[20];
231 r2ym.getRowMajor(tmp);
232 for (int i = 0; i < 20; ++i) {
233 float expected = 0;
234 if (i % 6 == 0) { // diagonal
235 expected = 1;
236 }
237 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(tmp[i], expected, tolerance));
238 }
239 }
240}
const char * options
reporter
Definition: FontMgrTest.cpp:39
std::unique_ptr< SkStreamAsset > GetResourceAsStream(const char *resource, bool useFileStream)
Definition: Resources.cpp:31
SkColorType
Definition: SkColorType.h:19
@ kUnknown_SkColorType
uninitialized
Definition: SkColorType.h:20
@ kTopLeft_SkEncodedOrigin
SkYUVColorSpace
Definition: SkImageInfo.h:68
@ kBT2020_SkYUVColorSpace
Definition: SkImageInfo.h:101
@ kJPEG_SkYUVColorSpace
Definition: SkImageInfo.h:98
@ kRec601_SkYUVColorSpace
Definition: SkImageInfo.h:99
@ kRec709_SkYUVColorSpace
Definition: SkImageInfo.h:100
@ kIdentity_SkYUVColorSpace
maps Y->R, U->G, V->B
Definition: SkImageInfo.h:93
@ kJPEG_Full_SkYUVColorSpace
describes full range
Definition: SkImageInfo.h:69
static bool SkScalarNearlyEqual(SkScalar x, SkScalar y, SkScalar tolerance=SK_ScalarNearlyZero)
Definition: SkScalar.h:107
static constexpr bool SkToBool(const T &x)
Definition: SkTo.h:35
#define REPORTER_ASSERT(r, cond,...)
Definition: Test.h:286
static void codec_yuv(skiatest::Reporter *reporter, const char path[], const SkYUVAInfo *expectedInfo)
Definition: YUVTest.cpp:31
static void verify_same(skiatest::Reporter *r, const SkYUVAPixmaps a, const SkYUVAPixmaps &b)
Definition: YUVTest.cpp:159
SkYUVAPixmaps decode_yuva(skiatest::Reporter *r, std::unique_ptr< SkStream > stream)
Definition: YUVTest.cpp:144
DEF_TEST(Jpeg_YUV_Codec, r)
Definition: YUVTest.cpp:98
static std::unique_ptr< SkCodec > MakeFromStream(std::unique_ptr< SkStream >, SkSpan< const SkCodecs::Decoder > decoders, Result *=nullptr, SkPngChunkReader *=nullptr, SelectionPolicy selectionPolicy=SelectionPolicy::kPreferStillImage)
Definition: SkCodec.cpp:163
@ kSuccess
Definition: SkCodec.h:80
static SkColorMatrix RGBtoYUV(SkYUVColorSpace)
void postConcat(const SkColorMatrix &mat)
Definition: SkColorMatrix.h:44
static SkColorMatrix YUVtoRGB(SkYUVColorSpace)
void getRowMajor(float dst[20]) const
Definition: SkColorMatrix.h:49
sk_sp< SkData > detachAsData()
Definition: SkStream.cpp:707
static std::unique_ptr< SkMemoryStream > Make(sk_sp< SkData > data)
Definition: SkStream.cpp:314
size_t rowBytes() const
Definition: SkPixmap.h:145
int width() const
Definition: SkPixmap.h:160
const SkImageInfo & info() const
Definition: SkPixmap.h:135
size_t computeByteSize() const
Definition: SkPixmap.h:231
const void * addr() const
Definition: SkPixmap.h:153
int height() const
Definition: SkPixmap.h:166
@ kY_U_V
Plane 0: Y, Plane 1: U, Plane 2: V.
static constexpr int kMaxPlanes
Definition: SkYUVAInfo.h:98
@ k420
1 set of UV values for each 2x2 block of Y values.
@ k422
1 set of UV values for each 2x1 block of Y values.
@ k444
No subsampling. UV values for each Y.
static constexpr SupportedDataTypes All()
size_t rowBytes(int i) const
const SkYUVAInfo & yuvaInfo() const
DataType dataType() const
static std::tuple< int, DataType > NumChannelsAndDataType(SkColorType)
int numPlanes() const
const SkImageInfo & planeInfo(int i) const
static SkYUVAPixmaps Allocate(const SkYUVAPixmapInfo &yuvaPixmapInfo)
static constexpr auto kMaxPlanes
static bool b
struct MyStruct a[10]
GAsyncResult * result
SK_API bool Encode(SkWStream *dst, const SkPixmap &src, const Options &options)
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
SIN Vec< N, float > abs(const Vec< N, float > &x)
Definition: SkVx.h:707
Definition: SkSize.h:16
bool isEmpty() const
Definition: SkSize.h:31
bool isEmpty() const
Definition: SkImageInfo.h:399
int bytesPerPixel() const
Definition: SkImageInfo.h:492
SkISize dimensions() const
Definition: SkImageInfo.h:421
SkColorType colorType() const
Definition: SkImageInfo.h:373
bool validRowBytes(size_t rowBytes) const
Definition: SkImageInfo.h:598