xref: /third_party/skia/tests/YUVTest.cpp (revision cb93a386)
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
16static 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
83DEF_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
134DEF_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