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;
38 typedef struct _renderpoint
52 enum {clip_type, solidfill_type, texturefill_type, gradientfill_type} type;
67 // texture- & gradientfill;
73 typedef struct _renderline
75 TAG*points; //incremented in 128 byte steps
78 typedef struct _bitmap {
86 typedef struct _renderbuf_internal
94 dummyshape_t*dshapes_next;
96 int background_width, background_height;
101 static void renderpoint_write(TAG*tag, renderpoint_t*p)
106 int num = GET32(tag->data);
107 PUT32(tag->data, num+1);
110 swf_SetBits(tag, p->type, 1);
111 swf_SetBits(tag, *(U32*)&p->x, 32);
112 if(p->depth & 0xffff) {
113 swf_SetBits(tag, 1, 1);
114 swf_SetBits(tag, p->depth, 32);
116 swf_SetBits(tag, 0, 1);
117 swf_SetBits(tag, p->depth >> 16, 16);
119 swf_SetBits(tag, *(U32*)&p->shapeline, 32);
120 if(p->type == clip_type) {
121 if(p->clipdepth & 0xffff) {
122 swf_SetBits(tag, 1, 1);
123 swf_SetBits(tag, p->clipdepth, 32);
125 swf_SetBits(tag, 0, 1);
126 swf_SetBits(tag, p->clipdepth >> 16, 16);
130 swf_SetBits(tag, *(U32*)&p->s, 32);
131 /* don't set clipdepth */
134 static renderpoint_t renderpoint_read(TAG*tag, int num)
140 p.type = swf_GetBits(tag, 1);
142 dummy = swf_GetBits(tag, 32);p.x = *(float*)&dummy;
143 flag = swf_GetBits(tag, 1);
145 p.depth = swf_GetBits(tag, 32);
147 p.depth = swf_GetBits(tag, 16) << 16;
149 dummy = swf_GetBits(tag, 32);p.shapeline = *(SHAPELINE**)&dummy;
150 if(p.type == clip_type) {
151 flag = swf_GetBits(tag, 1);
153 p.clipdepth = swf_GetBits(tag, 32);
155 p.clipdepth = swf_GetBits(tag, 16) << 16;
159 dummy = swf_GetBits(tag, 32);p.s = *(dummyshape_t**)&dummy;
166 static int renderpoint_num(TAG*tag)
170 return GET32(tag->data);
173 static renderpoint_t* renderpoint_readall(TAG*tag)
178 swf_SetTagPos(tag, 0);
182 num = swf_GetU32(tag);
183 p = (renderpoint_t*)rfx_alloc(num*sizeof(renderpoint_t));
185 p[t] = renderpoint_read(tag,t);
189 static inline void add_pixel(RENDERBUF*dest, float x, int y, renderpoint_t*p)
191 renderbuf_internal*i = (renderbuf_internal*)dest->internal;
192 if(x >= i->width2 || y >= i->height2 || y<0) return;
195 renderpoint_write(i->lines[y].points, p);
197 renderpoint_write(i->lines[y].points, p);
200 /* set this to 0.777777 or something if the "both fillstyles set while not inside shape"
201 problem appears to often */
204 static void add_line(RENDERBUF*buf, double x1, double y1, double x2, double y2, renderpoint_t*p)
206 renderbuf_internal*i = (renderbuf_internal*)buf->internal;
208 double ny1, ny2, stepx;
210 int l = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
211 printf(" l[%d - %.2f/%.2f -> %.2f/%.2f]", l, x1/20.0, y1/20.0, x2/20.0, y2/20.0);
238 ny1 = (int)(y1) + 1.0 + CUT;
241 ny2 = (int)(y2) - 1.0 + CUT;
248 x1 = x1 + (ny1-y1)*stepx;
249 x2 = x2 + (ny2-y2)*stepx;
258 float xx = (float)(startx + posx);
259 add_pixel(buf, xx ,posy, p);
265 #define PI 3.14159265358979
266 static void add_solidline(RENDERBUF*buf, double x1, double y1, double x2, double y2, int width, renderpoint_t*p)
268 renderbuf_internal*i = (renderbuf_internal*)buf->internal;
281 /* Make sure the line is always at least one pixel wide */
283 /* That's what Macromedia's Player does at least at zoom level >= 1. */
286 /* That's what Macromedia's Player seems to do at zoom level 0. */
287 /* TODO: needs testing */
292 sd = (double)dx*(double)dx+(double)dy*(double)dy;
314 add_line(buf, x1+vx, y1+vy, xx, yy, p);
317 for(t=1;t<segments;t++) {
318 double s = sin(t*PI/segments);
319 double c = cos(t*PI/segments);
320 xx = (x2 + vx*c - vy*s);
321 yy = (y2 + vx*s + vy*c);
322 add_line(buf, lastx, lasty, xx, yy, p);
329 add_line(buf, lastx, lasty, xx, yy, p);
334 add_line(buf, lastx, lasty, xx, yy, p);
337 for(t=1;t<segments;t++) {
338 double s = sin(t*PI/segments);
339 double c = cos(t*PI/segments);
340 xx = (x1 - vx*c + vy*s);
341 yy = (y1 - vx*s - vy*c);
342 add_line(buf, lastx, lasty, xx, yy, p);
346 add_line(buf, lastx, lasty, (x1+vx), (y1+vy), p);
349 static inline void transform_point(MATRIX*m, int x, int y, int*dx, int*dy)
354 d = swf_TurnPoint(p, m);
359 static int compare_renderpoints(const void * _a, const void * _b)
361 renderpoint_t*a = (renderpoint_t*)_a;
362 renderpoint_t*b = (renderpoint_t*)_b;
363 if(a->x < b->x) return -1;
364 if(a->x > b->x) return 1;
368 void swf_Render_Init(RENDERBUF*buf, int posx, int posy, int width, int height, char antialize, int multiply)
370 renderbuf_internal*i;
372 memset(buf, 0, sizeof(RENDERBUF));
373 buf->width = width*multiply;
374 buf->height = height*multiply;
377 buf->internal = (renderbuf_internal*)rfx_calloc(sizeof(renderbuf_internal));
378 i = (renderbuf_internal*)buf->internal;
379 i->antialize = !!antialize;
380 i->multiply = antialize?multiply*2:multiply;
381 i->height2 = antialize?2*buf->height:buf->height;
382 i->width2 = antialize?2*buf->width:buf->width;
383 i->lines = (renderline_t*)rfx_alloc(i->height2*sizeof(renderline_t));
384 for(y=0;y<i->height2;y++) {
385 i->lines[y].points = swf_InsertTag(0, 0);
388 void swf_Render_SetBackground(RENDERBUF*buf, RGBA*img, int width, int height)
390 renderbuf_internal*i = (renderbuf_internal*)buf->internal;
391 RGBA*bck = (RGBA*)rfx_alloc(sizeof(RGBA)*width*height);
392 memcpy(bck, img, sizeof(RGBA)*width*height);
394 i->background_width = width;
395 i->background_height = height;
397 void swf_Render_SetBackgroundColor(RENDERBUF*buf, RGBA color)
399 swf_Render_SetBackground(buf, &color, 1, 1);
401 void swf_Render_AddImage(RENDERBUF*buf, U16 id, RGBA*img, int width, int height)
403 renderbuf_internal*i = (renderbuf_internal*)buf->internal;
405 bitmap_t*bm = rfx_calloc(sizeof(bitmap_t));
411 bm->next = i->bitmaps;
414 void swf_Render_ClearCanvas(RENDERBUF*dest)
416 renderbuf_internal*i = (renderbuf_internal*)dest->internal;
418 for(y=0;y<i->height2;y++) {
419 swf_ClearTag(i->lines[y].points);
422 void swf_Render_Delete(RENDERBUF*dest)
424 renderbuf_internal*i = (renderbuf_internal*)dest->internal;
426 bitmap_t*b = i->bitmaps;
427 dummyshape_t*d = i->dshapes;
430 free(i->background);i->background=0;
433 /* delete line buffers */
434 for(y=0;y<i->height2;y++) {
435 swf_DeleteTag(i->lines[y].points);
436 i->lines[y].points = 0;
440 dummyshape_t*next = d->next;
441 swf_Shape2Free(d->shape);
442 free(d->shape);d->shape=0;
450 bitmap_t*next = b->next;
451 //free(b->data);b->data=0;
456 rfx_free(i->lines); i->lines = 0;
457 rfx_free(dest->internal); dest->internal = 0;
460 static void swf_Render_AddShape(RENDERBUF*dest,dummyshape_t*s)
462 renderbuf_internal*i = (renderbuf_internal*)dest->internal;
466 i->dshapes_next->next = s;
473 static SHAPE2* linestyle2fillstyle(SHAPE2*shape)
475 SHAPE2*s = rfx_calloc(sizeof(SHAPE2));
477 s->numfillstyles = shape->numlinestyles;
478 s->fillstyles = (FILLSTYLE*)rfx_calloc(sizeof(FILLSTYLE)*shape->numlinestyles);
479 s->lines = (SHAPELINE*)rfx_calloc(sizeof(SHAPELINE)*shape->numlinestyles);
480 for(t=0;t<shape->numlinestyles;t++) {
481 s->lines[t].fillstyle0 = t+1;
482 s->fillstyles[t].type = FILL_SOLID;
483 s->fillstyles[t].color = shape->linestyles[t].color;
488 void swf_RenderShape(RENDERBUF*dest, SHAPE2*shape, MATRIX*m, CXFORM*c, U16 _depth,U16 _clipdepth)
490 renderbuf_internal*i = (renderbuf_internal*)dest->internal;
492 SHAPELINE*line = shape->lines;
498 memset(&p, 0, sizeof(renderpoint_t));
499 memset(&lp, 0, sizeof(renderpoint_t));
501 p.type = _clipdepth?clip_type:fill_type;
502 p.depth = _depth << 16;
503 p.clipdepth = _clipdepth << 16;
505 mat.tx -= dest->posx*20;
506 mat.ty -= dest->posy*20;
508 if(shape->numfillstyles) {
509 dummyshape_t*fshape = rfx_calloc(sizeof(dummyshape_t));
511 SHAPE2* s2 = swf_Shape2Clone(shape);
517 /* multiply fillstyles matrices with placement matrix-
518 important for texture and gradient fill */
519 for(t=0;t<s2->numfillstyles;t++) {
521 swf_MatrixJoin(&nm, &s2->fillstyles[t].m, &mat); //TODO: is this the right order?
522 nm.sx *= i->multiply;
523 nm.sy *= i->multiply;
524 nm.r0 *= i->multiply;
525 nm.r1 *= i->multiply;
526 nm.tx *= i->multiply;
527 nm.ty *= i->multiply;
528 s2->fillstyles[t].m = nm;
531 /* add this shape to the global shape list, for deallocing */
532 swf_Render_AddShape(dest, fshape);
535 if(shape->numlinestyles) {
536 dummyshape_t*dshape = rfx_calloc(sizeof(dummyshape_t));
538 lshape = linestyle2fillstyle(shape);
542 lp.depth = (_depth << 16)+1;
544 dshape->shape = lshape;
546 /* add this shape to the global shape list, for deallocing */
547 swf_Render_AddShape(dest, dshape);
553 add_line(dest, -20, 0, -20, i->height2*20, &p);
558 int x1,y1,x2,y2,x3,y3;
562 if(line->type == moveTo) {
563 } else if(line->type == lineTo) {
570 l = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
571 printf("%d - %.2f/%.2f -> %.2f/%.2f ", l, x1/20.0, y1/20.0, x2/20.0, y2/20.0);
574 transform_point(&mat, x, y, &x1, &y1);
575 transform_point(&mat, line->x, line->y, &x3, &y3);
577 if(line->linestyle && ! p.clipdepth) {
578 lp.shapeline = &lshape->lines[line->linestyle-1];
579 add_solidline(dest, x1, y1, x3, y3, shape->linestyles[line->linestyle-1].width, &lp);
582 if(line->fillstyle0 || line->fillstyle1) {
583 assert(shape->numfillstyles);
584 add_line(dest, x1, y1, x3, y3, &p);
587 if(DEBUG&4) printf("\n");
588 } else if(line->type == splineTo) {
589 int c,t,parts,qparts;
592 transform_point(&mat, x, y, &x1, &y1);
593 transform_point(&mat, line->sx, line->sy, &x2, &y2);
594 transform_point(&mat, line->x, line->y, &x3, &y3);
596 c = abs(x3-2*x2+x1) + abs(y3-2*y2+y1);
600 parts = (int)(sqrt(c)/3);
601 if(!parts) parts = 1;
605 printf("spline %.2f/%.2f -(%.2f/%.2f)-> %.2f/%.2f (c=%d, %d parts)",
608 x3/20.0, y3/20.0, c, parts);
611 for(t=1;t<=parts;t++) {
612 double nx = (double)(t*t*x3 + 2*t*(parts-t)*x2 + (parts-t)*(parts-t)*x1)/(double)(parts*parts);
613 double ny = (double)(t*t*y3 + 2*t*(parts-t)*y2 + (parts-t)*(parts-t)*y1)/(double)(parts*parts);
615 if(line->linestyle && ! p.clipdepth) {
616 lp.shapeline = &lshape->lines[line->linestyle-1];
617 add_solidline(dest, xx, yy, nx, ny, shape->linestyles[line->linestyle-1].width, &lp);
620 if(line->fillstyle0 || line->fillstyle1) {
621 assert(shape->numfillstyles);
622 add_line(dest, (int)xx, (int)yy, (int)nx, (int)ny, &p);
637 typedef struct _layer {
649 static RGBA color_red = {255,255,0,0};
650 static RGBA color_white = {255,255,255,255};
652 static void fill_plain(RGBA*line, int x1, int x2, RGBA col)
656 int ainv = 255-col.a;
657 col.r = (col.r*col.a)>>8;
658 col.g = (col.g*col.a)>>8;
659 col.b = (col.b*col.a)>>8;
662 line[x].r = ((line[x].r*ainv)>>8)+col.r;
663 line[x].g = ((line[x].g*ainv)>>8)+col.g;
664 line[x].b = ((line[x].b*ainv)>>8)+col.b;
674 static void fill_bitmap(RGBA*line, int y, int x1, int x2, MATRIX*m, bitmap_t*b, int clip)
677 double m11=m->sx/65536.0, m21=m->r1/65536.0;
678 double m12=m->r0/65536.0, m22=m->sy/65536.0;
679 double rx = m->tx/20.0;
680 double ry = m->ty/20.0;
681 double det = m11*m22 - m12*m21;
682 if(fabs(det) < 0.0005) {
683 /* x direction equals y direction- the image is invisible */
688 if(!b->width || !b->height) {
689 fill_plain(line, x1, x2, color_red);
695 int xx = (int)(( (x - rx) * m22 - (y - ry) * m21)*det);
696 int yy = (int)((- (x - rx) * m12 + (y - ry) * m11)*det);
701 if(xx>=b->width) xx = b->width-1;
703 if(yy>=b->height) yy = b->height-1;
709 col = b->data[yy*b->width+xx];
712 line[x].r = ((line[x].r*ainv)>>8)+col.r;
713 line[x].g = ((line[x].g*ainv)>>8)+col.g;
714 line[x].b = ((line[x].b*ainv)>>8)+col.b;
719 static void fill(RENDERBUF*dest, RGBA*line, int y, int x1, int x2, state_t*clipstate, state_t*fillstate)
721 renderbuf_internal*i = (renderbuf_internal*)dest->internal;
724 layer_t*lc = clipstate->layers;
725 layer_t*lf = fillstate->layers;
728 if(x1>=x2) //zero width? nothing to do.
733 if(lc && (!lf || lc->p->depth < lf->p->depth)) {
736 } else if(lf && (!lc || lf->p->depth < lc->p->depth)) {
739 } else if(lf && lc && lf->p->depth == lc->p->depth) {
740 /* A clipshape and a fillshape at the same depth. Yuck.
741 Bug in the SWF file */
742 fprintf(stderr, "Error: Multiple use of depth %d in SWF\n", lf->p->depth);
746 fprintf(stderr, "Internal error: %08x %08x\n", lc, lf);
747 if(lc) fprintf(stderr, " lc->depth = %08x\n", lc->p->depth);
748 if(lf) fprintf(stderr, " lf->depth = %08x\n", lf->p->depth);
751 if(l->p->depth < clipdepth) {
752 if(DEBUG&2) printf("(clipped)");
755 if(l->fillid < 0 /*clip*/) {
756 if(DEBUG&2) printf("(add clip %d)", l->clipdepth);
757 if(l->clipdepth > clipdepth)
758 clipdepth = l->clipdepth;
759 } else if(l->fillid == 0) {
760 /* not filled. TODO: we should never add those in the first place */
762 printf("(not filled)");
763 } else if(l->fillid > l->p->s->shape->numfillstyles) {
764 fprintf(stderr, "Fill style out of bounds (%d>%d)", l->fillid, l->p->s->shape->numlinestyles);
768 printf("(%d -> %d style %d)", x1, x2, l->fillid);
770 f = &l->p->s->shape->fillstyles[l->fillid-1];
772 if(f->type == FILL_SOLID) {
773 /* plain color fill */
774 fill_plain(line, x1, x2, f->color);
775 } else if(f->type == FILL_TILED || f->type == FILL_CLIPPED) {
776 /* TODO: optimize (do this in add_pixel()?) */
777 bitmap_t* b = i->bitmaps;
778 while(b && b->id != f->id_bitmap) {
782 fprintf(stderr, "Shape references unknown bitmap %d\n", f->id_bitmap);
783 fill_plain(line, x1, x2, color_red);
785 //done in swf_RenderShape now
787 //m.tx -= dest->posx*20;
788 //m.ty -= dest->posy*20;
789 //m.sx *= i->multiply;
790 //m.sy *= i->multiply;
791 //m.r0 *= i->multiply;
792 //m.r1 *= i->multiply;
793 //m.tx *= i->multiply;
794 //m.ty *= i->multiply;
795 fill_bitmap(line, y, x1, x2, &f->m, b, FILL_CLIPPED?1:0);
802 static void search_layer(state_t*state, int depth, layer_t**before, layer_t**self, layer_t**after)
804 layer_t*last=0,*l = state->layers;
805 while(l && l->p->depth < depth) {
810 if(l && l->p->depth == depth)
815 static void delete_layer(state_t*state, layer_t*todel)
817 layer_t*before=todel->prev;
818 layer_t*next = todel->next;
821 state->layers = next;
827 before->next->prev = before;
830 static void add_layer(state_t*state, layer_t*before, layer_t*toadd)
833 toadd->next = state->layers;
837 toadd->next = before->next;
838 toadd->prev = before;
839 before->next = toadd;
842 toadd->next->prev = toadd;
844 static void free_layers(state_t* state)
846 layer_t*l = state->layers;
848 layer_t*next = l->next;
854 static void change_state(int y, state_t* state, renderpoint_t*p)
856 layer_t*before=0, *self=0, *after=0;
859 printf("[%s(%d,%d)/%d/%d-%d]", p->type==clip_type?"C":"F", p->x, y, p->depth, p->shapeline->fillstyle0, p->shapeline->fillstyle1);
862 search_layer(state, p->depth, &before, &self, &after);
866 if(self->fillid<0 || !p->shapeline->fillstyle0 || !p->shapeline->fillstyle1) {
867 /* filling/clipping ends */
868 if(DEBUG&2) printf("<D>");
870 delete_layer(state, self);
872 /*both fill0 and fill1 are set- exchange the two, updating the layer */
873 if(self->fillid == p->shapeline->fillstyle0) {
874 self->fillid = p->shapeline->fillstyle1;
877 if(DEBUG&2) printf("<X>");
878 } else if(self->fillid == p->shapeline->fillstyle1) {
879 self->fillid = p->shapeline->fillstyle0;
882 if(DEBUG&2) printf("<X>");
884 /* buggy shape. keep everything as-is. */
885 if(DEBUG&2) printf("<!>");
886 //fprintf(stderr, "<line %d: bad swap>\n", y);
892 if(p->shapeline && p->shapeline->fillstyle0 && p->shapeline->fillstyle1) {
893 /* this is a hack- a better way would be to make sure that
894 we always get (0,32), (32, 33), (33, 0) in the right order if
895 they happen to fall on the same pixel.
896 (not: (0,32), (33, 0), (32, 33))
898 fprintf(stderr, "<line %d: both fillstyles set while not inside shape>\n", y);
902 n = rfx_calloc(sizeof(layer_t));
904 if(DEBUG&2) printf("<+>");
906 if(p->type == clip_type) {
909 n->clipdepth = p->clipdepth;
912 n->fillid = p->shapeline->fillstyle0 ? p->shapeline->fillstyle0 : p->shapeline->fillstyle1;
917 add_layer(state, before, n);
921 RGBA* swf_Render(RENDERBUF*dest)
923 renderbuf_internal*i = (renderbuf_internal*)dest->internal;
924 RGBA* img = (RGBA*)rfx_alloc(sizeof(RGBA)*dest->width*dest->height);
927 RGBA * line1 = rfx_alloc(sizeof(RGBA)*i->width2);
928 RGBA * line2 = rfx_alloc(sizeof(RGBA)*i->width2);
930 printf("%d\n", sizeof(renderpoint_t));
932 for(y=0;y<i->height2;y++) {
933 TAG*tag = i->lines[y].points;
935 int num = renderpoint_num(tag);
936 renderpoint_t*points = renderpoint_readall(tag);
940 memset(&clipstate, 0, sizeof(state_t));
941 memset(&fillstate, 0, sizeof(state_t));
943 if((y&1) && i->antialize)
947 memset(line, 0, sizeof(RGBA)*i->width2);
950 int xstep=i->background_width*65536/i->width2;
951 RGBA*src = &i->background[(i->background_height*y/i->height2)*i->background_width];
952 for(x=0,xx=0;x<i->width2;x++,xx+=xstep) {
953 line[x] = src[xx>>16];
956 memory += tag->memsize;
957 qsort(points, num, sizeof(renderpoint_t), compare_renderpoints);
959 renderpoint_t*p = &points[n];
960 renderpoint_t*next= n<num-1?&points[n+1]:0;
962 int endx = next?next->x:i->width2;
968 if(p->type == clip_type)
969 change_state(y, &clipstate, p);
971 change_state(y, &fillstate, p);
973 fill(dest, line, y, startx, endx, &clipstate, &fillstate);
974 if(endx == i->width2)
977 free_layers(&clipstate);
978 free_layers(&fillstate);
979 if(DEBUG&2) printf("\n");
982 memcpy(&img[y*dest->width], line, sizeof(RGBA)*dest->width);
986 RGBA* p = &img[(y/2)*dest->width];
987 for(x=0;x<dest->width;x++) {
988 RGBA*p1 = &line1[x*2];
989 RGBA*p2 = &line1[x*2+1];
990 RGBA*p3 = &line2[x*2];
991 RGBA*p4 = &line2[x*2+1];
992 p[x].r = (p1->r + p2->r + p3->r + p4->r)/4;
993 p[x].g = (p1->g + p2->g + p3->g + p4->g)/4;
994 p[x].b = (p1->b + p2->b + p3->b + p4->b)/4;
995 p[x].a = (p1->a + p2->a + p3->a + p4->a)/4;
1004 if(DEBUG) printf("\nMemory used: %d\n", memory);
1006 if(DEBUG) printf("Statistics:\n");
1007 if(DEBUG) printf("Average layer depth: %f\n", (double)layers/layernum);