• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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