• 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 "Resources.h"
9 #include "SkAndroidCodec.h"
10 #include "SkBitmap.h"
11 #include "SkCodec.h"
12 #include "SkCodecImageGenerator.h"
13 #include "SkColor.h"
14 #include "SkData.h"
15 #include "SkEncodedImageFormat.h"
16 #include "SkImageGenerator.h"
17 #include "SkImageInfo.h"
18 #include "SkPixmapPriv.h"
19 #include "SkRefCnt.h"
20 #include "SkSize.h"
21 #include "SkString.h"
22 #include "SkTypes.h"
23 #include "Test.h"
24 
25 #include <algorithm>
26 #include <memory>
27 
times(const SkISize & size,float factor)28 static SkISize times(const SkISize& size, float factor) {
29     return { (int) (size.width() * factor), (int) (size.height() * factor) };
30 }
31 
plus(const SkISize & size,int term)32 static SkISize plus(const SkISize& size, int term) {
33     return { size.width() + term, size.height() + term };
34 }
35 
invalid(const SkISize & size)36 static bool invalid(const SkISize& size) {
37     return size.width() < 1 || size.height() < 1;
38 }
39 
DEF_TEST(AndroidCodec_computeSampleSize,r)40 DEF_TEST(AndroidCodec_computeSampleSize, r) {
41     if (GetResourcePath().isEmpty()) {
42         return;
43     }
44     for (const char* file : { "images/color_wheel.webp",
45                               "images/ship.png",
46                               "images/dog.jpg",
47                               "images/color_wheel.gif",
48                               "images/rle.bmp",
49                               "images/google_chrome.ico",
50                               "images/mandrill.wbmp",
51 #ifdef SK_CODEC_DECODES_RAW
52                               "images/sample_1mp.dng",
53 #endif
54                               }) {
55         auto data = GetResourceAsData(file);
56         if (!data) {
57             ERRORF(r, "Could not get %s", file);
58             continue;
59         }
60 
61         auto codec = SkAndroidCodec::MakeFromCodec(SkCodec::MakeFromData(std::move(data)));
62         if (!codec) {
63             ERRORF(r, "Could not create codec for %s", file);
64             continue;
65         }
66 
67         const auto dims = codec->getInfo().dimensions();
68         const SkISize downscales[] = {
69             plus(dims, -1),
70             times(dims, .15f),
71             times(dims, .6f),
72             { (int32_t) (dims.width() * .25f), (int32_t) (dims.height() * .75f ) },
73             { 1,  1 },
74             { 1,  2 },
75             { 2,  1 },
76             { 0, -1 },
77             { dims.width(), dims.height() - 1 },
78         };
79         for (SkISize size : downscales) {
80             const auto requested = size;
81             const int computedSampleSize = codec->computeSampleSize(&size);
82             REPORTER_ASSERT(r, size.width() >= 1 && size.height() >= 1);
83             if (codec->getEncodedFormat() == SkEncodedImageFormat::kWEBP) {
84                 // WebP supports arbitrary down-scaling.
85                 REPORTER_ASSERT(r, size == requested || invalid(requested));
86             } else if (computedSampleSize == 1) {
87                 REPORTER_ASSERT(r, size == dims);
88             } else {
89                 REPORTER_ASSERT(r, computedSampleSize > 1);
90                 if (size.width() >= dims.width() || size.height() >= dims.height()) {
91                     ERRORF(r, "File %s's computed sample size (%i) is bigger than"
92                               " original? original: %i x %i\tsampled: %i x %i",
93                               file, computedSampleSize, dims.width(), dims.height(),
94                               size.width(), size.height());
95                 }
96                 REPORTER_ASSERT(r, size.width()  >= requested.width() &&
97                                    size.height() >= requested.height());
98                 REPORTER_ASSERT(r, size.width()  <  dims.width() &&
99                                    size.height() <  dims.height());
100             }
101         }
102 
103         const SkISize upscales[] = {
104             dims, plus(dims, 5), times(dims, 2),
105         };
106         for (SkISize size : upscales) {
107             const int computedSampleSize = codec->computeSampleSize(&size);
108             REPORTER_ASSERT(r, computedSampleSize == 1);
109             REPORTER_ASSERT(r, dims == size);
110         }
111 
112         // This mimics how Android's ImageDecoder uses SkAndroidCodec. A client
113         // can choose their dimensions based on calling getSampledDimensions,
114         // but the ImageDecoder API takes an arbitrary size. It then uses
115         // computeSampleSize to determine the best dimensions and sampleSize.
116         // It should return the same dimensions. the sampleSize may be different
117         // due to integer division.
118         for (int sampleSize : { 1, 2, 3, 4, 8, 16, 32 }) {
119             const SkISize sampledDims = codec->getSampledDimensions(sampleSize);
120             SkISize size = sampledDims;
121             const int computedSampleSize = codec->computeSampleSize(&size);
122             if (sampledDims != size) {
123                 ERRORF(r, "File '%s'->getSampledDimensions(%i) yields computed"
124                           " sample size of %i\n\tsampledDimensions: %i x %i\t"
125                           "computed dimensions: %i x %i",
126                           file, sampleSize, computedSampleSize,
127                           sampledDims.width(), sampledDims.height(),
128                           size.width(), size.height());
129             }
130         }
131     }
132 }
133 
DEF_TEST(AndroidCodec_orientation,r)134 DEF_TEST(AndroidCodec_orientation, r) {
135     if (GetResourcePath().isEmpty()) {
136         return;
137     }
138 
139     for (const char* ext : { "jpg", "webp" })
140     for (char i = '1'; i <= '8'; ++i) {
141         SkString path = SkStringPrintf("images/orientation/%c.%s", i, ext);
142         auto data = GetResourceAsData(path.c_str());
143         auto gen = SkCodecImageGenerator::MakeFromEncodedCodec(data);
144         if (!gen) {
145             ERRORF(r, "failed to decode %s", path.c_str());
146             return;
147         }
148 
149         // Dimensions after adjusting for the origin.
150         const SkISize expectedDims = { 100, 80 };
151 
152         // SkCodecImageGenerator automatically adjusts for the origin.
153         REPORTER_ASSERT(r, gen->getInfo().dimensions() == expectedDims);
154 
155         auto androidCodec = SkAndroidCodec::MakeFromCodec(SkCodec::MakeFromData(data));
156         if (!androidCodec) {
157             ERRORF(r, "failed to decode %s", path.c_str());
158             return;
159         }
160 
161         // SkAndroidCodec does not adjust for the origin by default. Dimensions may be reversed.
162         if (SkPixmapPriv::ShouldSwapWidthHeight(androidCodec->codec()->getOrigin())) {
163             auto swappedDims = SkPixmapPriv::SwapWidthHeight(androidCodec->getInfo()).dimensions();
164             REPORTER_ASSERT(r, expectedDims == swappedDims);
165         } else {
166             REPORTER_ASSERT(r, expectedDims == androidCodec->getInfo().dimensions());
167         }
168 
169         // Passing kRespect adjusts for the origin.
170         androidCodec = SkAndroidCodec::MakeFromCodec(SkCodec::MakeFromData(std::move(data)),
171                 SkAndroidCodec::ExifOrientationBehavior::kRespect);
172         auto info = androidCodec->getInfo();
173         REPORTER_ASSERT(r, info.dimensions() == expectedDims);
174 
175         SkBitmap fromGenerator;
176         fromGenerator.allocPixels(info);
177         REPORTER_ASSERT(r, gen->getPixels(info, fromGenerator.getPixels(),
178                                           fromGenerator.rowBytes()));
179 
180         SkBitmap fromAndroidCodec;
181         fromAndroidCodec.allocPixels(info);
182         auto result = androidCodec->getPixels(info, fromAndroidCodec.getPixels(),
183                                               fromAndroidCodec.rowBytes());
184         REPORTER_ASSERT(r, result == SkCodec::kSuccess);
185 
186         for (int i = 0; i < info.width();  ++i)
187         for (int j = 0; j < info.height(); ++j) {
188             SkColor c1 = *fromGenerator   .getAddr32(i, j);
189             SkColor c2 = *fromAndroidCodec.getAddr32(i, j);
190             if (c1 != c2) {
191                 ERRORF(r, "Bitmaps for %s do not match starting at position %i, %i\n"
192                           "\tfromGenerator: %x\tfromAndroidCodec: %x", path.c_str(), i, j,
193                           c1, c2);
194                 return;
195             }
196         }
197     }
198 }
199