fix crashes reported by Debian Cylab Mayhem Team
[swftools.git] / lib / gfxtools.c
blob9b53a75368f85d29d238a56e0d5b7dcbfe729948
1 /* gfxtools.c
3 Various utility functions for dealing with gfxdevices.
5 Part of the swftools package.
7 Copyright (c) 2005 Matthias Kramm <kramm@quiss.org>
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <memory.h>
26 #include <math.h>
27 #include <string.h>
28 #include <assert.h>
29 #include <ctype.h>
30 #include "gfxtools.h"
31 #include "gfxfont.h"
32 #include "jpeg.h"
33 #include "q.h"
35 typedef struct _linedraw_internal
37 gfxline_t*start;
38 gfxline_t*next;
39 gfxcoord_t x0,y0;
40 char has_moveto;
41 } linedraw_internal_t;
43 static void linedraw_moveTo(gfxdrawer_t*d, gfxcoord_t x, gfxcoord_t y)
45 linedraw_internal_t*i = (linedraw_internal_t*)d->internal;
46 gfxline_t*l = (gfxline_t*)rfx_alloc(sizeof(gfxline_t));
47 l->type = gfx_moveTo;
48 i->has_moveto = 1;
49 i->x0 = x;
50 i->y0 = y;
51 l->sx = l->sy = 0;
52 d->x = l->x = x;
53 d->y = l->y = y;
54 l->next = 0;
55 if(i->next)
56 i->next->next = l;
57 i->next = l;
58 if(!i->start)
59 i->start = l;
61 static void linedraw_lineTo(gfxdrawer_t*d, gfxcoord_t x, gfxcoord_t y)
63 linedraw_internal_t*i = (linedraw_internal_t*)d->internal;
64 if(!i->has_moveto) {
65 /* starts with a line, not with a moveto. As this is the first
66 entry in the list, this is probably *meant* to be a moveto */
67 linedraw_moveTo(d, x, y);
68 return;
71 gfxline_t*l = (gfxline_t*)rfx_alloc(sizeof(gfxline_t));
72 l->type = gfx_lineTo;
73 d->x = l->x = x;
74 d->y = l->y = y;
76 l->next = 0;
77 if(i->next)
78 i->next->next = l;
79 i->next = l;
80 if(!i->start)
81 i->start = l;
83 static void linedraw_splineTo(gfxdrawer_t*d, gfxcoord_t sx, gfxcoord_t sy, gfxcoord_t x, gfxcoord_t y)
85 linedraw_internal_t*i = (linedraw_internal_t*)d->internal;
86 if(!i->has_moveto) {
87 linedraw_moveTo(d, x, y);
88 return;
91 gfxline_t*l = (gfxline_t*)rfx_alloc(sizeof(gfxline_t));
92 l->type = gfx_splineTo;
93 d->x = l->x = x;
94 d->y = l->y = y;
95 l->sx = sx;
96 l->sy = sy;
97 l->next = 0;
98 if(i->next)
99 i->next->next = l;
100 i->next = l;
101 if(!i->start)
102 i->start = l;
104 static void linedraw_close(gfxdrawer_t*d)
106 linedraw_internal_t*i = (linedraw_internal_t*)d->internal;
107 if(!i->has_moveto)
108 return;
109 linedraw_lineTo(d, i->x0, i->y0);
110 i->has_moveto = 0;
111 i->x0 = 0;
112 i->y0 = 0;
114 static void* linedraw_result(gfxdrawer_t*d)
116 linedraw_internal_t*i = (linedraw_internal_t*)d->internal;
117 void*result = (void*)i->start;
118 rfx_free(i);
119 memset(d, 0, sizeof(gfxdrawer_t));
120 return result;
123 void gfxdrawer_target_gfxline(gfxdrawer_t*d)
125 linedraw_internal_t*i = (linedraw_internal_t*)rfx_calloc(sizeof(linedraw_internal_t));
126 d->x = 0x7fffffff;
127 d->y = 0x7fffffff;
128 d->internal = i;
129 d->moveTo = linedraw_moveTo;
130 d->lineTo = linedraw_lineTo;
131 d->splineTo = linedraw_splineTo;
132 d->close = linedraw_close;
133 d->result = linedraw_result;
136 typedef struct _qspline_abc
138 double ax,bx,cx;
139 double ay,by,cy;
140 } qspline_abc_t;
142 typedef struct qspline_t
144 gfxpoint_t start;
145 gfxpoint_t control;
146 gfxpoint_t end;
147 } qspline_t;
149 typedef struct cspline_t
151 gfxpoint_t start;
152 gfxpoint_t control1;
153 gfxpoint_t control2;
154 gfxpoint_t end;
155 } cspline_t;
157 static void mkspline(qspline_abc_t*s, double x, double y, gfxline_t*l)
160 Form 1: x = t*t*l->x + 2*t*(1-t)*l->sx + (1-t)*(1-t)*x;
161 Form 2: x = a*t*t + b*t + c
163 s->cx = x; s->bx = 2*l->sx - 2*x; s->ax = l->x - 2*l->sx + x;
164 s->cy = y; s->by = 2*l->sy - 2*y; s->ay = l->y - 2*l->sy + y;
167 static void spline_get_controlpoint(qspline_abc_t*q, double t1, double t2, double*dx, double*dy)
169 double dt = t2-t1;
170 double nax = q->ax*dt*dt;
171 double nay = q->ay*dt*dt;
172 double nbx = 2*q->ax*dt*t1 + q->bx*dt;
173 double nby = 2*q->ay*dt*t1 + q->by*dt;
174 double ncx = q->ax*t1*t1 + q->bx*t1 + q->cx;
175 double ncy = q->ay*t1*t1 + q->by*t1 + q->cy;
176 *dx = ncx + nbx/2;
177 *dy = ncy + nby/2;
180 static double get_spline_len(qspline_abc_t*s)
182 int parts = (int)(sqrt(fabs(s->ax) + fabs(s->ay))*3);
183 int i;
184 double len = 0;
185 double r;
186 double r2;
187 if(parts < 3) parts = 3;
188 r = 1.0/parts;
189 r2 = 1.0/(parts*parts);
190 for(i=0;i<parts;i++)
192 double dx = s->ax*(2*i+1)*r2 + s->bx*r;
193 double dy = s->ay*(2*i+1)*r2 + s->by*r;
194 len += sqrt(dx*dx+dy*dy);
196 /*printf("Spline from %f,%f to %f,%f has len %f (%f)\n", s->cx, s->cy,
197 s->cx + s->bx + s->ax,
198 s->cy + s->by + s->ay, len,
199 sqrt((s->bx + s->ax)*(s->bx + s->ax) + (s->by + s->ay)*(s->by + s->ay))
201 assert(len+0.5 >= sqrt((s->bx + s->ax)*(s->bx + s->ax) + (s->by + s->ay)*(s->by + s->ay)));
203 return len;
206 void gfxtool_draw_dashed_line(gfxdrawer_t*d, gfxline_t*line, float*r, float phase)
208 double x=0,y=0;
209 double linepos = 0,nextpos = 0;
210 char on = 0;
211 int apos=0;
213 if(line && line->type != gfx_moveTo) {
214 fprintf(stderr, "gfxtool: outline doesn't start with a moveTo");
215 return;
218 int i;
219 double dashlen=0;
220 for(i=0;r[i]>=0;i++) {
221 dashlen+=r[i];
223 if(!r || (r[0]<=0 && r[0]>-0.01) || dashlen<0.001) {
224 // no dashing. just draw the thing
225 while(line) {
226 if(line->type == gfx_moveTo) {
227 d->moveTo(d, line->x, line->y);
228 } else if(line->type == gfx_lineTo) {
229 d->lineTo(d, line->x, line->y);
230 } else if(line->type == gfx_splineTo) {
231 d->splineTo(d, line->sx, line->sy, line->x, line->y);
233 line = line->next;
235 return;
238 if(phase < 0) {
239 phase = -phase;
242 if(r[0]<0 || phase<0) {
243 fprintf(stderr, "gfxtool: invalid (negative) dashes: %f, phase=%f\n", r[0], phase);
244 return;
247 for(;line;line=line->next) {
248 if(line->type == gfx_moveTo) {
249 d->moveTo(d, line->x, line->y);
250 on = 1; nextpos = r[0]; apos = 0; linepos = 0;
251 x = line->x; y = line->y;
252 while(linepos < phase) {
253 //printf("[+] linepos: %f, phase: %f, on:%d, apos:%d nextpos:%f\n", linepos, phase, on, apos, nextpos);
254 linepos += r[apos];
255 if(linepos < phase) {
256 on ^= 1;
257 if(r[++apos]<0)
258 apos = 0;
259 nextpos += r[apos];
262 linepos = phase;
263 //printf("[k] linepos: %f, phase: %f, on:%d, apos:%d nextpos:%f \n", linepos, phase, on, apos, nextpos);
264 } else if(line->type == gfx_lineTo) {
265 double dx = line->x - x;
266 double dy = line->y - y;
267 double len = sqrt(dx*dx+dy*dy);
268 double vx;
269 double vy;
270 double lineend = linepos+len;
271 if(len==0)
272 continue;
273 vx = dx/len;
274 vy = dy/len;
275 assert(nextpos>=linepos);
276 //printf("(line) on:%d apos: %d nextpos: %f, line pos: %f, line end: %f\n", on, apos, nextpos, linepos, linepos+len);
277 while(nextpos<lineend) {
278 double nx = x + vx*(nextpos-linepos);
279 double ny = y + vy*(nextpos-linepos);
280 if(on) {d->lineTo(d, nx,ny);/*printf("lineTo %f\n", nextpos);*/}
281 else {d->moveTo(d, nx,ny);/*printf("moveTo %f\n", nextpos);*/}
282 on^=1;
283 if(r[++apos]<0)
284 apos = 0;
285 nextpos+=r[apos];
287 linepos = lineend;
288 if(on) {
289 //printf("lineTo %f\n", 1.0);
290 d->lineTo(d, line->x,line->y);
292 x = line->x; y = line->y;
293 } else if(line->type == gfx_splineTo) {
294 qspline_abc_t q;
295 double len, lineend,lastt;
296 mkspline(&q, x, y, line);
298 len = get_spline_len(&q);
299 //printf("%f %f -> %f %f, len: %f\n", x, y, line->x, line->y, len);
300 if(len==0)
301 continue;
302 lineend = linepos+len;
303 lastt = 0;
304 if(nextpos<linepos)
305 printf("%f !< %f\n", nextpos, linepos);
306 assert(nextpos>=linepos);
307 //printf("(spline) on:%d apos: %d nextpos: %f, line pos: %f, line end: %f\n", on, apos, nextpos, linepos, linepos+len);
308 while(nextpos<lineend) {
309 double t = (nextpos-linepos)/len;
310 //printf("%f (%f-%f) apos=%d r[apos]=%f\n", t, nextpos, linepos, apos, r[apos]);
311 double nx = q.ax*t*t+q.bx*t+q.cx;
312 double ny = q.ay*t*t+q.by*t+q.cy;
313 if(on) {
314 double sx,sy;
315 spline_get_controlpoint(&q, lastt, t, &sx, &sy);
316 d->splineTo(d, sx, sy, nx,ny);
317 //printf("splineTo %f\n", nextpos);
318 } else {
319 d->moveTo(d, nx,ny);
320 //printf("moveTo %f\n", nextpos);
322 lastt = t;
323 on^=1;
324 if(r[++apos]<0)
325 apos = 0;
326 nextpos+=r[apos];
328 linepos = lineend;
329 if(on) {
330 double sx,sy;
331 spline_get_controlpoint(&q, lastt, 1, &sx, &sy);
332 d->splineTo(d, sx, sy, line->x,line->y);
333 //printf("splineTo %f\n", 1.0);
335 x = line->x; y = line->y;
340 static char* getToken(const char**p)
342 const char*start;
343 char*result;
344 while(**p && strchr(" ,()\t\n\r", **p)) {
345 (*p)++;
347 start = *p;
348 if (strchr("LMlm", **p) && (isdigit(*(*p+1))||strchr("+-", *(*p+1)))) {
349 (*p)++;
350 } else while(**p && !strchr(" ,()\t\n\r", **p)) {
351 (*p)++;
353 result = (char*)malloc((*p)-start+1);
354 memcpy(result,start,(*p)-start+1);
355 result[(*p)-start] = 0;
356 return result;
359 static float getFloat(const char** p)
361 char* token = getToken(p);
362 float result = atof(token);
363 free(token);
364 return result;
367 gfxline_t*gfxline_fromstring(const char*string)
369 gfxdrawer_t d;
370 gfxdrawer_target_gfxline(&d);
372 const char*p = string;
373 while(*p) {
374 char*token = getToken(&p);
375 if(!token)
376 break;
377 if (!*token) {
378 free(token);
379 break;
381 if(!strcmp(token, "M")) {
382 double x = getFloat(&p);
383 double y = getFloat(&p);
384 d.moveTo(&d, x, y);
385 } else if(!strncmp(token, "L", 1)) {
386 double x = getFloat(&p);
387 double y = getFloat(&p);
388 d.lineTo(&d, x, y);
389 } else if(!strncmp(token, "C", 1)) {
390 double x1 = getFloat(&p);
391 double y1 = getFloat(&p);
392 double x2 = getFloat(&p);
393 double y2 = getFloat(&p);
394 double x3 = getFloat(&p);
395 double y3 = getFloat(&p);
396 gfxdraw_cubicTo(&d, x1,y1, x2,y2, x3,y3, 0.9);
397 } else if(!strncmp(token, "z", 1)) {
398 //ignore
399 } else
400 fprintf(stderr, "gfxdraw: Warning: unknown primitive '%s'\n", token);
401 free(token);
403 gfxline_t*line = d.result(&d);
404 return line;
408 gfxline_t * gfxline_clone(gfxline_t*line)
410 gfxline_t*dest = 0;
411 gfxline_t*pos = 0;
412 while(line) {
413 gfxline_t*n = (gfxline_t*)rfx_calloc(sizeof(gfxline_t));
414 *n = *line;
415 n->next = 0;
416 if(!pos) {
417 dest = pos = n;
418 } else {
419 pos->next = n;
420 pos = n;
422 line = line->next;
424 return dest;
427 static char splineIsStraight(double x, double y, gfxline_t*l)
429 if(l->type == gfx_moveTo)
430 return 0;
431 if(l->type == gfx_lineTo)
432 return 1;
433 double dx = l->x-x;
434 double dy = l->y-y;
435 double sx = l->sx-x;
436 double sy = l->sy-y;
437 if(fabs(dx*sy - dy*sx) < 0.000001 && (dx*sx + dy*sy) >= 0) {
438 return 1;
440 return 0;
443 void gfxline_optimize(gfxline_t*line)
445 gfxline_t*l = line;
446 /* step 1: convert splines to lines, where possible */
447 double x=0,y=0;
448 while(l) {
449 if(l->type == gfx_splineTo && splineIsStraight(x,y,l)) {
450 l->type = gfx_lineTo;
452 x = l->x;
453 y = l->y;
454 l = l->next;
456 /* step 2: combine adjacent lines and splines, where possible */
457 l = line;
458 while(l && l->next) {
459 gfxline_t*next = l->next;
460 char combine = 0;
461 double sx=0,sy=0;
462 if(l->type == gfx_lineTo && next->type == gfx_lineTo) {
463 double dx = l->x-x;
464 double dy = l->y-y;
465 double nx = next->x-l->x;
466 double ny = next->y-l->y;
467 if(fabs(dx*ny - dy*nx) < 0.000001 && (dx*nx + dy*ny) >= 0) {
468 combine = 1;
470 } else if(l->type == gfx_splineTo && next->type == gfx_splineTo) {
471 /* TODO */
473 if(combine) {
474 l->next = next->next;
475 next->next = 0;
476 l->x = next->x;
477 l->y = next->y;
478 l->sx = sx;
479 l->sy = sy;
480 rfx_free(next);
481 } else {
482 x = l->x;
483 y = l->y;
484 l = l->next;
489 gfxline_t* gfxtool_dash_line(gfxline_t*line, float*dashes, float phase)
491 gfxdrawer_t d;
492 gfxline_t*result;
493 gfxdrawer_target_gfxline(&d);
494 gfxtool_draw_dashed_line(&d, line, dashes, phase);
495 result= (gfxline_t*)d.result(&d);
496 return result;
499 void gfxline_show(gfxline_t*l, FILE*fi)
501 while(l) {
502 if(l->type == gfx_moveTo) {
503 fprintf(fi, "moveTo %.2f,%.2f\n", l->x, l->y);
505 if(l->type == gfx_lineTo) {
506 fprintf(fi, "lineTo %.2f,%.2f\n", l->x, l->y);
508 if(l->type == gfx_splineTo) {
509 fprintf(fi, "splineTo %.2f,%.2f %.2f,%.2f\n", l->sx, l->sy, l->x, l->y);
511 l = l->next;
515 void gfxline_free(gfxline_t*l)
517 if(l && (l+1) == l->next) {
518 /* flattened */
519 rfx_free(l);
520 } else {
521 gfxline_t*next;
522 while(l) {
523 next = l->next;
524 l->next = 0;
525 rfx_free(l);
526 l = next;
531 static inline gfxpoint_t cspline_getpoint(const struct cspline_t*s, double t)
533 gfxpoint_t p;
534 double tt = t*t;
535 double ttt = tt*t;
536 double mt = (1-t);
537 double mtmt = mt*(1-t);
538 double mtmtmt = mtmt*(1-t);
539 p.x= s->end.x*ttt + 3*s->control2.x*tt*mt
540 + 3*s->control1.x*t*mtmt + s->start.x*mtmtmt;
541 p.y= s->end.y*ttt + 3*s->control2.y*tt*mt
542 + 3*s->control1.y*t*mtmt + s->start.y*mtmtmt;
543 return p;
545 static gfxpoint_t qspline_getpoint(const qspline_t*s, double t)
547 gfxpoint_t p;
548 p.x= s->end.x*t*t + 2*s->control.x*t*(1-t) + s->start.x*(1-t)*(1-t);
549 p.y= s->end.y*t*t + 2*s->control.y*t*(1-t) + s->start.y*(1-t)*(1-t);
550 return p;
553 static int approximate3(const cspline_t*s, qspline_t*q, int size, double quality2)
555 unsigned int gran = 0;
556 unsigned int istep = 0x80000000;
557 unsigned int istart = 0;
558 int num = 0;
559 int level = 0;
561 while(istart<0x80000000)
563 unsigned int iend = istart + istep;
564 double start = istart/(double)0x80000000;
565 double end = iend/(double)0x80000000;
566 qspline_t test;
567 double pos,qpos;
568 char left = 0,recurse=0;
569 int t;
570 int probes = 15;
571 double dx,dy;
573 /* create simple approximation: a qspline_t which run's through the
574 qspline_t point at 0.5 */
575 test.start = cspline_getpoint(s, start);
576 test.control = cspline_getpoint(s, (start+end)/2);
577 test.end = cspline_getpoint(s, end);
578 /* fix the control point:
579 move it so that the new spline does runs through it */
580 test.control.x = -(test.end.x + test.start.x)/2 + 2*(test.control.x);
581 test.control.y = -(test.end.y + test.start.y)/2 + 2*(test.control.y);
583 /* depending on where we are in the spline, we either try to match
584 the left or right tangent */
585 if(start<0.5)
586 left=1;
587 /* get derivative */
588 pos = left?start:end;
589 qpos = pos*pos;
590 test.control.x = s->end.x*(3*qpos) + 3*s->control2.x*(2*pos-3*qpos) +
591 3*s->control1.x*(1-4*pos+3*qpos) + s->start.x*(-3+6*pos-3*qpos);
592 test.control.y = s->end.y*(3*qpos) + 3*s->control2.y*(2*pos-3*qpos) +
593 3*s->control1.y*(1-4*pos+3*qpos) + s->start.y*(-3+6*pos-3*qpos);
594 if(left) {
595 test.control.x *= (end-start)/2;
596 test.control.y *= (end-start)/2;
597 test.control.x += test.start.x;
598 test.control.y += test.start.y;
599 } else {
600 test.control.x *= -(end-start)/2;
601 test.control.y *= -(end-start)/2;
602 test.control.x += test.end.x;
603 test.control.y += test.end.y;
606 //#define PROBES
607 #ifdef PROBES
608 /* measure the spline's accurancy, by taking a number of probes */
609 for(t=0;t<probes;t++) {
610 gfxpoint_t qr1,qr2,cr1,cr2;
611 double pos = 0.5/(probes*2)*(t*2+1);
612 double dx,dy;
613 double dist1,dist2;
614 qr1 = qspline_getpoint(&test, pos);
615 cr1 = cspline_getpoint(s, start+pos*(end-start));
617 dx = qr1.x - cr1.x;
618 dy = qr1.y - cr1.y;
619 dist1 = dx*dx+dy*dy;
621 if(dist1>quality2) {
622 recurse=1;break;
624 qr2 = qspline_getpoint(&test, (1-pos));
625 cr2 = cspline_getpoint(s, start+(1-pos)*(end-start));
627 dx = qr2.x - cr2.x;
628 dy = qr2.y - cr2.y;
629 dist2 = dx*dx+dy*dy;
631 if(dist2>quality2) {
632 recurse=1;break;
635 #else // quadratic error: *much* faster!
637 /* convert control point representation to
638 d*x^3 + c*x^2 + b*x + a */
639 dx= s->end.x - s->control2.x*3 + s->control1.x*3 - s->start.x;
640 dy= s->end.y - s->control2.y*3 + s->control1.y*3 - s->start.y;
642 /* we need to do this for the subspline between [start,end], not [0,1]
643 as a transformation of t->a*t+b does nothing to highest coefficient
644 of the spline except multiply it with a^3, we just need to modify
645 d here. */
646 {double m = end-start;
647 dx*=m*m*m;
648 dy*=m*m*m;
651 /* use the integral over (f(x)-g(x))^2 between 0 and 1
652 to measure the approximation quality.
653 (it boils down to const*d^2) */
654 recurse = (dx*dx + dy*dy > quality2);
655 #endif
657 if(recurse && istep>1 && size-level > num) {
658 istep >>= 1;
659 level++;
660 } else {
661 *q++ = test;
662 num++;
663 istart += istep;
664 while(!(istart & istep)) {
665 level--;
666 istep <<= 1;
670 return num;
673 void gfxdraw_conicTo(gfxdrawer_t*draw, double cx, double cy, double tox, double toy, double quality)
675 double c1x = (draw->x + 2 * cx) / 3;
676 double c1y = (draw->y + 2 * cy) / 3;
677 double c2x = (2 * cx + tox) / 3;
678 double c2y = (2 * cy + toy) / 3;
679 gfxdraw_cubicTo(draw, c1x, c1y, c2x, c2y, tox, toy, quality);
683 void gfxdraw_cubicTo(gfxdrawer_t*draw, double c1x, double c1y, double c2x, double c2y, double x, double y, double quality)
685 qspline_t q[128];
686 cspline_t c;
687 double maxerror = quality>0 ? quality : 1.0;
688 int t,num;
690 c.start.x = draw->x;
691 c.start.y = draw->y;
692 c.control1.x = c1x;
693 c.control1.y = c1y;
694 c.control2.x = c2x;
695 c.control2.y = c2y;
696 c.end.x = x;
697 c.end.y = y;
699 num = approximate3(&c, q, 128, maxerror);
701 for(t=0;t<num;t++) {
702 gfxpoint_t mid;
703 gfxpoint_t to;
704 mid.x = q[t].control.x;
705 mid.y = q[t].control.y;
706 to.x = q[t].end.x;
707 to.y = q[t].end.y;
708 draw->splineTo(draw, mid.x, mid.y, to.x, to.y);
712 gfxbbox_t gfxbbox_expand_to_point(gfxbbox_t box, gfxcoord_t x, gfxcoord_t y)
714 if(box.xmin==0 && box.ymin==0 && box.xmax==0 && box.ymax==0) {
715 box.xmin = x;
716 box.ymin = y;
717 box.xmax = x;
718 box.ymax = y;
719 if(x==0 && y==0) box.xmax = 0.0000001;
720 return box;
722 if(x < box.xmin)
723 box.xmin = x;
724 if(x > box.xmax)
725 box.xmax = x;
726 if(y < box.ymin)
727 box.ymin = y;
728 if(y > box.ymax)
729 box.ymax = y;
730 return box;
733 gfxbbox_t gfxbbox_expand_to_bbox(gfxbbox_t box, gfxbbox_t box2)
735 if(box2.xmin==0 && box2.ymin==0 && box2.xmax==0 && box2.ymax==0) {
736 return box;
738 box = gfxbbox_expand_to_point(box, box2.xmin, box2.ymin);
739 box = gfxbbox_expand_to_point(box, box2.xmax, box2.ymax);
740 return box;
743 void gfxbbox_intersect(gfxbbox_t*box1, gfxbbox_t*box2)
745 if(box2->xmin > box1->xmin)
746 box1->xmin = box2->xmin;
747 if(box2->ymin > box1->ymin)
748 box1->ymin = box2->ymin;
749 if(box2->xmax < box1->xmax)
750 box1->xmax = box2->xmax;
751 if(box2->ymax < box1->ymax)
752 box1->ymax = box2->ymax;
753 if(box1->xmin > box1->xmax)
754 box1->xmax = box1->xmin;
755 if(box1->ymin > box1->ymax)
756 box1->ymax = box1->ymin;
759 gfxbbox_t gfxline_getbbox(gfxline_t*line)
761 gfxcoord_t x=0,y=0;
762 gfxbbox_t bbox = {0,0,0,0};
763 char last = 0;
764 while(line) {
765 if(line->type == gfx_moveTo) {
766 last = 1;
767 } else if(line->type == gfx_lineTo) {
768 if(last) bbox = gfxbbox_expand_to_point(bbox, x, y);
769 bbox = gfxbbox_expand_to_point(bbox, line->x, line->y);
770 last = 0;
771 } else if(line->type == gfx_splineTo) {
772 if(last) bbox = gfxbbox_expand_to_point(bbox, x, y);
773 bbox = gfxbbox_expand_to_point(bbox, line->sx, line->sy);
774 bbox = gfxbbox_expand_to_point(bbox, line->x, line->y);
775 last = 0;
777 x = line->x;
778 y = line->y;
779 line = line->next;
781 return bbox;
784 gfxline_t* gfxline_append(gfxline_t*line1, gfxline_t*line2)
786 gfxline_t*l = line1;;
787 if(!l)
788 return line2;
789 while(l->next) {
790 l = l->next;
792 l->next = line2;
793 return line1;
796 void gfxline_transform(gfxline_t*line, gfxmatrix_t*matrix)
798 while(line) {
799 double x = matrix->m00*line->x + matrix->m10*line->y + matrix->tx;
800 double y = matrix->m01*line->x + matrix->m11*line->y + matrix->ty;
801 line->x = x;
802 line->y = y;
803 if(line->type == gfx_splineTo) {
804 double sx = matrix->m00*line->sx + matrix->m10*line->sy + matrix->tx;
805 double sy = matrix->m01*line->sx + matrix->m11*line->sy + matrix->ty;
806 line->sx = sx;
807 line->sy = sy;
809 line = line->next;
813 void gfxmatrix_dump(gfxmatrix_t*m, FILE*fi, char*prefix)
815 fprintf(fi, "%s%f %f | %f\n", prefix, m->m00, m->m10, m->tx);
816 fprintf(fi, "%s%f %f | %f\n", prefix, m->m01, m->m11, m->ty);
819 void gfxmatrix_transform(gfxmatrix_t*m, double* v, double*dest)
821 dest[0] = m->m00*v[0] + m->m10*v[1] + m->tx;
822 dest[1] = m->m01*v[0] + m->m11*v[1] + m->ty;
824 void gfxmatrix_invert(gfxmatrix_t*m, gfxmatrix_t*dest)
826 double det = m->m00 * m->m11 - m->m10 * m->m01;
827 if(!det) {
828 memset(dest, 0, sizeof(gfxmatrix_t));
829 return;
831 det = 1/det;
832 dest->m00 = m->m11 * det;
833 dest->m01 = -m->m01 * det;
834 dest->m10 = -m->m10 * det;
835 dest->m11 = m->m00 * det;
836 dest->tx = -(dest->m00 * m->tx + dest->m10 * m->ty);
837 dest->ty = -(dest->m01 * m->tx + dest->m11 * m->ty);
839 void gfxmatrix_unit(gfxmatrix_t*m)
841 memset(m, 0, sizeof(gfxmatrix_t));
842 m->m00 = 1.0;
843 m->m11 = 1.0;
845 void gfxmatrix_multiply(gfxmatrix_t*m1, gfxmatrix_t*m2, gfxmatrix_t*dest)
847 dest->m00 = m1->m00*m2->m00 + m1->m10*m2->m01;
848 dest->m01 = m1->m01*m2->m00 + m1->m11*m2->m01;
849 dest->m10 = m1->m00*m2->m10 + m1->m10*m2->m11;
850 dest->m11 = m1->m01*m2->m10 + m1->m11*m2->m11;
851 dest->tx = m1->m00*m2->tx + m1->m10*m2->ty + m1->tx;
852 dest->ty = m1->m01*m2->tx + m1->m11*m2->ty + m1->ty;
855 gfxfontlist_t* gfxfontlist_create()
857 /* Initial list ist empty */
858 return 0;
861 gfxfont_t*gfxfontlist_findfont(gfxfontlist_t*list, char*id)
863 gfxfontlist_t*l = list;
864 while(l) {
865 if(!strcmp((char*)l->font->id, id)) {
866 return l->font;
868 l = l->next;
870 return 0;
872 char gfxfontlist_hasfont(gfxfontlist_t*list, gfxfont_t*font)
874 gfxfontlist_t*l = list;
875 while(l) {
876 if(!strcmp((char*)l->font->id, font->id)) {
877 return 1;
879 l = l->next;
881 return 0;
883 void*gfxfontlist_getuserdata(gfxfontlist_t*list, const char*id)
885 gfxfontlist_t*l = list;
886 while(l) {
887 if(!strcmp((char*)l->font->id, id)) {
888 return l->user;
890 l = l->next;
892 return 0;
894 gfxfontlist_t*gfxfontlist_addfont2(gfxfontlist_t*list, gfxfont_t*font, void*user)
896 gfxfontlist_t*last=0,*l = list;
897 while(l) {
898 last = l;
899 if(l->font == font) {
900 return list; // we already know this font
902 l = l->next;
904 if(!font) {
905 fprintf(stderr, "Tried to add zero font\n");
907 l = (gfxfontlist_t*)rfx_calloc(sizeof(gfxfontlist_t));
908 l->font = font;
909 l->user = user;
910 l->next = 0;
911 if(last) {
912 last->next = l;
913 return list;
914 } else {
915 return l;
918 gfxfontlist_t*gfxfontlist_addfont(gfxfontlist_t*list, gfxfont_t*font)
920 return gfxfontlist_addfont2(list, font, 0);
922 void gfxfontlist_free(gfxfontlist_t*list, char deletefonts)
924 gfxfontlist_t*l = list;
925 while(l) {
926 gfxfontlist_t*next = l->next;
927 if(deletefonts && l->font) {
928 gfxfont_free(l->font);l->font=0;
930 l->next = 0;
931 free(l);
932 l = next;
936 gfxline_t*gfxline_makerectangle(double x1,double y1,double x2, double y2)
938 gfxline_t* line = (gfxline_t*)rfx_calloc(sizeof(gfxline_t)*5);
939 line[0].x = x1;line[0].y = y1;line[0].type = gfx_moveTo;line[0].next = &line[1];
940 line[1].x = x2;line[1].y = y1;line[1].type = gfx_lineTo;line[1].next = &line[2];
941 line[2].x = x2;line[2].y = y2;line[2].type = gfx_lineTo;line[2].next = &line[3];
942 line[3].x = x1;line[3].y = y2;line[3].type = gfx_lineTo;line[3].next = &line[4];
943 line[4].x = x1;line[4].y = y1;line[4].type = gfx_lineTo;
944 return line;
947 gfxline_t*gfxline_makecircle(double x,double y,double rx, double ry)
949 double C1 = 0.2930;
950 double C2 = 0.4140;
951 double begin = 0.7070;
952 gfxline_t** line = (gfxline_t**)rfx_calloc(sizeof(gfxline_t*)*9);
953 int t;
954 for(t=0;t<9;t++) {
955 line[t] = rfx_calloc(sizeof(gfxline_t));
957 line[0]->type = gfx_moveTo;
958 line[0]->x = x+begin*rx;
959 line[0]->y = y+begin*ry;
960 for(t=1;t<9;t++) {
961 line[t-1]->next = line[t];
962 line[t]->type = gfx_splineTo;
964 line[8]->next = 0;
965 #define R(nr,cx,cy,mx,my) \
966 line[nr]->sx = line[nr-1]->x + (cx); \
967 line[nr]->sy = line[nr-1]->y + (cy); \
968 line[nr]->x = line[nr]->sx + (mx); \
969 line[nr]->y = line[nr]->sy + (my);
970 R(1, -C1*rx, C1*ry, -C2*rx, 0);
971 R(2, -C2*rx, 0, -C1*rx, -C1*ry);
972 R(3, -C1*rx, -C1*ry, 0, -C2*ry);
973 R(4, 0, -C2*ry, C1*rx, -C1*ry);
974 R(5, C1*rx, -C1*ry, C2*rx, 0);
975 R(6, C2*rx, 0, C1*rx, C1*ry);
976 R(7, C1*rx, C1*ry, 0, C2*ry);
977 R(8, 0, C2*ry, -C1*rx, C1*ry);
978 gfxline_t*l = line[0];
979 free(line);
980 return l;
983 gfxbbox_t* gfxline_isrectangle(gfxline_t*_l)
985 if(!_l)
986 return 0;
988 gfxline_t*l = gfxline_clone(_l);
989 gfxline_optimize(l);
991 double x1=0,x2=0,y1=0,y2=0;
992 int xc=0,yc=0;
993 char corners=0;
995 char prev=0;
996 char fail=0;
997 for(;l; l=l->next) {
998 double x = l->x;
999 double y = l->y;
1001 char top=0,left=0;
1003 if(xc==2 && x!=x1 && x!=x2) {fail=1;break;}
1004 else if(xc>=1 && x==x1) {left=0;}
1005 else if(xc==2 && x==x2) {left=1;}
1006 else if(xc==1 && x!=x1) {x2 = x; xc=2; left=1;}
1007 else if(xc==0) {x1 = x; xc=1;left=0;}
1008 else {fprintf(stderr, "Internal error in rectangle detection\n");}
1010 if(yc==2 && y!=y1 && y!=y2) {fail=1;break;}
1011 else if(yc>=1 && y==y1) {top=0;}
1012 else if(yc==2 && y==y2) {top=1;}
1013 else if(yc==1 && y!=y1) {y2 = y; yc=2; top=1;}
1014 else if(yc==0) {y1 = y; yc=1;top=0;}
1015 else {fprintf(stderr, "Internal error in rectangle detection\n");}
1017 char pos=top<<1|left;
1019 if((pos^prev)==3) {
1020 /* diagonal lines not allowed */
1021 fail=1;break;
1023 prev = pos;
1025 /* no corner except the first one may be touched twice */
1026 if(pos && (corners & 1<<pos)) {
1027 fail=1;break;
1029 /* mark which corners have been touched so far */
1030 corners |= 1<<pos;
1032 if(fail) {
1033 gfxline_free(l);
1034 return 0;
1037 if(corners!=0x0f) return 0; // not all 4 corners reached
1039 if(x2<x1) {double x = x2;x2=x1;x1=x;}
1040 if(y2<y1) {double y = y2;y2=y1;y1=y;}
1042 gfxbbox_t*r = malloc(sizeof(gfxbbox_t));
1043 r->xmin = x1; r->ymin = y1;
1044 r->xmax = x2; r->ymax = y2;
1045 return r;
1048 void gfximage_transform(gfximage_t*img, gfxcxform_t*cxform)
1050 int t;
1051 int size = img->width*img->height;
1053 int rr,rg,rb,ra, tr;
1054 int gr,gg,gb,ga, tg;
1055 int br,bg,bb,ba, tb;
1056 int ar,ag,ab,aa, ta;
1057 rr = (int)(cxform->rr*256);gr = (int)(cxform->gr*256);
1058 rg = (int)(cxform->rg*256);gg = (int)(cxform->gg*256);
1059 rb = (int)(cxform->rb*256);gb = (int)(cxform->gb*256);
1060 ra = (int)(cxform->ra*256);ga = (int)(cxform->ga*256);
1061 br = (int)(cxform->br*256);ar = (int)(cxform->ar*256);tr = (int)(cxform->tr*256);
1062 bg = (int)(cxform->bg*256);ag = (int)(cxform->ag*256);tg = (int)(cxform->tg*256);
1063 bb = (int)(cxform->bb*256);ab = (int)(cxform->ab*256);tb = (int)(cxform->tb*256);
1064 ba = (int)(cxform->ba*256);aa = (int)(cxform->aa*256);ta = (int)(cxform->ta*256);
1066 for(t=0;t<size;t++) {
1067 gfxcolor_t*pixel = &img->data[t];
1068 unsigned char r = (pixel->r * rr + pixel->g * rg + pixel->b * rb + pixel->a * ra + tr) / 256;
1069 unsigned char g = (pixel->r * gr + pixel->g * gg + pixel->b * gb + pixel->a * ga + tg) / 256;
1070 unsigned char b = (pixel->r * br + pixel->g * bg + pixel->b * bb + pixel->a * ba + tb) / 256;
1071 unsigned char a = (pixel->r * ar + pixel->g * ag + pixel->b * ab + pixel->a * aa + ta) / 256;
1072 pixel->r = r;
1073 pixel->g = g;
1074 pixel->b = b;
1075 pixel->a = a;
1078 void gfxline_dump(gfxline_t*line, FILE*fi, char*prefix)
1080 while(line) {
1081 if(line->type == gfx_moveTo) {
1082 fprintf(fi, "%smoveTo %.2f %.2f\n", prefix, line->x, line->y);
1083 } else if(line->type == gfx_lineTo) {
1084 fprintf(fi, "%slineTo %.2f %.2f\n", prefix, line->x, line->y);
1085 } else if(line->type == gfx_splineTo) {
1086 fprintf(fi, "%ssplineTo (%.2f %.2f) %.2f %.2f\n", prefix, line->sx, line->sy, line->x, line->y);
1088 line = line->next;
1092 static char gfxpoint_equals(void*c1, void*c2)
1094 return !memcmp(c1, c2, sizeof(gfxpoint_t));
1096 static unsigned int gfxpoint_hash(void*c)
1098 return string_hash3(c, sizeof(gfxpoint_t));
1100 static void* gfxpoint_clone(void*c)
1102 void*n = malloc(sizeof(gfxpoint_t));
1103 memcpy(n, c, sizeof(gfxpoint_t));
1104 return n;
1106 static void gfxpoint_destroy(void*c)
1108 free(c);
1110 static type_t gfxpoint_type = {
1111 hash: (hash_func)gfxpoint_hash,
1112 equals: (equals_func)gfxpoint_equals,
1113 dup: (dup_func)gfxpoint_clone,
1114 free: (free_func)gfxpoint_destroy,
1117 /* makes sure that a gfxline is drawn in a single stroke.
1118 E.g. moveto 0,0 lineto 100,0 lineto 100,100
1119 moveto 0,0 lineto 0,100 lineto 100,100
1120 is converted to
1121 moveto 0,0, lineto 0,100 lineto 100,100 lineto 100,0 lineto 0,0
1123 gfxline_t* gfxline_restitch(gfxline_t*line)
1125 dict_t*ff = dict_new2(&gfxpoint_type);
1126 dict_t*rev = dict_new2(&gfxpoint_type);
1128 gfxline_t*prev=0;
1129 while(line) {
1130 gfxline_t*next = line->next;
1131 if(line->type == gfx_moveTo && (line->next && line->next->type != gfx_moveTo)) {
1132 gfxpoint_t xy = {line->x, line->y};
1133 dict_put(ff, &xy, line);
1134 prev = line;
1135 } else if(!line->next || line->next->type == gfx_moveTo) {
1136 if(prev) {
1137 gfxpoint_t xy = {line->x, line->y};
1138 dict_put(rev, &xy, prev);
1139 line->next = 0;
1140 prev=0;
1143 line = next;
1146 gfxpoint_t pos = {0,0};
1148 gfxline_t*result = 0;
1149 gfxline_t*last = 0;
1151 char first = 1;
1152 while(dict_count(ff)) {
1153 char reverse = 0, stitch = 1;
1154 gfxline_t*l = dict_lookup(ff, &pos);
1155 if(l) {
1156 char d = dict_del2(ff,&pos,l);assert(d);
1157 } else {
1158 l = dict_lookup(rev, &pos);
1159 if(l) {
1160 reverse = 1;
1161 char d = dict_del2(rev,&pos,l);assert(d);
1164 if(!l) {
1165 /* try to find *any* entry. this is costly, but
1166 doesn't happen too often */
1167 stitch = 0;
1168 DICT_ITERATE_DATA(ff, gfxline_t*, l2) {
1169 l = l2;
1170 break;
1172 assert(l);
1173 gfxpoint_t xy = {l->x,l->y};
1174 char d = dict_del2(ff,&xy,l);assert(d);
1177 gfxline_t*end = l;
1178 if(!reverse) {
1179 while(end->next) end = end->next;
1180 pos.x = end->x;
1181 pos.y = end->y;
1182 char d = dict_del2(rev,&pos,l);assert(d);
1183 } else {
1184 l = gfxline_reverse(l);
1185 pos.x = end->x;
1186 pos.y = end->y;
1187 char d = dict_del2(ff,&pos,end);assert(d);
1190 assert(l->type == gfx_moveTo);
1191 if(stitch && !first) {
1192 /* cut away the moveTo */
1193 gfxline_t*next = l->next;
1194 free(l);
1195 l = next;
1198 if(!last) {
1199 result = l;
1200 last = end;
1201 } else {
1202 last->next = l;
1203 last = end;
1205 first = 0;
1207 dict_destroy(ff);
1208 dict_destroy(rev);
1209 return result;
1212 gfxline_t* gfxline_reverse(gfxline_t*line)
1214 gfxline_t*b = 0;
1215 while(line) {
1216 gfxline_t*next = line->next;
1217 if(next && next->type != gfx_moveTo) {
1218 line->type = next->type;
1219 line->sx = next->sx;
1220 line->sy = next->sy;
1221 } else {
1222 line->type = gfx_moveTo;
1224 line->next = b;
1225 b = line;
1226 line = next;
1228 return b;
1231 void gfxline_normalize(gfxline_t*line, double sizex, double sizey)
1233 gfxbbox_t b = gfxline_getbbox(line);
1234 if(b.xmax == b.xmin || b.ymax == b.ymin)
1235 return;
1236 gfxmatrix_t m;
1237 double w = b.xmax - b.xmin;
1238 double h = b.ymax - b.ymin;
1239 double fx = sizex/w;
1240 double fy = sizey/h;
1241 double s = fmin(fx,fy);
1243 m.m00 = s;
1244 m.m11 = s;
1245 m.tx = -b.xmin * s;
1246 m.ty = -b.ymin * s;
1247 m.m01 = m.m10 = 0;
1248 gfxline_transform(line, &m);
1251 void gfxgradient_destroy(gfxgradient_t*gradient)
1253 while(gradient) {
1254 gfxgradient_t*next = gradient->next;
1255 free(gradient);
1256 gradient = next;
1260 gfxparams_t* gfxparams_new()
1262 return (gfxparams_t*)rfx_calloc(sizeof(gfxparams_t));
1265 void gfxparams_store(gfxparams_t*params, const char*key, const char*value)
1267 gfxparam_t*o = params->params;
1268 while(o) {
1269 if(!strcmp(key, o->key)) {
1270 /* overwrite old value */
1271 free((void*)o->value);
1272 o->value = strdup(value);
1273 return;
1275 o = o->next;
1277 gfxparam_t*p = (gfxparam_t*)malloc(sizeof(gfxparam_t));
1278 p->key = strdup(key);
1279 p->value = strdup(value);
1280 p->next = 0;
1282 if(params->last) {
1283 params->last->next = p;
1284 params->last = p;
1285 } else {
1286 params->params = p;
1287 params->last = p;
1291 void gfxparams_free(gfxparams_t*params)
1293 gfxparam_t*p = params->params;
1294 while(p) {
1295 gfxparam_t*next = p->next;
1296 free((void*)p->key);
1297 if(p->value) free((void*)p->value);
1298 free(p);
1299 p = next;
1301 free(params);
1304 static void turnpoint(double x, double y, gfxmatrix_t* m, double *_x, double*_y)
1306 *_x = m->m00*x + m->m10*y + m->tx;
1307 *_y = m->m01*x + m->m11*y + m->ty;
1310 gfxbbox_t gfxbbox_transform(gfxbbox_t*bbox, gfxmatrix_t*m)
1312 double x1, y1, x2, y2, x3, y3, x4, y4;
1313 turnpoint(bbox->xmin, bbox->xmin, m, &x1, &y1);
1314 turnpoint(bbox->xmax, bbox->ymin, m, &x2, &y2);
1315 turnpoint(bbox->xmin, bbox->ymax, m, &x3, &y3);
1316 turnpoint(bbox->xmax, bbox->ymax, m, &x4, &y4);
1318 gfxbbox_t new_bbox = {x1, y1, x1, y1};
1319 new_bbox = gfxbbox_expand_to_point(new_bbox, x2, y2);
1320 new_bbox = gfxbbox_expand_to_point(new_bbox, x3, y3);
1321 new_bbox = gfxbbox_expand_to_point(new_bbox, x4, y4);
1322 return new_bbox;