diff --git a/doc/users/whats_new.rst b/doc/users/whats_new.rst index b45bacdf9a9a..b87bc11ce098 100644 --- a/doc/users/whats_new.rst +++ b/doc/users/whats_new.rst @@ -148,6 +148,13 @@ default quality is 95; previously, the default quality was 75. This change minimizes the artifacting inherent in JPEG images, particularly with images that have sharp changes in color as plots often do. +Full control of the background color +------------------------------------ +Wes Campaigne and Phil Elson fixed the Agg backend such that PNGs are now +saved with the correct background color when :meth:`fig.patch.get_alpha` is +not 1. + + .. _whats-new-1-2-2: new in matplotlib 1.2.2 diff --git a/lib/matplotlib/backends/backend_agg.py b/lib/matplotlib/backends/backend_agg.py index d7a81b7e8f07..865a8fd0013f 100644 --- a/lib/matplotlib/backends/backend_agg.py +++ b/lib/matplotlib/backends/backend_agg.py @@ -16,9 +16,8 @@ TODO: - * allow save to file handle - * integrate screen dpi w/ ppi and text + """ from __future__ import division import threading @@ -438,7 +437,7 @@ def draw(self): """ if __debug__: verbose.report('FigureCanvasAgg.draw', 'debug-annoying') - self.renderer = self.get_renderer() + self.renderer = self.get_renderer(cleared=True) # acquire a lock on the shared font cache RendererAgg.lock.acquire() @@ -449,7 +448,7 @@ def draw(self): - def get_renderer(self): + def get_renderer(self, cleared=False): l, b, w, h = self.figure.bbox.bounds key = w, h, self.figure.dpi try: self._lastKey, self.renderer @@ -459,6 +458,8 @@ def get_renderer(self): if need_new_renderer: self.renderer = RendererAgg(w, h, self.figure.dpi) self._lastKey = key + elif cleared: + self.renderer.clear() return self.renderer def tostring_rgb(self): diff --git a/lib/matplotlib/backends/backend_pdf.py b/lib/matplotlib/backends/backend_pdf.py index 6b688cee8b10..bc1e09e34ab3 100644 --- a/lib/matplotlib/backends/backend_pdf.py +++ b/lib/matplotlib/backends/backend_pdf.py @@ -436,7 +436,7 @@ def __init__(self, filename): revision = '' self.infoDict = { - 'Creator': 'matplotlib %s, http://matplotlib.sf.net' % __version__, + 'Creator': 'matplotlib %s, http://matplotlib.org' % __version__, 'Producer': 'matplotlib pdf backend%s' % revision, 'CreationDate': datetime.today() } diff --git a/lib/matplotlib/tests/baseline_images/test_figure/alpha_background.png b/lib/matplotlib/tests/baseline_images/test_figure/alpha_background.png new file mode 100644 index 000000000000..cc3ccda25dac Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_figure/alpha_background.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_figure/alpha_background.svg b/lib/matplotlib/tests/baseline_images/test_figure/alpha_background.svg new file mode 100644 index 000000000000..f59b35d81258 --- /dev/null +++ b/lib/matplotlib/tests/baseline_images/test_figure/alpha_background.svg @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/test_agg.py b/lib/matplotlib/tests/test_agg.py index 7caf5c5fc622..1be2e7fb5292 100644 --- a/lib/matplotlib/tests/test_agg.py +++ b/lib/matplotlib/tests/test_agg.py @@ -1,6 +1,50 @@ from __future__ import print_function import os +import tempfile + + +from numpy.testing import assert_array_almost_equal + + +from matplotlib.image import imread +from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas +from matplotlib.figure import Figure +from matplotlib.testing.decorators import cleanup + + +@cleanup +def test_repeated_save_with_alpha(): + # We want an image which has a background color of bluish green, with an + # alpha of 0.25. + + fig = Figure([1, 0.4]) + canvas = FigureCanvas(fig) + fig.set_facecolor((0, 1, 0.4)) + fig.patch.set_alpha(0.25) + + # The target color is fig.patch.get_facecolor() + + _, img_fname = tempfile.mkstemp(suffix='.png') + try: + fig.savefig(img_fname, + facecolor=fig.get_facecolor(), + edgecolor='none') + + # Save the figure again to check that the + # colors don't bleed from the previous renderer. + fig.savefig(img_fname, + facecolor=fig.get_facecolor(), + edgecolor='none') + + # Check the first pixel has the desired color & alpha + # (approx: 0, 1.0, 0.4, 0.25) + assert_array_almost_equal(tuple(imread(img_fname)[0, 0]), + (0.0, 1.0, 0.4, 0.250), + decimal=3) + finally: + os.remove(img_fname) + def report_memory(i): pid = os.getpid() @@ -64,3 +108,8 @@ def report_memory(i): ## # w/o text and w/o write_png: Average memory consumed per loop: 0.02 ## # w/o text and w/ write_png : Average memory consumed per loop: 0.3400 ## # w/ text and w/ write_png : Average memory consumed per loop: 0.32 + + +if __name__ == "__main__": + import nose + nose.runmodule(argv=['-s', '--with-doctest'], exit=False) diff --git a/lib/matplotlib/tests/test_figure.py b/lib/matplotlib/tests/test_figure.py index f6f033feef0b..6aefb95ca62f 100644 --- a/lib/matplotlib/tests/test_figure.py +++ b/lib/matplotlib/tests/test_figure.py @@ -74,6 +74,25 @@ def test_suptitle(): fig.suptitle('title', color='g', rotation='30') +@image_comparison(baseline_images=['alpha_background'], + # only test png and svg. The PDF output appears correct, + # but Ghostscript does not preserve the background color. + extensions=['png', 'svg'], + savefig_kwarg={'facecolor': (0, 1, 0.4), 'edgecolor': 'none'}) +def test_alpha(): + # We want an image which has a background color and an + # alpha of 0.4. + fig = plt.figure(figsize=[2, 1]) + fig.set_facecolor((0, 1, 0.4)) + fig.patch.set_alpha(0.4) + + import matplotlib.patches as mpatches + fig.patches.append(mpatches.CirclePolygon([20, 20], + radius=15, + alpha=0.6, + facecolor='red')) + + if __name__ == "__main__": import nose nose.runmodule(argv=['-s', '--with-doctest'], exit=False) diff --git a/src/_backend_agg.cpp b/src/_backend_agg.cpp index fa9f93ae14eb..96ddf920e3a4 100644 --- a/src/_backend_agg.cpp +++ b/src/_backend_agg.cpp @@ -411,7 +411,7 @@ RendererAgg::RendererAgg(unsigned int width, unsigned int height, double dpi, renderingBuffer.attach(pixBuffer, width, height, stride); pixFmt.attach(renderingBuffer); rendererBase.attach(pixFmt); - rendererBase.clear(agg::rgba(1, 1, 1, 0)); + rendererBase.clear(agg::rgba(0, 0, 0, 0)); rendererAA.attach(rendererBase); rendererBin.attach(rendererBase); hatchRenderingBuffer.attach(hatchBuffer, HATCH_SIZE, HATCH_SIZE, @@ -1108,7 +1108,7 @@ RendererAgg::draw_image(const Py::Tuple& args) inv_mtx.invert(); typedef agg::span_allocator color_span_alloc_type; - typedef agg::image_accessor_clip + typedef agg::image_accessor_clip image_accessor_type; typedef agg::span_interpolator_linear<> interpolator_type; typedef agg::span_image_filter_rgba_nn renderer_base; typedef agg::renderer_scanline_aa_solid renderer_aa; typedef agg::renderer_scanline_bin_solid renderer_bin; diff --git a/src/_image.cpp b/src/_image.cpp index 520c8a82aa4b..13377eeed3e7 100644 --- a/src/_image.cpp +++ b/src/_image.cpp @@ -33,7 +33,7 @@ #include "mplutils.h" -typedef agg::pixfmt_rgba32 pixfmt; +typedef agg::pixfmt_rgba32_plain pixfmt; typedef agg::pixfmt_rgba32_pre pixfmt_pre; typedef agg::renderer_base renderer_base; typedef agg::span_interpolator_linear<> interpolator_type;