mirror of
https://github.com/holub/mame
synced 2025-06-07 13:23:50 +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 */
|
||||
// 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)
|
||||
{
|
||||
// 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)
|
||||
{
|
||||
/* loop over width/height */
|
||||
src = png->image;
|
||||
for (y = 0; y < png->height; y++)
|
||||
for (x = 0; x < png->width; x++, src++)
|
||||
// handle 8bpp palettized case
|
||||
for (u32 y = 0; height > y; ++y)
|
||||
{
|
||||
/* determine alpha and expand to 32bpp */
|
||||
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;
|
||||
bitmap.pix32(y, x) = rgb_t(alpha, png->palette[*src * 3], png->palette[*src * 3 + 1], png->palette[*src * 3 + 2]);
|
||||
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 8bpp grayscale case
|
||||
for (u32 y = 0; height > y; ++y)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 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 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 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)
|
||||
// handle 32bpp alpha case
|
||||
for (u32 y = 0; height > y; ++y)
|
||||
{
|
||||
for (u32 x = 0; width > x; ++x, src += 4)
|
||||
{
|
||||
accumalpha &= src[3];
|
||||
bitmap.pix32(y, x) = rgb_t(src[3], src[0], src[1], src[2]);
|
||||
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 */
|
||||
// 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)
|
||||
{
|
||||
// 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)
|
||||
{
|
||||
/* loop over width/height */
|
||||
src = png->image;
|
||||
for (y = 0; y < png->height; y++)
|
||||
for (x = 0; x < png->width; x++, src++)
|
||||
// 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();
|
||||
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;
|
||||
bitmap.pix32(y, x) = rgb_t(alpha, pixel.r(), pixel.g(), pixel.b());
|
||||
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++)
|
||||
// handle 8bpp grayscale case
|
||||
for (u32 y = 0; height > y; ++y)
|
||||
{
|
||||
rgb_t pixel = bitmap.pix32(y, x);
|
||||
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;
|
||||
bitmap.pix32(y, x) = rgb_t(*src, pixel.r(), pixel.g(), pixel.b());
|
||||
dest = 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)
|
||||
// handle 32bpp non-alpha case
|
||||
for (u32 y = 0; height > y; ++y)
|
||||
{
|
||||
rgb_t pixel = bitmap.pix32(y, x);
|
||||
u8 alpha = rgb_t(src[0], src[1], src[2]).brightness();
|
||||
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;
|
||||
bitmap.pix32(y, x) = rgb_t(alpha, pixel.r(), pixel.g(), pixel.b());
|
||||
dest = 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)
|
||||
// handle 32bpp alpha case
|
||||
for (u32 y = 0; height > y; ++y)
|
||||
{
|
||||
rgb_t pixel = bitmap.pix32(y, x);
|
||||
u8 alpha = rgb_t(src[0], src[1], src[2]).brightness();
|
||||
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;
|
||||
bitmap.pix32(y, x) = rgb_t(alpha, pixel.r(), pixel.g(), pixel.b());
|
||||
dest = 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,212 +103,303 @@ 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];
|
||||
public:
|
||||
png_private(png_info &info) : pnginfo(info)
|
||||
{
|
||||
}
|
||||
|
||||
/* read 8 bytes */
|
||||
if (fp.read(signature, 8) != 8)
|
||||
return PNGERR_FILE_TRUNCATED;
|
||||
png_error expand_buffer_8bit()
|
||||
{
|
||||
/* nothing to do if we're at 8 or greater already */
|
||||
if (pnginfo.bit_depth >= 8)
|
||||
return PNGERR_NONE;
|
||||
|
||||
/* return an error if we don't match */
|
||||
if (memcmp(signature, PNG_Signature, 8) != 0)
|
||||
return PNGERR_BAD_SIGNATURE;
|
||||
/* 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)
|
||||
{
|
||||
inp_offset[pass + 1] = inp_offset[pass] + get_pass_bytes(pass);
|
||||
outp_offset[pass + 1] = outp_offset[pass] + get_pass_bytes(pass, 8);
|
||||
}
|
||||
|
||||
/* 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_chunk - read the next PNG chunk
|
||||
-------------------------------------------------*/
|
||||
|
||||
static png_error read_chunk(util::core_file &fp, uint8_t **data, uint32_t *type, uint32_t *length)
|
||||
png_error read_file(util::core_file &fp)
|
||||
{
|
||||
uint32_t crc, chunk_crc;
|
||||
uint8_t tempbuff[4];
|
||||
/* initialize the data structures */
|
||||
png_error error = PNGERR_NONE;
|
||||
std::memset(&pnginfo, 0, sizeof(pnginfo));
|
||||
|
||||
/* fetch the length of this chunk */
|
||||
if (fp.read(tempbuff, 4) != 4)
|
||||
return PNGERR_FILE_TRUNCATED;
|
||||
*length = fetch_32bit(tempbuff);
|
||||
/* verify the signature at the start of the file */
|
||||
error = verify_header(fp);
|
||||
if (error != PNGERR_NONE)
|
||||
goto handle_error;
|
||||
|
||||
/* fetch the type of this chunk */
|
||||
if (fp.read(tempbuff, 4) != 4)
|
||||
return PNGERR_FILE_TRUNCATED;
|
||||
*type = fetch_32bit(tempbuff);
|
||||
/* 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 (*type == PNG_CN_IEND)
|
||||
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;
|
||||
}
|
||||
|
||||
png_info & pnginfo;
|
||||
|
||||
private:
|
||||
png_error process()
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
/* 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)
|
||||
{
|
||||
/* 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)
|
||||
{
|
||||
free(*data);
|
||||
*data = nullptr;
|
||||
return PNGERR_FILE_TRUNCATED;
|
||||
else
|
||||
return PNGERR_DECOMPRESS_ERROR;
|
||||
}
|
||||
|
||||
/* update the CRC */
|
||||
crc = crc32(crc, *data, *length);
|
||||
}
|
||||
|
||||
/* read the CRC */
|
||||
if (fp.read(tempbuff, 4) != 4)
|
||||
png_error process_chunk(std::unique_ptr<std::uint8_t []> &&data, uint32_t type, uint32_t length)
|
||||
{
|
||||
free(*data);
|
||||
*data = nullptr;
|
||||
return PNGERR_FILE_TRUNCATED;
|
||||
}
|
||||
chunk_crc = fetch_32bit(tempbuff);
|
||||
|
||||
/* validate the CRC */
|
||||
if (crc != chunk_crc)
|
||||
{
|
||||
free(*data);
|
||||
*data = nullptr;
|
||||
return PNGERR_FILE_CORRUPT;
|
||||
}
|
||||
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)
|
||||
{
|
||||
/* 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 */
|
||||
text = (png_text *)malloc(sizeof(*text));
|
||||
if (text == nullptr)
|
||||
png_text *const text = (png_text *)malloc(sizeof(*text));
|
||||
if (!text)
|
||||
return PNGERR_OUT_OF_MEMORY;
|
||||
|
||||
/* set the elements */
|
||||
text->keyword = (char *)data;
|
||||
text->keyword = (char *)data.release();
|
||||
text->text = text->keyword + strlen(text->keyword) + 1;
|
||||
text->next = nullptr;
|
||||
|
||||
/* add to the end of the list */
|
||||
for (pt = nullptr, ct = png->pnginfo->textlist; ct != nullptr; pt = ct, ct = ct->next) { }
|
||||
png_text *pt, *ct;
|
||||
for (pt = nullptr, ct = pnginfo.textlist; ct != nullptr; pt = ct, ct = ct->next) { }
|
||||
if (pt == nullptr)
|
||||
png->pnginfo->textlist = text;
|
||||
pnginfo.textlist = text;
|
||||
else
|
||||
pt->next = text;
|
||||
|
||||
*keepmem = true;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -331,29 +412,65 @@ static png_error process_chunk(png_private *png, uint8_t *data, uint32_t type, u
|
||||
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)
|
||||
unsigned get_pass_count() const
|
||||
{
|
||||
int x;
|
||||
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];
|
||||
@ -411,92 +528,85 @@ static png_error unfilter_row(int type, uint8_t *src, uint8_t *dst, uint8_t *dst
|
||||
return PNGERR_NONE;
|
||||
}
|
||||
|
||||
|
||||
/*-------------------------------------------------
|
||||
process_image - post-process a loaded iamge
|
||||
-------------------------------------------------*/
|
||||
|
||||
static png_error process_image(png_private *png)
|
||||
static png_error verify_header(util::core_file &fp)
|
||||
{
|
||||
int rowbytes, bpp, imagesize;
|
||||
png_error error = PNGERR_NONE;
|
||||
image_data_chunk *idat;
|
||||
uint8_t *src, *dst;
|
||||
z_stream stream;
|
||||
int zerr, y;
|
||||
uint8_t signature[8];
|
||||
|
||||
/* compute some basic parameters */
|
||||
bpp = compute_bpp(png->pnginfo);
|
||||
rowbytes = compute_rowbytes(png->pnginfo);
|
||||
imagesize = png->pnginfo->height * (rowbytes + 1);
|
||||
/* read 8 bytes */
|
||||
if (fp.read(signature, 8) != 8)
|
||||
return PNGERR_FILE_TRUNCATED;
|
||||
|
||||
/* allocate memory for the filtered image */
|
||||
png->pnginfo->image = (uint8_t *)malloc(imagesize);
|
||||
if (png->pnginfo->image == nullptr)
|
||||
return PNGERR_OUT_OF_MEMORY;
|
||||
/* return an error if we don't match */
|
||||
if (memcmp(signature, PNG_Signature, 8) != 0)
|
||||
return PNGERR_BAD_SIGNATURE;
|
||||
|
||||
/* 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;
|
||||
return PNGERR_NONE;
|
||||
}
|
||||
|
||||
/* loop over IDAT and decompress each as part of a larger stream */
|
||||
for (idat = png->idata; idat != nullptr; idat = idat->next)
|
||||
static png_error read_chunk(util::core_file &fp, std::unique_ptr<std::uint8_t []> &data, std::uint32_t &type, std::uint32_t &length)
|
||||
{
|
||||
/* decompress this chunk */
|
||||
stream.next_in = idat->data;
|
||||
stream.avail_in = idat->length;
|
||||
zerr = inflate(&stream, Z_NO_FLUSH);
|
||||
std::uint8_t tempbuff[4];
|
||||
|
||||
/* stop at the end of the stream */
|
||||
if (zerr == Z_STREAM_END)
|
||||
break;
|
||||
/* fetch the length of this chunk */
|
||||
if (fp.read(tempbuff, 4) != 4)
|
||||
return PNGERR_FILE_TRUNCATED;
|
||||
length = fetch_32bit(tempbuff);
|
||||
|
||||
/* other errors are fatal */
|
||||
if (zerr != Z_OK)
|
||||
/* 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)
|
||||
{
|
||||
error = PNGERR_DECOMPRESS_ERROR;
|
||||
goto handle_error;
|
||||
}
|
||||
}
|
||||
/* allocate memory for this chunk */
|
||||
try { data.reset(new std::uint8_t [length]); }
|
||||
catch (std::bad_alloc const &) { return PNGERR_OUT_OF_MEMORY; }
|
||||
|
||||
/* clean up */
|
||||
zerr = inflateEnd(&stream);
|
||||
if (zerr != Z_OK)
|
||||
/* read the data from the file */
|
||||
if (fp.read(data.get(), length) != length)
|
||||
{
|
||||
error = PNGERR_DECOMPRESS_ERROR;
|
||||
goto handle_error;
|
||||
data.reset();
|
||||
return PNGERR_FILE_TRUNCATED;
|
||||
}
|
||||
|
||||
/* we de-filter in place */
|
||||
src = dst = png->pnginfo->image;
|
||||
/* update the CRC */
|
||||
crc = crc32(crc, data.get(), length);
|
||||
}
|
||||
|
||||
/* iterate over rows */
|
||||
for (y = 0; y < png->pnginfo->height && error == PNGERR_NONE; y++)
|
||||
/* read the CRC */
|
||||
if (fp.read(tempbuff, 4) != 4)
|
||||
{
|
||||
/* 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;
|
||||
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;
|
||||
}
|
||||
|
||||
handle_error:
|
||||
/* if we errored, free the image data */
|
||||
if (error != PNGERR_NONE)
|
||||
{
|
||||
free(png->pnginfo->image);
|
||||
png->pnginfo->image = nullptr;
|
||||
}
|
||||
return error;
|
||||
return PNGERR_NONE;
|
||||
}
|
||||
|
||||
std::list<image_data_chunk> idata; // FIXME: this could really be a local
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
|
||||
|
||||
|
||||
/*-------------------------------------------------
|
||||
png_read_file - read a PNG from a core stream
|
||||
@ -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