Skip to content

Commit 8ec89ce

Browse files
Michiel de HoonMichiel de Hoon
authored andcommitted
draw paths in stretches of 100 points to avoid the severe decrease in performance that may occur if long paths are draw
1 parent 69e1649 commit 8ec89ce

File tree

1 file changed

+107
-76
lines changed

1 file changed

+107
-76
lines changed

src/_macosx.m

Lines changed: 107 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -240,11 +240,15 @@ static void _dealloc_atsui(void)
240240
}
241241
#endif
242242

243-
static int _draw_path(CGContextRef cr, void* iterator)
243+
static int _draw_path(CGContextRef cr, void* iterator, int nmax)
244244
{
245245
double x1, y1, x2, y2, x3, y3;
246+
static unsigned code = STOP;
247+
static double xs, ys;
248+
CGPoint current;
246249
int n = 0;
247-
unsigned code;
250+
251+
if (code == MOVETO) CGContextMoveToPoint(cr, xs, ys);
248252

249253
while (true)
250254
{
@@ -261,7 +265,6 @@ static int _draw_path(CGContextRef cr, void* iterator)
261265
else if (code == MOVETO)
262266
{
263267
CGContextMoveToPoint(cr, x1, y1);
264-
n++;
265268
}
266269
else if (code==LINETO)
267270
{
@@ -270,31 +273,50 @@ static int _draw_path(CGContextRef cr, void* iterator)
270273
}
271274
else if (code==CURVE3)
272275
{
273-
get_vertex(iterator, &x2, &y2);
274-
CGContextAddQuadCurveToPoint(cr, x1, y1, x2, y2);
276+
get_vertex(iterator, &xs, &ys);
277+
CGContextAddQuadCurveToPoint(cr, x1, y1, xs, ys);
275278
n+=2;
276279
}
277280
else if (code==CURVE4)
278281
{
279282
get_vertex(iterator, &x2, &y2);
280-
get_vertex(iterator, &x3, &y3);
281-
CGContextAddCurveToPoint(cr, x1, y1, x2, y2, x3, y3);
283+
get_vertex(iterator, &xs, &ys);
284+
CGContextAddCurveToPoint(cr, x1, y1, x2, y2, xs, ys);
282285
n+=3;
283286
}
287+
if (n >= nmax)
288+
{
289+
switch (code)
290+
{
291+
case MOVETO:
292+
case LINETO:
293+
xs = x1;
294+
ys = y1;
295+
break;
296+
case CLOSEPOLY:
297+
current = CGContextGetPathCurrentPoint(cr);
298+
xs = current.x;
299+
ys = current.y;
300+
break;
301+
/* nothing needed for CURVE3, CURVE4 */
302+
}
303+
code = MOVETO;
304+
return -n;
305+
}
284306
}
285307
return n;
286308
}
287309

288310
static void _draw_hatch(void *info, CGContextRef cr)
289311
{
312+
int n;
290313
PyObject* hatchpath = (PyObject*)info;
291314
PyObject* transform;
292315
int nd = 2;
293316
npy_intp dims[2] = {3, 3};
294317
int typenum = NPY_DOUBLE;
295318
double data[9] = {HATCH_SIZE, 0, 0, 0, HATCH_SIZE, 0, 0, 0, 1};
296319
double rect[4] = { 0.0, 0.0, HATCH_SIZE, HATCH_SIZE};
297-
int n;
298320
transform = PyArray_SimpleNewFromData(nd, dims, typenum, data);
299321
if (!transform)
300322
{
@@ -320,7 +342,7 @@ static void _draw_hatch(void *info, CGContextRef cr)
320342
PyGILState_Release(gstate);
321343
return;
322344
}
323-
n = _draw_path(cr, iterator);
345+
n = _draw_path(cr, iterator, INT_MAX);
324346
free_path_iterator(iterator);
325347
if (n==0) return;
326348
CGContextSetLineWidth(cr, 1.0);
@@ -702,7 +724,7 @@ static int _get_snap(GraphicsContext* self, enum e_snap_mode* mode)
702724
"set_clip_path: failed to obtain path iterator for clipping");
703725
return NULL;
704726
}
705-
n = _draw_path(cr, iterator);
727+
n = _draw_path(cr, iterator, INT_MAX);
706728
free_path_iterator(iterator);
707729

708730
if (n > 0) CGContextClip(cr);
@@ -952,89 +974,98 @@ static int _get_snap(GraphicsContext* self, enum e_snap_mode* mode)
952974
"draw_path: failed to obtain path iterator");
953975
return NULL;
954976
}
955-
n = _draw_path(cr, iterator);
956-
free_path_iterator(iterator);
957977

958-
if (n > 0)
978+
if(rgbFace)
959979
{
960-
PyObject* hatchpath;
961-
if(rgbFace)
980+
float r, g, b;
981+
if (!PyArg_ParseTuple(rgbFace, "fff", &r, &g, &b))
982+
return NULL;
983+
n = _draw_path(cr, iterator, INT_MAX);
984+
if (n > 0)
962985
{
963-
float r, g, b;
964-
if (!PyArg_ParseTuple(rgbFace, "fff", &r, &g, &b))
965-
return NULL;
966986
CGContextSaveGState(cr);
967987
CGContextSetRGBFillColor(cr, r, g, b, 1.0);
968988
CGContextDrawPath(cr, kCGPathFillStroke);
969989
CGContextRestoreGState(cr);
970990
}
971-
else CGContextStrokePath(cr);
991+
}
992+
else
993+
{
994+
const int nmax = 100;
995+
while (true)
996+
{
997+
n = _draw_path(cr, iterator, nmax);
998+
if (n != 0) CGContextStrokePath(cr);
999+
if (n >= 0) break;
1000+
}
1001+
}
1002+
free_path_iterator(iterator);
9721003

973-
hatchpath = PyObject_CallMethod((PyObject*)self, "get_hatch_path", "");
974-
if (!hatchpath)
1004+
PyObject* hatchpath;
1005+
hatchpath = PyObject_CallMethod((PyObject*)self, "get_hatch_path", "");
1006+
if (!hatchpath)
1007+
{
1008+
return NULL;
1009+
}
1010+
else if (hatchpath==Py_None)
1011+
{
1012+
Py_DECREF(hatchpath);
1013+
}
1014+
else
1015+
{
1016+
CGPatternRef pattern;
1017+
CGColorSpaceRef baseSpace;
1018+
CGColorSpaceRef patternSpace;
1019+
static const CGPatternCallbacks callbacks = {0,
1020+
&_draw_hatch,
1021+
&_release_hatch};
1022+
baseSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
1023+
if (!baseSpace)
9751024
{
1025+
Py_DECREF(hatchpath);
1026+
PyErr_SetString(PyExc_RuntimeError,
1027+
"draw_path: CGColorSpaceCreateWithName failed");
9761028
return NULL;
9771029
}
978-
else if (hatchpath==Py_None)
1030+
patternSpace = CGColorSpaceCreatePattern(baseSpace);
1031+
CGColorSpaceRelease(baseSpace);
1032+
if (!patternSpace)
9791033
{
9801034
Py_DECREF(hatchpath);
1035+
PyErr_SetString(PyExc_RuntimeError,
1036+
"draw_path: CGColorSpaceCreatePattern failed");
1037+
return NULL;
9811038
}
982-
else
1039+
CGContextSetFillColorSpace(cr, patternSpace);
1040+
CGColorSpaceRelease(patternSpace);
1041+
1042+
pattern = CGPatternCreate((void*)hatchpath,
1043+
CGRectMake(0, 0, HATCH_SIZE, HATCH_SIZE),
1044+
CGAffineTransformIdentity,
1045+
HATCH_SIZE, HATCH_SIZE,
1046+
kCGPatternTilingNoDistortion,
1047+
false,
1048+
&callbacks);
1049+
CGContextSetFillPattern(cr, pattern, self->color);
1050+
CGPatternRelease(pattern);
1051+
iterator = get_path_iterator(path,
1052+
transform,
1053+
1,
1054+
0,
1055+
rect,
1056+
SNAP_AUTO,
1057+
linewidth,
1058+
0);
1059+
if (!iterator)
9831060
{
984-
CGPatternRef pattern;
985-
CGColorSpaceRef baseSpace;
986-
CGColorSpaceRef patternSpace;
987-
static const CGPatternCallbacks callbacks = {0,
988-
&_draw_hatch,
989-
&_release_hatch};
990-
baseSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
991-
if (!baseSpace)
992-
{
993-
Py_DECREF(hatchpath);
994-
PyErr_SetString(PyExc_RuntimeError,
995-
"draw_path: CGColorSpaceCreateWithName failed");
996-
return NULL;
997-
}
998-
patternSpace = CGColorSpaceCreatePattern(baseSpace);
999-
CGColorSpaceRelease(baseSpace);
1000-
if (!patternSpace)
1001-
{
1002-
Py_DECREF(hatchpath);
1003-
PyErr_SetString(PyExc_RuntimeError,
1004-
"draw_path: CGColorSpaceCreatePattern failed");
1005-
return NULL;
1006-
}
1007-
CGContextSetFillColorSpace(cr, patternSpace);
1008-
CGColorSpaceRelease(patternSpace);
1009-
1010-
pattern = CGPatternCreate((void*)hatchpath,
1011-
CGRectMake(0, 0, HATCH_SIZE, HATCH_SIZE),
1012-
CGAffineTransformIdentity,
1013-
HATCH_SIZE, HATCH_SIZE,
1014-
kCGPatternTilingNoDistortion,
1015-
false,
1016-
&callbacks);
1017-
CGContextSetFillPattern(cr, pattern, self->color);
1018-
CGPatternRelease(pattern);
1019-
iterator = get_path_iterator(path,
1020-
transform,
1021-
1,
1022-
0,
1023-
rect,
1024-
SNAP_AUTO,
1025-
linewidth,
1026-
0);
1027-
if (!iterator)
1028-
{
1029-
Py_DECREF(hatchpath);
1030-
PyErr_SetString(PyExc_RuntimeError,
1031-
"draw_path: failed to obtain path iterator for hatching");
1032-
return NULL;
1033-
}
1034-
n = _draw_path(cr, iterator);
1035-
free_path_iterator(iterator);
1036-
CGContextFillPath(cr);
1061+
Py_DECREF(hatchpath);
1062+
PyErr_SetString(PyExc_RuntimeError,
1063+
"draw_path: failed to obtain path iterator for hatching");
1064+
return NULL;
10371065
}
1066+
n = _draw_path(cr, iterator, INT_MAX);
1067+
free_path_iterator(iterator);
1068+
if (n > 0) CGContextFillPath(cr);
10381069
}
10391070

10401071
Py_INCREF(Py_None);

0 commit comments

Comments
 (0)