From bd3ff10d72b8c89b3c63e3a00c68f058a2e36ec8 Mon Sep 17 00:00:00 2001 From: Bruno Beltran Date: Sat, 18 Apr 2020 03:19:42 -0700 Subject: [PATCH] BF: MixedModeRenderer scale rasters to "true" bbox Co-authored-by: Jody Klymak Co-authored-by: Thomas A Caswell --- lib/matplotlib/backends/backend_mixed.py | 111 +- lib/matplotlib/backends/backend_pdf.py | 13 +- lib/matplotlib/backends/backend_pgf.py | 8 +- lib/matplotlib/backends/backend_ps.py | 23 +- lib/matplotlib/backends/backend_svg.py | 25 +- .../baseline_images/test_axes/hist2d.pdf | Bin 3422 -> 3409 bytes .../baseline_images/test_axes/hist2d.svg | 28 +- .../test_axes/hist2d_transpose.pdf | Bin 3176 -> 3163 bytes .../test_axes/hist2d_transpose.svg | 28 +- .../test_backend_pgf/pgf_mixedmode.pdf | Bin 9822 -> 11056 bytes .../test_backend_ps/colorbar_raster.eps | 1242 +++++++++++++ .../bbox_inches_tight_raster.pdf | Bin 7245 -> 7244 bytes .../bbox_inches_tight_raster.svg | 62 +- .../constrained_layout4.svg | 556 +++--- .../test_image/rasterize_10dpi.pdf | Bin 2425 -> 2418 bytes .../test_image/rasterize_10dpi.svg | 18 +- .../test_streamplot/streamplot_colormap.pdf | Bin 16319 -> 16107 bytes .../test_streamplot/streamplot_colormap.svg | 1544 ++++++++--------- lib/matplotlib/tests/test_backend_ps.py | 13 + 19 files changed, 2499 insertions(+), 1172 deletions(-) create mode 100644 lib/matplotlib/tests/baseline_images/test_backend_ps/colorbar_raster.eps diff --git a/lib/matplotlib/backends/backend_mixed.py b/lib/matplotlib/backends/backend_mixed.py index 5fadb96a0f73..941aea314cc9 100644 --- a/lib/matplotlib/backends/backend_mixed.py +++ b/lib/matplotlib/backends/backend_mixed.py @@ -1,8 +1,9 @@ import numpy as np from matplotlib import cbook -from .backend_agg import RendererAgg from matplotlib._tight_bbox import process_figure_for_rasterizing +from matplotlib.backends.backend_agg import RendererAgg +from matplotlib.transforms import Bbox, Affine2D, IdentityTransform class MixedModeRenderer: @@ -68,6 +69,71 @@ def __getattr__(self, attr): # to the underlying C implementation). return getattr(self._renderer, attr) + # need to wrap each drawing function that might be called on the rasterized + # version of the renderer to save what the "true" bbox is for scaling the + # output correctly + # the functions we might want to overwrite are: + # `draw_path`, `draw_image`, `draw_gouraud_triangle`, `draw_text`, + # `draw_markers`, `draw_path_collection`, `draw_quad_mesh` + + def _update_true_bbox(self, bbox, transform=None): + """Convert to real units and update""" + if transform is None: + transform = IdentityTransform() + bbox = bbox.transformed(transform + Affine2D().scale( + self._figdpi / self.dpi)) + if self._true_bbox is None: + self._true_bbox = bbox + else: + self._true_bbox = Bbox.union([self._true_bbox, bbox]) + + def draw_path(self, gc, path, transform, rgbFace=None): + if self._rasterizing > 0: + bbox = Bbox.null() + bbox.update_from_path(path, ignore=True) + self._update_true_bbox(bbox, transform) + return self._renderer.draw_path(gc, path, transform, rgbFace) + + def draw_path_collection(self, gc, master_transform, paths, all_transforms, + offsets, offsetTrans, facecolors, edgecolors, + linewidths, linestyles, antialiaseds, urls, + offset_position): + if self._rasterizing > 0: + bbox = Bbox.null() + # TODO probably faster to merge all coordinates from path using + # numpy for large lists of paths, such as the one produced by the + # test case tests/test_backed_pgf.py:test_mixed_mode + for path in paths: + bbox.update_from_path(path, ignore=False) + self._update_true_bbox(bbox, master_transform) + return self._renderer.draw_path_collection( + gc, master_transform, paths, all_transforms, offsets, + offsetTrans, facecolors, edgecolors, linewidths, linestyles, + antialiaseds, urls, offset_position) + + def draw_quad_mesh(self, gc, master_transform, meshWidth, meshHeight, + coordinates, offsets, offsetTrans, facecolors, + antialiased, edgecolors): + if self._rasterizing > 0: + # TODO should check if this is always Bbox.unit for efficiency + bbox = Bbox.null() + cshape = coordinates.shape + flat_coords = coordinates.reshape((cshape[0]*cshape[1], cshape[2])) + bbox.update_from_data_xy(flat_coords, ignore=True) + self._update_true_bbox(bbox, master_transform) + + return self._renderer.draw_quad_mesh( + gc, master_transform, meshWidth, meshHeight, coordinates, + offsets, offsetTrans, facecolors, antialiased, edgecolors) + + def draw_gouraud_triangle(self, gc, points, colors, transform): + if self._rasterizing > 0: + bbox = Bbox.null() + bbox.update_from_data_xy(points, ignore=True) + self._update_true_bbox(bbox, transform) + return self._renderer.draw_gouraud_triangle( + gc, points, colors, transform) + def start_rasterizing(self): """ Enter "raster" mode. All subsequent drawing commands (until @@ -83,6 +149,7 @@ def start_rasterizing(self): self._raster_renderer = self._raster_renderer_class( self._width*self.dpi, self._height*self.dpi, self.dpi) self._renderer = self._raster_renderer + self._true_bbox = None def stop_rasterizing(self): """ @@ -92,21 +159,35 @@ def stop_rasterizing(self): """ self._renderer = self._vector_renderer - height = self._height * self.dpi - img = np.asarray(self._raster_renderer.buffer_rgba()) - slice_y, slice_x = cbook._get_nonzero_slices(img[..., 3]) - cropped_img = img[slice_y, slice_x] - if cropped_img.size: - gc = self._renderer.new_gc() - # TODO: If the mixedmode resolution differs from the figure's - # dpi, the image must be scaled (dpi->_figdpi). Not all - # backends support this. - self._renderer.draw_image( - gc, - slice_x.start * self._figdpi / self.dpi, - (height - slice_y.stop) * self._figdpi / self.dpi, - cropped_img[::-1]) + # these bounds are in pixels, relative to the figure when pixelated + # at the requested DPI. However, the vectorized backends draw at a + # fixed DPI of 72, and typically aren't snapped to the + # requested-DPI pixel grid, so we have to grab the actual bounds to + # put the image into some other way + if self._true_bbox is not None: + # raise NotImplementedError( + # "Something was drawn using a method not wrapped by " + # "MixedModeRenderer.") + img = np.asarray(self._raster_renderer.buffer_rgba()) + slice_y, slice_x = cbook._get_nonzero_slices(img[..., 3]) + cropped_img = img[slice_y, slice_x] + if cropped_img.size: + gc = self._renderer.new_gc() + # TODO: If the mixedmode resolution differs from the figure's + # dpi, the image must be scaled (dpi->_figdpi). Not all + # backends support this. + # because rasterizing will have rounded size to nearest + # pixel, we need to rescale our drawing to fit the original + # intended Bbox. This results in a slightly different DPI than + # requested, but that's better than the drawing not fitting + # into the space requested, see Issue #6827 + + self._renderer.draw_image( + gc, self._true_bbox.x0, self._true_bbox.y0, cropped_img[::-1], + true_size=(self._true_bbox.width, self._true_bbox.height) + ) + self._raster_renderer = None # restore the figure dpi. diff --git a/lib/matplotlib/backends/backend_pdf.py b/lib/matplotlib/backends/backend_pdf.py index d71b8dc7aa43..642a6c5c60e3 100644 --- a/lib/matplotlib/backends/backend_pdf.py +++ b/lib/matplotlib/backends/backend_pdf.py @@ -1980,21 +1980,28 @@ def check_gc(self, gc, fillcolor=None): def get_image_magnification(self): return self.image_dpi/72.0 - def draw_image(self, gc, x, y, im, transform=None): + def option_true_bbox_image(self): + return True + + def draw_image(self, gc, x, y, im, transform=None, true_size=None): # docstring inherited h, w = im.shape[:2] if w == 0 or h == 0: return + if true_size is not None: + w, h = true_size + if transform is None: # If there's no transform, alpha has already been applied gc.set_alpha(1.0) self.check_gc(gc) - w = 72.0 * w / self.image_dpi - h = 72.0 * h / self.image_dpi + if true_size is None: + w = 72.0 * w / self.image_dpi + h = 72.0 * h / self.image_dpi imob = self.file.imageObject(im) diff --git a/lib/matplotlib/backends/backend_pgf.py b/lib/matplotlib/backends/backend_pgf.py index b5e3dcc5cd87..88a3f68e0f13 100644 --- a/lib/matplotlib/backends/backend_pgf.py +++ b/lib/matplotlib/backends/backend_pgf.py @@ -605,13 +605,19 @@ def option_image_nocomposite(self): # docstring inherited return not mpl.rcParams['image.composite_image'] - def draw_image(self, gc, x, y, im, transform=None): + def option_true_bbox_image(self): + return True + + def draw_image(self, gc, x, y, im, transform=None, true_size=None): # docstring inherited h, w = im.shape[:2] if w == 0 or h == 0: return + if true_size is not None: + w, h = true_size + if not os.path.exists(getattr(self.fh, "name", "")): raise ValueError( "streamed pgf-code does not support raster graphics, consider " diff --git a/lib/matplotlib/backends/backend_ps.py b/lib/matplotlib/backends/backend_ps.py index 3231ed62da9c..360d3061234b 100644 --- a/lib/matplotlib/backends/backend_ps.py +++ b/lib/matplotlib/backends/backend_ps.py @@ -424,30 +424,41 @@ def _get_clip_cmd(self, gc): clip.append(f"{custom_clip_cmd}\n") return "".join(clip) + def option_true_bbox_image(self): + return True + @_log_if_debug_on - def draw_image(self, gc, x, y, im, transform=None): + def draw_image(self, gc, x, y, im, transform=None, true_size=None): # docstring inherited h, w = im.shape[:2] + + if h == 0 or w == 0: + return + imagecmd = "false 3 colorimage" data = im[::-1, :, :3] # Vertically flipped rgb values. hexdata = data.tobytes().hex("\n", -64) # Linewrap to 128 chars. if transform is None: matrix = "1 0 0 1 0 0" - xscale = w / self.image_magnification - yscale = h / self.image_magnification + if true_size is None: + xscale = w / self.image_magnification + yscale = h / self.image_magnification + else: + xscale = true_size[0] + yscale = true_size[1] else: - matrix = " ".join(map(str, transform.frozen().to_values())) xscale = 1.0 yscale = 1.0 + matrix = " ".join(map(str, transform.frozen().to_values())) self._pswriter.write(f"""\ gsave {self._get_clip_cmd(gc)} -{x:g} {y:g} translate +{x:.2f} {y:.2f} translate [{matrix}] concat -{xscale:g} {yscale:g} scale +{xscale:.2f} {yscale:.2f} scale /DataString {w:d} string def {w:d} {h:d} 8 [ {w:d} 0 0 -{h:d} 0 {h:d} ] {{ diff --git a/lib/matplotlib/backends/backend_svg.py b/lib/matplotlib/backends/backend_svg.py index 6e016b0d1e0c..0c889dcf51bc 100644 --- a/lib/matplotlib/backends/backend_svg.py +++ b/lib/matplotlib/backends/backend_svg.py @@ -915,10 +915,13 @@ def option_scale_image(self): # docstring inherited return True + def option_true_bbox_image(self): + return True + def get_image_magnification(self): return self.image_dpi / 72.0 - def draw_image(self, gc, x, y, im, transform=None): + def draw_image(self, gc, x, y, im, transform=None, true_size=None): # docstring inherited h, w = im.shape[:2] @@ -960,12 +963,28 @@ def draw_image(self, gc, x, y, im, transform=None): w = 72.0 * w / self.image_dpi h = 72.0 * h / self.image_dpi + if true_size is not None: + width, height = true_size + # because rasterization happens only for integer pixels, the + # round-trip width w = # int(width/72*image_dpi)*72/image_dpi + # need not match the "real" width + scale_x = width/w + scale_y = height/h + real_h = height + else: + scale_x = 1 + scale_y = 1 + real_h = h + self.writer.element( 'image', transform=_generate_transform([ - ('scale', (1, -1)), ('translate', (0, -h))]), + ('translate', + (x*(1 - scale_x), y*(1 - scale_y) + real_h)), + ('scale', (scale_x, -scale_y)) + ]), x=_short_float_fmt(x), - y=_short_float_fmt(-(self.height - y - h)), + y=_short_float_fmt(-(self.height - y - real_h)), width=_short_float_fmt(w), height=_short_float_fmt(h), attrib=attrib) else: diff --git a/lib/matplotlib/tests/baseline_images/test_axes/hist2d.pdf b/lib/matplotlib/tests/baseline_images/test_axes/hist2d.pdf index 8343cc2efd97079badb86aef86bcfa9118fd42c8..1a0834b02f88212255f6d053477cb8d636b133b9 100644 GIT binary patch delta 529 zcmca7bx~@=A;$W7&AGcktnAt_61rRn2Iy;nJ9h1N~UUM-_`N|SH% z#bY5ePxq8AO;CApi^8EktE)+bj62e{f8mGZCPv1-LSa)>c-mb@%8WjeoNeZ=pW1c z7pC)B**8zR5^(TmjqN)7r1H}{{vD_`U9&ceIbF4U!i+^fgtCgWmN*oeaBC;XYo@Kd zX?`h-O*CAyv#!{AZQ8|0nyF%m%~EF0*u5w` zwEMGhsz~!v7e%q>pPEdUCMsDrx=b=SKO>MQY(^Guc9CHS!V?OTgQ0+Rp*kn$#0lsm<&xfb1}1V)|(kB7=VC6o&pz` zVPIfxX^A0bVPt?IW@(72&e8;nIxKb=n3;mjo7~T1YG;nc97|(#`;4%dZERpeY diff --git a/lib/matplotlib/tests/baseline_images/test_axes/hist2d.svg b/lib/matplotlib/tests/baseline_images/test_axes/hist2d.svg index 025441b800db..d734ce2347e3 100644 --- a/lib/matplotlib/tests/baseline_images/test_axes/hist2d.svg +++ b/lib/matplotlib/tests/baseline_images/test_axes/hist2d.svg @@ -26,46 +26,46 @@ L 57.6 41.472 z " style="fill:#ffffff;"/> - + +" id="mcf74baa445" style="stroke:#000000;stroke-width:0.8;"/> - + - + - + - + - + @@ -76,38 +76,38 @@ L 0 3.5 +" id="mec4d43d1b1" style="stroke:#000000;stroke-width:0.8;"/> - + - + - + - + - + diff --git a/lib/matplotlib/tests/baseline_images/test_axes/hist2d_transpose.pdf b/lib/matplotlib/tests/baseline_images/test_axes/hist2d_transpose.pdf index d7a58f772a40bf899b67a7dd6681806e2f2bada0..22dbdf01e12d15b224a32ffac14f6627fbd4a8b5 100644 GIT binary patch delta 483 zcmaDMaa&@;A;x-NKW0Oo*6((${tu27{!8-=UD+gC(6UNoRi9P}=equME6+Pk5iXnN z+gYFQl?=_VREPl6`76h$MO3Q3&-1FjT+sd7e zM^d{~OO|nponp2O=LunDJ}bP;=_3D}$b~D;r?v2H6hEbxE}OZqCuu{>iYZT$kMFu< zxLz%Wt8zk@_Bx;Xv%i!#!t38V zM*ByvX8fQ0kx81_$kJppKeGgfoq>^p0SGAMDR6-q1_tKl7MNm|mKb6dmKKu(xrIeB tWh_k$Ciimt*_mP*WND6Jp^=$|A-ds4=4M8dC3)O9EljvnRbBnvxB$eExLE)I delta 532 zcmcaD@j_z5A;$XMe$0+MuGRIe`wvLo{(NhaMi7f^fYK@fra+|@rHS{C?|x$=Bycq2 zc=+*eU;8A#AK2mCUTvLTb141yK844(&hgE0b^mZNaNF&~eJlAzbc?=zWBb6>E$wY7 zxxH6J|K=SiW)!84KuNSOSOExu2zhk-i)sBeV{q^_EZ?}IHGFSiM zvb9D`=B$^Av!r)@_IArhkERuVZTy~g!)+_$Ik$HS0YTH!HXoUHC`EYMfs1pp%)Lad zJWTp}wSph<1y0vUxj#eeOMujgop&5vUo6PnG(+>mflgkoZ`Z9BI_h|xT$-YMHe`88 z#HlI8Q!Z>+X5w?ErQ41-YJ=ZP{|z=1Dnh@CW}j*~`(CTUJU6!T`f2mMcjmAD+Hibz zuhqYM8p;3eALKftS%1ADUxsxa4_9hlN^wb1YGN*km7kQwWoW2ipa5bR8<=lqVwT`Y zGc;8&00D(O1uihdz`)!Rs0vNY!q5mq%o1c8nmS8kEb2^+CpU2W*_mTeZh>LFk(s3l Yy0J#)=BAT*c-%NG&AC)nUH#p-0E~LdfB*mh diff --git a/lib/matplotlib/tests/baseline_images/test_axes/hist2d_transpose.svg b/lib/matplotlib/tests/baseline_images/test_axes/hist2d_transpose.svg index 8111cb56486a..ec558ac6523f 100644 --- a/lib/matplotlib/tests/baseline_images/test_axes/hist2d_transpose.svg +++ b/lib/matplotlib/tests/baseline_images/test_axes/hist2d_transpose.svg @@ -26,46 +26,46 @@ L 57.6 41.472 z " style="fill:#ffffff;"/> - + +" id="mee8b74cdb8" style="stroke:#000000;stroke-width:0.8;"/> - + - + - + - + - + @@ -76,38 +76,38 @@ L 0 3.5 +" id="m01ea264b27" style="stroke:#000000;stroke-width:0.8;"/> - + - + - + - + - + diff --git a/lib/matplotlib/tests/baseline_images/test_backend_pgf/pgf_mixedmode.pdf b/lib/matplotlib/tests/baseline_images/test_backend_pgf/pgf_mixedmode.pdf index fd7cf7a5c0d1849f2588809771c179c101a99a00..137f749978109c87ec956e0a9b080eab2dd011d7 100644 GIT binary patch delta 4425 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 aKYvGrKN10R0RW}sWq<%7p@)W=fPVqfsY%oT delta 3261 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 delta 544 zcmX?Oan@qPLB@JNW<#E~@0P9p6Q0>#R$c5R`ldlSBz3_nj)h(YwR4Wh?e2FK`M=TL zeo}>nB)_qR#lv6B{M9j=3s;Cu?%J#zmA)xy<+jCtFVEoLTzTjN_pvu;6PT~G#`JA7 z`R!A7hVf@{cjD|H0hRqF>~em~#w?SEO?yMH!L>GBP*uJ6(ezIQN>Sw`%J zP4$i1!`ja|KLn)*?tRhqEOzU*J6qb$9T%Rk__*zYnAMisCFh*Y?fSWl`F-`Oms+8l zFW(UMP3tc1V=0Z^uuf7%b%p$9*$O=&rpt_%by)eGx9JJ`ITctX%vt(iql$E8j8M~M z#>YCW`|E`pzviwl*I#j`$hplYyC-Pb+C8d@OSc^e5SCKbZ+w)e-&t>Vf%LY@K_m|TSTmT!j$<+V= diff --git a/lib/matplotlib/tests/baseline_images/test_bbox_tight/bbox_inches_tight_raster.svg b/lib/matplotlib/tests/baseline_images/test_bbox_tight/bbox_inches_tight_raster.svg index 505ea417de02..5d48ad02e998 100644 --- a/lib/matplotlib/tests/baseline_images/test_bbox_tight/bbox_inches_tight_raster.svg +++ b/lib/matplotlib/tests/baseline_images/test_bbox_tight/bbox_inches_tight_raster.svg @@ -1,7 +1,7 @@ - + - + +" id="mc63e59a608" style="stroke:#000000;stroke-width:0.5;"/> - + - + - + - + - + - + - + - + - + - + - + @@ -138,80 +138,80 @@ L 0 4 +" id="m556f96d829" style="stroke:#000000;stroke-width:0.5;"/> - + +" id="m27e32ca04a" style="stroke:#000000;stroke-width:0.5;"/> - + - + - + - + - + - + - + - + - + - + - + diff --git a/lib/matplotlib/tests/baseline_images/test_constrainedlayout/constrained_layout4.svg b/lib/matplotlib/tests/baseline_images/test_constrainedlayout/constrained_layout4.svg index 1f194eb09342..cd58ba633d3f 100644 --- a/lib/matplotlib/tests/baseline_images/test_constrainedlayout/constrained_layout4.svg +++ b/lib/matplotlib/tests/baseline_images/test_constrainedlayout/constrained_layout4.svg @@ -1,7 +1,7 @@ - +