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