1 /* 2 * Copyright 2018 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/SkAndroidCodec.h" 9 #include "include/codec/SkCodec.h" 10 #include "include/core/SkBitmap.h" 11 #include "include/core/SkColor.h" 12 #include "include/core/SkColorSpace.h" 13 #include "include/core/SkData.h" 14 #include "include/core/SkEncodedImageFormat.h" 15 #include "include/core/SkImageGenerator.h" 16 #include "include/core/SkImageInfo.h" 17 #include "include/core/SkRefCnt.h" 18 #include "include/core/SkSize.h" 19 #include "include/core/SkString.h" 20 #include "include/core/SkTypes.h" 21 #include "include/third_party/skcms/skcms.h" 22 #include "src/codec/SkCodecImageGenerator.h" 23 #include "src/core/SkPixmapPriv.h" 24 #include "tests/Test.h" 25 #include "tools/Resources.h" 26 27 #include <string.h> 28 #include <initializer_list> 29 #include <memory> 30 #include <utility> 31 times(const SkISize & size,float factor)32 static SkISize times(const SkISize& size, float factor) { 33 return { (int) (size.width() * factor), (int) (size.height() * factor) }; 34 } 35 plus(const SkISize & size,int term)36 static SkISize plus(const SkISize& size, int term) { 37 return { size.width() + term, size.height() + term }; 38 } 39 invalid(const SkISize & size)40 static bool invalid(const SkISize& size) { 41 return size.width() < 1 || size.height() < 1; 42 } 43 DEF_TEST(AndroidCodec_computeSampleSize,r)44 DEF_TEST(AndroidCodec_computeSampleSize, r) { 45 if (GetResourcePath().isEmpty()) { 46 return; 47 } 48 for (const char* file : { "images/color_wheel.webp", 49 "images/ship.png", 50 "images/dog.jpg", 51 "images/color_wheel.gif", 52 "images/rle.bmp", 53 "images/google_chrome.ico", 54 "images/mandrill.wbmp", 55 #ifdef SK_CODEC_DECODES_RAW 56 "images/sample_1mp.dng", 57 #endif 58 }) { 59 auto data = GetResourceAsData(file); 60 if (!data) { 61 ERRORF(r, "Could not get %s", file); 62 continue; 63 } 64 65 auto codec = SkAndroidCodec::MakeFromCodec(SkCodec::MakeFromData(std::move(data))); 66 if (!codec) { 67 ERRORF(r, "Could not create codec for %s", file); 68 continue; 69 } 70 71 const auto dims = codec->getInfo().dimensions(); 72 const SkISize downscales[] = { 73 plus(dims, -1), 74 times(dims, .15f), 75 times(dims, .6f), 76 { (int32_t) (dims.width() * .25f), (int32_t) (dims.height() * .75f ) }, 77 { 1, 1 }, 78 { 1, 2 }, 79 { 2, 1 }, 80 { 0, -1 }, 81 { dims.width(), dims.height() - 1 }, 82 }; 83 for (SkISize size : downscales) { 84 const auto requested = size; 85 const int computedSampleSize = codec->computeSampleSize(&size); 86 REPORTER_ASSERT(r, size.width() >= 1 && size.height() >= 1); 87 if (codec->getEncodedFormat() == SkEncodedImageFormat::kWEBP) { 88 // WebP supports arbitrary down-scaling. 89 REPORTER_ASSERT(r, size == requested || invalid(requested)); 90 } else if (computedSampleSize == 1) { 91 REPORTER_ASSERT(r, size == dims); 92 } else { 93 REPORTER_ASSERT(r, computedSampleSize > 1); 94 if (size.width() >= dims.width() || size.height() >= dims.height()) { 95 ERRORF(r, "File %s's computed sample size (%i) is bigger than" 96 " original? original: %i x %i\tsampled: %i x %i", 97 file, computedSampleSize, dims.width(), dims.height(), 98 size.width(), size.height()); 99 } 100 REPORTER_ASSERT(r, size.width() >= requested.width() && 101 size.height() >= requested.height()); 102 REPORTER_ASSERT(r, size.width() < dims.width() && 103 size.height() < dims.height()); 104 } 105 } 106 107 const SkISize upscales[] = { 108 dims, plus(dims, 5), times(dims, 2), 109 }; 110 for (SkISize size : upscales) { 111 const int computedSampleSize = codec->computeSampleSize(&size); 112 REPORTER_ASSERT(r, computedSampleSize == 1); 113 REPORTER_ASSERT(r, dims == size); 114 } 115 116 // This mimics how Android's ImageDecoder uses SkAndroidCodec. A client 117 // can choose their dimensions based on calling getSampledDimensions, 118 // but the ImageDecoder API takes an arbitrary size. It then uses 119 // computeSampleSize to determine the best dimensions and sampleSize. 120 // It should return the same dimensions. the sampleSize may be different 121 // due to integer division. 122 for (int sampleSize : { 1, 2, 3, 4, 8, 16, 32 }) { 123 const SkISize sampledDims = codec->getSampledDimensions(sampleSize); 124 SkISize size = sampledDims; 125 const int computedSampleSize = codec->computeSampleSize(&size); 126 if (sampledDims != size) { 127 ERRORF(r, "File '%s'->getSampledDimensions(%i) yields computed" 128 " sample size of %i\n\tsampledDimensions: %i x %i\t" 129 "computed dimensions: %i x %i", 130 file, sampleSize, computedSampleSize, 131 sampledDims.width(), sampledDims.height(), 132 size.width(), size.height()); 133 } 134 } 135 } 136 } 137 DEF_TEST(AndroidCodec_wide,r)138 DEF_TEST(AndroidCodec_wide, r) { 139 if (GetResourcePath().isEmpty()) { 140 return; 141 } 142 143 const char* path = "images/wide-gamut.png"; 144 auto data = GetResourceAsData(path); 145 if (!data) { 146 ERRORF(r, "Missing file %s", path); 147 return; 148 } 149 150 auto codec = SkAndroidCodec::MakeFromCodec(SkCodec::MakeFromData(std::move(data))); 151 if (!codec) { 152 ERRORF(r, "Failed to create codec from %s", path); 153 return; 154 } 155 156 auto info = codec->getInfo(); 157 auto cs = codec->computeOutputColorSpace(info.colorType(), nullptr); 158 if (!cs) { 159 ERRORF(r, "%s should have a color space", path); 160 return; 161 } 162 163 auto expected = SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, SkNamedGamut::kDisplayP3); 164 REPORTER_ASSERT(r, SkColorSpace::Equals(cs.get(), expected.get())); 165 } 166 DEF_TEST(AndroidCodec_P3,r)167 DEF_TEST(AndroidCodec_P3, r) { 168 if (GetResourcePath().isEmpty()) { 169 return; 170 } 171 172 const char* path = "images/purple-displayprofile.png"; 173 auto data = GetResourceAsData(path); 174 if (!data) { 175 ERRORF(r, "Missing file %s", path); 176 return; 177 } 178 179 auto codec = SkAndroidCodec::MakeFromCodec(SkCodec::MakeFromData(std::move(data))); 180 if (!codec) { 181 ERRORF(r, "Failed to create codec from %s", path); 182 return; 183 } 184 185 auto info = codec->getInfo(); 186 auto cs = codec->computeOutputColorSpace(info.colorType(), nullptr); 187 if (!cs) { 188 ERRORF(r, "%s should have a color space", path); 189 return; 190 } 191 192 REPORTER_ASSERT(r, !cs->isSRGB()); 193 REPORTER_ASSERT(r, cs->gammaCloseToSRGB()); 194 195 skcms_Matrix3x3 matrix; 196 cs->toXYZD50(&matrix); 197 198 static constexpr skcms_Matrix3x3 kExpected = {{ 199 { 0.426254272f, 0.369018555f, 0.168914795f }, 200 { 0.226013184f, 0.685974121f, 0.0880126953f }, 201 { 0.0116729736f, 0.0950927734f, 0.71812439f }, 202 }}; 203 REPORTER_ASSERT(r, 0 == memcmp(&matrix, &kExpected, sizeof(skcms_Matrix3x3))); 204 } 205