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