nana/source/paint/detail/image_png.hpp
Rei f42cd7b2eb Include cstring header on image_png.hpp
Compilation was failing with the "enable png" flag. This was happening
because the memcpy function was being called without the inclusion of
cstring, which declares it. This fixes it.
2016-11-05 04:46:29 -02:00

228 lines
5.9 KiB
C++

#ifndef NANA_PAINT_DETAIL_IMAGE_PNG_HPP
#define NANA_PAINT_DETAIL_IMAGE_PNG_HPP
#include "image_pixbuf.hpp"
#include <cstring>
//Separate the libpng from the package that system provides.
#if defined(NANA_LIBPNG)
#include <nana_extrlib/png.h>
#else
#include <png.h>
#endif
#include <stdio.h>
namespace nana
{
namespace paint{ namespace detail{
class image_png
: public basic_image_pixbuf
{
void _m_read_png(png_structp png_ptr, png_infop info_ptr)
{
::png_read_info(png_ptr, info_ptr);
const int png_width = ::png_get_image_width(png_ptr, info_ptr);
const int png_height = ::png_get_image_height(png_ptr, info_ptr);
png_byte color_type = ::png_get_color_type(png_ptr, info_ptr);
const auto bit_depth = ::png_get_bit_depth(png_ptr, info_ptr);
//do some extra work for palette/gray color type
if (PNG_COLOR_TYPE_PALETTE == color_type)
::png_set_palette_to_rgb(png_ptr);
else if ((PNG_COLOR_TYPE_GRAY == color_type) && (bit_depth < 8))
::png_set_gray_to_rgb(png_ptr);
auto is_alpha_enabled = (::png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS) != 0);
if (is_alpha_enabled)
::png_set_tRNS_to_alpha(png_ptr);
//make sure 8-bit per channel
if (16 == bit_depth)
::png_set_strip_16(png_ptr);
::png_set_interlace_handling(png_ptr);
::png_read_update_info(png_ptr, info_ptr);
//The following codes may longjmp while image_read error.
png_bytep * row_ptrs = new png_bytep[png_height];
const std::size_t png_rowbytes = ::png_get_rowbytes(png_ptr, info_ptr);
pixbuf_.open(png_width, png_height);
is_alpha_enabled |= ((PNG_COLOR_MASK_ALPHA & color_type) != 0);
pixbuf_.alpha_channel(is_alpha_enabled);
if (is_alpha_enabled && (png_rowbytes == png_width * sizeof(pixel_argb_t)))
{
for (int i = 0; i < png_height; ++i)
row_ptrs[i] = reinterpret_cast<png_bytep>(pixbuf_.raw_ptr(i));
::png_read_image(png_ptr, row_ptrs);
for (int i = 0; i < png_height; ++i)
{
auto p = pixbuf_.raw_ptr(i);
for (int u = 0; u < png_width; ++u)
{
auto t = p[u].element.red;
p[u].element.red = p[u].element.blue;
p[u].element.blue = t;
}
}
}
else
{
png_byte * png_pixbuf = new png_byte[png_height * png_rowbytes];
for (int i = 0; i < png_height; ++i)
row_ptrs[i] = reinterpret_cast<png_bytep>(png_pixbuf + png_rowbytes * i);
::png_read_image(png_ptr, row_ptrs);
//::png_destroy_read_struct(&png_ptr, &info_ptr, 0);
std::size_t png_pixel_bytes = png_rowbytes / png_width;
pixel_argb_t * rgb_row_ptr = pixbuf_.raw_ptr(0);
for (int y = 0; y < png_height; ++y)
{
png_bytep png_ptr = row_ptrs[y];
pixel_argb_t * rgb_end = rgb_row_ptr + png_width;
if (is_alpha_enabled)
{
for (pixel_argb_t * i = rgb_row_ptr; i < rgb_end; ++i)
{
i->element.red = png_ptr[0];
i->element.green = png_ptr[1];
i->element.blue = png_ptr[2];
i->element.alpha_channel = png_ptr[3];
png_ptr += png_pixel_bytes;
}
}
else
{
for (pixel_argb_t * i = rgb_row_ptr; i < rgb_end; ++i)
{
i->element.red = png_ptr[0];
i->element.green = png_ptr[1];
i->element.blue = png_ptr[2];
i->element.alpha_channel = 255;
png_ptr += png_pixel_bytes;
}
}
rgb_row_ptr = rgb_end;
}
delete[] png_pixbuf;
}
delete[] row_ptrs;
}
public:
bool open(const std::experimental::filesystem::path& png_file) override
{
auto fp = ::fopen(to_osmbstr(to_utf8(png_file.native())).c_str(), "rb");
if(nullptr == fp) return false;
bool is_opened = false;
png_byte png_sig[8];
::fread(png_sig, 1, 8, fp);
//Test whether the file is a png.
if(0 == png_sig_cmp(png_sig, 0, 8))
{
png_structp png_ptr = ::png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
if(png_ptr)
{
png_infop info_ptr = ::png_create_info_struct(png_ptr);
if(info_ptr)
{
if(!setjmp(png_jmpbuf(png_ptr)))
{
//The following codes may longjmp while init_io error.
::png_init_io(png_ptr, fp);
//8-byte of sig has been read, tell the libpng there are some bytes missing from start of file
::png_set_sig_bytes(png_ptr, 8);
_m_read_png(png_ptr, info_ptr);
is_opened = true;
}
}
::png_destroy_read_struct(&png_ptr, &info_ptr, nullptr);
}
}
::fclose(fp);
return is_opened;
}
class png_reader
{
public:
png_reader(const void* data, std::size_t bytes) noexcept
: data_ptr_(reinterpret_cast<const char*>(data)), bytes_(bytes)
{
}
static void PNGCAPI read(png_structp png_ptr, png_bytep buf, png_size_t bytes)
{
auto self = reinterpret_cast<png_reader*>(::png_get_io_ptr(png_ptr));
auto read_bytes = self->bytes_ < bytes ? self->bytes_ : bytes;
if (read_bytes)
std::memcpy(buf, self->data_ptr_, read_bytes);
self->bytes_ -= read_bytes;
self->data_ptr_ += read_bytes;
}
private:
const char* data_ptr_;
std::size_t bytes_;
};
bool open(const void* data, std::size_t bytes) override
{
if (bytes < 8 || 0 != ::png_sig_cmp(reinterpret_cast<png_const_bytep>(data), 0, 8))
return false;
auto png_ptr = ::png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
if (!png_ptr)
return false;
bool is_opened = false;
png_infop info_ptr = ::png_create_info_struct(png_ptr);
if (info_ptr)
{
png_reader reader{ data, bytes };
if (!setjmp(png_jmpbuf(png_ptr)))
{
::png_set_read_fn(png_ptr, &reader, &png_reader::read);
_m_read_png(png_ptr, info_ptr);
is_opened = true;
}
}
::png_destroy_read_struct(&png_ptr, &info_ptr, nullptr);
return is_opened;
}
};
}//end namespace detail
}//end namespace paint
}//end namespace nana
#endif