diff --git a/bindings/python/mapnik_image.cpp b/bindings/python/mapnik_image.cpp index 32384a034..f6c0550d0 100644 --- a/bindings/python/mapnik_image.cpp +++ b/bindings/python/mapnik_image.cpp @@ -164,6 +164,36 @@ boost::shared_ptr open_from_file(std::string const& filename) throw mapnik::image_reader_exception("Unsupported image format:" + filename); } +boost::shared_ptr fromstring(std::string const& str) +{ + std::auto_ptr reader(get_image_reader(str.c_str(),str.size())); + if (reader.get()) + { + boost::shared_ptr image_ptr = boost::make_shared(reader->width(),reader->height()); + reader->read(0,0,image_ptr->data()); + return image_ptr; + } + throw mapnik::image_reader_exception("Failed to load image from buffer" ); +} + +boost::shared_ptr frombuffer(PyObject * obj) +{ + void const* buffer=0; + Py_ssize_t buffer_len; + if (PyObject_AsReadBuffer(obj, &buffer, &buffer_len) == 0) + { + std::auto_ptr reader(get_image_reader(reinterpret_cast(buffer),buffer_len)); + if (reader.get()) + { + boost::shared_ptr image_ptr = boost::make_shared(reader->width(),reader->height()); + reader->read(0,0,image_ptr->data()); + return image_ptr; + } + throw mapnik::image_reader_exception("Failed to load image from buffer" ); + } +} + + void blend (image_32 & im, unsigned x, unsigned y, image_32 const& im2, float opacity) { im.set_rectangle_alpha2(im2.data(),x,y,opacity); @@ -257,6 +287,10 @@ void export_image() .def("save", &save_to_file3) .def("open",open_from_file) .staticmethod("open") + .def("frombuffer",&frombuffer) + .staticmethod("frombuffer") + .def("fromstring",&fromstring) + .staticmethod("fromstring") #if defined(HAVE_CAIRO) && defined(HAVE_PYCAIRO) .def("from_cairo",&from_cairo) .staticmethod("from_cairo") diff --git a/include/mapnik/factory.hpp b/include/mapnik/factory.hpp index 012c5de84..343460792 100644 --- a/include/mapnik/factory.hpp +++ b/include/mapnik/factory.hpp @@ -31,42 +31,23 @@ #include namespace mapnik { -template -class default_factory_error -{ -public: - struct factory_exception : public std::exception - { - const char* what() const throw() - { - return "uknown object type"; - } - }; - static product_type* on_unknown_type(const key_type&) - { - return 0; - } -}; template < typename product_type, typename key_type, -typename product_creator=product_type* (*)(), -template class factory_error_policy=default_factory_error -> +typename ...Args > class factory : public singleton >, - factory_error_policy + Args...> > { private: + typedef product_type* (*product_creator)(Args...); typedef std::map product_map; product_map map_; public: - bool register_product(const key_type& key,product_creator creator) + bool register_product(const key_type& key, product_creator creator) { return map_.insert(typename product_map::value_type(key,creator)).second; } @@ -76,14 +57,14 @@ public: return map_.erase(key)==1; } - product_type* create_object(const key_type& key,std::string const& file) + product_type* create_object(const key_type& key, Args const&...args) { typename product_map::const_iterator pos=map_.find(key); if (pos!=map_.end()) { - return (pos->second)(file); + return (pos->second)(args...); } - return factory_error_policy::on_unknown_type(key); + return 0; } }; } diff --git a/include/mapnik/image_reader.hpp b/include/mapnik/image_reader.hpp index dff902653..a3a6545e7 100644 --- a/include/mapnik/image_reader.hpp +++ b/include/mapnik/image_reader.hpp @@ -27,13 +27,16 @@ #include #include #include - +#include +// boost +#include // stl #include #include namespace mapnik { + class image_reader_exception : public std::exception { private: @@ -59,9 +62,15 @@ struct MAPNIK_DECL image_reader : private mapnik::noncopyable virtual ~image_reader() {} }; -bool register_image_reader(std::string const& type,image_reader* (*)(std::string const&)); +template +bool register_image_reader(std::string const& type, image_reader* (* fun)(Args...)) +{ + return factory::instance().register_product(type, fun); +} + MAPNIK_DECL image_reader* get_image_reader(std::string const& file,std::string const& type); MAPNIK_DECL image_reader* get_image_reader(std::string const& file); +MAPNIK_DECL image_reader* get_image_reader(char const* data, size_t size); } diff --git a/src/build.py b/src/build.py index e6b9b9b8f..1c45abf1a 100644 --- a/src/build.py +++ b/src/build.py @@ -55,9 +55,10 @@ ABI_VERSION = env['ABI_VERSION'] filesystem = 'boost_filesystem%s' % env['BOOST_APPEND'] regex = 'boost_regex%s' % env['BOOST_APPEND'] system = 'boost_system%s' % env['BOOST_APPEND'] +iostreams = 'boost_iostreams%s' % env['BOOST_APPEND'] # clear out and re-set libs for this env -lib_env['LIBS'] = ['freetype','z',env['ICU_LIB_NAME'],filesystem,system,regex] +lib_env['LIBS'] = ['freetype','z',env['ICU_LIB_NAME'],filesystem,system,regex,iostreams] if env['PROJ']: lib_env['LIBS'].append('proj') diff --git a/src/image_reader.cpp b/src/image_reader.cpp index 22e476e27..3bacd7cac 100644 --- a/src/image_reader.cpp +++ b/src/image_reader.cpp @@ -27,18 +27,45 @@ namespace mapnik { -typedef factory ImageReaderFactory; - -bool register_image_reader(std::string const& type,image_reader* (* fun)(std::string const&)) +inline boost::optional type_from_bytes(char const* data, size_t size) { - return ImageReaderFactory::instance().register_product(type,fun); + typedef boost::optional result_type; + if (size >= 4) + { + unsigned int magic = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]; + if (magic == 0x89504E47U) + { + return result_type("png"); + } + else if (magic == 0x49492A00 || magic == 0x4D4D002A) + { + return result_type("tiff"); + } + } + if (size>=2) + { + unsigned int magic = ((data[0] << 8) | data[1]) & 0xffff; + if (magic == 0xffd8) + { + return result_type("jpeg"); + } + } + + return result_type(); +} + +image_reader* get_image_reader(char const* data, size_t size) +{ + boost::optional type = type_from_bytes(data,size); + if (type) + return factory::instance().create_object(*type, data,size); + return 0; } image_reader* get_image_reader(std::string const& filename,std::string const& type) { - return ImageReaderFactory::instance().create_object(type,filename); + return factory::instance().create_object(type,filename); } image_reader* get_image_reader(std::string const& filename) @@ -46,7 +73,7 @@ image_reader* get_image_reader(std::string const& filename) boost::optional type = type_from_filename(filename); if (type) { - return ImageReaderFactory::instance().create_object(*type,filename); + return factory::instance().create_object(*type,filename); } return 0; } diff --git a/src/jpeg_reader.cpp b/src/jpeg_reader.cpp index 169db9a6b..c90f31df4 100644 --- a/src/jpeg_reader.cpp +++ b/src/jpeg_reader.cpp @@ -23,7 +23,6 @@ // mapnik #include #include -#include // jpeg extern "C" @@ -34,24 +33,29 @@ extern "C" // boost #include #include +#include +#include +#include // std #include namespace mapnik { + +template class jpeg_reader : public image_reader { - struct jpeg_file_guard +public: + typedef T source_type; + typedef boost::iostreams::stream input_stream; + const static unsigned BUF_SIZE = 4096; +private: + struct jpeg_stream_wrapper { - jpeg_file_guard(FILE * fd) - : fd_(fd) {} - - ~jpeg_file_guard() - { - if (fd_) fclose(fd_); - } - FILE * fd_; + jpeg_source_mgr manager; + input_stream * stream; + JOCTET buffer[BUF_SIZE]; }; struct jpeg_info_guard @@ -67,11 +71,13 @@ class jpeg_reader : public image_reader }; private: - std::string file_name_; + source_type source_; + input_stream stream_; unsigned width_; unsigned height_; public: - explicit jpeg_reader(std::string const& fileName); + explicit jpeg_reader(std::string const& file_name); + explicit jpeg_reader(char const* data, size_t size); ~jpeg_reader(); unsigned width() const; unsigned height() const; @@ -81,44 +87,135 @@ private: void init(); static void on_error(j_common_ptr cinfo); static void on_error_message(j_common_ptr cinfo); + static void init_source(j_decompress_ptr cinfo); + static boolean fill_input_buffer(j_decompress_ptr cinfo); + static void skip(j_decompress_ptr cinfo, long count); + static void term(j_decompress_ptr cinfo); + static void attach_stream(j_decompress_ptr cinfo, input_stream* in); }; namespace { image_reader* create_jpeg_reader(std::string const& file) { - return new jpeg_reader(file); -} -const bool registered = register_image_reader("jpeg",create_jpeg_reader); + return new jpeg_reader(file); } -jpeg_reader::jpeg_reader(std::string const& fileName) - : file_name_(fileName), +image_reader* create_jpeg_reader2(char const* data, size_t size) +{ + return new jpeg_reader(data, size); +} + +const bool registered = register_image_reader("jpeg",create_jpeg_reader); +const bool registered2 = register_image_reader("jpeg",create_jpeg_reader2); +} + +// ctors +template +jpeg_reader::jpeg_reader(std::string const& file_name) + : source_(file_name,std::ios_base::in | std::ios_base::binary), + stream_(source_), width_(0), height_(0) { + if (!stream_) throw image_reader_exception("cannot open image file "+ file_name); init(); } -jpeg_reader::~jpeg_reader() {} - -void jpeg_reader::on_error(j_common_ptr cinfo) +template +jpeg_reader::jpeg_reader(char const* data, size_t size) + : source_(data, size), + stream_(source_), + width_(0), + height_(0) +{ + if (!stream_) throw image_reader_exception("cannot open image stream"); + init(); +} + +// dtor +template +jpeg_reader::~jpeg_reader() {} + +// jpeg stream wrapper +template +void jpeg_reader::init_source (j_decompress_ptr cinfo) +{ + jpeg_stream_wrapper* wrap = reinterpret_cast(cinfo->src); + wrap->stream->seekg(0,std::ios_base::beg); +} + +template +boolean jpeg_reader::fill_input_buffer (j_decompress_ptr cinfo) +{ + jpeg_stream_wrapper* wrap = reinterpret_cast(cinfo->src); + wrap->stream->read(reinterpret_cast(&wrap->buffer[0]),BUF_SIZE); + std::streamsize size = wrap->stream->gcount(); + wrap->manager.next_input_byte = wrap->buffer; + wrap->manager.bytes_in_buffer = BUF_SIZE; + return (size > 0) ? TRUE : FALSE; +} + +template +void jpeg_reader::skip(j_decompress_ptr cinfo, long count) +{ + if (count <= 0) return; //A zero or negative skip count should be treated as a no-op. + jpeg_stream_wrapper* wrap = reinterpret_cast(cinfo->src); + + if (wrap->manager.bytes_in_buffer > 0 && count < wrap->manager.bytes_in_buffer) + { + wrap->manager.bytes_in_buffer -= count; + wrap->manager.next_input_byte = &wrap->buffer[BUF_SIZE - wrap->manager.bytes_in_buffer]; + } + else + { + wrap->stream->seekg(count, std::ios_base::cur); + // trigger buffer fill + wrap->manager.next_input_byte = 0; + wrap->manager.bytes_in_buffer = 0; //bytes_in_buffer may be zero on return. + } +} + +template +void jpeg_reader::term (j_decompress_ptr cinfo) +{ +// no-op +} + +template +void jpeg_reader::attach_stream (j_decompress_ptr cinfo, input_stream* in) +{ + if (cinfo->src == 0) + { + cinfo->src = (struct jpeg_source_mgr *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, sizeof(jpeg_stream_wrapper)); + } + jpeg_reader::jpeg_stream_wrapper * src = reinterpret_cast (cinfo->src); + src->manager.init_source = init_source; + src->manager.fill_input_buffer = fill_input_buffer; + src->manager.skip_input_data = skip; + src->manager.resync_to_restart = jpeg_resync_to_restart; + src->manager.term_source = term; + src->manager.bytes_in_buffer = 0; + src->manager.next_input_byte = 0; + src->stream = in; +} + +template +void jpeg_reader::on_error(j_common_ptr cinfo) { - //(*cinfo->err->output_message)(cinfo); - //jpeg_destroy(cinfo); throw image_reader_exception("JPEG Reader: libjpeg could not read image"); } -void jpeg_reader::on_error_message(j_common_ptr cinfo) +template +void jpeg_reader::on_error_message(j_common_ptr cinfo) { // used to supress jpeg from printing to stderr } -void jpeg_reader::init() +template +void jpeg_reader::init() { - FILE * fp = fopen(file_name_.c_str(),"rb"); - if (!fp) throw image_reader_exception("JPEG Reader: cannot open image file " + file_name_); - jpeg_file_guard guard(fp); jpeg_decompress_struct cinfo; jpeg_info_guard iguard(&cinfo); jpeg_error_mgr jerr; @@ -126,38 +223,42 @@ void jpeg_reader::init() jerr.error_exit = on_error; jerr.output_message = on_error_message; jpeg_create_decompress(&cinfo); - jpeg_stdio_src(&cinfo, fp); + attach_stream(&cinfo, &stream_); int ret = jpeg_read_header(&cinfo, TRUE); - if (ret != JPEG_HEADER_OK) throw image_reader_exception("JPEG Reader: failed to read header in " + file_name_); + if (ret != JPEG_HEADER_OK) + throw image_reader_exception("JPEG Reader: failed to read header"); jpeg_start_decompress(&cinfo); width_ = cinfo.output_width; height_ = cinfo.output_height; if (cinfo.out_color_space == JCS_UNKNOWN) { - throw image_reader_exception("JPEG Reader: failed to read unknown color space in " + file_name_); + throw image_reader_exception("JPEG Reader: failed to read unknown color space"); } if (cinfo.output_width == 0 || cinfo.output_height == 0) { - throw image_reader_exception("JPEG Reader: failed to read image size of " + file_name_); + throw image_reader_exception("JPEG Reader: failed to read image size of"); } } -unsigned jpeg_reader::width() const +template +unsigned jpeg_reader::width() const { return width_; } -unsigned jpeg_reader::height() const +template +unsigned jpeg_reader::height() const { return height_; } -void jpeg_reader::read(unsigned x0, unsigned y0, image_data_32& image) +template +void jpeg_reader::read(unsigned x0, unsigned y0, image_data_32& image) { - FILE * fp = fopen(file_name_.c_str(),"rb"); - if (!fp) throw image_reader_exception("JPEG Reader: cannot open image file " + file_name_); - jpeg_file_guard guard(fp); + stream_.clear(); + stream_.seekg(0, std::ios_base::beg); + jpeg_decompress_struct cinfo; jpeg_info_guard iguard(&cinfo); jpeg_error_mgr jerr; @@ -165,9 +266,9 @@ void jpeg_reader::read(unsigned x0, unsigned y0, image_data_32& image) jerr.error_exit = on_error; jerr.output_message = on_error_message; jpeg_create_decompress(&cinfo); - jpeg_stdio_src(&cinfo, fp); + attach_stream(&cinfo, &stream_); int ret = jpeg_read_header(&cinfo, TRUE); - if (ret != JPEG_HEADER_OK) throw image_reader_exception("JPEG Reader: failed to read header in " + file_name_); + if (ret != JPEG_HEADER_OK) throw image_reader_exception("JPEG Reader read(): failed to read header"); jpeg_start_decompress(&cinfo); JSAMPARRAY buffer; int row_stride; diff --git a/src/png_reader.cpp b/src/png_reader.cpp index 324c33e7d..67a873f65 100644 --- a/src/png_reader.cpp +++ b/src/png_reader.cpp @@ -20,32 +20,29 @@ * *****************************************************************************/ +// mapnik #include #include -#include extern "C" { #include } - +// boost #include +#include +#include +//#include +#include namespace mapnik { + +template class png_reader : public image_reader { - struct png_file_guard - { - png_file_guard(FILE * fd) - : fd_(fd) {} - - ~png_file_guard() - { - if (fd_) fclose(fd_); - } - FILE * fd_; - }; + typedef T source_type; + typedef boost::iostreams::stream ifstream; struct png_struct_guard { @@ -62,13 +59,16 @@ class png_reader : public image_reader }; private: - std::string fileName_; + + source_type source_; + ifstream stream_; unsigned width_; unsigned height_; int bit_depth_; int color_type_; public: - explicit png_reader(std::string const& fileName); + explicit png_reader(std::string const& file_name); + explicit png_reader(char const* data, std::size_t size); ~png_reader(); unsigned width() const; unsigned height() const; @@ -76,28 +76,26 @@ public: void read(unsigned x,unsigned y,image_data_32& image); private: void init(); + static void png_read_data(png_structp png_ptr, png_bytep data, png_size_t length); }; namespace { + image_reader* create_png_reader(std::string const& file) { - return new png_reader(file); -} -const bool registered = register_image_reader("png",create_png_reader); + return new png_reader(file); } -png_reader::png_reader(std::string const& fileName) - : fileName_(fileName), - width_(0), - height_(0), - bit_depth_(0), - color_type_(0) +image_reader* create_png_reader2(char const * data, std::size_t size) { - init(); + return new png_reader(data, size); +} + +const bool registered = register_image_reader("png",create_png_reader); +const bool registered2 = register_image_reader("png", create_png_reader2); } -png_reader::~png_reader() {} void user_error_fn(png_structp png_ptr, png_const_charp error_msg) { @@ -109,35 +107,64 @@ void user_warning_fn(png_structp png_ptr, png_const_charp warning_msg) MAPNIK_LOG_DEBUG(png_reader) << "libpng warning: '" << warning_msg << "'"; } -static void -png_read_data(png_structp png_ptr, png_bytep data, png_size_t length) +template +void png_reader::png_read_data(png_structp png_ptr, png_bytep data, png_size_t length) { - png_size_t check; - check = (png_size_t)fread(data, (png_size_t)1, length, - (FILE *)png_get_io_ptr(png_ptr)); - - if (check != length) + ifstream * fin = reinterpret_cast(png_get_io_ptr(png_ptr)); + fin->read(reinterpret_cast(data), length); + if (fin->gcount() != length) { png_error(png_ptr, "Read Error"); } } -void png_reader::init() +template +png_reader::png_reader(std::string const& file_name) + : source_(file_name,std::ios_base::in | std::ios_base::binary), + stream_(source_), + width_(0), + height_(0), + bit_depth_(0), + color_type_(0) { - FILE *fp=fopen(fileName_.c_str(),"rb"); - if (!fp) throw image_reader_exception("cannot open image file "+fileName_); - png_file_guard guard(fp); + if (!stream_) throw image_reader_exception("cannot open image file "+ file_name); + init(); +} + +template +png_reader::png_reader(char const* data, std::size_t size) + : source_(data,size), + stream_(source_), + width_(0), + height_(0), + bit_depth_(0), + color_type_(0) +{ + + if (!stream_) throw image_reader_exception("cannot open image stream"); + init(); +} + + +template +png_reader::~png_reader() {} + + +template +void png_reader::init() +{ png_byte header[8]; memset(header,0,8); - if ( fread(header,1,8,fp) != 8) + stream_.read(reinterpret_cast(header),8); + if ( stream_.gcount() != 8) { - throw image_reader_exception("Could not read " + fileName_); + throw image_reader_exception("Could not read image"); } int is_png=!png_sig_cmp(header,0,8); if (!is_png) { - throw image_reader_exception(fileName_ + " is not a png file"); + throw image_reader_exception(" File or steam is not a png"); } png_structp png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING,0,0,0); @@ -155,7 +182,7 @@ void png_reader::init() info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) throw image_reader_exception("failed to create info_ptr"); - png_set_read_fn(png_ptr, (png_voidp)fp, png_read_data); + png_set_read_fn(png_ptr, (png_voidp)&stream_, png_read_data); png_set_sig_bytes(png_ptr,8); png_read_info(png_ptr, info_ptr); @@ -169,21 +196,23 @@ void png_reader::init() MAPNIK_LOG_DEBUG(png_reader) << "png_reader: bit_depth=" << bit_depth_ << ",color_type=" << color_type_; } -unsigned png_reader::width() const +template +unsigned png_reader::width() const { return width_; } -unsigned png_reader::height() const +template +unsigned png_reader::height() const { return height_; } -void png_reader::read(unsigned x0, unsigned y0,image_data_32& image) +template +void png_reader::read(unsigned x0, unsigned y0,image_data_32& image) { - FILE *fp=fopen(fileName_.c_str(),"rb"); - if (!fp) throw image_reader_exception("cannot open image file "+fileName_); - png_file_guard guard(fp); + stream_.clear(); + stream_.seekg(0, std::ios_base::beg); png_structp png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING,0,0,0); @@ -201,7 +230,7 @@ void png_reader::read(unsigned x0, unsigned y0,image_data_32& image) info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) throw image_reader_exception("failed to create info_ptr"); - png_set_read_fn(png_ptr, (png_voidp)fp, png_read_data); + png_set_read_fn(png_ptr, (png_voidp)&stream_, png_read_data); png_read_info(png_ptr, info_ptr); if (color_type_ == PNG_COLOR_TYPE_PALETTE)