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