| OLD | NEW |
| (Empty) |
| 1 // 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. | |
| 4 | |
| 5 #include "base/basictypes.h" | |
| 6 #include "base/gfx/png_encoder.h" | |
| 7 #include "base/logging.h" | |
| 8 #include "base/scoped_ptr.h" | |
| 9 #include "third_party/skia/include/core/SkBitmap.h" | |
| 10 #include "third_party/skia/include/core/SkUnPreMultiply.h" | |
| 11 | |
| 12 extern "C" { | |
| 13 #if defined(USE_SYSTEM_LIBPNG) | |
| 14 #include <png.h> | |
| 15 #else | |
| 16 #include "third_party/libpng/png.h" | |
| 17 #endif | |
| 18 } | |
| 19 | |
| 20 namespace { | |
| 21 | |
| 22 // Converts BGRA->RGBA and RGBA->BGRA. | |
| 23 void ConvertBetweenBGRAandRGBA(const unsigned char* input, int pixel_width, | |
| 24 unsigned char* output) { | |
| 25 for (int x = 0; x < pixel_width; x++) { | |
| 26 const unsigned char* pixel_in = &input[x * 4]; | |
| 27 unsigned char* pixel_out = &output[x * 4]; | |
| 28 pixel_out[0] = pixel_in[2]; | |
| 29 pixel_out[1] = pixel_in[1]; | |
| 30 pixel_out[2] = pixel_in[0]; | |
| 31 pixel_out[3] = pixel_in[3]; | |
| 32 } | |
| 33 } | |
| 34 | |
| 35 void ConvertRGBAtoRGB(const unsigned char* rgba, int pixel_width, | |
| 36 unsigned char* rgb) { | |
| 37 for (int x = 0; x < pixel_width; x++) { | |
| 38 const unsigned char* pixel_in = &rgba[x * 4]; | |
| 39 unsigned char* pixel_out = &rgb[x * 3]; | |
| 40 pixel_out[0] = pixel_in[0]; | |
| 41 pixel_out[1] = pixel_in[1]; | |
| 42 pixel_out[2] = pixel_in[2]; | |
| 43 } | |
| 44 } | |
| 45 | |
| 46 } // namespace | |
| 47 | |
| 48 // Encoder -------------------------------------------------------------------- | |
| 49 // | |
| 50 // This section of the code is based on nsPNGEncoder.cpp in Mozilla | |
| 51 // (Copyright 2005 Google Inc.) | |
| 52 | |
| 53 namespace { | |
| 54 | |
| 55 // Passed around as the io_ptr in the png structs so our callbacks know where | |
| 56 // to write data. | |
| 57 struct PngEncoderState { | |
| 58 PngEncoderState(std::vector<unsigned char>* o) : out(o) {} | |
| 59 std::vector<unsigned char>* out; | |
| 60 }; | |
| 61 | |
| 62 // Called by libpng to flush its internal buffer to ours. | |
| 63 void EncoderWriteCallback(png_structp png, png_bytep data, png_size_t size) { | |
| 64 PngEncoderState* state = static_cast<PngEncoderState*>(png_get_io_ptr(png)); | |
| 65 DCHECK(state->out); | |
| 66 | |
| 67 size_t old_size = state->out->size(); | |
| 68 state->out->resize(old_size + size); | |
| 69 memcpy(&(*state->out)[old_size], data, size); | |
| 70 } | |
| 71 | |
| 72 void ConvertBGRAtoRGB(const unsigned char* bgra, int pixel_width, | |
| 73 unsigned char* rgb) { | |
| 74 for (int x = 0; x < pixel_width; x++) { | |
| 75 const unsigned char* pixel_in = &bgra[x * 4]; | |
| 76 unsigned char* pixel_out = &rgb[x * 3]; | |
| 77 pixel_out[0] = pixel_in[2]; | |
| 78 pixel_out[1] = pixel_in[1]; | |
| 79 pixel_out[2] = pixel_in[0]; | |
| 80 } | |
| 81 } | |
| 82 | |
| 83 // Automatically destroys the given write structs on destruction to make | |
| 84 // cleanup and error handling code cleaner. | |
| 85 class PngWriteStructDestroyer { | |
| 86 public: | |
| 87 PngWriteStructDestroyer(png_struct** ps, png_info** pi) : ps_(ps), pi_(pi) { | |
| 88 } | |
| 89 ~PngWriteStructDestroyer() { | |
| 90 png_destroy_write_struct(ps_, pi_); | |
| 91 } | |
| 92 private: | |
| 93 png_struct** ps_; | |
| 94 png_info** pi_; | |
| 95 | |
| 96 DISALLOW_EVIL_CONSTRUCTORS(PngWriteStructDestroyer); | |
| 97 }; | |
| 98 | |
| 99 } // namespace | |
| 100 | |
| 101 // static | |
| 102 bool PNGEncoder::Encode(const unsigned char* input, ColorFormat format, | |
| 103 int w, int h, int row_byte_width, | |
| 104 bool discard_transparency, | |
| 105 std::vector<unsigned char>* output) { | |
| 106 // Run to convert an input row into the output row format, NULL means no | |
| 107 // conversion is necessary. | |
| 108 void (*converter)(const unsigned char* in, int w, unsigned char* out) = NULL; | |
| 109 | |
| 110 int input_color_components, output_color_components; | |
| 111 int png_output_color_type; | |
| 112 switch (format) { | |
| 113 case FORMAT_RGB: | |
| 114 input_color_components = 3; | |
| 115 output_color_components = 3; | |
| 116 png_output_color_type = PNG_COLOR_TYPE_RGB; | |
| 117 discard_transparency = false; | |
| 118 break; | |
| 119 | |
| 120 case FORMAT_RGBA: | |
| 121 input_color_components = 4; | |
| 122 if (discard_transparency) { | |
| 123 output_color_components = 3; | |
| 124 png_output_color_type = PNG_COLOR_TYPE_RGB; | |
| 125 converter = ConvertRGBAtoRGB; | |
| 126 } else { | |
| 127 output_color_components = 4; | |
| 128 png_output_color_type = PNG_COLOR_TYPE_RGB_ALPHA; | |
| 129 converter = NULL; | |
| 130 } | |
| 131 break; | |
| 132 | |
| 133 case FORMAT_BGRA: | |
| 134 input_color_components = 4; | |
| 135 if (discard_transparency) { | |
| 136 output_color_components = 3; | |
| 137 png_output_color_type = PNG_COLOR_TYPE_RGB; | |
| 138 converter = ConvertBGRAtoRGB; | |
| 139 } else { | |
| 140 output_color_components = 4; | |
| 141 png_output_color_type = PNG_COLOR_TYPE_RGB_ALPHA; | |
| 142 converter = ConvertBetweenBGRAandRGBA; | |
| 143 } | |
| 144 break; | |
| 145 | |
| 146 default: | |
| 147 NOTREACHED() << "Unknown pixel format"; | |
| 148 return false; | |
| 149 } | |
| 150 | |
| 151 // Row stride should be at least as long as the length of the data. | |
| 152 DCHECK(input_color_components * w <= row_byte_width); | |
| 153 | |
| 154 png_struct* png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, | |
| 155 png_voidp_NULL, | |
| 156 png_error_ptr_NULL, | |
| 157 png_error_ptr_NULL); | |
| 158 if (!png_ptr) | |
| 159 return false; | |
| 160 png_info* info_ptr = png_create_info_struct(png_ptr); | |
| 161 if (!info_ptr) { | |
| 162 png_destroy_write_struct(&png_ptr, NULL); | |
| 163 return false; | |
| 164 } | |
| 165 PngWriteStructDestroyer destroyer(&png_ptr, &info_ptr); | |
| 166 | |
| 167 if (setjmp(png_jmpbuf(png_ptr))) { | |
| 168 // The destroyer will ensure that the structures are cleaned up in this | |
| 169 // case, even though we may get here as a jump from random parts of the | |
| 170 // PNG library called below. | |
| 171 return false; | |
| 172 } | |
| 173 | |
| 174 // Set our callback for libpng to give us the data. | |
| 175 PngEncoderState state(output); | |
| 176 png_set_write_fn(png_ptr, &state, EncoderWriteCallback, NULL); | |
| 177 | |
| 178 png_set_IHDR(png_ptr, info_ptr, w, h, 8, png_output_color_type, | |
| 179 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, | |
| 180 PNG_FILTER_TYPE_DEFAULT); | |
| 181 png_write_info(png_ptr, info_ptr); | |
| 182 | |
| 183 if (!converter) { | |
| 184 // No conversion needed, give the data directly to libpng. | |
| 185 for (int y = 0; y < h; y ++) | |
| 186 png_write_row(png_ptr, | |
| 187 const_cast<unsigned char*>(&input[y * row_byte_width])); | |
| 188 } else { | |
| 189 // Needs conversion using a separate buffer. | |
| 190 unsigned char* row = new unsigned char[w * output_color_components]; | |
| 191 for (int y = 0; y < h; y ++) { | |
| 192 converter(&input[y * row_byte_width], w, row); | |
| 193 png_write_row(png_ptr, row); | |
| 194 } | |
| 195 delete[] row; | |
| 196 } | |
| 197 | |
| 198 png_write_end(png_ptr, info_ptr); | |
| 199 return true; | |
| 200 } | |
| 201 | |
| 202 // static | |
| 203 bool PNGEncoder::EncodeBGRASkBitmap(const SkBitmap& input, | |
| 204 bool discard_transparency, | |
| 205 std::vector<unsigned char>* output) { | |
| 206 static const int bbp = 4; | |
| 207 | |
| 208 SkAutoLockPixels lock_input(input); | |
| 209 DCHECK(input.empty() || input.bytesPerPixel() == bbp); | |
| 210 | |
| 211 // SkBitmaps are premultiplied, we need to unpremultiply them. | |
| 212 scoped_array<unsigned char> divided( | |
| 213 new unsigned char[input.width() * input.height() * bbp]); | |
| 214 | |
| 215 int i = 0; | |
| 216 for (int y = 0; y < input.height(); y++) { | |
| 217 for (int x = 0; x < input.width(); x++) { | |
| 218 uint32 pixel = input.getAddr32(0, y)[x]; | |
| 219 | |
| 220 int alpha = SkColorGetA(pixel); | |
| 221 if (alpha != 0 && alpha != 255) { | |
| 222 SkColor unmultiplied = SkUnPreMultiply::PMColorToColor(pixel); | |
| 223 divided[i + 0] = SkColorGetR(unmultiplied); | |
| 224 divided[i + 1] = SkColorGetG(unmultiplied); | |
| 225 divided[i + 2] = SkColorGetB(unmultiplied); | |
| 226 divided[i + 3] = alpha; | |
| 227 } else { | |
| 228 divided[i + 0] = SkColorGetR(pixel); | |
| 229 divided[i + 1] = SkColorGetG(pixel); | |
| 230 divided[i + 2] = SkColorGetB(pixel); | |
| 231 divided[i + 3] = alpha; | |
| 232 } | |
| 233 i += bbp; | |
| 234 } | |
| 235 } | |
| 236 | |
| 237 return Encode(divided.get(), | |
| 238 PNGEncoder::FORMAT_RGBA, input.width(), input.height(), | |
| 239 input.width() * bbp, discard_transparency, output); | |
| 240 } | |
| OLD | NEW |