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 _renderbuf_internal
61 static inline void add_pixel(RENDERBUF*dest, float x, int y, renderpoint_t*p)
63 renderbuf_internal*i = (renderbuf_internal*)dest->internal;
64 if(x >= i->width2 || y >= i->height2 || y<0) return;
67 swf_SetBlock(i->lines[y].points, (U8*)p, sizeof(renderpoint_t));
70 /* set this to 0.777777 or something if the "both fillstyles set while not inside shape"
71 problem appears to often */
74 static void add_line(RENDERBUF*buf, double x1, double y1, double x2, double y2, renderpoint_t*p, char thin)
76 renderbuf_internal*i = (renderbuf_internal*)buf->internal;
78 int l = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
79 printf(" l[%d - %.2f/%.2f -> %.2f/%.2f]", l, x1/20.0, y1/20.0, x2/20.0, y2/20.0);
93 double x = x1;x1 = x2;x2=x;
94 double y = y1;y1 = y2;y2=y;
97 double diffx = x2 - x1;
98 double diffy = y2 - y1;
100 double ny1 = (int)(y1)+CUT;
101 double ny2 = (int)(y2)+CUT;
104 ny1 = (int)(y1) + 1.0 + CUT;
107 ny2 = (int)(y2) - 1.0 + CUT;
113 double stepx = diffx/diffy;
114 x1 = x1 + (ny1-y1)*stepx;
115 x2 = x2 + (ny2-y2)*stepx;
123 float xx = (float)(startx + posx);
124 add_pixel(buf, xx ,posy, p);
129 #define PI 3.14159265358979
130 static void add_solidline(RENDERBUF*buf, double x1, double y1, double x2, double y2, int width, renderpoint_t*p)
132 renderbuf_internal*i = (renderbuf_internal*)buf->internal;
145 /* The Flash Player does this, too. This means every line is always at least
149 sd = (double)dx*(double)dx+(double)dy*(double)dy;
171 add_line(buf, x1+vx, y1+vy, xx, yy, p, 0);
174 for(t=1;t<segments;t++) {
175 double s = sin(t*PI/segments);
176 double c = cos(t*PI/segments);
177 xx = (x2 + vx*c - vy*s);
178 yy = (y2 + vx*s + vy*c);
179 add_line(buf, lastx, lasty, xx, yy, p, 0);
186 add_line(buf, lastx, lasty, xx, yy, p, 0);
191 add_line(buf, lastx, lasty, xx, yy, p, 0);
194 for(t=1;t<segments;t++) {
195 double s = sin(t*PI/segments);
196 double c = cos(t*PI/segments);
197 xx = (x1 - vx*c + vy*s);
198 yy = (y1 - vx*s - vy*c);
199 add_line(buf, lastx, lasty, xx, yy, p, 0);
203 add_line(buf, lastx, lasty, (x1+vx), (y1+vy), p, 0);
206 static inline void transform_point(MATRIX*m, int x, int y, int*dx, int*dy)
211 d = swf_TurnPoint(p, m);
216 static int compare_renderpoints(const void * _a, const void * _b)
218 renderpoint_t*a = (renderpoint_t*)_a;
219 renderpoint_t*b = (renderpoint_t*)_b;
220 if(a->fx < b->fx) return -1;
221 if(a->fx > b->fx) return 1;
225 void swf_Render_Init(RENDERBUF*buf, int posx, int posy, int width, int height, char antialize, int multiply)
227 renderbuf_internal*i;
229 memset(buf, 0, sizeof(RENDERBUF));
230 buf->width = width*multiply;
231 buf->height = height*multiply;
234 buf->internal = (renderbuf_internal*)rfx_calloc(sizeof(renderbuf_internal));
235 i = (renderbuf_internal*)buf->internal;
236 i->antialize = antialize;
237 i->multiply = antialize?multiply*2:multiply;
238 i->height2 = antialize?2*buf->height:buf->height;
239 i->width2 = antialize?2*buf->width:buf->width;
240 i->lines = (renderline_t*)rfx_alloc(i->height2*sizeof(renderline_t));
241 for(y=0;y<i->height2;y++) {
242 i->lines[y].points = swf_InsertTag(0, 0);
245 void swf_Render_AddImage(RENDERBUF*buf, U16 id, RGBA*img, int width, int height)
247 renderbuf_internal*i = (renderbuf_internal*)buf->internal;
249 bitmap_t*bm = rfx_calloc(sizeof(bitmap_t));
255 bm->next = i->bitmaps;
258 void swf_Render_ClearCanvas(RENDERBUF*dest)
260 renderbuf_internal*i = (renderbuf_internal*)dest->internal;
262 for(y=0;y<i->height2;y++) {
263 swf_ClearTag(i->lines[y].points);
266 void swf_Render_Delete(RENDERBUF*dest)
268 renderbuf_internal*i = (renderbuf_internal*)dest->internal;
270 bitmap_t*b = i->bitmaps;
272 /* delete line buffers */
273 for(y=0;y<i->height2;y++) {
274 swf_DeleteTag(i->lines[y].points);
275 i->lines[y].points = 0;
280 bitmap_t*next = b->next;
281 //free(b->data);b->data=0;
286 rfx_free(i->lines); i->lines = 0;
287 rfx_free(dest->internal); dest->internal = 0;
290 void swf_RenderShape(RENDERBUF*dest, SHAPE2*shape, MATRIX*m, CXFORM*c, U16 _depth,U16 _clipdepth)
292 renderbuf_internal*i = (renderbuf_internal*)dest->internal;
294 SHAPELINE*line = shape->lines;
300 memset(&p, 0, sizeof(renderpoint_t));
301 memset(&lp, 0, sizeof(renderpoint_t));
302 p.type = _clipdepth?clip_type:fill_type;
304 p.depth = _depth << 16;
305 p.clipdepth = _clipdepth << 16;
306 mat.tx -= dest->posx*20;
307 mat.ty -= dest->posy*20;
309 if(shape->numlinestyles) {
310 /* TODO: free this again */
311 lshape = rfx_calloc(sizeof(SHAPE2));
313 lshape->numfillstyles = shape->numlinestyles;
314 lshape->fillstyles = (FILLSTYLE*)rfx_calloc(sizeof(FILLSTYLE)*shape->numlinestyles);
315 lshape->lines = (SHAPELINE*)rfx_calloc(sizeof(SHAPELINE)*shape->numlinestyles);
316 for(t=0;t<shape->numlinestyles;t++) {
317 lshape->lines[t].fillstyle0 = t+1;
318 lshape->fillstyles[t].type = FILL_SOLID;
319 lshape->fillstyles[t].color = shape->linestyles[t].color;
323 lp.depth = p.depth+1;
329 add_line(dest, -20, 0, -20, i->height2*20, &p, 0);
334 int x1,y1,x2,y2,x3,y3;
338 if(line->type == moveTo) {
339 } else if(line->type == lineTo) {
345 int l = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
346 printf("%d - %.2f/%.2f -> %.2f/%.2f ", l, x1/20.0, y1/20.0, x2/20.0, y2/20.0);
349 transform_point(&mat, x, y, &x1, &y1);
350 transform_point(&mat, line->x, line->y, &x3, &y3);
352 if(line->linestyle && ! p.clipdepth) {
353 lp.shapeline = &lshape->lines[line->linestyle-1];
354 add_solidline(dest, x1, y1, x3, y3, shape->linestyles[line->linestyle-1].width, &lp);
357 if(line->fillstyle0 || line->fillstyle1)
358 add_line(dest, x1, y1, x3, y3, &p, 0);
360 if(DEBUG&4) printf("\n");
361 } else if(line->type == splineTo) {
363 transform_point(&mat, x, y, &x1, &y1);
364 transform_point(&mat, line->sx, line->sy, &x2, &y2);
365 transform_point(&mat, line->x, line->y, &x3, &y3);
367 int c = abs(x3-2*x2+x1) + abs(y3-2*y2+y1);
372 parts = (int)(sqrt(c)/3);
373 if(!parts) parts = 1;
377 printf("spline %.2f/%.2f -(%.2f/%.2f)-> %.2f/%.2f (c=%d, %d parts)",
380 x3/20.0, y3/20.0, c, parts);
383 for(t=1;t<=parts;t++) {
384 double nx = (double)(t*t*x3 + 2*t*(parts-t)*x2 + (parts-t)*(parts-t)*x1)/(double)(parts*parts);
385 double ny = (double)(t*t*y3 + 2*t*(parts-t)*y2 + (parts-t)*(parts-t)*y1)/(double)(parts*parts);
387 if(line->linestyle && ! p.clipdepth) {
388 lp.shapeline = &lshape->lines[line->linestyle-1];
389 add_solidline(dest, xx, yy, nx, ny, shape->linestyles[line->linestyle-1].width, &lp);
392 if(line->fillstyle0 || line->fillstyle1)
393 add_line(dest, (int)xx, (int)yy, (int)nx, (int)ny, &p, 0);
407 typedef struct _layer {
419 static RGBA color_red = {255,255,0,0};
420 static RGBA color_white = {255,255,255,255};
422 static void fill_plain(RGBA*line, int x1, int x2, RGBA col)
426 int ainv = 255-col.a;
427 col.r = (col.r*col.a)>>8;
428 col.g = (col.g*col.a)>>8;
429 col.b = (col.b*col.a)>>8;
432 line[x].r = ((line[x].r*ainv)>>8)+col.r;
433 line[x].g = ((line[x].g*ainv)>>8)+col.g;
434 line[x].b = ((line[x].b*ainv)>>8)+col.b;
444 static void fill_bitmap(RGBA*line, int y, int x1, int x2, MATRIX*m, bitmap_t*b, int clip)
447 double m11=m->sx/65536.0, m21=m->r1/65536.0;
448 double m12=m->r0/65536.0, m22=m->sy/65536.0;
449 double rx = m->tx/20.0;
450 double ry = m->ty/20.0;
451 double det = m11*m22 - m12*m21;
452 if(fabs(det) < 0.0005) {
453 /* x direction equals y direction- the image is invisible */
459 int xx = (int)(( (x - rx) * m22 - (y - ry) * m21)*det);
460 int yy = (int)((- (x - rx) * m12 + (y - ry) * m11)*det);
463 if(xx<0 || xx>=b->width || yy<0 || yy>=b->height) {
464 //line[x] = color_red;
472 RGBA col = b->data[yy*b->width+xx];
473 int ainv = 255-col.a;
475 line[x].r = ((line[x].r*ainv)>>8)+col.r;
476 line[x].g = ((line[x].g*ainv)>>8)+col.g;
477 line[x].b = ((line[x].b*ainv)>>8)+col.b;
482 static void fill(RENDERBUF*dest, RGBA*line, int y, int x1, int x2, state_t*state)
484 renderbuf_internal*i = (renderbuf_internal*)dest->internal;
486 layer_t*l = state->layers;
488 if(x1>=x2) //zero width? nothing to do.
493 if(l->p->depth < clipdepth) {
494 if(DEBUG&2) printf("(clipped)");
498 if(l->fillid < 0 /*clip*/) {
499 if(DEBUG&2) printf("(add clip %d)", l->clipdepth);
500 if(l->clipdepth > clipdepth)
501 clipdepth = l->clipdepth;
502 } else if(l->fillid == 0) {
503 /* not filled. TODO: we should never add those in the first place */
505 printf("(not filled)");
506 } else if(l->fillid > l->p->shape->numfillstyles) {
507 fprintf(stderr, "Fill style out of bounds (%d>%d)", l->fillid, l->p->shape->numlinestyles);
511 printf("(%d -> %d style %d)", x1, x2, l->fillid);
513 f = &l->p->shape->fillstyles[l->fillid-1];
515 if(f->type == FILL_SOLID) {
516 /* plain color fill */
517 fill_plain(line, x1, x2, f->color);
518 } else if(f->type == FILL_TILED || f->type == FILL_CLIPPED) {
519 /* TODO: optimize (do this in add_pixel()?) */
520 bitmap_t* b = i->bitmaps;
521 while(b && b->id != f->id_bitmap) {
525 fprintf(stderr, "Shape references unknown bitmap %d\n", f->id_bitmap);
526 fill_plain(line, x1, x2, color_red);
529 m.tx -= dest->posx*20;
530 m.ty -= dest->posy*20;
537 fill_bitmap(line, y, x1, x2, &m, b, FILL_CLIPPED?1:0);
545 static void search_layer(state_t*state, int depth, layer_t**before, layer_t**self, layer_t**after)
547 layer_t*last=0,*l = state->layers;
548 while(l && l->p->depth < depth) {
553 if(l && l->p->depth == depth)
558 static void delete_layer(state_t*state, layer_t*todel)
560 layer_t*before=todel->prev;
561 layer_t*next = todel->next;
564 state->layers = next;
570 before->next->prev = before;
573 static void add_layer(state_t*state, layer_t*before, layer_t*toadd)
576 toadd->next = state->layers;
580 toadd->next = before->next;
581 toadd->prev = before;
582 before->next = toadd;
585 toadd->next->prev = toadd;
587 static void free_layers(state_t* state)
589 layer_t*l = state->layers;
591 layer_t*next = l->next;
597 static void change_state(int y, state_t* state, renderpoint_t*p)
599 layer_t*before=0, *self=0, *after=0;
602 printf("[%s(%d,%d)/%d/%d-%d]", p->type==clip_type?"C":"F", p->x, y, p->depth, p->shapeline->fillstyle0, p->shapeline->fillstyle1);
605 search_layer(state, p->depth, &before, &self, &after);
609 if(self->fillid<0 || !p->shapeline->fillstyle0 || !p->shapeline->fillstyle1) {
610 /* filling/clipping ends */
611 if(DEBUG&2) printf("<D>");
613 delete_layer(state, self);
615 /*both fill0 and fill1 are set- exchange the two, updating the layer */
616 if(self->fillid == p->shapeline->fillstyle0) {
617 self->fillid = p->shapeline->fillstyle1;
620 if(DEBUG&2) printf("<X>");
621 } else if(self->fillid == p->shapeline->fillstyle1) {
622 self->fillid = p->shapeline->fillstyle0;
625 if(DEBUG&2) printf("<X>");
627 /* buggy shape. keep everything as-is. */
628 if(DEBUG&2) printf("<!>");
629 //fprintf(stderr, "<line %d: bad swap>\n", y);
635 if(p->shapeline && p->shapeline->fillstyle0 && p->shapeline->fillstyle1) {
636 /* this is a hack- a better way would be to make sure that
637 we always get (0,32), (32, 33), (33, 0) in the right order if
638 they happen to fall on the same pixel.
639 (not: (0,32), (33, 0), (32, 33))
641 fprintf(stderr, "<line %d: both fillstyles set while not inside shape>\n", y);
645 n = rfx_calloc(sizeof(layer_t));
647 if(DEBUG&2) printf("<+>");
649 if(p->type == clip_type) {
652 n->clipdepth = p->clipdepth;
655 n->fillid = p->shapeline->fillstyle0 ? p->shapeline->fillstyle0 : p->shapeline->fillstyle1;
660 add_layer(state, before, n);
664 RGBA* swf_Render(RENDERBUF*dest)
666 renderbuf_internal*i = (renderbuf_internal*)dest->internal;
667 RGBA* img = (RGBA*)rfx_alloc(sizeof(RGBA)*dest->width*dest->height);
670 RGBA * line1 = rfx_alloc(sizeof(RGBA)*i->width2);
671 RGBA * line2 = rfx_alloc(sizeof(RGBA)*i->width2);
673 for(y=0;y<i->height2;y++) {
674 TAG*tag = i->lines[y].points;
676 int size = sizeof(renderpoint_t);
677 int num = tag->len / size;
679 if((y&1) && i->antialize)
683 memset(&state, 0, sizeof(state_t));
685 memset(line, 0, sizeof(RGBA)*i->width2);
686 memory += tag->memsize;
687 qsort(tag->data, num, size, compare_renderpoints);
689 renderpoint_t*p = (renderpoint_t*)&tag->data[size*n];
690 renderpoint_t*next= n<num-1?(renderpoint_t*)&tag->data[size*(n+1)]:0;
692 int endx = next?next->x:i->width2;
698 change_state(y, &state, p);
700 fill(dest, line, y, startx, endx, &state);
701 if(endx == i->width2)
705 if(DEBUG&2) printf("\n");
708 memcpy(&img[y*dest->width], line, sizeof(RGBA)*dest->width);
712 RGBA* p = &img[(y/2)*dest->width];
713 for(x=0;x<dest->width;x++) {
714 RGBA*p1 = &line1[x*2];
715 RGBA*p2 = &line1[x*2+1];
716 RGBA*p3 = &line2[x*2];
717 RGBA*p4 = &line2[x*2+1];
718 p[x].r = (p1->r + p2->r + p3->r + p4->r)/4;
719 p[x].g = (p1->g + p2->g + p3->g + p4->g)/4;
720 p[x].b = (p1->b + p2->b + p3->b + p4->b)/4;
721 p[x].a = (p1->a + p2->a + p3->a + p4->a)/4;
729 if(DEBUG) printf("\nMemory used: %d\n", memory);
731 if(DEBUG) printf("Statistics:\n");
732 if(DEBUG) printf("Average layer depth: %f\n", (double)layers/layernum);