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 int l = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
114 printf(" l[%d - %.2f/%.2f -> %.2f/%.2f]", l, x1/20.0, y1/20.0, x2/20.0, y2/20.0);
128 double x = x1;x1 = x2;x2=x;
129 double y = y1;y1 = y2;y2=y;
132 double diffx = x2 - x1;
133 double diffy = y2 - y1;
135 double ny1 = (int)(y1)+CUT;
136 double ny2 = (int)(y2)+CUT;
139 ny1 = (int)(y1) + 1.0 + CUT;
142 ny2 = (int)(y2) - 1.0 + CUT;
148 double stepx = diffx/diffy;
149 x1 = x1 + (ny1-y1)*stepx;
150 x2 = x2 + (ny2-y2)*stepx;
158 float xx = (float)(startx + posx);
159 add_pixel(buf, xx ,posy, p);
164 #define PI 3.14159265358979
165 static void add_solidline(RENDERBUF*buf, double x1, double y1, double x2, double y2, int width, renderpoint_t*p)
167 renderbuf_internal*i = (renderbuf_internal*)buf->internal;
180 /* The Flash Player does this, too. This means every line is always at least
184 sd = (double)dx*(double)dx+(double)dy*(double)dy;
206 add_line(buf, x1+vx, y1+vy, xx, yy, p);
209 for(t=1;t<segments;t++) {
210 double s = sin(t*PI/segments);
211 double c = cos(t*PI/segments);
212 xx = (x2 + vx*c - vy*s);
213 yy = (y2 + vx*s + vy*c);
214 add_line(buf, lastx, lasty, xx, yy, p);
221 add_line(buf, lastx, lasty, xx, yy, p);
226 add_line(buf, lastx, lasty, xx, yy, p);
229 for(t=1;t<segments;t++) {
230 double s = sin(t*PI/segments);
231 double c = cos(t*PI/segments);
232 xx = (x1 - vx*c + vy*s);
233 yy = (y1 - vx*s - vy*c);
234 add_line(buf, lastx, lasty, xx, yy, p);
238 add_line(buf, lastx, lasty, (x1+vx), (y1+vy), p);
241 static inline void transform_point(MATRIX*m, int x, int y, int*dx, int*dy)
246 d = swf_TurnPoint(p, m);
251 static int compare_renderpoints(const void * _a, const void * _b)
253 renderpoint_t*a = (renderpoint_t*)_a;
254 renderpoint_t*b = (renderpoint_t*)_b;
255 if(a->fx < b->fx) return -1;
256 if(a->fx > b->fx) return 1;
260 void swf_Render_Init(RENDERBUF*buf, int posx, int posy, int width, int height, char antialize, int multiply)
262 renderbuf_internal*i;
264 memset(buf, 0, sizeof(RENDERBUF));
265 buf->width = width*multiply;
266 buf->height = height*multiply;
269 buf->internal = (renderbuf_internal*)rfx_calloc(sizeof(renderbuf_internal));
270 i = (renderbuf_internal*)buf->internal;
271 i->antialize = !!antialize;
272 i->multiply = antialize?multiply*2:multiply;
273 i->height2 = antialize?2*buf->height:buf->height;
274 i->width2 = antialize?2*buf->width:buf->width;
275 i->lines = (renderline_t*)rfx_alloc(i->height2*sizeof(renderline_t));
276 for(y=0;y<i->height2;y++) {
277 i->lines[y].points = swf_InsertTag(0, 0);
280 void swf_Render_SetBackground(RENDERBUF*buf, RGBA*img, int width, int height)
282 renderbuf_internal*i = (renderbuf_internal*)buf->internal;
283 RGBA*bck = (RGBA*)rfx_alloc(sizeof(RGBA)*width*height);
284 memcpy(bck, img, sizeof(RGBA)*width*height);
286 i->background_width = width;
287 i->background_height = height;
289 void swf_Render_SetBackgroundColor(RENDERBUF*buf, RGBA color)
291 swf_Render_SetBackground(buf, &color, 1, 1);
293 void swf_Render_AddImage(RENDERBUF*buf, U16 id, RGBA*img, int width, int height)
295 renderbuf_internal*i = (renderbuf_internal*)buf->internal;
297 bitmap_t*bm = rfx_calloc(sizeof(bitmap_t));
303 bm->next = i->bitmaps;
306 void swf_Render_ClearCanvas(RENDERBUF*dest)
308 renderbuf_internal*i = (renderbuf_internal*)dest->internal;
310 for(y=0;y<i->height2;y++) {
311 swf_ClearTag(i->lines[y].points);
314 void swf_Render_Delete(RENDERBUF*dest)
316 renderbuf_internal*i = (renderbuf_internal*)dest->internal;
318 bitmap_t*b = i->bitmaps;
319 dummyshape_t*d = i->dshapes;
322 free(i->background);i->background=0;
325 /* delete line buffers */
326 for(y=0;y<i->height2;y++) {
327 swf_DeleteTag(i->lines[y].points);
328 i->lines[y].points = 0;
332 dummyshape_t*next = d->next;
333 swf_Shape2Free(d->shape);
334 free(d->shape);d->shape=0;
342 bitmap_t*next = b->next;
343 //free(b->data);b->data=0;
348 rfx_free(i->lines); i->lines = 0;
349 rfx_free(dest->internal); dest->internal = 0;
352 static void swf_Render_AddShape(RENDERBUF*dest,dummyshape_t*s)
354 renderbuf_internal*i = (renderbuf_internal*)dest->internal;
358 i->dshapes_next->next = s;
365 static SHAPE2* linestyle2fillstyle(SHAPE2*shape)
367 SHAPE2*s = rfx_calloc(sizeof(SHAPE2));
369 s->numfillstyles = shape->numlinestyles;
370 s->fillstyles = (FILLSTYLE*)rfx_calloc(sizeof(FILLSTYLE)*shape->numlinestyles);
371 s->lines = (SHAPELINE*)rfx_calloc(sizeof(SHAPELINE)*shape->numlinestyles);
372 for(t=0;t<shape->numlinestyles;t++) {
373 s->lines[t].fillstyle0 = t+1;
374 s->fillstyles[t].type = FILL_SOLID;
375 s->fillstyles[t].color = shape->linestyles[t].color;
380 void swf_RenderShape(RENDERBUF*dest, SHAPE2*shape, MATRIX*m, CXFORM*c, U16 _depth,U16 _clipdepth)
382 renderbuf_internal*i = (renderbuf_internal*)dest->internal;
384 SHAPELINE*line = shape->lines;
390 memset(&p, 0, sizeof(renderpoint_t));
391 memset(&lp, 0, sizeof(renderpoint_t));
393 p.type = _clipdepth?clip_type:fill_type;
394 p.depth = _depth << 16;
395 p.clipdepth = _clipdepth << 16;
397 mat.tx -= dest->posx*20;
398 mat.ty -= dest->posy*20;
400 if(shape->numfillstyles) {
401 dummyshape_t*fshape = rfx_calloc(sizeof(dummyshape_t));
403 SHAPE2* s2 = swf_Shape2Clone(shape);
409 /* multiply fillstyles matrices with placement matrix-
410 important for texture and gradient fill */
411 for(t=0;t<s2->numfillstyles;t++) {
413 swf_MatrixJoin(&nm, &s2->fillstyles[t].m, m); //TODO: is this the right order?
414 nm.sx *= i->multiply;
415 nm.sy *= i->multiply;
416 nm.r0 *= i->multiply;
417 nm.r1 *= i->multiply;
418 nm.tx *= i->multiply;
419 nm.ty *= i->multiply;
420 s2->fillstyles[t].m = nm;
423 /* add this shape to the global shape list, for deallocing */
424 swf_Render_AddShape(dest, fshape);
427 if(shape->numlinestyles) {
428 dummyshape_t*dshape = rfx_calloc(sizeof(dummyshape_t));
430 lshape = linestyle2fillstyle(shape);
434 lp.depth = (_depth << 16)+1;
436 dshape->shape = lshape;
438 /* add this shape to the global shape list, for deallocing */
439 swf_Render_AddShape(dest, dshape);
445 add_line(dest, -20, 0, -20, i->height2*20, &p);
450 int x1,y1,x2,y2,x3,y3;
454 if(line->type == moveTo) {
455 } else if(line->type == lineTo) {
461 int l = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
462 printf("%d - %.2f/%.2f -> %.2f/%.2f ", l, x1/20.0, y1/20.0, x2/20.0, y2/20.0);
465 transform_point(&mat, x, y, &x1, &y1);
466 transform_point(&mat, line->x, line->y, &x3, &y3);
468 if(line->linestyle && ! p.clipdepth) {
469 lp.shapeline = &lshape->lines[line->linestyle-1];
470 add_solidline(dest, x1, y1, x3, y3, shape->linestyles[line->linestyle-1].width, &lp);
473 if(line->fillstyle0 || line->fillstyle1) {
474 assert(shape->numfillstyles);
475 add_line(dest, x1, y1, x3, y3, &p);
478 if(DEBUG&4) printf("\n");
479 } else if(line->type == splineTo) {
481 transform_point(&mat, x, y, &x1, &y1);
482 transform_point(&mat, line->sx, line->sy, &x2, &y2);
483 transform_point(&mat, line->x, line->y, &x3, &y3);
485 int c = abs(x3-2*x2+x1) + abs(y3-2*y2+y1);
490 parts = (int)(sqrt(c)/3);
491 if(!parts) parts = 1;
495 printf("spline %.2f/%.2f -(%.2f/%.2f)-> %.2f/%.2f (c=%d, %d parts)",
498 x3/20.0, y3/20.0, c, parts);
501 for(t=1;t<=parts;t++) {
502 double nx = (double)(t*t*x3 + 2*t*(parts-t)*x2 + (parts-t)*(parts-t)*x1)/(double)(parts*parts);
503 double ny = (double)(t*t*y3 + 2*t*(parts-t)*y2 + (parts-t)*(parts-t)*y1)/(double)(parts*parts);
505 if(line->linestyle && ! p.clipdepth) {
506 lp.shapeline = &lshape->lines[line->linestyle-1];
507 add_solidline(dest, xx, yy, nx, ny, shape->linestyles[line->linestyle-1].width, &lp);
510 if(line->fillstyle0 || line->fillstyle1) {
511 assert(shape->numfillstyles);
512 add_line(dest, (int)xx, (int)yy, (int)nx, (int)ny, &p);
527 typedef struct _layer {
539 static RGBA color_red = {255,255,0,0};
540 static RGBA color_white = {255,255,255,255};
542 static void fill_plain(RGBA*line, int x1, int x2, RGBA col)
546 int ainv = 255-col.a;
547 col.r = (col.r*col.a)>>8;
548 col.g = (col.g*col.a)>>8;
549 col.b = (col.b*col.a)>>8;
552 line[x].r = ((line[x].r*ainv)>>8)+col.r;
553 line[x].g = ((line[x].g*ainv)>>8)+col.g;
554 line[x].b = ((line[x].b*ainv)>>8)+col.b;
564 static void fill_bitmap(RGBA*line, int y, int x1, int x2, MATRIX*m, bitmap_t*b, int clip)
567 double m11=m->sx/65536.0, m21=m->r1/65536.0;
568 double m12=m->r0/65536.0, m22=m->sy/65536.0;
569 double rx = m->tx/20.0;
570 double ry = m->ty/20.0;
571 double det = m11*m22 - m12*m21;
572 if(fabs(det) < 0.0005) {
573 /* x direction equals y direction- the image is invisible */
578 if(!b->width || !b->height) {
579 fill_plain(line, x1, x2, color_red);
584 int xx = (int)(( (x - rx) * m22 - (y - ry) * m21)*det);
585 int yy = (int)((- (x - rx) * m12 + (y - ry) * m11)*det);
589 if(xx>=b->width) xx = b->width-1;
591 if(yy>=b->height) yy = b->height-1;
597 RGBA col = b->data[yy*b->width+xx];
598 int ainv = 255-col.a;
600 line[x].r = ((line[x].r*ainv)>>8)+col.r;
601 line[x].g = ((line[x].g*ainv)>>8)+col.g;
602 line[x].b = ((line[x].b*ainv)>>8)+col.b;
607 static void fill(RENDERBUF*dest, RGBA*line, int y, int x1, int x2, state_t*state)
609 renderbuf_internal*i = (renderbuf_internal*)dest->internal;
611 layer_t*l = state->layers;
613 if(x1>=x2) //zero width? nothing to do.
618 if(l->p->depth < clipdepth) {
619 if(DEBUG&2) printf("(clipped)");
623 if(l->fillid < 0 /*clip*/) {
624 if(DEBUG&2) printf("(add clip %d)", l->clipdepth);
625 if(l->clipdepth > clipdepth)
626 clipdepth = l->clipdepth;
627 } else if(l->fillid == 0) {
628 /* not filled. TODO: we should never add those in the first place */
630 printf("(not filled)");
631 } else if(l->fillid > l->p->s->shape->numfillstyles) {
632 fprintf(stderr, "Fill style out of bounds (%d>%d)", l->fillid, l->p->s->shape->numlinestyles);
636 printf("(%d -> %d style %d)", x1, x2, l->fillid);
638 f = &l->p->s->shape->fillstyles[l->fillid-1];
640 if(f->type == FILL_SOLID) {
641 /* plain color fill */
642 fill_plain(line, x1, x2, f->color);
643 } else if(f->type == FILL_TILED || f->type == FILL_CLIPPED) {
644 /* TODO: optimize (do this in add_pixel()?) */
645 bitmap_t* b = i->bitmaps;
646 while(b && b->id != f->id_bitmap) {
650 fprintf(stderr, "Shape references unknown bitmap %d\n", f->id_bitmap);
651 fill_plain(line, x1, x2, color_red);
653 //done in swf_RenderShape now
655 //m.tx -= dest->posx*20;
656 //m.ty -= dest->posy*20;
657 //m.sx *= i->multiply;
658 //m.sy *= i->multiply;
659 //m.r0 *= i->multiply;
660 //m.r1 *= i->multiply;
661 //m.tx *= i->multiply;
662 //m.ty *= i->multiply;
663 fill_bitmap(line, y, x1, x2, &f->m, b, FILL_CLIPPED?1:0);
671 static void search_layer(state_t*state, int depth, layer_t**before, layer_t**self, layer_t**after)
673 layer_t*last=0,*l = state->layers;
674 while(l && l->p->depth < depth) {
679 if(l && l->p->depth == depth)
684 static void delete_layer(state_t*state, layer_t*todel)
686 layer_t*before=todel->prev;
687 layer_t*next = todel->next;
690 state->layers = next;
696 before->next->prev = before;
699 static void add_layer(state_t*state, layer_t*before, layer_t*toadd)
702 toadd->next = state->layers;
706 toadd->next = before->next;
707 toadd->prev = before;
708 before->next = toadd;
711 toadd->next->prev = toadd;
713 static void free_layers(state_t* state)
715 layer_t*l = state->layers;
717 layer_t*next = l->next;
723 static void change_state(int y, state_t* state, renderpoint_t*p)
725 layer_t*before=0, *self=0, *after=0;
728 printf("[%s(%d,%d)/%d/%d-%d]", p->type==clip_type?"C":"F", p->x, y, p->depth, p->shapeline->fillstyle0, p->shapeline->fillstyle1);
731 search_layer(state, p->depth, &before, &self, &after);
735 if(self->fillid<0 || !p->shapeline->fillstyle0 || !p->shapeline->fillstyle1) {
736 /* filling/clipping ends */
737 if(DEBUG&2) printf("<D>");
739 delete_layer(state, self);
741 /*both fill0 and fill1 are set- exchange the two, updating the layer */
742 if(self->fillid == p->shapeline->fillstyle0) {
743 self->fillid = p->shapeline->fillstyle1;
746 if(DEBUG&2) printf("<X>");
747 } else if(self->fillid == p->shapeline->fillstyle1) {
748 self->fillid = p->shapeline->fillstyle0;
751 if(DEBUG&2) printf("<X>");
753 /* buggy shape. keep everything as-is. */
754 if(DEBUG&2) printf("<!>");
755 //fprintf(stderr, "<line %d: bad swap>\n", y);
761 if(p->shapeline && p->shapeline->fillstyle0 && p->shapeline->fillstyle1) {
762 /* this is a hack- a better way would be to make sure that
763 we always get (0,32), (32, 33), (33, 0) in the right order if
764 they happen to fall on the same pixel.
765 (not: (0,32), (33, 0), (32, 33))
767 fprintf(stderr, "<line %d: both fillstyles set while not inside shape>\n", y);
771 n = rfx_calloc(sizeof(layer_t));
773 if(DEBUG&2) printf("<+>");
775 if(p->type == clip_type) {
778 n->clipdepth = p->clipdepth;
781 n->fillid = p->shapeline->fillstyle0 ? p->shapeline->fillstyle0 : p->shapeline->fillstyle1;
786 add_layer(state, before, n);
790 RGBA* swf_Render(RENDERBUF*dest)
792 renderbuf_internal*i = (renderbuf_internal*)dest->internal;
793 RGBA* img = (RGBA*)rfx_alloc(sizeof(RGBA)*dest->width*dest->height);
796 RGBA * line1 = rfx_alloc(sizeof(RGBA)*i->width2);
797 RGBA * line2 = rfx_alloc(sizeof(RGBA)*i->width2);
799 for(y=0;y<i->height2;y++) {
800 TAG*tag = i->lines[y].points;
802 int size = sizeof(renderpoint_t);
803 int num = tag->len / size;
805 if((y&1) && i->antialize)
809 memset(&state, 0, sizeof(state_t));
812 memset(line, 0, sizeof(RGBA)*i->width2);
815 int xstep=i->background_width*65536/i->width2;
816 RGBA*src = &i->background[(i->background_height*y/i->height2)*i->background_width];
817 for(x=0,xx=0;x<i->width2;x++,xx+=xstep) {
818 line[x] = src[xx>>16];
821 memory += tag->memsize;
822 qsort(tag->data, num, size, compare_renderpoints);
824 renderpoint_t*p = (renderpoint_t*)&tag->data[size*n];
825 renderpoint_t*next= n<num-1?(renderpoint_t*)&tag->data[size*(n+1)]:0;
827 int endx = next?next->x:i->width2;
833 change_state(y, &state, p);
835 fill(dest, line, y, startx, endx, &state);
836 if(endx == i->width2)
840 if(DEBUG&2) printf("\n");
843 memcpy(&img[y*dest->width], line, sizeof(RGBA)*dest->width);
847 RGBA* p = &img[(y/2)*dest->width];
848 for(x=0;x<dest->width;x++) {
849 RGBA*p1 = &line1[x*2];
850 RGBA*p2 = &line1[x*2+1];
851 RGBA*p3 = &line2[x*2];
852 RGBA*p4 = &line2[x*2+1];
853 p[x].r = (p1->r + p2->r + p3->r + p4->r)/4;
854 p[x].g = (p1->g + p2->g + p3->g + p4->g)/4;
855 p[x].b = (p1->b + p2->b + p3->b + p4->b)/4;
856 p[x].a = (p1->a + p2->a + p3->a + p4->a)/4;
864 if(DEBUG) printf("\nMemory used: %d\n", memory);
866 if(DEBUG) printf("Statistics:\n");
867 if(DEBUG) printf("Average layer depth: %f\n", (double)layers/layernum);