blob: c9534204b01cd4e3c50446c4cdef18ac7e95388b [file] [log] [blame]
// Copyright 2015 Google Inc. 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
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "cobalt/renderer/test/png_utils/png_encode.h"
#include <vector>
#include "base/debug/trace_event.h"
#include "base/file_util.h"
#include "base/logging.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 =
reinterpret_cast<PNGByteVector*>(png_get_io_ptr(png_ptr));
// 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 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.
size_t size;
scoped_array<uint8> buffer =
EncodeRGBAToBuffer(pixel_data, width, height, pitch_in_bytes, &size);
if (!buffer || size == 0) {
DLOG(ERROR) << "Failed to encode PNG.";
return;
}
int bytes_written = file_util::WriteFile(
png_file_path, 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.
scoped_array<uint8> EncodeRGBAToBuffer(const uint8_t* pixel_data, int width,
int height, int pitch_in_bytes,
size_t* out_size) {
TRACE_EVENT0("cobalt::renderer", "PNGEncode::EncodeRGBAToBuffer()");
// Initialize png library and headers for writing.
png_structp png =
png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
DCHECK(png);
png_infop info = png_create_info_struct(png);
DCHECK(info);
// 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 scoped_array<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_COLOR_TYPE_RGBA, PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
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.
scoped_array<uint8> out_buffer(new uint8[num_bytes]);
memcpy(out_buffer.get(), &(png_buffer[0]), num_bytes);
return out_buffer.Pass();
}
} // namespace png_utils
} // namespace test
} // namespace renderer
} // namespace cobalt