blob: 7b44a5259867babfd88750acc40f2be593436182 [file] [log] [blame]
license.botf003cfe2008-08-24 09:55:55 +09001// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
initial.commit3f4a7322008-07-27 06:49:38 +09004
5#include "base/basictypes.h"
6#include "base/gfx/png_encoder.h"
7#include "base/logging.h"
8
9extern "C" {
10#include "png.h"
11}
12
13namespace {
14
15// Converts BGRA->RGBA and RGBA->BGRA.
16void ConvertBetweenBGRAandRGBA(const unsigned char* input, int pixel_width,
17 unsigned char* output) {
18 for (int x = 0; x < pixel_width; x++) {
19 const unsigned char* pixel_in = &input[x * 4];
20 unsigned char* pixel_out = &output[x * 4];
21 pixel_out[0] = pixel_in[2];
22 pixel_out[1] = pixel_in[1];
23 pixel_out[2] = pixel_in[0];
24 pixel_out[3] = pixel_in[3];
25 }
26}
27
28void ConvertRGBAtoRGB(const unsigned char* rgba, int pixel_width,
29 unsigned char* rgb) {
30 for (int x = 0; x < pixel_width; x++) {
31 const unsigned char* pixel_in = &rgba[x * 4];
32 unsigned char* pixel_out = &rgb[x * 3];
33 pixel_out[0] = pixel_in[0];
34 pixel_out[1] = pixel_in[1];
35 pixel_out[2] = pixel_in[2];
36 }
37}
38
39} // namespace
40
41// Encoder --------------------------------------------------------------------
42//
43// This section of the code is based on nsPNGEncoder.cpp in Mozilla
44// (Copyright 2005 Google Inc.)
45
46namespace {
47
48// Passed around as the io_ptr in the png structs so our callbacks know where
49// to write data.
50struct PngEncoderState {
51 PngEncoderState(std::vector<unsigned char>* o) : out(o) {}
52 std::vector<unsigned char>* out;
53};
54
55// Called by libpng to flush its internal buffer to ours.
56void EncoderWriteCallback(png_structp png, png_bytep data, png_size_t size) {
57 PngEncoderState* state = static_cast<PngEncoderState*>(png_get_io_ptr(png));
58 DCHECK(state->out);
59
60 size_t old_size = state->out->size();
61 state->out->resize(old_size + size);
62 memcpy(&(*state->out)[old_size], data, size);
63}
64
65void ConvertBGRAtoRGB(const unsigned char* bgra, int pixel_width,
66 unsigned char* rgb) {
67 for (int x = 0; x < pixel_width; x++) {
68 const unsigned char* pixel_in = &bgra[x * 4];
69 unsigned char* pixel_out = &rgb[x * 3];
70 pixel_out[0] = pixel_in[2];
71 pixel_out[1] = pixel_in[1];
72 pixel_out[2] = pixel_in[0];
73 }
74}
75
76// Automatically destroys the given write structs on destruction to make
77// cleanup and error handling code cleaner.
78class PngWriteStructDestroyer {
79 public:
80 PngWriteStructDestroyer(png_struct** ps, png_info** pi) : ps_(ps), pi_(pi) {
81 }
82 ~PngWriteStructDestroyer() {
83 png_destroy_write_struct(ps_, pi_);
84 }
85 private:
86 png_struct** ps_;
87 png_info** pi_;
88
89 DISALLOW_EVIL_CONSTRUCTORS(PngWriteStructDestroyer);
90};
91
92} // namespace
93
94// static
95bool PNGEncoder::Encode(const unsigned char* input, ColorFormat format,
96 int w, int h, int row_byte_width,
97 bool discard_transparency,
98 std::vector<unsigned char>* output) {
99 // Run to convert an input row into the output row format, NULL means no
100 // conversion is necessary.
101 void (*converter)(const unsigned char* in, int w, unsigned char* out) = NULL;
102
103 int input_color_components, output_color_components;
104 int png_output_color_type;
105 switch (format) {
106 case FORMAT_RGB:
107 input_color_components = 3;
108 output_color_components = 3;
109 png_output_color_type = PNG_COLOR_TYPE_RGB;
110 discard_transparency = false;
111 break;
112
113 case FORMAT_RGBA:
114 input_color_components = 4;
115 if (discard_transparency) {
116 output_color_components = 3;
117 png_output_color_type = PNG_COLOR_TYPE_RGB;
118 converter = ConvertRGBAtoRGB;
119 } else {
120 output_color_components = 4;
121 png_output_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
122 converter = NULL;
123 }
124 break;
125
126 case FORMAT_BGRA:
127 input_color_components = 4;
128 if (discard_transparency) {
129 output_color_components = 3;
130 png_output_color_type = PNG_COLOR_TYPE_RGB;
131 converter = ConvertBGRAtoRGB;
132 } else {
133 output_color_components = 4;
134 png_output_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
135 converter = ConvertBetweenBGRAandRGBA;
136 }
137 break;
138
139 default:
140 NOTREACHED() << "Unknown pixel format";
141 return false;
142 }
143
144 // Row stride should be at least as long as the length of the data.
145 DCHECK(input_color_components * w <= row_byte_width);
146
147 png_struct* png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
148 png_voidp_NULL,
149 png_error_ptr_NULL,
150 png_error_ptr_NULL);
151 if (!png_ptr)
152 return false;
153 png_info* info_ptr = png_create_info_struct(png_ptr);
154 if (!info_ptr) {
155 png_destroy_write_struct(&png_ptr, NULL);
156 return false;
157 }
158 PngWriteStructDestroyer destroyer(&png_ptr, &info_ptr);
159
160 if (setjmp(png_jmpbuf(png_ptr))) {
161 // The destroyer will ensure that the structures are cleaned up in this
162 // case, even though we may get here as a jump from random parts of the
163 // PNG library called below.
164 return false;
165 }
166
167 // Set our callback for libpng to give us the data.
168 PngEncoderState state(output);
169 png_set_write_fn(png_ptr, &state, EncoderWriteCallback, NULL);
170
171 png_set_IHDR(png_ptr, info_ptr, w, h, 8, png_output_color_type,
172 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
173 PNG_FILTER_TYPE_DEFAULT);
174 png_write_info(png_ptr, info_ptr);
175
176 if (!converter) {
177 // No conversion needed, give the data directly to libpng.
178 for (int y = 0; y < h; y ++)
179 png_write_row(png_ptr, const_cast<unsigned char*>(&input[y * row_byte_width]));
180 } else {
181 // Needs conversion using a separate buffer.
182 unsigned char* row = new unsigned char[w * output_color_components];
183 for (int y = 0; y < h; y ++) {
184 converter(&input[y * row_byte_width], w, row);
185 png_write_row(png_ptr, row);
186 }
187 delete[] row;
188 }
189
190 png_write_end(png_ptr, info_ptr);
191 return true;
192}
license.botf003cfe2008-08-24 09:55:55 +0900193