+ fix reading a region from JPEG file

+ re-use jpeg lib structures
+ avoid resource leaking (store FILE in boost::shared_ptr with custom deleter)
This commit is contained in:
artemp 2013-04-08 15:56:19 +01:00
parent 40c01fd0ed
commit 2ca306290a

View file

@ -32,6 +32,7 @@ extern "C"
} }
// boost // boost
#include <boost/shared_ptr.hpp>
#include <boost/scoped_array.hpp> #include <boost/scoped_array.hpp>
// std // std
@ -41,13 +42,22 @@ namespace mapnik
{ {
class JpegReader : public image_reader, mapnik::noncopyable class JpegReader : public image_reader, mapnik::noncopyable
{ {
struct file_closer
{
void operator() (FILE * file)
{
if (file != 0) fclose(file);
}
};
typedef boost::shared_ptr<FILE> file_ptr;
private: private:
std::string fileName_; std::string fileName_;
unsigned width_; unsigned width_;
unsigned height_; unsigned height_;
jpeg_decompress_struct cinfo; jpeg_decompress_struct cinfo;
jpeg_error_mgr jerr; jpeg_error_mgr jerr;
FILE *fp; file_ptr file_;
public: public:
explicit JpegReader(std::string const& fileName); explicit JpegReader(std::string const& fileName);
~JpegReader(); ~JpegReader();
@ -74,16 +84,13 @@ JpegReader::JpegReader(std::string const& fileName)
: fileName_(fileName), : fileName_(fileName),
width_(0), width_(0),
height_(0), height_(0),
fp(NULL) file_()
{ {
init(); init();
} }
JpegReader::~JpegReader() { JpegReader::~JpegReader()
if (fp) {
{
fclose(fp);
}
jpeg_destroy_decompress(&cinfo); jpeg_destroy_decompress(&cinfo);
} }
@ -101,22 +108,28 @@ void JpegReader::on_error_message(j_common_ptr cinfo)
void JpegReader::init() void JpegReader::init()
{ {
fp = fopen(fileName_.c_str(),"rb"); FILE * fp = fopen(fileName_.c_str(),"rb");
if (!fp) throw image_reader_exception("JPEG Reader: cannot open image file " + fileName_); if (!fp) throw image_reader_exception("JPEG Reader: cannot open image file " + fileName_);
file_ = boost::shared_ptr<FILE>(fp,file_closer());
cinfo.err = jpeg_std_error(&jerr); cinfo.err = jpeg_std_error(&jerr);
jerr.error_exit = on_error; jerr.error_exit = on_error;
jerr.output_message = on_error_message; jerr.output_message = on_error_message;
jpeg_create_decompress(&cinfo); jpeg_create_decompress(&cinfo);
jpeg_stdio_src(&cinfo, fp); jpeg_stdio_src(&cinfo, &*file_);
jpeg_read_header(&cinfo, TRUE); jpeg_read_header(&cinfo, TRUE);
jpeg_start_decompress(&cinfo); jpeg_start_decompress(&cinfo);
width_ = cinfo.output_width; width_ = cinfo.output_width;
height_ = cinfo.output_height; height_ = cinfo.output_height;
// if enabled: "Application transferred too few scanlines"
//jpeg_finish_decompress(&cinfo); if (cinfo.out_color_space == JCS_UNKNOWN)
jpeg_destroy_decompress(&cinfo); {
fclose(fp); throw image_reader_exception("JPEG Reader: failed to read unknown color space in " + fileName_);
}
if (cinfo.output_width == 0 || cinfo.output_height == 0)
{
jpeg_destroy_decompress (&cinfo);
throw image_reader_exception("JPEG Reader: failed to read image size of " + fileName_);
}
} }
unsigned JpegReader::width() const unsigned JpegReader::width() const
@ -131,59 +144,42 @@ unsigned JpegReader::height() const
void JpegReader::read(unsigned x0, unsigned y0, image_data_32& image) void JpegReader::read(unsigned x0, unsigned y0, image_data_32& image)
{ {
fp = fopen(fileName_.c_str(),"rb");
if (!fp) throw image_reader_exception("JPEG Reader: cannot open image file " + fileName_);
cinfo.err = jpeg_std_error(&jerr);
jpeg_create_decompress(&cinfo);
jpeg_stdio_src(&cinfo, fp);
jpeg_read_header(&cinfo, TRUE);
if (cinfo.out_color_space == JCS_UNKNOWN)
throw image_reader_exception("JPEG Reader: failed to read unknown color space in " + fileName_);
jpeg_start_decompress(&cinfo);
if (cinfo.output_width == 0) {
jpeg_destroy_decompress (&cinfo);
throw image_reader_exception("JPEG Reader: failed to read image size of " + fileName_);
}
JSAMPARRAY buffer; JSAMPARRAY buffer;
int row_stride; int row_stride;
unsigned char a,r,g,b; unsigned char a,r,g,b;
row_stride = cinfo.output_width * cinfo.output_components; row_stride = cinfo.output_width * cinfo.output_components;
buffer = (*cinfo.mem->alloc_sarray) ((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1); buffer = (*cinfo.mem->alloc_sarray) ((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1);
unsigned w = std::min(unsigned(image.width()),width_); unsigned w = std::min(unsigned(image.width()),width_ - x0);
unsigned h = std::min(unsigned(image.height()),height_); unsigned h = std::min(unsigned(image.height()),height_ - y0);
boost::scoped_array<unsigned int> out_row(new unsigned int[w]); boost::scoped_array<unsigned int> out_row(new unsigned int[w]);
// TODO - handle x0 unsigned row = 0;
for (unsigned i=0;i<h;++i) while (cinfo.output_scanline < cinfo.output_height)
{ {
jpeg_read_scanlines(&cinfo, buffer, 1); jpeg_read_scanlines(&cinfo, buffer, 1);
if (i>=y0 && i<h) if (row >= y0 && row < y0 + h)
{ {
for (unsigned int x=0; x<w; x++) for (unsigned int x = 0; x < w; ++x)
{ {
unsigned col = x + x0;
a = 255; // alpha not supported in jpg a = 255; // alpha not supported in jpg
r = buffer[0][cinfo.output_components * x]; r = buffer[0][cinfo.output_components * col];
if (cinfo.output_components > 2) if (cinfo.output_components > 2)
{ {
g = buffer[0][cinfo.output_components*x+1]; g = buffer[0][cinfo.output_components * col + 1];
b = buffer[0][cinfo.output_components*x+2]; b = buffer[0][cinfo.output_components * col + 2];
} else { } else {
g = r; g = r;
b = r; b = r;
} }
out_row[x] = color(r, g, b, a).rgba(); out_row[x] = color(r, g, b, a).rgba();
} }
image.setRow(i-y0, out_row.get(), w); image.setRow(row - y0, out_row.get(), w);
} }
++row;
} }
jpeg_finish_decompress(&cinfo);
jpeg_destroy_decompress(&cinfo); jpeg_destroy_decompress(&cinfo);
} }
} }