-#include <png.h>
#include "pngloader.h"
+#include <png.h>
using namespace std;
struct PngLoader::Private
{
- std::string message;
- png_struct *png;
- png_info *info;
+ string message;
+ png_struct *png = nullptr;
+ png_info *info = nullptr;
+ int interlace = PNG_INTERLACE_NONE;
};
-PngLoader::PngLoader(IO::Base &io, const string &sig):
+
+PngLoader::PngLoader(IO::Base &io, unsigned sig_bytes):
priv(new Private)
{
- priv->png = png_create_read_struct(PNG_LIBPNG_VER_STRING, &priv->message, error, 0);
+ priv->png = png_create_read_struct(PNG_LIBPNG_VER_STRING, &priv->message, error, nullptr);
priv->info = png_create_info_struct(priv->png);
// These probably won't give any errors
png_set_read_fn(priv->png, &io, read);
- png_set_sig_bytes(priv->png, sig.size());
+ png_set_sig_bytes(priv->png, sig_bytes);
}
PngLoader::~PngLoader()
{
- png_destroy_read_struct(&priv->png, &priv->info, 0);
+ png_destroy_read_struct(&priv->png, &priv->info, nullptr);
delete priv;
}
-bool PngLoader::detect(const std::string &sig)
+bool PngLoader::detect(const string &sig)
{
return !png_sig_cmp(reinterpret_cast<png_byte *>(const_cast<char*>(sig.data())), 0, sig.size());
}
-void PngLoader::load(Image::Data &data)
+void PngLoader::load_headers_(Image::Data &data)
{
if(setjmp(png_jmpbuf(priv->png)))
+ {
throw bad_image_data(priv->message);
+ }
png_read_info(priv->png, priv->info);
png_uint_32 width;
png_uint_32 height;
int depth;
int color;
- png_get_IHDR(priv->png, priv->info, &width, &height, &depth, &color, 0, 0, 0);
- data.width = width;
- data.height = height;
+ png_get_IHDR(priv->png, priv->info, &width, &height, &depth, &color, &priv->interlace, nullptr, nullptr);
+ unsigned nchans = png_get_channels(priv->png, priv->info);
+
if(depth!=8)
throw unsupported_image_format("depth!=8");
+
+ data.width = width;
+ data.height = height;
+ data.stride = data.width*nchans;
+
switch(color)
{
case PNG_COLOR_TYPE_PALETTE: data.fmt = COLOR_INDEX; break;
case PNG_COLOR_TYPE_GRAY: data.fmt = LUMINANCE; break;
case PNG_COLOR_TYPE_GRAY_ALPHA: data.fmt = LUMINANCE_ALPHA; break;
- case PNG_COLOR_TYPE_RGB: data.fmt = RGB; break;
+ case PNG_COLOR_TYPE_RGB: data.fmt = (nchans==4 ? RGBX : RGB); break;
case PNG_COLOR_TYPE_RGB_ALPHA: data.fmt = RGBA; break;
default: throw unsupported_image_format("unknown color type");
}
+}
- unsigned nchans = png_get_channels(priv->png, priv->info);
- if(nchans==4 && data.fmt==RGB)
- png_set_strip_alpha(priv->png);
+void PngLoader::load_pixels_(Image::Data &data)
+{
+ png_byte **rows = nullptr;
+
+ if(setjmp(png_jmpbuf(priv->png)))
+ {
+ delete[] rows;
+ throw bad_image_data(priv->message);
+ }
- unsigned rowstride = data.width*nchans;
- data.data = new char[rowstride*data.height];
- for(unsigned y=0; y<data.height; ++y)
- png_read_row(priv->png, reinterpret_cast<png_byte *>(data.data+rowstride*(data.height-1-y)), 0);
+ if(priv->interlace==PNG_INTERLACE_ADAM7)
+ {
+ // ADAM7 requires all rows to be loaded at once
+ unsigned n_passes = png_set_interlace_handling(priv->png);
+ rows = new png_byte *[data.height];
+ for(unsigned y=0; y<data.height; ++y)
+ rows[y] = reinterpret_cast<png_byte *>(data.pixels+data.stride*(data.height-1-y));
+
+ for(unsigned i=0; i<n_passes; ++i)
+ png_read_rows(priv->png, rows, nullptr, data.height);
+
+ delete[] rows;
+ }
+ else
+ {
+ for(unsigned y=0; y<data.height; ++y)
+ png_read_row(priv->png, reinterpret_cast<png_byte *>(data.pixels+data.stride*(data.height-1-y)), nullptr);
+ }
- png_read_end(priv->png, 0);
+ png_read_end(priv->png, nullptr);
}
} // namespace Graphics