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;
37 typedef struct _renderpoint
51 enum {clip_type, solidfill_type, texturefill_type, gradientfill_type} type;
66 // texture- & gradientfill;
72 typedef struct _renderline
74 TAG*points; //incremented in 128 byte steps
77 typedef struct _bitmap {
85 typedef struct _renderbuf_internal
93 dummyshape_t*dshapes_next;
95 int background_width, background_height;
100 static void renderpoint_write(TAG*tag, renderpoint_t*p)
105 PUT32(tag->data, GET32(tag->data)+1);
108 swf_SetBlock(tag, (U8*)p, sizeof(renderpoint_t));
110 static renderpoint_t renderpoint_read(TAG*tag)
114 swf_GetBlock(tag, (U8*)&p, sizeof(renderpoint_t));
118 static int renderpoint_num(TAG*tag)
122 return GET32(tag->data);
125 static renderpoint_t* renderpoint_readall(TAG*tag)
130 swf_SetTagPos(tag, 0);
131 num = swf_GetU32(tag);
132 p = (renderpoint_t*)rfx_alloc(num*sizeof(renderpoint_t));
134 p[t] = renderpoint_read(tag);
138 static inline void add_pixel(RENDERBUF*dest, float x, int y, renderpoint_t*p)
140 renderbuf_internal*i = (renderbuf_internal*)dest->internal;
141 if(x >= i->width2 || y >= i->height2 || y<0) return;
143 renderpoint_write(i->lines[y].points, p);
146 /* set this to 0.777777 or something if the "both fillstyles set while not inside shape"
147 problem appears to often */
150 static void add_line(RENDERBUF*buf, double x1, double y1, double x2, double y2, renderpoint_t*p)
152 renderbuf_internal*i = (renderbuf_internal*)buf->internal;
154 double ny1, ny2, stepx;
156 int l = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
157 printf(" l[%d - %.2f/%.2f -> %.2f/%.2f]", l, x1/20.0, y1/20.0, x2/20.0, y2/20.0);
184 ny1 = (int)(y1) + 1.0 + CUT;
187 ny2 = (int)(y2) - 1.0 + CUT;
194 x1 = x1 + (ny1-y1)*stepx;
195 x2 = x2 + (ny2-y2)*stepx;
204 float xx = (float)(startx + posx);
205 add_pixel(buf, xx ,posy, p);
211 #define PI 3.14159265358979
212 static void add_solidline(RENDERBUF*buf, double x1, double y1, double x2, double y2, int width, renderpoint_t*p)
214 renderbuf_internal*i = (renderbuf_internal*)buf->internal;
227 /* Make sure the line is always at least one pixel wide */
229 /* That's what Macromedia's Player does at least at zoom level >= 1. */
232 /* That's what Macromedia's Player seems to do at zoom level 0. */
233 /* TODO: needs testing */
238 sd = (double)dx*(double)dx+(double)dy*(double)dy;
260 add_line(buf, x1+vx, y1+vy, xx, yy, p);
263 for(t=1;t<segments;t++) {
264 double s = sin(t*PI/segments);
265 double c = cos(t*PI/segments);
266 xx = (x2 + vx*c - vy*s);
267 yy = (y2 + vx*s + vy*c);
268 add_line(buf, lastx, lasty, xx, yy, p);
275 add_line(buf, lastx, lasty, xx, yy, p);
280 add_line(buf, lastx, lasty, xx, yy, p);
283 for(t=1;t<segments;t++) {
284 double s = sin(t*PI/segments);
285 double c = cos(t*PI/segments);
286 xx = (x1 - vx*c + vy*s);
287 yy = (y1 - vx*s - vy*c);
288 add_line(buf, lastx, lasty, xx, yy, p);
292 add_line(buf, lastx, lasty, (x1+vx), (y1+vy), p);
295 static inline void transform_point(MATRIX*m, int x, int y, int*dx, int*dy)
300 d = swf_TurnPoint(p, m);
305 static int compare_renderpoints(const void * _a, const void * _b)
307 renderpoint_t*a = (renderpoint_t*)_a;
308 renderpoint_t*b = (renderpoint_t*)_b;
309 if(a->x < b->x) return -1;
310 if(a->x > b->x) return 1;
314 void swf_Render_Init(RENDERBUF*buf, int posx, int posy, int width, int height, char antialize, int multiply)
316 renderbuf_internal*i;
318 memset(buf, 0, sizeof(RENDERBUF));
319 buf->width = width*multiply;
320 buf->height = height*multiply;
323 buf->internal = (renderbuf_internal*)rfx_calloc(sizeof(renderbuf_internal));
324 i = (renderbuf_internal*)buf->internal;
325 i->antialize = !!antialize;
326 i->multiply = antialize?multiply*2:multiply;
327 i->height2 = antialize?2*buf->height:buf->height;
328 i->width2 = antialize?2*buf->width:buf->width;
329 i->lines = (renderline_t*)rfx_alloc(i->height2*sizeof(renderline_t));
330 for(y=0;y<i->height2;y++) {
331 i->lines[y].points = swf_InsertTag(0, 0);
334 void swf_Render_SetBackground(RENDERBUF*buf, RGBA*img, int width, int height)
336 renderbuf_internal*i = (renderbuf_internal*)buf->internal;
337 RGBA*bck = (RGBA*)rfx_alloc(sizeof(RGBA)*width*height);
338 memcpy(bck, img, sizeof(RGBA)*width*height);
340 i->background_width = width;
341 i->background_height = height;
343 void swf_Render_SetBackgroundColor(RENDERBUF*buf, RGBA color)
345 swf_Render_SetBackground(buf, &color, 1, 1);
347 void swf_Render_AddImage(RENDERBUF*buf, U16 id, RGBA*img, int width, int height)
349 renderbuf_internal*i = (renderbuf_internal*)buf->internal;
351 bitmap_t*bm = rfx_calloc(sizeof(bitmap_t));
357 bm->next = i->bitmaps;
360 void swf_Render_ClearCanvas(RENDERBUF*dest)
362 renderbuf_internal*i = (renderbuf_internal*)dest->internal;
364 for(y=0;y<i->height2;y++) {
365 swf_ClearTag(i->lines[y].points);
368 void swf_Render_Delete(RENDERBUF*dest)
370 renderbuf_internal*i = (renderbuf_internal*)dest->internal;
372 bitmap_t*b = i->bitmaps;
373 dummyshape_t*d = i->dshapes;
376 free(i->background);i->background=0;
379 /* delete line buffers */
380 for(y=0;y<i->height2;y++) {
381 swf_DeleteTag(i->lines[y].points);
382 i->lines[y].points = 0;
386 dummyshape_t*next = d->next;
387 swf_Shape2Free(d->shape);
388 free(d->shape);d->shape=0;
396 bitmap_t*next = b->next;
397 //free(b->data);b->data=0;
402 rfx_free(i->lines); i->lines = 0;
403 rfx_free(dest->internal); dest->internal = 0;
406 static void swf_Render_AddShape(RENDERBUF*dest,dummyshape_t*s)
408 renderbuf_internal*i = (renderbuf_internal*)dest->internal;
412 i->dshapes_next->next = s;
419 static SHAPE2* linestyle2fillstyle(SHAPE2*shape)
421 SHAPE2*s = rfx_calloc(sizeof(SHAPE2));
423 s->numfillstyles = shape->numlinestyles;
424 s->fillstyles = (FILLSTYLE*)rfx_calloc(sizeof(FILLSTYLE)*shape->numlinestyles);
425 s->lines = (SHAPELINE*)rfx_calloc(sizeof(SHAPELINE)*shape->numlinestyles);
426 for(t=0;t<shape->numlinestyles;t++) {
427 s->lines[t].fillstyle0 = t+1;
428 s->fillstyles[t].type = FILL_SOLID;
429 s->fillstyles[t].color = shape->linestyles[t].color;
434 void swf_RenderShape(RENDERBUF*dest, SHAPE2*shape, MATRIX*m, CXFORM*c, U16 _depth,U16 _clipdepth)
436 renderbuf_internal*i = (renderbuf_internal*)dest->internal;
438 SHAPELINE*line = shape->lines;
444 memset(&p, 0, sizeof(renderpoint_t));
445 memset(&lp, 0, sizeof(renderpoint_t));
447 p.type = _clipdepth?clip_type:fill_type;
448 p.depth = _depth << 16;
449 p.clipdepth = _clipdepth << 16;
451 mat.tx -= dest->posx*20;
452 mat.ty -= dest->posy*20;
454 if(shape->numfillstyles) {
455 dummyshape_t*fshape = rfx_calloc(sizeof(dummyshape_t));
457 SHAPE2* s2 = swf_Shape2Clone(shape);
463 /* multiply fillstyles matrices with placement matrix-
464 important for texture and gradient fill */
465 for(t=0;t<s2->numfillstyles;t++) {
467 swf_MatrixJoin(&nm, &s2->fillstyles[t].m, &mat); //TODO: is this the right order?
468 nm.sx *= i->multiply;
469 nm.sy *= i->multiply;
470 nm.r0 *= i->multiply;
471 nm.r1 *= i->multiply;
472 nm.tx *= i->multiply;
473 nm.ty *= i->multiply;
474 s2->fillstyles[t].m = nm;
477 /* add this shape to the global shape list, for deallocing */
478 swf_Render_AddShape(dest, fshape);
481 if(shape->numlinestyles) {
482 dummyshape_t*dshape = rfx_calloc(sizeof(dummyshape_t));
484 lshape = linestyle2fillstyle(shape);
488 lp.depth = (_depth << 16)+1;
490 dshape->shape = lshape;
492 /* add this shape to the global shape list, for deallocing */
493 swf_Render_AddShape(dest, dshape);
499 add_line(dest, -20, 0, -20, i->height2*20, &p);
504 int x1,y1,x2,y2,x3,y3;
508 if(line->type == moveTo) {
509 } else if(line->type == lineTo) {
516 l = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
517 printf("%d - %.2f/%.2f -> %.2f/%.2f ", l, x1/20.0, y1/20.0, x2/20.0, y2/20.0);
520 transform_point(&mat, x, y, &x1, &y1);
521 transform_point(&mat, line->x, line->y, &x3, &y3);
523 if(line->linestyle && ! p.clipdepth) {
524 lp.shapeline = &lshape->lines[line->linestyle-1];
525 add_solidline(dest, x1, y1, x3, y3, shape->linestyles[line->linestyle-1].width, &lp);
528 if(line->fillstyle0 || line->fillstyle1) {
529 assert(shape->numfillstyles);
530 add_line(dest, x1, y1, x3, y3, &p);
533 if(DEBUG&4) printf("\n");
534 } else if(line->type == splineTo) {
535 int c,t,parts,qparts;
538 transform_point(&mat, x, y, &x1, &y1);
539 transform_point(&mat, line->sx, line->sy, &x2, &y2);
540 transform_point(&mat, line->x, line->y, &x3, &y3);
542 c = abs(x3-2*x2+x1) + abs(y3-2*y2+y1);
546 parts = (int)(sqrt(c)/3);
547 if(!parts) parts = 1;
551 printf("spline %.2f/%.2f -(%.2f/%.2f)-> %.2f/%.2f (c=%d, %d parts)",
554 x3/20.0, y3/20.0, c, parts);
557 for(t=1;t<=parts;t++) {
558 double nx = (double)(t*t*x3 + 2*t*(parts-t)*x2 + (parts-t)*(parts-t)*x1)/(double)(parts*parts);
559 double ny = (double)(t*t*y3 + 2*t*(parts-t)*y2 + (parts-t)*(parts-t)*y1)/(double)(parts*parts);
561 if(line->linestyle && ! p.clipdepth) {
562 lp.shapeline = &lshape->lines[line->linestyle-1];
563 add_solidline(dest, xx, yy, nx, ny, shape->linestyles[line->linestyle-1].width, &lp);
566 if(line->fillstyle0 || line->fillstyle1) {
567 assert(shape->numfillstyles);
568 add_line(dest, (int)xx, (int)yy, (int)nx, (int)ny, &p);
583 typedef struct _layer {
595 static RGBA color_red = {255,255,0,0};
596 static RGBA color_white = {255,255,255,255};
598 static void fill_plain(RGBA*line, int x1, int x2, RGBA col)
602 int ainv = 255-col.a;
603 col.r = (col.r*col.a)>>8;
604 col.g = (col.g*col.a)>>8;
605 col.b = (col.b*col.a)>>8;
608 line[x].r = ((line[x].r*ainv)>>8)+col.r;
609 line[x].g = ((line[x].g*ainv)>>8)+col.g;
610 line[x].b = ((line[x].b*ainv)>>8)+col.b;
620 static void fill_bitmap(RGBA*line, int y, int x1, int x2, MATRIX*m, bitmap_t*b, int clip)
623 double m11=m->sx/65536.0, m21=m->r1/65536.0;
624 double m12=m->r0/65536.0, m22=m->sy/65536.0;
625 double rx = m->tx/20.0;
626 double ry = m->ty/20.0;
627 double det = m11*m22 - m12*m21;
628 if(fabs(det) < 0.0005) {
629 /* x direction equals y direction- the image is invisible */
634 if(!b->width || !b->height) {
635 fill_plain(line, x1, x2, color_red);
641 int xx = (int)(( (x - rx) * m22 - (y - ry) * m21)*det);
642 int yy = (int)((- (x - rx) * m12 + (y - ry) * m11)*det);
647 if(xx>=b->width) xx = b->width-1;
649 if(yy>=b->height) yy = b->height-1;
655 col = b->data[yy*b->width+xx];
658 line[x].r = ((line[x].r*ainv)>>8)+col.r;
659 line[x].g = ((line[x].g*ainv)>>8)+col.g;
660 line[x].b = ((line[x].b*ainv)>>8)+col.b;
665 static void fill(RENDERBUF*dest, RGBA*line, int y, int x1, int x2, state_t*clipstate, state_t*fillstate)
667 renderbuf_internal*i = (renderbuf_internal*)dest->internal;
670 layer_t*lc = clipstate->layers;
671 layer_t*lf = fillstate->layers;
674 if(x1>=x2) //zero width? nothing to do.
679 if(lc && (!lf || lc->p->depth < lf->p->depth)) {
682 } else if(lf && (!lc || lf->p->depth < lc->p->depth)) {
685 } else if(lf && lc && lf->p->depth == lc->p->depth) {
686 /* A clipshape and a fillshape at the same depth. Yuck.
687 Bug in the SWF file */
688 fprintf(stderr, "Error: Multiple use of depth %d in SWF\n", lf->p->depth);
692 fprintf(stderr, "Internal error: %08x %08x\n", lc, lf);
693 if(lc) fprintf(stderr, " lc->depth = %08x\n", lc->p->depth);
694 if(lf) fprintf(stderr, " lf->depth = %08x\n", lf->p->depth);
697 if(l->p->depth < clipdepth) {
698 if(DEBUG&2) printf("(clipped)");
701 if(l->fillid < 0 /*clip*/) {
702 if(DEBUG&2) printf("(add clip %d)", l->clipdepth);
703 if(l->clipdepth > clipdepth)
704 clipdepth = l->clipdepth;
705 } else if(l->fillid == 0) {
706 /* not filled. TODO: we should never add those in the first place */
708 printf("(not filled)");
709 } else if(l->fillid > l->p->s->shape->numfillstyles) {
710 fprintf(stderr, "Fill style out of bounds (%d>%d)", l->fillid, l->p->s->shape->numlinestyles);
714 printf("(%d -> %d style %d)", x1, x2, l->fillid);
716 f = &l->p->s->shape->fillstyles[l->fillid-1];
718 if(f->type == FILL_SOLID) {
719 /* plain color fill */
720 fill_plain(line, x1, x2, f->color);
721 } else if(f->type == FILL_TILED || f->type == FILL_CLIPPED) {
722 /* TODO: optimize (do this in add_pixel()?) */
723 bitmap_t* b = i->bitmaps;
724 while(b && b->id != f->id_bitmap) {
728 fprintf(stderr, "Shape references unknown bitmap %d\n", f->id_bitmap);
729 fill_plain(line, x1, x2, color_red);
731 //done in swf_RenderShape now
733 //m.tx -= dest->posx*20;
734 //m.ty -= dest->posy*20;
735 //m.sx *= i->multiply;
736 //m.sy *= i->multiply;
737 //m.r0 *= i->multiply;
738 //m.r1 *= i->multiply;
739 //m.tx *= i->multiply;
740 //m.ty *= i->multiply;
741 fill_bitmap(line, y, x1, x2, &f->m, b, FILL_CLIPPED?1:0);
748 static void search_layer(state_t*state, int depth, layer_t**before, layer_t**self, layer_t**after)
750 layer_t*last=0,*l = state->layers;
751 while(l && l->p->depth < depth) {
756 if(l && l->p->depth == depth)
761 static void delete_layer(state_t*state, layer_t*todel)
763 layer_t*before=todel->prev;
764 layer_t*next = todel->next;
767 state->layers = next;
773 before->next->prev = before;
776 static void add_layer(state_t*state, layer_t*before, layer_t*toadd)
779 toadd->next = state->layers;
783 toadd->next = before->next;
784 toadd->prev = before;
785 before->next = toadd;
788 toadd->next->prev = toadd;
790 static void free_layers(state_t* state)
792 layer_t*l = state->layers;
794 layer_t*next = l->next;
800 static void change_state(int y, state_t* state, renderpoint_t*p)
802 layer_t*before=0, *self=0, *after=0;
805 printf("[%s(%d,%d)/%d/%d-%d]", p->type==clip_type?"C":"F", p->x, y, p->depth, p->shapeline->fillstyle0, p->shapeline->fillstyle1);
808 search_layer(state, p->depth, &before, &self, &after);
812 if(self->fillid<0 || !p->shapeline->fillstyle0 || !p->shapeline->fillstyle1) {
813 /* filling/clipping ends */
814 if(DEBUG&2) printf("<D>");
816 delete_layer(state, self);
818 /*both fill0 and fill1 are set- exchange the two, updating the layer */
819 if(self->fillid == p->shapeline->fillstyle0) {
820 self->fillid = p->shapeline->fillstyle1;
823 if(DEBUG&2) printf("<X>");
824 } else if(self->fillid == p->shapeline->fillstyle1) {
825 self->fillid = p->shapeline->fillstyle0;
828 if(DEBUG&2) printf("<X>");
830 /* buggy shape. keep everything as-is. */
831 if(DEBUG&2) printf("<!>");
832 //fprintf(stderr, "<line %d: bad swap>\n", y);
838 if(p->shapeline && p->shapeline->fillstyle0 && p->shapeline->fillstyle1) {
839 /* this is a hack- a better way would be to make sure that
840 we always get (0,32), (32, 33), (33, 0) in the right order if
841 they happen to fall on the same pixel.
842 (not: (0,32), (33, 0), (32, 33))
844 fprintf(stderr, "<line %d: both fillstyles set while not inside shape>\n", y);
848 n = rfx_calloc(sizeof(layer_t));
850 if(DEBUG&2) printf("<+>");
852 if(p->type == clip_type) {
855 n->clipdepth = p->clipdepth;
858 n->fillid = p->shapeline->fillstyle0 ? p->shapeline->fillstyle0 : p->shapeline->fillstyle1;
863 add_layer(state, before, n);
867 RGBA* swf_Render(RENDERBUF*dest)
869 renderbuf_internal*i = (renderbuf_internal*)dest->internal;
870 RGBA* img = (RGBA*)rfx_alloc(sizeof(RGBA)*dest->width*dest->height);
873 RGBA * line1 = rfx_alloc(sizeof(RGBA)*i->width2);
874 RGBA * line2 = rfx_alloc(sizeof(RGBA)*i->width2);
876 printf("%d\n", sizeof(renderpoint_t));
878 for(y=0;y<i->height2;y++) {
879 TAG*tag = i->lines[y].points;
881 int size = sizeof(renderpoint_t);
882 int num = renderpoint_num(tag);
883 renderpoint_t*points = renderpoint_readall(tag);
887 memset(&clipstate, 0, sizeof(state_t));
888 memset(&fillstate, 0, sizeof(state_t));
890 if((y&1) && i->antialize)
895 memset(line, 0, sizeof(RGBA)*i->width2);
898 int xstep=i->background_width*65536/i->width2;
899 RGBA*src = &i->background[(i->background_height*y/i->height2)*i->background_width];
900 for(x=0,xx=0;x<i->width2;x++,xx+=xstep) {
901 line[x] = src[xx>>16];
904 memory += tag->memsize;
905 qsort(points, num, sizeof(renderpoint_t), compare_renderpoints);
907 renderpoint_t*p = &points[n];
908 renderpoint_t*next= n<num-1?&points[n+1]:0;
910 int endx = next?next->x:i->width2;
916 if(p->type == clip_type)
917 change_state(y, &clipstate, p);
919 change_state(y, &fillstate, p);
921 fill(dest, line, y, startx, endx, &clipstate, &fillstate);
922 if(endx == i->width2)
925 free_layers(&clipstate);
926 free_layers(&fillstate);
927 if(DEBUG&2) printf("\n");
930 memcpy(&img[y*dest->width], line, sizeof(RGBA)*dest->width);
934 RGBA* p = &img[(y/2)*dest->width];
935 for(x=0;x<dest->width;x++) {
936 RGBA*p1 = &line1[x*2];
937 RGBA*p2 = &line1[x*2+1];
938 RGBA*p3 = &line2[x*2];
939 RGBA*p4 = &line2[x*2+1];
940 p[x].r = (p1->r + p2->r + p3->r + p4->r)/4;
941 p[x].g = (p1->g + p2->g + p3->g + p4->g)/4;
942 p[x].b = (p1->b + p2->b + p3->b + p4->b)/4;
943 p[x].a = (p1->a + p2->a + p3->a + p4->a)/4;
951 if(DEBUG) printf("\nMemory used: %d\n", memory);
953 if(DEBUG) printf("Statistics:\n");
954 if(DEBUG) printf("Average layer depth: %f\n", (double)layers/layernum);