From: <md...@us...> - 2007-11-12 20:27:36
|
Revision: 4237 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=4237&view=rev Author: mdboom Date: 2007-11-12 12:27:33 -0800 (Mon, 12 Nov 2007) Log Message: ----------- [ 1448342 ] FigureCanvasAgg.print_figure fails with StringIO 'file' Added support to write png to an arbitrary Python file-like object in the agg backend. Will continue to use faster C-only calls if the file-like object is in fact a file. Also, clean up exception handling in write_png. Modified Paths: -------------- trunk/matplotlib/lib/matplotlib/backends/backend_agg.py trunk/matplotlib/src/_backend_agg.cpp Modified: trunk/matplotlib/lib/matplotlib/backends/backend_agg.py =================================================================== --- trunk/matplotlib/lib/matplotlib/backends/backend_agg.py 2007-11-12 20:08:11 UTC (rev 4236) +++ trunk/matplotlib/lib/matplotlib/backends/backend_agg.py 2007-11-12 20:27:33 UTC (rev 4237) @@ -415,5 +415,5 @@ def print_png(self, filename, *args, **kwargs): self.draw() - self.get_renderer()._renderer.write_png(str(filename), self.figure.dpi.get()) + self.get_renderer()._renderer.write_png(filename, self.figure.dpi.get()) Modified: trunk/matplotlib/src/_backend_agg.cpp =================================================================== --- trunk/matplotlib/src/_backend_agg.cpp 2007-11-12 20:08:11 UTC (rev 4236) +++ trunk/matplotlib/src/_backend_agg.cpp 2007-11-12 20:27:33 UTC (rev 4237) @@ -2256,7 +2256,22 @@ } +static void write_png_data(png_structp png_ptr, png_bytep data, png_size_t length) { + PyObject* py_file_obj = (PyObject*)png_get_io_ptr(png_ptr); + PyObject* write_method = PyObject_GetAttrString(py_file_obj, "write"); + PyObject_CallFunction(write_method, "s#", data, length); + + // MGDTODO: Check NULL on failure +} +static void flush_png_data(png_structp png_ptr) { + PyObject* py_file_obj = (PyObject*)png_get_io_ptr(png_ptr); + PyObject* flush_method = PyObject_GetAttrString(py_file_obj, "write"); + if (flush_method) { + PyObject_CallFunction(flush_method, ""); + } +} + // this code is heavily adapted from the paint license, which is in // the file paint.license (BSD compatible) included in this // distribution. TODO, add license file to MANIFEST.in and CVS @@ -2267,97 +2282,96 @@ args.verify_length(1, 2); - FILE *fp; - Py::Object o = Py::Object(args[0]); - bool fpclose = true; - if (o.isString()) { - std::string fileName = Py::String(o); + FILE *fp = NULL; + Py::Object py_fileobj = Py::Object(args[0]); + if (py_fileobj.isString()) { + std::string fileName = Py::String(py_fileobj); const char *file_name = fileName.c_str(); if ((fp = fopen(file_name, "wb")) == NULL) throw Py::RuntimeError( Printf("Could not open file %s", file_name).str() ); } else { - if ((fp = PyFile_AsFile(o.ptr())) == NULL) - throw Py::TypeError("Could not convert object to file pointer"); - fpclose = false; + if ((fp = PyFile_AsFile(py_fileobj.ptr())) == NULL) { + PyObject* write_method = PyObject_GetAttrString(py_fileobj.ptr(), "write"); + if (!(write_method && PyCallable_Check(write_method))) + throw Py::TypeError("Object does not appear to be a Python file-like object"); + } } + + png_bytep *row_pointers = NULL; + png_structp png_ptr = NULL; + png_infop info_ptr = NULL; - png_structp png_ptr; - png_infop info_ptr; - struct png_color_8_struct sig_bit; - png_uint_32 row; + try { + struct png_color_8_struct sig_bit; + png_uint_32 row; + + row_pointers = new png_bytep[height]; + for (row = 0; row < height; ++row) { + row_pointers[row] = pixBuffer + row * width * 4; + } - png_bytep *row_pointers = new png_bytep[height]; - for (row = 0; row < height; ++row) { - row_pointers[row] = pixBuffer + row * width * 4; - } + png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (png_ptr == NULL) { + throw Py::RuntimeError("Could not create write struct"); + } + info_ptr = png_create_info_struct(png_ptr); + if (info_ptr == NULL) { + throw Py::RuntimeError("Could not create info struct"); + } - if (fp == NULL) { - delete [] row_pointers; - throw Py::RuntimeError("Could not open file"); - } + if (setjmp(png_ptr->jmpbuf)) { + throw Py::RuntimeError("Error building image"); + } - - png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); - if (png_ptr == NULL) { - if (fpclose) fclose(fp); - delete [] row_pointers; - throw Py::RuntimeError("Could not create write struct"); - } - - info_ptr = png_create_info_struct(png_ptr); - if (info_ptr == NULL) { - if (fpclose) fclose(fp); - png_destroy_write_struct(&png_ptr, &info_ptr); - delete [] row_pointers; - throw Py::RuntimeError("Could not create info struct"); - } - - if (setjmp(png_ptr->jmpbuf)) { - if (fpclose) fclose(fp); - png_destroy_write_struct(&png_ptr, &info_ptr); - delete [] row_pointers; - throw Py::RuntimeError("Error building image"); - } - - png_init_io(png_ptr, fp); - png_set_IHDR(png_ptr, info_ptr, - width, height, 8, - PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, - PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); + if (fp) { + png_init_io(png_ptr, fp); + } else { + png_set_write_fn(png_ptr, (void*)py_fileobj.ptr(), + &write_png_data, &flush_png_data); + } + png_set_IHDR(png_ptr, info_ptr, + width, height, 8, + PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); - // Save the dpi of the image in the file - if (args.size() == 2) { - double dpi = Py::Float(args[1]); - size_t dots_per_meter = (size_t)(dpi / (2.54 / 100.0)); - png_set_pHYs(png_ptr, info_ptr, dots_per_meter, dots_per_meter, PNG_RESOLUTION_METER); + // Save the dpi of the image in the file + if (args.size() == 2) { + double dpi = Py::Float(args[1]); + size_t dots_per_meter = (size_t)(dpi / (2.54 / 100.0)); + png_set_pHYs(png_ptr, info_ptr, dots_per_meter, dots_per_meter, PNG_RESOLUTION_METER); + } + + // this a a color image! + sig_bit.gray = 0; + sig_bit.red = 8; + sig_bit.green = 8; + sig_bit.blue = 8; + /* if the image has an alpha channel then */ + sig_bit.alpha = 8; + png_set_sBIT(png_ptr, info_ptr, &sig_bit); + + png_write_info(png_ptr, info_ptr); + png_write_image(png_ptr, row_pointers); + png_write_end(png_ptr, info_ptr); + + /* Changed calls to png_destroy_write_struct to follow + http://www.libpng.org/pub/png/libpng-manual.txt. + This ensures the info_ptr memory is released. + */ + + } catch (...) { + if (fp) fclose(fp); + delete [] row_pointers; + if (png_ptr && info_ptr) png_destroy_write_struct(&png_ptr, &info_ptr); + throw; } - // this a a color image! - sig_bit.gray = 0; - sig_bit.red = 8; - sig_bit.green = 8; - sig_bit.blue = 8; - /* if the image has an alpha channel then */ - sig_bit.alpha = 8; - png_set_sBIT(png_ptr, info_ptr, &sig_bit); - - png_write_info(png_ptr, info_ptr); - png_write_image(png_ptr, row_pointers); - png_write_end(png_ptr, info_ptr); - - /* Changed calls to png_destroy_write_struct to follow - http://www.libpng.org/pub/png/libpng-manual.txt. - This ensures the info_ptr memory is released. - */ - png_destroy_write_struct(&png_ptr, &info_ptr); - delete [] row_pointers; - - if (fpclose) fclose(fp); - + if (fp) fclose(fp); + return Py::Object(); } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |