3 functions for rendering swf content
5 Extension module for the rfxswf library.
6 Part of the swftools package.
8 Copyright (c) 2004 Mederra Oy <http://www.mederra.fi>
9 Copyright (c) 2004 Matthias Kramm
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.
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.
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 */
27 typedef struct _dummyshape
31 struct _dummyshape*next;
34 typedef struct _renderpoint
36 enum {clip_type, fill_type} type;
37 float fx; //for sorting
47 enum {clip_type, solidfill_type, texturefill_type, gradientfill_type} type;
62 // texture- & gradientfill;
68 typedef struct _renderline
70 TAG*points; //incremented in 128 byte steps
73 typedef struct _bitmap {
81 typedef struct _renderbuf_internal
89 dummyshape_t*dshapes_next;
91 int background_width, background_height;
96 static inline void add_pixel(RENDERBUF*dest, float x, int y, renderpoint_t*p)
98 renderbuf_internal*i = (renderbuf_internal*)dest->internal;
99 if(x >= i->width2 || y >= i->height2 || y<0) return;
102 swf_SetBlock(i->lines[y].points, (U8*)p, sizeof(renderpoint_t));
105 /* set this to 0.777777 or something if the "both fillstyles set while not inside shape"
106 problem appears to often */
109 static void add_line(RENDERBUF*buf, double x1, double y1, double x2, double y2, renderpoint_t*p)
111 renderbuf_internal*i = (renderbuf_internal*)buf->internal;
113 double ny1, ny2, stepx;
115 int l = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
116 printf(" l[%d - %.2f/%.2f -> %.2f/%.2f]", l, x1/20.0, y1/20.0, x2/20.0, y2/20.0);
143 ny1 = (int)(y1) + 1.0 + CUT;
146 ny2 = (int)(y2) - 1.0 + CUT;
153 x1 = x1 + (ny1-y1)*stepx;
154 x2 = x2 + (ny2-y2)*stepx;
163 float xx = (float)(startx + posx);
164 add_pixel(buf, xx ,posy, p);
170 #define PI 3.14159265358979
171 static void add_solidline(RENDERBUF*buf, double x1, double y1, double x2, double y2, int width, renderpoint_t*p)
173 renderbuf_internal*i = (renderbuf_internal*)buf->internal;
186 /* Make sure the line is always at least one pixel wide */
188 /* That's what Macromedia's Player does at least at zoom level >= 1. */
191 /* That's what Macromedia's Player seems to do at zoom level 0. */
192 /* TODO: needs testing */
197 sd = (double)dx*(double)dx+(double)dy*(double)dy;
219 add_line(buf, x1+vx, y1+vy, xx, yy, p);
222 for(t=1;t<segments;t++) {
223 double s = sin(t*PI/segments);
224 double c = cos(t*PI/segments);
225 xx = (x2 + vx*c - vy*s);
226 yy = (y2 + vx*s + vy*c);
227 add_line(buf, lastx, lasty, xx, yy, p);
234 add_line(buf, lastx, lasty, xx, yy, p);
239 add_line(buf, lastx, lasty, xx, yy, p);
242 for(t=1;t<segments;t++) {
243 double s = sin(t*PI/segments);
244 double c = cos(t*PI/segments);
245 xx = (x1 - vx*c + vy*s);
246 yy = (y1 - vx*s - vy*c);
247 add_line(buf, lastx, lasty, xx, yy, p);
251 add_line(buf, lastx, lasty, (x1+vx), (y1+vy), p);
254 static inline void transform_point(MATRIX*m, int x, int y, int*dx, int*dy)
259 d = swf_TurnPoint(p, m);
264 static int compare_renderpoints(const void * _a, const void * _b)
266 renderpoint_t*a = (renderpoint_t*)_a;
267 renderpoint_t*b = (renderpoint_t*)_b;
268 if(a->fx < b->fx) return -1;
269 if(a->fx > b->fx) return 1;
273 void swf_Render_Init(RENDERBUF*buf, int posx, int posy, int width, int height, char antialize, int multiply)
275 renderbuf_internal*i;
277 memset(buf, 0, sizeof(RENDERBUF));
278 buf->width = width*multiply;
279 buf->height = height*multiply;
282 buf->internal = (renderbuf_internal*)rfx_calloc(sizeof(renderbuf_internal));
283 i = (renderbuf_internal*)buf->internal;
284 i->antialize = !!antialize;
285 i->multiply = antialize?multiply*2:multiply;
286 i->height2 = antialize?2*buf->height:buf->height;
287 i->width2 = antialize?2*buf->width:buf->width;
288 i->lines = (renderline_t*)rfx_alloc(i->height2*sizeof(renderline_t));
289 for(y=0;y<i->height2;y++) {
290 i->lines[y].points = swf_InsertTag(0, 0);
293 void swf_Render_SetBackground(RENDERBUF*buf, RGBA*img, int width, int height)
295 renderbuf_internal*i = (renderbuf_internal*)buf->internal;
296 RGBA*bck = (RGBA*)rfx_alloc(sizeof(RGBA)*width*height);
297 memcpy(bck, img, sizeof(RGBA)*width*height);
299 i->background_width = width;
300 i->background_height = height;
302 void swf_Render_SetBackgroundColor(RENDERBUF*buf, RGBA color)
304 swf_Render_SetBackground(buf, &color, 1, 1);
306 void swf_Render_AddImage(RENDERBUF*buf, U16 id, RGBA*img, int width, int height)
308 renderbuf_internal*i = (renderbuf_internal*)buf->internal;
310 bitmap_t*bm = rfx_calloc(sizeof(bitmap_t));
316 bm->next = i->bitmaps;
319 void swf_Render_ClearCanvas(RENDERBUF*dest)
321 renderbuf_internal*i = (renderbuf_internal*)dest->internal;
323 for(y=0;y<i->height2;y++) {
324 swf_ClearTag(i->lines[y].points);
327 void swf_Render_Delete(RENDERBUF*dest)
329 renderbuf_internal*i = (renderbuf_internal*)dest->internal;
331 bitmap_t*b = i->bitmaps;
332 dummyshape_t*d = i->dshapes;
335 free(i->background);i->background=0;
338 /* delete line buffers */
339 for(y=0;y<i->height2;y++) {
340 swf_DeleteTag(i->lines[y].points);
341 i->lines[y].points = 0;
345 dummyshape_t*next = d->next;
346 swf_Shape2Free(d->shape);
347 free(d->shape);d->shape=0;
355 bitmap_t*next = b->next;
356 //free(b->data);b->data=0;
361 rfx_free(i->lines); i->lines = 0;
362 rfx_free(dest->internal); dest->internal = 0;
365 static void swf_Render_AddShape(RENDERBUF*dest,dummyshape_t*s)
367 renderbuf_internal*i = (renderbuf_internal*)dest->internal;
371 i->dshapes_next->next = s;
378 static SHAPE2* linestyle2fillstyle(SHAPE2*shape)
380 SHAPE2*s = rfx_calloc(sizeof(SHAPE2));
382 s->numfillstyles = shape->numlinestyles;
383 s->fillstyles = (FILLSTYLE*)rfx_calloc(sizeof(FILLSTYLE)*shape->numlinestyles);
384 s->lines = (SHAPELINE*)rfx_calloc(sizeof(SHAPELINE)*shape->numlinestyles);
385 for(t=0;t<shape->numlinestyles;t++) {
386 s->lines[t].fillstyle0 = t+1;
387 s->fillstyles[t].type = FILL_SOLID;
388 s->fillstyles[t].color = shape->linestyles[t].color;
393 void swf_RenderShape(RENDERBUF*dest, SHAPE2*shape, MATRIX*m, CXFORM*c, U16 _depth,U16 _clipdepth)
395 renderbuf_internal*i = (renderbuf_internal*)dest->internal;
397 SHAPELINE*line = shape->lines;
403 memset(&p, 0, sizeof(renderpoint_t));
404 memset(&lp, 0, sizeof(renderpoint_t));
406 p.type = _clipdepth?clip_type:fill_type;
407 p.depth = _depth << 16;
408 p.clipdepth = _clipdepth << 16;
410 mat.tx -= dest->posx*20;
411 mat.ty -= dest->posy*20;
413 if(shape->numfillstyles) {
414 dummyshape_t*fshape = rfx_calloc(sizeof(dummyshape_t));
416 SHAPE2* s2 = swf_Shape2Clone(shape);
422 /* multiply fillstyles matrices with placement matrix-
423 important for texture and gradient fill */
424 for(t=0;t<s2->numfillstyles;t++) {
426 swf_MatrixJoin(&nm, &s2->fillstyles[t].m, &mat); //TODO: is this the right order?
427 nm.sx *= i->multiply;
428 nm.sy *= i->multiply;
429 nm.r0 *= i->multiply;
430 nm.r1 *= i->multiply;
431 nm.tx *= i->multiply;
432 nm.ty *= i->multiply;
433 s2->fillstyles[t].m = nm;
436 /* add this shape to the global shape list, for deallocing */
437 swf_Render_AddShape(dest, fshape);
440 if(shape->numlinestyles) {
441 dummyshape_t*dshape = rfx_calloc(sizeof(dummyshape_t));
443 lshape = linestyle2fillstyle(shape);
447 lp.depth = (_depth << 16)+1;
449 dshape->shape = lshape;
451 /* add this shape to the global shape list, for deallocing */
452 swf_Render_AddShape(dest, dshape);
458 add_line(dest, -20, 0, -20, i->height2*20, &p);
463 int x1,y1,x2,y2,x3,y3;
467 if(line->type == moveTo) {
468 } else if(line->type == lineTo) {
475 l = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
476 printf("%d - %.2f/%.2f -> %.2f/%.2f ", l, x1/20.0, y1/20.0, x2/20.0, y2/20.0);
479 transform_point(&mat, x, y, &x1, &y1);
480 transform_point(&mat, line->x, line->y, &x3, &y3);
482 if(line->linestyle && ! p.clipdepth) {
483 lp.shapeline = &lshape->lines[line->linestyle-1];
484 add_solidline(dest, x1, y1, x3, y3, shape->linestyles[line->linestyle-1].width, &lp);
487 if(line->fillstyle0 || line->fillstyle1) {
488 assert(shape->numfillstyles);
489 add_line(dest, x1, y1, x3, y3, &p);
492 if(DEBUG&4) printf("\n");
493 } else if(line->type == splineTo) {
494 int c,t,parts,qparts;
497 transform_point(&mat, x, y, &x1, &y1);
498 transform_point(&mat, line->sx, line->sy, &x2, &y2);
499 transform_point(&mat, line->x, line->y, &x3, &y3);
501 c = abs(x3-2*x2+x1) + abs(y3-2*y2+y1);
505 parts = (int)(sqrt(c)/3);
506 if(!parts) parts = 1;
510 printf("spline %.2f/%.2f -(%.2f/%.2f)-> %.2f/%.2f (c=%d, %d parts)",
513 x3/20.0, y3/20.0, c, parts);
516 for(t=1;t<=parts;t++) {
517 double nx = (double)(t*t*x3 + 2*t*(parts-t)*x2 + (parts-t)*(parts-t)*x1)/(double)(parts*parts);
518 double ny = (double)(t*t*y3 + 2*t*(parts-t)*y2 + (parts-t)*(parts-t)*y1)/(double)(parts*parts);
520 if(line->linestyle && ! p.clipdepth) {
521 lp.shapeline = &lshape->lines[line->linestyle-1];
522 add_solidline(dest, xx, yy, nx, ny, shape->linestyles[line->linestyle-1].width, &lp);
525 if(line->fillstyle0 || line->fillstyle1) {
526 assert(shape->numfillstyles);
527 add_line(dest, (int)xx, (int)yy, (int)nx, (int)ny, &p);
542 typedef struct _layer {
554 static RGBA color_red = {255,255,0,0};
555 static RGBA color_white = {255,255,255,255};
557 static void fill_plain(RGBA*line, int x1, int x2, RGBA col)
561 int ainv = 255-col.a;
562 col.r = (col.r*col.a)>>8;
563 col.g = (col.g*col.a)>>8;
564 col.b = (col.b*col.a)>>8;
567 line[x].r = ((line[x].r*ainv)>>8)+col.r;
568 line[x].g = ((line[x].g*ainv)>>8)+col.g;
569 line[x].b = ((line[x].b*ainv)>>8)+col.b;
579 static void fill_bitmap(RGBA*line, int y, int x1, int x2, MATRIX*m, bitmap_t*b, int clip)
582 double m11=m->sx/65536.0, m21=m->r1/65536.0;
583 double m12=m->r0/65536.0, m22=m->sy/65536.0;
584 double rx = m->tx/20.0;
585 double ry = m->ty/20.0;
586 double det = m11*m22 - m12*m21;
587 if(fabs(det) < 0.0005) {
588 /* x direction equals y direction- the image is invisible */
593 if(!b->width || !b->height) {
594 fill_plain(line, x1, x2, color_red);
600 int xx = (int)(( (x - rx) * m22 - (y - ry) * m21)*det);
601 int yy = (int)((- (x - rx) * m12 + (y - ry) * m11)*det);
606 if(xx>=b->width) xx = b->width-1;
608 if(yy>=b->height) yy = b->height-1;
614 col = b->data[yy*b->width+xx];
617 line[x].r = ((line[x].r*ainv)>>8)+col.r;
618 line[x].g = ((line[x].g*ainv)>>8)+col.g;
619 line[x].b = ((line[x].b*ainv)>>8)+col.b;
624 static void fill(RENDERBUF*dest, RGBA*line, int y, int x1, int x2, state_t*state)
626 renderbuf_internal*i = (renderbuf_internal*)dest->internal;
629 layer_t*l = state->layers;
631 if(x1>=x2) //zero width? nothing to do.
636 if(l->p->depth < clipdepth) {
637 if(DEBUG&2) printf("(clipped)");
641 if(l->fillid < 0 /*clip*/) {
642 if(DEBUG&2) printf("(add clip %d)", l->clipdepth);
643 if(l->clipdepth > clipdepth)
644 clipdepth = l->clipdepth;
645 } else if(l->fillid == 0) {
646 /* not filled. TODO: we should never add those in the first place */
648 printf("(not filled)");
649 } else if(l->fillid > l->p->s->shape->numfillstyles) {
650 fprintf(stderr, "Fill style out of bounds (%d>%d)", l->fillid, l->p->s->shape->numlinestyles);
654 printf("(%d -> %d style %d)", x1, x2, l->fillid);
656 f = &l->p->s->shape->fillstyles[l->fillid-1];
658 if(f->type == FILL_SOLID) {
659 /* plain color fill */
660 fill_plain(line, x1, x2, f->color);
661 } else if(f->type == FILL_TILED || f->type == FILL_CLIPPED) {
662 /* TODO: optimize (do this in add_pixel()?) */
663 bitmap_t* b = i->bitmaps;
664 while(b && b->id != f->id_bitmap) {
668 fprintf(stderr, "Shape references unknown bitmap %d\n", f->id_bitmap);
669 fill_plain(line, x1, x2, color_red);
671 //done in swf_RenderShape now
673 //m.tx -= dest->posx*20;
674 //m.ty -= dest->posy*20;
675 //m.sx *= i->multiply;
676 //m.sy *= i->multiply;
677 //m.r0 *= i->multiply;
678 //m.r1 *= i->multiply;
679 //m.tx *= i->multiply;
680 //m.ty *= i->multiply;
681 fill_bitmap(line, y, x1, x2, &f->m, b, FILL_CLIPPED?1:0);
689 static void search_layer(state_t*state, int depth, layer_t**before, layer_t**self, layer_t**after)
691 layer_t*last=0,*l = state->layers;
692 while(l && l->p->depth < depth) {
697 if(l && l->p->depth == depth)
702 static void delete_layer(state_t*state, layer_t*todel)
704 layer_t*before=todel->prev;
705 layer_t*next = todel->next;
708 state->layers = next;
714 before->next->prev = before;
717 static void add_layer(state_t*state, layer_t*before, layer_t*toadd)
720 toadd->next = state->layers;
724 toadd->next = before->next;
725 toadd->prev = before;
726 before->next = toadd;
729 toadd->next->prev = toadd;
731 static void free_layers(state_t* state)
733 layer_t*l = state->layers;
735 layer_t*next = l->next;
741 static void change_state(int y, state_t* state, renderpoint_t*p)
743 layer_t*before=0, *self=0, *after=0;
746 printf("[%s(%d,%d)/%d/%d-%d]", p->type==clip_type?"C":"F", p->x, y, p->depth, p->shapeline->fillstyle0, p->shapeline->fillstyle1);
749 search_layer(state, p->depth, &before, &self, &after);
753 if(self->fillid<0 || !p->shapeline->fillstyle0 || !p->shapeline->fillstyle1) {
754 /* filling/clipping ends */
755 if(DEBUG&2) printf("<D>");
757 delete_layer(state, self);
759 /*both fill0 and fill1 are set- exchange the two, updating the layer */
760 if(self->fillid == p->shapeline->fillstyle0) {
761 self->fillid = p->shapeline->fillstyle1;
764 if(DEBUG&2) printf("<X>");
765 } else if(self->fillid == p->shapeline->fillstyle1) {
766 self->fillid = p->shapeline->fillstyle0;
769 if(DEBUG&2) printf("<X>");
771 /* buggy shape. keep everything as-is. */
772 if(DEBUG&2) printf("<!>");
773 //fprintf(stderr, "<line %d: bad swap>\n", y);
779 if(p->shapeline && p->shapeline->fillstyle0 && p->shapeline->fillstyle1) {
780 /* this is a hack- a better way would be to make sure that
781 we always get (0,32), (32, 33), (33, 0) in the right order if
782 they happen to fall on the same pixel.
783 (not: (0,32), (33, 0), (32, 33))
785 fprintf(stderr, "<line %d: both fillstyles set while not inside shape>\n", y);
789 n = rfx_calloc(sizeof(layer_t));
791 if(DEBUG&2) printf("<+>");
793 if(p->type == clip_type) {
796 n->clipdepth = p->clipdepth;
799 n->fillid = p->shapeline->fillstyle0 ? p->shapeline->fillstyle0 : p->shapeline->fillstyle1;
804 add_layer(state, before, n);
808 RGBA* swf_Render(RENDERBUF*dest)
810 renderbuf_internal*i = (renderbuf_internal*)dest->internal;
811 RGBA* img = (RGBA*)rfx_alloc(sizeof(RGBA)*dest->width*dest->height);
814 RGBA * line1 = rfx_alloc(sizeof(RGBA)*i->width2);
815 RGBA * line2 = rfx_alloc(sizeof(RGBA)*i->width2);
817 for(y=0;y<i->height2;y++) {
818 TAG*tag = i->lines[y].points;
820 int size = sizeof(renderpoint_t);
821 int num = tag->len / size;
824 memset(&state, 0, sizeof(state_t));
826 if((y&1) && i->antialize)
831 memset(line, 0, sizeof(RGBA)*i->width2);
834 int xstep=i->background_width*65536/i->width2;
835 RGBA*src = &i->background[(i->background_height*y/i->height2)*i->background_width];
836 for(x=0,xx=0;x<i->width2;x++,xx+=xstep) {
837 line[x] = src[xx>>16];
840 memory += tag->memsize;
841 qsort(tag->data, num, size, compare_renderpoints);
843 renderpoint_t*p = (renderpoint_t*)&tag->data[size*n];
844 renderpoint_t*next= n<num-1?(renderpoint_t*)&tag->data[size*(n+1)]:0;
846 int endx = next?next->x:i->width2;
852 change_state(y, &state, p);
854 fill(dest, line, y, startx, endx, &state);
855 if(endx == i->width2)
859 if(DEBUG&2) printf("\n");
862 memcpy(&img[y*dest->width], line, sizeof(RGBA)*dest->width);
866 RGBA* p = &img[(y/2)*dest->width];
867 for(x=0;x<dest->width;x++) {
868 RGBA*p1 = &line1[x*2];
869 RGBA*p2 = &line1[x*2+1];
870 RGBA*p3 = &line2[x*2];
871 RGBA*p4 = &line2[x*2+1];
872 p[x].r = (p1->r + p2->r + p3->r + p4->r)/4;
873 p[x].g = (p1->g + p2->g + p3->g + p4->g)/4;
874 p[x].b = (p1->b + p2->b + p3->b + p4->b)/4;
875 p[x].a = (p1->a + p2->a + p3->a + p4->a)/4;
883 if(DEBUG) printf("\nMemory used: %d\n", memory);
885 if(DEBUG) printf("Statistics:\n");
886 if(DEBUG) printf("Average layer depth: %f\n", (double)layers/layernum);