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