image_diff_png.cc 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645
  1. // Copyright 2013 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. #include "tools/imagediff/image_diff_png.h"
  5. #include <stdlib.h>
  6. #include <string.h>
  7. #include <ostream>
  8. #include "base/check.h"
  9. #include "base/notreached.h"
  10. #include "build/build_config.h"
  11. #include "third_party/libpng/png.h"
  12. #include "third_party/zlib/zlib.h"
  13. namespace image_diff_png {
  14. // This is a duplicate of ui/gfx/codec/png_codec.cc, after removing code related
  15. // to Skia, that we can use when running layout tests with minimal dependencies.
  16. namespace {
  17. enum ColorFormat {
  18. // 3 bytes per pixel (packed), in RGB order regardless of endianness.
  19. // This is the native JPEG format.
  20. FORMAT_RGB,
  21. // 4 bytes per pixel, in RGBA order in memory regardless of endianness.
  22. FORMAT_RGBA,
  23. // 4 bytes per pixel, in BGRA order in memory regardless of endianness.
  24. // This is the default Windows DIB order.
  25. FORMAT_BGRA,
  26. // 4 bytes per pixel, in pre-multiplied kARGB_8888_Config format. For use
  27. // with directly writing to a skia bitmap.
  28. FORMAT_SkBitmap
  29. };
  30. // Represents a comment in the tEXt ancillary chunk of the png.
  31. struct Comment {
  32. std::string key;
  33. std::string text;
  34. };
  35. // Converts BGRA->RGBA and RGBA->BGRA.
  36. void ConvertBetweenBGRAandRGBA(const unsigned char* input, int pixel_width,
  37. unsigned char* output, bool* is_opaque) {
  38. for (int x = 0; x < pixel_width; x++) {
  39. const unsigned char* pixel_in = &input[x * 4];
  40. unsigned char* pixel_out = &output[x * 4];
  41. pixel_out[0] = pixel_in[2];
  42. pixel_out[1] = pixel_in[1];
  43. pixel_out[2] = pixel_in[0];
  44. pixel_out[3] = pixel_in[3];
  45. }
  46. }
  47. void ConvertRGBAtoRGB(const unsigned char* rgba, int pixel_width,
  48. unsigned char* rgb, bool* is_opaque) {
  49. for (int x = 0; x < pixel_width; x++) {
  50. const unsigned char* pixel_in = &rgba[x * 4];
  51. unsigned char* pixel_out = &rgb[x * 3];
  52. pixel_out[0] = pixel_in[0];
  53. pixel_out[1] = pixel_in[1];
  54. pixel_out[2] = pixel_in[2];
  55. }
  56. }
  57. } // namespace
  58. // Decoder --------------------------------------------------------------------
  59. //
  60. // This code is based on WebKit libpng interface (PNGImageDecoder), which is
  61. // in turn based on the Mozilla png decoder.
  62. namespace {
  63. // Gamma constants: We assume we're on Windows which uses a gamma of 2.2.
  64. const double kMaxGamma = 21474.83; // Maximum gamma accepted by png library.
  65. const double kDefaultGamma = 2.2;
  66. const double kInverseGamma = 1.0 / kDefaultGamma;
  67. class PngDecoderState {
  68. public:
  69. // Output is a vector<unsigned char>.
  70. PngDecoderState(ColorFormat ofmt, std::vector<unsigned char>* o)
  71. : output_format(ofmt),
  72. output_channels(0),
  73. is_opaque(true),
  74. output(o),
  75. row_converter(nullptr),
  76. width(0),
  77. height(0),
  78. done(false) {}
  79. ColorFormat output_format;
  80. int output_channels;
  81. // Used during the reading of an SkBitmap. Defaults to true until we see a
  82. // pixel with anything other than an alpha of 255.
  83. bool is_opaque;
  84. // An intermediary buffer for decode output.
  85. std::vector<unsigned char>* output;
  86. // Called to convert a row from the library to the correct output format.
  87. // When NULL, no conversion is necessary.
  88. void (*row_converter)(const unsigned char* in, int w, unsigned char* out,
  89. bool* is_opaque);
  90. // Size of the image, set in the info callback.
  91. int width;
  92. int height;
  93. // Set to true when we've found the end of the data.
  94. bool done;
  95. };
  96. void ConvertRGBtoRGBA(const unsigned char* rgb, int pixel_width,
  97. unsigned char* rgba, bool* is_opaque) {
  98. for (int x = 0; x < pixel_width; x++) {
  99. const unsigned char* pixel_in = &rgb[x * 3];
  100. unsigned char* pixel_out = &rgba[x * 4];
  101. pixel_out[0] = pixel_in[0];
  102. pixel_out[1] = pixel_in[1];
  103. pixel_out[2] = pixel_in[2];
  104. pixel_out[3] = 0xff;
  105. }
  106. }
  107. void ConvertRGBtoBGRA(const unsigned char* rgb, int pixel_width,
  108. unsigned char* bgra, bool* is_opaque) {
  109. for (int x = 0; x < pixel_width; x++) {
  110. const unsigned char* pixel_in = &rgb[x * 3];
  111. unsigned char* pixel_out = &bgra[x * 4];
  112. pixel_out[0] = pixel_in[2];
  113. pixel_out[1] = pixel_in[1];
  114. pixel_out[2] = pixel_in[0];
  115. pixel_out[3] = 0xff;
  116. }
  117. }
  118. // Called when the png header has been read. This code is based on the WebKit
  119. // PNGImageDecoder
  120. void DecodeInfoCallback(png_struct* png_ptr, png_info* info_ptr) {
  121. PngDecoderState* state = static_cast<PngDecoderState*>(
  122. png_get_progressive_ptr(png_ptr));
  123. int bit_depth, color_type, interlace_type, compression_type;
  124. int filter_type, channels;
  125. png_uint_32 w, h;
  126. png_get_IHDR(png_ptr, info_ptr, &w, &h, &bit_depth, &color_type,
  127. &interlace_type, &compression_type, &filter_type);
  128. // Bounds check. When the image is unreasonably big, we'll error out and
  129. // end up back at the setjmp call when we set up decoding. "Unreasonably big"
  130. // means "big enough that w * h * 32bpp might overflow an int"; we choose this
  131. // threshold to match WebKit and because a number of places in code assume
  132. // that an image's size (in bytes) fits in a (signed) int.
  133. unsigned long long total_size =
  134. static_cast<unsigned long long>(w) * static_cast<unsigned long long>(h);
  135. if (total_size > ((1 << 29) - 1))
  136. longjmp(png_jmpbuf(png_ptr), 1);
  137. state->width = static_cast<int>(w);
  138. state->height = static_cast<int>(h);
  139. // Expand to ensure we use 24-bit for RGB and 32-bit for RGBA.
  140. if (color_type == PNG_COLOR_TYPE_PALETTE ||
  141. (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8))
  142. png_set_expand(png_ptr);
  143. // Transparency for paletted images.
  144. if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
  145. png_set_expand(png_ptr);
  146. // Convert 16-bit to 8-bit.
  147. if (bit_depth == 16)
  148. png_set_strip_16(png_ptr);
  149. // Expand grayscale to RGB.
  150. if (color_type == PNG_COLOR_TYPE_GRAY ||
  151. color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
  152. png_set_gray_to_rgb(png_ptr);
  153. // Deal with gamma and keep it under our control.
  154. double gamma;
  155. if (png_get_gAMA(png_ptr, info_ptr, &gamma)) {
  156. if (gamma <= 0.0 || gamma > kMaxGamma) {
  157. gamma = kInverseGamma;
  158. png_set_gAMA(png_ptr, info_ptr, gamma);
  159. }
  160. png_set_gamma(png_ptr, kDefaultGamma, gamma);
  161. } else {
  162. png_set_gamma(png_ptr, kDefaultGamma, kInverseGamma);
  163. }
  164. // Tell libpng to send us rows for interlaced pngs.
  165. if (interlace_type == PNG_INTERLACE_ADAM7)
  166. png_set_interlace_handling(png_ptr);
  167. // Update our info now
  168. png_read_update_info(png_ptr, info_ptr);
  169. channels = png_get_channels(png_ptr, info_ptr);
  170. // Pick our row format converter necessary for this data.
  171. if (channels == 3) {
  172. switch (state->output_format) {
  173. case FORMAT_RGB:
  174. state->row_converter = NULL; // no conversion necessary
  175. state->output_channels = 3;
  176. break;
  177. case FORMAT_RGBA:
  178. state->row_converter = &ConvertRGBtoRGBA;
  179. state->output_channels = 4;
  180. break;
  181. case FORMAT_BGRA:
  182. state->row_converter = &ConvertRGBtoBGRA;
  183. state->output_channels = 4;
  184. break;
  185. default:
  186. NOTREACHED() << "Unknown output format";
  187. break;
  188. }
  189. } else if (channels == 4) {
  190. switch (state->output_format) {
  191. case FORMAT_RGB:
  192. state->row_converter = &ConvertRGBAtoRGB;
  193. state->output_channels = 3;
  194. break;
  195. case FORMAT_RGBA:
  196. state->row_converter = NULL; // no conversion necessary
  197. state->output_channels = 4;
  198. break;
  199. case FORMAT_BGRA:
  200. state->row_converter = &ConvertBetweenBGRAandRGBA;
  201. state->output_channels = 4;
  202. break;
  203. default:
  204. NOTREACHED() << "Unknown output format";
  205. break;
  206. }
  207. } else {
  208. NOTREACHED() << "Unknown input channels";
  209. longjmp(png_jmpbuf(png_ptr), 1);
  210. }
  211. state->output->resize(
  212. state->width * state->output_channels * state->height);
  213. }
  214. void DecodeRowCallback(png_struct* png_ptr, png_byte* new_row,
  215. png_uint_32 row_num, int pass) {
  216. PngDecoderState* state = static_cast<PngDecoderState*>(
  217. png_get_progressive_ptr(png_ptr));
  218. DCHECK(pass == 0);
  219. if (static_cast<int>(row_num) > state->height) {
  220. NOTREACHED() << "Invalid row";
  221. return;
  222. }
  223. unsigned char* base = NULL;
  224. base = &state->output->front();
  225. unsigned char* dest = &base[state->width * state->output_channels * row_num];
  226. if (state->row_converter)
  227. state->row_converter(new_row, state->width, dest, &state->is_opaque);
  228. else
  229. memcpy(dest, new_row, state->width * state->output_channels);
  230. }
  231. void DecodeEndCallback(png_struct* png_ptr, png_info* info) {
  232. PngDecoderState* state = static_cast<PngDecoderState*>(
  233. png_get_progressive_ptr(png_ptr));
  234. // Mark the image as complete, this will tell the Decode function that we
  235. // have successfully found the end of the data.
  236. state->done = true;
  237. }
  238. // Automatically destroys the given read structs on destruction to make
  239. // cleanup and error handling code cleaner.
  240. class PngReadStructDestroyer {
  241. public:
  242. PngReadStructDestroyer(png_struct** ps, png_info** pi) : ps_(ps), pi_(pi) {
  243. }
  244. ~PngReadStructDestroyer() {
  245. png_destroy_read_struct(ps_, pi_, NULL);
  246. }
  247. private:
  248. png_struct** ps_;
  249. png_info** pi_;
  250. };
  251. bool BuildPNGStruct(const unsigned char* input, size_t input_size,
  252. png_struct** png_ptr, png_info** info_ptr) {
  253. if (input_size < 8)
  254. return false; // Input data too small to be a png
  255. // Have libpng check the signature, it likes the first 8 bytes.
  256. if (png_sig_cmp(const_cast<unsigned char*>(input), 0, 8) != 0)
  257. return false;
  258. *png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
  259. if (!*png_ptr)
  260. return false;
  261. *info_ptr = png_create_info_struct(*png_ptr);
  262. if (!*info_ptr) {
  263. png_destroy_read_struct(png_ptr, NULL, NULL);
  264. return false;
  265. }
  266. return true;
  267. }
  268. } // namespace
  269. // static
  270. bool Decode(const unsigned char* input, size_t input_size,
  271. ColorFormat format, std::vector<unsigned char>* output,
  272. int* w, int* h) {
  273. png_struct* png_ptr = NULL;
  274. png_info* info_ptr = NULL;
  275. if (!BuildPNGStruct(input, input_size, &png_ptr, &info_ptr))
  276. return false;
  277. PngReadStructDestroyer destroyer(&png_ptr, &info_ptr);
  278. if (setjmp(png_jmpbuf(png_ptr))) {
  279. // The destroyer will ensure that the structures are cleaned up in this
  280. // case, even though we may get here as a jump from random parts of the
  281. // PNG library called below.
  282. return false;
  283. }
  284. PngDecoderState state(format, output);
  285. png_set_progressive_read_fn(png_ptr, &state, &DecodeInfoCallback,
  286. &DecodeRowCallback, &DecodeEndCallback);
  287. png_process_data(png_ptr,
  288. info_ptr,
  289. const_cast<unsigned char*>(input),
  290. input_size);
  291. if (!state.done) {
  292. // Fed it all the data but the library didn't think we got all the data, so
  293. // this file must be truncated.
  294. output->clear();
  295. return false;
  296. }
  297. *w = state.width;
  298. *h = state.height;
  299. return true;
  300. }
  301. // Encoder --------------------------------------------------------------------
  302. //
  303. // This section of the code is based on nsPNGEncoder.cpp in Mozilla
  304. // (Copyright 2005 Google Inc.)
  305. namespace {
  306. // Passed around as the io_ptr in the png structs so our callbacks know where
  307. // to write data.
  308. struct PngEncoderState {
  309. explicit PngEncoderState(std::vector<unsigned char>* o) : out(o) {}
  310. std::vector<unsigned char>* out;
  311. };
  312. // Called by libpng to flush its internal buffer to ours.
  313. void EncoderWriteCallback(png_structp png, png_bytep data, png_size_t size) {
  314. PngEncoderState* state = static_cast<PngEncoderState*>(png_get_io_ptr(png));
  315. DCHECK(state->out);
  316. size_t old_size = state->out->size();
  317. state->out->resize(old_size + size);
  318. memcpy(&(*state->out)[old_size], data, size);
  319. }
  320. void FakeFlushCallback(png_structp png) {
  321. // We don't need to perform any flushing since we aren't doing real IO, but
  322. // we're required to provide this function by libpng.
  323. }
  324. void ConvertBGRAtoRGB(const unsigned char* bgra, int pixel_width,
  325. unsigned char* rgb, bool* is_opaque) {
  326. for (int x = 0; x < pixel_width; x++) {
  327. const unsigned char* pixel_in = &bgra[x * 4];
  328. unsigned char* pixel_out = &rgb[x * 3];
  329. pixel_out[0] = pixel_in[2];
  330. pixel_out[1] = pixel_in[1];
  331. pixel_out[2] = pixel_in[0];
  332. }
  333. }
  334. #ifdef PNG_TEXT_SUPPORTED
  335. inline char* strdup(const char* str) {
  336. #if BUILDFLAG(IS_WIN)
  337. return _strdup(str);
  338. #else
  339. return ::strdup(str);
  340. #endif
  341. }
  342. class CommentWriter {
  343. public:
  344. explicit CommentWriter(const std::vector<Comment>& comments)
  345. : comments_(comments),
  346. png_text_(new png_text[comments.size()]) {
  347. for (size_t i = 0; i < comments.size(); ++i)
  348. AddComment(i, comments[i]);
  349. }
  350. ~CommentWriter() {
  351. for (size_t i = 0; i < comments_.size(); ++i) {
  352. free(png_text_[i].key);
  353. free(png_text_[i].text);
  354. }
  355. delete [] png_text_;
  356. }
  357. bool HasComments() {
  358. return !comments_.empty();
  359. }
  360. png_text* get_png_text() {
  361. return png_text_;
  362. }
  363. int size() {
  364. return static_cast<int>(comments_.size());
  365. }
  366. private:
  367. void AddComment(size_t pos, const Comment& comment) {
  368. png_text_[pos].compression = PNG_TEXT_COMPRESSION_NONE;
  369. // A PNG comment's key can only be 79 characters long.
  370. DCHECK(comment.key.length() < 79);
  371. png_text_[pos].key = strdup(comment.key.substr(0, 78).c_str());
  372. png_text_[pos].text = strdup(comment.text.c_str());
  373. png_text_[pos].text_length = comment.text.length();
  374. #ifdef PNG_iTXt_SUPPORTED
  375. png_text_[pos].itxt_length = 0;
  376. png_text_[pos].lang = 0;
  377. png_text_[pos].lang_key = 0;
  378. #endif
  379. }
  380. const std::vector<Comment> comments_;
  381. png_text* png_text_;
  382. };
  383. #endif // PNG_TEXT_SUPPORTED
  384. // The type of functions usable for converting between pixel formats.
  385. typedef void (*FormatConverter)(const unsigned char* in, int w,
  386. unsigned char* out, bool* is_opaque);
  387. // libpng uses a wacky setjmp-based API, which makes the compiler nervous.
  388. // We constrain all of the calls we make to libpng where the setjmp() is in
  389. // place to this function.
  390. // Returns true on success.
  391. bool DoLibpngWrite(png_struct* png_ptr, png_info* info_ptr,
  392. PngEncoderState* state,
  393. int width, int height, int row_byte_width,
  394. const unsigned char* input, int compression_level,
  395. int png_output_color_type, int output_color_components,
  396. FormatConverter converter,
  397. const std::vector<Comment>& comments) {
  398. #ifdef PNG_TEXT_SUPPORTED
  399. CommentWriter comment_writer(comments);
  400. #endif
  401. unsigned char* row_buffer = NULL;
  402. // Make sure to not declare any locals here -- locals in the presence
  403. // of setjmp() in C++ code makes gcc complain.
  404. if (setjmp(png_jmpbuf(png_ptr))) {
  405. delete[] row_buffer;
  406. return false;
  407. }
  408. png_set_compression_level(png_ptr, compression_level);
  409. // Set our callback for libpng to give us the data.
  410. png_set_write_fn(png_ptr, state, EncoderWriteCallback, FakeFlushCallback);
  411. png_set_IHDR(png_ptr, info_ptr, width, height, 8, png_output_color_type,
  412. PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
  413. PNG_FILTER_TYPE_DEFAULT);
  414. #ifdef PNG_TEXT_SUPPORTED
  415. if (comment_writer.HasComments()) {
  416. png_set_text(png_ptr, info_ptr, comment_writer.get_png_text(),
  417. comment_writer.size());
  418. }
  419. #endif
  420. png_write_info(png_ptr, info_ptr);
  421. if (!converter) {
  422. // No conversion needed, give the data directly to libpng.
  423. for (int y = 0; y < height; y ++) {
  424. png_write_row(png_ptr,
  425. const_cast<unsigned char*>(&input[y * row_byte_width]));
  426. }
  427. } else {
  428. // Needs conversion using a separate buffer.
  429. row_buffer = new unsigned char[width * output_color_components];
  430. for (int y = 0; y < height; y ++) {
  431. converter(&input[y * row_byte_width], width, row_buffer, NULL);
  432. png_write_row(png_ptr, row_buffer);
  433. }
  434. delete[] row_buffer;
  435. }
  436. png_write_end(png_ptr, info_ptr);
  437. return true;
  438. }
  439. } // namespace
  440. // static
  441. bool EncodeWithCompressionLevel(const unsigned char* input, ColorFormat format,
  442. const int width, const int height,
  443. int row_byte_width,
  444. bool discard_transparency,
  445. const std::vector<Comment>& comments,
  446. int compression_level,
  447. std::vector<unsigned char>* output) {
  448. // Run to convert an input row into the output row format, NULL means no
  449. // conversion is necessary.
  450. FormatConverter converter = NULL;
  451. int input_color_components, output_color_components;
  452. int png_output_color_type;
  453. switch (format) {
  454. case FORMAT_RGB:
  455. input_color_components = 3;
  456. output_color_components = 3;
  457. png_output_color_type = PNG_COLOR_TYPE_RGB;
  458. discard_transparency = false;
  459. break;
  460. case FORMAT_RGBA:
  461. input_color_components = 4;
  462. if (discard_transparency) {
  463. output_color_components = 3;
  464. png_output_color_type = PNG_COLOR_TYPE_RGB;
  465. converter = ConvertRGBAtoRGB;
  466. } else {
  467. output_color_components = 4;
  468. png_output_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
  469. converter = NULL;
  470. }
  471. break;
  472. case FORMAT_BGRA:
  473. input_color_components = 4;
  474. if (discard_transparency) {
  475. output_color_components = 3;
  476. png_output_color_type = PNG_COLOR_TYPE_RGB;
  477. converter = ConvertBGRAtoRGB;
  478. } else {
  479. output_color_components = 4;
  480. png_output_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
  481. converter = ConvertBetweenBGRAandRGBA;
  482. }
  483. break;
  484. default:
  485. NOTREACHED() << "Unknown pixel format";
  486. return false;
  487. }
  488. // Row stride should be at least as long as the length of the data.
  489. DCHECK(input_color_components * width <= row_byte_width);
  490. png_struct* png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
  491. NULL, NULL, NULL);
  492. if (!png_ptr)
  493. return false;
  494. png_info* info_ptr = png_create_info_struct(png_ptr);
  495. if (!info_ptr) {
  496. png_destroy_write_struct(&png_ptr, NULL);
  497. return false;
  498. }
  499. PngEncoderState state(output);
  500. bool success = DoLibpngWrite(png_ptr, info_ptr, &state,
  501. width, height, row_byte_width,
  502. input, compression_level, png_output_color_type,
  503. output_color_components, converter, comments);
  504. png_destroy_write_struct(&png_ptr, &info_ptr);
  505. return success;
  506. }
  507. // static
  508. bool Encode(const unsigned char* input, ColorFormat format,
  509. const int width, const int height, int row_byte_width,
  510. bool discard_transparency,
  511. const std::vector<Comment>& comments,
  512. std::vector<unsigned char>* output) {
  513. return EncodeWithCompressionLevel(input, format, width, height,
  514. row_byte_width,
  515. discard_transparency,
  516. comments, Z_DEFAULT_COMPRESSION,
  517. output);
  518. }
  519. // Decode a PNG into an RGBA pixel array.
  520. bool DecodePNG(const unsigned char* input, size_t input_size,
  521. std::vector<unsigned char>* output,
  522. int* width, int* height) {
  523. return Decode(input, input_size, FORMAT_RGBA, output, width, height);
  524. }
  525. // Encode an RGBA pixel array into a PNG.
  526. bool EncodeRGBAPNG(const unsigned char* input,
  527. int width,
  528. int height,
  529. int row_byte_width,
  530. std::vector<unsigned char>* output) {
  531. return Encode(input, FORMAT_RGBA,
  532. width, height, row_byte_width, false,
  533. std::vector<Comment>(), output);
  534. }
  535. // Encode an BGRA pixel array into a PNG.
  536. bool EncodeBGRAPNG(const unsigned char* input,
  537. int width,
  538. int height,
  539. int row_byte_width,
  540. bool discard_transparency,
  541. std::vector<unsigned char>* output) {
  542. return Encode(input, FORMAT_BGRA,
  543. width, height, row_byte_width, discard_transparency,
  544. std::vector<Comment>(), output);
  545. }
  546. } // image_diff_png