12
12
13
13
import six
14
14
15
+ import copy
15
16
import gzip
16
17
import sys
17
18
import warnings
@@ -77,35 +78,41 @@ def buffer_info(self):
77
78
78
79
79
80
def _convert_path (ctx , path , transform , clip = None ):
81
+ return _convert_paths (ctx , [path ], [transform ], clip )
82
+
83
+
84
+ def _convert_paths (ctx , paths , transforms , clip = None ):
80
85
if HAS_CAIRO_CFFI :
81
86
try :
82
- return _convert_path_fast (ctx , path , transform , clip )
87
+ return _convert_paths_fast (ctx , paths , transforms , clip )
83
88
except NotImplementedError :
84
89
pass
85
- return _convert_path_slow (ctx , path , transform , clip )
86
-
87
-
88
- def _convert_path_slow (ctx , path , transform , clip = None ):
89
- for points , code in path .iter_segments (transform , clip = clip ):
90
- if code == Path .MOVETO :
91
- ctx .move_to (* points )
92
- elif code == Path .CLOSEPOLY :
93
- ctx .close_path ()
94
- elif code == Path .LINETO :
95
- ctx .line_to (* points )
96
- elif code == Path .CURVE3 :
97
- ctx .curve_to (points [0 ], points [1 ],
98
- points [0 ], points [1 ],
99
- points [2 ], points [3 ])
100
- elif code == Path .CURVE4 :
101
- ctx .curve_to (* points )
102
-
103
-
104
- def _convert_path_fast (ctx , path , transform , clip = None ):
90
+ return _convert_paths_slow (ctx , paths , transforms , clip )
91
+
92
+
93
+ def _convert_paths_slow (ctx , paths , transforms , clip = None ):
94
+ for path , transform in zip (paths , transforms ):
95
+ for points , code in path .iter_segments (transform , clip = clip ):
96
+ if code == Path .MOVETO :
97
+ ctx .move_to (* points )
98
+ elif code == Path .CLOSEPOLY :
99
+ ctx .close_path ()
100
+ elif code == Path .LINETO :
101
+ ctx .line_to (* points )
102
+ elif code == Path .CURVE3 :
103
+ ctx .curve_to (points [0 ], points [1 ],
104
+ points [0 ], points [1 ],
105
+ points [2 ], points [3 ])
106
+ elif code == Path .CURVE4 :
107
+ ctx .curve_to (* points )
108
+
109
+
110
+ def _convert_paths_fast (ctx , paths , transforms , clip = None ):
105
111
ffi = cairo .ffi
106
- cleaned = path .cleaned (transform = transform , clip = clip )
107
- vertices = cleaned .vertices
108
- codes = cleaned .codes
112
+ cleaneds = [path .cleaned (transform = transform , clip = clip )
113
+ for path , transform in zip (paths , transforms )]
114
+ vertices = np .concatenate ([cleaned .vertices for cleaned in cleaneds ])
115
+ codes = np .concatenate ([cleaned .codes for cleaned in cleaneds ])
109
116
110
117
# TODO: Implement Bezier degree elevation formula. Note that the "slow"
111
118
# implementation is, in fact, also incorrect...
@@ -114,10 +121,8 @@ def _convert_path_fast(ctx, path, transform, clip=None):
114
121
# Remove unused vertices and convert to cairo codes. Note that unlike
115
122
# cairo_close_path, we do not explicitly insert an extraneous MOVE_TO after
116
123
# CLOSE_PATH, so our resulting buffer may be smaller.
117
- if codes [- 1 ] == Path .STOP :
118
- codes = codes [:- 1 ]
119
- vertices = vertices [:- 1 ]
120
- vertices = vertices [codes != Path .CLOSEPOLY ]
124
+ vertices = vertices [(codes != Path .STOP ) & (codes != Path .CLOSEPOLY )]
125
+ codes = codes [codes != Path .STOP ]
121
126
codes = _MPL_TO_CAIRO_PATH_TYPE [codes ]
122
127
# Where are the headers of each cairo portions?
123
128
cairo_type_sizes = _CAIRO_PATH_TYPE_SIZES [codes ]
@@ -263,6 +268,54 @@ def draw_markers(self, gc, marker_path, marker_trans, path, transform,
263
268
self ._fill_and_stroke (
264
269
ctx , rgbFace , gc .get_alpha (), gc .get_forced_alpha ())
265
270
271
+ def draw_path_collection (
272
+ self , gc , master_transform , paths , all_transforms , offsets ,
273
+ offsetTrans , facecolors , edgecolors , linewidths , linestyles ,
274
+ antialiaseds , urls , offset_position ):
275
+
276
+ path_ids = []
277
+ for path , transform in self ._iter_collection_raw_paths (
278
+ master_transform , paths , all_transforms ):
279
+ path_ids .append ((path , Affine2D (transform )))
280
+
281
+ reuse_key = None
282
+ grouped_draw = []
283
+
284
+ def _draw_paths ():
285
+ if not grouped_draw :
286
+ return
287
+ gc_vars , rgb_fc = reuse_key
288
+ gc = copy .copy (gc0 )
289
+ vars (gc ).update (gc_vars )
290
+ for k , v in gc_vars .items ():
291
+ try :
292
+ getattr (gc , "set" + k )(v )
293
+ except (AttributeError , TypeError ):
294
+ pass
295
+ gc .ctx .new_path ()
296
+ paths , transforms = zip (* grouped_draw )
297
+ grouped_draw .clear ()
298
+ _convert_paths (gc .ctx , paths , transforms )
299
+ self ._fill_and_stroke (
300
+ gc .ctx , rgb_fc , gc .get_alpha (), gc .get_forced_alpha ())
301
+
302
+ for xo , yo , path_id , gc0 , rgb_fc in self ._iter_collection (
303
+ gc , master_transform , all_transforms , path_ids , offsets ,
304
+ offsetTrans , facecolors , edgecolors , linewidths , linestyles ,
305
+ antialiaseds , urls , offset_position ):
306
+ path , transform = path_id
307
+ transform = (Affine2D (transform .get_matrix ()).translate (xo , yo )
308
+ + Affine2D ().scale (1 , - 1 ).translate (0 , self .height ))
309
+ # rgb_fc could be a ndarray, for which equality is elementwise.
310
+ new_key = vars (gc0 ), tuple (rgb_fc ) if rgb_fc is not None else None
311
+ if new_key == reuse_key :
312
+ grouped_draw .append ((path , transform ))
313
+ else :
314
+ _draw_paths ()
315
+ grouped_draw .append ((path , transform ))
316
+ reuse_key = new_key
317
+ _draw_paths ()
318
+
266
319
def draw_image (self , gc , x , y , im ):
267
320
# bbox - not currently used
268
321
if sys .byteorder == 'little' :
0 commit comments