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