Home | History | Annotate | Download | only in tests
      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 
     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 
     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 
     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 
     92 DEF_TEST(Encode, r) {
     93     test_encode(r, SkEncodedImageFormat::kJPEG);
     94     test_encode(r, SkEncodedImageFormat::kPNG);
     95 }
     96 
     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 
    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 
    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 
    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 
    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 
    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 
    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, 45));
    340 }
    341