new method gfxbbox_transform
[swftools.git] / lib / gfxtools.c
blob4541c38b38365bea33681e02ebe4cebe0b465c71
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 box = gfxbbox_expand_to_point(box, box2.xmin, box2.ymin);
736 box = gfxbbox_expand_to_point(box, box2.xmax, box2.ymax);
737 return box;
740 void gfxbbox_intersect(gfxbbox_t*box1, gfxbbox_t*box2)
742 if(box2->xmin > box1->xmin)
743 box1->xmin = box2->xmin;
744 if(box2->ymin > box1->ymin)
745 box1->ymin = box2->ymin;
746 if(box2->xmax < box1->xmax)
747 box1->xmax = box2->xmax;
748 if(box2->ymax < box1->ymax)
749 box1->ymax = box2->ymax;
750 if(box1->xmin > box1->xmax)
751 box1->xmax = box1->xmin;
752 if(box1->ymin > box1->ymax)
753 box1->ymax = box1->ymin;
756 gfxbbox_t gfxline_getbbox(gfxline_t*line)
758 gfxcoord_t x=0,y=0;
759 gfxbbox_t bbox = {0,0,0,0};
760 char last = 0;
761 while(line) {
762 if(line->type == gfx_moveTo) {
763 last = 1;
764 } else if(line->type == gfx_lineTo) {
765 if(last) bbox = gfxbbox_expand_to_point(bbox, x, y);
766 bbox = gfxbbox_expand_to_point(bbox, line->x, line->y);
767 last = 0;
768 } else if(line->type == gfx_splineTo) {
769 if(last) bbox = gfxbbox_expand_to_point(bbox, x, y);
770 bbox = gfxbbox_expand_to_point(bbox, line->sx, line->sy);
771 bbox = gfxbbox_expand_to_point(bbox, line->x, line->y);
772 last = 0;
774 x = line->x;
775 y = line->y;
776 line = line->next;
778 return bbox;
781 gfxline_t* gfxline_append(gfxline_t*line1, gfxline_t*line2)
783 gfxline_t*l = line1;;
784 if(!l)
785 return line2;
786 while(l->next) {
787 l = l->next;
789 l->next = line2;
790 return line1;
793 void gfxline_transform(gfxline_t*line, gfxmatrix_t*matrix)
795 while(line) {
796 double x = matrix->m00*line->x + matrix->m10*line->y + matrix->tx;
797 double y = matrix->m01*line->x + matrix->m11*line->y + matrix->ty;
798 line->x = x;
799 line->y = y;
800 if(line->type == gfx_splineTo) {
801 double sx = matrix->m00*line->sx + matrix->m10*line->sy + matrix->tx;
802 double sy = matrix->m01*line->sx + matrix->m11*line->sy + matrix->ty;
803 line->sx = sx;
804 line->sy = sy;
806 line = line->next;
810 void gfxmatrix_dump(gfxmatrix_t*m, FILE*fi, char*prefix)
812 fprintf(fi, "%s%f %f | %f\n", prefix, m->m00, m->m10, m->tx);
813 fprintf(fi, "%s%f %f | %f\n", prefix, m->m01, m->m11, m->ty);
816 void gfxmatrix_transform(gfxmatrix_t*m, double* v, double*dest)
818 dest[0] = m->m00*v[0] + m->m10*v[1] + m->tx;
819 dest[1] = m->m01*v[0] + m->m11*v[1] + m->ty;
821 void gfxmatrix_invert(gfxmatrix_t*m, gfxmatrix_t*dest)
823 double det = m->m00 * m->m11 - m->m10 * m->m01;
824 if(!det) {
825 memset(dest, 0, sizeof(gfxmatrix_t));
826 return;
828 det = 1/det;
829 dest->m00 = m->m11 * det;
830 dest->m01 = -m->m01 * det;
831 dest->m10 = -m->m10 * det;
832 dest->m11 = m->m00 * det;
833 dest->tx = -(dest->m00 * m->tx + dest->m10 * m->ty);
834 dest->ty = -(dest->m01 * m->tx + dest->m11 * m->ty);
836 void gfxmatrix_unit(gfxmatrix_t*m)
838 memset(m, 0, sizeof(gfxmatrix_t));
839 m->m00 = 1.0;
840 m->m11 = 1.0;
842 void gfxmatrix_multiply(gfxmatrix_t*m1, gfxmatrix_t*m2, gfxmatrix_t*dest)
844 dest->m00 = m1->m00*m2->m00 + m1->m10*m2->m01;
845 dest->m01 = m1->m01*m2->m00 + m1->m11*m2->m01;
846 dest->m10 = m1->m00*m2->m10 + m1->m10*m2->m11;
847 dest->m11 = m1->m01*m2->m10 + m1->m11*m2->m11;
848 dest->tx = m1->m00*m2->tx + m1->m10*m2->ty + m1->tx;
849 dest->ty = m1->m01*m2->tx + m1->m11*m2->ty + m1->ty;
852 gfxfontlist_t* gfxfontlist_create()
854 /* Initial list ist empty */
855 return 0;
858 gfxfont_t*gfxfontlist_findfont(gfxfontlist_t*list, char*id)
860 gfxfontlist_t*l = list;
861 while(l) {
862 if(!strcmp((char*)l->font->id, id)) {
863 return l->font;
865 l = l->next;
867 return 0;
869 char gfxfontlist_hasfont(gfxfontlist_t*list, gfxfont_t*font)
871 gfxfontlist_t*l = list;
872 while(l) {
873 if(!strcmp((char*)l->font->id, font->id)) {
874 return 1;
876 l = l->next;
878 return 0;
880 void*gfxfontlist_getuserdata(gfxfontlist_t*list, const char*id)
882 gfxfontlist_t*l = list;
883 while(l) {
884 if(!strcmp((char*)l->font->id, id)) {
885 return l->user;
887 l = l->next;
889 return 0;
891 gfxfontlist_t*gfxfontlist_addfont2(gfxfontlist_t*list, gfxfont_t*font, void*user)
893 gfxfontlist_t*last=0,*l = list;
894 while(l) {
895 last = l;
896 if(l->font == font) {
897 return list; // we already know this font
899 l = l->next;
901 if(!font) {
902 fprintf(stderr, "Tried to add zero font\n");
904 l = (gfxfontlist_t*)rfx_calloc(sizeof(gfxfontlist_t));
905 l->font = font;
906 l->user = user;
907 l->next = 0;
908 if(last) {
909 last->next = l;
910 return list;
911 } else {
912 return l;
915 gfxfontlist_t*gfxfontlist_addfont(gfxfontlist_t*list, gfxfont_t*font)
917 return gfxfontlist_addfont2(list, font, 0);
919 void gfxfontlist_free(gfxfontlist_t*list, char deletefonts)
921 gfxfontlist_t*l = list;
922 while(l) {
923 gfxfontlist_t*next = l->next;
924 if(deletefonts && l->font) {
925 gfxfont_free(l->font);l->font=0;
927 l->next = 0;
928 free(l);
929 l = next;
933 gfxline_t*gfxline_makerectangle(double x1,double y1,double x2, double y2)
935 gfxline_t* line = (gfxline_t*)rfx_calloc(sizeof(gfxline_t)*5);
936 line[0].x = x1;line[0].y = y1;line[0].type = gfx_moveTo;line[0].next = &line[1];
937 line[1].x = x2;line[1].y = y1;line[1].type = gfx_lineTo;line[1].next = &line[2];
938 line[2].x = x2;line[2].y = y2;line[2].type = gfx_lineTo;line[2].next = &line[3];
939 line[3].x = x1;line[3].y = y2;line[3].type = gfx_lineTo;line[3].next = &line[4];
940 line[4].x = x1;line[4].y = y1;line[4].type = gfx_lineTo;
941 return line;
944 gfxline_t*gfxline_makecircle(double x,double y,double rx, double ry)
946 double C1 = 0.2930;
947 double C2 = 0.4140;
948 double begin = 0.7070;
949 gfxline_t** line = (gfxline_t**)rfx_calloc(sizeof(gfxline_t*)*9);
950 int t;
951 for(t=0;t<9;t++) {
952 line[t] = rfx_calloc(sizeof(gfxline_t));
954 line[0]->type = gfx_moveTo;
955 line[0]->x = x+begin*rx;
956 line[0]->y = y+begin*ry;
957 for(t=1;t<9;t++) {
958 line[t-1]->next = line[t];
959 line[t]->type = gfx_splineTo;
961 line[8]->next = 0;
962 #define R(nr,cx,cy,mx,my) \
963 line[nr]->sx = line[nr-1]->x + (cx); \
964 line[nr]->sy = line[nr-1]->y + (cy); \
965 line[nr]->x = line[nr]->sx + (mx); \
966 line[nr]->y = line[nr]->sy + (my);
967 R(1, -C1*rx, C1*ry, -C2*rx, 0);
968 R(2, -C2*rx, 0, -C1*rx, -C1*ry);
969 R(3, -C1*rx, -C1*ry, 0, -C2*ry);
970 R(4, 0, -C2*ry, C1*rx, -C1*ry);
971 R(5, C1*rx, -C1*ry, C2*rx, 0);
972 R(6, C2*rx, 0, C1*rx, C1*ry);
973 R(7, C1*rx, C1*ry, 0, C2*ry);
974 R(8, 0, C2*ry, -C1*rx, C1*ry);
975 gfxline_t*l = line[0];
976 free(line);
977 return l;
980 gfxbbox_t* gfxline_isrectangle(gfxline_t*_l)
982 if(!_l)
983 return 0;
985 gfxline_t*l = gfxline_clone(_l);
986 gfxline_optimize(l);
988 double x1=0,x2=0,y1=0,y2=0;
989 int xc=0,yc=0;
990 char corners=0;
992 char prev=0;
993 char fail=0;
994 for(;l; l=l->next) {
995 double x = l->x;
996 double y = l->y;
998 char top=0,left=0;
1000 if(xc==2 && x!=x1 && x!=x2) {fail=1;break;}
1001 else if(xc>=1 && x==x1) {left=0;}
1002 else if(xc==2 && x==x2) {left=1;}
1003 else if(xc==1 && x!=x1) {x2 = x; xc=2; left=1;}
1004 else if(xc==0) {x1 = x; xc=1;left=0;}
1005 else {fprintf(stderr, "Internal error in rectangle detection\n");}
1007 if(yc==2 && y!=y1 && y!=y2) {fail=1;break;}
1008 else if(yc>=1 && y==y1) {top=0;}
1009 else if(yc==2 && y==y2) {top=1;}
1010 else if(yc==1 && y!=y1) {y2 = y; yc=2; top=1;}
1011 else if(yc==0) {y1 = y; yc=1;top=0;}
1012 else {fprintf(stderr, "Internal error in rectangle detection\n");}
1014 char pos=top<<1|left;
1016 if((pos^prev)==3) {
1017 /* diagonal lines not allowed */
1018 fail=1;break;
1020 prev = pos;
1022 /* no corner except the first one may be touched twice */
1023 if(pos && (corners & 1<<pos)) {
1024 fail=1;break;
1026 /* mark which corners have been touched so far */
1027 corners |= 1<<pos;
1029 if(fail) {
1030 gfxline_free(l);
1031 return 0;
1034 if(corners!=0x0f) return 0; // not all 4 corners reached
1036 if(x2<x1) {double x = x2;x2=x1;x1=x;}
1037 if(y2<y1) {double y = y2;y2=y1;y1=y;}
1039 gfxbbox_t*r = malloc(sizeof(gfxbbox_t));
1040 r->xmin = x1; r->ymin = y1;
1041 r->xmax = x2; r->ymax = y2;
1042 return r;
1045 void gfximage_transform(gfximage_t*img, gfxcxform_t*cxform)
1047 int t;
1048 int size = img->width*img->height;
1050 int rr,rg,rb,ra, tr;
1051 int gr,gg,gb,ga, tg;
1052 int br,bg,bb,ba, tb;
1053 int ar,ag,ab,aa, ta;
1054 rr = (int)(cxform->rr*256);gr = (int)(cxform->gr*256);
1055 rg = (int)(cxform->rg*256);gg = (int)(cxform->gg*256);
1056 rb = (int)(cxform->rb*256);gb = (int)(cxform->gb*256);
1057 ra = (int)(cxform->ra*256);ga = (int)(cxform->ga*256);
1058 br = (int)(cxform->br*256);ar = (int)(cxform->ar*256);tr = (int)(cxform->tr*256);
1059 bg = (int)(cxform->bg*256);ag = (int)(cxform->ag*256);tg = (int)(cxform->tg*256);
1060 bb = (int)(cxform->bb*256);ab = (int)(cxform->ab*256);tb = (int)(cxform->tb*256);
1061 ba = (int)(cxform->ba*256);aa = (int)(cxform->aa*256);ta = (int)(cxform->ta*256);
1063 for(t=0;t<size;t++) {
1064 gfxcolor_t*pixel = &img->data[t];
1065 unsigned char r = (pixel->r * rr + pixel->g * rg + pixel->b * rb + pixel->a * ra + tr) / 256;
1066 unsigned char g = (pixel->r * gr + pixel->g * gg + pixel->b * gb + pixel->a * ga + tg) / 256;
1067 unsigned char b = (pixel->r * br + pixel->g * bg + pixel->b * bb + pixel->a * ba + tb) / 256;
1068 unsigned char a = (pixel->r * ar + pixel->g * ag + pixel->b * ab + pixel->a * aa + ta) / 256;
1069 pixel->r = r;
1070 pixel->g = g;
1071 pixel->b = b;
1072 pixel->a = a;
1075 void gfxline_dump(gfxline_t*line, FILE*fi, char*prefix)
1077 while(line) {
1078 if(line->type == gfx_moveTo) {
1079 fprintf(fi, "%smoveTo %.2f %.2f\n", prefix, line->x, line->y);
1080 } else if(line->type == gfx_lineTo) {
1081 fprintf(fi, "%slineTo %.2f %.2f\n", prefix, line->x, line->y);
1082 } else if(line->type == gfx_splineTo) {
1083 fprintf(fi, "%ssplineTo (%.2f %.2f) %.2f %.2f\n", prefix, line->sx, line->sy, line->x, line->y);
1085 line = line->next;
1089 static char gfxpoint_equals(void*c1, void*c2)
1091 return !memcmp(c1, c2, sizeof(gfxpoint_t));
1093 static unsigned int gfxpoint_hash(void*c)
1095 return string_hash3(c, sizeof(gfxpoint_t));
1097 static void* gfxpoint_clone(void*c)
1099 void*n = malloc(sizeof(gfxpoint_t));
1100 memcpy(n, c, sizeof(gfxpoint_t));
1101 return n;
1103 static void gfxpoint_destroy(void*c)
1105 free(c);
1107 static type_t gfxpoint_type = {
1108 hash: (hash_func)gfxpoint_hash,
1109 equals: (equals_func)gfxpoint_equals,
1110 dup: (dup_func)gfxpoint_clone,
1111 free: (free_func)gfxpoint_destroy,
1114 /* makes sure that a gfxline is drawn in a single stroke.
1115 E.g. moveto 0,0 lineto 100,0 lineto 100,100
1116 moveto 0,0 lineto 0,100 lineto 100,100
1117 is converted to
1118 moveto 0,0, lineto 0,100 lineto 100,100 lineto 100,0 lineto 0,0
1120 gfxline_t* gfxline_restitch(gfxline_t*line)
1122 dict_t*ff = dict_new2(&gfxpoint_type);
1123 dict_t*rev = dict_new2(&gfxpoint_type);
1125 gfxline_t*prev=0;
1126 while(line) {
1127 gfxline_t*next = line->next;
1128 if(line->type == gfx_moveTo && (line->next && line->next->type != gfx_moveTo)) {
1129 gfxpoint_t xy = {line->x, line->y};
1130 dict_put(ff, &xy, line);
1131 prev = line;
1132 } else if(!line->next || line->next->type == gfx_moveTo) {
1133 if(prev) {
1134 gfxpoint_t xy = {line->x, line->y};
1135 dict_put(rev, &xy, prev);
1136 line->next = 0;
1137 prev=0;
1140 line = next;
1143 gfxpoint_t pos = {0,0};
1145 gfxline_t*result = 0;
1146 gfxline_t*last = 0;
1148 char first = 1;
1149 while(dict_count(ff)) {
1150 char reverse = 0, stitch = 1;
1151 gfxline_t*l = dict_lookup(ff, &pos);
1152 if(l) {
1153 char d = dict_del2(ff,&pos,l);assert(d);
1154 } else {
1155 l = dict_lookup(rev, &pos);
1156 if(l) {
1157 reverse = 1;
1158 char d = dict_del2(rev,&pos,l);assert(d);
1161 if(!l) {
1162 /* try to find *any* entry. this is costly, but
1163 doesn't happen too often */
1164 stitch = 0;
1165 DICT_ITERATE_DATA(ff, gfxline_t*, l2) {
1166 l = l2;
1167 break;
1169 assert(l);
1170 gfxpoint_t xy = {l->x,l->y};
1171 char d = dict_del2(ff,&xy,l);assert(d);
1174 gfxline_t*end = l;
1175 if(!reverse) {
1176 while(end->next) end = end->next;
1177 pos.x = end->x;
1178 pos.y = end->y;
1179 char d = dict_del2(rev,&pos,l);assert(d);
1180 } else {
1181 l = gfxline_reverse(l);
1182 pos.x = end->x;
1183 pos.y = end->y;
1184 char d = dict_del2(ff,&pos,end);assert(d);
1187 assert(l->type == gfx_moveTo);
1188 if(stitch && !first) {
1189 /* cut away the moveTo */
1190 gfxline_t*next = l->next;
1191 free(l);
1192 l = next;
1195 if(!last) {
1196 result = l;
1197 last = end;
1198 } else {
1199 last->next = l;
1200 last = end;
1202 first = 0;
1204 dict_destroy(ff);
1205 dict_destroy(rev);
1206 return result;
1209 gfxline_t* gfxline_reverse(gfxline_t*line)
1211 gfxline_t*b = 0;
1212 while(line) {
1213 gfxline_t*next = line->next;
1214 if(next && next->type != gfx_moveTo) {
1215 line->type = next->type;
1216 line->sx = next->sx;
1217 line->sy = next->sy;
1218 } else {
1219 line->type = gfx_moveTo;
1221 line->next = b;
1222 b = line;
1223 line = next;
1225 return b;
1228 void gfxline_normalize(gfxline_t*line, double sizex, double sizey)
1230 gfxbbox_t b = gfxline_getbbox(line);
1231 if(b.xmax == b.xmin || b.ymax == b.ymin)
1232 return;
1233 gfxmatrix_t m;
1234 double w = b.xmax - b.xmin;
1235 double h = b.ymax - b.ymin;
1236 double fx = sizex/w;
1237 double fy = sizey/h;
1238 double s = fmin(fx,fy);
1240 m.m00 = s;
1241 m.m11 = s;
1242 m.tx = -b.xmin * s;
1243 m.ty = -b.ymin * s;
1244 m.m01 = m.m10 = 0;
1245 gfxline_transform(line, &m);
1248 void gfxgradient_destroy(gfxgradient_t*gradient)
1250 while(gradient) {
1251 gfxgradient_t*next = gradient->next;
1252 free(gradient);
1253 gradient = next;
1257 gfxparams_t* gfxparams_new()
1259 return (gfxparams_t*)rfx_calloc(sizeof(gfxparams_t));
1262 void gfxparams_store(gfxparams_t*params, const char*key, const char*value)
1264 gfxparam_t*o = params->params;
1265 while(o) {
1266 if(!strcmp(key, o->key)) {
1267 /* overwrite old value */
1268 free((void*)o->value);
1269 o->value = strdup(value);
1270 return;
1272 o = o->next;
1274 gfxparam_t*p = (gfxparam_t*)malloc(sizeof(gfxparam_t));
1275 p->key = strdup(key);
1276 p->value = strdup(value);
1277 p->next = 0;
1279 if(params->last) {
1280 params->last->next = p;
1281 params->last = p;
1282 } else {
1283 params->params = p;
1284 params->last = p;
1288 void gfxparams_free(gfxparams_t*params)
1290 gfxparam_t*p = params->params;
1291 while(p) {
1292 gfxparam_t*next = p->next;
1293 free((void*)p->key);
1294 if(p->value) free((void*)p->value);
1295 free(p);
1296 p = next;
1298 free(params);
1301 static void turnpoint(double x, double y, gfxmatrix_t* m, double *_x, double*_y)
1303 *_x = m->m00*x + m->m10*y + m->tx;
1304 *_y = m->m01*x + m->m11*y + m->ty;
1307 gfxbbox_t gfxbbox_transform(gfxbbox_t*bbox, gfxmatrix_t*m)
1309 double x1, y1, x2, y2, x3, y3, x4, y4;
1310 turnpoint(bbox->xmin, bbox->xmin, m, &x1, &y1);
1311 turnpoint(bbox->xmax, bbox->ymin, m, &x2, &y2);
1312 turnpoint(bbox->xmin, bbox->ymax, m, &x3, &y3);
1313 turnpoint(bbox->xmax, bbox->ymax, m, &x4, &y4);
1315 gfxbbox_t new_bbox = {x1, y1, x1, y1};
1316 new_bbox = gfxbbox_expand_to_point(new_bbox, x2, y2);
1317 new_bbox = gfxbbox_expand_to_point(new_bbox, x3, y3);
1318 new_bbox = gfxbbox_expand_to_point(new_bbox, x4, y4);
1319 return new_bbox;