@@ -295,6 +295,15 @@ def _adjust_frame_size(self):
295295 _log .debug ('frame size in pixels is %s x %s' , * self .frame_size )
296296 return w , h
297297
298+ @property
299+ def _supports_transparency (self ):
300+ """
301+ Whether this writer supports transparency.
302+
303+ Writers may consult output file type and codec to determine this at runtime.
304+ """
305+ return False
306+
298307 def setup (self , fig , outfile , dpi = None ):
299308 # docstring inherited
300309 super ().setup (fig , outfile , dpi = dpi )
@@ -468,6 +477,10 @@ def finish(self):
468477
469478@writers .register ('pillow' )
470479class PillowWriter (AbstractMovieWriter ):
480+ @property
481+ def _supports_transparency (self ):
482+ return True
483+
471484 @classmethod
472485 def isAvailable (cls ):
473486 return True
@@ -503,6 +516,15 @@ class FFMpegBase:
503516 _exec_key = 'animation.ffmpeg_path'
504517 _args_key = 'animation.ffmpeg_args'
505518
519+ @property
520+ def _supports_transparency (self ):
521+ suffix = Path (self .outfile ).suffix
522+ if suffix in {'.apng' , '.avif' , '.gif' , '.webm' , '.webp' }:
523+ return True
524+ elif suffix == '.mov' :
525+ return self .codec == 'png'
526+ return False
527+
506528 @property
507529 def output_args (self ):
508530 args = []
@@ -519,11 +541,17 @@ def output_args(self):
519541 # macOS). Also fixes internet explorer. This is as of 2015/10/29.
520542 if self .codec == 'h264' and '-pix_fmt' not in extra_args :
521543 args .extend (['-pix_fmt' , 'yuv420p' ])
522- # For GIF, we're telling FFMPEG to split the video stream, to generate
544+ # For GIF, we're telling FFmpeg to split the video stream, to generate
523545 # a palette, and then use it for encoding.
524546 elif self .codec == 'gif' and '-filter_complex' not in extra_args :
525547 args .extend (['-filter_complex' ,
526548 'split [a][b];[a] palettegen [p];[b][p] paletteuse' ])
549+ # For AVIF, we're telling FFmpeg to split the video stream, extract the alpha,
550+ # in order to place it in a secondary stream, as needed by AVIF-in-FFmpeg.
551+ elif self .codec == 'avif' and '-filter_complex' not in extra_args :
552+ args .extend (['-filter_complex' ,
553+ 'split [rgb][rgba]; [rgba] alphaextract [alpha]' ,
554+ '-map' , '[rgb]' , '-map' , '[alpha]' ])
527555 if self .bitrate > 0 :
528556 args .extend (['-b' , '%dk' % self .bitrate ]) # %dk: bitrate in kbps.
529557 for k , v in self .metadata .items ():
@@ -611,6 +639,11 @@ class ImageMagickBase:
611639 _exec_key = 'animation.convert_path'
612640 _args_key = 'animation.convert_args'
613641
642+ @property
643+ def _supports_transparency (self ):
644+ suffix = Path (self .outfile ).suffix
645+ return suffix in {'.apng' , '.avif' , '.gif' , '.webm' , '.webp' }
646+
614647 def _args (self ):
615648 # ImageMagick does not recognize "raw".
616649 fmt = "rgba" if self .frame_format == "raw" else self .frame_format
@@ -1046,22 +1079,23 @@ def func(current_frame: int, total_frames: int) -> Any
10461079 # since GUI widgets are gone. Either need to remove extra code to
10471080 # allow for this non-existent use case or find a way to make it work.
10481081
1049- facecolor = savefig_kwargs .get ('facecolor' ,
1050- mpl .rcParams ['savefig.facecolor' ])
1051- if facecolor == 'auto' :
1052- facecolor = self ._fig .get_facecolor ()
1053-
10541082 def _pre_composite_to_white (color ):
10551083 r , g , b , a = mcolors .to_rgba (color )
10561084 return a * np .array ([r , g , b ]) + 1 - a
10571085
1058- savefig_kwargs ['facecolor' ] = _pre_composite_to_white (facecolor )
1059- savefig_kwargs ['transparent' ] = False # just to be safe!
10601086 # canvas._is_saving = True makes the draw_event animation-starting
10611087 # callback a no-op; canvas.manager = None prevents resizing the GUI
10621088 # widget (both are likewise done in savefig()).
10631089 with (writer .saving (self ._fig , filename , dpi ),
10641090 cbook ._setattr_cm (self ._fig .canvas , _is_saving = True , manager = None )):
1091+ if not getattr (writer , '_supports_transparency' , False ):
1092+ facecolor = savefig_kwargs .get ('facecolor' ,
1093+ mpl .rcParams ['savefig.facecolor' ])
1094+ if facecolor == 'auto' :
1095+ facecolor = self ._fig .get_facecolor ()
1096+ savefig_kwargs ['facecolor' ] = _pre_composite_to_white (facecolor )
1097+ savefig_kwargs ['transparent' ] = False # just to be safe!
1098+
10651099 for anim in all_anim :
10661100 anim ._init_draw () # Clear the initial frame
10671101 frame_number = 0
0 commit comments