8000 Use iterator rather than caching approach for paths · matplotlib/matplotlib@d1b14ac · GitHub
[go: up one dir, main page]

Skip to content

Commit d1b14ac

Browse files
committed
Use iterator rather than caching approach for paths
svn path=/branches/transforms/; revision=3860
1 parent 548ddfa commit d1b14ac

File tree

3 files changed

+103
-35
lines changed

3 files changed

+103
-35
lines changed

lib/matplotlib/backends/backend_agg.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,8 +112,7 @@ def __init__(self, width, height, dpi):
112112
self.dpi = dpi
113113
self.width = width
114114
self.height = height
115-
if __debug__: verbose.report('RendererAgg.__init__ width=%s, \
116-
height=%s'%(width, height), 'debug-annoying')
115+
if __debug__: verbose.report('RendererAgg.__init__ width=%s, height=%s'%(width, height), 'debug-annoying')
117116
self._renderer = _RendererAgg(int(width), int(height), dpi,
118117
debug=False)
119118
if __debug__: verbose.report('RendererAgg.__init__ _RendererAgg done',

src/_backend_agg.cpp

Lines changed: 100 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -87,22 +87,88 @@ agg::trans_affine py_to_agg_transformation_matrix(const Py::Object& obj) {
8787
inline void get_next_vertex(const char* & vertex_i, const char* vertex_end,
8888
double& x, double& y,
8989
size_t next_vertex_stride,
90-
size_t next_axis_stride) {
90+
size_t next_axis_stride,
91+
const char* & code_i, size_t code_stride) {
9192
if (vertex_i + next_axis_stride >= vertex_end)
9293
throw Py::ValueError("Error parsing path. Read past end of vertices");
9394
x = *(double*)vertex_i;
9495
y = *(double*)(vertex_i + next_axis_stride);
9596
vertex_i += next_vertex_stride;
97+
code_i += code_stride;
9698
}
9799

98-
#define GET_NEXT_VERTEX(x, y) get_next_vertex(vertex_i, vertex_end, x, y, next_vertex_stride, next_axis_stride)
100+
#define GET_NEXT_VERTEX(x, y) get_next_vertex(vertex_i, vertex_end, x, y, next_vertex_stride, next_axis_stride, code_i, code_stride)
99101

100102
Py::Object BufferRegion::to_string(const Py::Tuple &args) {
101103

102104
// owned=true to prevent memory leak
103105
return Py::String(PyString_FromStringAndSize((const char*)aggbuf.data,aggbuf.height*aggbuf.stride), true);
104106
}
105107

108+
class PathIterator {
109+
PyArrayObject* vertices;
110+
PyArrayObject* codes;
111+
size_t m_iterator;
112+
size_t m_total_vertices;
113+
114+
public:
115+
PathIterator(const Py::Object& path_obj) :
116+
vertices(NULL), codes(NULL), m_iterator(0) {
117+
Py::Object vertices_obj = path_obj.getAttr("vertices");
118+
Py::Object codes_obj = path_obj.getAttr("codes");
119+
120+
vertices = (PyArrayObject*)PyArray_ContiguousFromObject
121+
(vertices_obj.ptr(), PyArray_DOUBLE, 2, 2);
122+
if (!vertices || vertices->nd != 2 || vertices->dimensions[1] != 2)
123+
throw Py::ValueError("Invalid vertices array.");
124+
codes = (PyArrayObject*)PyArray_ContiguousFromObject
125+
(codes_obj.ptr(), PyArray_UINT8, 1, 1);
126+
if (!codes)
127+
throw Py::ValueError("Invalid codes array.");
128+
129+
if (codes->dimensions[0] != vertices->dimensions[0])
130+
throw Py::ValueError("Vertices and codes array are not the same length.");
131+
132+
m_total_vertices = codes->dimensions[0];
133+
}
134+
135+
~PathIterator() {
136+
Py_XDECREF(vertices);
137+
Py_XDECREF(codes);
138+
}
139+
140+
static const char code_map[];
141+
142+
inline unsigned vertex(unsigned idx, double* x, double* y) {
143+
if (idx > m_total_vertices)
144+
throw Py::RuntimeError("Requested vertex past end");
145+
double* pv = (double*)(vertices->data + (idx * vertices->strides[0]));
146+
*x = *pv++;
147+
*y = *pv;
148+
// MGDTODO: Range check
149+
return code_map[(unsigned int)*(codes->data + (idx * codes->strides[0]))];
150+
}
151+
152+
inline unsigned vertex(double* x, double* y) {
153+
if(m_iterator >= m_total_vertices) return agg::path_cmd_stop;
154+
return vertex(m_iterator++, x, y);
155+
}
156+
157+
inline void rewind(unsigned path_id) {
158+
m_iterator = path_id;
159+
}
160+
161+
inline unsigned total_vertices() {
162+
return m_total_vertices;
163+
}
164+
};
165+
166+
const char PathIterator::code_map[] = {0,
167+
agg::path_cmd_move_to,
168+
agg::path_cmd_line_to,
169+
agg::path_cmd_curve3,
170+
agg::path_cmd_curve4,
171+
agg::path_cmd_end_poly | agg::path_flags_close};
106172

107173
GCAgg::GCAgg(const Py::Object &gc, double dpi, bool snapto) :
108174
dpi(dpi), snapto(snapto), isaa(true), linewidth(1.0), alpha(1.0),
@@ -634,7 +700,7 @@ RendererAgg::draw_markers(const Py::Tuple& args) {
634700
if (num_vertices) {
635701
for (size_t j=0; j<num_vertices; ++j)
636702
GET_NEXT_VERTEX(x, y);
637-
if (code_i == IGNORE)
703+
if (*code_i == STOP || *code_i == CLOSEPOLY)
638704
continue;
639705

640706
trans.transform(&x, &y);
@@ -863,9 +929,10 @@ PathAgg::PathAgg(const Py::Object& path_obj) : curvy(false) {
863929

864930
for (size_t i = 0; i < N; ++i) {
865931
switch (*(unsigned char*)(code_i)) {
866-
case IGNORE:
932+
case STOP:
867933
GET_NEXT_VERTEX(x0, y0);
868-
_VERBOSE("IGNORE");
934+
_VERBOSE("STOP");
935+
// MGDTODO: If this isn't the end, we should raise an error
869936
break;
870937
case MOVETO:
871938
GET_NEXT_VERTEX(x0, y0);
@@ -894,10 +961,10 @@ PathAgg::PathAgg(const Py::Object& path_obj) : curvy(false) {
894961
break;
895962
case CLOSEPOLY:
< F438 /td>
896963
close_polygon();
964+
GET_NEXT_VERTEX(x0, y0);
897965
_VERBOSE("CLOSEPOLY");
898966
break;
899967
}
900-
code_i += code_stride;
901968
}
902969
} catch(...) {
903970
Py_XDECREF(vertices);
@@ -911,7 +978,7 @@ PathAgg::PathAgg(const Py::Object& path_obj) : curvy(false) {
911978

912979
Py::Object
913980
RendererAgg::draw_path(const Py::Tuple& args) {
914-
typedef agg::conv_transform<agg::path_storage> transformed_path_t;
981+
typedef agg::conv_transform<PathIterator> transformed_path_t;
915982
typedef agg::conv_curve<transformed_path_t> curve_t;
916983
typedef agg::conv_stroke<curve_t> stroke_t;
917984
typedef agg::conv_dash<curve_t> dash_t;
@@ -928,9 +995,11 @@ RendererAgg::draw_path(const Py::Tuple& args) {
928995

929996
GCAgg gc = GCAgg(args[0], dpi);
930997
Py::Object path_obj = args[1];
931-
if (!PathAgg::check(path_obj))
932-
throw Py::TypeError("Native path object is not of correct type");
933-
PathAgg* path = static_cast<PathAgg*>(path_obj.ptr());
998+
// if (!PathAgg::check(path_obj))
999+
// throw Py::TypeError("Native path object is not of correct type");
1000+
// PathAgg* path = static_cast<PathAgg*>(path_obj.ptr());
1001+
PathIterator path(path_obj);
1002+
9341003
agg::trans_affine trans = py_to_agg_transformation_matrix(args[2]);
9351004
facepair_t face = _get_rgba_face(args[3], gc.alpha);
9361005

@@ -943,34 +1012,34 @@ RendererAgg::draw_path(const Py::Tuple& args) {
9431012
bool has_clippath = (gc.clippath != NULL);
9441013

9451014
if (has_clippath && (gc.clippath != lastclippath || trans != lastclippath_transform)) {
946-
rendererBaseAlphaMask->clear(agg::gray8(0, 0));
947-
gc.clippath->rewind(0);
948-
transformed_path_t transformed_clippath(*(gc.clippath), trans);
949-
theRasterizer->add_path(transformed_clippath);
950-
rendererAlphaMask->color(agg::gray8(255, 255));
951-
agg::render_scanlines(*theRasterizer, *scanlineAlphaMask, *rendererAlphaMask);
952-
lastclippath = gc.clippath;
953-
lastclippath_transform = trans;
1015+
// rendererBaseAlphaMask->clear(agg::gray8(0, 0));
1016+
// gc.clippath->rewind(0);
1017+
// transformed_path_t transformed_clippath(*(gc.clippath), trans);
1018+
// theRasterizer->add_path(transformed_clippath);
1019+
// rendererAlphaMask->color(agg::gray8(255, 255));
1020+
// agg::render_scanlines(*theRasterizer, *scanlineAlphaMask, *rendererAlphaMask);
1021+
// lastclippath = gc.clippath;
1022+
// lastclippath_transform = trans;
9541023
}
9551024

9561025
try {
9571026
// If this is a straight horizontal or vertical line, quantize to nearest
9581027
// pixels
959-
if (path->total_vertices() == 2) {
960-
double x0, y0, x1, y1;
961-
path->vertex(0, &x0, &y0);
962-
trans.transform(&x0, &y0);
963-
path->vertex(1, &x1, &y1);
964-
trans.transform(&x1, &y1);
965-
if (((int)x0 == (int)x1) || ((int)y0 == (int)y1)) {
966-
new_path.move_to((int)x0 + 0.5, (int)y0 + 0.5);
967-
new_path.line_to((int)x1 + 0.5, (int)y1 + 0.5);
968-
tpath = new transformed_path_t(new_path, agg::trans_affine());
969-
}
970-
}
1028+
// if (path.total_vertices() == 2) {
1029+
// double x0, y0, x1, y1;
1030+
// path.vertex(0, &x0, &y0);
1031+
// trans.transform(&x0, &y0);
1032+
// path.vertex(1, &x1, &y1);
1033+
// trans.transform(&x1, &y1);
1034+
// if (((int)x0 == (int)x1) || ((int)y0 == (int)y1)) {
1035+
// new_path.move_to((int)x0 + 0.5, (int)y0 + 0.5);
1036+
// new_path.line_to((int)x1 + 0.5, (int)y1 + 0.5);
1037+
// tpath = new transformed_path_t(new_path, agg::trans_affine());
1038+
// }
1039+
// }
9711040

9721041
if (!tpath) {
973-
tpath = new transformed_path_t(*path, trans);
1042+
tpath = new transformed_path_t(path, trans);
9741043
}
9751044

9761045
// Benchmarking shows that there is no noticable slowdown to always

src/_backend_agg.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,14 +40,14 @@
4040
#include "agg_vcgen_markers_term.h"
4141

4242
// These are copied directly from path.py, and must be kept in sync
43-
#define IGNORE 0
43+
#define STOP 0
4444
#define MOVETO 1
4545
#define LINETO 2
4646
#define CURVE3 3
4747
#define CURVE4 4
4848
#define CLOSEPOLY 5
4949

50-
const size_t NUM_VERTICES[] = { 1, 1, 1, 2, 3, 0 };
50+
const size_t NUM_VERTICES[] = { 1, 1, 1, 2, 3, 1 };
5151

5252
typedef agg::pixfmt_rgba32 pixfmt;
5353
typedef agg::renderer_base<pixfmt> renderer_base;

0 commit comments

Comments
 (0)
0