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
8 #include "include/codec/SkCodec.h"
9 #include "include/core/SkPixmap.h"
10 #include "include/core/SkStream.h"
11 #include "include/private/SkTemplates.h"
12 #include "src/core/SkAutoMalloc.h"
13 #include "tests/Test.h"
14 #include "tools/Resources.h"
15
codec_yuv(skiatest::Reporter * reporter,const char path[],const SkYUVAInfo * expectedInfo)16 static void codec_yuv(skiatest::Reporter* reporter,
17 const char path[],
18 const SkYUVAInfo* expectedInfo) {
19 std::unique_ptr<SkStream> stream(GetResourceAsStream(path));
20 if (!stream) {
21 return;
22 }
23 std::unique_ptr<SkCodec> codec(SkCodec::MakeFromStream(std::move(stream)));
24 REPORTER_ASSERT(reporter, codec);
25 if (!codec) {
26 return;
27 }
28
29 // Test queryYUBAInfo()
30 SkYUVAPixmapInfo yuvaPixmapInfo;
31
32 static constexpr auto kAllTypes = SkYUVAPixmapInfo::SupportedDataTypes::All();
33 static constexpr auto kNoTypes = SkYUVAPixmapInfo::SupportedDataTypes();
34
35 // SkYUVAInfo param is required to be non-null.
36 bool success = codec->queryYUVAInfo(kAllTypes, nullptr);
37 REPORTER_ASSERT(reporter, !success);
38 // Fails when there is no support for YUVA planes.
39 success = codec->queryYUVAInfo(kNoTypes, &yuvaPixmapInfo);
40 REPORTER_ASSERT(reporter, !success);
41
42 success = codec->queryYUVAInfo(kAllTypes, &yuvaPixmapInfo);
43 REPORTER_ASSERT(reporter, SkToBool(expectedInfo) == success);
44 if (!success) {
45 return;
46 }
47 REPORTER_ASSERT(reporter, *expectedInfo == yuvaPixmapInfo.yuvaInfo());
48
49 int numPlanes = yuvaPixmapInfo.numPlanes();
50 REPORTER_ASSERT(reporter, numPlanes <= SkYUVAInfo::kMaxPlanes);
51 for (int i = 0; i < numPlanes; ++i) {
52 const SkImageInfo& planeInfo = yuvaPixmapInfo.planeInfo(i);
53 SkColorType planeCT = planeInfo.colorType();
54 REPORTER_ASSERT(reporter, !planeInfo.isEmpty());
55 REPORTER_ASSERT(reporter, planeCT != kUnknown_SkColorType);
56 REPORTER_ASSERT(reporter, planeInfo.validRowBytes(yuvaPixmapInfo.rowBytes(i)));
57 // Currently all planes must share a data type, gettable as SkYUVAPixmapInfo::dataType().
58 auto [numChannels, planeDataType] = SkYUVAPixmapInfo::NumChannelsAndDataType(planeCT);
59 REPORTER_ASSERT(reporter, planeDataType == yuvaPixmapInfo.dataType());
60 }
61 for (int i = numPlanes; i < SkYUVAInfo::kMaxPlanes; ++i) {
62 const SkImageInfo& planeInfo = yuvaPixmapInfo.planeInfo(i);
63 REPORTER_ASSERT(reporter, planeInfo.dimensions().isEmpty());
64 REPORTER_ASSERT(reporter, planeInfo.colorType() == kUnknown_SkColorType);
65 REPORTER_ASSERT(reporter, yuvaPixmapInfo.rowBytes(i) == 0);
66 }
67
68 // Allocate the memory for the YUV decode.
69 auto pixmaps = SkYUVAPixmaps::Allocate(yuvaPixmapInfo);
70 REPORTER_ASSERT(reporter, pixmaps.isValid());
71
72 for (int i = 0; i < SkYUVAPixmaps::kMaxPlanes; ++i) {
73 REPORTER_ASSERT(reporter, pixmaps.plane(i).info() == yuvaPixmapInfo.planeInfo(i));
74 }
75 for (int i = numPlanes; i < SkYUVAInfo::kMaxPlanes; ++i) {
76 REPORTER_ASSERT(reporter, pixmaps.plane(i).rowBytes() == 0);
77 }
78
79 // Test getYUVAPlanes()
80 REPORTER_ASSERT(reporter, SkCodec::kSuccess == codec->getYUVAPlanes(pixmaps));
81 }
82
DEF_TEST(Jpeg_YUV_Codec,r)83 DEF_TEST(Jpeg_YUV_Codec, r) {
84 auto setExpectations = [](SkISize dims, SkYUVAInfo::Subsampling subsampling) {
85 return SkYUVAInfo(dims,
86 SkYUVAInfo::PlaneConfig::kY_U_V,
87 subsampling,
88 kJPEG_Full_SkYUVColorSpace,
89 kTopLeft_SkEncodedOrigin,
90 SkYUVAInfo::Siting::kCentered,
91 SkYUVAInfo::Siting::kCentered);
92 };
93
94 SkYUVAInfo expectations = setExpectations({128, 128}, SkYUVAInfo::Subsampling::k420);
95 codec_yuv(r, "images/color_wheel.jpg", &expectations);
96
97 // H2V2
98 expectations = setExpectations({512, 512}, SkYUVAInfo::Subsampling::k420);
99 codec_yuv(r, "images/mandrill_512_q075.jpg", &expectations);
100
101 // H1V1
102 expectations = setExpectations({512, 512}, SkYUVAInfo::Subsampling::k444);
103 codec_yuv(r, "images/mandrill_h1v1.jpg", &expectations);
104
105 // H2V1
106 expectations = setExpectations({512, 512}, SkYUVAInfo::Subsampling::k422);
107 codec_yuv(r, "images/mandrill_h2v1.jpg", &expectations);
108
109 // Non-power of two dimensions
110 expectations = setExpectations({439, 154}, SkYUVAInfo::Subsampling::k420);
111 codec_yuv(r, "images/cropped_mandrill.jpg", &expectations);
112
113 expectations = setExpectations({8, 8}, SkYUVAInfo::Subsampling::k420);
114 codec_yuv(r, "images/randPixels.jpg", &expectations);
115
116 // Progressive images
117 expectations = setExpectations({512, 512}, SkYUVAInfo::Subsampling::k444);
118 codec_yuv(r, "images/brickwork-texture.jpg", &expectations);
119 codec_yuv(r, "images/brickwork_normal-map.jpg", &expectations);
120
121 // A CMYK encoded image should fail.
122 codec_yuv(r, "images/CMYK.jpg", nullptr);
123 // A grayscale encoded image should fail.
124 codec_yuv(r, "images/grayscale.jpg", nullptr);
125 // A PNG should fail.
126 codec_yuv(r, "images/arrow.png", nullptr);
127 }
128
129 #include "include/effects/SkColorMatrix.h"
130 #include "src/core/SkYUVMath.h"
131
132 // Be sure that the two matrices are inverses of each other
133 // (i.e. rgb2yuv and yuv2rgb
DEF_TEST(YUVMath,reporter)134 DEF_TEST(YUVMath, reporter) {
135 const SkYUVColorSpace spaces[] = {
136 kJPEG_SkYUVColorSpace,
137 kRec601_SkYUVColorSpace,
138 kRec709_SkYUVColorSpace,
139 kBT2020_SkYUVColorSpace,
140 kIdentity_SkYUVColorSpace,
141 };
142
143 // Not sure what the theoretical precision we can hope for is, so pick a big value that
144 // passes (when I think we're correct).
145 const float tolerance = 1.0f/(1 << 18);
146
147 for (auto cs : spaces) {
148 SkColorMatrix r2ym = SkColorMatrix::RGBtoYUV(cs),
149 y2rm = SkColorMatrix::YUVtoRGB(cs);
150 r2ym.postConcat(y2rm);
151
152 float tmp[20];
153 r2ym.getRowMajor(tmp);
154 for (int i = 0; i < 20; ++i) {
155 float expected = 0;
156 if (i % 6 == 0) { // diagonal
157 expected = 1;
158 }
159 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(tmp[i], expected, tolerance));
160 }
161 }
162 }
163