fixed moments calculcation
[swftools.git] / lib / gfxtools.c
blob0f39da7cd2cfb0fef299395fa7c8f3023e3d3044
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 gfxline_t* gfxline_restitch(gfxline_t*line)
1116 dict_t*ff = dict_new2(&gfxpoint_type);
1117 dict_t*rev = dict_new2(&gfxpoint_type);
1119 gfxline_t*prev=0;
1120 while(line) {
1121 gfxline_t*next = line->next;
1122 if(line->type == gfx_moveTo && (line->next && line->next->type != gfx_moveTo)) {
1123 gfxpoint_t xy = {line->x, line->y};
1124 dict_put(ff, &xy, line);
1125 prev = line;
1126 } else if(!line->next || line->next->type == gfx_moveTo) {
1127 if(prev) {
1128 gfxpoint_t xy = {line->x, line->y};
1129 dict_put(rev, &xy, prev);
1130 line->next = 0;
1131 prev=0;
1134 line = next;
1137 gfxpoint_t pos = {0,0};
1139 gfxline_t*result = 0;
1140 gfxline_t*last = 0;
1142 char first = 1;
1143 while(dict_count(ff)) {
1144 char reverse = 0, stitch = 1;
1145 gfxline_t*l = dict_lookup(ff, &pos);
1146 if(l) {
1147 char d = dict_del2(ff,&pos,l);assert(d);
1148 } else {
1149 l = dict_lookup(rev, &pos);
1150 if(l) {
1151 reverse = 1;
1152 char d = dict_del2(rev,&pos,l);assert(d);
1155 if(!l) {
1156 /* try to find *any* entry. this is costly, but
1157 doesn't happen too often */
1158 stitch = 0;
1159 DICT_ITERATE_DATA(ff, gfxline_t*, l2) {
1160 l = l2;
1161 break;
1163 assert(l);
1164 gfxpoint_t xy = {l->x,l->y};
1165 char d = dict_del2(ff,&xy,l);assert(d);
1168 gfxline_t*end = l;
1169 if(!reverse) {
1170 while(end->next) end = end->next;
1171 pos.x = end->x;
1172 pos.y = end->y;
1173 char d = dict_del2(rev,&pos,l);assert(d);
1174 } else {
1175 l = gfxline_reverse(l);
1176 pos.x = end->x;
1177 pos.y = end->y;
1178 char d = dict_del2(ff,&pos,end);assert(d);
1181 assert(l->type == gfx_moveTo);
1182 if(stitch && !first) {
1183 /* cut away the moveTo */
1184 gfxline_t*next = l->next;
1185 free(l);
1186 l = next;
1189 if(!last) {
1190 result = l;
1191 last = end;
1192 } else {
1193 last->next = l;
1194 last = end;
1196 first = 0;
1198 dict_destroy(ff);
1199 dict_destroy(rev);
1200 return result;
1203 gfxline_t* gfxline_reverse(gfxline_t*line)
1205 gfxline_t*b = 0;
1206 while(line) {
1207 gfxline_t*next = line->next;
1208 if(next && next->type != gfx_moveTo) {
1209 line->type = next->type;
1210 line->sx = next->sx;
1211 line->sy = next->sy;
1212 } else {
1213 line->type = gfx_moveTo;
1215 line->next = b;
1216 b = line;
1217 line = next;
1219 return b;
1222 void gfxgradient_destroy(gfxgradient_t*gradient)
1224 while(gradient) {
1225 gfxgradient_t*next = gradient->next;
1226 free(gradient);
1227 gradient = next;
1231 gfxparams_t* gfxparams_new()
1233 return (gfxparams_t*)rfx_calloc(sizeof(gfxparams_t));
1236 void gfxparams_store(gfxparams_t*params, const char*key, const char*value)
1238 gfxparam_t*o = params->params;
1239 while(o) {
1240 if(!strcmp(key, o->key)) {
1241 /* overwrite old value */
1242 free((void*)o->value);
1243 o->value = strdup(value);
1244 return;
1246 o = o->next;
1248 gfxparam_t*p = (gfxparam_t*)malloc(sizeof(gfxparam_t));
1249 p->key = strdup(key);
1250 p->value = strdup(value);
1251 p->next = 0;
1253 if(params->last) {
1254 params->last->next = p;
1255 params->last = p;
1256 } else {
1257 params->params = p;
1258 params->last = p;
1262 void gfxparams_free(gfxparams_t*params)
1264 gfxparam_t*p = params->params;
1265 while(p) {
1266 gfxparam_t*next = p->next;
1267 free((void*)p->key);
1268 if(p->value) free((void*)p->value);
1269 free(p);
1270 p = next;
1272 free(params);