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 */
25 typedef struct _renderpoint
27 enum {clip_type, fill_type} type;
37 typedef struct _renderline
39 TAG*points; //incremented in 128 byte steps
42 typedef struct _bitmap {
50 typedef struct _dummyshape
53 struct _dummyshape*next;
56 typedef struct _renderbuf_internal
64 dummyshape_t*dshapes_next;
66 int background_width, background_height;
71 static inline void add_pixel(RENDERBUF*dest, float x, int y, renderpoint_t*p)
73 renderbuf_internal*i = (renderbuf_internal*)dest->internal;
74 if(x >= i->width2 || y >= i->height2 || y<0) return;
77 swf_SetBlock(i->lines[y].points, (U8*)p, sizeof(renderpoint_t));
80 /* set this to 0.777777 or something if the "both fillstyles set while not inside shape"
81 problem appears to often */
84 static void add_line(RENDERBUF*buf, double x1, double y1, double x2, double y2, renderpoint_t*p, char thin)
86 renderbuf_internal*i = (renderbuf_internal*)buf->internal;
88 int l = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
89 printf(" l[%d - %.2f/%.2f -> %.2f/%.2f]", l, x1/20.0, y1/20.0, x2/20.0, y2/20.0);
103 double x = x1;x1 = x2;x2=x;
104 double y = y1;y1 = y2;y2=y;
107 double diffx = x2 - x1;
108 double diffy = y2 - y1;
110 double ny1 = (int)(y1)+CUT;
111 double ny2 = (int)(y2)+CUT;
114 ny1 = (int)(y1) + 1.0 + CUT;
117 ny2 = (int)(y2) - 1.0 + CUT;
123 double stepx = diffx/diffy;
124 x1 = x1 + (ny1-y1)*stepx;
125 x2 = x2 + (ny2-y2)*stepx;
133 float xx = (float)(startx + posx);
134 add_pixel(buf, xx ,posy, p);
139 #define PI 3.14159265358979
140 static void add_solidline(RENDERBUF*buf, double x1, double y1, double x2, double y2, int width, renderpoint_t*p)
142 renderbuf_internal*i = (renderbuf_internal*)buf->internal;
155 /* The Flash Player does this, too. This means every line is always at least
159 sd = (double)dx*(double)dx+(double)dy*(double)dy;
181 add_line(buf, x1+vx, y1+vy, xx, yy, p, 0);
184 for(t=1;t<segments;t++) {
185 double s = sin(t*PI/segments);
186 double c = cos(t*PI/segments);
187 xx = (x2 + vx*c - vy*s);
188 yy = (y2 + vx*s + vy*c);
189 add_line(buf, lastx, lasty, xx, yy, p, 0);
196 add_line(buf, lastx, lasty, xx, yy, p, 0);
201 add_line(buf, lastx, lasty, xx, yy, p, 0);
204 for(t=1;t<segments;t++) {
205 double s = sin(t*PI/segments);
206 double c = cos(t*PI/segments);
207 xx = (x1 - vx*c + vy*s);
208 yy = (y1 - vx*s - vy*c);
209 add_line(buf, lastx, lasty, xx, yy, p, 0);
213 add_line(buf, lastx, lasty, (x1+vx), (y1+vy), p, 0);
216 static inline void transform_point(MATRIX*m, int x, int y, int*dx, int*dy)
221 d = swf_TurnPoint(p, m);
226 static int compare_renderpoints(const void * _a, const void * _b)
228 renderpoint_t*a = (renderpoint_t*)_a;
229 renderpoint_t*b = (renderpoint_t*)_b;
230 if(a->fx < b->fx) return -1;
231 if(a->fx > b->fx) return 1;
235 void swf_Render_Init(RENDERBUF*buf, int posx, int posy, int width, int height, char antialize, int multiply)
237 renderbuf_internal*i;
239 memset(buf, 0, sizeof(RENDERBUF));
240 buf->width = width*multiply;
241 buf->height = height*multiply;
244 buf->internal = (renderbuf_internal*)rfx_calloc(sizeof(renderbuf_internal));
245 i = (renderbuf_internal*)buf->internal;
246 i->antialize = !!antialize;
247 i->multiply = antialize?multiply*2:multiply;
248 i->height2 = antialize?2*buf->height:buf->height;
249 i->width2 = antialize?2*buf->width:buf->width;
250 i->lines = (renderline_t*)rfx_alloc(i->height2*sizeof(renderline_t));
251 for(y=0;y<i->height2;y++) {
252 i->lines[y].points = swf_InsertTag(0, 0);
255 void swf_Render_SetBackground(RENDERBUF*buf, RGBA*img, int width, int height)
257 renderbuf_internal*i = (renderbuf_internal*)buf->internal;
258 RGBA*bck = (RGBA*)rfx_alloc(sizeof(RGBA)*width*height);
259 memcpy(bck, img, sizeof(RGBA)*width*height);
261 i->background_width = width;
262 i->background_height = height;
264 void swf_Render_SetBackgroundColor(RENDERBUF*buf, RGBA color)
266 swf_Render_SetBackground(buf, &color, 1, 1);
268 void swf_Render_AddImage(RENDERBUF*buf, U16 id, RGBA*img, int width, int height)
270 renderbuf_internal*i = (renderbuf_internal*)buf->internal;
272 bitmap_t*bm = rfx_calloc(sizeof(bitmap_t));
278 bm->next = i->bitmaps;
281 void swf_Render_ClearCanvas(RENDERBUF*dest)
283 renderbuf_internal*i = (renderbuf_internal*)dest->internal;
285 for(y=0;y<i->height2;y++) {
286 swf_ClearTag(i->lines[y].points);
289 void swf_Render_Delete(RENDERBUF*dest)
291 renderbuf_internal*i = (renderbuf_internal*)dest->internal;
293 bitmap_t*b = i->bitmaps;
294 dummyshape_t*d = i->dshapes;
297 free(i->background);i->background=0;
300 /* delete line buffers */
301 for(y=0;y<i->height2;y++) {
302 swf_DeleteTag(i->lines[y].points);
303 i->lines[y].points = 0;
307 dummyshape_t*next = d->next;
308 swf_Shape2Free(d->shape);
309 free(d->shape);d->shape=0;
317 bitmap_t*next = b->next;
318 //free(b->data);b->data=0;
323 rfx_free(i->lines); i->lines = 0;
324 rfx_free(dest->internal); dest->internal = 0;
327 void swf_RenderShape(RENDERBUF*dest, SHAPE2*shape, MATRIX*m, CXFORM*c, U16 _depth,U16 _clipdepth)
329 renderbuf_internal*i = (renderbuf_internal*)dest->internal;
331 SHAPELINE*line = shape->lines;
337 memset(&p, 0, sizeof(renderpoint_t));
338 memset(&lp, 0, sizeof(renderpoint_t));
339 p.type = _clipdepth?clip_type:fill_type;
341 p.depth = _depth << 16;
342 p.clipdepth = _clipdepth << 16;
343 mat.tx -= dest->posx*20;
344 mat.ty -= dest->posy*20;
346 if(shape->numlinestyles) {
347 dummyshape_t*dshape = rfx_calloc(sizeof(dummyshape_t));
348 lshape = rfx_calloc(sizeof(SHAPE2));
350 lshape->numfillstyles = shape->numlinestyles;
351 lshape->fillstyles = (FILLSTYLE*)rfx_calloc(sizeof(FILLSTYLE)*shape->numlinestyles);
352 lshape->lines = (SHAPELINE*)rfx_calloc(sizeof(SHAPELINE)*shape->numlinestyles);
353 for(t=0;t<shape->numlinestyles;t++) {
354 lshape->lines[t].fillstyle0 = t+1;
355 lshape->fillstyles[t].type = FILL_SOLID;
356 lshape->fillstyles[t].color = shape->linestyles[t].color;
360 lp.depth = p.depth+1;
362 /* add this shape to the global shape list, for deallocing */
363 dshape->shape = lshape;
364 i->dshapes_next = dshape;
373 add_line(dest, -20, 0, -20, i->height2*20, &p, 0);
378 int x1,y1,x2,y2,x3,y3;
382 if(line->type == moveTo) {
383 } else if(line->type == lineTo) {
389 int l = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
390 printf("%d - %.2f/%.2f -> %.2f/%.2f ", l, x1/20.0, y1/20.0, x2/20.0, y2/20.0);
393 transform_point(&mat, x, y, &x1, &y1);
394 transform_point(&mat, line->x, line->y, &x3, &y3);
396 if(line->linestyle && ! p.clipdepth) {
397 lp.shapeline = &lshape->lines[line->linestyle-1];
398 add_solidline(dest, x1, y1, x3, y3, shape->linestyles[line->linestyle-1].width, &lp);
401 if(line->fillstyle0 || line->fillstyle1)
402 add_line(dest, x1, y1, x3, y3, &p, 0);
404 if(DEBUG&4) printf("\n");
405 } else if(line->type == splineTo) {
407 transform_point(&mat, x, y, &x1, &y1);
408 transform_point(&mat, line->sx, line->sy, &x2, &y2);
409 transform_point(&mat, line->x, line->y, &x3, &y3);
411 int c = abs(x3-2*x2+x1) + abs(y3-2*y2+y1);
416 parts = (int)(sqrt(c)/3);
417 if(!parts) parts = 1;
421 printf("spline %.2f/%.2f -(%.2f/%.2f)-> %.2f/%.2f (c=%d, %d parts)",
424 x3/20.0, y3/20.0, c, parts);
427 for(t=1;t<=parts;t++) {
428 double nx = (double)(t*t*x3 + 2*t*(parts-t)*x2 + (parts-t)*(parts-t)*x1)/(double)(parts*parts);
429 double ny = (double)(t*t*y3 + 2*t*(parts-t)*y2 + (parts-t)*(parts-t)*y1)/(double)(parts*parts);
431 if(line->linestyle && ! p.clipdepth) {
432 lp.shapeline = &lshape->lines[line->linestyle-1];
433 add_solidline(dest, xx, yy, nx, ny, shape->linestyles[line->linestyle-1].width, &lp);
436 if(line->fillstyle0 || line->fillstyle1)
437 add_line(dest, (int)xx, (int)yy, (int)nx, (int)ny, &p, 0);
451 typedef struct _layer {
463 static RGBA color_red = {255,255,0,0};
464 static RGBA color_white = {255,255,255,255};
466 static void fill_plain(RGBA*line, int x1, int x2, RGBA col)
470 int ainv = 255-col.a;
471 col.r = (col.r*col.a)>>8;
472 col.g = (col.g*col.a)>>8;
473 col.b = (col.b*col.a)>>8;
476 line[x].r = ((line[x].r*ainv)>>8)+col.r;
477 line[x].g = ((line[x].g*ainv)>>8)+col.g;
478 line[x].b = ((line[x].b*ainv)>>8)+col.b;
488 static void fill_bitmap(RGBA*line, int y, int x1, int x2, MATRIX*m, bitmap_t*b, int clip)
491 double m11=m->sx/65536.0, m21=m->r1/65536.0;
492 double m12=m->r0/65536.0, m22=m->sy/65536.0;
493 double rx = m->tx/20.0;
494 double ry = m->ty/20.0;
495 double det = m11*m22 - m12*m21;
496 if(fabs(det) < 0.0005) {
497 /* x direction equals y direction- the image is invisible */
502 if(!b->width || !b->height) {
503 fill_plain(line, x1, x2, color_red);
508 int xx = (int)(( (x - rx) * m22 - (y - ry) * m21)*det);
509 int yy = (int)((- (x - rx) * m12 + (y - ry) * m11)*det);
513 if(xx>=b->width) xx = b->width-1;
515 if(yy>=b->height) yy = b->height-1;
521 RGBA col = b->data[yy*b->width+xx];
522 int ainv = 255-col.a;
524 line[x].r = ((line[x].r*ainv)>>8)+col.r;
525 line[x].g = ((line[x].g*ainv)>>8)+col.g;
526 line[x].b = ((line[x].b*ainv)>>8)+col.b;
531 static void fill(RENDERBUF*dest, RGBA*line, int y, int x1, int x2, state_t*state)
533 renderbuf_internal*i = (renderbuf_internal*)dest->internal;
535 layer_t*l = state->layers;
537 if(x1>=x2) //zero width? nothing to do.
542 if(l->p->depth < clipdepth) {
543 if(DEBUG&2) printf("(clipped)");
547 if(l->fillid < 0 /*clip*/) {
548 if(DEBUG&2) printf("(add clip %d)", l->clipdepth);
549 if(l->clipdepth > clipdepth)
550 clipdepth = l->clipdepth;
551 } else if(l->fillid == 0) {
552 /* not filled. TODO: we should never add those in the first place */
554 printf("(not filled)");
555 } else if(l->fillid > l->p->shape->numfillstyles) {
556 fprintf(stderr, "Fill style out of bounds (%d>%d)", l->fillid, l->p->shape->numlinestyles);
560 printf("(%d -> %d style %d)", x1, x2, l->fillid);
562 f = &l->p->shape->fillstyles[l->fillid-1];
564 if(f->type == FILL_SOLID) {
565 /* plain color fill */
566 fill_plain(line, x1, x2, f->color);
567 } else if(f->type == FILL_TILED || f->type == FILL_CLIPPED) {
568 /* TODO: optimize (do this in add_pixel()?) */
569 bitmap_t* b = i->bitmaps;
570 while(b && b->id != f->id_bitmap) {
574 fprintf(stderr, "Shape references unknown bitmap %d\n", f->id_bitmap);
575 fill_plain(line, x1, x2, color_red);
578 m.tx -= dest->posx*20;
579 m.ty -= dest->posy*20;
586 fill_bitmap(line, y, x1, x2, &m, b, FILL_CLIPPED?1:0);
594 static void search_layer(state_t*state, int depth, layer_t**before, layer_t**self, layer_t**after)
596 layer_t*last=0,*l = state->layers;
597 while(l && l->p->depth < depth) {
602 if(l && l->p->depth == depth)
607 static void delete_layer(state_t*state, layer_t*todel)
609 layer_t*before=todel->prev;
610 layer_t*next = todel->next;
613 state->layers = next;
619 before->next->prev = before;
622 static void add_layer(state_t*state, layer_t*before, layer_t*toadd)
625 toadd->next = state->layers;
629 toadd->next = before->next;
630 toadd->prev = before;
631 before->next = toadd;
634 toadd->next->prev = toadd;
636 static void free_layers(state_t* state)
638 layer_t*l = state->layers;
640 layer_t*next = l->next;
646 static void change_state(int y, state_t* state, renderpoint_t*p)
648 layer_t*before=0, *self=0, *after=0;
651 printf("[%s(%d,%d)/%d/%d-%d]", p->type==clip_type?"C":"F", p->x, y, p->depth, p->shapeline->fillstyle0, p->shapeline->fillstyle1);
654 search_layer(state, p->depth, &before, &self, &after);
658 if(self->fillid<0 || !p->shapeline->fillstyle0 || !p->shapeline->fillstyle1) {
659 /* filling/clipping ends */
660 if(DEBUG&2) printf("<D>");
662 delete_layer(state, self);
664 /*both fill0 and fill1 are set- exchange the two, updating the layer */
665 if(self->fillid == p->shapeline->fillstyle0) {
666 self->fillid = p->shapeline->fillstyle1;
669 if(DEBUG&2) printf("<X>");
670 } else if(self->fillid == p->shapeline->fillstyle1) {
671 self->fillid = p->shapeline->fillstyle0;
674 if(DEBUG&2) printf("<X>");
676 /* buggy shape. keep everything as-is. */
677 if(DEBUG&2) printf("<!>");
678 //fprintf(stderr, "<line %d: bad swap>\n", y);
684 if(p->shapeline && p->shapeline->fillstyle0 && p->shapeline->fillstyle1) {
685 /* this is a hack- a better way would be to make sure that
686 we always get (0,32), (32, 33), (33, 0) in the right order if
687 they happen to fall on the same pixel.
688 (not: (0,32), (33, 0), (32, 33))
690 fprintf(stderr, "<line %d: both fillstyles set while not inside shape>\n", y);
694 n = rfx_calloc(sizeof(layer_t));
696 if(DEBUG&2) printf("<+>");
698 if(p->type == clip_type) {
701 n->clipdepth = p->clipdepth;
704 n->fillid = p->shapeline->fillstyle0 ? p->shapeline->fillstyle0 : p->shapeline->fillstyle1;
709 add_layer(state, before, n);
713 RGBA* swf_Render(RENDERBUF*dest)
715 renderbuf_internal*i = (renderbuf_internal*)dest->internal;
716 RGBA* img = (RGBA*)rfx_alloc(sizeof(RGBA)*dest->width*dest->height);
719 RGBA * line1 = rfx_alloc(sizeof(RGBA)*i->width2);
720 RGBA * line2 = rfx_alloc(sizeof(RGBA)*i->width2);
722 for(y=0;y<i->height2;y++) {
723 TAG*tag = i->lines[y].points;
725 int size = sizeof(renderpoint_t);
726 int num = tag->len / size;
728 if((y&1) && i->antialize)
732 memset(&state, 0, sizeof(state_t));
735 memset(line, 0, sizeof(RGBA)*i->width2);
738 int xstep=i->background_width*65536/i->width2;
739 RGBA*src = &i->background[(i->background_height*y/i->height2)*i->background_width];
740 for(x=0,xx=0;x<i->width2;x++,xx+=xstep) {
741 line[x] = src[xx>>16];
744 memory += tag->memsize;
745 qsort(tag->data, num, size, compare_renderpoints);
747 renderpoint_t*p = (renderpoint_t*)&tag->data[size*n];
748 renderpoint_t*next= n<num-1?(renderpoint_t*)&tag->data[size*(n+1)]:0;
750 int endx = next?next->x:i->width2;
756 change_state(y, &state, p);
758 fill(dest, line, y, startx, endx, &state);
759 if(endx == i->width2)
763 if(DEBUG&2) printf("\n");
766 memcpy(&img[y*dest->width], line, sizeof(RGBA)*dest->width);
770 RGBA* p = &img[(y/2)*dest->width];
771 for(x=0;x<dest->width;x++) {
772 RGBA*p1 = &line1[x*2];
773 RGBA*p2 = &line1[x*2+1];
774 RGBA*p3 = &line2[x*2];
775 RGBA*p4 = &line2[x*2+1];
776 p[x].r = (p1->r + p2->r + p3->r + p4->r)/4;
777 p[x].g = (p1->g + p2->g + p3->g + p4->g)/4;
778 p[x].b = (p1->b + p2->b + p3->b + p4->b)/4;
779 p[x].a = (p1->a + p2->a + p3->a + p4->a)/4;
787 if(DEBUG) printf("\nMemory used: %d\n", memory);
789 if(DEBUG) printf("Statistics:\n");
790 if(DEBUG) printf("Average layer depth: %f\n", (double)layers/layernum);