Commits

Anonymous committed a75be3a

Updated version zlib.h to reflect pending release of pngxx 0.3.0. Updated reading and writing to use an appropriate imagexx::exception_store object. Removed obsolete updatelibpng.py script. The net effect of this is that pngxx can now use an existing copy of libpng rather than the bundled one.

Comments (0)

Files changed (9)

 defines : common : PNG_NO_MNG_FEATURES PNG_NO_PROGRESSIVE_READ PNG_NO_WRITE_FLUSH
 defines : common : PNG_USER_WIDTH_MAX=0x7fffffffL PNG_USER_HEIGHT_MAX=0x7fffffffL
 defines : common : PNG_NO_READ_TEXT PNG_NO_READ_UNKNOWN_CHUNKS PNG_NO_WRITE_TEXT PNG_NO_WRITE_UNKNOWN_CHUNKS
-defines : common : PNG_NO_INFO_IMAGE PNG_SETJMP_NOT_SUPPORTED PNG_NO_SETJMP_SUPPORTED
+defines : common : PNG_NO_INFO_IMAGE PNG_SETJMP_NOT_SUPPORTED PNG_NO_SETJMP_SUPPORTED PNG_USER_MEM_SUPPORTED
 defines : common : PNGXX_CAN_THROW_EXCEPTIONS_THROUGH_C_LIB=1
 
 include : common : src/3rd_party/zlib src/3rd_party/libpng ../imagexx/include include

src/3rd_party/zlib/zlib.h

 extern "C" {
 #endif
 
-#define ZLIB_VERSION "1.2.3.f-pngxx-0.2.0"
+#define ZLIB_VERSION "1.2.3.f-pngxx-0.3.0"
 #define ZLIB_VERNUM 0x1230f
 
 /*

src/memory_pool.cpp

 
 namespace pngxx
 {
-    namespace
-    {
-        png_voidp allocate_in_pool(png_structp context, png_size_t bytes)
-        {
-            pngxx::memory_pool *pool = static_cast<pngxx::memory_pool *>(png_get_mem_ptr(context));
-            return static_cast<png_voidp>(pool->allocate(bytes));
-        }
-
-        void free_from_pool(png_structp context, png_voidp mem)
-        {
-            pngxx::memory_pool *pool = static_cast<pngxx::memory_pool *>(png_get_mem_ptr(context));
-            return pool->free(mem);
-        }
-
-    } // close anonymous namespace
-
-
     memory_pool::~memory_pool()
     {
         std::for_each(recorded_.begin(), recorded_.end(), &std::free);
 
     void memory_pool::free(void *mem)
     {
+        recorded_.erase(mem);
         std::free(mem);
-        recorded_.erase(mem);
     }
 
-    void memory_pool::install(png_structp context)
+    png_voidp allocate_in_pool(png_structp context, png_size_t bytes)
     {
-        png_set_mem_fn(context, this, &allocate_in_pool, &free_from_pool);
+        pngxx::memory_pool *pool = static_cast<pngxx::memory_pool *>(png_get_mem_ptr(context));
+        return static_cast<png_voidp>(pool->allocate(bytes));
     }
 
+    void free_from_pool(png_structp context, png_voidp mem)
+    {
+        pngxx::memory_pool *pool = static_cast<pngxx::memory_pool *>(png_get_mem_ptr(context));
+        return pool->free(mem);
+    }
+
+
 } // close namespace pngxx

src/memory_pool.hpp

             void *allocate(std::size_t bytes);
             void free(void *mem);
 
-            //! Assumes this pool exists for at least as long as context
-            void install(png_structp context);
-
         private:
             std::set<void *> recorded_;
     };
 
+    png_voidp allocate_in_pool(png_structp context, png_size_t bytes);
+    void free_from_pool(png_structp context, png_voidp mem);
+
 } // close namespace pngxx
 
 #endif // MEMORY_POOL_HPP_0240_13072009
+// Copyright 2009 Edd Dawson.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file BOOST_LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#include <cstring>
+#include <cctype>
+#include <algorithm>
+
+namespace pngxx
+{
+    namespace
+    {
+        struct ci_equal
+        {
+            bool operator() (char lhs, char rhs) const { return std::tolower(lhs) == std::tolower(rhs); }
+        };
+    }
+
+    namespace detail
+    {
+        bool oom(const char *message)
+        {
+            // This seems brittle on the face of things, but looking at the code in pngmem.c I think
+            // it's safe to say that this is ok.
+            // The worst that could happen is that a decompression_failure will be thrown at a point
+            // when it might have been slightly better to throw a bad_alloc instead.
+            const char what[] = "out of memory";
+            const char *what_end = what + (sizeof what) - 1;
+            const char *message_end = message + std::strlen(message);
+
+            return std::search(message, message_end, 
+                               static_cast<const char *>(what), what_end, 
+                               ci_equal()) != message_end;
+        }
+    }
+
+}
+// Copyright 2009 Edd Dawson.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file BOOST_LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+namespace pngxx
+{
+    namespace detail
+    {
+        // Returns true if the libpng error message indicates an out of memory condition
+        bool oom(const char *message);
+    }
+}
 #include <pngxx/exceptions.hpp>
 
 #include "memory_pool.hpp"
+#include "best_exception_store.hpp"
+#include "oom.hpp"
 
 #include <stdexcept>
 #include <cassert>
 #include <new>
+#include <csetjmp>
+
 #include <png.h>
 
+#define TRY(expr) \
+    do { \
+    if (setjmp(pimpl_->jmpbuf_) == 0) expr; \
+    pimpl_->ex_store_.rethrow_stored_exception(); \
+    } while (false)
+
 namespace pngxx
 {
-    namespace
-    {
-        void error(png_structp, const char *message)
-        {
-            throw pngxx::decompression_failure(message);
-        }
-
-        void warning(png_structp, const char *)
-        {
-        }
-
-        void read_bytes(png_structp context, unsigned char *target, std::size_t num_bytes)
-        {
-            imagexx::source *src = static_cast<imagexx::source *>(png_get_io_ptr(context));
-
-            assert(src);
-            if (src->read(target, num_bytes) != num_bytes)
-                throw imagexx::iterator_range_depleted();
-        }
-    }
-
     struct loader::impl
     {
         impl() :
             context_(0),
             info_(0),
             details_(imagexx::rgb, 0, 0),
-            passes_(1)
+            passes_(1),
+            ex_store_(decompression_failure("An unknown decompression error occurred"))
         {
         }
 
+        static impl &self(png_structp context)
+        {
+            return *static_cast<impl *>(png_get_error_ptr(context));
+        }
+
+        static void error(png_structp context, const char *message)
+        {
+            if (detail::oom(message))
+                self(context).ex_store_.store(std::bad_alloc());
+            else
+            {
+                impl &me = self(context);
+                try { me.ex_store_.store(decompression_failure(message)); }
+                catch (const std::bad_alloc &ba) { me.ex_store_.store(ba); }
+            }
+
+            std::longjmp(self(context).jmpbuf_, 42);
+        }
+
+        static void warning(png_structp, const char *)
+        {
+            // TODO: log this somewhere?
+        }
+
+        static void read_bytes(png_structp context, unsigned char *target, std::size_t num_bytes)
+        {
+            try
+            {
+                impl &me = self(context);
+                imagexx::source *src = me.source_.get();
+                assert(src);
+                if (src->read(target, num_bytes) != num_bytes)
+                    me.ex_store_.store(imagexx::iterator_range_depleted());
+            }
+            catch (...)
+            {
+                self(context).ex_store_.store_current_exception();
+            }
+
+            if (self(context).ex_store_.has_exception())
+                std::longjmp(self(context).jmpbuf_, 42);
+        }
+
         std::auto_ptr<imagexx::source> source_;
 
         png_structp context_;
         imagexx::raster_details details_;
         std::size_t passes_;
         memory_pool pool_;
+
+        std::jmp_buf jmpbuf_;
+        best_exception_store ex_store_;
     };
 
-
     loader::loader() : pimpl_(new impl) { }
 
     loader::~loader()
 
     void loader::reset()
     {
-        png_structp rs = png_create_read_struct(PNG_LIBPNG_VER_STRING, this, &error, &warning);
+        png_structp rs = 0;
+
+        // We supply a custom allocator that records allocations. This means that it's fine to throw/longjmp
+        // from any callback we supply to libpng without worrying about leaks being caused by not returning 
+        // to the calling function.
+        TRY(rs = png_create_read_struct_2(PNG_LIBPNG_VER_STRING, 
+                                          pimpl_.get(), &impl::error, &impl::warning, 
+                                          &pimpl_->pool_, &allocate_in_pool, &free_from_pool));
         if (!rs) throw std::bad_alloc();
 
-        // We supply a custom allocator that records allocations. This means that it's fine to throw
-        // from any callback we supply to libpng without worrying about leaks being caused by not
-        // returning to the calling function.
-        pimpl_->pool_.install(rs);
+        png_infop info = 0;
+        if (setjmp(pimpl_->jmpbuf_) == 0)
+            info = png_create_info_struct(rs);
+        if (pimpl_->ex_store_.has_exception() || !info)
+            png_destroy_read_struct(&rs, png_infopp_NULL, png_infopp_NULL);
 
-        png_infop info = png_create_info_struct(rs);
-        if (!info)
-        {
-            png_destroy_read_struct(&rs, png_infopp_NULL, png_infopp_NULL);
-            throw std::bad_alloc();
-        }
+        pimpl_->ex_store_.rethrow_stored_exception();
+        if (!info) throw std::bad_alloc();
 
         clear();
 
         assert(src.get());
         pimpl_->source_ = src;
         assert(pimpl_->context_);
-        png_set_read_fn(pimpl_->context_, pimpl_->source_.get(), &read_bytes);
+        TRY(png_set_read_fn(pimpl_->context_, 0, &impl::read_bytes));
     }
 
     void loader::read_details()
         assert(pimpl_->source_.get());
 
         png_structp rsp = pimpl_->context_;
-        png_read_info(rsp, pimpl_->info_);
+
+        TRY(png_read_info(rsp, pimpl_->info_));
 
         png_uint_32 width = 0;
         png_uint_32 height = 0;
         int bit_depth = 0;
         int interlace_type = 0;
 
-        png_get_IHDR(rsp, pimpl_->info_, &width, &height, &bit_depth, &colour_type, &interlace_type, 0, 0);
+        TRY(png_get_IHDR(rsp, pimpl_->info_, &width, &height, &bit_depth, &colour_type, &interlace_type, 0, 0));
 
         imagexx::pixel_format format = imagexx::rgb;
 
         pimpl_->details_ = imagexx::raster_details(format, width, height, width_mm, height_mm);
 
         // Prepare to start reading the raster
-        png_set_strip_16(rsp); // 16 bit pixels read as 8 bit pixels
-        png_set_packing(rsp); // unpack 1-bit, 2-bit and 4-bit pixels in to their own bytes
+        TRY(png_set_strip_16(rsp)); // 16 bit pixels read as 8 bit pixels
+        TRY(png_set_packing(rsp)); // unpack 1-bit, 2-bit and 4-bit pixels in to their own bytes
 
         if (colour_type & PNG_COLOR_TYPE_PALETTE)
-            png_set_palette_to_rgb(rsp); // a paletted image should come through as an RGB raster
+            TRY(png_set_palette_to_rgb(rsp)); // a paletted image should come through as an RGB raster
 
         if (colour_type & PNG_COLOR_TYPE_GRAY && bit_depth < 8)
-            png_set_gray_1_2_4_to_8(rsp); // expand 1, 2, 4 bpp greyscale images to 8 bpp
+            TRY(png_set_gray_1_2_4_to_8(rsp)); // expand 1, 2, 4 bpp greyscale images to 8 bpp
 
         if (format == imagexx::rgba || format == imagexx::grey_alpha)
-            png_set_tRNS_to_alpha(rsp); // if image has transparency, read pixels as RGBA or GA
+            TRY(png_set_tRNS_to_alpha(rsp)); // if image has transparency, read pixels as RGBA or GA
 
-        pimpl_->passes_ = png_set_interlace_handling(rsp);
+        TRY(pimpl_->passes_ = png_set_interlace_handling(rsp));
     }
 
     void loader::read_row(std::vector<unsigned char> &row)
     {
-        png_read_row(pimpl_->context_, &row.front(), NULL);
+        TRY(png_read_row(pimpl_->context_, &row.front(), NULL));
     }
 
     void loader::read_entire_raster(std::vector<unsigned char> &raster)
         for (std::size_t r = 0, h = d.height(); r != h; ++r)
             rows[r] = &raster[stride * r];
 
-        png_read_image(pimpl_->context_, &rows.front());
+        TRY(png_read_image(pimpl_->context_, &rows.front()));
     }
 
     bool loader::interlaced() const

src/updatelibpng.py

-from glob import glob
-from os.path import *
-import os, sys
-
-sources = glob('libpng/*.cpp')
-headers = glob('libpng/*.h')
-
-def diff(original, new):
-    os.system('diff -uN %s %s' % (original, new))
-
-def patch(original, new):
-    os.system('diff -uN %s %s > patch.txt' % (original, new))
-    os.system('patch %s < patch.txt' % original)
-    os.remove('patch.txt')
-
-def overwrite(original, new):
-    os.system('cp %s %s' % (new, original))
-
-def patch_counterpart(source, counterpart_loc, counterpart_ext, cb):
-    filename = splitext(basename(source))[0] + counterpart_ext
-    counterpart = join(counterpart_loc, filename)
-    if not exists(counterpart):
-        raise "TODO - proper exception"
-
-    cb(source, counterpart)
-
-callbacks = { 'diff' : diff, 'patch' : patch, 'overwrite' : overwrite }
-
-if len(sys.argv) != 3 or sys.argv[2] not in callbacks:
-    print 'updatelibpng.py <newpngsrcdir> <diff|patch|overwrite>'
-    sys.exit(1)
-
-cb = callbacks[sys.argv[2]]
-
-for s in sources:
-    patch_counterpart(s, sys.argv[1], '.c', cb)
-
-for h in headers:
-    patch_counterpart(h, sys.argv[1], '.h', cb)
 // http://www.boost.org/LICENSE_1_0.txt)
 
 #include <pngxx/write.hpp>
+
 #include "memory_pool.hpp"
+#include "best_exception_store.hpp"
+#include "oom.hpp"
+
 #include <stdexcept>
 #include <cassert>
+#include <csetjmp>
+
 #include <png.h>
 
+#define TRY(expr) \
+    do { \
+    if (setjmp(pimpl_->jmpbuf_) == 0) expr; \
+    pimpl_->ex_store_.rethrow_stored_exception(); \
+    } while (false)
+
 namespace pngxx
 {
     namespace
     {
-        void error(png_structp, const char *message)
-        {
-            throw pngxx::decompression_failure(message);
-        }
-
-        void warning(png_structp, const char *)
-        {
-        }
-
-        void write_bytes(png_structp context, unsigned char *target, std::size_t num_bytes)
-        {
-            imagexx::sink *snk = static_cast<imagexx::sink *>(png_get_io_ptr(context));
-
-            assert(snk);
-            snk->write(target, num_bytes);
-        }
-
-        void flush(png_structp)
-        {
-        }
-
         // only for non-negative numbers
         png_uint_32 round(double d)
         {
     {
         struct writer::impl
         {
-            impl() : context_(0), info_(0) { }
+            impl() :
+                context_(0),
+                info_(0),
+                ex_store_(compression_failure("An unknown compression error occurred")) 
+            {
+            }
+
+            ~impl()
+            {
+                if (context_)
+                    png_destroy_write_struct(&context_, info_ ? &info_: 0);
+            }
+
+            static impl &self(png_structp context)
+            {
+                return *static_cast<impl *>(png_get_error_ptr(context));
+            }
+
+            static void error(png_structp context, const char *message)
+            {
+                if (detail::oom(message))
+                    self(context).ex_store_.store(std::bad_alloc());
+                else
+                {
+                    impl &me = self(context);
+                    try { me.ex_store_.store(compression_failure(message)); }
+                    catch (const std::bad_alloc &ba) { me.ex_store_.store(ba); }
+                }
+
+                std::longjmp(self(context).jmpbuf_, 42);
+            }
+
+            static void warning(png_structp, const char *)
+            {
+                // TODO: log message somewhere?
+            }
+
+            static void write_bytes(png_structp context, unsigned char *target, std::size_t num_bytes)
+            {
+                try
+                {
+                    impl &me = self(context);
+                    imagexx::sink *snk = me.sink_.get();
+                    assert(snk);
+                    snk->write(target, num_bytes);
+                }
+                catch (...)
+                {
+                    self(context).ex_store_.store_current_exception();
+                }
+
+                if (self(context).ex_store_.has_exception())
+                    std::longjmp(self(context).jmpbuf_, 42);
+            }
+
+            static void flush(png_structp)
+            {
+            }
 
             std::auto_ptr<imagexx::sink> sink_;
             png_structp context_;
             png_infop info_;
             memory_pool pool_;
+
+            best_exception_store ex_store_;
+            std::jmp_buf jmpbuf_;
         };
 
         writer::writer(const imagexx::raster_details &details, double compression, std::auto_ptr<imagexx::sink> &snk) :
             pimpl_->sink_ = snk;
             assert(pimpl_->sink_.get());
 
-            png_structp ws = png_create_write_struct(PNG_LIBPNG_VER_STRING, this, &error, &warning);
+            png_structp ws = 0;
+            png_infop info = 0;
+            
+            // We supply a custom allocator that records allocations. This means that it's fine to throw/longjmp
+            // from any callback we supply to libpng without worrying about leaks being caused by not returning
+            // to the calling function.
+            TRY(ws = png_create_write_struct_2(PNG_LIBPNG_VER_STRING, 
+                                               pimpl_.get(), &impl::error, &impl::warning,
+                                               &pimpl_->pool_, &allocate_in_pool, &free_from_pool));
             if (!ws) throw std::bad_alloc();
 
-            png_infop info = png_create_info_struct(ws);
-            if (!info)
-            {
-                png_destroy_write_struct(&ws, png_infopp_NULL);
-                throw std::bad_alloc();
-            }
-        
-            // We supply a custom allocator that records allocations. This means that it's fine to throw
-            // from any callback we supply to libpng without worrying about leaks being caused by not
-            // returning to the calling function.
-            pimpl_->pool_.install(ws);
+            pimpl_->context_ = ws;
 
-            pimpl_->context_ = ws;
+            TRY(info = png_create_info_struct(ws));
+            if (!info) throw std::bad_alloc();
+
             pimpl_->info_ = info;
-
-            png_set_write_fn(ws, pimpl_->sink_.get(), &write_bytes, &flush);
+            
+            TRY(png_set_write_fn(ws, 0, &impl::write_bytes, &impl::flush));
 
             if (compression < 0.0) compression = 1.0;
             else if (compression > 1.0) compression = 0.0;
 
             if (compression == 0) png_set_compression_level(ws, Z_NO_COMPRESSION);
             else if (compression < 0.5) png_set_compression_level(ws, Z_BEST_SPEED);
-            png_set_compression_level(ws, Z_BEST_COMPRESSION);
+
+            TRY(png_set_compression_level(ws, Z_BEST_COMPRESSION));
 
             typedef imagexx::raster_details rdetails;
             int colour_type = PNG_COLOR_MASK_COLOR;
                 default: break;
             }
 
-            png_set_IHDR(ws, info,
-                         details.width(), details.height(),
-                         8, colour_type,
-                         PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
+            TRY(png_set_IHDR(ws, info,
+                             details.width(), details.height(),
+                             8, colour_type,
+                             PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT));
 
             png_color_8 colour_bits;
             if (colour_type & PNG_COLOR_MASK_COLOR)
             if (colour_type & PNG_COLOR_MASK_ALPHA)
                 colour_bits.alpha = 8;
 
-            png_set_sBIT(ws, info, &colour_bits);
+            TRY(png_set_sBIT(ws, info, &colour_bits));
 
             if (details.width_mm() && details.height_mm())
             {
                 const double pixels_per_m_x = 1000.0 * details.width() / details.width_mm();
                 const double pixels_per_m_y = 1000.0 * details.height() / details.height_mm();
-                png_set_pHYs(ws, info, pngxx::round(pixels_per_m_x), pngxx::round(pixels_per_m_y), PNG_RESOLUTION_METER);
+                TRY(png_set_pHYs(ws, info, 
+                                 pngxx::round(pixels_per_m_x), pngxx::round(pixels_per_m_y), PNG_RESOLUTION_METER));
             }
 
-            png_write_info(ws, info);
+            TRY(png_write_info(ws, info));
         }
 
         writer::~writer()
         {
-            png_destroy_write_struct(&pimpl_->context_, &pimpl_->info_);
         }
 
         void writer::finish()
         {
-            png_write_end(pimpl_->context_, pimpl_->info_);
+            TRY(png_write_end(pimpl_->context_, pimpl_->info_));
         }
 
         void writer::write_row(const unsigned char *bytes)
         {
             unsigned char *row = const_cast<unsigned char *>(bytes); // Grr!
-            png_write_row(pimpl_->context_, row);
+            TRY(png_write_row(pimpl_->context_, row));
         }
 
     } // close namespace detail