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 /* The Flash Player does this, too. This means every line is always at least
190 sd = (double)dx*(double)dx+(double)dy*(double)dy;
212 add_line(buf, x1+vx, y1+vy, xx, yy, p);
215 for(t=1;t<segments;t++) {
216 double s = sin(t*PI/segments);
217 double c = cos(t*PI/segments);
218 xx = (x2 + vx*c - vy*s);
219 yy = (y2 + vx*s + vy*c);
220 add_line(buf, lastx, lasty, xx, yy, p);
227 add_line(buf, lastx, lasty, xx, yy, p);
232 add_line(buf, lastx, lasty, xx, yy, p);
235 for(t=1;t<segments;t++) {
236 double s = sin(t*PI/segments);
237 double c = cos(t*PI/segments);
238 xx = (x1 - vx*c + vy*s);
239 yy = (y1 - vx*s - vy*c);
240 add_line(buf, lastx, lasty, xx, yy, p);
244 add_line(buf, lastx, lasty, (x1+vx), (y1+vy), p);
247 static inline void transform_point(MATRIX*m, int x, int y, int*dx, int*dy)
252 d = swf_TurnPoint(p, m);
257 static int compare_renderpoints(const void * _a, const void * _b)
259 renderpoint_t*a = (renderpoint_t*)_a;
260 renderpoint_t*b = (renderpoint_t*)_b;
261 if(a->fx < b->fx) return -1;
262 if(a->fx > b->fx) return 1;
266 void swf_Render_Init(RENDERBUF*buf, int posx, int posy, int width, int height, char antialize, int multiply)
268 renderbuf_internal*i;
270 memset(buf, 0, sizeof(RENDERBUF));
271 buf->width = width*multiply;
272 buf->height = height*multiply;
275 buf->internal = (renderbuf_internal*)rfx_calloc(sizeof(renderbuf_internal));
276 i = (renderbuf_internal*)buf->internal;
277 i->antialize = !!antialize;
278 i->multiply = antialize?multiply*2:multiply;
279 i->height2 = antialize?2*buf->height:buf->height;
280 i->width2 = antialize?2*buf->width:buf->width;
281 i->lines = (renderline_t*)rfx_alloc(i->height2*sizeof(renderline_t));
282 for(y=0;y<i->height2;y++) {
283 i->lines[y].points = swf_InsertTag(0, 0);
286 void swf_Render_SetBackground(RENDERBUF*buf, RGBA*img, int width, int height)
288 renderbuf_internal*i = (renderbuf_internal*)buf->internal;
289 RGBA*bck = (RGBA*)rfx_alloc(sizeof(RGBA)*width*height);
290 memcpy(bck, img, sizeof(RGBA)*width*height);
292 i->background_width = width;
293 i->background_height = height;
295 void swf_Render_SetBackgroundColor(RENDERBUF*buf, RGBA color)
297 swf_Render_SetBackground(buf, &color, 1, 1);
299 void swf_Render_AddImage(RENDERBUF*buf, U16 id, RGBA*img, int width, int height)
301 renderbuf_internal*i = (renderbuf_internal*)buf->internal;
303 bitmap_t*bm = rfx_calloc(sizeof(bitmap_t));
309 bm->next = i->bitmaps;
312 void swf_Render_ClearCanvas(RENDERBUF*dest)
314 renderbuf_internal*i = (renderbuf_internal*)dest->internal;
316 for(y=0;y<i->height2;y++) {
317 swf_ClearTag(i->lines[y].points);
320 void swf_Render_Delete(RENDERBUF*dest)
322 renderbuf_internal*i = (renderbuf_internal*)dest->internal;
324 bitmap_t*b = i->bitmaps;
325 dummyshape_t*d = i->dshapes;
328 free(i->background);i->background=0;
331 /* delete line buffers */
332 for(y=0;y<i->height2;y++) {
333 swf_DeleteTag(i->lines[y].points);
334 i->lines[y].points = 0;
338 dummyshape_t*next = d->next;
339 swf_Shape2Free(d->shape);
340 free(d->shape);d->shape=0;
348 bitmap_t*next = b->next;
349 //free(b->data);b->data=0;
354 rfx_free(i->lines); i->lines = 0;
355 rfx_free(dest->internal); dest->internal = 0;
358 static void swf_Render_AddShape(RENDERBUF*dest,dummyshape_t*s)
360 renderbuf_internal*i = (renderbuf_internal*)dest->internal;
364 i->dshapes_next->next = s;
371 static SHAPE2* linestyle2fillstyle(SHAPE2*shape)
373 SHAPE2*s = rfx_calloc(sizeof(SHAPE2));
375 s->numfillstyles = shape->numlinestyles;
376 s->fillstyles = (FILLSTYLE*)rfx_calloc(sizeof(FILLSTYLE)*shape->numlinestyles);
377 s->lines = (SHAPELINE*)rfx_calloc(sizeof(SHAPELINE)*shape->numlinestyles);
378 for(t=0;t<shape->numlinestyles;t++) {
379 s->lines[t].fillstyle0 = t+1;
380 s->fillstyles[t].type = FILL_SOLID;
381 s->fillstyles[t].color = shape->linestyles[t].color;
386 void swf_RenderShape(RENDERBUF*dest, SHAPE2*shape, MATRIX*m, CXFORM*c, U16 _depth,U16 _clipdepth)
388 renderbuf_internal*i = (renderbuf_internal*)dest->internal;
390 SHAPELINE*line = shape->lines;
396 memset(&p, 0, sizeof(renderpoint_t));
397 memset(&lp, 0, sizeof(renderpoint_t));
399 p.type = _clipdepth?clip_type:fill_type;
400 p.depth = _depth << 16;
401 p.clipdepth = _clipdepth << 16;
403 mat.tx -= dest->posx*20;
404 mat.ty -= dest->posy*20;
406 if(shape->numfillstyles) {
407 dummyshape_t*fshape = rfx_calloc(sizeof(dummyshape_t));
409 SHAPE2* s2 = swf_Shape2Clone(shape);
415 /* multiply fillstyles matrices with placement matrix-
416 important for texture and gradient fill */
417 for(t=0;t<s2->numfillstyles;t++) {
419 swf_MatrixJoin(&nm, &s2->fillstyles[t].m, &mat); //TODO: is this the right order?
420 nm.sx *= i->multiply;
421 nm.sy *= i->multiply;
422 nm.r0 *= i->multiply;
423 nm.r1 *= i->multiply;
424 nm.tx *= i->multiply;
425 nm.ty *= i->multiply;
426 s2->fillstyles[t].m = nm; //!!!!!!!!!!!!!!!!
429 /* add this shape to the global shape list, for deallocing */
430 swf_Render_AddShape(dest, fshape);
433 if(shape->numlinestyles) {
434 dummyshape_t*dshape = rfx_calloc(sizeof(dummyshape_t));
436 lshape = linestyle2fillstyle(shape);
440 lp.depth = (_depth << 16)+1;
442 dshape->shape = lshape;
444 /* add this shape to the global shape list, for deallocing */
445 swf_Render_AddShape(dest, dshape);
451 add_line(dest, -20, 0, -20, i->height2*20, &p);
456 int x1,y1,x2,y2,x3,y3;
460 if(line->type == moveTo) {
461 } else if(line->type == lineTo) {
468 l = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
469 printf("%d - %.2f/%.2f -> %.2f/%.2f ", l, x1/20.0, y1/20.0, x2/20.0, y2/20.0);
472 transform_point(&mat, x, y, &x1, &y1);
473 transform_point(&mat, line->x, line->y, &x3, &y3);
475 if(line->linestyle && ! p.clipdepth) {
476 lp.shapeline = &lshape->lines[line->linestyle-1];
477 add_solidline(dest, x1, y1, x3, y3, shape->linestyles[line->linestyle-1].width, &lp);
480 if(line->fillstyle0 || line->fillstyle1) {
481 assert(shape->numfillstyles);
482 add_line(dest, x1, y1, x3, y3, &p);
485 if(DEBUG&4) printf("\n");
486 } else if(line->type == splineTo) {
487 int c,t,y,parts,qparts;
490 transform_point(&mat, x, y, &x1, &y1);
491 transform_point(&mat, line->sx, line->sy, &x2, &y2);
492 transform_point(&mat, line->x, line->y, &x3, &y3);
494 c = abs(x3-2*x2+x1) + abs(y3-2*y2+y1);
498 parts = (int)(sqrt(c)/3);
499 if(!parts) parts = 1;
503 printf("spline %.2f/%.2f -(%.2f/%.2f)-> %.2f/%.2f (c=%d, %d parts)",
506 x3/20.0, y3/20.0, c, parts);
509 for(t=1;t<=parts;t++) {
510 double nx = (double)(t*t*x3 + 2*t*(parts-t)*x2 + (parts-t)*(parts-t)*x1)/(double)(parts*parts);
511 double ny = (double)(t*t*y3 + 2*t*(parts-t)*y2 + (parts-t)*(parts-t)*y1)/(double)(parts*parts);
513 if(line->linestyle && ! p.clipdepth) {
514 lp.shapeline = &lshape->lines[line->linestyle-1];
515 add_solidline(dest, xx, yy, nx, ny, shape->linestyles[line->linestyle-1].width, &lp);
518 if(line->fillstyle0 || line->fillstyle1) {
519 assert(shape->numfillstyles);
520 add_line(dest, (int)xx, (int)yy, (int)nx, (int)ny, &p);
535 typedef struct _layer {
547 static RGBA color_red = {255,255,0,0};
548 static RGBA color_white = {255,255,255,255};
550 static void fill_plain(RGBA*line, int x1, int x2, RGBA col)
554 int ainv = 255-col.a;
555 col.r = (col.r*col.a)>>8;
556 col.g = (col.g*col.a)>>8;
557 col.b = (col.b*col.a)>>8;
560 line[x].r = ((line[x].r*ainv)>>8)+col.r;
561 line[x].g = ((line[x].g*ainv)>>8)+col.g;
562 line[x].b = ((line[x].b*ainv)>>8)+col.b;
572 static void fill_bitmap(RGBA*line, int y, int x1, int x2, MATRIX*m, bitmap_t*b, int clip)
575 double m11=m->sx/65536.0, m21=m->r1/65536.0;
576 double m12=m->r0/65536.0, m22=m->sy/65536.0;
577 double rx = m->tx/20.0;
578 double ry = m->ty/20.0;
579 double det = m11*m22 - m12*m21;
580 if(fabs(det) < 0.0005) {
581 /* x direction equals y direction- the image is invisible */
586 if(!b->width || !b->height) {
587 fill_plain(line, x1, x2, color_red);
593 int xx = (int)(( (x - rx) * m22 - (y - ry) * m21)*det);
594 int yy = (int)((- (x - rx) * m12 + (y - ry) * m11)*det);
599 if(xx>=b->width) xx = b->width-1;
601 if(yy>=b->height) yy = b->height-1;
607 col = b->data[yy*b->width+xx];
610 line[x].r = ((line[x].r*ainv)>>8)+col.r;
611 line[x].g = ((line[x].g*ainv)>>8)+col.g;
612 line[x].b = ((line[x].b*ainv)>>8)+col.b;
617 static void fill(RENDERBUF*dest, RGBA*line, int y, int x1, int x2, state_t*state)
619 renderbuf_internal*i = (renderbuf_internal*)dest->internal;
622 layer_t*l = state->layers;
624 if(x1>=x2) //zero width? nothing to do.
629 if(l->p->depth < clipdepth) {
630 if(DEBUG&2) printf("(clipped)");
634 if(l->fillid < 0 /*clip*/) {
635 if(DEBUG&2) printf("(add clip %d)", l->clipdepth);
636 if(l->clipdepth > clipdepth)
637 clipdepth = l->clipdepth;
638 } else if(l->fillid == 0) {
639 /* not filled. TODO: we should never add those in the first place */
641 printf("(not filled)");
642 } else if(l->fillid > l->p->s->shape->numfillstyles) {
643 fprintf(stderr, "Fill style out of bounds (%d>%d)", l->fillid, l->p->s->shape->numlinestyles);
647 printf("(%d -> %d style %d)", x1, x2, l->fillid);
649 f = &l->p->s->shape->fillstyles[l->fillid-1];
651 if(f->type == FILL_SOLID) {
652 /* plain color fill */
653 fill_plain(line, x1, x2, f->color);
654 } else if(f->type == FILL_TILED || f->type == FILL_CLIPPED) {
655 /* TODO: optimize (do this in add_pixel()?) */
656 bitmap_t* b = i->bitmaps;
657 while(b && b->id != f->id_bitmap) {
661 fprintf(stderr, "Shape references unknown bitmap %d\n", f->id_bitmap);
662 fill_plain(line, x1, x2, color_red);
664 //done in swf_RenderShape now
666 //m.tx -= dest->posx*20;
667 //m.ty -= dest->posy*20;
668 //m.sx *= i->multiply;
669 //m.sy *= i->multiply;
670 //m.r0 *= i->multiply;
671 //m.r1 *= i->multiply;
672 //m.tx *= i->multiply;
673 //m.ty *= i->multiply;
674 fill_bitmap(line, y, x1, x2, &f->m, b, FILL_CLIPPED?1:0);
682 static void search_layer(state_t*state, int depth, layer_t**before, layer_t**self, layer_t**after)
684 layer_t*last=0,*l = state->layers;
685 while(l && l->p->depth < depth) {
690 if(l && l->p->depth == depth)
695 static void delete_layer(state_t*state, layer_t*todel)
697 layer_t*before=todel->prev;
698 layer_t*next = todel->next;
701 state->layers = next;
707 before->next->prev = before;
710 static void add_layer(state_t*state, layer_t*before, layer_t*toadd)
713 toadd->next = state->layers;
717 toadd->next = before->next;
718 toadd->prev = before;
719 before->next = toadd;
722 toadd->next->prev = toadd;
724 static void free_layers(state_t* state)
726 layer_t*l = state->layers;
728 layer_t*next = l->next;
734 static void change_state(int y, state_t* state, renderpoint_t*p)
736 layer_t*before=0, *self=0, *after=0;
739 printf("[%s(%d,%d)/%d/%d-%d]", p->type==clip_type?"C":"F", p->x, y, p->depth, p->shapeline->fillstyle0, p->shapeline->fillstyle1);
742 search_layer(state, p->depth, &before, &self, &after);
746 if(self->fillid<0 || !p->shapeline->fillstyle0 || !p->shapeline->fillstyle1) {
747 /* filling/clipping ends */
748 if(DEBUG&2) printf("<D>");
750 delete_layer(state, self);
752 /*both fill0 and fill1 are set- exchange the two, updating the layer */
753 if(self->fillid == p->shapeline->fillstyle0) {
754 self->fillid = p->shapeline->fillstyle1;
757 if(DEBUG&2) printf("<X>");
758 } else if(self->fillid == p->shapeline->fillstyle1) {
759 self->fillid = p->shapeline->fillstyle0;
762 if(DEBUG&2) printf("<X>");
764 /* buggy shape. keep everything as-is. */
765 if(DEBUG&2) printf("<!>");
766 //fprintf(stderr, "<line %d: bad swap>\n", y);
772 if(p->shapeline && p->shapeline->fillstyle0 && p->shapeline->fillstyle1) {
773 /* this is a hack- a better way would be to make sure that
774 we always get (0,32), (32, 33), (33, 0) in the right order if
775 they happen to fall on the same pixel.
776 (not: (0,32), (33, 0), (32, 33))
778 fprintf(stderr, "<line %d: both fillstyles set while not inside shape>\n", y);
782 n = rfx_calloc(sizeof(layer_t));
784 if(DEBUG&2) printf("<+>");
786 if(p->type == clip_type) {
789 n->clipdepth = p->clipdepth;
792 n->fillid = p->shapeline->fillstyle0 ? p->shapeline->fillstyle0 : p->shapeline->fillstyle1;
797 add_layer(state, before, n);
801 RGBA* swf_Render(RENDERBUF*dest)
803 renderbuf_internal*i = (renderbuf_internal*)dest->internal;
804 RGBA* img = (RGBA*)rfx_alloc(sizeof(RGBA)*dest->width*dest->height);
807 RGBA * line1 = rfx_alloc(sizeof(RGBA)*i->width2);
808 RGBA * line2 = rfx_alloc(sizeof(RGBA)*i->width2);
810 for(y=0;y<i->height2;y++) {
811 TAG*tag = i->lines[y].points;
813 int size = sizeof(renderpoint_t);
814 int num = tag->len / size;
817 memset(&state, 0, sizeof(state_t));
819 if((y&1) && i->antialize)
824 memset(line, 0, sizeof(RGBA)*i->width2);
827 int xstep=i->background_width*65536/i->width2;
828 RGBA*src = &i->background[(i->background_height*y/i->height2)*i->background_width];
829 for(x=0,xx=0;x<i->width2;x++,xx+=xstep) {
830 line[x] = src[xx>>16];
833 memory += tag->memsize;
834 qsort(tag->data, num, size, compare_renderpoints);
836 renderpoint_t*p = (renderpoint_t*)&tag->data[size*n];
837 renderpoint_t*next= n<num-1?(renderpoint_t*)&tag->data[size*(n+1)]:0;
839 int endx = next?next->x:i->width2;
845 change_state(y, &state, p);
847 fill(dest, line, y, startx, endx, &state);
848 if(endx == i->width2)
852 if(DEBUG&2) printf("\n");
855 memcpy(&img[y*dest->width], line, sizeof(RGBA)*dest->width);
859 RGBA* p = &img[(y/2)*dest->width];
860 for(x=0;x<dest->width;x++) {
861 RGBA*p1 = &line1[x*2];
862 RGBA*p2 = &line1[x*2+1];
863 RGBA*p3 = &line2[x*2];
864 RGBA*p4 = &line2[x*2+1];
865 p[x].r = (p1->r + p2->r + p3->r + p4->r)/4;
866 p[x].g = (p1->g + p2->g + p3->g + p4->g)/4;
867 p[x].b = (p1->b + p2->b + p3->b + p4->b)/4;
868 p[x].a = (p1->a + p2->a + p3->a + p4->a)/4;
876 if(DEBUG) printf("\nMemory used: %d\n", memory);
878 if(DEBUG) printf("Statistics:\n");
879 if(DEBUG) printf("Average layer depth: %f\n", (double)layers/layernum);