@@ -575,7 +575,25 @@ Image::set_interpolation(const Py::Tuple& args) {
575575
576576}
577577
578+ static void write_png_data (png_structp png_ptr, png_bytep data, png_size_t length) {
579+ PyObject* py_file_obj = (PyObject*)png_get_io_ptr (png_ptr);
580+ PyObject* write_method = PyObject_GetAttrString (py_file_obj, " write" );
581+ PyObject* result = NULL ;
582+ if (write_method)
583+ result = PyObject_CallFunction (write_method, " s#" , data, length);
584+ Py_XDECREF (write_method);
585+ Py_XDECREF (result);
586+ }
578587
588+ static void flush_png_data (png_structp png_ptr) {
589+ PyObject* py_file_obj = (PyObject*)png_get_io_ptr (png_ptr);
590+ PyObject* flush_method = PyObject_GetAttrString (py_file_obj, " flush" );
591+ PyObject* result = NULL ;
592+ if (flush_method)
593+ result = PyObject_CallFunction (flush_method, " " );
594+ Py_XDECREF (flush_method);
595+ Py_XDECREF (result);
596+ }
579597
580598// this code is heavily adapted from the paint license, which is in
581599// the file paint.license (BSD compatible) included in this
@@ -593,79 +611,90 @@ Image::write_png(const Py::Tuple& args)
593611
594612 args.verify_length (1 );
595613
596- std::pair<agg::int8u*,bool > bufpair = _get_output_buffer ();
614+ FILE *fp = NULL ;
615+ Py::Object py_fileobj = Py::Object (args[0 ]);
616+ if (py_fileobj.isString ()) {
617+ std::string fileName = Py::String (py_fileobj);
618+ const char *file_name = fileName.c_str ();
619+ if ((fp = fopen (file_name, " wb" )) == NULL )
620+ throw Py::RuntimeError ( Printf (" Could not open file %s" , file_name).str () );
621+ }
622+ else {
623+ PyObject* write_method = PyObject_GetAttrString (py_fileobj.ptr (), " write" );
624+ if (!(write_method && PyCallable_Check (write_method))) {
625+ Py_XDECREF (write_method);
626+ throw Py::TypeError (" Object does not appear to be a path or a Python file-like object" );
627+ }
628+ Py_XDECREF (write_method);
629+ }
597630
598- std::string fileName = Py::String (args[0 ]);
599- const char *file_name = fileName.c_str ();
600- FILE *fp;
601631 png_structp png_ptr;
602632 png_infop info_ptr;
603- struct png_color_8_struct sig_bit;
633+ struct png_color_8_struct sig_bit;
604634 png_uint_32 row=0 ;
605635
606636 // todo: allocate on heap
607- png_bytep *row_pointers = new png_bytep[rowsOut];
608-
609- for (row = 0 ; row < rowsOut; ++row)
610- row_pointers[row] = bufpair.first + row * colsOut * 4 ;
611-
612- fp = fopen (file_name, " wb" );
613- if (fp == NULL ) {
614- if (bufpair.second ) delete [] bufpair.first ;
615- delete [] row_pointers;
616- throw Py::RuntimeError (Printf (" Could not open file %s" , file_name).str ());
617- }
618-
619-
620- png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, NULL , NULL , NULL );
621- if (png_ptr == NULL ) {
622- if (bufpair.second ) delete [] bufpair.first ;
623- fclose (fp);
624- delete [] row_pointers;
625- throw Py::RuntimeError (" Could not create write struct" );
626- }
627-
628- info_ptr = png_create_info_struct (png_ptr);
629- if (info_ptr == NULL ) {
630- if (bufpair.second ) delete [] bufpair.first ;
631- fclose (fp);
637+ png_bytep *row_pointers = NULL ;
638+ std::pair<agg::int8u*,bool > bufpair;
639+ bufpair.first = NULL ;
640+ bufpair.second = false ;
641+
642+ try {
643+ row_pointers = new png_bytep[rowsOut];
644+ if (!row_pointers)
645+ throw Py::RuntimeError (" Out of memory" );
646+
647+ bufpair = _get_output_buffer ();
648+ for (row = 0 ; row < rowsOut; ++row)
649+ row_pointers[row] = bufpair.first + row * colsOut * 4 ;
650+
651+ png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, NULL , NULL , NULL );
652+ if (png_ptr == NULL )
653+ throw Py::RuntimeError (" Could not create write struct" );
654+
655+ info_ptr = png_create_info_struct (png_ptr);
656+ if (info_ptr == NULL )
657+ throw Py::RuntimeError (" Could not create info struct" );
658+
659+ if (setjmp (png_ptr->jmpbuf ))
660+ throw Py::RuntimeError (" Error building image" );
661+
662+ if (fp) {
663+ png_init_io (png_ptr, fp);
664+ } else {
665+ png_set_write_fn (png_ptr, (void *)py_fileobj.ptr (),
666+ &write_png_data, &flush_png_data);
667+ }
668+ png_set_IHDR (png_ptr, info_ptr,
669+ colsOut, rowsOut, 8 ,
670+ PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE,
671+ PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
672+
673+ // this a a color image!
674+ sig_bit.gray = 0 ;
675+ sig_bit.red = 8 ;
676+ sig_bit.green = 8 ;
677+ sig_bit.blue = 8 ;
678+ /* if the image has an alpha channel then */
679+ sig_bit.alpha = 8 ;
680+ png_set_sBIT (png_ptr, info_ptr, &sig_bit);
681+
682+ png_write_info (png_ptr, info_ptr);
683+ png_write_image (png_ptr, row_pointers);
684+ png_write_end (png_ptr, info_ptr);
632685 png_destroy_write_struct (&png_ptr, &info_ptr);
633- delete [] row_pointers;
634- throw Py::RuntimeError (" Could not create info struct" );
635- }
636-
637- if (setjmp (png_ptr->jmpbuf )) {
686+ } catch (...) {
638687 if (bufpair.second ) delete [] bufpair.first ;
639- fclose (fp);
688+ if (fp) fclose (fp);
640689 png_destroy_write_struct (&png_ptr, &info_ptr);
641690 delete [] row_pointers;
642- throw Py::RuntimeError ( " Error building image " ) ;
691+ throw ;
643692 }
644693
645- png_init_io (png_ptr, fp);
646- png_set_IHDR (png_ptr, info_ptr,
647- colsOut, rowsOut, 8 ,
648- PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE,
649- PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
650-
651- // this a a color image!
652- sig_bit.gray = 0 ;
653- sig_bit.red = 8 ;
654- sig_bit.green = 8 ;
655- sig_bit.blue = 8 ;
656- /* if the image has an alpha channel then */
657- sig_bit.alpha = 8 ;
658- png_set_sBIT (png_ptr, info_ptr, &sig_bit);
659-
660- png_write_info (png_ptr, info_ptr);
661- png_write_image (png_ptr, row_pointers);
662- png_write_end (png_ptr, info_ptr);
663- png_destroy_write_struct (&png_ptr, &info_ptr);
664- fclose (fp);
665-
694+ if (fp) fclose (fp);
666695 delete [] row_pointers;
667-
668696 if (bufpair.second ) delete [] bufpair.first ;
697+
669698 return Py::Object ();
670699}
671700
0 commit comments