open png/jpg image with memory buffer
This commit is contained in:
@@ -25,6 +25,27 @@ namespace nana
|
||||
struct ::jpeg_error_mgr pub;
|
||||
std::jmp_buf setjmp_buf;
|
||||
};
|
||||
|
||||
void _m_read_jpg(jpeg_decompress_struct& jdstru)
|
||||
{
|
||||
::jpeg_read_header(&jdstru, true); //Reject a tables-only JPEG file as an error
|
||||
|
||||
::jpeg_start_decompress(&jdstru);
|
||||
|
||||
//JSAMPLEs per row in output buffer
|
||||
auto row_stride = jdstru.output_width * jdstru.output_components;
|
||||
|
||||
pixbuf_.open(jdstru.output_width, jdstru.output_height);
|
||||
|
||||
auto row_buf = jdstru.mem->alloc_sarray(reinterpret_cast<j_common_ptr>(&jdstru), JPOOL_IMAGE, row_stride, 1);
|
||||
|
||||
while (jdstru.output_scanline < jdstru.output_height)
|
||||
{
|
||||
::jpeg_read_scanlines(&jdstru, row_buf, 1);
|
||||
|
||||
pixbuf_.fill_row(jdstru.output_scanline - 1, reinterpret_cast<unsigned char*>(*row_buf), row_stride, jdstru.output_components * 8);
|
||||
}
|
||||
}
|
||||
public:
|
||||
bool open(const experimental::filesystem::path& jpeg_file) override
|
||||
{
|
||||
@@ -45,25 +66,10 @@ namespace nana
|
||||
|
||||
::jpeg_stdio_src(&jdstru, fp);
|
||||
|
||||
::jpeg_read_header(&jdstru, true); //Reject a tables-only JPEG file as an error
|
||||
|
||||
::jpeg_start_decompress(&jdstru);
|
||||
|
||||
//JSAMPLEs per row in output buffer
|
||||
auto row_stride = jdstru.output_width * jdstru.output_components;
|
||||
|
||||
pixbuf_.open(jdstru.output_width, jdstru.output_height);
|
||||
|
||||
auto row_buf = jdstru.mem->alloc_sarray(reinterpret_cast<j_common_ptr>(&jdstru), JPOOL_IMAGE, row_stride, 1);
|
||||
|
||||
while (jdstru.output_scanline < jdstru.output_height)
|
||||
{
|
||||
::jpeg_read_scanlines(&jdstru, row_buf, 1);
|
||||
|
||||
pixbuf_.fill_row(jdstru.output_scanline - 1, reinterpret_cast<unsigned char*>(*row_buf), row_stride, jdstru.output_components * 8);
|
||||
}
|
||||
_m_read_jpg(jdstru);
|
||||
|
||||
jpeg_finish_decompress(&jdstru);
|
||||
is_opened = true;
|
||||
}
|
||||
|
||||
::jpeg_destroy_decompress(&jdstru);
|
||||
@@ -73,8 +79,27 @@ namespace nana
|
||||
|
||||
bool open(const void* data, std::size_t bytes) override
|
||||
{
|
||||
throw std::logic_error("JPEG is not supported for raw data buffer");
|
||||
return false;
|
||||
bool is_opened = false;
|
||||
|
||||
struct ::jpeg_decompress_struct jdstru;
|
||||
error_mgr jerr;
|
||||
|
||||
jdstru.err = ::jpeg_std_error(&jerr.pub);
|
||||
jerr.pub.error_exit = _m_error_handler;
|
||||
|
||||
if (!setjmp(jerr.setjmp_buf))
|
||||
{
|
||||
::jpeg_create_decompress(&jdstru);
|
||||
|
||||
::jpeg_mem_src(&jdstru, const_cast<unsigned char*>(reinterpret_cast<const unsigned char*>(data)), bytes);
|
||||
_m_read_jpg(jdstru);
|
||||
|
||||
jpeg_finish_decompress(&jdstru);
|
||||
is_opened = true;
|
||||
}
|
||||
|
||||
::jpeg_destroy_decompress(&jdstru);
|
||||
return is_opened;
|
||||
}
|
||||
private:
|
||||
static void _m_error_handler(::j_common_ptr jdstru)
|
||||
|
||||
@@ -19,6 +19,107 @@ namespace nana
|
||||
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 experimental::filesystem::path& png_file) override
|
||||
{
|
||||
@@ -44,109 +145,17 @@ namespace nana
|
||||
{
|
||||
//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);
|
||||
::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);
|
||||
_m_read_png(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);
|
||||
|
||||
bool 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);
|
||||
::png_destroy_read_struct(&png_ptr, &info_ptr, nullptr);
|
||||
|
||||
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;
|
||||
is_opened = true;
|
||||
}
|
||||
}
|
||||
|
||||
::png_destroy_read_struct(&png_ptr, &info_ptr, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -154,10 +163,60 @@ namespace nana
|
||||
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
|
||||
{
|
||||
throw std::logic_error("PNG is not supported for raw data buffer");
|
||||
return false;
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user