Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit efa3434

Browse files
committed
Merge pull request #3485 from jkseppan/reduce-xobjects
ENH : Reduce the use of XObjects in pdf backend
1 parent 17d1bf9 commit efa3434

File tree

8 files changed

+115
-5
lines changed

8 files changed

+115
-5
lines changed

lib/matplotlib/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1372,6 +1372,7 @@ def tk_window_focus():
13721372
'matplotlib.tests.test_artist',
13731373
'matplotlib.tests.test_axes',
13741374
'matplotlib.tests.test_axes_grid1',
1375+
'matplotlib.tests.test_backend_bases',
13751376
'matplotlib.tests.test_backend_pdf',
13761377
'matplotlib.tests.test_backend_pgf',
13771378
'matplotlib.tests.test_backend_ps',

lib/matplotlib/backend_bases.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -384,6 +384,23 @@ def _iter_collection_raw_paths(self, master_transform, paths,
384384
transform = Affine2D(all_transforms[i % Ntransforms])
385385
yield path, transform + master_transform
386386

387+
def _iter_collection_uses_per_path(self, paths, all_transforms,
388+
offsets, facecolors, edgecolors):
389+
"""
390+
Compute how many times each raw path object returned by
391+
_iter_collection_raw_paths would be used when calling
392+
_iter_collection. This is intended for the backend to decide
393+
on the tradeoff between using the paths in-line and storing
394+
them once and reusing. Rounds up in case the number of uses
395+
is not the same for every path.
396+
"""
397+
Npaths = len(paths)
398+
if Npaths == 0 or (len(facecolors) == 0 and len(edgecolors) == 0):
399+
return 0
400+
Npath_ids = max(Npaths, len(all_transforms))
401+
N = max(Npath_ids, len(offsets))
402+
return (N + Npath_ids - 1) // Npath_ids
403+
387404
def _iter_collection(self, gc, master_transform, all_transforms,
388405
path_ids, offsets, offsetTrans, facecolors,
389406
edgecolors, linewidths, linestyles,

lib/matplotlib/backends/backend_pdf.py

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1617,12 +1617,24 @@ def draw_path_collection(self, gc, master_transform, paths, all_transforms,
16171617
if not len(edgecolors):
16181618
stroked = False
16191619
else:
1620-
if np.all(edgecolors[:, 3] == edgecolors[0, 3]):
1620+
if np.all(np.asarray(linewidths) == 0.0):
1621+
stroked = False
1622+
elif np.all(edgecolors[:, 3] == edgecolors[0, 3]):
16211623
stroked = edgecolors[0, 3] != 0.0
16221624
else:
16231625
can_do_optimization = False
16241626

1625-
if not can_do_optimization:
1627+
# Is the optimization worth it? Rough calculation:
1628+
# cost of emitting a path in-line is len_path * uses_per_path
1629+
# cost of XObject is len_path + 5 for the definition,
1630+
# uses_per_path for the uses
1631+
len_path = len(paths[0].vertices) if len(paths) > 0 else 0
1632+
uses_per_path = self._iter_collection_uses_per_path(
1633+
paths, all_transforms, offsets, facecolors, edgecolors)
1634+
should_do_optimization = \
1635+
len_path + uses_per_path + 5 < len_path * uses_per_path
1636+
1637+
if (not can_do_optimization) or (not should_do_optimization):
16261638
return RendererBase.draw_path_collection(
16271639
self, gc, master_transform, paths, all_transforms,
16281640
offsets, offsetTrans, facecolors, edgecolors,
@@ -1654,9 +1666,10 @@ def draw_path_collection(self, gc, master_transform, paths, all_transforms,
16541666

16551667
def draw_markers(self, gc, marker_path, marker_trans, path, trans,
16561668
rgbFace=None):
1657-
# For simple paths or small numbers of markers, don't bother
1658-
# making an XObject
1659-
if len(path) * len(marker_path) <= 10:
1669+
# Same logic as in draw_path_collection
1670+
len_marker_path = len(marker_path)
1671+
uses = len(path)
1672+
if len_marker_path * uses < len_marker_path + uses + 5:
16601673
RendererBase.draw_markers(self, gc, marker_path, marker_trans,
16611674
path, trans, rgbFace)
16621675
return

lib/matplotlib/backends/backend_ps.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -637,6 +637,23 @@ def draw_path_collection(self, gc, master_transform, paths, all_transforms,
637637
offsets, offsetTrans, facecolors, edgecolors,
638638
linewidths, linestyles, antialiaseds, urls,
639639
offset_position):
640+
# Is the optimization worth it? Rough calculation:
641+
# cost of emitting a path in-line is
642+
# (len_path + 2) * uses_per_path
643+
# cost of definition+use is
644+
# (len_path + 3) + 3 * uses_per_path
645+
len_path = len(paths[0].vertices) if len(paths) > 0 else 0
646+
uses_per_path = self._iter_collection_uses_per_path(
647+
paths, all_transforms, offsets, facecolors, edgecolors)
648+
should_do_optimization = \
649+
len_path + 3 * uses_per_path + 3 < (len_path + 2) * uses_per_path
650+
if not should_do_optimization:
651+
return RendererBase.draw_path_collection(
652+
self, gc, master_transform, paths, all_transforms,
653+
offsets, offsetTrans, facecolors, edgecolors,
654+
linewidths, linestyles, antialiaseds, urls,
655+
offset_position)
656+
640657
write = self._pswriter.write
641658

642659
path_codes = []

lib/matplotlib/backends/backend_svg.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -607,6 +607,23 @@ def draw_path_collection(self, gc, master_transform, paths, all_transforms,
607607
offsets, offsetTrans, facecolors, edgecolors,
608608
linewidths, linestyles, antialiaseds, urls,
609609
offset_position):
610+
# Is the optimization worth it? Rough calculation:
611+
# cost of emitting a path in-line is
612+
# (len_path + 5) * uses_per_path
613+
# cost of definition+use is
614+
# (len_path + 3) + 9 * uses_per_path
615+
len_path = len(paths[0].vertices) if len(paths) > 0 else 0
616+
uses_per_path = self._iter_collection_uses_per_path(
617+
paths, all_transforms, offsets, facecolors, edgecolors)
618+
should_do_optimization = \
619+
len_path + 9 * uses_per_path + 3 < (len_path + 5) * uses_per_path
620+
if not should_do_optimization:
621+
return RendererBase.draw_path_collection(
622+
self, gc, master_transform, paths, all_transforms,
623+
offsets, offsetTrans, facecolors, edgecolors,
624+
linewidths, linestyles, antialiaseds, urls,
625+
offset_position)
626+
610627
writer = self.writer
611628
path_codes = []
612629
writer.start('defs')
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
from matplotlib.backend_bases import RendererBase
2+
import matplotlib.transforms as transforms
3+
import matplotlib.path as path
4+
import numpy as np
5+
6+
7+
def test_uses_per_path():
8+
id = transforms.Affine2D()
9+
paths = [path.Path.unit_regular_polygon(i) for i in range(3, 7)]
10+
tforms = [id.rotate(i) for i in range(1, 5)]
11+
offsets = np.arange(20).reshape((10, 2))
12+
facecolors = ['red', 'green']
13+
edgecolors = ['red', 'green']
14+
15+
def check(master_transform, paths, all_transforms,
16+
offsets, facecolors, edgecolors):
17+
rb = RendererBase()
18+
raw_paths = list(rb._iter_collection_raw_paths(master_transform,
19+
paths, all_transforms))
20+
gc = rb.new_gc()
21+
ids = [path_id for xo, yo, path_id, gc0, rgbFace in
22+
rb._iter_collection(gc, master_transform, all_transforms,
23+
range(len(raw_paths)), offsets,
24+
transforms.IdentityTransform(),
25+
facecolors, edgecolors, [], [], [False],
26+
[], 'data')]
27+
uses = rb._iter_collection_uses_per_path(
28+
paths, all_transforms, offsets, facecolors, edgecolors)
29+
seen = [0] * len(raw_paths)
30+
for i in ids:
31+
seen[i] += 1
32+
for n in seen:
33+
assert n in (uses-1, uses)
34+
35+
check(id, paths, tforms, offsets, facecolors, edgecolors)
36+
check(id, paths[0:1], tforms, offsets, facecolors, edgecolors)
37+
check(id, [], tforms, offsets, facecolors, edgecolors)
38+
check(id, paths, tforms[0:1], offsets, facecolors, edgecolors)
39+
check(id, paths, [], offsets, facecolors, edgecolors)
40+
for n in range(0, offsets.shape[0]):
41+
check(id, paths, tforms, offsets[0:n, :], facecolors, edgecolors)
42+
check(id, paths, tforms, offsets, [], edgecolors)
43+
check(id, paths, tforms, offsets, facecolors, [])
44+
check(id, paths, tforms, offsets, [], [])
45+
check(id, paths, tforms, offsets, facecolors[0:1], edgecolors)
Binary file not shown.
Binary file not shown.

0 commit comments

Comments
 (0)