2525
2626import abc
2727import contextlib
28+ from io import BytesIO
2829import itertools
2930import logging
3031import os
4344
4445if six .PY2 :
4546 from base64 import encodestring as encodebytes
46- from cStringIO import StringIO as BytesIO
4747else :
4848 from base64 import encodebytes
49- from io import BytesIO
5049
5150
5251_log = logging .getLogger (__name__ )
@@ -575,6 +574,45 @@ def cleanup(self):
575574 os .remove (fname )
576575
577576
577+ @writers .register ('pillow' )
578+ class PillowWriter (MovieWriter ):
579+ @classmethod
580+ def isAvailable (cls ):
581+ try :
582+ import PIL
583+ except ImportError :
584+ return False
585+ return True
586+
587+ def __init__ (self , * args , ** kwargs ):
588+ if kwargs .get ("extra_args" ) is None :
589+ kwargs ["extra_args" ] = ()
590+ super (PillowWriter , self ).__init__ (* args , ** kwargs )
591+
592+ def setup (self , fig , outfile , dpi = None ):
593+ self ._frames = []
594+ self ._outfile = outfile
595+ self ._dpi = dpi
596+ self ._fig = fig
597+
598+ def grab_frame (self , ** savefig_kwargs ):
599+ from PIL import Image
600+ buf = BytesIO ()
601+ self ._fig .savefig (buf , ** dict (savefig_kwargs , format = "rgba" ))
602+ renderer = self ._fig .canvas .get_renderer ()
603+ # Using frombuffer / getbuffer may be slightly more efficient, but
604+ # Py3-only.
605+ self ._frames .append (Image .frombytes (
606+ "RGBA" ,
607+ (int (renderer .width ), int (renderer .height )),
608+ buf .getvalue ()))
609+
610+ def finish (self ):
611+ self ._frames [0 ].save (
612+ self ._outfile , save_all = True , append_images = self ._frames [1 :],
613+ duration = int (1000 / self .fps ))
614+
615+
578616# Base class of ffmpeg information. Has the config keys and the common set
579617# of arguments that controls the *output* side of things.
580618class FFMpegBase (object ):
@@ -1138,8 +1176,8 @@ class to use, such as 'ffmpeg'. If ``None``, defaults to
11381176
11391177 if 'bbox_inches' in savefig_kwargs :
11401178 _log .warning ("Warning: discarding the 'bbox_inches' argument in "
1141- "'savefig_kwargs' as it may cause frame size "
1142- "to vary, which is inappropriate for animation." )
1179+ "'savefig_kwargs' as it may cause frame size "
1180+ "to vary, which is inappropriate for animation." )
11431181 savefig_kwargs .pop ('bbox_inches' )
11441182
11451183 # Create a new sequence of frames for saved data. This is different
@@ -1150,16 +1188,15 @@ class to use, such as 'ffmpeg'. If ``None``, defaults to
11501188 # allow for this non-existent use case or find a way to make it work.
11511189 with rc_context ():
11521190 if rcParams ['savefig.bbox' ] == 'tight' :
1153- _log .info ("Disabling savefig.bbox = 'tight', as it "
1154- "may cause frame size to vary, which "
1155- "is inappropriate for animation." )
1191+ _log .info ("Disabling savefig.bbox = 'tight', as it may cause "
1192+ " frame size to vary, which is inappropriate for "
1193+ " animation." )
11561194 rcParams ['savefig.bbox' ] = None
11571195 with writer .saving (self ._fig , filename , dpi ):
11581196 for anim in all_anim :
11591197 # Clear the initial frame
11601198 anim ._init_draw ()
1161- for data in zip (* [a .new_saved_frame_seq ()
1162- for a in all_anim ]):
1199+ for data in zip (* [a .new_saved_frame_seq () for a in all_anim ]):
11631200 for anim , d in zip (all_anim , data ):
11641201 # TODO: See if turning off blit is really necessary
11651202 anim ._draw_next_frame (d , blit = False )
0 commit comments