@@ -408,6 +408,26 @@ def _constrain_fig_resolution(fig, *, max_width, max_res):
408408 fig .set_dpi (dpi )
409409
410410
411+ def _compress_img (img , image_format , dpi ):
412+ """Drop the alpha channel and compress, for space and to avoid rendering issues."""
413+ from PIL import Image
414+
415+ # https://pillow.readthedocs.io/en/stable/handbook/image-file-formats.html
416+ pil_kwargs = dict ()
417+ if image_format == "webp" :
418+ # Here quality means speed/size tradeoff (either way the result is lossless);
419+ # 20 rather than 50 encodes ~1.5x faster for a few percent more bytes
420+ pil_kwargs .update (lossless = True , quality = 20 )
421+ else :
422+ assert image_format == "png" , image_format
423+ pil_kwargs .update (optimize = True , compress_level = 9 )
424+ background = Image .new ("RGBA" , img .size , (255 , 255 , 255 ))
425+ img = Image .alpha_composite (background , img ).convert ("RGB" )
426+ output = BytesIO ()
427+ img .save (output , format = image_format , dpi = (dpi , dpi ), ** pil_kwargs )
428+ return output
429+
430+
411431def _fig_to_img (
412432 fig ,
413433 * ,
@@ -425,10 +445,22 @@ def _fig_to_img(
425445 if isinstance (fig , np .ndarray ):
426446 # In this case, we are creating the fig, so we might as well
427447 # auto-close in all cases
428- fig = _ndarray_to_fig (fig )
448+ img , fig = fig , _ndarray_to_fig (fig )
449+ dpi = fig .get_dpi ()
429450 if own_figure :
430451 _constrain_fig_resolution (fig , max_width = max_width , max_res = max_res )
431452 own_figure = True # close the figure we just created
453+ if fig .get_dpi () == dpi and image_format in ("png" , "webp" ):
454+ # Nothing rescaled the pixels, so compress them as they are rather than
455+ # rendering them back through the figure only to read them out again
456+ from PIL import Image
457+
458+ plt .close (fig )
459+ if img .dtype .kind == "f" : # float in [0, 1], as _fig_to_img returns
460+ img = np .clip (img , 0 , 1 ) * 255
461+ img = Image .fromarray (img .astype (np .uint8 )).convert ("RGBA" )
462+ output = _compress_img (img , image_format , dpi )
463+ return base64 .b64encode (output .getvalue ()).decode ("ascii" )
432464 elif isinstance (fig , Figure ):
433465 if own_figure :
434466 _constrain_fig_resolution (fig , max_width = max_width , max_res = max_res )
@@ -484,20 +516,10 @@ def _fig_to_img(
484516 if image_format not in ("svg" , "ndarray" ):
485517 from PIL import Image
486518
487- # https://pillow.readthedocs.io/en/stable/handbook/image-file-formats.html
488- pil_kwargs = dict ()
489- if image_format == "webp" :
490- # Here quality means speed/size tradeoff (either way the result is lossless)
491- pil_kwargs .update (lossless = True , quality = 50 )
492- elif image_format == "png" :
493- pil_kwargs .update (optimize = True , compress_level = 9 )
494519 output .seek (0 )
495520 orig = Image .open (output )
496521 if orig .mode == "RGBA" :
497- background = Image .new ("RGBA" , orig .size , (255 , 255 , 255 ))
498- new = Image .alpha_composite (background , orig ).convert ("RGB" )
499- output = BytesIO ()
500- new .save (output , format = image_format , dpi = (dpi , dpi ), ** pil_kwargs )
522+ output = _compress_img (orig , image_format , dpi )
501523
502524 if image_format == "ndarray" :
503525 # float in [0, 1], like the PNG this used to go through
0 commit comments