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 */
189 /* That's what Macromedia's Player does at least at zoom level >= 1. */
192 /* That's what Macromedia's Player seems to do at zoom level 0. */
193 /* TODO: needs testing */
198 sd = (double)dx*(double)dx+(double)dy*(double)dy;
220 add_line(buf, x1+vx, y1+vy, xx, yy, p);
223 for(t=1;t<segments;t++) {
224 double s = sin(t*PI/segments);
225 double c = cos(t*PI/segments);
226 xx = (x2 + vx*c - vy*s);
227 yy = (y2 + vx*s + vy*c);
228 add_line(buf, lastx, lasty, xx, yy, p);
235 add_line(buf, lastx, lasty, xx, yy, p);
240 add_line(buf, lastx, lasty, xx, yy, p);
243 for(t=1;t<segments;t++) {
244 double s = sin(t*PI/segments);
245 double c = cos(t*PI/segments);
246 xx = (x1 - vx*c + vy*s);
247 yy = (y1 - vx*s - vy*c);
248 add_line(buf, lastx, lasty, xx, yy, p);
252 add_line(buf, lastx, lasty, (x1+vx), (y1+vy), p);
255 static inline void transform_point(MATRIX*m, int x, int y, int*dx, int*dy)
260 d = swf_TurnPoint(p, m);
265 static int compare_renderpoints(const void * _a, const void * _b)
267 renderpoint_t*a = (renderpoint_t*)_a;
268 renderpoint_t*b = (renderpoint_t*)_b;
269 if(a->fx < b->fx) return -1;
270 if(a->fx > b->fx) return 1;
274 void swf_Render_Init(RENDERBUF*buf, int posx, int posy, int width, int height, char antialize, int multiply)
276 renderbuf_internal*i;
278 memset(buf, 0, sizeof(RENDERBUF));
279 buf->width = width*multiply;
280 buf->height = height*multiply;
283 buf->internal = (renderbuf_internal*)rfx_calloc(sizeof(renderbuf_internal));
284 i = (renderbuf_internal*)buf->internal;
285 i->antialize = !!antialize;
286 i->multiply = antialize?multiply*2:multiply;
287 i->height2 = antialize?2*buf->height:buf->height;
288 i->width2 = antialize?2*buf->width:buf->width;
289 i->lines = (renderline_t*)rfx_alloc(i->height2*sizeof(renderline_t));
290 for(y=0;y<i->height2;y++) {
291 i->lines[y].points = swf_InsertTag(0, 0);
294 void swf_Render_SetBackground(RENDERBUF*buf, RGBA*img, int width, int height)
296 renderbuf_internal*i = (renderbuf_internal*)buf->internal;
297 RGBA*bck = (RGBA*)rfx_alloc(sizeof(RGBA)*width*height);
298 memcpy(bck, img, sizeof(RGBA)*width*height);
300 i->background_width = width;
301 i->background_height = height;
303 void swf_Render_SetBackgroundColor(RENDERBUF*buf, RGBA color)
305 swf_Render_SetBackground(buf, &color, 1, 1);
307 void swf_Render_AddImage(RENDERBUF*buf, U16 id, RGBA*img, int width, int height)
309 renderbuf_internal*i = (renderbuf_internal*)buf->internal;
311 bitmap_t*bm = rfx_calloc(sizeof(bitmap_t));
317 bm->next = i->bitmaps;
320 void swf_Render_ClearCanvas(RENDERBUF*dest)
322 renderbuf_internal*i = (renderbuf_internal*)dest->internal;
324 for(y=0;y<i->height2;y++) {
325 swf_ClearTag(i->lines[y].points);
328 void swf_Render_Delete(RENDERBUF*dest)
330 renderbuf_internal*i = (renderbuf_internal*)dest->internal;
332 bitmap_t*b = i->bitmaps;
333 dummyshape_t*d = i->dshapes;
336 free(i->background);i->background=0;
339 /* delete line buffers */
340 for(y=0;y<i->height2;y++) {
341 swf_DeleteTag(i->lines[y].points);
342 i->lines[y].points = 0;
346 dummyshape_t*next = d->next;
347 swf_Shape2Free(d->shape);
348 free(d->shape);d->shape=0;
356 bitmap_t*next = b->next;
357 //free(b->data);b->data=0;
362 rfx_free(i->lines); i->lines = 0;
363 rfx_free(dest->internal); dest->internal = 0;
366 static void swf_Render_AddShape(RENDERBUF*dest,dummyshape_t*s)
368 renderbuf_internal*i = (renderbuf_internal*)dest->internal;
372 i->dshapes_next->next = s;
379 static SHAPE2* linestyle2fillstyle(SHAPE2*shape)
381 SHAPE2*s = rfx_calloc(sizeof(SHAPE2));
383 s->numfillstyles = shape->numlinestyles;
384 s->fillstyles = (FILLSTYLE*)rfx_calloc(sizeof(FILLSTYLE)*shape->numlinestyles);
385 s->lines = (SHAPELINE*)rfx_calloc(sizeof(SHAPELINE)*shape->numlinestyles);
386 for(t=0;t<shape->numlinestyles;t++) {
387 s->lines[t].fillstyle0 = t+1;
388 s->fillstyles[t].type = FILL_SOLID;
389 s->fillstyles[t].color = shape->linestyles[t].color;
394 void swf_RenderShape(RENDERBUF*dest, SHAPE2*shape, MATRIX*m, CXFORM*c, U16 _depth,U16 _clipdepth)
396 renderbuf_internal*i = (renderbuf_internal*)dest->internal;
398 SHAPELINE*line = shape->lines;
404 memset(&p, 0, sizeof(renderpoint_t));
405 memset(&lp, 0, sizeof(renderpoint_t));
407 p.type = _clipdepth?clip_type:fill_type;
408 p.depth = _depth << 16;
409 p.clipdepth = _clipdepth << 16;
411 mat.tx -= dest->posx*20;
412 mat.ty -= dest->posy*20;
414 if(shape->numfillstyles) {
415 dummyshape_t*fshape = rfx_calloc(sizeof(dummyshape_t));
417 SHAPE2* s2 = swf_Shape2Clone(shape);
423 /* multiply fillstyles matrices with placement matrix-
424 important for texture and gradient fill */
425 for(t=0;t<s2->numfillstyles;t++) {
427 swf_MatrixJoin(&nm, &s2->fillstyles[t].m, &mat); //TODO: is this the right order?
428 nm.sx *= i->multiply;
429 nm.sy *= i->multiply;
430 nm.r0 *= i->multiply;
431 nm.r1 *= i->multiply;
432 nm.tx *= i->multiply;
433 nm.ty *= i->multiply;
434 s2->fillstyles[t].m = nm;
437 /* add this shape to the global shape list, for deallocing */
438 swf_Render_AddShape(dest, fshape);
441 if(shape->numlinestyles) {
442 dummyshape_t*dshape = rfx_calloc(sizeof(dummyshape_t));
444 lshape = linestyle2fillstyle(shape);
448 lp.depth = (_depth << 16)+1;
450 dshape->shape = lshape;
452 /* add this shape to the global shape list, for deallocing */
453 swf_Render_AddShape(dest, dshape);
459 add_line(dest, -20, 0, -20, i->height2*20, &p);
464 int x1,y1,x2,y2,x3,y3;
468 if(line->type == moveTo) {
469 } else if(line->type == lineTo) {
476 l = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
477 printf("%d - %.2f/%.2f -> %.2f/%.2f ", l, x1/20.0, y1/20.0, x2/20.0, y2/20.0);
480 transform_point(&mat, x, y, &x1, &y1);
481 transform_point(&mat, line->x, line->y, &x3, &y3);
483 if(line->linestyle && ! p.clipdepth) {
484 lp.shapeline = &lshape->lines[line->linestyle-1];
485 add_solidline(dest, x1, y1, x3, y3, shape->linestyles[line->linestyle-1].width, &lp);
488 if(line->fillstyle0 || line->fillstyle1) {
489 assert(shape->numfillstyles);
490 add_line(dest, x1, y1, x3, y3, &p);
493 if(DEBUG&4) printf("\n");
494 } else if(line->type == splineTo) {
495 int c,t,parts,qparts;
498 transform_point(&mat, x, y, &x1, &y1);
499 transform_point(&mat, line->sx, line->sy, &x2, &y2);
500 transform_point(&mat, line->x, line->y, &x3, &y3);
502 c = abs(x3-2*x2+x1) + abs(y3-2*y2+y1);
506 parts = (int)(sqrt(c)/3);
507 if(!parts) parts = 1;
511 printf("spline %.2f/%.2f -(%.2f/%.2f)-> %.2f/%.2f (c=%d, %d parts)",
514 x3/20.0, y3/20.0, c, parts);
517 for(t=1;t<=parts;t++) {
518 double nx = (double)(t*t*x3 + 2*t*(parts-t)*x2 + (parts-t)*(parts-t)*x1)/(double)(parts*parts);
519 double ny = (double)(t*t*y3 + 2*t*(parts-t)*y2 + (parts-t)*(parts-t)*y1)/(double)(parts*parts);
521 if(line->linestyle && ! p.clipdepth) {
522 lp.shapeline = &lshape->lines[line->linestyle-1];
523 add_solidline(dest, xx, yy, nx, ny, shape->linestyles[line->linestyle-1].width, &lp);
526 if(line->fillstyle0 || line->fillstyle1) {
527 assert(shape->numfillstyles);
528 add_line(dest, (int)xx, (int)yy, (int)nx, (int)ny, &p);
543 typedef struct _layer {
555 static RGBA color_red = {255,255,0,0};
556 static RGBA color_white = {255,255,255,255};
558 static void fill_plain(RGBA*line, int x1, int x2, RGBA col)
562 int ainv = 255-col.a;
563 col.r = (col.r*col.a)>>8;
564 col.g = (col.g*col.a)>>8;
565 col.b = (col.b*col.a)>>8;
568 line[x].r = ((line[x].r*ainv)>>8)+col.r;
569 line[x].g = ((line[x].g*ainv)>>8)+col.g;
570 line[x].b = ((line[x].b*ainv)>>8)+col.b;
580 static void fill_bitmap(RGBA*line, int y, int x1, int x2, MATRIX*m, bitmap_t*b, int clip)
583 double m11=m->sx/65536.0, m21=m->r1/65536.0;
584 double m12=m->r0/65536.0, m22=m->sy/65536.0;
585 double rx = m->tx/20.0;
586 double ry = m->ty/20.0;
587 double det = m11*m22 - m12*m21;
588 if(fabs(det) < 0.0005) {
589 /* x direction equals y direction- the image is invisible */
594 if(!b->width || !b->height) {
595 fill_plain(line, x1, x2, color_red);
601 int xx = (int)(( (x - rx) * m22 - (y - ry) * m21)*det);
602 int yy = (int)((- (x - rx) * m12 + (y - ry) * m11)*det);
607 if(xx>=b->width) xx = b->width-1;
609 if(yy>=b->height) yy = b->height-1;
615 col = b->data[yy*b->width+xx];
618 line[x].r = ((line[x].r*ainv)>>8)+col.r;
619 line[x].g = ((line[x].g*ainv)>>8)+col.g;
620 line[x].b = ((line[x].b*ainv)>>8)+col.b;
625 static void fill(RENDERBUF*dest, RGBA*line, int y, int x1, int x2, state_t*state)
627 renderbuf_internal*i = (renderbuf_internal*)dest->internal;
630 layer_t*l = state->layers;
632 if(x1>=x2) //zero width? nothing to do.
637 if(l->p->depth < clipdepth) {
638 if(DEBUG&2) printf("(clipped)");
642 if(l->fillid < 0 /*clip*/) {
643 if(DEBUG&2) printf("(add clip %d)", l->clipdepth);
644 if(l->clipdepth > clipdepth)
645 clipdepth = l->clipdepth;
646 } else if(l->fillid == 0) {
647 /* not filled. TODO: we should never add those in the first place */
649 printf("(not filled)");
650 } else if(l->fillid > l->p->s->shape->numfillstyles) {
651 fprintf(stderr, "Fill style out of bounds (%d>%d)", l->fillid, l->p->s->shape->numlinestyles);
655 printf("(%d -> %d style %d)", x1, x2, l->fillid);
657 f = &l->p->s->shape->fillstyles[l->fillid-1];
659 if(f->type == FILL_SOLID) {
660 /* plain color fill */
661 fill_plain(line, x1, x2, f->color);
662 } else if(f->type == FILL_TILED || f->type == FILL_CLIPPED) {
663 /* TODO: optimize (do this in add_pixel()?) */
664 bitmap_t* b = i->bitmaps;
665 while(b && b->id != f->id_bitmap) {
669 fprintf(stderr, "Shape references unknown bitmap %d\n", f->id_bitmap);
670 fill_plain(line, x1, x2, color_red);
672 //done in swf_RenderShape now
674 //m.tx -= dest->posx*20;
675 //m.ty -= dest->posy*20;
676 //m.sx *= i->multiply;
677 //m.sy *= i->multiply;
678 //m.r0 *= i->multiply;
679 //m.r1 *= i->multiply;
680 //m.tx *= i->multiply;
681 //m.ty *= i->multiply;
682 fill_bitmap(line, y, x1, x2, &f->m, b, FILL_CLIPPED?1:0);
690 static void search_layer(state_t*state, int depth, layer_t**before, layer_t**self, layer_t**after)
692 layer_t*last=0,*l = state->layers;
693 while(l && l->p->depth < depth) {
698 if(l && l->p->depth == depth)
703 static void delete_layer(state_t*state, layer_t*todel)
705 layer_t*before=todel->prev;
706 layer_t*next = todel->next;
709 state->layers = next;
715 before->next->prev = before;
718 static void add_layer(state_t*state, layer_t*before, layer_t*toadd)
721 toadd->next = state->layers;
725 toadd->next = before->next;
726 toadd->prev = before;
727 before->next = toadd;
730 toadd->next->prev = toadd;
732 static void free_layers(state_t* state)
734 layer_t*l = state->layers;
736 layer_t*next = l->next;
742 static void change_state(int y, state_t* state, renderpoint_t*p)
744 layer_t*before=0, *self=0, *after=0;
747 printf("[%s(%d,%d)/%d/%d-%d]", p->type==clip_type?"C":"F", p->x, y, p->depth, p->shapeline->fillstyle0, p->shapeline->fillstyle1);
750 search_layer(state, p->depth, &before, &self, &after);
754 if(self->fillid<0 || !p->shapeline->fillstyle0 || !p->shapeline->fillstyle1) {
755 /* filling/clipping ends */
756 if(DEBUG&2) printf("<D>");
758 delete_layer(state, self);
760 /*both fill0 and fill1 are set- exchange the two, updating the layer */
761 if(self->fillid == p->shapeline->fillstyle0) {
762 self->fillid = p->shapeline->fillstyle1;
765 if(DEBUG&2) printf("<X>");
766 } else if(self->fillid == p->shapeline->fillstyle1) {
767 self->fillid = p->shapeline->fillstyle0;
770 if(DEBUG&2) printf("<X>");
772 /* buggy shape. keep everything as-is. */
773 if(DEBUG&2) printf("<!>");
774 //fprintf(stderr, "<line %d: bad swap>\n", y);
780 if(p->shapeline && p->shapeline->fillstyle0 && p->shapeline->fillstyle1) {
781 /* this is a hack- a better way would be to make sure that
782 we always get (0,32), (32, 33), (33, 0) in the right order if
783 they happen to fall on the same pixel.
784 (not: (0,32), (33, 0), (32, 33))
786 fprintf(stderr, "<line %d: both fillstyles set while not inside shape>\n", y);
790 n = rfx_calloc(sizeof(layer_t));
792 if(DEBUG&2) printf("<+>");
794 if(p->type == clip_type) {
797 n->clipdepth = p->clipdepth;
800 n->fillid = p->shapeline->fillstyle0 ? p->shapeline->fillstyle0 : p->shapeline->fillstyle1;
805 add_layer(state, before, n);
809 RGBA* swf_Render(RENDERBUF*dest)
811 renderbuf_internal*i = (renderbuf_internal*)dest->internal;
812 RGBA* img = (RGBA*)rfx_alloc(sizeof(RGBA)*dest->width*dest->height);
815 RGBA * line1 = rfx_alloc(sizeof(RGBA)*i->width2);
816 RGBA * line2 = rfx_alloc(sizeof(RGBA)*i->width2);
818 for(y=0;y<i->height2;y++) {
819 TAG*tag = i->lines[y].points;
821 int size = sizeof(renderpoint_t);
822 int num = tag->len / size;
825 memset(&state, 0, sizeof(state_t));
827 if((y&1) && i->antialize)
832 memset(line, 0, sizeof(RGBA)*i->width2);
835 int xstep=i->background_width*65536/i->width2;
836 RGBA*src = &i->background[(i->background_height*y/i->height2)*i->background_width];
837 for(x=0,xx=0;x<i->width2;x++,xx+=xstep) {
838 line[x] = src[xx>>16];
841 memory += tag->memsize;
842 qsort(tag->data, num, size, compare_renderpoints);
844 renderpoint_t*p = (renderpoint_t*)&tag->data[size*n];
845 renderpoint_t*next= n<num-1?(renderpoint_t*)&tag->data[size*(n+1)]:0;
847 int endx = next?next->x:i->width2;
853 change_state(y, &state, p);
855 fill(dest, line, y, startx, endx, &state);
856 if(endx == i->width2)
860 if(DEBUG&2) printf("\n");
863 memcpy(&img[y*dest->width], line, sizeof(RGBA)*dest->width);
867 RGBA* p = &img[(y/2)*dest->width];
868 for(x=0;x<dest->width;x++) {
869 RGBA*p1 = &line1[x*2];
870 RGBA*p2 = &line1[x*2+1];
871 RGBA*p3 = &line2[x*2];
872 RGBA*p4 = &line2[x*2+1];
873 p[x].r = (p1->r + p2->r + p3->r + p4->r)/4;
874 p[x].g = (p1->g + p2->g + p3->g + p4->g)/4;
875 p[x].b = (p1->b + p2->b + p3->b + p4->b)/4;
876 p[x].a = (p1->a + p2->a + p3->a + p4->a)/4;
884 if(DEBUG) printf("\nMemory used: %d\n", memory);
886 if(DEBUG) printf("Statistics:\n");
887 if(DEBUG) printf("Average layer depth: %f\n", (double)layers/layernum);