2
* Copyright 2009 Google Inc.
4
* Licensed under the Apache License, Version 2.0 (the "License");
5
* you may not use this file except in compliance with the License.
6
* You may obtain a copy of the License at
8
* http://www.apache.org/licenses/LICENSE-2.0
10
* Unless required by applicable law or agreed to in writing, software
11
* distributed under the License is distributed on an "AS IS" BASIS,
12
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
* See the License for the specific language governing permissions and
14
* limitations under the License.
17
// Author: Bryan McQuade, Satyanarayana Manyam
19
#ifndef PAGESPEED_KERNEL_IMAGE_PNG_OPTIMIZER_H_
20
#define PAGESPEED_KERNEL_IMAGE_PNG_OPTIMIZER_H_
22
// Note: we should not include setjmp.h here, since libpng 1.2 headers
23
// include it themselves, and get unhappy if we do it ourselves.
26
#ifdef USE_SYSTEM_LIBPNG
27
#include "png.h" // NOLINT
29
#include "third_party/libpng/png.h"
35
#include "third_party/optipng/src/opngreduc/opngreduc.h"
36
#include "pagespeed/kernel/base/basictypes.h"
37
#include "pagespeed/kernel/base/scoped_ptr.h"
38
#include "pagespeed/kernel/base/string.h"
39
#include "pagespeed/kernel/image/image_util.h"
40
#include "pagespeed/kernel/image/scanline_interface.h"
41
#include "pagespeed/kernel/image/scanline_status.h"
43
namespace net_instaweb {
49
namespace image_compression {
51
using net_instaweb::MessageHandler;
53
class ScanlineStreamInput;
55
struct PngCompressParams {
56
PngCompressParams(int level, int strategy, bool is_progressive);
57
PngCompressParams(bool try_best_compression, bool is_progressive);
59
// Indicates what png filter type to be used while compressing the image.
60
// Valid values for this are
68
// Indicates which compression strategy to use while compressing the image.
69
// Valid values for this are
75
int compression_strategy;
76
// Indicates whether to search for the smallest output by using Opti-PNG and
77
// multiple runs of compression. This mode will use more computation.
78
bool try_best_compression;
79
// Indicates whether to encode the image in progressive / interlacing format.
83
// Helper that manages the lifetime of the png_ptr and info_ptr.
84
class ScopedPngStruct {
91
ScopedPngStruct(Type type, MessageHandler* handler);
94
bool valid() const { return png_ptr_ != NULL && info_ptr_ != NULL; }
96
// This will only return false as a result of a longjmp due to an
97
// unhandled libpng error.
100
png_structp png_ptr() const { return png_ptr_; }
101
png_infop info_ptr() const { return info_ptr_; }
104
png_structp png_ptr_;
107
MessageHandler* message_handler_;
110
// Helper class that provides an API to read a PNG image from some
112
class PngReaderInterface {
114
PngReaderInterface();
115
virtual ~PngReaderInterface();
117
// Parse the contents of body, convert to a PNG, and populate the
118
// PNG structures with the PNG representation. If 'require_opaque'
119
// is true, returns an image without an alpha channel if the
120
// original image has no transparent pixels, and fails
121
// otherwise. Returns true on success, false on failure.
122
virtual bool ReadPng(const GoogleString& body,
126
bool require_opaque) const = 0;
128
// Parse the contents of body, convert to a PNG, and populate the
129
// PNG structures with the PNG representation. Returns true on
130
// success, false on failure.
131
bool ReadPng(const GoogleString& body,
134
int transforms) const {
135
return ReadPng(body, png_ptr, info_ptr, transforms, false);
138
// Get just the attributes of the given image. out_bit_depth is the
139
// number of bits per channel. out_color_type is one of the
140
// PNG_COLOR_TYPE_* declared in png.h.
141
// TODO(bmcquade): consider merging this with ImageAttributes.
142
virtual bool GetAttributes(const GoogleString& body,
146
int* out_color_type) const = 0;
148
// Get the background color, in the form of 8-bit RGB triplets. Note
149
// that if the underlying image uses a bit_depth other than 8, the
150
// background color will be scaled to 8-bits per channel.
151
static bool GetBackgroundColor(
152
png_structp png_ptr, png_infop info_ptr,
153
unsigned char *red, unsigned char* green, unsigned char* blue,
154
MessageHandler* handler);
156
// Returns true if the alpha channel is actually a opaque. Returns
157
// false otherwise. It is an error to call this method for an image
158
// that does not have an alpha channel.
159
static bool IsAlphaChannelOpaque(png_structp png_ptr, png_infop info_ptr,
160
MessageHandler* handler);
163
DISALLOW_COPY_AND_ASSIGN(PngReaderInterface);
166
// Reader for PNG-encoded data.
167
// This is sample code on how someone can use the scanline reader
170
// if (setjmp(*GetJmpBuf())) {
174
// InitializeRead(...)
175
// while (HasMoreScanlines()) {
177
// ReadNextScanline(line);
182
class PngScanlineReader : public ScanlineReaderInterface {
184
explicit PngScanlineReader(MessageHandler* handler);
185
virtual ~PngScanlineReader();
187
jmp_buf* GetJmpBuf();
189
// This will only return false as a result of a longjmp due to an
190
// unhandled libpng error.
191
virtual bool Reset();
193
// Initializes the read structures with the given input.
194
bool InitializeRead(const PngReaderInterface& reader, const GoogleString& in);
195
bool InitializeRead(const PngReaderInterface& reader, const GoogleString& in,
198
virtual size_t GetBytesPerScanline();
199
virtual bool HasMoreScanLines();
200
virtual ScanlineStatus ReadNextScanlineWithStatus(void** out_scanline_bytes);
201
virtual size_t GetImageHeight();
202
virtual size_t GetImageWidth();
203
virtual PixelFormat GetPixelFormat();
204
virtual bool IsProgressive();
206
void set_transform(int transform);
207
void set_require_opaque(bool require_opaque);
209
bool GetBackgroundColor(
210
unsigned char* red, unsigned char* green, unsigned char* blue);
212
// This is a no-op and should not be called.
213
virtual ScanlineStatus InitializeWithStatus(const void* image_buffer,
214
size_t buffer_length);
217
ScopedPngStruct read_;
218
size_t current_scanline_;
220
bool require_opaque_;
221
MessageHandler* message_handler_;
223
DISALLOW_COPY_AND_ASSIGN(PngScanlineReader);
228
static bool OptimizePng(const PngReaderInterface& reader,
229
const GoogleString& in,
231
MessageHandler* handler);
233
static bool OptimizePngBestCompression(const PngReaderInterface& reader,
234
const GoogleString& in,
236
MessageHandler* handler);
238
static bool CopyPngStructs(const ScopedPngStruct& from, ScopedPngStruct* to);
241
explicit PngOptimizer(MessageHandler* handler);
244
// Take the given input and losslessly compress it by removing
245
// all unnecessary chunks, and by choosing an optimal PNG encoding.
246
// @return true on success, false on failure.
247
bool CreateOptimizedPng(const PngReaderInterface& reader,
248
const GoogleString& in,
250
MessageHandler* handler);
252
// Turn on best compression. Requires additional CPU but produces
254
void EnableBestCompression() { best_compression_ = true; }
256
bool WritePng(ScopedPngStruct* write, GoogleString* buffer);
257
bool CopyReadToWrite();
258
bool CreateBestOptimizedPngForParams(const PngCompressParams* param_list,
259
size_t param_list_size,
261
bool CreateOptimizedPngWithParams(ScopedPngStruct* write,
262
const PngCompressParams& params,
264
ScopedPngStruct read_;
265
ScopedPngStruct write_;
266
bool best_compression_;
267
MessageHandler* message_handler_;
269
DISALLOW_COPY_AND_ASSIGN(PngOptimizer);
272
// Reader for PNG-encoded data.
273
class PngReader : public PngReaderInterface {
275
explicit PngReader(MessageHandler* handler);
276
virtual ~PngReader();
277
virtual bool ReadPng(const GoogleString& body,
281
bool require_opaque) const;
283
virtual bool GetAttributes(const GoogleString& body,
287
int* out_color_type) const;
290
MessageHandler* message_handler_;
291
DISALLOW_COPY_AND_ASSIGN(PngReader);
294
// Class PngScanlineReaderRaw decodes PNG images and outputs the raw pixel data,
295
// image size, pixel type, etc. The class accepts all formats supported by
296
// libpng. The output is Gray_8, RGB_888, or RGBA_8888. The following
297
// transformations are used:
298
// - Image with depth other than 8 bits/pixel is expanded or stripped to
300
// - Paletted image is converted to RGB or RGBA depending on whether
301
// transparency is specified.
302
// - Gray_Alpha is converted to RGBA.
304
// Note: The input image stream must be valid throughout the life of the
305
// object. In other words, the image_buffer input you set to the Initialize()
306
// method cannot be changed until your last call to the ReadNextScanline()
309
class PngScanlineReaderRaw : public ScanlineReaderInterface {
311
explicit PngScanlineReaderRaw(MessageHandler* handler);
312
virtual ~PngScanlineReaderRaw();
314
// This will only return false as a result of a longjmp due to an
315
// unhandled libpng error.
316
virtual bool Reset();
318
// Initialize the reader with the given image stream. Note that image_buffer
319
// must remain unchanged until the last call to ReadNextScanline().
320
virtual ScanlineStatus InitializeWithStatus(const void* image_buffer,
321
size_t buffer_length);
323
// Return the next row of pixels. For non-progressive PNG,
324
// ReadNextScanlineWithStatus will decode one row of pixels each
325
// time when it is called, but for progressive PNG,
326
// ReadNextScanlineWithStatus will decode the entire image at the
327
// first time when it is called.
328
virtual ScanlineStatus ReadNextScanlineWithStatus(void** out_scanline_bytes);
330
// Return the number of bytes in a row (without padding).
331
virtual size_t GetBytesPerScanline() { return bytes_per_row_; }
333
virtual bool HasMoreScanLines() { return (row_ < height_); }
334
virtual PixelFormat GetPixelFormat() { return pixel_format_; }
335
virtual size_t GetImageHeight() { return height_; }
336
virtual size_t GetImageWidth() { return width_; }
337
virtual bool IsProgressive() { return is_progressive_; }
340
PixelFormat pixel_format_;
341
bool is_progressive_;
344
size_t bytes_per_row_;
346
bool was_initialized_;
347
net_instaweb::scoped_array<png_byte> image_buffer_;
348
net_instaweb::scoped_array<png_bytep> row_pointers_;
349
scoped_ptr<ScopedPngStruct> png_struct_;
350
// png_input_ stores a pointer to the input image stream. It also keeps
351
// tracking the length of data that libpng has read. It is initialized
352
// in Initialize() and is updated in ReadNextScanline().
353
scoped_ptr<ScanlineStreamInput> png_input_;
354
MessageHandler* message_handler_;
356
DISALLOW_COPY_AND_ASSIGN(PngScanlineReaderRaw);
359
// Class PngScanlineWriter writes a PNG image. It supports Gray_8, RGB_888,
360
// and RGBA_8888 formats.
361
class PngScanlineWriter : public ScanlineWriterInterface {
363
explicit PngScanlineWriter(MessageHandler* handler);
364
virtual ~PngScanlineWriter();
366
// Initialize the basic parameters for writing the image. Size of the image
367
// must be 1-by-1 or larger.
368
virtual ScanlineStatus InitWithStatus(const size_t width, const size_t height,
369
PixelFormat pixel_format);
371
// Initialize additional parameters for writing the image using
372
// 'params', which should be a PngCompressParams*. You can set
373
// 'params' to NULL to use the default compression configuration.
374
virtual ScanlineStatus InitializeWriteWithStatus(const void* params,
375
GoogleString* png_image);
377
// Write a scanline with the data provided. Return false in case of error.
378
virtual ScanlineStatus WriteNextScanlineWithStatus(
379
const void *scanline_bytes);
381
// Finalize write structure once all scanlines are written.
382
// If FinalizeWriter() is called before all of the scanlines have been
383
// written, the object will be reset to the initial state.
384
virtual ScanlineStatus FinalizeWriteWithStatus();
387
// Reset the object to the usable state.
390
// Validate the input parameters.
391
bool Validate(const PngCompressParams* params,
392
GoogleString* png_image);
394
bool DoBestCompression();
399
size_t bytes_per_row_;
401
PixelFormat pixel_format_;
402
scoped_ptr<ScopedPngStruct> png_struct_;
403
bool was_initialized_;
404
bool try_best_compression_;
405
net_instaweb::scoped_array<unsigned char> pixel_buffer_;
406
MessageHandler* message_handler_;
408
DISALLOW_COPY_AND_ASSIGN(PngScanlineWriter);
411
} // namespace image_compression
413
} // namespace pagespeed
415
#endif // PAGESPEED_KERNEL_IMAGE_PNG_OPTIMIZER_H_