blob: 38416f422e2f9e7a649bd47acd88102f62f544b7 [file] [log] [blame]
// Copyright 2015 The Cobalt Authors. All Rights Reserved.
// 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
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.
#include "cobalt/renderer/test/png_utils/png_encode.h"
#include <memory>
#include <vector>
#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/trace_event/trace_event.h"
#include "third_party/libpng/png.h"
namespace cobalt {
namespace renderer {
namespace test {
namespace png_utils {
namespace {
// Write PNG data to a vector to simplify memory management.
typedef std::vector<png_byte> PNGByteVector;
void PNGWriteFunction(png_structp png_ptr, png_bytep data, png_size_t length) {
PNGByteVector* out_buffer =
// Append the data to the array using pointers to the beginning and end of the
// buffer as the first and last iterators.
out_buffer->insert(out_buffer->end(), data, data + length);
} // namespace
void EncodeRGBAToPNG(const base::FilePath& png_file_path,
const uint8_t* pixel_data, int width, int height,
int pitch_in_bytes) {
// Write the PNG to an in-memory buffer and then write it to disk.
TRACE_EVENT0("cobalt::renderer", "png_encode::EncodeRGBAToPNG()");
size_t size;
std::unique_ptr<uint8[]> buffer =
EncodeRGBAToBuffer(pixel_data, width, height, pitch_in_bytes, &size);
if (!buffer || size == 0) {
DLOG(ERROR) << "Failed to encode PNG.";
SbFile file = SbFileOpen(png_file_path.value().c_str(),
kSbFileOpenAlways | kSbFileWrite, NULL, NULL);
DCHECK_NE(file, kSbFileInvalid);
int bytes_written =
SbFileWrite(file, reinterpret_cast<char*>(buffer.get()), size);
DLOG_IF(ERROR, bytes_written != size) << "Error writing PNG to file.";
// Encodes RGBA8 formatted pixel data to an in memory buffer.
std::unique_ptr<uint8[]> EncodeRGBAToBuffer(const uint8_t* pixel_data,
int width, int height,
int pitch_in_bytes,
size_t* out_size) {
TRACE_EVENT0("cobalt::renderer", "png_encode::EncodeRGBAToBuffer()");
// Initialize png library and headers for writing.
png_structp png =
png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
png_infop info = png_create_info_struct(png);
// if error encountered png will call longjmp(), so we set up a setjmp() here
// with a failed assert to indicate an error in one of the png functions.
// yo libpng, 1980 called, they want their longjmp() back....
if (setjmp(png->jmpbuf)) {
png_destroy_write_struct(&png, &info);
NOTREACHED() << "libpng encountered an error during processing.";
return std::unique_ptr<uint8[]>();
// Structure into which png data will be written.
PNGByteVector png_buffer;
// Set the write callback. Don't set the flush function, since there's no
// need for buffered IO when writing to memory.
png_set_write_fn(png, &png_buffer, &PNGWriteFunction, NULL);
// Stuff and then write png header.
png_set_IHDR(png, info, width, height,
8, // 8 bits per color channel.
png_write_info(png, info);
// Write image bytes, row by row.
png_bytep row = (png_bytep)pixel_data;
for (int i = 0; i < height; ++i) {
png_write_row(png, row);
row += pitch_in_bytes;
png_write_end(png, NULL);
png_destroy_write_struct(&png, &info);
size_t num_bytes = png_buffer.size() * sizeof(PNGByteVector::value_type);
*out_size = num_bytes;
// Copy the memory from the buffer to a scoped_array to return to the caller.
std::unique_ptr<uint8[]> out_buffer(new uint8[num_bytes]);
memcpy(out_buffer.get(), &(png_buffer[0]), num_bytes);
return std::move(out_buffer);
} // namespace png_utils
} // namespace test
} // namespace renderer
} // namespace cobalt