• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2017 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 "Test.h"
10 
11 #include "SkBitmap.h"
12 #include "SkColorPriv.h"
13 #include "SkEncodedImageFormat.h"
14 #include "SkImage.h"
15 #include "SkJpegEncoder.h"
16 #include "SkPngEncoder.h"
17 #include "SkStream.h"
18 #include "SkWebpEncoder.h"
19 
20 #include "png.h"
21 
22 #include <algorithm>
23 #include <string>
24 #include <vector>
25 
encode(SkEncodedImageFormat format,SkWStream * dst,const SkPixmap & src)26 static bool encode(SkEncodedImageFormat format, SkWStream* dst, const SkPixmap& src) {
27     switch (format) {
28         case SkEncodedImageFormat::kJPEG:
29             return SkJpegEncoder::Encode(dst, src, SkJpegEncoder::Options());
30         case SkEncodedImageFormat::kPNG:
31             return SkPngEncoder::Encode(dst, src, SkPngEncoder::Options());
32         default:
33             return false;
34     }
35 }
36 
make(SkEncodedImageFormat format,SkWStream * dst,const SkPixmap & src)37 static std::unique_ptr<SkEncoder> make(SkEncodedImageFormat format, SkWStream* dst,
38                                        const SkPixmap& src) {
39     switch (format) {
40         case SkEncodedImageFormat::kJPEG:
41             return SkJpegEncoder::Make(dst, src, SkJpegEncoder::Options());
42         case SkEncodedImageFormat::kPNG:
43             return SkPngEncoder::Make(dst, src, SkPngEncoder::Options());
44         default:
45             return nullptr;
46     }
47 }
48 
test_encode(skiatest::Reporter * r,SkEncodedImageFormat format)49 static void test_encode(skiatest::Reporter* r, SkEncodedImageFormat format) {
50     SkBitmap bitmap;
51     bool success = GetResourceAsBitmap("images/mandrill_128.png", &bitmap);
52     if (!success) {
53         return;
54     }
55 
56     SkPixmap src;
57     success = bitmap.peekPixels(&src);
58     REPORTER_ASSERT(r, success);
59     if (!success) {
60         return;
61     }
62 
63     SkDynamicMemoryWStream dst0, dst1, dst2, dst3;
64     success = encode(format, &dst0, src);
65     REPORTER_ASSERT(r, success);
66 
67     auto encoder1 = make(format, &dst1, src);
68     for (int i = 0; i < src.height(); i++) {
69         success = encoder1->encodeRows(1);
70         REPORTER_ASSERT(r, success);
71     }
72 
73     auto encoder2 = make(format, &dst2, src);
74     for (int i = 0; i < src.height(); i+=3) {
75         success = encoder2->encodeRows(3);
76         REPORTER_ASSERT(r, success);
77     }
78 
79     auto encoder3 = make(format, &dst3, src);
80     success = encoder3->encodeRows(200);
81     REPORTER_ASSERT(r, success);
82 
83     sk_sp<SkData> data0 = dst0.detachAsData();
84     sk_sp<SkData> data1 = dst1.detachAsData();
85     sk_sp<SkData> data2 = dst2.detachAsData();
86     sk_sp<SkData> data3 = dst3.detachAsData();
87     REPORTER_ASSERT(r, data0->equals(data1.get()));
88     REPORTER_ASSERT(r, data0->equals(data2.get()));
89     REPORTER_ASSERT(r, data0->equals(data3.get()));
90 }
91 
DEF_TEST(Encode,r)92 DEF_TEST(Encode, r) {
93     test_encode(r, SkEncodedImageFormat::kJPEG);
94     test_encode(r, SkEncodedImageFormat::kPNG);
95 }
96 
almost_equals(SkPMColor a,SkPMColor b,int tolerance)97 static inline bool almost_equals(SkPMColor a, SkPMColor b, int tolerance) {
98     if (SkTAbs((int)SkGetPackedR32(a) - (int)SkGetPackedR32(b)) > tolerance) {
99         return false;
100     }
101 
102     if (SkTAbs((int)SkGetPackedG32(a) - (int)SkGetPackedG32(b)) > tolerance) {
103         return false;
104     }
105 
106     if (SkTAbs((int)SkGetPackedB32(a) - (int)SkGetPackedB32(b)) > tolerance) {
107         return false;
108     }
109 
110     if (SkTAbs((int)SkGetPackedA32(a) - (int)SkGetPackedA32(b)) > tolerance) {
111         return false;
112     }
113 
114     return true;
115 }
116 
almost_equals(const SkBitmap & a,const SkBitmap & b,int tolerance)117 static inline bool almost_equals(const SkBitmap& a, const SkBitmap& b, int tolerance) {
118     if (a.info() != b.info()) {
119         return false;
120     }
121 
122     SkASSERT(kN32_SkColorType == a.colorType());
123     for (int y = 0; y < a.height(); y++) {
124         for (int x = 0; x < a.width(); x++) {
125             if (!almost_equals(*a.getAddr32(x, y), *b.getAddr32(x, y), tolerance)) {
126                 return false;
127             }
128         }
129     }
130 
131     return true;
132 }
133 
DEF_TEST(Encode_JpegDownsample,r)134 DEF_TEST(Encode_JpegDownsample, r) {
135     SkBitmap bitmap;
136     bool success = GetResourceAsBitmap("images/mandrill_128.png", &bitmap);
137     if (!success) {
138         return;
139     }
140 
141     SkPixmap src;
142     success = bitmap.peekPixels(&src);
143     REPORTER_ASSERT(r, success);
144     if (!success) {
145         return;
146     }
147 
148     SkDynamicMemoryWStream dst0, dst1, dst2;
149     SkJpegEncoder::Options options;
150     success = SkJpegEncoder::Encode(&dst0, src, options);
151     REPORTER_ASSERT(r, success);
152 
153     options.fDownsample = SkJpegEncoder::Downsample::k422;
154     success = SkJpegEncoder::Encode(&dst1, src, options);
155     REPORTER_ASSERT(r, success);
156 
157     options.fDownsample = SkJpegEncoder::Downsample::k444;
158     success = SkJpegEncoder::Encode(&dst2, src, options);
159     REPORTER_ASSERT(r, success);
160 
161     sk_sp<SkData> data0 = dst0.detachAsData();
162     sk_sp<SkData> data1 = dst1.detachAsData();
163     sk_sp<SkData> data2 = dst2.detachAsData();
164     REPORTER_ASSERT(r, data0->size() < data1->size());
165     REPORTER_ASSERT(r, data1->size() < data2->size());
166 
167     SkBitmap bm0, bm1, bm2;
168     SkImage::MakeFromEncoded(data0)->asLegacyBitmap(&bm0);
169     SkImage::MakeFromEncoded(data1)->asLegacyBitmap(&bm1);
170     SkImage::MakeFromEncoded(data2)->asLegacyBitmap(&bm2);
171     REPORTER_ASSERT(r, almost_equals(bm0, bm1, 60));
172     REPORTER_ASSERT(r, almost_equals(bm1, bm2, 60));
173 }
174 
pushComment(std::vector<std::string> & comments,const char * keyword,const char * text)175 static inline void pushComment(
176         std::vector<std::string>& comments, const char* keyword, const char* text) {
177     comments.push_back(keyword);
178     comments.push_back(text);
179 }
180 
testPngComments(const SkPixmap & src,SkPngEncoder::Options & options,skiatest::Reporter * r)181 static void testPngComments(const SkPixmap& src, SkPngEncoder::Options& options,
182         skiatest::Reporter* r) {
183     std::vector<std::string> commentStrings;
184     pushComment(commentStrings, "key", "text");
185     pushComment(commentStrings, "test", "something");
186     pushComment(commentStrings, "have some", "spaces in both");
187 
188     std::string longKey(PNG_KEYWORD_MAX_LENGTH, 'x');
189 #ifdef SK_DEBUG
190     commentStrings.push_back(longKey);
191 #else
192     // We call SkDEBUGFAILF it the key is too long so we'll only test this in release mode.
193     commentStrings.push_back(longKey + "x");
194 #endif
195     commentStrings.push_back("");
196 
197     std::vector<const char*> commentPointers;
198     std::vector<size_t> commentSizes;
199     for(auto& str : commentStrings) {
200         commentPointers.push_back(str.c_str());
201         commentSizes.push_back(str.length() + 1);
202     }
203 
204     options.fComments = SkDataTable::MakeCopyArrays((void const *const *)commentPointers.data(),
205             commentSizes.data(), commentStrings.size());
206 
207 
208     SkDynamicMemoryWStream dst;
209     bool success = SkPngEncoder::Encode(&dst, src, options);
210     REPORTER_ASSERT(r, success);
211 
212     std::vector<char> output(dst.bytesWritten());
213     dst.copyTo(output.data());
214 
215     // Each chunk is of the form length (4 bytes), chunk type (tEXt), data,
216     // checksum (4 bytes).  Make sure we find all of them in the encoded
217     // results.
218     const char kExpected1[] =
219         "\x00\x00\x00\x08tEXtkey\x00text\x9e\xe7\x66\x51";
220     const char kExpected2[] =
221         "\x00\x00\x00\x0etEXttest\x00something\x29\xba\xef\xac";
222     const char kExpected3[] =
223         "\x00\x00\x00\x18tEXthave some\x00spaces in both\x8d\x69\x34\x2d";
224     std::string longKeyRecord = "tEXt" + longKey; // A snippet of our long key comment
225     std::string tooLongRecord = "tExt" + longKey + "x"; // A snippet whose key is too long
226 
227     auto search1 = std::search(output.begin(), output.end(),
228             kExpected1, kExpected1 + sizeof(kExpected1));
229     auto search2 = std::search(output.begin(), output.end(),
230             kExpected2, kExpected2 + sizeof(kExpected2));
231     auto search3 = std::search(output.begin(), output.end(),
232             kExpected3, kExpected3 + sizeof(kExpected3));
233     auto search4 = std::search(output.begin(), output.end(),
234             longKeyRecord.begin(), longKeyRecord.end());
235     auto search5 = std::search(output.begin(), output.end(),
236             tooLongRecord.begin(), tooLongRecord.end());
237 
238     REPORTER_ASSERT(r, search1 != output.end());
239     REPORTER_ASSERT(r, search2 != output.end());
240     REPORTER_ASSERT(r, search3 != output.end());
241     REPORTER_ASSERT(r, search4 != output.end());
242     REPORTER_ASSERT(r, search5 == output.end());
243     // Comments test ends
244 }
245 
DEF_TEST(Encode_PngOptions,r)246 DEF_TEST(Encode_PngOptions, r) {
247     SkBitmap bitmap;
248     bool success = GetResourceAsBitmap("images/mandrill_128.png", &bitmap);
249     if (!success) {
250         return;
251     }
252 
253     SkPixmap src;
254     success = bitmap.peekPixels(&src);
255     REPORTER_ASSERT(r, success);
256     if (!success) {
257         return;
258     }
259 
260     SkDynamicMemoryWStream dst0, dst1, dst2;
261     SkPngEncoder::Options options;
262     success = SkPngEncoder::Encode(&dst0, src, options);
263     REPORTER_ASSERT(r, success);
264 
265     options.fFilterFlags = SkPngEncoder::FilterFlag::kUp;
266     success = SkPngEncoder::Encode(&dst1, src, options);
267     REPORTER_ASSERT(r, success);
268 
269     options.fZLibLevel = 3;
270     success = SkPngEncoder::Encode(&dst2, src, options);
271     REPORTER_ASSERT(r, success);
272 
273     testPngComments(src, options, r);
274 
275     sk_sp<SkData> data0 = dst0.detachAsData();
276     sk_sp<SkData> data1 = dst1.detachAsData();
277     sk_sp<SkData> data2 = dst2.detachAsData();
278     REPORTER_ASSERT(r, data0->size() < data1->size());
279     REPORTER_ASSERT(r, data1->size() < data2->size());
280 
281     SkBitmap bm0, bm1, bm2;
282     SkImage::MakeFromEncoded(data0)->asLegacyBitmap(&bm0);
283     SkImage::MakeFromEncoded(data1)->asLegacyBitmap(&bm1);
284     SkImage::MakeFromEncoded(data2)->asLegacyBitmap(&bm2);
285     REPORTER_ASSERT(r, almost_equals(bm0, bm1, 0));
286     REPORTER_ASSERT(r, almost_equals(bm0, bm2, 0));
287 }
288 
DEF_TEST(Encode_WebpOptions,r)289 DEF_TEST(Encode_WebpOptions, r) {
290     SkBitmap bitmap;
291     bool success = GetResourceAsBitmap("images/google_chrome.ico", &bitmap);
292     if (!success) {
293         return;
294     }
295 
296     SkPixmap src;
297     success = bitmap.peekPixels(&src);
298     REPORTER_ASSERT(r, success);
299     if (!success) {
300         return;
301     }
302 
303     SkDynamicMemoryWStream dst0, dst1, dst2, dst3;
304     SkWebpEncoder::Options options;
305     options.fCompression = SkWebpEncoder::Compression::kLossless;
306     options.fQuality = 0.0f;
307     success = SkWebpEncoder::Encode(&dst0, src, options);
308     REPORTER_ASSERT(r, success);
309 
310     options.fQuality = 100.0f;
311     success = SkWebpEncoder::Encode(&dst1, src, options);
312     REPORTER_ASSERT(r, success);
313 
314     options.fCompression = SkWebpEncoder::Compression::kLossy;
315     options.fQuality = 100.0f;
316     success = SkWebpEncoder::Encode(&dst2, src, options);
317     REPORTER_ASSERT(r, success);
318 
319     options.fCompression = SkWebpEncoder::Compression::kLossy;
320     options.fQuality = 50.0f;
321     success = SkWebpEncoder::Encode(&dst3, src, options);
322     REPORTER_ASSERT(r, success);
323 
324     sk_sp<SkData> data0 = dst0.detachAsData();
325     sk_sp<SkData> data1 = dst1.detachAsData();
326     sk_sp<SkData> data2 = dst2.detachAsData();
327     sk_sp<SkData> data3 = dst3.detachAsData();
328     REPORTER_ASSERT(r, data0->size() > data1->size());
329     REPORTER_ASSERT(r, data1->size() > data2->size());
330     REPORTER_ASSERT(r, data2->size() > data3->size());
331 
332     SkBitmap bm0, bm1, bm2, bm3;
333     SkImage::MakeFromEncoded(data0)->asLegacyBitmap(&bm0);
334     SkImage::MakeFromEncoded(data1)->asLegacyBitmap(&bm1);
335     SkImage::MakeFromEncoded(data2)->asLegacyBitmap(&bm2);
336     SkImage::MakeFromEncoded(data3)->asLegacyBitmap(&bm3);
337     REPORTER_ASSERT(r, almost_equals(bm0, bm1, 0));
338     REPORTER_ASSERT(r, almost_equals(bm0, bm2, 90));
339     REPORTER_ASSERT(r, almost_equals(bm2, bm3, 50));
340 }
341