/[gnustep]/gnustep/core/back/Source/art/path.m
ViewVC logotype

Contents of /gnustep/core/back/Source/art/path.m

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.13 - (show annotations) (download)
Sat Oct 4 12:30:33 2003 UTC (20 years, 7 months ago) by alexm
Branch: MAIN
CVS Tags: back-0_9_1, alex_latest_semistable
Changes since 1.12: +2 -2 lines
(-DPSrectclip::::): Make sure -DPSnewpath is called on all paths.

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

savannah-hackers-public@gnu.org
ViewVC Help
Powered by ViewVC 1.1.26