frameresets option
[swftools.git] / lib / devices / render.c
1 /* render.c
2
3    Part of the swftools package.
4
5    Copyright (c) 2005/2006/2007 Matthias Kramm <kramm@quiss.org> 
6  
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
20
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include <math.h>
24 #include <memory.h>
25 #include "../gfxdevice.h"
26 #include "../gfxtools.h"
27 #include "../mem.h"
28 #define PNG_INLINE_EXPORTS
29 #include "../types.h"
30 #include "../png.c"
31 #include "render.h"
32
33 typedef gfxcolor_t RGBA;
34
35 typedef struct _renderpoint
36 {
37     float x;
38 } renderpoint_t;
39
40 typedef struct _renderline
41 {
42     renderpoint_t*points;
43     int size;
44     int num;
45 } renderline_t;
46
47 typedef struct _internal_result {
48     gfximage_t img;
49     struct _internal_result*next;
50 } internal_result_t;
51
52 typedef struct _clipbuffer {
53     U32*data;
54     struct _clipbuffer*next;
55 } clipbuffer_t;
56
57 typedef struct _internal {
58     int width;
59     int height;
60     int width2;
61     int height2;
62     int bitwidth;
63     int multiply;
64     int antialize;
65     int zoom;
66     int ymin, ymax;
67     int fillwhite;
68
69     RGBA* img;
70
71     clipbuffer_t*clipbuf;
72
73     renderline_t*lines;
74
75     internal_result_t*results;
76     internal_result_t*result_next;
77 } internal_t;
78
79 typedef enum {filltype_solid,filltype_clip,filltype_bitmap} filltype_t;
80
81 typedef struct _fillinfo {
82     filltype_t type; //0=solid,1=clip
83     gfxcolor_t*color;
84     gfximage_t*image;
85     gfxmatrix_t*matrix;
86     gfxcxform_t*cxform;
87     char clip;
88 } fillinfo_t;
89
90
91 static inline void add_pixel(internal_t*i, float x, int y)
92 {
93     renderpoint_t p;
94
95     if(x >= i->width2 || y >= i->height2 || y<0) return;
96     p.x = x;
97     if(y<i->ymin) i->ymin = y;
98     if(y>i->ymax) i->ymax = y;
99
100     renderline_t*l = &i->lines[y];
101
102     if(l->num == l->size) {
103         l->size += 32;
104         l->points = (renderpoint_t*)rfx_realloc(l->points, l->size * sizeof(renderpoint_t));
105     }
106     l->points[l->num] = p;
107     l->num++;
108 }
109
110 /* set this to 0.777777 or something if the "both fillstyles set while not inside shape"
111    problem appears to often */
112 #define CUT 0.5
113
114 #define INT(x) ((int)((x)+16)-16)
115
116 static void add_line(gfxdevice_t*dev , double x1, double y1, double x2, double y2)
117 {
118     internal_t*i = (internal_t*)dev->internal;
119     double diffx, diffy;
120     double ny1, ny2, stepx;
121 /*    if(DEBUG&4) {
122         int l = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
123         printf(" l[%d - %.2f/%.2f -> %.2f/%.2f]\n", l, x1/20.0, y1/20.0, x2/20.0, y2/20.0);
124     }*/
125
126     if(y2 < y1) {
127         double x;
128         double y;
129         x = x1;x1 = x2;x2=x;
130         y = y1;y1 = y2;y2=y;
131     }
132     
133     diffx = x2 - x1;
134     diffy = y2 - y1;
135     
136     ny1 = INT(y1)+CUT;
137     ny2 = INT(y2)+CUT;
138
139     if(ny1 < y1) {
140         ny1 = INT(y1) + 1.0 + CUT;
141     }
142     if(ny2 >= y2) {
143         ny2 = INT(y2) - 1.0 + CUT;
144     }
145
146     if(ny1 > ny2)
147         return;
148
149     stepx = diffx/diffy;
150     x1 = x1 + (ny1-y1)*stepx;
151     x2 = x2 + (ny2-y2)*stepx;
152
153     {
154         int posy=INT(ny1);
155         int endy=INT(ny2);
156         double posx=0;
157         double startx = x1;
158
159         while(posy<=endy) {
160             float xx = (float)(startx + posx);
161             add_pixel(i, xx ,posy);
162             posx+=stepx;
163             posy++;
164         }
165     }
166 }
167 #define PI 3.14159265358979
168 static void add_solidline(gfxdevice_t*dev, double x1, double y1, double x2, double y2, double width)
169 {
170     /* TODO: handle cap styles */
171
172     internal_t*i = (internal_t*)dev->internal;
173
174     double dx = x2-x1;
175     double dy = y2-y1;
176     double sd;
177     double d;
178
179     int t;
180     int segments;
181     double lastx,lasty;
182     double vx,vy;
183     double xx,yy;
184   
185     /* Make sure the line is always at least one pixel wide */
186 #ifdef LINEMODE1
187     /* That's what Macromedia's Player does at least at zoom level >= 1.  */
188     width += 1.0;
189 #else
190     /* That's what Macromedia's Player seems to do at zoom level 0.  */
191     /* TODO: needs testing */
192
193     /* TODO: how does this interact with scaling? */
194     if(width * i->multiply < 1.0)
195         width = 1.0 / i->multiply;
196 #endif
197
198     sd = (double)dx*(double)dx+(double)dy*(double)dy;
199     d = sqrt(sd);
200
201     if(!dx && !dy) {
202         vx = 1;
203         vy = 0;
204     } else {
205         vx = ( dy/d);
206         vy = (-dx/d);
207     }
208
209     segments = width/2;
210     if(segments < 2)
211         segments = 2;
212
213     segments = 8;
214
215     vx=vx*width*0.5;
216     vy=vy*width*0.5;
217
218     xx = x2+vx;
219     yy = y2+vy;
220     add_line(dev, x1+vx, y1+vy, xx, yy);
221     lastx = xx;
222     lasty = yy;
223     for(t=1;t<segments;t++) {
224         double s = sin(t*PI/segments);
225         double c = cos(t*PI/segments);
226         xx = (x2 + vx*c - vy*s);
227         yy = (y2 + vx*s + vy*c);
228         add_line(dev, lastx, lasty, xx, yy);
229         lastx = xx;
230         lasty = yy;
231     }
232     
233     xx = (x2-vx);
234     yy = (y2-vy);
235     add_line(dev, lastx, lasty, xx, yy);
236     lastx = xx;
237     lasty = yy;
238     xx = (x1-vx);
239     yy = (y1-vy);
240     add_line(dev, lastx, lasty, xx, yy);
241     lastx = xx;
242     lasty = yy;
243     for(t=1;t<segments;t++) {
244         double s = sin(t*PI/segments);
245         double c = cos(t*PI/segments);
246         xx = (x1 - vx*c + vy*s);
247         yy = (y1 - vx*s - vy*c);
248         add_line(dev, lastx, lasty, xx, yy);
249         lastx = xx;
250         lasty = yy;
251     }
252     add_line(dev, lastx, lasty, (x1+vx), (y1+vy));
253 }
254
255 static int compare_renderpoints(const void * _a, const void * _b)
256 {
257     renderpoint_t*a = (renderpoint_t*)_a;
258     renderpoint_t*b = (renderpoint_t*)_b;
259     if(a->x < b->x) return -1;
260     if(a->x > b->x) return 1;
261     return 0;
262 }
263
264 static void fill_line_solid(RGBA*line, U32*z, int y, int x1, int x2, RGBA col)
265 {
266     int x = x1;
267
268     U32 bit = 1<<(x1&31);
269     int bitpos = (x1/32);
270
271     if(col.a!=255) {
272         int ainv = 255-col.a;
273         col.r = (col.r*col.a)>>8;
274         col.g = (col.g*col.a)>>8;
275         col.b = (col.b*col.a)>>8;
276         do {
277             if(z[bitpos]&bit) {
278                 line[x].r = ((line[x].r*ainv)>>8)+col.r;
279                 line[x].g = ((line[x].g*ainv)>>8)+col.g;
280                 line[x].b = ((line[x].b*ainv)>>8)+col.b;
281                 //line[x].a = 255;
282                 line[x].a = ((line[x].a*ainv)>>8)+col.a;
283             }
284             bit <<= 1;
285             if(!bit) {
286                 bit = 1;bitpos++;
287             }
288         } while(++x<x2);
289     } else {
290         do {
291             if(z[bitpos]&bit) {
292                 line[x] = col;
293             }
294             bit <<= 1;
295             if(!bit) {
296                 bit = 1;bitpos++;
297             }
298         } while(++x<x2);
299     }
300 }
301
302 static void fill_line_bitmap(RGBA*line, U32*z, int y, int x1, int x2, fillinfo_t*info)
303 {
304     int x = x1;
305
306     gfxmatrix_t*m = info->matrix;
307     gfximage_t*b = info->image;
308     
309     if(!b->width || !b->height) {
310         gfxcolor_t red = {255,255,0,0};
311         fill_line_solid(line, z, y, x1, x2, red);
312         return;
313     }
314     
315     double det = m->m00*m->m11 - m->m01*m->m10;
316     if(fabs(det) < 0.0005) { 
317         /* x direction equals y direction- the image is invisible */
318         return;
319     }
320     det = 1.0/det;
321     double xx1 =  (  (-m->tx) * m->m11 - (y - m->ty) * m->m10) * det;
322     double yy1 =  (- (-m->tx) * m->m01 + (y - m->ty) * m->m00) * det;
323     double xinc1 = m->m11 * det;
324     double yinc1 = m->m01 * det;
325     
326     U32 bit = 1<<(x1&31);
327     int bitpos = (x1/32);
328
329     do {
330         if(z[bitpos]&bit) {
331             RGBA col;
332             int xx = (int)(xx1 + x * xinc1);
333             int yy = (int)(yy1 - x * yinc1);
334             int ainv;
335
336             if(info->clip) {
337                 if(xx<0) xx=0;
338                 if(xx>=b->width) xx = b->width-1;
339                 if(yy<0) yy=0;
340                 if(yy>=b->height) yy = b->height-1;
341             } else {
342                 xx %= b->width;
343                 yy %= b->height;
344                 if(xx<0) xx += b->width;
345                 if(yy<0) yy += b->height;
346             }
347
348             col = b->data[yy*b->width+xx];
349             ainv = 255-col.a;
350
351             /* needs bitmap with premultiplied alpha */
352             line[x].r = ((line[x].r*ainv)>>8)+col.r;
353             line[x].g = ((line[x].g*ainv)>>8)+col.g;
354             line[x].b = ((line[x].b*ainv)>>8)+col.b;
355             line[x].a = 255;
356         }
357         bit <<= 1;
358         if(!bit) {
359             bit = 1;bitpos++;
360         }
361     } while(++x<x2);
362 }
363
364 static void fill_line_clip(RGBA*line, U32*z, int y, int x1, int x2)
365 {
366     int x = x1;
367
368     U32 bit = 1<<(x1&31);
369     int bitpos = (x1/32);
370
371     do {
372         z[bitpos]|=bit;
373         bit <<= 1;
374         if(!bit) {
375             bit = 1;bitpos++;
376         }
377     } while(++x<x2);
378 }
379
380 void fill_line(gfxdevice_t*dev, RGBA*line, U32*zline, int y, int startx, int endx, fillinfo_t*fill)
381 {
382     if(fill->type == filltype_solid)
383         fill_line_solid(line, zline, y, startx, endx, *fill->color);
384     else if(fill->type == filltype_clip)
385         fill_line_clip(line, zline, y, startx, endx);
386     else if(fill->type == filltype_bitmap)
387         fill_line_bitmap(line, zline, y, startx, endx, fill);
388     // etc.
389 }
390
391 void fill(gfxdevice_t*dev, fillinfo_t*fill)
392 {
393     internal_t*i = (internal_t*)dev->internal;
394     int y;
395     U32 clipdepth = 0;
396     for(y=i->ymin;y<=i->ymax;y++) {
397         renderpoint_t*points = i->lines[y].points;
398         RGBA*line = &i->img[i->width2*y];
399         U32*zline = &i->clipbuf->data[i->bitwidth*y];
400
401         int n;
402         int num = i->lines[y].num;
403         int lastx;
404         qsort(points, num, sizeof(renderpoint_t), compare_renderpoints);
405
406         for(n=0;n<num;n++) {
407             renderpoint_t*p = &points[n];
408             renderpoint_t*next= n<num-1?&points[n+1]:0;
409             int startx = p->x;
410             int endx = next?next->x:i->width2;
411             if(endx > i->width2)
412                 endx = i->width2;
413             if(startx < 0)
414                 startx = 0;
415             if(endx < 0)
416                 endx = 0;
417
418             if(!(n&1))
419                 fill_line(dev, line, zline, y, startx, endx, fill);
420
421             lastx = endx;
422             if(endx == i->width2)
423                 break;
424         }
425         if(fill->type == filltype_clip) {
426             if(i->clipbuf->next) {
427                 U32*line2 = &i->clipbuf->next->data[i->bitwidth*y];
428                 int x;
429                 for(x=0;x<i->bitwidth;x++)
430                     zline[x] &= line2[x];
431             }
432         }
433
434         i->lines[y].num = 0;
435     }
436 }
437
438 void fill_solid(gfxdevice_t*dev, gfxcolor_t* color)
439 {
440     fillinfo_t info;
441     memset(&info, 0, sizeof(info));
442     info.type = filltype_solid;
443     info.color = color;
444     fill(dev, &info);
445 }
446
447 int render_setparameter(struct _gfxdevice*dev, const char*key, const char*value)
448 {
449     internal_t*i = (internal_t*)dev->internal;
450     if(!strcmp(key, "antialize") || !strcmp(key, "antialise")) {
451         i->antialize = atoi(value);
452         i->zoom = i->antialize * i->multiply;
453         return 1;
454     } else if(!strcmp(key, "multiply")) {
455         i->multiply = atoi(value);
456         i->zoom = i->antialize * i->multiply;
457         fprintf(stderr, "Warning: multiply not implemented yet\n");
458         return 1;
459     } else if(!strcmp(key, "fillwhite")) {
460         i->fillwhite = atoi(value);
461         return 1;
462     }
463     return 0;
464 }
465
466 void newclip(struct _gfxdevice*dev)
467 {
468     internal_t*i = (internal_t*)dev->internal;
469     
470     clipbuffer_t*c = (clipbuffer_t*)rfx_calloc(sizeof(clipbuffer_t));
471     c->data = (U32*)rfx_calloc(sizeof(U32) * i->bitwidth * i->height2);
472     c->next = i->clipbuf;
473     i->clipbuf = c;
474     if(c->next)
475         memcpy(c->data, c->next->data, i->bitwidth*i->height2);
476     else
477         memset(c->data, 0, sizeof(U32)*i->bitwidth*i->height2);
478 }
479
480 void endclip(struct _gfxdevice*dev, char removelast)
481 {
482     internal_t*i = (internal_t*)dev->internal;
483    
484     /* test for at least one cliplevel (the one we created ourselves) */
485     if(!i->clipbuf || (!i->clipbuf->next && !removelast)) {
486         fprintf(stderr, "endclip without any active clip buffers\n");
487         return;
488     }
489
490     clipbuffer_t*c = i->clipbuf;
491     i->clipbuf = i->clipbuf->next;
492     c->next = 0;
493     free(c->data);c->data = 0;
494     free(c);
495 }
496
497 void render_stroke(struct _gfxdevice*dev, gfxline_t*line, gfxcoord_t width, gfxcolor_t*color, gfx_capType cap_style, gfx_joinType joint_style, gfxcoord_t miterLimit)
498 {
499     internal_t*i = (internal_t*)dev->internal;
500     double x,y;
501     
502     /*if(cap_style != gfx_capRound || joint_style != gfx_joinRound) {
503         fprintf(stderr, "Warning: cap/joint style != round not yet supported\n");
504     }*/
505
506     while(line) {
507         if(line->type == gfx_moveTo) {
508         } else if(line->type == gfx_lineTo) {
509             double x1=x*i->zoom,y1=y*i->zoom;
510             double x3=line->x*i->zoom,y3=line->y*i->zoom;
511             add_solidline(dev, x1, y1, x3, y3, width * i->multiply);
512             fill_solid(dev, color);
513         } else if(line->type == gfx_splineTo) {
514             int t,parts;
515             double xx,yy;
516            
517             double x1=x*i->zoom,y1=y*i->zoom;
518             double x2=line->sx*i->zoom,y2=line->sy*i->zoom;
519             double x3=line->x*i->zoom,y3=line->y*i->zoom;
520             
521             double c = abs(x3-2*x2+x1) + abs(y3-2*y2+y1);
522             xx=x1;
523             yy=y1;
524
525             parts = (int)(sqrt(c)/3);
526             if(!parts) parts = 1;
527
528             for(t=1;t<=parts;t++) {
529                 double nx = (double)(t*t*x3 + 2*t*(parts-t)*x2 + (parts-t)*(parts-t)*x1)/(double)(parts*parts);
530                 double ny = (double)(t*t*y3 + 2*t*(parts-t)*y2 + (parts-t)*(parts-t)*y1)/(double)(parts*parts);
531                 
532                 add_solidline(dev, xx, yy, nx, ny, width * i->multiply);
533                 fill_solid(dev, color);
534                 xx = nx;
535                 yy = ny;
536             }
537         }
538         x = line->x;
539         y = line->y;
540         line = line->next;
541     }
542 }
543
544 static void draw_line(gfxdevice_t*dev, gfxline_t*line)
545 {
546     internal_t*i = (internal_t*)dev->internal;
547     double x,y;
548
549     while(line)
550     {
551         int x1,y1,x2,y2,x3,y3;
552
553         if(line->type == gfx_moveTo) {
554         } else if(line->type == gfx_lineTo) {
555             double x1=x*i->zoom,y1=y*i->zoom;
556             double x3=line->x*i->zoom,y3=line->y*i->zoom;
557             
558             add_line(dev, x1, y1, x3, y3);
559         } else if(line->type == gfx_splineTo) {
560             int c,t,parts,qparts;
561             double xx,yy;
562             
563             double x1=x*i->zoom,y1=y*i->zoom;
564             double x2=line->sx*i->zoom,y2=line->sy*i->zoom;
565             double x3=line->x*i->zoom,y3=line->y*i->zoom;
566             
567             c = abs(x3-2*x2+x1) + abs(y3-2*y2+y1);
568             xx=x1;
569             yy=y1;
570
571             parts = (int)(sqrt(c));
572             if(!parts) parts = 1;
573
574             for(t=1;t<=parts;t++) {
575                 double nx = (double)(t*t*x3 + 2*t*(parts-t)*x2 + (parts-t)*(parts-t)*x1)/(double)(parts*parts);
576                 double ny = (double)(t*t*y3 + 2*t*(parts-t)*y2 + (parts-t)*(parts-t)*y1)/(double)(parts*parts);
577                 
578                 add_line(dev, xx, yy, nx, ny);
579                 xx = nx;
580                 yy = ny;
581             }
582         }
583         x = line->x;
584         y = line->y;
585         line = line->next;
586     }
587 }
588
589 void render_startclip(struct _gfxdevice*dev, gfxline_t*line)
590 {
591     internal_t*i = (internal_t*)dev->internal;
592     fillinfo_t info;
593     memset(&info, 0, sizeof(info));
594     newclip(dev);
595     info.type = filltype_clip;
596     draw_line(dev, line);
597     fill(dev, &info);
598 }
599
600 void render_endclip(struct _gfxdevice*dev)
601 {
602     internal_t*i = (internal_t*)dev->internal;
603     endclip(dev, 0);
604 }
605
606 void render_fill(struct _gfxdevice*dev, gfxline_t*line, gfxcolor_t*color)
607 {
608     internal_t*i = (internal_t*)dev->internal;
609
610     draw_line(dev, line);
611     fill_solid(dev, color);
612 }
613
614 void render_fillbitmap(struct _gfxdevice*dev, gfxline_t*line, gfximage_t*img, gfxmatrix_t*matrix, gfxcxform_t*cxform)
615 {
616     internal_t*i = (internal_t*)dev->internal;
617
618     gfxcolor_t black = {255,0,0,0};
619
620     gfxmatrix_t m2 = *matrix;
621
622     draw_line(dev, line);
623
624     fillinfo_t info;
625     memset(&info, 0, sizeof(info));
626     info.type = filltype_bitmap;
627     info.image = img;
628     info.matrix = &m2;
629     info.cxform = cxform;
630
631     m2.m00 *= i->zoom; m2.m01 *= i->zoom; m2.tx *= i->zoom;
632     m2.m10 *= i->zoom; m2.m11 *= i->zoom; m2.ty *= i->zoom;
633
634     fill(dev, &info);
635 }
636
637 void render_fillgradient(struct _gfxdevice*dev, gfxline_t*line, gfxgradient_t*gradient, gfxgradienttype_t type, gfxmatrix_t*matrix)
638 {
639     internal_t*i = (internal_t*)dev->internal;
640     
641     gfxcolor_t black = {255,0,0,0};
642
643     draw_line(dev, line);
644     fill_solid(dev, &black);
645 }
646
647 void render_addfont(struct _gfxdevice*dev, gfxfont_t*font)
648 {
649 }
650
651 void render_drawchar(struct _gfxdevice*dev, gfxfont_t*font, int glyphnr, gfxcolor_t*color, gfxmatrix_t*matrix)
652 {
653     internal_t*i = (internal_t*)dev->internal;
654     if(!font)
655         return;
656
657     /* align characters to whole pixels */
658     matrix->tx = (int)(matrix->tx * i->antialize) / i->antialize;
659     matrix->ty = (int)(matrix->ty * i->antialize) / i->antialize;
660
661     gfxglyph_t*glyph = &font->glyphs[glyphnr];
662     gfxline_t*line2 = gfxline_clone(glyph->line);
663     gfxline_transform(line2, matrix);
664     draw_line(dev, line2);
665     fill_solid(dev, color);
666     gfxline_free(line2);
667     
668     return;
669 }
670
671 void render_result_write(gfxresult_t*r, int filedesc)
672 {
673     internal_result_t*i= (internal_result_t*)r->internal;
674 }
675 int render_result_save(gfxresult_t*r, const char*filename)
676 {
677     internal_result_t*i= (internal_result_t*)r->internal;
678     if(!i) {
679         return 0; // no pages drawn
680     }
681     if(i->next) {
682         int nr=0;
683         char filenamebuf[256];
684         char*origname = strdup(filename);
685         int l = strlen(origname);
686         if(l>3 && strchr("gG",origname[l-1]) && strchr("nN",filename[l-2]) &&
687                 strchr("pP",origname[l-3]) && filename[l-4]=='.') {
688             origname[l-4] = 0;
689         }
690         while(i->next) {
691             sprintf(filenamebuf, "%s.%d.png", origname, nr);
692             writePNG(filename, (unsigned char*)i->img.data, i->img.width, i->img.height);
693             nr++;
694         }
695         free(origname);
696     } else {
697         writePNG(filename, (unsigned char*)i->img.data, i->img.width, i->img.height);
698     }
699     return 1;
700 }
701 char*gfximage_asXPM(gfximage_t*img, int depth)
702 {
703     int d= 256/depth;
704     char*str = (char*)malloc(img->width*img->height*4 + 500 + 16*depth*depth*depth);
705     char*p = str;
706     p+= sprintf(p, "static char *noname[] = {\n\"%d %d 262144 3\",\n");
707     int r,g,b;
708     for(r=0;r<depth;r++)
709     for(g=0;g<depth;g++)
710     for(b=0;b<depth;b++) {
711         p += sprintf(p, "\"%c%c%c c #%02x%02x%02x\",\n", r+32,g+32,b+32, r*d,g*d,b*d);
712     }
713     int y;
714     for(y=0;y<img->height;y++)  {
715         p+=sprintf(p, "\"");
716         gfxcolor_t*col = &img->data[y*img->height];
717         int x;
718         for(x=0;x<img->width;x++) {
719             p+=sprintf(p, "%c%c%c", 32+(col->r/d), 32+(col->g/d), 32+(col->b/d));
720         }
721         p+=sprintf(p, "\",\n");
722     }
723     *p = 0;
724     return p;
725 }
726 void*render_result_get(gfxresult_t*r, const char*name)
727 {
728     internal_result_t*i= (internal_result_t*)r->internal;
729     if(!strncmp(name,"xpm",3)) {
730         int pagenr = atoi(&name[3]);
731         if(pagenr<0)
732             pagenr=0;
733         while(pagenr>0) {
734             i = i->next;
735             if(!i)
736                 return 0;
737             pagenr--;
738         }
739         return gfximage_asXPM(&i->img, 64);
740     } else if(!strncmp(name,"page",4)) {
741         int pagenr = atoi(&name[4]);
742         if(pagenr<0)
743             pagenr=0;
744         while(pagenr>0) {
745             i = i->next;
746             if(!i)
747                 return 0;
748             pagenr--;
749         }
750         return &i->img;
751     }
752     return 0;
753 }
754 void render_result_destroy(gfxresult_t*r)
755 {
756     internal_result_t*i= (internal_result_t*)r->internal;
757     r->internal = 0;
758     while(i) {
759         internal_result_t*next = i->next;
760         free(i->img.data);i->img.data = 0;
761
762         /* FIXME memleak
763            the following rfx_free causes a segfault on WIN32 machines,
764            if executed */
765         //rfx_free(i);
766
767         i = next;
768     }
769     rfx_free(r);
770 }
771
772 gfxresult_t* render_finish(struct _gfxdevice*dev)
773 {
774     internal_t*i = (internal_t*)dev->internal;
775     
776     gfxresult_t* res = (gfxresult_t*)rfx_calloc(sizeof(gfxresult_t));
777     
778     res->internal = i->results;i->results = 0;
779     res->write = render_result_write;
780     res->save = render_result_save;
781     res->get = render_result_get;
782     res->destroy = render_result_destroy;
783
784     free(dev->internal); dev->internal = 0; i = 0;
785
786     return res;
787 }
788
789 void render_startpage(struct _gfxdevice*dev, int width, int height)
790 {
791     internal_t*i = (internal_t*)dev->internal;
792     int y;
793
794     if(i->width2 || i->height2) {
795         fprintf(stderr, "Error: startpage() called twice (no endpage()?)\n");
796         exit(1);
797     }
798     
799     i->width = width*i->multiply;
800     i->height = height*i->multiply;
801     i->width2 = width*i->zoom;
802     i->height2 = height*i->zoom;
803     i->bitwidth = (i->width2+31)/32;
804
805     i->lines = (renderline_t*)rfx_alloc(i->height2*sizeof(renderline_t));
806     for(y=0;y<i->height2;y++) {
807         memset(&i->lines[y], 0, sizeof(renderline_t));
808         i->lines[y].points = 0;
809         i->lines[y].num = 0;
810     }
811     i->img = (RGBA*)rfx_calloc(sizeof(RGBA)*i->width2*i->height2);
812     if(i->fillwhite) {
813         memset(i->img, 0xff, sizeof(RGBA)*i->width2*i->height2);
814     }
815
816     i->ymin = 0x7fffffff;
817     i->ymax = -0x80000000;
818
819
820     /* initialize initial clipping field, which doesn't clip anything yet */
821     newclip(dev);
822     memset(i->clipbuf->data, 255, sizeof(U32)*i->bitwidth*i->height2);
823 }
824
825 static void store_image(internal_t*i, internal_result_t*ir)
826 {
827     ir->img.data = (gfxcolor_t*)malloc(i->width*i->height*sizeof(gfxcolor_t));
828     ir->img.width = i->width;
829     ir->img.height = i->height;
830
831     gfxcolor_t*dest = ir->img.data;
832
833     if(i->antialize <= 1) /* no antializing */ {
834         int y;
835         for(y=0;y<i->height;y++) {
836             RGBA*line = &i->img[y*i->width];
837             memcpy(&dest[y*i->width], line, sizeof(RGBA)*i->width);
838         }
839     } else {
840         RGBA**lines = (RGBA**)rfx_calloc(sizeof(RGBA*)*i->antialize);
841         int q = i->antialize*i->antialize;
842         int ypos = 0;
843         int y;
844         int y2=0;
845         for(y=0;y<i->height2;y++) {
846             int n;
847             ypos = y % i->antialize;
848             lines[ypos] = &i->img[y*i->width2];
849             if(ypos == i->antialize-1) {
850                 RGBA*out = &dest[(y2++)*i->width];
851                 int x;
852                 int r,g,b,a;
853                 for(x=0;x<i->width;x++) {
854                     int xpos = x*i->antialize;
855                     int yp;
856                     U32 r=0,g=0,b=0,a=0;
857                     for(yp=0;yp<i->antialize;yp++) {
858                         RGBA*lp = &lines[yp][xpos];
859                         int xp;
860                         for(xp=0;xp<i->antialize;xp++) {
861                             RGBA*p = &lp[xp];
862                             r += p->r;
863                             g += p->g;
864                             b += p->b;
865                             a += p->a;
866                         }
867                     }
868                     out[x].r = r / q;
869                     out[x].g = g / q;
870                     out[x].b = b / q;
871                     out[x].a = a / q;
872                 }
873             }
874         }
875         rfx_free(lines);
876     }
877 }
878
879 void render_endpage(struct _gfxdevice*dev)
880 {
881     internal_t*i = (internal_t*)dev->internal;
882     
883     if(!i->width2 || !i->height2) {
884         fprintf(stderr, "Error: endpage() called without corresponding startpage()\n");
885         exit(1);
886     }
887
888     endclip(dev, 1);
889     while(i->clipbuf) {
890         fprintf(stderr, "Warning: unclosed clip while processing endpage()\n");
891         endclip(dev, 1);
892     }
893     
894     internal_result_t*ir= (internal_result_t*)rfx_calloc(sizeof(internal_result_t));
895
896     int y,x;
897
898     store_image(i, ir);
899
900     ir->next = 0;
901     if(i->result_next) {
902         i->result_next->next = ir;
903     }
904     if(!i->results) {
905         i->results = ir;
906     }
907     i->result_next = ir;
908
909     for(y=0;y<i->height2;y++) {
910         rfx_free(i->lines[y].points); i->lines[y].points = 0;
911     }
912     rfx_free(i->lines);i->lines=0;
913
914     if(i->img) {rfx_free(i->img);i->img = 0;}
915
916     i->width2 = 0;
917     i->height2 = 0;
918 }
919
920 void render_drawlink(struct _gfxdevice*dev, gfxline_t*line, const char*action)
921 {
922     /* not supported for this output device */
923 }
924
925 void gfxdevice_render_init(gfxdevice_t*dev)
926 {
927     internal_t*i = (internal_t*)rfx_calloc(sizeof(internal_t));
928     memset(dev, 0, sizeof(gfxdevice_t));
929     
930     dev->name = "render";
931
932     dev->internal = i;
933     
934     i->width = 0;
935     i->width2 = 0;
936     i->height = 0;
937     i->height2 = 0;
938     i->antialize = 1;
939     i->multiply = 1;
940     i->zoom = 1;
941
942     dev->setparameter = render_setparameter;
943     dev->startpage = render_startpage;
944     dev->startclip = render_startclip;
945     dev->endclip = render_endclip;
946     dev->stroke = render_stroke;
947     dev->fill = render_fill;
948     dev->fillbitmap = render_fillbitmap;
949     dev->fillgradient = render_fillgradient;
950     dev->addfont = render_addfont;
951     dev->drawchar = render_drawchar;
952     dev->drawlink = render_drawlink;
953     dev->endpage = render_endpage;
954     dev->finish = render_finish;
955 }
956