• 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