blob: fb684813b2a33c68a27e6f74b9551f13b2c18e73 [file] [log] [blame]
/*
* Copyright 2016 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Author: huibao@google.com (Huibao Lin)
#include "pagespeed/kernel/image/image_optimizer.h"
#include <cstddef>
#include "pagespeed/kernel/base/basictypes.h"
#include "pagespeed/kernel/base/gtest.h"
#include "pagespeed/kernel/base/mock_message_handler.h"
#include "pagespeed/kernel/base/null_mutex.h"
#include "pagespeed/kernel/base/string.h"
#include "pagespeed/kernel/base/string_util.h"
#include "pagespeed/kernel/image/gif_reader.h"
#include "pagespeed/kernel/image/image_analysis.h"
#include "pagespeed/kernel/image/image_util.h"
#include "pagespeed/kernel/image/test_utils.h"
namespace {
using net_instaweb::MockMessageHandler;
using net_instaweb::NullMutex;
using net_instaweb::Timer;
using pagespeed::image_compression::kGifTestDir;
using pagespeed::image_compression::kJpegTestDir;
using pagespeed::image_compression::kPngSuiteTestDir;
using pagespeed::image_compression::kPngSuiteGifTestDir;
using pagespeed::image_compression::kPngTestDir;
using pagespeed::image_compression::GifReader;
using pagespeed::image_compression::ImageDimensions;
using pagespeed::image_compression::ImageFormat;
using pagespeed::image_compression::ImageOptions;
using pagespeed::image_compression::IMAGE_GIF;
using pagespeed::image_compression::IMAGE_JPEG;
using pagespeed::image_compression::IMAGE_PNG;
using pagespeed::image_compression::IMAGE_UNKNOWN;
using pagespeed::image_compression::IMAGE_WEBP;
using pagespeed::image_compression::kMessagePatternLibpngError;
using pagespeed::image_compression::kMessagePatternLibpngWarning;
using pagespeed::image_compression::kMessagePatternPixelFormat;
using pagespeed::image_compression::kMessagePatternStats;
using pagespeed::image_compression::kMessagePatternUnexpectedEOF;
using pagespeed::image_compression::kMessagePatternWritingToWebp;
using pagespeed::image_compression::kTestRootDir;
using pagespeed::image_compression::ImageOptimizer;
using pagespeed::image_compression::ReadTestFileWithExt;
struct TestingImageInfo {
const char* file_name;
int width;
int height;
int rewritten_width;
int rewritten_height;
int original_size;
int rewritten_png_or_jpeg_size;
};
// In kGifImages[], rewritten_width === 7 and rewritten_height === 8.
const TestingImageInfo kGifImages[] = {
{"basi0g01.gif", 32, 32, 7, 8, 153, 166},
{"basi0g02.gif", 32, 32, 7, 8, 185, 112},
{"basi0g04.gif", 32, 32, 7, 8, 344, 144},
{"basi0g08.gif", 32, 32, 7, 8, 1736, 116},
{"basn0g01.gif", 32, 32, 7, 8, 153, 166},
{"basn0g02.gif", 32, 32, 7, 8, 185, 112},
{"basn0g04.gif", 32, 32, 7, 8, 344, 144},
{"basn0g08.gif", 32, 32, 7, 8, 1736, 116},
{"basi3p01.gif", 32, 32, 7, 8, 138, 96},
{"basi3p02.gif", 32, 32, 7, 8, 186, 114},
{"basi3p04.gif", 32, 32, 7, 8, 344, 155},
{"basi3p08.gif", 32, 32, 7, 8, 1737, 495},
{"basn3p01.gif", 32, 32, 7, 8, 138, 96},
{"basn3p02.gif", 32, 32, 7, 8, 186, 114},
{"basn3p04.gif", 32, 32, 7, 8, 344, 155},
{"basn3p08.gif", 32, 32, 7, 8, 1737, 495},
};
// In kJpegImages[], the images were requested to be resized to height = 8.
const TestingImageInfo kJpegImages[] = {
{"sjpeg1.jpg", 120, 90, 11, 8, 1552, 1165},
{"sjpeg3.jpg", 512, 384, 11, 8, 44084, 25814},
{"sjpeg6.jpg", 512, 512, 8, 8, 149600, 84641},
{"testgray.jpg", 130, 97, 11, 8, 5014, 3060},
{"sjpeg2.jpg", 130, 97, 11, 8, 3612, 3272},
{"sjpeg4.jpg", 512, 384, 11, 8, 168895, 48717},
{"test411.jpg", 130, 97, 11, 8, 6883, 3705},
{"test420.jpg", 130, 97, 11, 8, 6173, 3653},
{"test422.jpg", 130, 97, 11, 8, 6501, 3709},
};
const TestingImageInfo kAnimatedGifImages[] = {
{"animated.gif", 120, 50, 120, 50, 4382, 0},
{"animated_interlaced.gif", 120, 50, 120, 50, 3855, 0},
{"full2loop.gif", 100, 200, 100, 200, 648, 0},
};
// In kPngImages[], the images were requested to be resized to width = 7.
const TestingImageInfo kPngImages[] = {
{"basi0g01.png", 32, 32, 7, 7, 217, 166},
{"basi0g02.png", 32, 32, 7, 7, 154, 105},
{"basi0g04.png", 32, 32, 7, 7, 247, 137},
{"basi0g08.png", 32, 32, 7, 7, 254, 116},
{"basi0g16.png", 32, 32, 7, 7, 299, 122},
{"basi2c08.png", 32, 32, 7, 7, 315, 129},
{"basi2c16.png", 32, 32, 7, 7, 595, 216},
{"basi3p01.png", 32, 32, 7, 7, 132, 96},
{"basi3p02.png", 32, 32, 7, 7, 193, 114},
{"basi3p04.png", 32, 32, 7, 7, 327, 155},
{"basi4a08.png", 32, 32, 7, 7, 214, 105},
{"basi4a16.png", 32, 32, 7, 7, 2855, 778},
{"basi6a08.png", 32, 32, 7, 7, 361, 160},
{"basi6a16.png", 32, 32, 7, 7, 4180, 1213},
{"basn0g01.png", 32, 32, 7, 7, 164, 166},
{"basn0g02.png", 32, 32, 7, 7, 104, 105},
{"basn0g04.png", 32, 32, 7, 7, 145, 137},
{"basn0g08.png", 32, 32, 7, 7, 138, 116},
{"basn0g16.png", 32, 32, 7, 7, 167, 122},
{"basn2c08.png", 32, 32, 7, 7, 145, 129},
{"basn2c16.png", 32, 32, 7, 7, 302, 216},
{"basn3p01.png", 32, 32, 7, 7, 112, 96},
{"basn3p02.png", 32, 32, 7, 7, 146, 114},
{"basn3p04.png", 32, 32, 7, 7, 216, 155},
{"basn4a08.png", 32, 32, 7, 7, 126, 105},
{"basn4a16.png", 32, 32, 7, 7, 2206, 778},
{"basn6a08.png", 32, 32, 7, 7, 184, 160},
{"basn6a16.png", 32, 32, 7, 7, 3435, 1213},
{"bgai4a08.png", 32, 32, 7, 7, 214, 105},
{"bgai4a16.png", 32, 32, 7, 7, 2855, 778},
{"bgan6a08.png", 32, 32, 7, 7, 184, 160},
{"bgan6a16.png", 32, 32, 7, 7, 3435, 1213},
{"bgbn4a08.png", 32, 32, 7, 7, 140, 105},
{"bggn4a16.png", 32, 32, 7, 7, 2220, 778},
{"bgwn6a08.png", 32, 32, 7, 7, 202, 160},
{"bgyn6a16.png", 32, 32, 7, 7, 3453, 1213},
{"cdfn2c08.png", 8, 32, 7, 28, 404, 336},
{"cdhn2c08.png", 32, 8, 7, 2, 344, 308},
{"cdsn2c08.png", 8, 8, 7, 7, 232, 177},
{"cdun2c08.png", 32, 32, 7, 7, 724, 666},
{"ch1n3p04.png", 32, 32, 7, 7, 258, 155},
{"cm0n0g04.png", 32, 32, 7, 7, 292, 272},
{"cm7n0g04.png", 32, 32, 7, 7, 292, 272},
{"cm9n0g04.png", 32, 32, 7, 7, 292, 272},
{"cs3n2c16.png", 32, 32, 7, 7, 214, 142},
{"cs3n3p08.png", 32, 32, 7, 7, 259, 142},
{"cs5n2c08.png", 32, 32, 7, 7, 186, 148},
{"cs5n3p08.png", 32, 32, 7, 7, 271, 148},
{"cs8n2c08.png", 32, 32, 7, 7, 149, 142},
{"cs8n3p08.png", 32, 32, 7, 7, 256, 142},
{"ct0n0g04.png", 32, 32, 7, 7, 273, 272},
{"ct1n0g04.png", 32, 32, 7, 7, 792, 272},
{"ctzn0g04.png", 32, 32, 7, 7, 753, 272},
{"f00n0g08.png", 32, 32, 7, 7, 319, 312},
{"f01n0g08.png", 32, 32, 7, 7, 321, 246},
{"f02n0g08.png", 32, 32, 7, 7, 355, 289},
{"f03n0g08.png", 32, 32, 7, 7, 389, 292},
{"f04n0g08.png", 32, 32, 7, 7, 269, 273},
{"g03n0g16.png", 32, 32, 7, 7, 345, 257},
{"g03n2c08.png", 32, 32, 7, 7, 370, 352},
{"g03n3p04.png", 32, 32, 7, 7, 214, 189},
{"g04n0g16.png", 32, 32, 7, 7, 363, 271},
{"g04n2c08.png", 32, 32, 7, 7, 377, 358},
{"g04n3p04.png", 32, 32, 7, 7, 219, 190},
{"g05n0g16.png", 32, 32, 7, 7, 339, 259},
{"g05n2c08.png", 32, 32, 7, 7, 350, 348},
{"g05n3p04.png", 32, 32, 7, 7, 206, 181},
{"g07n0g16.png", 32, 32, 7, 7, 321, 245},
{"g07n2c08.png", 32, 32, 7, 7, 340, 352},
{"g07n3p04.png", 32, 32, 7, 7, 207, 177},
{"g10n0g16.png", 32, 32, 7, 7, 262, 194},
{"g10n2c08.png", 32, 32, 7, 7, 285, 351},
{"g10n3p04.png", 32, 32, 7, 7, 214, 188},
{"g25n0g16.png", 32, 32, 7, 7, 383, 289},
{"g25n2c08.png", 32, 32, 7, 7, 405, 350},
{"g25n3p04.png", 32, 32, 7, 7, 215, 192},
{"oi1n0g16.png", 32, 32, 7, 7, 167, 122},
{"oi1n2c16.png", 32, 32, 7, 7, 302, 216},
{"oi2n0g16.png", 32, 32, 7, 7, 179, 122},
{"oi2n2c16.png", 32, 32, 7, 7, 314, 216},
{"oi4n0g16.png", 32, 32, 7, 7, 203, 122},
{"oi4n2c16.png", 32, 32, 7, 7, 338, 216},
{"oi9n0g16.png", 32, 32, 7, 7, 1283, 122},
{"oi9n2c16.png", 32, 32, 7, 7, 3038, 216},
{"pp0n2c16.png", 32, 32, 7, 7, 962, 216},
{"pp0n6a08.png", 32, 32, 7, 7, 818, 142},
{"ps1n0g08.png", 32, 32, 7, 7, 1477, 116},
{"ps1n2c16.png", 32, 32, 7, 7, 1641, 216},
{"ps2n0g08.png", 32, 32, 7, 7, 2341, 116},
{"ps2n2c16.png", 32, 32, 7, 7, 2505, 216},
{"s01i3p01.png", 1, 1, 1, 1, 113, 69},
{"s01n3p01.png", 1, 1, 1, 1, 113, 69},
{"s02i3p01.png", 2, 2, 2, 2, 114, 72},
{"s02n3p01.png", 2, 2, 2, 2, 115, 72},
{"s03i3p01.png", 3, 3, 3, 3, 118, 77},
{"s03n3p01.png", 3, 3, 3, 3, 120, 77},
{"s04i3p01.png", 4, 4, 4, 4, 126, 79},
{"s04n3p01.png", 4, 4, 4, 4, 121, 79},
{"s05i3p02.png", 5, 5, 5, 5, 134, 86},
{"s05n3p02.png", 5, 5, 5, 5, 129, 86},
{"s06i3p02.png", 6, 6, 6, 6, 143, 86},
{"s06n3p02.png", 6, 6, 6, 6, 131, 86},
{"s07i3p02.png", 7, 7, 7, 7, 149, 94},
{"s07n3p02.png", 7, 7, 7, 7, 138, 94},
{"s08i3p02.png", 8, 8, 7, 7, 149, 99},
{"s08n3p02.png", 8, 8, 7, 7, 139, 99},
{"s09i3p02.png", 9, 9, 7, 7, 147, 102},
{"s09n3p02.png", 9, 9, 7, 7, 143, 102},
{"s32i3p04.png", 32, 32, 7, 7, 355, 213},
{"s32n3p04.png", 32, 32, 7, 7, 263, 213},
{"s33i3p04.png", 33, 33, 7, 7, 385, 250},
{"s33n3p04.png", 33, 33, 7, 7, 329, 250},
{"s34i3p04.png", 34, 34, 7, 7, 349, 205},
{"s34n3p04.png", 34, 34, 7, 7, 248, 205},
{"s35i3p04.png", 35, 35, 7, 7, 399, 257},
{"s35n3p04.png", 35, 35, 7, 7, 338, 257},
{"s36i3p04.png", 36, 36, 7, 7, 356, 205},
{"s36n3p04.png", 36, 36, 7, 7, 258, 205},
{"s37i3p04.png", 37, 37, 7, 7, 393, 246},
{"s37n3p04.png", 37, 37, 7, 7, 336, 246},
{"s38i3p04.png", 38, 38, 7, 7, 357, 200},
{"s38n3p04.png", 38, 38, 7, 7, 245, 200},
{"s39i3p04.png", 39, 39, 7, 7, 420, 269},
{"s39n3p04.png", 39, 39, 7, 7, 352, 269},
{"s40i3p04.png", 40, 40, 7, 7, 357, 220},
{"s40n3p04.png", 40, 40, 7, 7, 256, 220},
{"tbbn1g04.png", 32, 32, 7, 7, 419, 439},
{"tbbn2c16.png", 32, 32, 7, 7, 1994, 1016},
{"tbbn3p08.png", 32, 32, 7, 7, 1128, 1016},
{"tbgn2c16.png", 32, 32, 7, 7, 1994, 1016},
{"tbgn3p08.png", 32, 32, 7, 7, 1128, 1016},
{"tbrn2c08.png", 32, 32, 7, 7, 1347, 1016},
{"tbwn1g16.png", 32, 32, 7, 7, 1146, 890},
{"tbwn3p08.png", 32, 32, 7, 7, 1131, 1016},
{"tbyn3p08.png", 32, 32, 7, 7, 1131, 1016},
{"tp0n1g08.png", 32, 32, 7, 7, 689, 552},
{"tp1n3p08.png", 32, 32, 7, 7, 1115, 1016},
{"z00n2c08.png", 32, 32, 7, 7, 3172, 224},
{"z03n2c08.png", 32, 32, 7, 7, 232, 224},
{"z06n2c08.png", 32, 32, 7, 7, 224, 224},
{"z09n2c08.png", 32, 32, 7, 7, 224, 224},
{"basi3p08.png", 32, 32, 7, 7, 1527, 495},
{"basn3p08.png", 32, 32, 7, 7, 1286, 495},
{"ccwn2c08.png", 32, 32, 7, 7, 1514, 1440},
{"ccwn3p08.png", 32, 32, 7, 7, 1554, 1226},
{"ch2n3p08.png", 32, 32, 7, 7, 1810, 495},
{"f00n2c08.png", 32, 32, 7, 7, 2475, 1070},
{"f01n2c08.png", 32, 32, 7, 7, 1180, 965},
{"f02n2c08.png", 32, 32, 7, 7, 1729, 1024},
{"f03n2c08.png", 32, 32, 7, 7, 1291, 1062},
{"f04n2c08.png", 32, 32, 7, 7, 985, 985},
{"tp0n2c08.png", 32, 32, 7, 7, 1311, 919},
{"tp0n3p08.png", 32, 32, 7, 7, 1120, 919},
};
const TestingImageInfo kInvalidImages[] = {
{"x00n0g01.png", 0, 0, 0, 0, 0, 0},
{"xcrn0g04.png", 0, 0, 0, 0, 0, 0},
{"xlfn0g04.png", 0, 0, 0, 0, 0, 0},
};
const size_t kGifImageCount = arraysize(kGifImages);
const size_t kPngImageCount = arraysize(kPngImages);
const size_t kJpegImageCount = arraysize(kJpegImages);
const size_t kAnimatedGifImageCount = arraysize(kAnimatedGifImages);
const size_t kInvalidImageCount = arraysize(kInvalidImages);
class ImageOptimizerTest : public testing::Test {
public:
ImageOptimizerTest()
: message_handler_(new NullMutex) {
}
protected:
void SetUp() override {
message_handler_.AddPatternToSkipPrinting(kMessagePatternLibpngError);
message_handler_.AddPatternToSkipPrinting(kMessagePatternLibpngWarning);
message_handler_.AddPatternToSkipPrinting(kMessagePatternPixelFormat);
message_handler_.AddPatternToSkipPrinting(kMessagePatternStats);
message_handler_.AddPatternToSkipPrinting(kMessagePatternUnexpectedEOF);
message_handler_.AddPatternToSkipPrinting(kMessagePatternWritingToWebp);
}
void RewriteAndVerifyImages(
const char* dir, const TestingImageInfo* images, int num_images,
bool is_animated, const ImageOptions& options,
const ImageDimensions& requested_dimension,
const ImageFormat original_format,
bool expected_success,
const ImageFormat expected_format,
bool compare_to_rewritten_dimensions,
bool expected_uses_lossy_format) {
for (size_t i = 0; i < num_images; ++i) {
// Load test image.
const TestingImageInfo& image = images[i];
GoogleString original_image;
ASSERT_TRUE(ReadTestFileWithExt(dir, image.file_name, &original_image));
// Try to optimize the image.
GoogleString rewritten_image;
ImageFormat rewritten_format;
ImageOptimizer optimizer(&message_handler_);
optimizer.set_options(options);
optimizer.set_requested_dimension(requested_dimension);
bool result = optimizer.Optimize(StringPiece(original_image),
&rewritten_image, &rewritten_format);
if (!expected_success) {
EXPECT_FALSE(result);
return;
}
ASSERT_TRUE(result) << " file: " << image.file_name;
EXPECT_EQ(expected_format, rewritten_format)
<< " file: " << image.file_name;
EXPECT_EQ(expected_uses_lossy_format, optimizer.UsesLossyFormat())
<< " file: " << image.file_name;
if (compare_to_rewritten_dimensions) {
EXPECT_EQ(image.rewritten_width, optimizer.optimized_width())
<< " file: " << image.file_name;
EXPECT_EQ(image.rewritten_height, optimizer.optimized_height())
<< " file: " << image.file_name;
} else {
EXPECT_EQ(image.width, optimizer.optimized_width())
<< " file: " << image.file_name;
EXPECT_EQ(image.height, optimizer.optimized_height())
<< " file: " << image.file_name;
if (expected_format != IMAGE_WEBP) {
int expected_rewritten_size = image.rewritten_png_or_jpeg_size;
// If the image was not resized, we expect the rewritten
// image to have the predicted size. In considering version
// difference of encoder, the rewritten size is compared with
// a window.
const int threshold = 20;
EXPECT_LE(expected_rewritten_size - threshold,
rewritten_image.length())
<< " file: " << image.file_name;
EXPECT_GE(expected_rewritten_size + threshold,
rewritten_image.length())
<< " file: " << image.file_name;
}
}
if (!is_animated) {
int rewritten_width = -1;
int rewritten_height = -1;
EXPECT_TRUE(
AnalyzeImage(rewritten_format, rewritten_image.data(),
rewritten_image.length(), &rewritten_width,
&rewritten_height, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr,
&message_handler_));
EXPECT_EQ(optimizer.optimized_width(), rewritten_width);
EXPECT_EQ(optimizer.optimized_height(), rewritten_height);
// If the original and rewritten images have the same dimensions,
// we verify that they match pixel by pixel. If they have different
// dimension, we only verify the rewritten dimension. Pixel accuracy
// of the resized image is tested in image_resizer_test.cc.
if (!compare_to_rewritten_dimensions) {
if (original_format == IMAGE_JPEG) {
double min_psnr = 32.0;
DecodeAndCompareImagesByPSNR(
original_format, original_image.data(), original_image.length(),
rewritten_format, rewritten_image.data(),
rewritten_image.length(), min_psnr,
true, // expand colors
true, // ignore transparent pixels
&message_handler_);
} else {
// For lossless formats (GIF and PNG) the pixels must match
// bit-by-bit.
DecodeAndCompareImages(
original_format, original_image.data(), original_image.length(),
rewritten_format, rewritten_image.data(),
rewritten_image.length(),
true, // ignore transparent pixels
&message_handler_);
}
}
} else {
EXPECT_TRUE(
pagespeed::image_compression::CompareAnimatedImages(
net_instaweb::StrCat(dir, images[i].file_name),
rewritten_image, &message_handler_));
}
}
}
protected:
MockMessageHandler message_handler_;
private:
DISALLOW_COPY_AND_ASSIGN(ImageOptimizerTest);
};
// Test converting PNG, JPEG, and GIF (single-frame and animated) to WebP.
TEST_F(ImageOptimizerTest, AllFormats) {
ImageOptions options;
options.set_allow_webp_lossless_or_alpha(true);
options.set_allow_webp_animated(true);
options.set_must_reduce_bytes(false);
ImageDimensions requested_dimension;
bool is_animated = false;
bool expected_success = true;
bool compare_to_rewritten_dimensions = false;
bool expected_uses_lossy_format = false;
RewriteAndVerifyImages(kPngSuiteGifTestDir, kGifImages, kGifImageCount,
is_animated, options, requested_dimension,
IMAGE_GIF, expected_success, IMAGE_WEBP,
compare_to_rewritten_dimensions,
expected_uses_lossy_format);
RewriteAndVerifyImages(kPngSuiteTestDir, kPngImages, kPngImageCount,
is_animated, options, requested_dimension,
IMAGE_PNG, expected_success, IMAGE_WEBP,
compare_to_rewritten_dimensions,
expected_uses_lossy_format);
expected_uses_lossy_format = true;
RewriteAndVerifyImages(kJpegTestDir, kJpegImages, kJpegImageCount,
is_animated, options, requested_dimension,
IMAGE_JPEG, expected_success, IMAGE_WEBP,
compare_to_rewritten_dimensions,
expected_uses_lossy_format);
is_animated = true;
expected_uses_lossy_format = false;
RewriteAndVerifyImages(kGifTestDir, kAnimatedGifImages,
kAnimatedGifImageCount, is_animated, options,
requested_dimension, IMAGE_GIF, expected_success,
IMAGE_WEBP, compare_to_rewritten_dimensions,
expected_uses_lossy_format);
}
// Test converting single-frame GIF to PNG.
TEST_F(ImageOptimizerTest, GifToPng) {
ImageOptions options;
options.set_must_reduce_bytes(false);
ImageDimensions requested_dimension;
bool is_animated = false;
bool expected_success = true;
bool compare_to_rewritten_dimensions = false;
bool expected_uses_lossy_format = false;
RewriteAndVerifyImages(kPngSuiteGifTestDir, kGifImages, kGifImageCount,
is_animated, options, requested_dimension,
IMAGE_GIF, expected_success, IMAGE_PNG,
compare_to_rewritten_dimensions,
expected_uses_lossy_format);
}
// Test recompressing PNG.
TEST_F(ImageOptimizerTest, PngToPng) {
ImageOptions options;
options.set_must_reduce_bytes(false);
ImageDimensions requested_dimension;
bool is_animated = false;
bool expected_success = true;
bool compare_to_rewritten_dimensions = false;
bool expected_uses_lossy_format = false;
RewriteAndVerifyImages(kPngSuiteTestDir, kPngImages, kPngImageCount,
is_animated, options, requested_dimension,
IMAGE_PNG, expected_success, IMAGE_PNG,
compare_to_rewritten_dimensions,
expected_uses_lossy_format);
}
// Test recompressing JPEG.
TEST_F(ImageOptimizerTest, JpegToJpeg) {
ImageOptions options;
options.set_allow_webp_lossy(false);
options.set_must_reduce_bytes(false);
ImageDimensions requested_dimension;
bool is_animated = false;
bool expected_success = true;
bool compare_to_rewritten_dimensions = false;
bool expected_uses_lossy_format = true;
RewriteAndVerifyImages(kJpegTestDir, kJpegImages, kJpegImageCount,
is_animated, options, requested_dimension,
IMAGE_JPEG, expected_success, IMAGE_JPEG,
compare_to_rewritten_dimensions,
expected_uses_lossy_format);
}
// Test resizing and compressing PNG, JPEG, and single-frame GIF.
TEST_F(ImageOptimizerTest, Resize) {
ImageOptions options;
options.set_allow_webp_lossy(false);
options.set_must_reduce_bytes(false);
ImageDimensions requested_dimension;
bool is_animated = false;
bool expected_success = true;
bool compare_to_rewritten_dimensions = true;
bool expected_uses_lossy_format = false;
requested_dimension.set_width(7);
requested_dimension.set_height(8);
RewriteAndVerifyImages(kPngSuiteGifTestDir, kGifImages, kGifImageCount,
is_animated, options, requested_dimension,
IMAGE_GIF, expected_success, IMAGE_PNG,
compare_to_rewritten_dimensions,
expected_uses_lossy_format);
requested_dimension.set_width(7);
requested_dimension.clear_height();
RewriteAndVerifyImages(kPngSuiteTestDir, kPngImages, kPngImageCount,
is_animated, options, requested_dimension,
IMAGE_PNG, expected_success, IMAGE_PNG,
compare_to_rewritten_dimensions,
expected_uses_lossy_format);
requested_dimension.clear_width();
requested_dimension.set_height(8);
expected_uses_lossy_format = true;
RewriteAndVerifyImages(kJpegTestDir, kJpegImages, kJpegImageCount,
is_animated, options, requested_dimension,
IMAGE_JPEG, expected_success, IMAGE_JPEG,
compare_to_rewritten_dimensions,
expected_uses_lossy_format);
}
// Test un-resizeable images, e.g., requested dimension is larger than the
// original or unsupported format (i.e., animated image).
TEST_F(ImageOptimizerTest, NotResize) {
ImageOptions options;
options.set_allow_webp_lossless_or_alpha(true);
options.set_allow_webp_animated(true);
options.set_must_reduce_bytes(false);
ImageDimensions requested_dimension;
bool is_animated = false;
bool expected_success = true;
bool compare_to_rewritten_dimensions = false;
bool expected_uses_lossy_format = false;
// Both dimensions are too large.
requested_dimension.set_width(1000000);
requested_dimension.set_height(1000000);
RewriteAndVerifyImages(kPngSuiteGifTestDir, kGifImages, kGifImageCount,
is_animated, options, requested_dimension,
IMAGE_GIF, expected_success, IMAGE_WEBP,
compare_to_rewritten_dimensions,
expected_uses_lossy_format);
// Width too large.
requested_dimension.set_width(1000000);
requested_dimension.clear_height();
RewriteAndVerifyImages(kPngSuiteTestDir, kPngImages, kPngImageCount,
is_animated, options, requested_dimension,
IMAGE_PNG, expected_success, IMAGE_WEBP,
compare_to_rewritten_dimensions,
expected_uses_lossy_format);
// Height too large.
requested_dimension.clear_width();
requested_dimension.set_height(1000000);
expected_uses_lossy_format = true;
RewriteAndVerifyImages(kJpegTestDir, kJpegImages, kJpegImageCount,
is_animated, options, requested_dimension,
IMAGE_JPEG, expected_success, IMAGE_WEBP,
compare_to_rewritten_dimensions,
expected_uses_lossy_format);
// Animated images can't be resized currently.
requested_dimension.set_width(7);
requested_dimension.set_height(8);
expected_uses_lossy_format = false;
is_animated = true;
RewriteAndVerifyImages(kGifTestDir, kAnimatedGifImages,
kAnimatedGifImageCount, is_animated, options,
requested_dimension, IMAGE_GIF, expected_success,
IMAGE_WEBP, compare_to_rewritten_dimensions,
expected_uses_lossy_format);
}
// Test invalid images.
TEST_F(ImageOptimizerTest, InvalidImages) {
ImageOptions options;
ImageDimensions requested_dimension;
bool is_animated = false;
bool expected_success = false;
bool compare_to_rewritten_dimensions = false;
bool expected_uses_lossy_format = false;
RewriteAndVerifyImages(kPngSuiteTestDir, kInvalidImages, kInvalidImageCount,
is_animated, options, requested_dimension,
IMAGE_PNG, expected_success, IMAGE_UNKNOWN,
compare_to_rewritten_dimensions,
expected_uses_lossy_format);
}
// Make sure that an ImageOptimizer object can only be used once.
TEST_F(ImageOptimizerTest, SingleUse) {
GoogleString original_image;
ASSERT_TRUE(ReadTestFileWithExt(kPngSuiteTestDir, "basi0g01.png",
&original_image));
// Try to optimize the image.
GoogleString rewritten_image;
ImageFormat rewritten_format;
ImageOptimizer optimizer(&message_handler_);
ASSERT_TRUE(optimizer.Optimize(StringPiece(original_image),
&rewritten_image, &rewritten_format));
EXPECT_DEATH(optimizer.Optimize(StringPiece(original_image),
&rewritten_image, &rewritten_format),
"Check failed: is_valid_");
}
// Make sure that we return false if "must_reduce_bytes" was set to true and
// we can't make the image smaller.
TEST_F(ImageOptimizerTest, MustReduceBytes) {
GoogleString original_image;
GoogleString rewritten_image;
ImageFormat rewritten_format;
ImageOptimizer optimizer(&message_handler_);
// o.gif is a well-optimized GIF image with only 43 bytes. We can't improve
// this image.
ASSERT_TRUE(ReadTestFileWithExt(kGifTestDir, "o.gif", &original_image));
ASSERT_FALSE(optimizer.Optimize(StringPiece(original_image),
&rewritten_image, &rewritten_format));
}
} // namespace