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

Skip to content

Commit bc6bd9b

Browse files
committed
cairo: remove the append_path() fast path
With the performance improvements in #13040 and #13039 the old slow path is now faster than the previously fast one. And it also works with pycairo.
1 parent cdc3ab1 commit bc6bd9b

File tree

1 file changed

+15
-86
lines changed

1 file changed

+15
-86
lines changed

lib/matplotlib/backends/backend_cairo.py

Lines changed: 15 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -72,93 +72,22 @@ def buffer_info(self):
7272
return (self.__data, self.__size)
7373

7474

75-
# Mapping from Matplotlib Path codes to cairo path codes.
76-
_MPL_TO_CAIRO_PATH_TYPE = np.zeros(80, dtype=int) # CLOSEPOLY = 79.
77-
_MPL_TO_CAIRO_PATH_TYPE[Path.MOVETO] = cairo.PATH_MOVE_TO
78-
_MPL_TO_CAIRO_PATH_TYPE[Path.LINETO] = cairo.PATH_LINE_TO
79-
_MPL_TO_CAIRO_PATH_TYPE[Path.CURVE4] = cairo.PATH_CURVE_TO
80-
_MPL_TO_CAIRO_PATH_TYPE[Path.CLOSEPOLY] = cairo.PATH_CLOSE_PATH
81-
# Sizes in cairo_path_data_t of each cairo path element.
82-
_CAIRO_PATH_TYPE_SIZES = np.zeros(4, dtype=int)
83-
_CAIRO_PATH_TYPE_SIZES[cairo.PATH_MOVE_TO] = 2
84-
_CAIRO_PATH_TYPE_SIZES[cairo.PATH_LINE_TO] = 2
85-
_CAIRO_PATH_TYPE_SIZES[cairo.PATH_CURVE_TO] = 4
86-
_CAIRO_PATH_TYPE_SIZES[cairo.PATH_CLOSE_PATH] = 1
87-
88-
89-
def _append_paths_slow(ctx, paths, transforms, clip=None):
90-
for path, transform in zip(paths, transforms):
91-
for points, code in path.iter_segments(
92-
transform, remove_nans=True, clip=clip):
93-
if code == Path.MOVETO:
94-
ctx.move_to(*points)
95-
elif code == Path.CLOSEPOLY:
96-
ctx.close_path()
97-
elif code == Path.LINETO:
98-
ctx.line_to(*points)
99-
elif code == Path.CURVE3:
100-
cur = np.asarray(ctx.get_current_point())
101-
a = points[:2]
102-
b = points[-2:]
103-
ctx.curve_to(*(cur / 3 + a * 2 / 3), *(a * 2 / 3 + b / 3), *b)
104-
elif code == Path.CURVE4:
105-
ctx.curve_to(*points)
106-
107-
108-
def _append_paths_fast(ctx, paths, transforms, clip=None):
109-
# We directly convert to the internal representation used by cairo, for
110-
# which ABI compatibility is guaranteed. The layout for each item is
111-
# --CODE(4)-- -LENGTH(4)- ---------PAD(8)---------
112-
# ----------X(8)---------- ----------Y(8)----------
113-
# with the size in bytes in parentheses, and (X, Y) repeated as many times
114-
# as there are points for the current code.
115-
ffi = cairo.ffi
116-
117-
# Convert curves to segment, so that 1. we don't have to handle
118-
# variable-sized CURVE-n codes, and 2. we don't have to implement degree
119-
# elevation for quadratic Beziers.
120-
cleaneds = [path.cleaned(transform, remove_nans=True, clip=clip)
121-
for path, transform in zip(paths, transforms)]
122-
vertices = np.concatenate([cleaned.vertices for cleaned in cleaneds])
123-
codes = np.concatenate([cleaned.codes for cleaned in cleaneds])
124-
125-
# Remove unused vertices and convert to cairo codes. Note that unlike
126-
# cairo_close_path, we do not explicitly insert an extraneous MOVE_TO after
127-
# CLOSE_PATH, so our resulting buffer may be smaller.
128-
vertices = vertices[(codes != Path.STOP) & (codes != Path.CLOSEPOLY)]
129-
codes = codes[codes != Path.STOP]
130-
codes = _MPL_TO_CAIRO_PATH_TYPE[codes]
131-
132-
# Where are the headers of each cairo portions?
133-
cairo_type_sizes = _CAIRO_PATH_TYPE_SIZES[codes]
134-
cairo_type_positions = np.insert(np.cumsum(cairo_type_sizes), 0, 0)
135-
cairo_num_data = cairo_type_positions[-1]
136-
cairo_type_positions = cairo_type_positions[:-1]
137-
138-
# Fill the buffer.
139-
buf = np.empty(cairo_num_data * 16, np.uint8)
140-
as_int = np.frombuffer(buf.data, np.int32)
141-
as_int[::4][cairo_type_positions] = codes
142-
as_int[1::4][cairo_type_positions] = cairo_type_sizes
143-
as_float = np.frombuffer(buf.data, np.float64)
144-
mask = np.ones_like(as_float, bool)
145-
mask[::2][cairo_type_positions] = mask[1::2][cairo_type_positions] = False
146-
as_float[mask] = vertices.ravel()
147-
148-
# Construct the cairo_path_t, and pass it to the context.
149-
ptr = ffi.new("cairo_path_t *")
150-
ptr.status = cairo.STATUS_SUCCESS
151-
ptr.data = ffi.cast("cairo_path_data_t *", ffi.from_buffer(buf))
152-
ptr.num_data = cairo_num_data
153-
cairo.cairo.cairo_append_path(ctx._pointer, ptr)
154-
155-
156-
_append_paths = (_append_paths_fast if cairo.__name__ == "cairocffi"
157-
else _append_paths_slow)
158-
159-
16075
def _append_path(ctx, path, transform, clip=None):
161-
return _append_paths(ctx, [path], [transform], clip)
76+
for points, code in path.iter_segments(
77+
transform, remove_nans=True, clip=clip):
78+
if code == Path.MOVETO:
79+
ctx.move_to(*points)
80+
elif code == Path.CLOSEPOLY:
81+
ctx.close_path()
82+
elif code == Path.LINETO:
83+
ctx.line_to(*points)
84+
elif code == Path.CURVE3:
85+
cur = np.asarray(ctx.get_current_point())
86+
a = points[:2]
87+
b = points[-2:]
88+
ctx.curve_to(*(cur / 3 + a * 2 / 3), *(a * 2 / 3 + b / 3), *b)
89+
elif code == Path.CURVE4:
90+
ctx.curve_to(*points)
16291

16392

16493
class RendererCairo(RendererBase):

0 commit comments

Comments
 (0)