mirror of
https://github.com/holub/mame
synced 2025-06-03 11:26:56 +03:00
add minimal support for reading Adam7 interlaced PNG
(nw) This is the path of least resistance, and I plan to fix it up later, I just wanted to get it to actually work first. Decompression and unfiltering is fully supported, at least for all the pixel formats that previously worked. Expanding 1/2/4bpp to 8bpp should work properly, too. Bitmap mapping for Adam7 is only implemented in rendutil.cpp which is whate everything in MAME uses. The function in png.cpp (used by pngcmp) has not been updated. At some point I'll unify at least one of the functions in rendutil.cpp with the one in png.cpp and we can go from three functions that need to do the mapping down to two at the most.
This commit is contained in:
parent
ac6762603c
commit
a7b15fc928
@ -655,7 +655,7 @@ bool render_load_png(bitmap_argb32 &bitmap, emu_file &file, const char *dirname,
|
||||
png_free(&png);
|
||||
return false;
|
||||
}
|
||||
if (png.interlace_method != 0)
|
||||
if (png.interlace_method != 0 && png.interlace_method != 1)
|
||||
{
|
||||
osd_printf_error("%s: Interlace unsupported\n", filename);
|
||||
png_free(&png);
|
||||
@ -696,59 +696,86 @@ bool render_load_png(bitmap_argb32 &bitmap, emu_file &file, const char *dirname,
|
||||
|
||||
static bool copy_png_to_bitmap(bitmap_argb32 &bitmap, const png_info *png)
|
||||
{
|
||||
// FIXME: this function is basically copy/pasted from the PNG code in util, and should be unified with it
|
||||
u8 accumalpha = 0xff;
|
||||
u8 *src;
|
||||
int x, y;
|
||||
|
||||
/* handle 8bpp palettized case */
|
||||
if (png->color_type == 3)
|
||||
// colour format table
|
||||
static constexpr unsigned samples[] = { 1, 0, 3, 1, 2, 0, 4 };
|
||||
|
||||
// adam7 interlace tables
|
||||
static constexpr unsigned x_bias[7] = { 7, 3, 3, 1, 1, 0, 0 };
|
||||
static constexpr unsigned y_bias[7] = { 7, 7, 3, 3, 1, 1, 0 };
|
||||
static constexpr unsigned x_shift[7] = { 3, 3, 2, 2, 1, 1, 0 };
|
||||
static constexpr unsigned y_shift[7] = { 3, 3, 3, 2, 2, 1, 1 };
|
||||
|
||||
unsigned const pass_count(png->interlace_method ? 7 : 1);
|
||||
u32 pass_offset[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||
for (unsigned pass = 0; pass_count > pass; ++pass)
|
||||
{
|
||||
/* loop over width/height */
|
||||
src = png->image;
|
||||
for (y = 0; y < png->height; y++)
|
||||
for (x = 0; x < png->width; x++, src++)
|
||||
// calculate offset for next interlace pass
|
||||
u32 const width(png->interlace_method ? ((png->width + x_bias[pass]) >> x_shift[pass]) : png->width);
|
||||
u32 const height(png->interlace_method ? ((png->height + y_bias[pass]) >> y_shift[pass]) : png->height);
|
||||
u32 const rowbytes(((width * samples[png->color_type] * png->bit_depth) + 7) >> 3);
|
||||
pass_offset[pass + 1] = pass_offset[pass] + (height * (rowbytes + 1));
|
||||
u8 const *src(png->image + pass_offset[pass]);
|
||||
auto const x_trans = [offs = (1 << x_shift[pass]) - x_bias[pass] - 1, shift = x_shift[pass]] (u32 x) { return (x << shift) + offs; };
|
||||
auto const y_trans = [offs = (1 << y_shift[pass]) - y_bias[pass] - 1, shift = y_shift[pass]] (u32 y) { return (y << shift) + offs; };
|
||||
|
||||
if (png->color_type == 3)
|
||||
{
|
||||
// handle 8bpp palettized case
|
||||
for (u32 y = 0; height > y; ++y)
|
||||
{
|
||||
/* determine alpha and expand to 32bpp */
|
||||
u8 alpha = (*src < png->num_trans) ? png->trans[*src] : 0xff;
|
||||
accumalpha &= alpha;
|
||||
bitmap.pix32(y, x) = rgb_t(alpha, png->palette[*src * 3], png->palette[*src * 3 + 1], png->palette[*src * 3 + 2]);
|
||||
for (u32 x = 0; width > x; ++x, ++src)
|
||||
{
|
||||
// determine alpha and expand to 32bpp
|
||||
u8 alpha = (*src < png->num_trans) ? png->trans[*src] : 0xff;
|
||||
accumalpha &= alpha;
|
||||
rgb_t const pix(alpha, png->palette[*src * 3], png->palette[*src * 3 + 1], png->palette[*src * 3 + 2]);
|
||||
(png->interlace_method ? bitmap.pix32(y_trans(y), x_trans(x)) : bitmap.pix32(y, x)) = pix;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* handle 8bpp grayscale case */
|
||||
else if (png->color_type == 0)
|
||||
{
|
||||
/* loop over width/height */
|
||||
src = png->image;
|
||||
for (y = 0; y < png->height; y++)
|
||||
for (x = 0; x < png->width; x++, src++)
|
||||
bitmap.pix32(y, x) = rgb_t(0xff, *src, *src, *src);
|
||||
}
|
||||
|
||||
/* handle 32bpp non-alpha case */
|
||||
else if (png->color_type == 2)
|
||||
{
|
||||
/* loop over width/height */
|
||||
src = png->image;
|
||||
for (y = 0; y < png->height; y++)
|
||||
for (x = 0; x < png->width; x++, src += 3)
|
||||
bitmap.pix32(y, x) = rgb_t(0xff, src[0], src[1], src[2]);
|
||||
}
|
||||
|
||||
/* handle 32bpp alpha case */
|
||||
else
|
||||
{
|
||||
/* loop over width/height */
|
||||
src = png->image;
|
||||
for (y = 0; y < png->height; y++)
|
||||
for (x = 0; x < png->width; x++, src += 4)
|
||||
}
|
||||
else if (png->color_type == 0)
|
||||
{
|
||||
// handle 8bpp grayscale case
|
||||
for (u32 y = 0; height > y; ++y)
|
||||
{
|
||||
accumalpha &= src[3];
|
||||
bitmap.pix32(y, x) = rgb_t(src[3], src[0], src[1], src[2]);
|
||||
for (u32 x = 0; width > x; ++x, ++src)
|
||||
{
|
||||
rgb_t const pix(0xff, *src, *src, *src);
|
||||
(png->interlace_method ? bitmap.pix32(y_trans(y), x_trans(x)) : bitmap.pix32(y, x)) = pix;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (png->color_type == 2)
|
||||
{
|
||||
// handle 32bpp non-alpha case
|
||||
for (u32 y = 0; height > y; ++y)
|
||||
{
|
||||
for (u32 x = 0; width > x; ++x, src += 3)
|
||||
{
|
||||
rgb_t const pix(0xff, src[0], src[1], src[2]);
|
||||
(png->interlace_method ? bitmap.pix32(y_trans(y), x_trans(x)) : bitmap.pix32(y, x)) = pix;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// handle 32bpp alpha case
|
||||
for (u32 y = 0; height > y; ++y)
|
||||
{
|
||||
for (u32 x = 0; width > x; ++x, src += 4)
|
||||
{
|
||||
accumalpha &= src[3];
|
||||
rgb_t const pix(src[3], src[0], src[1], src[2]);
|
||||
(png->interlace_method ? bitmap.pix32(y_trans(y), x_trans(x)) : bitmap.pix32(y, x)) = pix;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* set the hasalpha flag */
|
||||
// set the hasalpha flag
|
||||
return (accumalpha != 0xff);
|
||||
}
|
||||
|
||||
@ -760,69 +787,92 @@ static bool copy_png_to_bitmap(bitmap_argb32 &bitmap, const png_info *png)
|
||||
|
||||
static bool copy_png_alpha_to_bitmap(bitmap_argb32 &bitmap, const png_info *png)
|
||||
{
|
||||
// FIXME: this function is basically copy/pasted from the PNG code in util, and should be unified with it
|
||||
u8 accumalpha = 0xff;
|
||||
u8 *src;
|
||||
int x, y;
|
||||
|
||||
/* handle 8bpp palettized case */
|
||||
if (png->color_type == 3)
|
||||
// colour format table
|
||||
static constexpr unsigned samples[] = { 1, 0, 3, 1, 2, 0, 4 };
|
||||
|
||||
// adam7 interlace tables
|
||||
static constexpr unsigned x_bias[7] = { 7, 3, 3, 1, 1, 0, 0 };
|
||||
static constexpr unsigned y_bias[7] = { 7, 7, 3, 3, 1, 1, 0 };
|
||||
static constexpr unsigned x_shift[7] = { 3, 3, 2, 2, 1, 1, 0 };
|
||||
static constexpr unsigned y_shift[7] = { 3, 3, 3, 2, 2, 1, 1 };
|
||||
|
||||
unsigned const pass_count(png->interlace_method ? 7 : 1);
|
||||
u32 pass_offset[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||
for (unsigned pass = 0; pass_count > pass; ++pass)
|
||||
{
|
||||
/* loop over width/height */
|
||||
src = png->image;
|
||||
for (y = 0; y < png->height; y++)
|
||||
for (x = 0; x < png->width; x++, src++)
|
||||
// calculate offset for next interlace pass
|
||||
u32 const width(png->interlace_method ? ((png->width + x_bias[pass]) >> x_shift[pass]) : png->width);
|
||||
u32 const height(png->interlace_method ? ((png->height + y_bias[pass]) >> y_shift[pass]) : png->height);
|
||||
u32 const rowbytes(((width * samples[png->color_type] * png->bit_depth) + 7) >> 3);
|
||||
pass_offset[pass + 1] = pass_offset[pass] + (height * (rowbytes + 1));
|
||||
u8 const *src(png->image + pass_offset[pass]);
|
||||
auto const x_trans = [offs = (1 << x_shift[pass]) - x_bias[pass] - 1, shift = x_shift[pass]] (u32 x) { return (x << shift) + offs; };
|
||||
auto const y_trans = [offs = (1 << y_shift[pass]) - y_bias[pass] - 1, shift = y_shift[pass]] (u32 y) { return (y << shift) + offs; };
|
||||
|
||||
if (png->color_type == 3)
|
||||
{
|
||||
// handle 8bpp palettized case
|
||||
for (u32 y = 0; height > y; ++y)
|
||||
{
|
||||
rgb_t pixel = bitmap.pix32(y, x);
|
||||
u8 alpha = rgb_t(png->palette[*src * 3], png->palette[*src * 3 + 1], png->palette[*src * 3 + 2]).brightness();
|
||||
accumalpha &= alpha;
|
||||
bitmap.pix32(y, x) = rgb_t(alpha, pixel.r(), pixel.g(), pixel.b());
|
||||
for (u32 x = 0; width > x; ++x, ++src)
|
||||
{
|
||||
bitmap_argb32::pixel_t &dest(png->interlace_method ? bitmap.pix32(y_trans(y), x_trans(x)) : bitmap.pix32(y, x));
|
||||
rgb_t const pixel(dest);
|
||||
u8 const alpha(rgb_t(png->palette[*src * 3], png->palette[*src * 3 + 1], png->palette[*src * 3 + 2]).brightness());
|
||||
accumalpha &= alpha;
|
||||
dest = rgb_t(alpha, pixel.r(), pixel.g(), pixel.b());
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (png->color_type == 0)
|
||||
{
|
||||
// handle 8bpp grayscale case
|
||||
for (u32 y = 0; height > y; ++y)
|
||||
{
|
||||
for (u32 x = 0; width > x; ++x, ++src)
|
||||
{
|
||||
bitmap_argb32::pixel_t &dest(png->interlace_method ? bitmap.pix32(y_trans(y), x_trans(x)) : bitmap.pix32(y, x));
|
||||
rgb_t const pixel(dest);
|
||||
accumalpha &= *src;
|
||||
dest = rgb_t(*src, pixel.r(), pixel.g(), pixel.b());
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (png->color_type == 2)
|
||||
{
|
||||
// handle 32bpp non-alpha case
|
||||
for (u32 y = 0; height > y; ++y)
|
||||
{
|
||||
for (u32 x = 0; width > x; ++x, src += 3)
|
||||
{
|
||||
bitmap_argb32::pixel_t &dest(png->interlace_method ? bitmap.pix32(y_trans(y), x_trans(x)) : bitmap.pix32(y, x));
|
||||
rgb_t const pixel(dest);
|
||||
u8 const alpha(rgb_t(src[0], src[1], src[2]).brightness());
|
||||
accumalpha &= alpha;
|
||||
dest = rgb_t(alpha, pixel.r(), pixel.g(), pixel.b());
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// handle 32bpp alpha case
|
||||
for (u32 y = 0; height > y; ++y)
|
||||
{
|
||||
for (u32 x = 0; width > x; ++x, src += 4)
|
||||
{
|
||||
bitmap_argb32::pixel_t &dest(png->interlace_method ? bitmap.pix32(y_trans(y), x_trans(x)) : bitmap.pix32(y, x));
|
||||
rgb_t const pixel(dest);
|
||||
u8 const alpha(rgb_t(src[0], src[1], src[2]).brightness());
|
||||
accumalpha &= alpha;
|
||||
dest = rgb_t(alpha, pixel.r(), pixel.g(), pixel.b());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* handle 8bpp grayscale case */
|
||||
else if (png->color_type == 0)
|
||||
{
|
||||
/* loop over width/height */
|
||||
src = png->image;
|
||||
for (y = 0; y < png->height; y++)
|
||||
for (x = 0; x < png->width; x++, src++)
|
||||
{
|
||||
rgb_t pixel = bitmap.pix32(y, x);
|
||||
accumalpha &= *src;
|
||||
bitmap.pix32(y, x) = rgb_t(*src, pixel.r(), pixel.g(), pixel.b());
|
||||
}
|
||||
}
|
||||
|
||||
/* handle 32bpp non-alpha case */
|
||||
else if (png->color_type == 2)
|
||||
{
|
||||
/* loop over width/height */
|
||||
src = png->image;
|
||||
for (y = 0; y < png->height; y++)
|
||||
for (x = 0; x < png->width; x++, src += 3)
|
||||
{
|
||||
rgb_t pixel = bitmap.pix32(y, x);
|
||||
u8 alpha = rgb_t(src[0], src[1], src[2]).brightness();
|
||||
accumalpha &= alpha;
|
||||
bitmap.pix32(y, x) = rgb_t(alpha, pixel.r(), pixel.g(), pixel.b());
|
||||
}
|
||||
}
|
||||
|
||||
/* handle 32bpp alpha case */
|
||||
else
|
||||
{
|
||||
/* loop over width/height */
|
||||
src = png->image;
|
||||
for (y = 0; y < png->height; y++)
|
||||
for (x = 0; x < png->width; x++, src += 4)
|
||||
{
|
||||
rgb_t pixel = bitmap.pix32(y, x);
|
||||
u8 alpha = rgb_t(src[0], src[1], src[2]).brightness();
|
||||
accumalpha &= alpha;
|
||||
bitmap.pix32(y, x) = rgb_t(alpha, pixel.r(), pixel.g(), pixel.b());
|
||||
}
|
||||
}
|
||||
|
||||
/* set the hasalpha flag */
|
||||
// set the hasalpha flag
|
||||
return (accumalpha != 0xff);
|
||||
}
|
||||
|
@ -2059,7 +2059,7 @@ void menu_select_launch::arts_render(float origx1, float origy1, float origx2, f
|
||||
game_driver const *driver;
|
||||
get_selection(software, driver);
|
||||
|
||||
if (software && ((software->startempty != 1) || !driver))
|
||||
if (software && (!software->startempty || !driver))
|
||||
{
|
||||
m_cache->set_snapx_driver(nullptr);
|
||||
|
||||
|
@ -8,15 +8,20 @@
|
||||
|
||||
***************************************************************************/
|
||||
|
||||
#include "png.h"
|
||||
|
||||
#include <zlib.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <new>
|
||||
#include <utility>
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <zlib.h>
|
||||
#include "png.h"
|
||||
|
||||
#include <new>
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
TYPE DEFINITIONS
|
||||
@ -24,19 +29,10 @@
|
||||
|
||||
struct image_data_chunk
|
||||
{
|
||||
image_data_chunk * next;
|
||||
int length;
|
||||
uint8_t * data;
|
||||
};
|
||||
image_data_chunk(std::uint32_t l, std::unique_ptr<std::uint8_t []> &&d) : length(l), data(std::move(d)) { }
|
||||
|
||||
|
||||
struct png_private
|
||||
{
|
||||
png_info * pnginfo;
|
||||
image_data_chunk * idata;
|
||||
image_data_chunk ** idata_next;
|
||||
uint8_t bpp;
|
||||
uint32_t rowbytes;
|
||||
std::uint32_t length = 0;
|
||||
std::unique_ptr<std::uint8_t []> data;
|
||||
};
|
||||
|
||||
|
||||
@ -91,15 +87,9 @@ static inline void put_32bit(uint8_t *v, uint32_t data)
|
||||
}
|
||||
|
||||
|
||||
static inline int compute_bpp(const png_info *pnginfo)
|
||||
static inline int compute_rowbytes(const png_info &pnginfo)
|
||||
{
|
||||
return samples[pnginfo->color_type] * pnginfo->bit_depth / 8;
|
||||
}
|
||||
|
||||
|
||||
static inline int compute_rowbytes(const png_info *pnginfo)
|
||||
{
|
||||
return (pnginfo->width * samples[pnginfo->color_type] * pnginfo->bit_depth + 7) / 8;
|
||||
return (pnginfo.width * samples[pnginfo.color_type] * pnginfo.bit_depth + 7) / 8;
|
||||
}
|
||||
|
||||
|
||||
@ -113,247 +103,374 @@ static inline int compute_rowbytes(const png_info *pnginfo)
|
||||
pnginfo structure
|
||||
-------------------------------------------------*/
|
||||
|
||||
void png_free(png_info *pnginfo)
|
||||
static void png_free(png_info &pnginfo)
|
||||
{
|
||||
while (pnginfo->textlist != nullptr)
|
||||
while (pnginfo.textlist)
|
||||
{
|
||||
png_text *temp = pnginfo->textlist;
|
||||
pnginfo->textlist = temp->next;
|
||||
if (temp->keyword != nullptr)
|
||||
free((void *)temp->keyword);
|
||||
png_text *const temp = pnginfo.textlist;
|
||||
pnginfo.textlist = temp->next;
|
||||
if (temp->keyword)
|
||||
delete[] (std::uint8_t *)temp->keyword;
|
||||
free(temp);
|
||||
}
|
||||
|
||||
if (pnginfo->palette != nullptr)
|
||||
free(pnginfo->palette);
|
||||
pnginfo->palette = nullptr;
|
||||
if (pnginfo.palette)
|
||||
delete[] pnginfo.palette;
|
||||
pnginfo.palette = nullptr;
|
||||
|
||||
if (pnginfo->trans != nullptr)
|
||||
free(pnginfo->trans);
|
||||
pnginfo->trans = nullptr;
|
||||
if (pnginfo.trans)
|
||||
delete[] pnginfo.trans;
|
||||
pnginfo.trans = nullptr;
|
||||
|
||||
if (pnginfo->image != nullptr)
|
||||
free(pnginfo->image);
|
||||
pnginfo->image = nullptr;
|
||||
if (pnginfo.image)
|
||||
delete[] pnginfo.image;
|
||||
pnginfo.image = nullptr;
|
||||
}
|
||||
|
||||
void png_free(png_info *pnginfo) { png_free(*pnginfo); } // TODO: make external interface use reference
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
PNG READING FUNCTIONS
|
||||
***************************************************************************/
|
||||
|
||||
/*-------------------------------------------------
|
||||
verify_header - verify the PNG
|
||||
header at the current file location
|
||||
-------------------------------------------------*/
|
||||
namespace {
|
||||
|
||||
static png_error verify_header(util::core_file &fp)
|
||||
class png_private
|
||||
{
|
||||
uint8_t signature[8];
|
||||
|
||||
/* read 8 bytes */
|
||||
if (fp.read(signature, 8) != 8)
|
||||
return PNGERR_FILE_TRUNCATED;
|
||||
|
||||
/* return an error if we don't match */
|
||||
if (memcmp(signature, PNG_Signature, 8) != 0)
|
||||
return PNGERR_BAD_SIGNATURE;
|
||||
|
||||
return PNGERR_NONE;
|
||||
}
|
||||
|
||||
|
||||
/*-------------------------------------------------
|
||||
read_chunk - read the next PNG chunk
|
||||
-------------------------------------------------*/
|
||||
|
||||
static png_error read_chunk(util::core_file &fp, uint8_t **data, uint32_t *type, uint32_t *length)
|
||||
{
|
||||
uint32_t crc, chunk_crc;
|
||||
uint8_t tempbuff[4];
|
||||
|
||||
/* fetch the length of this chunk */
|
||||
if (fp.read(tempbuff, 4) != 4)
|
||||
return PNGERR_FILE_TRUNCATED;
|
||||
*length = fetch_32bit(tempbuff);
|
||||
|
||||
/* fetch the type of this chunk */
|
||||
if (fp.read(tempbuff, 4) != 4)
|
||||
return PNGERR_FILE_TRUNCATED;
|
||||
*type = fetch_32bit(tempbuff);
|
||||
|
||||
/* stop when we hit an IEND chunk */
|
||||
if (*type == PNG_CN_IEND)
|
||||
return PNGERR_NONE;
|
||||
|
||||
/* start the CRC with the chunk type (but not the length) */
|
||||
crc = crc32(0, tempbuff, 4);
|
||||
|
||||
/* read the chunk itself into an allocated memory buffer */
|
||||
*data = nullptr;
|
||||
if (*length != 0)
|
||||
public:
|
||||
png_private(png_info &info) : pnginfo(info)
|
||||
{
|
||||
/* allocate memory for this chunk */
|
||||
*data = (uint8_t *)malloc(*length);
|
||||
if (*data == nullptr)
|
||||
return PNGERR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
/* read the data from the file */
|
||||
if (fp.read(*data, *length) != *length)
|
||||
png_error expand_buffer_8bit()
|
||||
{
|
||||
/* nothing to do if we're at 8 or greater already */
|
||||
if (pnginfo.bit_depth >= 8)
|
||||
return PNGERR_NONE;
|
||||
|
||||
/* calculate the offset for each pass of the interlace on the input and output */
|
||||
unsigned const pass_count(get_pass_count());
|
||||
std::uint32_t inp_offset[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||
std::uint32_t outp_offset[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||
for (unsigned pass = 0; pass_count > pass; ++pass)
|
||||
{
|
||||
free(*data);
|
||||
*data = nullptr;
|
||||
return PNGERR_FILE_TRUNCATED;
|
||||
inp_offset[pass + 1] = inp_offset[pass] + get_pass_bytes(pass);
|
||||
outp_offset[pass + 1] = outp_offset[pass] + get_pass_bytes(pass, 8);
|
||||
}
|
||||
|
||||
/* update the CRC */
|
||||
crc = crc32(crc, *data, *length);
|
||||
/* allocate a new buffer at 8-bit */
|
||||
std::unique_ptr<std::uint8_t []> outbuf;
|
||||
try { outbuf.reset(new std::uint8_t [outp_offset[pass_count]]); }
|
||||
catch (std::bad_alloc const &) { return PNGERR_OUT_OF_MEMORY; }
|
||||
|
||||
for (unsigned pass = 0; pass_count > pass; ++pass)
|
||||
{
|
||||
std::pair<std::uint32_t, std::uint32_t> const dimensions(get_pass_dimensions(pass));
|
||||
std::uint8_t const *inp(&pnginfo.image[inp_offset[pass]]);
|
||||
std::uint8_t *outp(&outbuf[outp_offset[pass]]);
|
||||
|
||||
for (std::uint32_t y = 0; dimensions.second > y; ++y)
|
||||
{
|
||||
for (std::uint32_t i = 0; (dimensions.first / (8 / pnginfo.bit_depth)); ++i, ++inp)
|
||||
{
|
||||
for (std::int8_t j = (8 / pnginfo.bit_depth) - 1; 0 <= j; --j)
|
||||
*outp++ = (*inp >> (j * pnginfo.bit_depth)) & (0xffU >> (8 - pnginfo.bit_depth));
|
||||
}
|
||||
if (pnginfo.width % (8 / pnginfo.bit_depth))
|
||||
{
|
||||
for (std::int8_t j = (pnginfo.width % (8 / pnginfo.bit_depth)) - 1; 0 <= j; --j)
|
||||
*outp++ = (*inp >> (j * pnginfo.bit_depth)) & (0xffU >> (8 - pnginfo.bit_depth));
|
||||
inp++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delete[] pnginfo.image;
|
||||
pnginfo.image = outbuf.release();
|
||||
pnginfo.bit_depth = 8;
|
||||
return PNGERR_NONE;
|
||||
}
|
||||
|
||||
/* read the CRC */
|
||||
if (fp.read(tempbuff, 4) != 4)
|
||||
png_error read_file(util::core_file &fp)
|
||||
{
|
||||
free(*data);
|
||||
*data = nullptr;
|
||||
return PNGERR_FILE_TRUNCATED;
|
||||
/* initialize the data structures */
|
||||
png_error error = PNGERR_NONE;
|
||||
std::memset(&pnginfo, 0, sizeof(pnginfo));
|
||||
|
||||
/* verify the signature at the start of the file */
|
||||
error = verify_header(fp);
|
||||
if (error != PNGERR_NONE)
|
||||
goto handle_error;
|
||||
|
||||
/* loop until we hit an IEND chunk */
|
||||
for ( ; ; )
|
||||
{
|
||||
/* read a chunk */
|
||||
std::unique_ptr<std::uint8_t []> chunk_data;
|
||||
std::uint32_t chunk_type, chunk_length;
|
||||
error = read_chunk(fp, chunk_data, chunk_type, chunk_length);
|
||||
if (error != PNGERR_NONE)
|
||||
goto handle_error;
|
||||
|
||||
/* stop when we hit an IEND chunk */
|
||||
if (chunk_type == PNG_CN_IEND)
|
||||
break;
|
||||
|
||||
/* process the chunk */
|
||||
error = process_chunk(std::move(chunk_data), chunk_type, chunk_length);
|
||||
if (error != PNGERR_NONE)
|
||||
goto handle_error;
|
||||
}
|
||||
|
||||
/* finish processing the image */
|
||||
error = process();
|
||||
|
||||
handle_error:
|
||||
/* no need for the intermediate data any more */
|
||||
idata.clear();
|
||||
|
||||
/* if we have an error, free all the other data as well */
|
||||
if (error != PNGERR_NONE)
|
||||
{
|
||||
png_free(pnginfo);
|
||||
memset(&pnginfo, 0, sizeof(pnginfo));
|
||||
}
|
||||
return error;
|
||||
}
|
||||
chunk_crc = fetch_32bit(tempbuff);
|
||||
|
||||
/* validate the CRC */
|
||||
if (crc != chunk_crc)
|
||||
png_info & pnginfo;
|
||||
|
||||
private:
|
||||
png_error process()
|
||||
{
|
||||
free(*data);
|
||||
*data = nullptr;
|
||||
return PNGERR_FILE_CORRUPT;
|
||||
png_error error = PNGERR_NONE;
|
||||
|
||||
/* calculate the offset for each pass of the interlace */
|
||||
unsigned const pass_count(get_pass_count());
|
||||
std::uint32_t pass_offset[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||
for (unsigned pass = 0; pass_count > pass; ++pass)
|
||||
pass_offset[pass + 1] = pass_offset[pass] + get_pass_bytes(pass);
|
||||
|
||||
/* allocate memory for the filtered image */
|
||||
try { pnginfo.image = new std::uint8_t [pass_offset[pass_count]]; }
|
||||
catch (std::bad_alloc const &) { return PNGERR_OUT_OF_MEMORY; }
|
||||
|
||||
/* decompress image data */
|
||||
error = decompress(pass_offset[pass_count]);
|
||||
for (unsigned pass = 0; (pass_count > pass) && (PNGERR_NONE == error); ++pass)
|
||||
{
|
||||
/* compute some basic parameters */
|
||||
std::pair<std::uint32_t, std::uint32_t> const dimensions(get_pass_dimensions(pass));
|
||||
std::uint32_t const bpp(get_bytes_per_pixel());
|
||||
std::uint32_t const rowbytes(get_row_bytes(dimensions.first));
|
||||
|
||||
/* we de-filter in place */
|
||||
uint8_t *dst = pnginfo.image + pass_offset[pass];
|
||||
uint8_t const *src = dst;
|
||||
|
||||
/* iterate over rows */
|
||||
for (std::uint32_t y = 0; (dimensions.second > y) && (PNGERR_NONE == error); ++y)
|
||||
{
|
||||
/* first byte of each row is the filter type */
|
||||
uint8_t const filter(*src++);
|
||||
error = unfilter_row(filter, src, dst, y ? &dst[-rowbytes] : nullptr, bpp, rowbytes);
|
||||
src += rowbytes;
|
||||
dst += rowbytes;
|
||||
}
|
||||
}
|
||||
|
||||
/* if we errored, free the image data */
|
||||
if (error != PNGERR_NONE)
|
||||
{
|
||||
delete[] pnginfo.image;
|
||||
pnginfo.image = nullptr;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
return PNGERR_NONE;
|
||||
}
|
||||
|
||||
|
||||
/*-------------------------------------------------
|
||||
process_chunk - process a PNG chunk
|
||||
-------------------------------------------------*/
|
||||
|
||||
static png_error process_chunk(png_private *png, uint8_t *data, uint32_t type, uint32_t length, bool *keepmem)
|
||||
{
|
||||
/* default to not keeping memory */
|
||||
*keepmem = false;
|
||||
|
||||
/* switch off of the type */
|
||||
switch (type)
|
||||
png_error decompress(std::uint32_t expected)
|
||||
{
|
||||
/* only deflate is permitted */
|
||||
if (pnginfo.compression_method != 0)
|
||||
return PNGERR_DECOMPRESS_ERROR;
|
||||
|
||||
/* allocate zlib stream */
|
||||
z_stream stream;
|
||||
int zerr;
|
||||
std::memset(&stream, 0, sizeof(stream));
|
||||
stream.zalloc = Z_NULL;
|
||||
stream.zfree = Z_NULL;
|
||||
stream.opaque = Z_NULL;
|
||||
stream.avail_in = 0;
|
||||
stream.next_in = Z_NULL;
|
||||
zerr = inflateInit(&stream);
|
||||
if (Z_OK != zerr)
|
||||
return PNGERR_DECOMPRESS_ERROR;
|
||||
|
||||
/* decompress IDAT blocks */
|
||||
stream.next_out = pnginfo.image;
|
||||
stream.avail_out = expected;
|
||||
stream.avail_in = 0;
|
||||
std::list<image_data_chunk>::const_iterator it = idata.begin();
|
||||
while ((idata.end() != it) && ((Z_OK == zerr) || (Z_BUF_ERROR == zerr)) && !stream.avail_in)
|
||||
{
|
||||
stream.avail_in = it->length;
|
||||
stream.next_in = it->data.get();
|
||||
do
|
||||
{
|
||||
zerr = inflate(&stream, Z_NO_FLUSH);
|
||||
}
|
||||
while (stream.avail_in && (Z_OK == zerr));
|
||||
if (!stream.avail_in)
|
||||
++it;
|
||||
}
|
||||
|
||||
/* it's all good if we got end-of-stream or we have with no data remaining */
|
||||
if ((Z_OK == inflateEnd(&stream)) && ((Z_STREAM_END == zerr) || ((Z_OK == zerr) && (idata.end() == it) && !stream.avail_in)))
|
||||
return PNGERR_NONE;
|
||||
else
|
||||
return PNGERR_DECOMPRESS_ERROR;
|
||||
}
|
||||
|
||||
png_error process_chunk(std::unique_ptr<std::uint8_t []> &&data, uint32_t type, uint32_t length)
|
||||
{
|
||||
/* switch off of the type */
|
||||
switch (type)
|
||||
{
|
||||
/* image header */
|
||||
case PNG_CN_IHDR:
|
||||
png->pnginfo->width = fetch_32bit(data);
|
||||
png->pnginfo->height = fetch_32bit(data + 4);
|
||||
png->pnginfo->bit_depth = fetch_8bit(data + 8);
|
||||
png->pnginfo->color_type = fetch_8bit(data + 9);
|
||||
png->pnginfo->compression_method = fetch_8bit(data + 10);
|
||||
png->pnginfo->filter_method = fetch_8bit(data + 11);
|
||||
png->pnginfo->interlace_method = fetch_8bit(data + 12);
|
||||
pnginfo.width = fetch_32bit(&data[0]);
|
||||
pnginfo.height = fetch_32bit(&data[4]);
|
||||
pnginfo.bit_depth = fetch_8bit(&data[8]);
|
||||
pnginfo.color_type = fetch_8bit(&data[9]);
|
||||
pnginfo.compression_method = fetch_8bit(&data[10]);
|
||||
pnginfo.filter_method = fetch_8bit(&data[11]);
|
||||
pnginfo.interlace_method = fetch_8bit(&data[12]);
|
||||
break;
|
||||
|
||||
/* palette */
|
||||
case PNG_CN_PLTE:
|
||||
png->pnginfo->num_palette = length / 3;
|
||||
png->pnginfo->palette = data;
|
||||
*keepmem = true;
|
||||
pnginfo.num_palette = length / 3;
|
||||
pnginfo.palette = data.release();
|
||||
break;
|
||||
|
||||
/* transparency information */
|
||||
case PNG_CN_tRNS:
|
||||
png->pnginfo->num_trans = length;
|
||||
png->pnginfo->trans = data;
|
||||
*keepmem = true;
|
||||
pnginfo.num_trans = length;
|
||||
pnginfo.trans = data.release();
|
||||
break;
|
||||
|
||||
/* image data */
|
||||
case PNG_CN_IDAT:
|
||||
|
||||
/* allocate a new image data descriptor */
|
||||
*png->idata_next = (image_data_chunk *)malloc(sizeof(**png->idata_next));
|
||||
if (*png->idata_next == nullptr)
|
||||
return PNGERR_OUT_OF_MEMORY;
|
||||
/* allocate a new image data descriptor and add it to the tail of the list */
|
||||
try { idata.emplace_back(length, std::move(data)); }
|
||||
catch (std::bad_alloc const &) { return PNGERR_OUT_OF_MEMORY; }
|
||||
|
||||
/* add it to the tail of the list */
|
||||
(*png->idata_next)->next = nullptr;
|
||||
(*png->idata_next)->length = length;
|
||||
(*png->idata_next)->data = data;
|
||||
png->idata_next = &(*png->idata_next)->next;
|
||||
*keepmem = true;
|
||||
break;
|
||||
|
||||
/* gamma */
|
||||
case PNG_CN_gAMA:
|
||||
png->pnginfo->source_gamma = fetch_32bit(data) / 100000.0;
|
||||
pnginfo.source_gamma = fetch_32bit(data.get()) / 100000.0;
|
||||
break;
|
||||
|
||||
/* physical information */
|
||||
case PNG_CN_pHYs:
|
||||
png->pnginfo->xres = fetch_32bit(data);
|
||||
png->pnginfo->yres = fetch_32bit(data + 4);
|
||||
png->pnginfo->resolution_unit = fetch_8bit(data + 8);
|
||||
pnginfo.xres = fetch_32bit(&data[0]);
|
||||
pnginfo.yres = fetch_32bit(&data[4]);
|
||||
pnginfo.resolution_unit = fetch_8bit(&data[8]);
|
||||
break;
|
||||
|
||||
/* text */
|
||||
case PNG_CN_tEXt:
|
||||
{
|
||||
png_text *text, *pt, *ct;
|
||||
{
|
||||
/* allocate a new text item */
|
||||
png_text *const text = (png_text *)malloc(sizeof(*text));
|
||||
if (!text)
|
||||
return PNGERR_OUT_OF_MEMORY;
|
||||
|
||||
/* allocate a new text item */
|
||||
text = (png_text *)malloc(sizeof(*text));
|
||||
if (text == nullptr)
|
||||
return PNGERR_OUT_OF_MEMORY;
|
||||
/* set the elements */
|
||||
text->keyword = (char *)data.release();
|
||||
text->text = text->keyword + strlen(text->keyword) + 1;
|
||||
text->next = nullptr;
|
||||
|
||||
/* set the elements */
|
||||
text->keyword = (char *)data;
|
||||
text->text = text->keyword + strlen(text->keyword) + 1;
|
||||
text->next = nullptr;
|
||||
/* add to the end of the list */
|
||||
png_text *pt, *ct;
|
||||
for (pt = nullptr, ct = pnginfo.textlist; ct != nullptr; pt = ct, ct = ct->next) { }
|
||||
if (pt == nullptr)
|
||||
pnginfo.textlist = text;
|
||||
else
|
||||
pt->next = text;
|
||||
|
||||
/* add to the end of the list */
|
||||
for (pt = nullptr, ct = png->pnginfo->textlist; ct != nullptr; pt = ct, ct = ct->next) { }
|
||||
if (pt == nullptr)
|
||||
png->pnginfo->textlist = text;
|
||||
else
|
||||
pt->next = text;
|
||||
|
||||
*keepmem = true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/* anything else */
|
||||
default:
|
||||
if ((type & 0x20000000) == 0)
|
||||
return PNGERR_UNKNOWN_CHUNK;
|
||||
break;
|
||||
}
|
||||
return PNGERR_NONE;
|
||||
}
|
||||
return PNGERR_NONE;
|
||||
}
|
||||
|
||||
|
||||
/*-------------------------------------------------
|
||||
unfilter_row - unfilter a single row of pixels
|
||||
-------------------------------------------------*/
|
||||
|
||||
static png_error unfilter_row(int type, uint8_t *src, uint8_t *dst, uint8_t *dstprev, int bpp, int rowbytes)
|
||||
{
|
||||
int x;
|
||||
|
||||
/* switch off of it */
|
||||
switch (type)
|
||||
unsigned get_pass_count() const
|
||||
{
|
||||
return (1 == pnginfo.interlace_method) ? 7 : 1;
|
||||
}
|
||||
|
||||
std::pair<uint32_t, uint32_t> get_pass_dimensions(unsigned pass) const
|
||||
{
|
||||
static constexpr unsigned x_bias[7] = { 7, 3, 3, 1, 1, 0, 0 };
|
||||
static constexpr unsigned y_bias[7] = { 7, 7, 3, 3, 1, 1, 0 };
|
||||
static constexpr unsigned x_shift[7] = { 3, 3, 2, 2, 1, 1, 0 };
|
||||
static constexpr unsigned y_shift[7] = { 3, 3, 3, 2, 2, 1, 1 };
|
||||
if (0 == pnginfo.interlace_method)
|
||||
return std::make_pair(pnginfo.width, pnginfo.height);
|
||||
else
|
||||
return std::make_pair((pnginfo.width + x_bias[pass]) >> x_shift[pass], (pnginfo.height + y_bias[pass]) >> y_shift[pass]);
|
||||
}
|
||||
|
||||
std::uint32_t get_pass_bytes(unsigned pass) const
|
||||
{
|
||||
return get_pass_bytes(pass, pnginfo.bit_depth);
|
||||
}
|
||||
|
||||
std::uint32_t get_pass_bytes(unsigned pass, uint8_t bit_depth) const
|
||||
{
|
||||
std::pair<std::uint32_t, std::uint32_t> const dimensions(get_pass_dimensions(pass));
|
||||
return (get_row_bytes(dimensions.first, bit_depth) + 1) * dimensions.second;
|
||||
}
|
||||
|
||||
std::uint32_t get_row_bytes(std::uint32_t width) const
|
||||
{
|
||||
return get_row_bytes(width, pnginfo.bit_depth);
|
||||
}
|
||||
|
||||
std::uint32_t get_row_bytes(std::uint32_t width, uint8_t bit_depth) const
|
||||
{
|
||||
return ((width * samples[pnginfo.color_type] * bit_depth) + 7) >> 3;
|
||||
}
|
||||
|
||||
std::uint32_t get_bytes_per_pixel() const
|
||||
{
|
||||
return (samples[pnginfo.color_type] * pnginfo.bit_depth) / 8; // FIXME: won't work for 4bpp greyscale etc.
|
||||
}
|
||||
|
||||
static png_error unfilter_row(int type, uint8_t const *src, uint8_t *dst, uint8_t const *dstprev, int bpp, int rowbytes)
|
||||
{
|
||||
/* switch off of it */
|
||||
switch (type)
|
||||
{
|
||||
/* no filter, just copy */
|
||||
case PNG_PF_None:
|
||||
for (x = 0; x < rowbytes; x++)
|
||||
for (int x = 0; x < rowbytes; x++)
|
||||
*dst++ = *src++;
|
||||
break;
|
||||
|
||||
/* SUB = previous pixel */
|
||||
case PNG_PF_Sub:
|
||||
for (x = 0; x < bpp; x++)
|
||||
for (int x = 0; x < bpp; x++)
|
||||
*dst++ = *src++;
|
||||
for (x = bpp; x < rowbytes; x++, dst++)
|
||||
for (int x = bpp; x < rowbytes; x++, dst++)
|
||||
*dst = *src++ + dst[-bpp];
|
||||
break;
|
||||
|
||||
@ -361,7 +478,7 @@ static png_error unfilter_row(int type, uint8_t *src, uint8_t *dst, uint8_t *dst
|
||||
case PNG_PF_Up:
|
||||
if (dstprev == nullptr)
|
||||
return unfilter_row(PNG_PF_None, src, dst, dstprev, bpp, rowbytes);
|
||||
for (x = 0; x < rowbytes; x++, dst++)
|
||||
for (int x = 0; x < rowbytes; x++, dst++)
|
||||
*dst = *src++ + *dstprev++;
|
||||
break;
|
||||
|
||||
@ -369,23 +486,23 @@ static png_error unfilter_row(int type, uint8_t *src, uint8_t *dst, uint8_t *dst
|
||||
case PNG_PF_Average:
|
||||
if (dstprev == nullptr)
|
||||
{
|
||||
for (x = 0; x < bpp; x++)
|
||||
for (int x = 0; x < bpp; x++)
|
||||
*dst++ = *src++;
|
||||
for (x = bpp; x < rowbytes; x++, dst++)
|
||||
for (int x = bpp; x < rowbytes; x++, dst++)
|
||||
*dst = *src++ + dst[-bpp] / 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (x = 0; x < bpp; x++, dst++)
|
||||
for (int x = 0; x < bpp; x++, dst++)
|
||||
*dst = *src++ + *dstprev++ / 2;
|
||||
for (x = bpp; x < rowbytes; x++, dst++)
|
||||
for (int x = bpp; x < rowbytes; x++, dst++)
|
||||
*dst = *src++ + (*dstprev++ + dst[-bpp]) / 2;
|
||||
}
|
||||
break;
|
||||
|
||||
/* PAETH = special filter */
|
||||
case PNG_PF_Paeth:
|
||||
for (x = 0; x < rowbytes; x++)
|
||||
for (int x = 0; x < rowbytes; x++)
|
||||
{
|
||||
int32_t pa = (x < bpp) ? 0 : dst[-bpp];
|
||||
int32_t pc = (x < bpp || dstprev == nullptr) ? 0 : dstprev[-bpp];
|
||||
@ -406,96 +523,89 @@ static png_error unfilter_row(int type, uint8_t *src, uint8_t *dst, uint8_t *dst
|
||||
/* unknown filter type */
|
||||
default:
|
||||
return PNGERR_UNKNOWN_FILTER;
|
||||
}
|
||||
|
||||
return PNGERR_NONE;
|
||||
}
|
||||
|
||||
|
||||
/*-------------------------------------------------
|
||||
process_image - post-process a loaded iamge
|
||||
-------------------------------------------------*/
|
||||
|
||||
static png_error process_image(png_private *png)
|
||||
{
|
||||
int rowbytes, bpp, imagesize;
|
||||
png_error error = PNGERR_NONE;
|
||||
image_data_chunk *idat;
|
||||
uint8_t *src, *dst;
|
||||
z_stream stream;
|
||||
int zerr, y;
|
||||
|
||||
/* compute some basic parameters */
|
||||
bpp = compute_bpp(png->pnginfo);
|
||||
rowbytes = compute_rowbytes(png->pnginfo);
|
||||
imagesize = png->pnginfo->height * (rowbytes + 1);
|
||||
|
||||
/* allocate memory for the filtered image */
|
||||
png->pnginfo->image = (uint8_t *)malloc(imagesize);
|
||||
if (png->pnginfo->image == nullptr)
|
||||
return PNGERR_OUT_OF_MEMORY;
|
||||
|
||||
/* initialize the stream */
|
||||
memset(&stream, 0, sizeof(stream));
|
||||
stream.next_out = png->pnginfo->image;
|
||||
stream.avail_out = imagesize;
|
||||
zerr = inflateInit(&stream);
|
||||
if (zerr != Z_OK)
|
||||
{
|
||||
error = PNGERR_DECOMPRESS_ERROR;
|
||||
goto handle_error;
|
||||
}
|
||||
|
||||
/* loop over IDAT and decompress each as part of a larger stream */
|
||||
for (idat = png->idata; idat != nullptr; idat = idat->next)
|
||||
{
|
||||
/* decompress this chunk */
|
||||
stream.next_in = idat->data;
|
||||
stream.avail_in = idat->length;
|
||||
zerr = inflate(&stream, Z_NO_FLUSH);
|
||||
|
||||
/* stop at the end of the stream */
|
||||
if (zerr == Z_STREAM_END)
|
||||
break;
|
||||
|
||||
/* other errors are fatal */
|
||||
if (zerr != Z_OK)
|
||||
{
|
||||
error = PNGERR_DECOMPRESS_ERROR;
|
||||
goto handle_error;
|
||||
}
|
||||
|
||||
return PNGERR_NONE;
|
||||
}
|
||||
|
||||
/* clean up */
|
||||
zerr = inflateEnd(&stream);
|
||||
if (zerr != Z_OK)
|
||||
static png_error verify_header(util::core_file &fp)
|
||||
{
|
||||
error = PNGERR_DECOMPRESS_ERROR;
|
||||
goto handle_error;
|
||||
uint8_t signature[8];
|
||||
|
||||
/* read 8 bytes */
|
||||
if (fp.read(signature, 8) != 8)
|
||||
return PNGERR_FILE_TRUNCATED;
|
||||
|
||||
/* return an error if we don't match */
|
||||
if (memcmp(signature, PNG_Signature, 8) != 0)
|
||||
return PNGERR_BAD_SIGNATURE;
|
||||
|
||||
return PNGERR_NONE;
|
||||
}
|
||||
|
||||
/* we de-filter in place */
|
||||
src = dst = png->pnginfo->image;
|
||||
|
||||
/* iterate over rows */
|
||||
for (y = 0; y < png->pnginfo->height && error == PNGERR_NONE; y++)
|
||||
static png_error read_chunk(util::core_file &fp, std::unique_ptr<std::uint8_t []> &data, std::uint32_t &type, std::uint32_t &length)
|
||||
{
|
||||
/* first byte of each row is the filter type */
|
||||
int filter = *src++;
|
||||
error = unfilter_row(filter, src, dst, (y == 0) ? nullptr : &dst[-rowbytes], bpp, rowbytes);
|
||||
src += rowbytes;
|
||||
dst += rowbytes;
|
||||
std::uint8_t tempbuff[4];
|
||||
|
||||
/* fetch the length of this chunk */
|
||||
if (fp.read(tempbuff, 4) != 4)
|
||||
return PNGERR_FILE_TRUNCATED;
|
||||
length = fetch_32bit(tempbuff);
|
||||
|
||||
/* fetch the type of this chunk */
|
||||
if (fp.read(tempbuff, 4) != 4)
|
||||
return PNGERR_FILE_TRUNCATED;
|
||||
type = fetch_32bit(tempbuff);
|
||||
|
||||
/* stop when we hit an IEND chunk */
|
||||
if (type == PNG_CN_IEND)
|
||||
return PNGERR_NONE;
|
||||
|
||||
/* start the CRC with the chunk type (but not the length) */
|
||||
std::uint32_t crc = crc32(0, tempbuff, 4);
|
||||
|
||||
/* read the chunk itself into an allocated memory buffer */
|
||||
if (length)
|
||||
{
|
||||
/* allocate memory for this chunk */
|
||||
try { data.reset(new std::uint8_t [length]); }
|
||||
catch (std::bad_alloc const &) { return PNGERR_OUT_OF_MEMORY; }
|
||||
|
||||
/* read the data from the file */
|
||||
if (fp.read(data.get(), length) != length)
|
||||
{
|
||||
data.reset();
|
||||
return PNGERR_FILE_TRUNCATED;
|
||||
}
|
||||
|
||||
/* update the CRC */
|
||||
crc = crc32(crc, data.get(), length);
|
||||
}
|
||||
|
||||
/* read the CRC */
|
||||
if (fp.read(tempbuff, 4) != 4)
|
||||
{
|
||||
data.reset();
|
||||
return PNGERR_FILE_TRUNCATED;
|
||||
}
|
||||
std::uint32_t const chunk_crc = fetch_32bit(tempbuff);
|
||||
|
||||
/* validate the CRC */
|
||||
if (crc != chunk_crc)
|
||||
{
|
||||
data.reset();
|
||||
return PNGERR_FILE_CORRUPT;
|
||||
}
|
||||
|
||||
return PNGERR_NONE;
|
||||
}
|
||||
|
||||
handle_error:
|
||||
/* if we errored, free the image data */
|
||||
if (error != PNGERR_NONE)
|
||||
{
|
||||
free(png->pnginfo->image);
|
||||
png->pnginfo->image = nullptr;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
std::list<image_data_chunk> idata; // FIXME: this could really be a local
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
|
||||
|
||||
|
||||
/*-------------------------------------------------
|
||||
@ -504,73 +614,8 @@ handle_error:
|
||||
|
||||
png_error png_read_file(util::core_file &fp, png_info *pnginfo)
|
||||
{
|
||||
uint8_t *chunk_data = nullptr;
|
||||
png_private png;
|
||||
png_error error;
|
||||
|
||||
/* initialize the data structures */
|
||||
memset(&png, 0, sizeof(png));
|
||||
memset(pnginfo, 0, sizeof(*pnginfo));
|
||||
png.pnginfo = pnginfo;
|
||||
png.idata_next = &png.idata;
|
||||
|
||||
/* verify the signature at the start of the file */
|
||||
error = verify_header(fp);
|
||||
if (error != PNGERR_NONE)
|
||||
goto handle_error;
|
||||
|
||||
/* loop until we hit an IEND chunk */
|
||||
for ( ; ; )
|
||||
{
|
||||
uint32_t chunk_type, chunk_length;
|
||||
bool keepmem;
|
||||
|
||||
/* read a chunk */
|
||||
error = read_chunk(fp, &chunk_data, &chunk_type, &chunk_length);
|
||||
if (error != PNGERR_NONE)
|
||||
goto handle_error;
|
||||
|
||||
/* stop when we hit an IEND chunk */
|
||||
if (chunk_type == PNG_CN_IEND)
|
||||
break;
|
||||
|
||||
/* process the chunk */
|
||||
error = process_chunk(&png, chunk_data, chunk_type, chunk_length, &keepmem);
|
||||
if (error != PNGERR_NONE)
|
||||
goto handle_error;
|
||||
|
||||
/* free memory if we didn't want to keep it */
|
||||
if (!keepmem)
|
||||
free(chunk_data);
|
||||
chunk_data = nullptr;
|
||||
}
|
||||
|
||||
/* finish processing the image */
|
||||
error = process_image(&png);
|
||||
if (error != PNGERR_NONE)
|
||||
goto handle_error;
|
||||
|
||||
handle_error:
|
||||
|
||||
/* free all intermediate data */
|
||||
while (png.idata != nullptr)
|
||||
{
|
||||
image_data_chunk *next = png.idata->next;
|
||||
if (png.idata->data != nullptr)
|
||||
free(png.idata->data);
|
||||
free(png.idata);
|
||||
png.idata = next;
|
||||
}
|
||||
if (chunk_data != nullptr)
|
||||
free(chunk_data);
|
||||
|
||||
/* if we have an error, free all the other data as well */
|
||||
if (error != PNGERR_NONE)
|
||||
{
|
||||
png_free(pnginfo);
|
||||
memset(pnginfo, 0, sizeof(*pnginfo));
|
||||
}
|
||||
return error;
|
||||
png_private png(*pnginfo);
|
||||
return png.read_file(fp);
|
||||
}
|
||||
|
||||
|
||||
@ -656,40 +701,7 @@ png_error png_read_bitmap(util::core_file &fp, bitmap_argb32 &bitmap)
|
||||
|
||||
png_error png_expand_buffer_8bit(png_info *pnginfo)
|
||||
{
|
||||
int i,j, k;
|
||||
uint8_t *inp, *outp, *outbuf;
|
||||
|
||||
/* nothing to do if we're at 8 or greater already */
|
||||
if (pnginfo->bit_depth >= 8)
|
||||
return PNGERR_NONE;
|
||||
|
||||
/* allocate a new buffer at 8-bit */
|
||||
outbuf = (uint8_t *)malloc(pnginfo->width * pnginfo->height);
|
||||
if (outbuf == nullptr)
|
||||
return PNGERR_OUT_OF_MEMORY;
|
||||
|
||||
inp = pnginfo->image;
|
||||
outp = outbuf;
|
||||
|
||||
for (i = 0; i < pnginfo->height; i++)
|
||||
{
|
||||
for(j = 0; j < pnginfo->width / ( 8 / pnginfo->bit_depth); j++)
|
||||
{
|
||||
for (k = 8 / pnginfo->bit_depth-1; k >= 0; k--)
|
||||
*outp++ = (*inp >> k * pnginfo->bit_depth) & (0xff >> (8 - pnginfo->bit_depth));
|
||||
inp++;
|
||||
}
|
||||
if (pnginfo->width % (8 / pnginfo->bit_depth))
|
||||
{
|
||||
for (k = pnginfo->width % (8 / pnginfo->bit_depth)-1; k >= 0; k--)
|
||||
*outp++ = (*inp >> k * pnginfo->bit_depth) & (0xff >> (8 - pnginfo->bit_depth));
|
||||
inp++;
|
||||
}
|
||||
}
|
||||
free (pnginfo->image);
|
||||
pnginfo->image = outbuf;
|
||||
|
||||
return PNGERR_NONE;
|
||||
return png_private(*pnginfo).expand_buffer_8bit();
|
||||
}
|
||||
|
||||
|
||||
@ -1041,7 +1053,7 @@ static png_error write_png_stream(util::core_file &fp, png_info *pnginfo, const
|
||||
goto handle_error;
|
||||
|
||||
/* write a single IDAT chunk */
|
||||
error = write_deflated_chunk(fp, pnginfo->image, PNG_CN_IDAT, pnginfo->height * (compute_rowbytes(pnginfo) + 1));
|
||||
error = write_deflated_chunk(fp, pnginfo->image, PNG_CN_IDAT, pnginfo->height * (compute_rowbytes(*pnginfo) + 1));
|
||||
if (error != PNGERR_NONE)
|
||||
goto handle_error;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user