@@ -2256,6 +2256,21 @@ RendererAgg::write_rgba(const Py::Tuple& args) {
22562256
22572257}
22582258
2259+ static void write_png_data (png_structp png_ptr, png_bytep data, png_size_t length) {
2260+ PyObject* py_file_obj = (PyObject*)png_get_io_ptr (png_ptr);
2261+ PyObject* write_method = PyObject_GetAttrString (py_file_obj, " write" );
2262+ PyObject_CallFunction (write_method, " s#" , data, length);
2263+
2264+ // MGDTODO: Check NULL on failure
2265+ }
2266+
2267+ static void flush_png_data (png_structp png_ptr) {
2268+ PyObject* py_file_obj = (PyObject*)png_get_io_ptr (png_ptr);
2269+ PyObject* flush_method = PyObject_GetAttrString (py_file_obj, " write" );
2270+ if (flush_method) {
2271+ PyObject_CallFunction (flush_method, " " );
2272+ }
2273+ }
22592274
22602275// this code is heavily adapted from the paint license, which is in
22612276// the file paint.license (BSD compatible) included in this
@@ -2267,97 +2282,96 @@ RendererAgg::write_png(const Py::Tuple& args)
22672282
22682283 args.verify_length (1 , 2 );
22692284
2270- FILE *fp;
2271- Py::Object o = Py::Object (args[0 ]);
2272- bool fpclose = true ;
2273- if (o.isString ()) {
2274- std::string fileName = Py::String (o);
2285+ FILE *fp = NULL ;
2286+ Py::Object py_fileobj = Py::Object (args[0 ]);
2287+ if (py_fileobj.isString ()) {
2288+ std::string fileName = Py::String (py_fileobj);
22752289 const char *file_name = fileName.c_str ();
22762290 if ((fp = fopen (file_name, " wb" )) == NULL )
22772291 throw Py::RuntimeError ( Printf (" Could not open file %s" , file_name).str () );
22782292 }
22792293 else {
2280- if ((fp = PyFile_AsFile (o.ptr ())) == NULL )
2281- throw Py::TypeError (" Could not convert object to file pointer" );
2282- fpclose = false ;
2283- }
2284-
2285- png_structp png_ptr;
2286- png_infop info_ptr;
2287- struct png_color_8_struct sig_bit;
2288- png_uint_32 row;
2289-
2290- png_bytep *row_pointers = new png_bytep[height];
2291- for (row = 0 ; row < height; ++row) {
2292- row_pointers[row] = pixBuffer + row * width * 4 ;
2293- }
2294-
2295-
2296- if (fp == NULL ) {
2297- delete [] row_pointers;
2298- throw Py::RuntimeError (" Could not open file" );
2294+ if ((fp = PyFile_AsFile (py_fileobj.ptr ())) == NULL ) {
2295+ PyObject* write_method = PyObject_GetAttrString (py_fileobj.ptr (), " write" );
2296+ if (!(write_method && PyCallable_Check (write_method)))
2297+ throw Py::TypeError (" Object does not appear to be a Python file-like object" );
2298+ }
22992299 }
2300+
2301+ png_bytep *row_pointers = NULL ;
2302+ png_structp png_ptr = NULL ;
2303+ png_infop info_ptr = NULL ;
23002304
2305+ try {
2306+ struct png_color_8_struct sig_bit;
2307+ png_uint_32 row;
2308+
2309+ row_pointers = new png_bytep[height];
2310+ for (row = 0 ; row < height; ++row) {
2311+ row_pointers[row] = pixBuffer + row * width * 4 ;
2312+ }
23012313
2302- png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, NULL , NULL , NULL );
2303- if (png_ptr == NULL ) {
2304- if (fpclose) fclose (fp);
2305- delete [] row_pointers;
2306- throw Py::RuntimeError (" Could not create write struct" );
2307- }
2314+ png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, NULL , NULL , NULL );
2315+ if (png_ptr == NULL ) {
2316+ throw Py::RuntimeError (" Could not create write struct" );
2317+ }
23082318
2309- info_ptr = png_create_info_struct (png_ptr);
2310- if (info_ptr == NULL ) {
2311- if (fpclose) fclose (fp);
2312- png_destroy_write_struct (&png_ptr, &info_ptr);
2313- delete [] row_pointers;
2314- throw Py::RuntimeError (" Could not create info struct" );
2315- }
2319+ info_ptr = png_create_info_struct (png_ptr);
2320+ if (info_ptr == NULL ) {
2321+ throw Py::RuntimeError (" Could not create info struct" );
2322+ }
23162323
2317- if (setjmp (png_ptr->jmpbuf )) {
2318- if (fpclose) fclose (fp);
2319- png_destroy_write_struct (&png_ptr, &info_ptr);
2320- delete [] row_pointers;
2321- throw Py::RuntimeError (" Error building image" );
2322- }
2324+ if (setjmp (png_ptr->jmpbuf )) {
2325+ throw Py::RuntimeError (" Error building image" );
2326+ }
23232327
2324- png_init_io (png_ptr, fp);
2325- png_set_IHDR (png_ptr, info_ptr,
2326- width, height, 8 ,
2327- PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE,
2328- PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
2329-
2330- // Save the dpi of the image in the file
2331- if (args.size () == 2 ) {
2332- double dpi = Py::Float (args[1 ]);
2333- size_t dots_per_meter = (size_t )(dpi / (2.54 / 100.0 ));
2334- png_set_pHYs (png_ptr, info_ptr, dots_per_meter, dots_per_meter, PNG_RESOLUTION_METER);
2328+ if (fp) {
2329+ png_init_io (png_ptr, fp);
2330+ } else {
2331+ png_set_write_fn (png_ptr, (void *)py_fileobj.ptr (),
2332+ &write_png_data, &flush_png_data);
2333+ }
2334+ png_set_IHDR (png_ptr, info_ptr,
2335+ width, height, 8 ,
2336+ PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE,
2337+ PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
2338+
2339+ // Save the dpi of the image in the file
2340+ if (args.size () == 2 ) {
2341+ double dpi = Py::Float (args[1 ]);
2342+ size_t dots_per_meter = (size_t )(dpi / (2.54 / 100.0 ));
2343+ png_set_pHYs (png_ptr, info_ptr, dots_per_meter, dots_per_meter, PNG_RESOLUTION_METER);
2344+ }
2345+
2346+ // this a a color image!
2347+ sig_bit.gray = 0 ;
2348+ sig_bit.red = 8 ;
2349+ sig_bit.green = 8 ;
2350+ sig_bit.blue = 8 ;
2351+ /* if the image has an alpha channel then */
2352+ sig_bit.alpha = 8 ;
2353+ png_set_sBIT (png_ptr, info_ptr, &sig_bit);
2354+
2355+ png_write_info (png_ptr, info_ptr);
2356+ png_write_image (png_ptr, row_pointers);
2357+ png_write_end (png_ptr, info_ptr);
2358+
2359+ /* Changed calls to png_destroy_write_struct to follow
2360+ http://www.libpng.org/pub/png/libpng-manual.txt.
2361+ This ensures the info_ptr memory is released.
2362+ */
2363+
2364+ } catch (...) {
2365+ if (fp) fclose (fp);
2366+ delete [] row_pointers;
2367+ if (png_ptr && info_ptr) png_destroy_write_struct (&png_ptr, &info_ptr);
2368+ throw ;
23352369 }
23362370
2337- // this a a color image!
2338- sig_bit.gray = 0 ;
2339- sig_bit.red = 8 ;
2340- sig_bit.green = 8 ;
2341- sig_bit.blue = 8 ;
2342- /* if the image has an alpha channel then */
2343- sig_bit.alpha = 8 ;
2344- png_set_sBIT (png_ptr, info_ptr, &sig_bit);
2345-
2346- png_write_info (png_ptr, info_ptr);
2347- png_write_image (png_ptr, row_pointers);
2348- png_write_end (png_ptr, info_ptr);
2349-
2350- /* Changed calls to png_destroy_write_struct to follow
2351- http://www.libpng.org/pub/png/libpng-manual.txt.
2352- This ensures the info_ptr memory is released.
2353- */
2354-
23552371 png_destroy_write_struct (&png_ptr, &info_ptr);
2356-
23572372 delete [] row_pointers;
2358-
2359- if (fpclose) fclose (fp);
2360-
2373+ if (fp) fclose (fp);
2374+
23612375 return Py::Object ();
23622376}
23632377
0 commit comments