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