1 |
/* |
2 |
Copyright (C) 2002 Free Software Foundation, Inc. |
3 |
|
4 |
Author: Alexander Malmberg <alexander@malmberg.org> |
5 |
|
6 |
This file is part of GNUstep. |
7 |
|
8 |
This library is free software; you can redistribute it and/or |
9 |
modify it under the terms of the GNU Library General Public |
10 |
License as published by the Free Software Foundation; either |
11 |
version 2 of the License, or (at your option) any later version. |
12 |
|
13 |
This library is distributed in the hope that it will be useful, |
14 |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
16 |
Library General Public License for more details. |
17 |
|
18 |
You should have received a copy of the GNU Library General Public |
19 |
License along with this library; if not, write to the Free |
20 |
Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
21 |
*/ |
22 |
|
23 |
/* |
24 |
Path handling. |
25 |
*/ |
26 |
|
27 |
#include <math.h> |
28 |
|
29 |
#include <AppKit/NSAffineTransform.h> |
30 |
#include <AppKit/NSBezierPath.h> |
31 |
|
32 |
#include "ARTGState.h" |
33 |
|
34 |
#ifndef RDS |
35 |
#include "x11/XWindowBuffer.h" |
36 |
#endif |
37 |
#include "blit.h" |
38 |
|
39 |
|
40 |
#include <libart_lgpl/libart.h> |
41 |
#include <libart_lgpl/art_svp_intersect.h> |
42 |
|
43 |
|
44 |
#if 0 |
45 |
/* useful when debugging */ |
46 |
static void dump_bpath(ArtBpath *vp) |
47 |
{ |
48 |
int i; |
49 |
printf("** dumping %p **\n", vp); |
50 |
for (i = 0; ; i++) |
51 |
{ |
52 |
if (vp[i].code == ART_MOVETO_OPEN) |
53 |
printf(" moveto_open"); |
54 |
else if (vp[i].code == ART_MOVETO) |
55 |
printf(" moveto"); |
56 |
else if (vp[i].code == ART_LINETO) |
57 |
printf(" lineto"); |
58 |
else if (vp[i].code == ART_CURVETO) |
59 |
printf(" curveto"); |
60 |
else |
61 |
printf(" unknown %i", vp[i].code); |
62 |
|
63 |
printf(" (%g %g) (%g %g) (%g %g)\n", |
64 |
vp[i].x1, vp[i].y1, |
65 |
vp[i].x2, vp[i].y2, |
66 |
vp[i].x3, vp[i].y3); |
67 |
if (vp[i].code == ART_END) |
68 |
break; |
69 |
} |
70 |
} |
71 |
|
72 |
{ |
73 |
int i; |
74 |
NSBezierPathElement type; |
75 |
NSPoint pts[3]; |
76 |
for (i = 0; i < [newPath elementCount]; i++) |
77 |
{ |
78 |
type = [newPath elementAtIndex: i associatedPoints: pts]; |
79 |
switch (type) |
80 |
{ |
81 |
case NSMoveToBezierPathElement: |
82 |
printf("moveto (%g %g)\n", pts[0].x, pts[0].y); |
83 |
break; |
84 |
case NSLineToBezierPathElement: |
85 |
printf("lineto (%g %g)\n", pts[0].x, pts[0].y); |
86 |
break; |
87 |
case NSCurveToBezierPathElement: |
88 |
printf("curveto (%g %g) (%g %g) (%g %g)\n", |
89 |
pts[0].x, pts[0].y, |
90 |
pts[1].x, pts[1].y, |
91 |
pts[2].x, pts[2].y); |
92 |
break; |
93 |
} |
94 |
} |
95 |
} |
96 |
|
97 |
{ |
98 |
int i,j; |
99 |
printf("size=%i num=%i\n",ci.span_size,ci.num_span); |
100 |
for (i=0;i<clip_sy;i++) |
101 |
{ |
102 |
printf("y=%3i:",i); |
103 |
for (j=clip_index[i];j<clip_index[i+1];j++) |
104 |
{ |
105 |
printf(" %i",clip_span[j]); |
106 |
} |
107 |
printf("\n"); |
108 |
} |
109 |
} |
110 |
|
111 |
#endif |
112 |
|
113 |
|
114 |
/* rendering helpers */ |
115 |
|
116 |
typedef struct |
117 |
{ |
118 |
render_run_t ri; |
119 |
|
120 |
unsigned char real_a; |
121 |
int x0, x1, y0; |
122 |
int rowstride, arowstride, bpp; |
123 |
void (*run_alpha)(struct render_run_s *ri, int num); |
124 |
void (*run_opaque)(struct render_run_s *ri, int num); |
125 |
|
126 |
unsigned int *clip_span, *clip_index; |
127 |
} svp_render_info_t; |
128 |
|
129 |
static void render_svp_callback(void *data, int y, int start, |
130 |
ArtSVPRenderAAStep *steps, int n_steps) |
131 |
{ |
132 |
svp_render_info_t *ri = data; |
133 |
int x0 = ri->x0, x1; |
134 |
int num; |
135 |
int alpha; |
136 |
unsigned char *dst, *dsta; |
137 |
|
138 |
alpha = start; |
139 |
|
140 |
/* empty line; very common case */ |
141 |
if (alpha < 0x10000 && !n_steps) |
142 |
{ |
143 |
ri->ri.dst += ri->rowstride; |
144 |
ri->ri.dsta += ri->arowstride; |
145 |
return; |
146 |
} |
147 |
|
148 |
dst = ri->ri.dst + ri->rowstride; |
149 |
dsta = ri->ri.dsta + ri->arowstride; |
150 |
|
151 |
for (; n_steps; n_steps--, steps++) |
152 |
{ |
153 |
x1 = steps->x; |
154 |
num = x1 - x0; |
155 |
|
156 |
ri->ri.a = (alpha * ri->real_a + 0x800000) >> 24; |
157 |
if (ri->ri.a && num) |
158 |
{ |
159 |
if (ri->ri.a == 255) |
160 |
ri->run_opaque(&ri->ri, num); |
161 |
else |
162 |
ri->run_alpha(&ri->ri, num); |
163 |
} |
164 |
ri->ri.dst += ri->bpp * num; |
165 |
ri->ri.dsta += num; |
166 |
|
167 |
alpha += steps->delta; |
168 |
x0 = x1; |
169 |
} |
170 |
|
171 |
x1 = ri->x1; |
172 |
num = x1 - x0; |
173 |
ri->ri.a = (alpha * ri->real_a + 0x800000) >> 24; |
174 |
if (ri->ri.a && num) |
175 |
{ |
176 |
if (ri->ri.a == 255) |
177 |
ri->run_opaque(&ri->ri, num); |
178 |
else |
179 |
ri->run_alpha(&ri->ri, num); |
180 |
} |
181 |
|
182 |
ri->ri.dst = dst; |
183 |
ri->ri.dsta = dsta; |
184 |
} |
185 |
|
186 |
static void render_svp_clipped_callback(void *data, int y, int start, |
187 |
ArtSVPRenderAAStep *steps, int n_steps) |
188 |
{ |
189 |
svp_render_info_t *ri = data; |
190 |
int x0, x1; |
191 |
int num; |
192 |
int alpha; |
193 |
unsigned char *dst, *dsta; |
194 |
|
195 |
unsigned int *span, *end; |
196 |
BOOL state; |
197 |
|
198 |
alpha = start; |
199 |
|
200 |
/* empty line; very common case */ |
201 |
if (alpha < 0x10000 && !n_steps) |
202 |
{ |
203 |
ri->ri.dst += ri->rowstride; |
204 |
ri->ri.dsta += ri->arowstride; |
205 |
return; |
206 |
} |
207 |
|
208 |
/* completely clipped line? */ |
209 |
if (ri->clip_index[y - ri->y0] == ri->clip_index[y - ri->y0 + 1]) |
210 |
{ |
211 |
ri->ri.dst += ri->rowstride; |
212 |
ri->ri.dsta += ri->arowstride; |
213 |
return; |
214 |
} |
215 |
|
216 |
span = &ri->clip_span[ri->clip_index[y - ri->y0]]; |
217 |
end = &ri->clip_span[ri->clip_index[y - ri->y0 + 1]]; |
218 |
state = NO; |
219 |
|
220 |
dst = ri->ri.dst + ri->rowstride; |
221 |
dsta = ri->ri.dsta + ri->arowstride; |
222 |
|
223 |
x0 = 0; |
224 |
for (; n_steps; n_steps--, steps++) |
225 |
{ |
226 |
x1 = steps->x - ri->x0; |
227 |
|
228 |
ri->ri.a = (alpha * ri->real_a + 0x800000) >> 24; |
229 |
if (ri->ri.a) |
230 |
{ |
231 |
while (*span < x1) |
232 |
{ |
233 |
num = *span - x0; |
234 |
if (state) |
235 |
{ |
236 |
if (ri->ri.a == 255) |
237 |
ri->run_opaque(&ri->ri, num); |
238 |
else |
239 |
ri->run_alpha(&ri->ri, num); |
240 |
} |
241 |
x0 = *span; |
242 |
ri->ri.dst += ri->bpp * num; |
243 |
ri->ri.dsta += num; |
244 |
state = !state; |
245 |
span++; |
246 |
if (span == end) |
247 |
{ |
248 |
ri->ri.dst = dst; |
249 |
ri->ri.dsta = dsta; |
250 |
return; |
251 |
} |
252 |
} |
253 |
num = x1 - x0; |
254 |
if (num && state) |
255 |
{ |
256 |
if (ri->ri.a == 255) |
257 |
ri->run_opaque(&ri->ri, num); |
258 |
else |
259 |
ri->run_alpha(&ri->ri, num); |
260 |
} |
261 |
ri->ri.dst += ri->bpp * num; |
262 |
ri->ri.dsta += num; |
263 |
} |
264 |
else |
265 |
{ |
266 |
num = x1 - x0; |
267 |
while (*span <= x1) |
268 |
{ |
269 |
state = !state; |
270 |
span++; |
271 |
if (span == end) |
272 |
{ |
273 |
ri->ri.dst = dst; |
274 |
ri->ri.dsta = dsta; |
275 |
return; |
276 |
} |
277 |
} |
278 |
ri->ri.dst += ri->bpp * num; |
279 |
ri->ri.dsta += num; |
280 |
} |
281 |
|
282 |
alpha += steps->delta; |
283 |
x0 = x1; |
284 |
} |
285 |
x1 = ri->x1 - ri->x0; |
286 |
num = x1 - x0; |
287 |
ri->ri.a = (alpha * ri->real_a + 0x800000) >> 24; |
288 |
if (ri->ri.a && num) |
289 |
{ |
290 |
while (*span < x1) |
291 |
{ |
292 |
num = *span - x0; |
293 |
if (state) |
294 |
{ |
295 |
if (ri->ri.a == 255) |
296 |
ri->run_opaque(&ri->ri, num); |
297 |
else |
298 |
ri->run_alpha(&ri->ri, num); |
299 |
} |
300 |
x0 = *span; |
301 |
ri->ri.dst += ri->bpp * num; |
302 |
ri->ri.dsta += num; |
303 |
state = !state; |
304 |
span++; |
305 |
if (span == end) |
306 |
{ |
307 |
ri->ri.dst = dst; |
308 |
ri->ri.dsta = dsta; |
309 |
return; |
310 |
} |
311 |
} |
312 |
num = x1 - x0; |
313 |
if (num && state) |
314 |
{ |
315 |
if (ri->ri.a == 255) |
316 |
ri->run_opaque(&ri->ri, num); |
317 |
else |
318 |
ri->run_alpha(&ri->ri, num); |
319 |
} |
320 |
} |
321 |
|
322 |
ri->ri.dst = dst; |
323 |
ri->ri.dsta = dsta; |
324 |
} |
325 |
|
326 |
static void artcontext_render_svp(const ArtSVP *svp, int x0, int y0, int x1, int y1, |
327 |
unsigned char r, unsigned char g, unsigned char b, unsigned char a, |
328 |
unsigned char *dst, int rowstride, |
329 |
unsigned char *dsta, int arowstride, int has_alpha, |
330 |
draw_info_t *di, |
331 |
unsigned int *clip_span, unsigned int *clip_index) |
332 |
{ |
333 |
svp_render_info_t ri; |
334 |
|
335 |
ri.x0 = x0; |
336 |
ri.x1 = x1; |
337 |
ri.y0 = y0; |
338 |
|
339 |
ri.ri.r = r; |
340 |
ri.ri.g = g; |
341 |
ri.ri.b = b; |
342 |
ri.real_a = ri.ri.a = a; |
343 |
|
344 |
ri.bpp = di->bytes_per_pixel; |
345 |
|
346 |
ri.ri.dst = dst; |
347 |
ri.rowstride = rowstride; |
348 |
|
349 |
ri.clip_span = clip_span; |
350 |
ri.clip_index = clip_index; |
351 |
|
352 |
if (has_alpha) |
353 |
{ |
354 |
ri.ri.dsta = dsta; |
355 |
ri.arowstride = arowstride; |
356 |
ri.run_alpha = di->render_run_alpha_a; |
357 |
ri.run_opaque = di->render_run_opaque_a; |
358 |
} |
359 |
else |
360 |
{ |
361 |
ri.run_alpha = di->render_run_alpha; |
362 |
ri.run_opaque = di->render_run_opaque; |
363 |
} |
364 |
|
365 |
art_svp_render_aa(svp, x0, y0, x1, y1, |
366 |
clip_span? render_svp_clipped_callback : render_svp_callback, &ri); |
367 |
} |
368 |
|
369 |
|
370 |
@implementation ARTGState (path) |
371 |
|
372 |
/* Fills in vp. If the rectangle is axis- (and optionally pixel)-aligned, |
373 |
also fills in the axis coordinates (x0/y0 is min) and returns 1. Otherwise |
374 |
returns 0. (Actually, if pixel is NO, it's enough that the edges remain |
375 |
within one pixel.) */ |
376 |
-(int) _axis_rectangle: (float)x : (float)y : (float)w : (float)h |
377 |
vpath: (ArtVpath *)vp |
378 |
axis: (int *)px0 : (int *)py0 : (int *)px1 : (int *)py1 |
379 |
pixel: (BOOL)pixel |
380 |
{ |
381 |
float matrix[6]; |
382 |
float det; |
383 |
int i; |
384 |
int x0, y0, x1, y1; |
385 |
|
386 |
if (w < 0) x += w, w = -w; |
387 |
if (h < 0) y += h, h = -h; |
388 |
|
389 |
matrix[0]= ctm->matrix.m11; |
390 |
matrix[1]=-ctm->matrix.m12; |
391 |
matrix[2]= ctm->matrix.m21; |
392 |
matrix[3]=-ctm->matrix.m22; |
393 |
matrix[4]= ctm->matrix.tx; |
394 |
matrix[5]=-ctm->matrix.ty + wi->sy; |
395 |
|
396 |
/* If the matrix is 'inverted', ie. if the determinant is negative, |
397 |
we need to flip the order of the vertices. Since it's a rectangle |
398 |
we can just swap vertex 1 and 3. */ |
399 |
det = matrix[0] * matrix[3] - matrix[1] * matrix[2]; |
400 |
|
401 |
vp[0].code = ART_MOVETO; |
402 |
vp[0].x = x * matrix[0] + y * matrix[2] + matrix[4]; |
403 |
vp[0].y = x * matrix[1] + y * matrix[3] + matrix[5]; |
404 |
|
405 |
i = det > 0? 3 : 1; |
406 |
vp[i].code = ART_LINETO; |
407 |
vp[i].x = vp[0].x + w * matrix[0]; |
408 |
vp[i].y = vp[0].y + w * matrix[1]; |
409 |
|
410 |
vp[2].code = ART_LINETO; |
411 |
vp[2].x = vp[0].x + w * matrix[0] + h * matrix[2]; |
412 |
vp[2].y = vp[0].y + w * matrix[1] + h * matrix[3]; |
413 |
|
414 |
i ^= 2; |
415 |
vp[i].code = ART_LINETO; |
416 |
vp[i].x = vp[0].x + h * matrix[2]; |
417 |
vp[i].y = vp[0].y + h * matrix[3]; |
418 |
|
419 |
vp[4].code = ART_LINETO; |
420 |
vp[4].x = vp[0].x; |
421 |
vp[4].y = vp[0].y; |
422 |
|
423 |
vp[5].code = ART_END; |
424 |
vp[5].x = vp[5].y = 0; |
425 |
|
426 |
/* Check if this rectangle is axis-aligned and on whole pixel |
427 |
boundaries. */ |
428 |
x0 = vp[0].x + 0.5; |
429 |
x1 = vp[2].x + 0.5; |
430 |
y0 = vp[0].y + 0.5; |
431 |
y1 = vp[2].y + 0.5; |
432 |
|
433 |
if (pixel) |
434 |
{ |
435 |
if (x0 > x1) |
436 |
*px0 = x1, *px1 = x0; |
437 |
else |
438 |
*px0 = x0, *px1 = x1; |
439 |
if (y0 > y1) |
440 |
*py0 = y1, *py1 = y0; |
441 |
else |
442 |
*py0 = y0, *py1 = y1; |
443 |
|
444 |
if (fabs(vp[0].x - vp[1].x) < 0.01 && fabs(vp[1].y - vp[2].y) < 0.01 && |
445 |
fabs(vp[0].x - x0) < 0.01 && fabs(vp[0].y - y0) < 0.01 && |
446 |
fabs(vp[2].x - x1) < 0.01 && fabs(vp[2].y - y1) < 0.01) |
447 |
{ |
448 |
return 1; |
449 |
} |
450 |
|
451 |
if (fabs(vp[0].y - vp[1].y) < 0.01 && fabs(vp[1].x - vp[2].x) < 0.01 && |
452 |
fabs(vp[0].x - x0) < 0.01 && fabs(vp[0].y - y0) < 0.01 && |
453 |
fabs(vp[2].x - x1) < 0.01 && fabs(vp[2].y - y1) < 0.01) |
454 |
{ |
455 |
return 1; |
456 |
} |
457 |
} |
458 |
else |
459 |
{ |
460 |
/* This is used when clipping, so we need to make sure we |
461 |
contain all pixels. */ |
462 |
if (vp[0].x < vp[2].x) |
463 |
*px0 = floor(vp[0].x), *px1 = ceil(vp[2].x); |
464 |
else |
465 |
*px0 = floor(vp[2].x), *px1 = ceil(vp[0].x); |
466 |
if (vp[0].y < vp[2].y) |
467 |
*py0 = floor(vp[0].y), *py1 = ceil(vp[2].y); |
468 |
else |
469 |
*py0 = floor(vp[2].y), *py1 = ceil(vp[0].y); |
470 |
|
471 |
if (floor(vp[0].x) == floor(vp[1].x) && |
472 |
floor(vp[0].y) == floor(vp[3].y) && |
473 |
floor(vp[1].y) == floor(vp[2].y) && |
474 |
floor(vp[2].x) == floor(vp[3].x)) |
475 |
{ |
476 |
return 1; |
477 |
} |
478 |
|
479 |
if (floor(vp[0].y) == floor(vp[1].y) && |
480 |
floor(vp[0].x) == floor(vp[3].x) && |
481 |
floor(vp[1].x) == floor(vp[2].x) && |
482 |
floor(vp[2].y) == floor(vp[3].y)) |
483 |
{ |
484 |
return 1; |
485 |
} |
486 |
} |
487 |
|
488 |
return 0; |
489 |
} |
490 |
|
491 |
|
492 |
-(ArtVpath *) _vpath_from_current_path: (BOOL)fill |
493 |
{ |
494 |
ArtBpath *bpath, *bp2; |
495 |
ArtVpath *vp; |
496 |
int i, j, c, cur_start, cur_line; |
497 |
NSPoint points[3]; |
498 |
NSBezierPathElement t; |
499 |
double matrix[6]; |
500 |
|
501 |
|
502 |
c = [path elementCount]; |
503 |
if (!c) |
504 |
return NULL; |
505 |
|
506 |
if (fill) |
507 |
bpath = art_new(ArtBpath, 2 * c + 1); |
508 |
else |
509 |
bpath = art_new(ArtBpath, c + 1); |
510 |
|
511 |
cur_start = -1; |
512 |
cur_line = 0; |
513 |
for (i = j = 0; i < c; i++) |
514 |
{ |
515 |
t = [path elementAtIndex: i associatedPoints: points]; |
516 |
switch (t) |
517 |
{ |
518 |
case NSMoveToBezierPathElement: |
519 |
/* When filling, the path must be closed, so if |
520 |
it isn't already closed, we fix that here. */ |
521 |
if (fill) |
522 |
{ |
523 |
if (cur_start != -1 && cur_line) |
524 |
{ |
525 |
if (bpath[j - 1].x3 != bpath[cur_start].x3 || |
526 |
bpath[j - 1].y3 != bpath[cur_start].y3) |
527 |
{ |
528 |
bpath[j].x3 = bpath[cur_start].x3; |
529 |
bpath[j].y3 = bpath[cur_start].y3; |
530 |
bpath[j].code = ART_LINETO; |
531 |
j++; |
532 |
} |
533 |
} |
534 |
bpath[j].code = ART_MOVETO; |
535 |
} |
536 |
else |
537 |
{ |
538 |
bpath[j].code = ART_MOVETO_OPEN; |
539 |
} |
540 |
bpath[j].x3 = points[0].x; |
541 |
bpath[j].y3 = points[0].y; |
542 |
cur_start = j; |
543 |
j++; |
544 |
cur_line = 0; |
545 |
break; |
546 |
|
547 |
case NSLineToBezierPathElement: |
548 |
cur_line++; |
549 |
bpath[j].code = ART_LINETO; |
550 |
bpath[j].x3 = points[0].x; |
551 |
bpath[j].y3 = points[0].y; |
552 |
j++; |
553 |
break; |
554 |
|
555 |
case NSCurveToBezierPathElement: |
556 |
cur_line++; |
557 |
bpath[j].code = ART_CURVETO; |
558 |
bpath[j].x1 = points[0].x; |
559 |
bpath[j].y1 = points[0].y; |
560 |
bpath[j].x2 = points[1].x; |
561 |
bpath[j].y2 = points[1].y; |
562 |
bpath[j].x3 = points[2].x; |
563 |
bpath[j].y3 = points[2].y; |
564 |
j++; |
565 |
break; |
566 |
|
567 |
case NSClosePathBezierPathElement: |
568 |
if (cur_start != -1 && cur_line) |
569 |
{ |
570 |
bpath[cur_start].code = ART_MOVETO; |
571 |
bpath[j].code = ART_LINETO; |
572 |
bpath[j].x3 = bpath[cur_start].x3; |
573 |
bpath[j].y3 = bpath[cur_start].y3; |
574 |
j++; |
575 |
} |
576 |
break; |
577 |
|
578 |
default: |
579 |
NSLog(@"invalid type %i\n", t); |
580 |
art_free(bpath); |
581 |
return NULL; |
582 |
} |
583 |
} |
584 |
|
585 |
if (fill && cur_start != -1 && cur_line) |
586 |
{ |
587 |
if (bpath[j - 1].x3 != bpath[cur_start].x3 || |
588 |
bpath[j - 1].y3 != bpath[cur_start].y3) |
589 |
{ |
590 |
bpath[j].x3 = bpath[cur_start].x3; |
591 |
bpath[j].y3 = bpath[cur_start].y3; |
592 |
bpath[j].code = ART_LINETO; |
593 |
j++; |
594 |
} |
595 |
} |
596 |
bpath[j].code = ART_END; |
597 |
|
598 |
matrix[0]= 1; |
599 |
matrix[1]= 0; |
600 |
matrix[2]= 0; |
601 |
matrix[3]=-1; |
602 |
matrix[4]= 0; |
603 |
matrix[5]= wi->sy; |
604 |
|
605 |
bp2 = art_bpath_affine_transform(bpath, matrix); |
606 |
art_free(bpath); |
607 |
|
608 |
vp = art_bez_path_to_vec(bp2, 0.5); |
609 |
art_free(bp2); |
610 |
|
611 |
return vp; |
612 |
} |
613 |
|
614 |
|
615 |
/** Clipping **/ |
616 |
|
617 |
typedef struct |
618 |
{ |
619 |
int x0, x1, y0, sy; |
620 |
|
621 |
int minx,maxx; |
622 |
int first_y,last_y; |
623 |
|
624 |
unsigned int *span; |
625 |
unsigned int *index; |
626 |
int span_size, num_span; |
627 |
|
628 |
unsigned int *cur_span; |
629 |
unsigned int *cur_index; |
630 |
} clip_info_t; |
631 |
|
632 |
|
633 |
static void clip_svp_callback(void *data, int y, int start, |
634 |
ArtSVPRenderAAStep *steps, int n_steps) |
635 |
{ |
636 |
clip_info_t *ci = data; |
637 |
int x; |
638 |
int alpha; |
639 |
BOOL state, nstate; |
640 |
|
641 |
alpha = start; |
642 |
|
643 |
ci->index[y - ci->y0 - ci->first_y] = ci->num_span; |
644 |
if (y-ci->y0<0 || y-ci->y0>=ci->sy) |
645 |
{ |
646 |
printf("weird y=%i (%i)\n",y,y-ci->y0); |
647 |
} |
648 |
|
649 |
/* empty line; very common case */ |
650 |
if (alpha < 0x10000 && !n_steps) |
651 |
{ |
652 |
if (ci->first_y == y - ci->y0) |
653 |
ci->first_y++; |
654 |
return; |
655 |
} |
656 |
|
657 |
ci->last_y = y - ci->y0 + 1; |
658 |
|
659 |
x = 0; |
660 |
state = alpha >= 0x10000; |
661 |
if (state) |
662 |
{ |
663 |
if (ci->num_span == ci->span_size) |
664 |
{ |
665 |
ci->span_size += 16; |
666 |
ci->span = realloc(ci->span, sizeof(unsigned int) * ci->span_size); |
667 |
} |
668 |
ci->span[ci->num_span++] = x; |
669 |
if (x < ci->minx) ci->minx = x; |
670 |
} |
671 |
|
672 |
|
673 |
for (; n_steps; n_steps--, steps++) |
674 |
{ |
675 |
alpha += steps->delta; |
676 |
x = steps->x - ci->x0; |
677 |
nstate = alpha >= 0x10000; |
678 |
if (state != nstate) |
679 |
{ |
680 |
if (ci->num_span == ci->span_size) |
681 |
{ |
682 |
ci->span_size += 16; |
683 |
ci->span = realloc(ci->span, sizeof(unsigned int) * ci->span_size); |
684 |
} |
685 |
ci->span[ci->num_span++] = x; |
686 |
if (x < ci->minx) ci->minx = x; |
687 |
if (x > ci->maxx) ci->maxx = x; |
688 |
state = nstate; |
689 |
} |
690 |
} |
691 |
if (state) |
692 |
{ |
693 |
if (ci->num_span == ci->span_size) |
694 |
{ |
695 |
ci->span_size += 16; |
696 |
ci->span = realloc(ci->span, sizeof(unsigned int) * ci->span_size); |
697 |
} |
698 |
x = ci->x1 - ci->x0; |
699 |
ci->span[ci->num_span++] = x; |
700 |
if (x > ci->maxx) ci->maxx = x; |
701 |
} |
702 |
} |
703 |
|
704 |
/* will free the passed in svp */ |
705 |
-(void) _clip_add_svp: (ArtSVP *)svp |
706 |
{ |
707 |
clip_info_t ci; |
708 |
ci.span = NULL; |
709 |
ci.index = malloc(sizeof(unsigned int) * (clip_sy + 1)); |
710 |
if (!ci.index) |
711 |
{ |
712 |
NSLog(@"Warning: out of memory calculating clipping spans (%i bytes)", |
713 |
sizeof(unsigned int) * (clip_sy + 1)); |
714 |
return; |
715 |
} |
716 |
ci.span_size = ci.num_span = 0; |
717 |
ci.x0 = clip_x0; |
718 |
ci.x1 = clip_x1; |
719 |
ci.y0 = clip_y0; |
720 |
ci.sy = clip_sy; |
721 |
|
722 |
ci.minx = ci.x1 - ci.x0; |
723 |
ci.maxx = 0; |
724 |
ci.first_y = 0; |
725 |
ci.last_y = -1; |
726 |
|
727 |
if (clip_span) |
728 |
{ |
729 |
NSLog(@"TODO: _clip_add_svp: with existing clip_span not implemented"); |
730 |
free(ci.index); |
731 |
return; |
732 |
} |
733 |
else |
734 |
{ |
735 |
art_svp_render_aa(svp, clip_x0, clip_y0, clip_x1, clip_y1, clip_svp_callback, &ci); |
736 |
clip_span = ci.span; |
737 |
clip_index = ci.index; |
738 |
clip_index[clip_sy - ci.first_y] = clip_num_span = ci.num_span; |
739 |
|
740 |
clip_y1 = clip_y0 + ci.last_y; |
741 |
clip_y0 += ci.first_y; |
742 |
clip_sy = clip_y1 - clip_y0; |
743 |
if (clip_y1 <= clip_y0) |
744 |
all_clipped = YES; |
745 |
|
746 |
if (ci.minx > 0) |
747 |
{ |
748 |
int i; |
749 |
for (i = 0; i < clip_num_span; i++) |
750 |
{ |
751 |
if (clip_span[i] < ci.minx) |
752 |
NSLog(@"_clip_add_svp: clip_span[i]<0 when adjusting for minx"); |
753 |
clip_span[i] -= ci.minx; |
754 |
if (clip_span[i] > ci.maxx - ci.minx) |
755 |
NSLog(@"_clip_add_svp: clip_span[i] too large when adjusting for minx"); |
756 |
} |
757 |
} |
758 |
|
759 |
clip_x1 = clip_x0 + ci.maxx; |
760 |
clip_x0 += ci.minx; |
761 |
clip_sx = clip_x1 - clip_x0; |
762 |
if (clip_x1 <= clip_x0) |
763 |
all_clipped = YES; |
764 |
} |
765 |
art_svp_free(svp); |
766 |
} |
767 |
|
768 |
-(void) _clip: (int)rule |
769 |
{ |
770 |
ArtVpath *vp; |
771 |
ArtSVP *svp; |
772 |
|
773 |
vp = [self _vpath_from_current_path: NO]; |
774 |
if (!vp) |
775 |
return; |
776 |
svp = art_svp_from_vpath(vp); |
777 |
art_free(vp); |
778 |
|
779 |
{ |
780 |
ArtSVP *svp2; |
781 |
ArtSvpWriter *svpw; |
782 |
|
783 |
svpw = art_svp_writer_rewind_new(rule); |
784 |
art_svp_intersector(svp, svpw); |
785 |
svp2 = art_svp_writer_rewind_reap(svpw); |
786 |
art_svp_free(svp); |
787 |
svp = svp2; |
788 |
} |
789 |
|
790 |
[self _clip_add_svp: svp]; |
791 |
} |
792 |
|
793 |
|
794 |
- (void) DPSclip |
795 |
{ |
796 |
[self _clip: ART_WIND_RULE_NONZERO]; |
797 |
} |
798 |
|
799 |
- (void) DPSeoclip |
800 |
{ |
801 |
[self _clip: ART_WIND_RULE_ODDEVEN]; |
802 |
} |
803 |
|
804 |
- (void) DPSrectclip: (float)x : (float)y : (float)w : (float)h |
805 |
{ |
806 |
ArtVpath vp[6]; |
807 |
ArtSVP *svp; |
808 |
int x0, y0, x1, y1; |
809 |
int axis_aligned; |
810 |
|
811 |
[self DPSnewpath]; |
812 |
|
813 |
if (all_clipped) |
814 |
return; |
815 |
|
816 |
if (!wi) |
817 |
{ |
818 |
all_clipped = YES; |
819 |
return; |
820 |
} |
821 |
|
822 |
axis_aligned = [self _axis_rectangle: x : y : w : h vpath: vp |
823 |
axis: &x0 : &y0 : &x1 : &y1 |
824 |
pixel: NO]; |
825 |
|
826 |
if (!axis_aligned) |
827 |
{ |
828 |
svp = art_svp_from_vpath(vp); |
829 |
[self _clip_add_svp: svp]; |
830 |
return; |
831 |
} |
832 |
|
833 |
if (x0 > clip_x0) |
834 |
clip_x0 = x0; |
835 |
if (y0 > clip_y0) |
836 |
clip_y0 = y0; |
837 |
|
838 |
if (x1 < clip_x1) |
839 |
clip_x1 = x1; |
840 |
if (y1 < clip_y1) |
841 |
clip_y1 = y1; |
842 |
|
843 |
if (clip_x0 >= clip_x1 || clip_y0 >= clip_y1) |
844 |
{ |
845 |
all_clipped = YES; |
846 |
} |
847 |
|
848 |
clip_sx = clip_x1 - clip_x0; |
849 |
clip_sy = clip_y1 - clip_y0; |
850 |
} |
851 |
|
852 |
- (void) DPSinitclip; |
853 |
{ |
854 |
if (!wi) |
855 |
{ |
856 |
all_clipped = YES; |
857 |
return; |
858 |
} |
859 |
|
860 |
clip_x0 = clip_y0 = 0; |
861 |
clip_x1 = wi->sx; |
862 |
clip_y1 = wi->sy; |
863 |
all_clipped = NO; |
864 |
clip_sx = clip_x1 - clip_x0; |
865 |
clip_sy = clip_y1 - clip_y0; |
866 |
|
867 |
if (clip_span) |
868 |
{ |
869 |
free(clip_span); |
870 |
free(clip_index); |
871 |
clip_span = clip_index = NULL; |
872 |
clip_num_span = 0; |
873 |
} |
874 |
} |
875 |
|
876 |
|
877 |
/** Filling **/ |
878 |
|
879 |
-(void) _fill: (int)rule |
880 |
{ |
881 |
ArtVpath *vp; |
882 |
ArtSVP *svp; |
883 |
|
884 |
if (!wi || !wi->data) return; |
885 |
if (all_clipped) return; |
886 |
if (!fill_color[3]) return; |
887 |
|
888 |
vp = [self _vpath_from_current_path: YES]; |
889 |
if (!vp) |
890 |
return; |
891 |
svp = art_svp_from_vpath(vp); |
892 |
art_free(vp); |
893 |
|
894 |
{ |
895 |
ArtSVP *svp2; |
896 |
ArtSvpWriter *svpw; |
897 |
|
898 |
svpw = art_svp_writer_rewind_new(rule); |
899 |
art_svp_intersector(svp, svpw); |
900 |
svp2 = art_svp_writer_rewind_reap(svpw); |
901 |
art_svp_free(svp); |
902 |
svp = svp2; |
903 |
} |
904 |
|
905 |
|
906 |
artcontext_render_svp(svp, clip_x0, clip_y0, clip_x1, clip_y1, |
907 |
fill_color[0], fill_color[1], fill_color[2], fill_color[3], |
908 |
CLIP_DATA, wi->bytes_per_line, |
909 |
wi->has_alpha? wi->alpha + clip_x0 + clip_y0 * wi->sx : NULL, wi->sx, |
910 |
wi->has_alpha, |
911 |
&DI, clip_span, clip_index); |
912 |
|
913 |
art_svp_free(svp); |
914 |
|
915 |
[path removeAllPoints]; |
916 |
|
917 |
UPDATE_UNBUFFERED |
918 |
} |
919 |
|
920 |
- (void) DPSeofill |
921 |
{ |
922 |
[self _fill: ART_WIND_RULE_ODDEVEN]; |
923 |
} |
924 |
|
925 |
- (void) DPSfill |
926 |
{ |
927 |
[self _fill: ART_WIND_RULE_NONZERO]; |
928 |
} |
929 |
|
930 |
- (void) DPSrectfill: (float)x : (float)y : (float)w : (float)h |
931 |
{ |
932 |
ArtVpath vp[6]; |
933 |
ArtSVP *svp; |
934 |
int x0, y0, x1, y1; |
935 |
int axis_aligned; |
936 |
|
937 |
if (!wi || !wi->data) return; |
938 |
if (all_clipped) return; |
939 |
if (!fill_color[3]) return; |
940 |
|
941 |
axis_aligned = [self _axis_rectangle: x : y : w : h vpath: vp |
942 |
axis: &x0 : &y0 : &x1 : &y1 |
943 |
pixel: YES]; |
944 |
|
945 |
if (!axis_aligned || clip_span) |
946 |
{ |
947 |
/* Not properly aligned. Handle the general case. */ |
948 |
svp = art_svp_from_vpath(vp); |
949 |
|
950 |
artcontext_render_svp(svp, clip_x0, clip_y0, clip_x1, clip_y1, |
951 |
fill_color[0], fill_color[1], fill_color[2], fill_color[3], |
952 |
CLIP_DATA, wi->bytes_per_line, |
953 |
wi->has_alpha? wi->alpha + clip_x0 + clip_y0 * wi->sx : NULL, wi->sx, |
954 |
wi->has_alpha, |
955 |
&DI, clip_span, clip_index); |
956 |
|
957 |
art_svp_free(svp); |
958 |
UPDATE_UNBUFFERED |
959 |
return; |
960 |
} |
961 |
|
962 |
/* optimize axis- and pixel-aligned rectangles */ |
963 |
{ |
964 |
unsigned char *dst = CLIP_DATA; |
965 |
unsigned char *dsta = wi->alpha + clip_x0 + clip_y0 * wi->sx; |
966 |
render_run_t ri; |
967 |
|
968 |
x0 -= clip_x0; |
969 |
x1 -= clip_x0; |
970 |
if (x0 <= 0) |
971 |
x0 = 0; |
972 |
else |
973 |
{ |
974 |
dst += x0 * DI.bytes_per_pixel; |
975 |
dsta += x0; |
976 |
} |
977 |
if (x1 > clip_sx) x1 = clip_sx; |
978 |
|
979 |
x1 -= x0; |
980 |
if (x1 <= 0) |
981 |
return; |
982 |
|
983 |
y0 -= clip_y0; |
984 |
y1 -= clip_y0; |
985 |
if (y0 <= 0) |
986 |
y0 = 0; |
987 |
else |
988 |
{ |
989 |
dst += y0 * wi->bytes_per_line; |
990 |
dsta += y0 * wi->sx; |
991 |
} |
992 |
if (y1 > clip_sy) y1 = clip_sy; |
993 |
|
994 |
if (y1 <= y0) |
995 |
return; |
996 |
|
997 |
ri.dst = dst; |
998 |
ri.r = fill_color[0]; |
999 |
ri.g = fill_color[1]; |
1000 |
ri.b = fill_color[2]; |
1001 |
ri.a = fill_color[3]; |
1002 |
if (wi->has_alpha) |
1003 |
{ |
1004 |
ri.dsta = dsta; |
1005 |
|
1006 |
if (fill_color[3] == 255) |
1007 |
{ |
1008 |
for (; y0 < y1; y0++, ri.dst += wi->bytes_per_line, ri.dsta += wi->sx) |
1009 |
RENDER_RUN_OPAQUE_A(&ri, x1); |
1010 |
} |
1011 |
else |
1012 |
{ |
1013 |
for (; y0 < y1; y0++, ri.dst += wi->bytes_per_line, ri.dsta += wi->sx) |
1014 |
RENDER_RUN_ALPHA_A(&ri, x1); |
1015 |
} |
1016 |
} |
1017 |
else |
1018 |
{ |
1019 |
if (fill_color[3] == 255) |
1020 |
{ |
1021 |
for (; y0 < y1; y0++, ri.dst += wi->bytes_per_line) |
1022 |
RENDER_RUN_OPAQUE(&ri, x1); |
1023 |
} |
1024 |
else |
1025 |
{ |
1026 |
for (; y0 < y1; y0++, ri.dst += wi->bytes_per_line) |
1027 |
RENDER_RUN_ALPHA(&ri, x1); |
1028 |
} |
1029 |
} |
1030 |
UPDATE_UNBUFFERED |
1031 |
} |
1032 |
} |
1033 |
|
1034 |
|
1035 |
/** Stroking **/ |
1036 |
|
1037 |
/* will free the passed in vpath */ |
1038 |
-(void) _stroke: (ArtVpath *)vp : (BOOL)adjust_dash_ofs |
1039 |
{ |
1040 |
double temp_scale; |
1041 |
ArtVpath *vp2; |
1042 |
ArtSVP *svp; |
1043 |
|
1044 |
/* TODO: this is a hack, but it's better than nothing */ |
1045 |
/* since we flip vertically, the signs here should really be |
1046 |
inverted, but the fabs() means that it doesn't matter */ |
1047 |
temp_scale = sqrt(fabs(ctm->matrix.m11 * ctm->matrix.m22 - |
1048 |
ctm->matrix.m12 * ctm->matrix.m21)); |
1049 |
if (temp_scale <= 0) temp_scale = 1; |
1050 |
|
1051 |
if (do_dash) |
1052 |
{ |
1053 |
/* try to adjust the offset so dashes appear on pixel boundaries |
1054 |
(otherwise it turns into an antialiased blur) */ |
1055 |
int i; |
1056 |
|
1057 |
if (adjust_dash_ofs) |
1058 |
dash.offset += (((int)line_width) & 1) / 2.0; |
1059 |
|
1060 |
for (i = 0; i < dash.n_dash; i++) |
1061 |
dash.dash[i] *= temp_scale; |
1062 |
dash.offset *= temp_scale; |
1063 |
vp2 = art_vpath_dash(vp, &dash); |
1064 |
dash.offset /= temp_scale; |
1065 |
for (i = 0; i < dash.n_dash; i++) |
1066 |
dash.dash[i] /= temp_scale; |
1067 |
art_free(vp); |
1068 |
vp = vp2; |
1069 |
|
1070 |
if (adjust_dash_ofs) |
1071 |
dash.offset -= (((int)line_width) & 1) / 2.0; |
1072 |
} |
1073 |
|
1074 |
svp = art_svp_vpath_stroke(vp, linejoinstyle, linecapstyle, |
1075 |
temp_scale * line_width, miter_limit, 0.5); |
1076 |
art_free(vp); |
1077 |
|
1078 |
artcontext_render_svp(svp, clip_x0, clip_y0, clip_x1, clip_y1, |
1079 |
stroke_color[0], stroke_color[1], stroke_color[2], stroke_color[3], |
1080 |
CLIP_DATA, wi->bytes_per_line, |
1081 |
wi->has_alpha? wi->alpha + clip_x0 + clip_y0 * wi->sx : NULL, wi->sx, |
1082 |
wi->has_alpha, |
1083 |
&DI, clip_span, clip_index); |
1084 |
|
1085 |
art_svp_free(svp); |
1086 |
UPDATE_UNBUFFERED |
1087 |
} |
1088 |
|
1089 |
- (void) DPSrectstroke: (float)x : (float)y : (float)w : (float)h |
1090 |
{ |
1091 |
ArtVpath *vp, *vp2; |
1092 |
double matrix[6]; |
1093 |
|
1094 |
if (!wi || !wi->data) return; |
1095 |
if (all_clipped) return; |
1096 |
if (!stroke_color[3]) return; |
1097 |
|
1098 |
vp = art_new(ArtVpath, 6); |
1099 |
|
1100 |
if (line_width == (int)line_width && ((int)line_width) & 1) |
1101 |
{ /* TODO: only do this if stroke-adjust is on? */ |
1102 |
/* TODO: check coordinates to see how much we should adjust */ |
1103 |
x += 0.5; |
1104 |
y += 0.5; |
1105 |
w -= 1; |
1106 |
h -= 1; |
1107 |
} |
1108 |
|
1109 |
vp[0].code = ART_MOVETO; |
1110 |
vp[0].x = x; vp[0].y = y; |
1111 |
|
1112 |
vp[1].code = ART_LINETO; |
1113 |
vp[1].x = x + w; vp[1].y = y; |
1114 |
|
1115 |
vp[2].code = ART_LINETO; |
1116 |
vp[2].x = x + w; vp[2].y = y + h; |
1117 |
|
1118 |
vp[3].code = ART_LINETO; |
1119 |
vp[3].x = x; vp[3].y = y + h; |
1120 |
|
1121 |
vp[4].code = ART_LINETO; |
1122 |
vp[4].x = x; vp[4].y = y; |
1123 |
|
1124 |
vp[5].code = ART_END; |
1125 |
vp[5].x = vp[5].y = 0; |
1126 |
|
1127 |
matrix[0] = ctm->matrix.m11; |
1128 |
matrix[1] =-ctm->matrix.m12; |
1129 |
matrix[2] = ctm->matrix.m21; |
1130 |
matrix[3] =-ctm->matrix.m22; |
1131 |
matrix[4] = ctm->matrix.tx; |
1132 |
matrix[5] =-ctm->matrix.ty + wi->sy; |
1133 |
|
1134 |
vp2 = art_vpath_affine_transform(vp, matrix); |
1135 |
art_free(vp); |
1136 |
vp = vp2; |
1137 |
|
1138 |
[self _stroke: vp : YES]; |
1139 |
} |
1140 |
|
1141 |
- (void) DPSstroke |
1142 |
{ |
1143 |
/* TODO: Resolve line-width and dash scaling issues. The way this is |
1144 |
currently done is the most obvious libart approach: |
1145 |
|
1146 |
1. convert the NSBezierPath to an ArtBpath |
1147 |
2. transform the Bpath |
1148 |
3. convert the Bpath to a Vpath, approximating the curves with lines |
1149 |
(1-3 are done in -_vpath_from_current_path:) |
1150 |
|
1151 |
4. apply dashing to the Vpath |
1152 |
(art_vpath_dash, called below) |
1153 |
5. stroke and convert the Vpath to an svp |
1154 |
(art_svp_vpath_stroke, called below) |
1155 |
|
1156 |
To do this correctly, we need to do dashing and stroking (4 and part of 5) |
1157 |
in user space. It is possible to do the transform _after_ step 5 (although |
1158 |
it's less efficient), but we want to do any curve approximation (3, and 5 if |
1159 |
there are round line ends or joins) in device space. |
1160 |
|
1161 |
The best way to solve this is probably to keep doing the transform first, |
1162 |
and to add transform-aware dashing and stroking functions to libart. |
1163 |
|
1164 |
Currently, a single scale value is applied to dashing and stroking. This |
1165 |
will give correct results as long as both axises are scaled the same. |
1166 |
|
1167 |
*/ |
1168 |
ArtVpath *vp; |
1169 |
|
1170 |
if (!wi || !wi->data) return; |
1171 |
if (all_clipped) return; |
1172 |
if (!stroke_color[3]) return; |
1173 |
|
1174 |
/* TODO: this is wrong. we should transform _after_ we dash and |
1175 |
stroke */ |
1176 |
vp = [self _vpath_from_current_path: NO]; |
1177 |
if (!vp) |
1178 |
return; |
1179 |
|
1180 |
[self _stroke: vp : NO]; |
1181 |
|
1182 |
[path removeAllPoints]; |
1183 |
} |
1184 |
|
1185 |
@end |
1186 |
|
1187 |
|
1188 |
@interface ARTGState (path_testing) |
1189 |
-(void) GScurrentpath: (NSBezierPath **)p; |
1190 |
@end |
1191 |
|
1192 |
@implementation ARTGState (path_testing) |
1193 |
-(void) GScurrentpath: (NSBezierPath **)p |
1194 |
{ |
1195 |
*p = [path copy]; |
1196 |
} |
1197 |
@end |
1198 |
|
1199 |
@implementation ARTContext (path_testing) |
1200 |
/* TODO: this is just for testing */ |
1201 |
-(void) GScurrentpath: (NSBezierPath **)p |
1202 |
{ |
1203 |
[(ARTGState *)gstate GScurrentpath: p]; |
1204 |
} |
1205 |
@end |
1206 |
|