8000 Merge consecutive rasterizations · matplotlib/matplotlib@2ab32eb · GitHub
[go: up one dir, main page]

Skip to content

Commit 2ab32eb

Browse files
committed
Merge consecutive rasterizations
In vector output it is possible to flag artists to be rasterized. In many cases with multiple rasterized objects there can be significant file size savings by combining the rendered bitmaps into a single bitmap. This is achieved by moving the depth tracking logic from start_rasterizing() and stop_rasterizing() functions into the allow_rasterization() wrapper. This allows delaying the call to stop_rasterizing() until we are about to draw an non rasterized artist. stop_rasterizing() must also be called from the finalize() method. This fixes #17149
1 parent b94812c commit 2ab32eb

File tree

3 files changed

+46
-36
lines changed

3 files changed

+46
-36
lines changed

lib/matplotlib/artist.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,17 @@ def allow_rasterization(draw):
3434
def draw_wrapper(artist, renderer, *args, **kwargs):
3535
try:
3636
if artist.get_rasterized():
37-
renderer.start_rasterizing()
37+
if renderer._raster_depth == 0 and not renderer._rasterizing:
38+
renderer.start_rasterizing()
39+
renderer._rasterizing = True
40+
renderer._raster_depth += 1
41+
else:
42+
if renderer._raster_depth == 0 and renderer._rasterizing:
43+
# Only stop when we are not in a rasterized parent
44+
# and something has be rasterized since last stop
45+
renderer.stop_rasterizing()
46+
renderer._rasterizing = False
47+
3848
if artist.get_agg_filter() is not None:
3949
renderer.start_filter()
4050

@@ -43,7 +53,7 @@ def draw_wrapper(artist, renderer, *args, **kwargs):
4353
if artist.get_agg_filter() is not None:
4454
renderer.stop_filter(artist.get_agg_filter())
4555
if artist.get_rasterized():
46-
renderer.stop_rasterizing()
56+
renderer._raster_depth -= 1
4757

4858
draw_wrapper._supports_rasterization = True
4959
return draw_wrapper

lib/matplotlib/backend_bases.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,8 @@ def __init__(self):
142142
super().__init__()
143143
self._texmanager = None
144144
self._text2path = textpath.TextToPath()
145+
self._raster_depth = 0
146+
self._rasterizing = False
145147

146148
def open_group(self, s, gid=None):
147149
"""

lib/matplotlib/backends/backend_mixed.py

Lines changed: 32 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@ def __init__(self, figure, width, height, dpi, vector_renderer,
5252
self._vector_renderer = vector_renderer
5353

5454
self._raster_renderer = None
55-
self._rasterizing = 0
5655

5756
# A reference to the figure is needed as we need to change
5857
# the figure dpi before and after the rasterization. Although
@@ -84,50 +83,49 @@ def start_rasterizing(self):
8483
r = process_figure_for_rasterizing(self.figure,
8584
self._bbox_inches_restore)
8685
self._bbox_inches_restore = r
87-
if self._rasterizing == 0:
88-
self._raster_renderer = self._raster_renderer_class(
89-
self._width*self.dpi, self._height*self.dpi, self.dpi)
90-
self._renderer = self._raster_renderer
91-
self._rasterizing += 1
86+
87+
self._raster_renderer = self._raster_renderer_class(
88+
self._width*self.dpi, self._height*self.dpi, self.dpi)
89+
self._renderer = self._raster_renderer
9290

9391
def stop_rasterizing(self):
9492
"""
9593
Exit "raster" mode. All of the drawing that was done since
9694
the last `start_rasterizing` call will be copied to the
9795
vector backend by calling draw_image.
98-
99-
If `start_rasterizing` has been called multiple times,
100-
`stop_rasterizing` must be called the same number of times before
101-
"raster" mode is exited.
10296
"""
103-
self._rasterizing -= 1
104-
if self._rasterizing == 0:
105-
self._renderer = self._vector_renderer
106-
107-
height = self._height * self.dpi
108-
buffer, bounds = self._raster_renderer.tostring_rgba_minimized()
109-
l, b, w, h = bounds
110-
if w > 0 and h > 0:
111-
image = np.frombuffer(buffer, dtype=np.uint8)
112-
image = image.reshape((h, w, 4))
113-
image = image[::-1]
114-
gc = self._renderer.new_gc()
115-
# TODO: If the mixedmode resolution differs from the figure's
116-
# dpi, the image must be scaled (dpi->_figdpi). Not all
117-
# backends support this.
118-
self._renderer.draw_image(
119-
gc,
120-
l * self._figdpi / self.dpi,
121-
(height-b-h) * self._figdpi / self.dpi,
122-
image)
123-
self._raster_renderer = None
124-
self._rasterizing = False
12597

126-
# restore the figure dpi.
127-
self.figure.set_dpi(self._figdpi)
98+
self._renderer = self._vector_renderer
99+
100+
height = self._height * self.dpi
101+
buffer, bounds = self._raster_renderer.tostring_rgba_minimized()
102+
l, b, w, h = bounds
103+
if w > 0 and h > 0:
104+
image = np.frombuffer(buffer, dtype=np.uint8)
105+
image = image.reshape((h, w, 4))
106+
image = image[::-1]
107+
gc = self._renderer.new_gc()
108+
# TODO: If the mixedmode resolution differs from the figure's
109+
# dpi, the image must be scaled (dpi->_figdpi). Not all
110+
# backends support this.
111+
self._renderer.draw_image(
112+
gc,
113+
l * self._figdpi / self.dpi,
114+
(height-b-h) * self._figdpi / self.dpi,
115+
image)
116+
self._raster_renderer = None
117+
118+
# restore the figure dpi.
119+
self.figure.set_dpi(self._figdpi)
128120

129121
if self._bbox_inches_restore: # when tight bbox is used
130122
r = process_figure_for_rasterizing(self.figure,
131123
self._bbox_inches_restore,
132124
self._figdpi)
133125
self._bbox_inches_restore = r
126+
127+
def finalize(self):
128+
if self._rasterizing:
129+
self.stop_rasterizing()
130+
self._rasterizing = False
131+
self._renderer.finalize()

0 commit comments

Comments
 (0)
FD
0