* fixed clipped bitmap handling
[swftools.git] / lib / modules / swfrender.c
1 /* swfrender.c
2
3    functions for rendering swf content
4       
5    Extension module for the rfxswf library.
6    Part of the swftools package.
7
8    Copyright (c) 2004 Mederra Oy <http://www.mederra.fi>
9    Copyright (c) 2004 Matthias Kramm
10  
11    This program is free software; you can redistribute it and/or modify
12    it under the terms of the GNU General Public License as published by
13    the Free Software Foundation; either version 2 of the License, or
14    (at your option) any later version.
15
16    This program is distributed in the hope that it will be useful,
17    but WITHOUT ANY WARRANTY; without even the implied warranty of
18    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19    GNU General Public License for more details.
20
21    You should have received a copy of the GNU General Public License
22    along with this program; if not, write to the Free Software
23    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
24
25 typedef struct _renderpoint
26 {
27     enum {clip_type, fill_type} type;
28     float fx;
29     int x;
30     U32 depth;
31     U32 clipdepth;
32     SHAPE2*shape;
33     SHAPELINE*shapeline;
34     //CXFORM?
35 } renderpoint_t;
36
37 typedef struct _renderline
38 {
39     TAG*points; //incremented in 128 byte steps
40 } renderline_t;
41
42 typedef struct _bitmap {
43     int width;
44     int height;
45     RGBA*data;
46     int id;
47     struct _bitmap*next;
48 } bitmap_t;
49
50 typedef struct _dummyshape
51 {
52     SHAPE2*shape;
53     struct _dummyshape*next;
54 } dummyshape_t;
55
56 typedef struct _renderbuf_internal
57 {
58     renderline_t*lines;
59     bitmap_t*bitmaps;
60     char antialize;
61     int multiply;
62     int width2,height2;
63     dummyshape_t*dshapes;
64     dummyshape_t*dshapes_next;
65 } renderbuf_internal;
66
67 #define DEBUG 0
68
69 static inline void add_pixel(RENDERBUF*dest, float x, int y, renderpoint_t*p)
70 {
71     renderbuf_internal*i = (renderbuf_internal*)dest->internal;
72     if(x >= i->width2 || y >= i->height2 || y<0) return;
73     p->x = (int)x;
74     p->fx = x;
75     swf_SetBlock(i->lines[y].points, (U8*)p, sizeof(renderpoint_t));
76 }
77
78 /* set this to 0.777777 or something if the "both fillstyles set while not inside shape"
79    problem appears to often */
80 #define CUT 0.5
81
82 static void add_line(RENDERBUF*buf, double x1, double y1, double x2, double y2, renderpoint_t*p, char thin)
83 {
84     renderbuf_internal*i = (renderbuf_internal*)buf->internal;
85 /*    if(DEBUG&4) {
86         int l = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
87         printf(" l[%d - %.2f/%.2f -> %.2f/%.2f]", l, x1/20.0, y1/20.0, x2/20.0, y2/20.0);
88     }*/
89
90     y1=y1*i->multiply;
91     y2=y2*i->multiply;
92     x1=x1*i->multiply;
93     x2=x2*i->multiply;
94     
95     y1 = y1/20.0;
96     y2 = y2/20.0;
97     x1 = x1/20.0;
98     x2 = x2/20.0;
99
100     if(y2 < y1) {
101         double x = x1;x1 = x2;x2=x;
102         double y = y1;y1 = y2;y2=y;
103     }
104     
105     double diffx = x2 - x1;
106     double diffy = y2 - y1;
107     
108     double ny1 = (int)(y1)+CUT;
109     double ny2 = (int)(y2)+CUT;
110
111     if(ny1 < y1) {
112         ny1 = (int)(y1) + 1.0 + CUT;
113     }
114     if(ny2 >= y2) {
115         ny2 = (int)(y2) - 1.0 + CUT;
116     }
117
118     if(ny1 > ny2)
119         return;
120
121     double stepx = diffx/diffy;
122     x1 = x1 + (ny1-y1)*stepx;
123     x2 = x2 + (ny2-y2)*stepx;
124
125     int posy=(int)ny1;
126     int endy=(int)ny2;
127     double posx=0;
128     double startx = x1;
129
130     while(posy<=endy) {
131         float xx = (float)(startx + posx);
132         add_pixel(buf, xx ,posy, p);
133         posx+=stepx;
134         posy++;
135     }
136 }
137 #define PI 3.14159265358979
138 static void add_solidline(RENDERBUF*buf, double x1, double y1, double x2, double y2, int width, renderpoint_t*p)
139 {
140     renderbuf_internal*i = (renderbuf_internal*)buf->internal;
141
142     double dx = x2-x1;
143     double dy = y2-y1;
144     double sd;
145     double d;
146
147     int t;
148     int segments;
149     double lastx,lasty;
150     double vx,vy;
151     double xx,yy;
152    
153     /* The Flash Player does this, too. This means every line is always at least
154        one pixel wide */
155     width += 20;
156
157     sd = (double)dx*(double)dx+(double)dy*(double)dy;
158     d = sqrt(sd);
159
160     if(!dx && !dy) {
161         vx = 1;
162         vy = 0;
163     } else {
164         vx = ( dy/d);
165         vy = (-dx/d);
166     }
167
168     segments = width/2;
169     if(segments < 2)
170         segments = 2;
171
172     segments = 8;
173
174     vx=vx*width*0.5;
175     vy=vy*width*0.5;
176
177     xx = x2+vx;
178     yy = y2+vy;
179     add_line(buf, x1+vx, y1+vy, xx, yy, p, 0);
180     lastx = xx;
181     lasty = yy;
182     for(t=1;t<segments;t++) {
183         double s = sin(t*PI/segments);
184         double c = cos(t*PI/segments);
185         xx = (x2 + vx*c - vy*s);
186         yy = (y2 + vx*s + vy*c);
187         add_line(buf, lastx, lasty, xx, yy, p, 0);
188         lastx = xx;
189         lasty = yy;
190     }
191     
192     xx = (x2-vx);
193     yy = (y2-vy);
194     add_line(buf, lastx, lasty, xx, yy, p, 0);
195     lastx = xx;
196     lasty = yy;
197     xx = (x1-vx);
198     yy = (y1-vy);
199     add_line(buf, lastx, lasty, xx, yy, p, 0);
200     lastx = xx;
201     lasty = yy;
202     for(t=1;t<segments;t++) {
203         double s = sin(t*PI/segments);
204         double c = cos(t*PI/segments);
205         xx = (x1 - vx*c + vy*s);
206         yy = (y1 - vx*s - vy*c);
207         add_line(buf, lastx, lasty, xx, yy, p, 0);
208         lastx = xx;
209         lasty = yy;
210     }
211     add_line(buf, lastx, lasty, (x1+vx), (y1+vy), p, 0);
212 }
213
214 static inline void transform_point(MATRIX*m, int x, int y, int*dx, int*dy)
215 {
216     SPOINT p,d;
217     p.x = x;
218     p.y = y;
219     d = swf_TurnPoint(p, m);
220     *dx = d.x;
221     *dy = d.y;
222 }
223
224 static int compare_renderpoints(const void * _a, const void * _b)
225 {
226     renderpoint_t*a = (renderpoint_t*)_a;
227     renderpoint_t*b = (renderpoint_t*)_b;
228     if(a->fx < b->fx) return -1;
229     if(a->fx > b->fx) return 1;
230     return 0;
231 }
232
233 void swf_Render_Init(RENDERBUF*buf, int posx, int posy, int width, int height, char antialize, int multiply)
234 {
235     renderbuf_internal*i;
236     int y;
237     memset(buf, 0, sizeof(RENDERBUF));
238     buf->width = width*multiply;
239     buf->height = height*multiply;
240     buf->posx = posx;
241     buf->posy = posy;
242     buf->internal = (renderbuf_internal*)rfx_calloc(sizeof(renderbuf_internal));
243     i = (renderbuf_internal*)buf->internal;
244     i->antialize = antialize;
245     i->multiply = antialize?multiply*2:multiply;
246     i->height2 = antialize?2*buf->height:buf->height;
247     i->width2 = antialize?2*buf->width:buf->width;
248     i->lines = (renderline_t*)rfx_alloc(i->height2*sizeof(renderline_t));
249     for(y=0;y<i->height2;y++) {
250         i->lines[y].points = swf_InsertTag(0, 0);
251     }
252 }
253 void swf_Render_AddImage(RENDERBUF*buf, U16 id, RGBA*img, int width, int height)
254 {
255     renderbuf_internal*i = (renderbuf_internal*)buf->internal;
256
257     bitmap_t*bm = rfx_calloc(sizeof(bitmap_t));
258     bm->id = id;
259     bm->width = width;
260     bm->height = height;
261     bm->data = img;
262
263     bm->next = i->bitmaps;
264     i->bitmaps = bm;
265 }
266 void swf_Render_ClearCanvas(RENDERBUF*dest)
267 {
268     renderbuf_internal*i = (renderbuf_internal*)dest->internal;
269     int y;
270     for(y=0;y<i->height2;y++) {
271         swf_ClearTag(i->lines[y].points);
272     }
273 }
274 void swf_Render_Delete(RENDERBUF*dest)
275 {
276     renderbuf_internal*i = (renderbuf_internal*)dest->internal;
277     int y;
278     bitmap_t*b = i->bitmaps;
279     dummyshape_t*d = i->dshapes;
280
281     /* delete line buffers */
282     for(y=0;y<i->height2;y++) {
283         swf_DeleteTag(i->lines[y].points);
284         i->lines[y].points = 0;
285     }
286
287     while(d) {
288         dummyshape_t*next = d->next;
289         swf_Shape2Free(d->shape);
290         free(d->shape);d->shape=0;
291         free(d);
292         d=next;
293     }
294     i->dshapes = 0;
295     
296     /* delete bitmaps */
297     while(b) {
298         bitmap_t*next = b->next;
299         //free(b->data);b->data=0;
300         rfx_free(b);
301         b = next;
302     }
303
304     rfx_free(i->lines); i->lines = 0;
305     rfx_free(dest->internal); dest->internal = 0;
306 }
307
308 void swf_RenderShape(RENDERBUF*dest, SHAPE2*shape, MATRIX*m, CXFORM*c, U16 _depth,U16 _clipdepth)
309 {
310     renderbuf_internal*i = (renderbuf_internal*)dest->internal;
311     
312     SHAPELINE*line = shape->lines;
313     int x=0,y=0;
314     MATRIX mat = *m;
315     SHAPE2* lshape = 0;
316
317     renderpoint_t p, lp;
318     memset(&p, 0, sizeof(renderpoint_t));
319     memset(&lp, 0, sizeof(renderpoint_t));
320     p.type = _clipdepth?clip_type:fill_type;
321     p.shape = shape;
322     p.depth = _depth << 16;
323     p.clipdepth = _clipdepth << 16;
324     mat.tx -= dest->posx*20;
325     mat.ty -= dest->posy*20;
326
327     if(shape->numlinestyles) {
328         dummyshape_t*dshape = rfx_calloc(sizeof(dummyshape_t));
329         lshape = rfx_calloc(sizeof(SHAPE2));
330         int t;
331         lshape->numfillstyles = shape->numlinestyles;
332         lshape->fillstyles = (FILLSTYLE*)rfx_calloc(sizeof(FILLSTYLE)*shape->numlinestyles);
333         lshape->lines = (SHAPELINE*)rfx_calloc(sizeof(SHAPELINE)*shape->numlinestyles);
334         for(t=0;t<shape->numlinestyles;t++) {
335             lshape->lines[t].fillstyle0 = t+1;
336             lshape->fillstyles[t].type = FILL_SOLID;
337             lshape->fillstyles[t].color = shape->linestyles[t].color;
338         }
339         lp.type = fill_type;
340         lp.shape = lshape;
341         lp.depth = p.depth+1;
342
343         /* add this shape to the global shape list, for deallocing */
344         dshape->shape = lshape;
345         i->dshapes_next = dshape;
346         if(!i->dshapes) {
347             i->dshapes = dshape;
348         }
349     }
350
351     if(p.clipdepth) {
352         /* reverse shape */
353         p.shapeline = 0;
354         add_line(dest, -20, 0, -20, i->height2*20, &p, 0);
355     }
356
357     while(line)
358     {
359         int x1,y1,x2,y2,x3,y3;
360
361         p.shapeline = line;
362
363         if(line->type == moveTo) {
364         } else if(line->type == lineTo) {
365             if(DEBUG&4) {
366                 x1 = x;
367                 y1 = y;
368                 x2 = line->x;
369                 y2 = line->y;
370                 int l = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
371                 printf("%d - %.2f/%.2f -> %.2f/%.2f ", l, x1/20.0, y1/20.0, x2/20.0, y2/20.0);
372             }
373
374             transform_point(&mat, x, y, &x1, &y1);
375             transform_point(&mat, line->x, line->y, &x3, &y3);
376             
377             if(line->linestyle && ! p.clipdepth) {
378                 lp.shapeline = &lshape->lines[line->linestyle-1];
379                 add_solidline(dest, x1, y1, x3, y3, shape->linestyles[line->linestyle-1].width, &lp);
380                 lp.depth++;
381             }
382             if(line->fillstyle0 || line->fillstyle1)
383                 add_line(dest, x1, y1, x3, y3, &p, 0);
384             
385             if(DEBUG&4) printf("\n");
386         } else if(line->type == splineTo) {
387             
388             transform_point(&mat, x, y, &x1, &y1);
389             transform_point(&mat, line->sx, line->sy, &x2, &y2);
390             transform_point(&mat, line->x, line->y, &x3, &y3);
391             
392             int c = abs(x3-2*x2+x1) + abs(y3-2*y2+y1);
393             int parts,qparts;
394             int t;
395             double xx=x1,yy=y1;
396
397             parts = (int)(sqrt(c)/3);
398             if(!parts) parts = 1;
399
400             if(DEBUG&4)
401             {
402                 printf("spline %.2f/%.2f -(%.2f/%.2f)-> %.2f/%.2f (c=%d, %d parts)", 
403                         x1/20.0, y1/20.0, 
404                         x2/20.0, y2/20.0, 
405                         x3/20.0, y3/20.0, c, parts);
406             }
407
408             for(t=1;t<=parts;t++) {
409                 double nx = (double)(t*t*x3 + 2*t*(parts-t)*x2 + (parts-t)*(parts-t)*x1)/(double)(parts*parts);
410                 double ny = (double)(t*t*y3 + 2*t*(parts-t)*y2 + (parts-t)*(parts-t)*y1)/(double)(parts*parts);
411                 
412                 if(line->linestyle && ! p.clipdepth) {
413                     lp.shapeline = &lshape->lines[line->linestyle-1];
414                     add_solidline(dest, xx, yy, nx, ny, shape->linestyles[line->linestyle-1].width, &lp);
415                     lp.depth++;
416                 }
417                 if(line->fillstyle0 || line->fillstyle1)
418                     add_line(dest, (int)xx, (int)yy, (int)nx, (int)ny, &p, 0);
419
420                 xx = nx;
421                 yy = ny;
422             }
423             if(DEBUG&4) 
424                 printf("\n");
425         }
426         x = line->x;
427         y = line->y;
428         line = line->next;
429     }
430 }
431
432 typedef struct _layer {
433     int fillid;
434     U32 clipdepth;
435     renderpoint_t*p;
436     struct _layer*next;
437     struct _layer*prev;
438 } layer_t;
439
440 typedef struct {
441     layer_t*layers;
442 } state_t;
443
444 static RGBA color_red = {255,255,0,0};
445 static RGBA color_white = {255,255,255,255};
446
447 static void fill_plain(RGBA*line, int x1, int x2, RGBA col)
448 {
449     int x = x1;
450     if(col.a!=255) {
451         int ainv = 255-col.a;
452         col.r = (col.r*col.a)>>8;
453         col.g = (col.g*col.a)>>8;
454         col.b = (col.b*col.a)>>8;
455         col.a = 255;
456         do {
457             line[x].r = ((line[x].r*ainv)>>8)+col.r;
458             line[x].g = ((line[x].g*ainv)>>8)+col.g;
459             line[x].b = ((line[x].b*ainv)>>8)+col.b;
460             line[x].a = 255;
461         } while(++x<x2);
462     } else {
463         do {
464             line[x] = col;
465         } while(++x<x2);
466     }
467 }
468
469 static void fill_bitmap(RGBA*line, int y, int x1, int x2, MATRIX*m, bitmap_t*b, int clip)
470 {
471     int x = x1;
472     double m11=m->sx/65536.0, m21=m->r1/65536.0;
473     double m12=m->r0/65536.0, m22=m->sy/65536.0;
474     double rx = m->tx/20.0;
475     double ry = m->ty/20.0;
476     double det = m11*m22 - m12*m21;
477     if(fabs(det) < 0.0005) { 
478         /* x direction equals y direction- the image is invisible */
479         return;
480     }
481     det = 20.0/det;
482  
483     if(!b->width || !b->height) {
484         fill_plain(line, x1, x2, color_red);
485         return;
486     }
487
488     do {
489         int xx = (int)((  (x - rx) * m22 - (y - ry) * m21)*det);
490         int yy = (int)((- (x - rx) * m12 + (y - ry) * m11)*det);
491         
492         if(clip) {
493             if(xx<0) xx=0;
494             if(xx>=b->width) xx = b->width-1;
495             if(yy<0) yy=0;
496             if(yy>=b->height) yy = b->height-1;
497         } else {
498             xx %= b->width;
499             yy %= b->height;
500         }
501
502         RGBA col = b->data[yy*b->width+xx];
503         int ainv = 255-col.a;
504
505         line[x].r = ((line[x].r*ainv)>>8)+col.r;
506         line[x].g = ((line[x].g*ainv)>>8)+col.g;
507         line[x].b = ((line[x].b*ainv)>>8)+col.b;
508         line[x].a = 255;
509     } while(++x<x2);
510 }
511
512 static void fill(RENDERBUF*dest, RGBA*line, int y, int x1, int x2, state_t*state)
513 {
514     renderbuf_internal*i = (renderbuf_internal*)dest->internal;
515
516     layer_t*l = state->layers;
517
518     if(x1>=x2) //zero width? nothing to do.
519         return;
520
521     U32 clipdepth = 0;
522     while(l) {
523         if(l->p->depth < clipdepth) {
524             if(DEBUG&2) printf("(clipped)");
525             l = l->next;
526             continue;
527         }
528         if(l->fillid < 0 /*clip*/) {
529             if(DEBUG&2) printf("(add clip %d)", l->clipdepth);
530             if(l->clipdepth > clipdepth)
531                 clipdepth = l->clipdepth;
532         } else if(l->fillid == 0) {
533             /* not filled. TODO: we should never add those in the first place */
534             if(DEBUG&2)
535                 printf("(not filled)");
536         } else if(l->fillid > l->p->shape->numfillstyles) {
537             fprintf(stderr, "Fill style out of bounds (%d>%d)", l->fillid, l->p->shape->numlinestyles);
538         } else {
539             FILLSTYLE*f;
540             if(DEBUG&2) 
541                 printf("(%d -> %d style %d)", x1, x2, l->fillid);
542
543             f = &l->p->shape->fillstyles[l->fillid-1];
544
545             if(f->type == FILL_SOLID) {
546                 /* plain color fill */
547                 fill_plain(line, x1, x2, f->color);
548             } else if(f->type == FILL_TILED || f->type == FILL_CLIPPED) {
549                 /* TODO: optimize (do this in add_pixel()?) */
550                 bitmap_t* b = i->bitmaps;
551                 while(b && b->id != f->id_bitmap) {
552                     b = b->next;
553                 }
554                 if(!b) {
555                     fprintf(stderr, "Shape references unknown bitmap %d\n", f->id_bitmap);
556                     fill_plain(line, x1, x2, color_red);
557                 } else {
558                     MATRIX m = f->m;
559                     m.tx -= dest->posx*20;
560                     m.ty -= dest->posy*20;
561                     m.sx *= i->multiply;
562                     m.sy *= i->multiply;
563                     m.r0 *= i->multiply;
564                     m.r1 *= i->multiply;
565                     m.tx *= i->multiply;
566                     m.ty *= i->multiply;
567                     fill_bitmap(line, y, x1, x2, &m, b, FILL_CLIPPED?1:0);
568                 }
569             }
570         }
571         l = l->next;
572     }
573 }
574
575 static void search_layer(state_t*state, int depth, layer_t**before, layer_t**self, layer_t**after)
576 {
577     layer_t*last=0,*l = state->layers;
578     while(l && l->p->depth < depth) {
579         last = l;
580         l = l->next;
581     }
582     *before = last;
583     if(l && l->p->depth == depth)
584         *self = l;
585     else
586         *after = l;
587 }
588 static void delete_layer(state_t*state, layer_t*todel)
589 {
590     layer_t*before=todel->prev;
591     layer_t*next = todel->next;
592     rfx_free(todel);
593     if(!before) {
594         state->layers = next;
595         if(next)
596             next->prev = 0;
597     } else {
598         before->next = next;
599         if(before->next)
600             before->next->prev = before;
601     }
602 }
603 static void add_layer(state_t*state, layer_t*before, layer_t*toadd)
604 {
605     if(!before) {
606         toadd->next = state->layers;
607         toadd->prev = 0;
608         state->layers=toadd;
609     } else {
610         toadd->next = before->next;
611         toadd->prev = before;
612         before->next = toadd;
613     }
614     if(toadd->next)
615         toadd->next->prev = toadd;
616 }
617 static void free_layers(state_t* state)
618 {
619     layer_t*l = state->layers;
620     while(l) {
621         layer_t*next = l->next;
622         rfx_free(l);
623         l = next;
624     }
625 }
626
627 static void change_state(int y, state_t* state, renderpoint_t*p)
628 {
629     layer_t*before=0, *self=0, *after=0;
630
631     if(DEBUG&2) { 
632         printf("[%s(%d,%d)/%d/%d-%d]", p->type==clip_type?"C":"F", p->x, y, p->depth, p->shapeline->fillstyle0, p->shapeline->fillstyle1);
633     }
634
635     search_layer(state, p->depth, &before, &self, &after);
636
637     if(self) {
638         /* shape update */
639         if(self->fillid<0 || !p->shapeline->fillstyle0 || !p->shapeline->fillstyle1) {
640             /* filling/clipping ends */
641             if(DEBUG&2) printf("<D>");
642             
643             delete_layer(state, self);
644         } else { 
645             /*both fill0 and fill1 are set- exchange the two, updating the layer */
646             if(self->fillid == p->shapeline->fillstyle0) {
647                 self->fillid = p->shapeline->fillstyle1;
648                 self->clipdepth = 0;
649                 self->p = p;
650                 if(DEBUG&2) printf("<X>");
651             } else if(self->fillid == p->shapeline->fillstyle1) {
652                 self->fillid = p->shapeline->fillstyle0;
653                 self->clipdepth = 0;
654                 self->p = p;
655                 if(DEBUG&2) printf("<X>");
656             } else {
657                 /* buggy shape. keep everything as-is. */
658                 if(DEBUG&2) printf("<!>");
659                 //fprintf(stderr, "<line %d: bad swap>\n", y);
660             }
661         }
662         return;
663     } else {
664         layer_t* n = 0;
665         if(p->shapeline && p->shapeline->fillstyle0 && p->shapeline->fillstyle1) {
666             /* this is a hack- a better way would be to make sure that
667                we always get (0,32), (32, 33), (33, 0) in the right order if
668                they happen to fall on the same pixel.
669                (not: (0,32), (33, 0), (32, 33))
670             */
671             fprintf(stderr, "<line %d: both fillstyles set while not inside shape>\n", y);
672             return;
673         }
674         
675         n = rfx_calloc(sizeof(layer_t));
676
677         if(DEBUG&2) printf("<+>");
678
679         if(p->type == clip_type) {
680             /* add clipping */
681             n->fillid = -1;
682             n->clipdepth = p->clipdepth;
683             n->p = p;
684         } else {
685             n->fillid = p->shapeline->fillstyle0 ? p->shapeline->fillstyle0 : p->shapeline->fillstyle1;
686             n->clipdepth = 0;
687             n->p = p;
688         }
689
690         add_layer(state, before, n);
691     }
692 }
693
694 RGBA* swf_Render(RENDERBUF*dest)
695 {
696     renderbuf_internal*i = (renderbuf_internal*)dest->internal;
697     RGBA* img = (RGBA*)rfx_alloc(sizeof(RGBA)*dest->width*dest->height);
698     int y;
699     long memory = 0;
700     RGBA * line1 = rfx_alloc(sizeof(RGBA)*i->width2);
701     RGBA * line2 = rfx_alloc(sizeof(RGBA)*i->width2);
702
703     for(y=0;y<i->height2;y++) {
704         TAG*tag = i->lines[y].points;
705         int n;
706         int size = sizeof(renderpoint_t);
707         int num = tag->len / size;
708         RGBA*line = line1;
709         if((y&1) && i->antialize)
710             line = line2;
711
712         state_t state;
713         memset(&state, 0, sizeof(state_t));
714
715         memset(line, 0, sizeof(RGBA)*i->width2);
716         memory += tag->memsize;
717         qsort(tag->data, num, size, compare_renderpoints);
718         for(n=0;n<num;n++) {
719             renderpoint_t*p = (renderpoint_t*)&tag->data[size*n];
720             renderpoint_t*next= n<num-1?(renderpoint_t*)&tag->data[size*(n+1)]:0;
721             int startx = p->x;
722             int endx = next?next->x:i->width2;
723             if(endx > i->width2)
724                 endx = i->width2;
725             if(startx < 0)
726                 startx = 0;
727
728             change_state(y, &state, p);
729
730             fill(dest, line, y, startx, endx, &state);
731             if(endx == i->width2)
732                 break;
733         }
734         free_layers(&state);
735         if(DEBUG&2) printf("\n");
736
737         if(!i->antialize) {
738             memcpy(&img[y*dest->width], line, sizeof(RGBA)*dest->width);
739         } else {
740             if(y&1) {
741                 int x;
742                 RGBA* p = &img[(y/2)*dest->width];
743                 for(x=0;x<dest->width;x++) {
744                     RGBA*p1 = &line1[x*2];
745                     RGBA*p2 = &line1[x*2+1];
746                     RGBA*p3 = &line2[x*2];
747                     RGBA*p4 = &line2[x*2+1];
748                     p[x].r = (p1->r + p2->r + p3->r + p4->r)/4;
749                     p[x].g = (p1->g + p2->g + p3->g + p4->g)/4;
750                     p[x].b = (p1->b + p2->b + p3->b + p4->b)/4;
751                     p[x].a = (p1->a + p2->a + p3->a + p4->a)/4;
752                 }
753             }
754         }
755     }
756     free(line1);
757     free(line2);
758     
759     if(DEBUG) printf("\nMemory used: %d\n", memory);
760 #ifdef STATISTICS
761     if(DEBUG) printf("Statistics:\n");
762     if(DEBUG) printf("Average layer depth: %f\n", (double)layers/layernum);
763 #endif
764
765
766     return img;
767 }