blob: be5cfbd44b3de4976d326b73d2c84e2b6cc70db2 [file] [log] [blame]
// Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "window_manager/image_container.h"
#include <png.h>
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
using std::string;
namespace window_manager {
static const int kPngSignatureSize = 8;
// static
ImageContainer* ImageContainer::CreateContainerFromFile(
const string& filename) {
if (PngImageContainer::IsPngImage(filename)) {
return new PngImageContainer(filename);
} else {
LOG(ERROR) << "Unable to determine file type of '" << filename
<< "' in ImageContainer::CreateContainerFromFile()";
return NULL;
}
}
ImageContainer::ImageContainer()
: data_(NULL),
data_was_allocated_with_malloc_(false),
width_(0),
height_(0),
format_(IMAGE_FORMAT_UNKNOWN) {
}
void ImageContainer::SetData(uint8_t* new_data,
bool was_allocated_with_malloc) {
if (data_) {
if (data_was_allocated_with_malloc_)
free(data_);
else
delete[] data_;
}
data_ = new_data;
data_was_allocated_with_malloc_ = was_allocated_with_malloc;
}
// static
bool PngImageContainer::IsPngImage(const string& filename) {
// Load the image.
FILE* fp = fopen(filename.c_str(), "rb");
if (!fp) {
LOG(ERROR) << "Unable to open '" << filename
<< "' for reading in IsPngImage.";
return false;
}
// Allocate a buffer where we can put the file signature.
png_byte pngsig[kPngSignatureSize];
// Read the signature from the file into the signature buffer.
size_t bytes_read = fread(&pngsig[0], sizeof(png_byte),
kPngSignatureSize, fp);
fclose(fp);
if (bytes_read != (sizeof(png_byte) * kPngSignatureSize)) {
LOG(ERROR) << "Unable to read data from '" << filename
<< "' in IsPngImage.";
return false;
}
return png_sig_cmp(pngsig, 0, kPngSignatureSize) == 0 ? true : false;
}
PngImageContainer::PngImageContainer(const string& filename)
: filename_(filename) {
}
static void PngErrorHandler(png_structp container_ptr,
png_const_charp error_str) {
PngImageContainer* container =
reinterpret_cast<PngImageContainer*>(container_ptr);
LOG(ERROR) << "PNG error while reading '" << container->filename()
<< "':" << error_str;
}
static void PngWarningHandler(png_structp container_ptr,
png_const_charp error_str) {
PngImageContainer* container =
reinterpret_cast<PngImageContainer*>(container_ptr);
LOG(WARNING) << "PNG warning while reading '" << container->filename()
<< "':" << error_str;
}
ImageContainer::Result PngImageContainer::LoadImage() {
png_structp read_obj = png_create_read_struct(PNG_LIBPNG_VER_STRING,
dynamic_cast<void*>(this),
PngErrorHandler,
PngWarningHandler);
if (!read_obj) {
LOG(ERROR) << "Couldn't initialize png read struct in LoadImage.";
return ImageContainer::IMAGE_LOAD_FAILURE;
}
png_infop info_obj = png_create_info_struct(read_obj);
if (!info_obj) {
LOG(ERROR) << "Couldn't initialize png info struct in LoadImage.";
png_destroy_read_struct(&read_obj, NULL, NULL);
return ImageContainer::IMAGE_LOAD_FAILURE;
}
// Load the image.
FILE* fp = fopen(filename_.c_str(), "rb");
if (!fp) {
LOG(ERROR) << "Unable to open '" << filename_
<< "' for reading in LoadImage.";
png_destroy_read_struct(&read_obj, &info_obj, NULL);
return ImageContainer::IMAGE_LOAD_FAILURE;
}
png_init_io(read_obj, fp);
png_read_info(read_obj, info_obj);
set_width(png_get_image_width(read_obj, info_obj));
set_height(png_get_image_height(read_obj, info_obj));
png_uint_32 color_type = png_get_color_type(read_obj, info_obj);
png_uint_32 depth = png_get_bit_depth(read_obj, info_obj);
switch (color_type) {
case PNG_COLOR_TYPE_PALETTE:
// Read paletted images as RGB
png_set_palette_to_rgb(read_obj);
break;
case PNG_COLOR_TYPE_GRAY_ALPHA:
case PNG_COLOR_TYPE_GRAY:
// Expand smaller bit depths to eight-bit.
if (depth < 8)
png_set_gray_1_2_4_to_8(read_obj);
// Convert grayscale images to RGB.
png_set_gray_to_rgb(read_obj);
break;
default:
break;
}
// Add an opaque alpha channel if there isn't one already.
if (!(color_type & PNG_COLOR_MASK_ALPHA)) {
png_set_filler(read_obj, 0xff, PNG_FILLER_AFTER);
set_format(IMAGE_FORMAT_RGBX_32);
} else {
set_format(IMAGE_FORMAT_RGBA_32);
}
// If the image has a transparancy color set, convert it to an alpha
// channel.
if (png_get_valid(read_obj, info_obj, PNG_INFO_tRNS)) {
png_set_tRNS_to_alpha(read_obj);
}
// We don't support 16 bit precision, so if the image has 16 bits
// per channel, truncate it to 8 bits.
if (depth == 16) {
png_set_strip_16(read_obj);
}
scoped_array<uint8_t*> row_pointers(new uint8_t*[height()]);
SetData(new uint8_t[height() * stride()], false); // malloc=false
for (size_t i = 0; i < height(); i++) {
size_t position = i * stride();
row_pointers[i] = data() + position;
}
png_read_image(read_obj, reinterpret_cast<png_byte**>(row_pointers.get()));
png_destroy_read_struct(&read_obj, &info_obj, NULL);
fclose(fp);
DLOG(INFO) << "Successfully loaded image '" << filename_ << "' ("
<< width() << "x" << height() << ", "
<< bits_per_pixel() << " bit(s)/pixel)";
return ImageContainer::IMAGE_LOAD_SUCCESS;
}
InMemoryImageContainer::InMemoryImageContainer(
uint8_t* new_data, size_t new_width, size_t new_height,
ImageFormat new_format, bool was_allocated_with_malloc) {
DCHECK(new_data);
SetData(new_data, was_allocated_with_malloc);
set_width(new_width);
set_height(new_height);
set_format(new_format);
}
} // namespace window_manager