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;
69 static inline void add_pixel(RENDERBUF*dest, float x, int y, renderpoint_t*p)
71 renderbuf_internal*i = (renderbuf_internal*)dest->internal;
72 if(x >= i->width2 || y >= i->height2 || y<0) return;
75 swf_SetBlock(i->lines[y].points, (U8*)p, sizeof(renderpoint_t));
78 /* set this to 0.777777 or something if the "both fillstyles set while not inside shape"
79 problem appears to often */
82 static void add_line(RENDERBUF*buf, double x1, double y1, double x2, double y2, renderpoint_t*p, char thin)
84 renderbuf_internal*i = (renderbuf_internal*)buf->internal;
86 int l = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
87 printf(" l[%d - %.2f/%.2f -> %.2f/%.2f]", l, x1/20.0, y1/20.0, x2/20.0, y2/20.0);
101 double x = x1;x1 = x2;x2=x;
102 double y = y1;y1 = y2;y2=y;
105 double diffx = x2 - x1;
106 double diffy = y2 - y1;
108 double ny1 = (int)(y1)+CUT;
109 double ny2 = (int)(y2)+CUT;
112 ny1 = (int)(y1) + 1.0 + CUT;
115 ny2 = (int)(y2) - 1.0 + CUT;
121 double stepx = diffx/diffy;
122 x1 = x1 + (ny1-y1)*stepx;
123 x2 = x2 + (ny2-y2)*stepx;
131 float xx = (float)(startx + posx);
132 add_pixel(buf, xx ,posy, p);
137 #define PI 3.14159265358979
138 static void add_solidline(RENDERBUF*buf, double x1, double y1, double x2, double y2, int width, renderpoint_t*p)
140 renderbuf_internal*i = (renderbuf_internal*)buf->internal;
153 /* The Flash Player does this, too. This means every line is always at least
157 sd = (double)dx*(double)dx+(double)dy*(double)dy;
179 add_line(buf, x1+vx, y1+vy, xx, yy, p, 0);
182 for(t=1;t<segments;t++) {
183 double s = sin(t*PI/segments);
184 double c = cos(t*PI/segments);
185 xx = (x2 + vx*c - vy*s);
186 yy = (y2 + vx*s + vy*c);
187 add_line(buf, lastx, lasty, xx, yy, p, 0);
194 add_line(buf, lastx, lasty, xx, yy, p, 0);
199 add_line(buf, lastx, lasty, xx, yy, p, 0);
202 for(t=1;t<segments;t++) {
203 double s = sin(t*PI/segments);
204 double c = cos(t*PI/segments);
205 xx = (x1 - vx*c + vy*s);
206 yy = (y1 - vx*s - vy*c);
207 add_line(buf, lastx, lasty, xx, yy, p, 0);
211 add_line(buf, lastx, lasty, (x1+vx), (y1+vy), p, 0);
214 static inline void transform_point(MATRIX*m, int x, int y, int*dx, int*dy)
219 d = swf_TurnPoint(p, m);
224 static int compare_renderpoints(const void * _a, const void * _b)
226 renderpoint_t*a = (renderpoint_t*)_a;
227 renderpoint_t*b = (renderpoint_t*)_b;
228 if(a->fx < b->fx) return -1;
229 if(a->fx > b->fx) return 1;
233 void swf_Render_Init(RENDERBUF*buf, int posx, int posy, int width, int height, char antialize, int multiply)
235 renderbuf_internal*i;
237 memset(buf, 0, sizeof(RENDERBUF));
238 buf->width = width*multiply;
239 buf->height = height*multiply;
242 buf->internal = (renderbuf_internal*)rfx_calloc(sizeof(renderbuf_internal));
243 i = (renderbuf_internal*)buf->internal;
244 i->antialize = antialize;
245 i->multiply = antialize?multiply*2:multiply;
246 i->height2 = antialize?2*buf->height:buf->height;
247 i->width2 = antialize?2*buf->width:buf->width;
248 i->lines = (renderline_t*)rfx_alloc(i->height2*sizeof(renderline_t));
249 for(y=0;y<i->height2;y++) {
250 i->lines[y].points = swf_InsertTag(0, 0);
253 void swf_Render_AddImage(RENDERBUF*buf, U16 id, RGBA*img, int width, int height)
255 renderbuf_internal*i = (renderbuf_internal*)buf->internal;
257 bitmap_t*bm = rfx_calloc(sizeof(bitmap_t));
263 bm->next = i->bitmaps;
266 void swf_Render_ClearCanvas(RENDERBUF*dest)
268 renderbuf_internal*i = (renderbuf_internal*)dest->internal;
270 for(y=0;y<i->height2;y++) {
271 swf_ClearTag(i->lines[y].points);
274 void swf_Render_Delete(RENDERBUF*dest)
276 renderbuf_internal*i = (renderbuf_internal*)dest->internal;
278 bitmap_t*b = i->bitmaps;
279 dummyshape_t*d = i->dshapes;
281 /* delete line buffers */
282 for(y=0;y<i->height2;y++) {
283 swf_DeleteTag(i->lines[y].points);
284 i->lines[y].points = 0;
288 dummyshape_t*next = d->next;
289 swf_Shape2Free(d->shape);
290 free(d->shape);d->shape=0;
298 bitmap_t*next = b->next;
299 //free(b->data);b->data=0;
304 rfx_free(i->lines); i->lines = 0;
305 rfx_free(dest->internal); dest->internal = 0;
308 void swf_RenderShape(RENDERBUF*dest, SHAPE2*shape, MATRIX*m, CXFORM*c, U16 _depth,U16 _clipdepth)
310 renderbuf_internal*i = (renderbuf_internal*)dest->internal;
312 SHAPELINE*line = shape->lines;
318 memset(&p, 0, sizeof(renderpoint_t));
319 memset(&lp, 0, sizeof(renderpoint_t));
320 p.type = _clipdepth?clip_type:fill_type;
322 p.depth = _depth << 16;
323 p.clipdepth = _clipdepth << 16;
324 mat.tx -= dest->posx*20;
325 mat.ty -= dest->posy*20;
327 if(shape->numlinestyles) {
328 dummyshape_t*dshape = rfx_calloc(sizeof(dummyshape_t));
329 lshape = rfx_calloc(sizeof(SHAPE2));
331 lshape->numfillstyles = shape->numlinestyles;
332 lshape->fillstyles = (FILLSTYLE*)rfx_calloc(sizeof(FILLSTYLE)*shape->numlinestyles);
333 lshape->lines = (SHAPELINE*)rfx_calloc(sizeof(SHAPELINE)*shape->numlinestyles);
334 for(t=0;t<shape->numlinestyles;t++) {
335 lshape->lines[t].fillstyle0 = t+1;
336 lshape->fillstyles[t].type = FILL_SOLID;
337 lshape->fillstyles[t].color = shape->linestyles[t].color;
341 lp.depth = p.depth+1;
343 /* add this shape to the global shape list, for deallocing */
344 dshape->shape = lshape;
345 i->dshapes_next = dshape;
354 add_line(dest, -20, 0, -20, i->height2*20, &p, 0);
359 int x1,y1,x2,y2,x3,y3;
363 if(line->type == moveTo) {
364 } else if(line->type == lineTo) {
370 int l = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
371 printf("%d - %.2f/%.2f -> %.2f/%.2f ", l, x1/20.0, y1/20.0, x2/20.0, y2/20.0);
374 transform_point(&mat, x, y, &x1, &y1);
375 transform_point(&mat, line->x, line->y, &x3, &y3);
377 if(line->linestyle && ! p.clipdepth) {
378 lp.shapeline = &lshape->lines[line->linestyle-1];
379 add_solidline(dest, x1, y1, x3, y3, shape->linestyles[line->linestyle-1].width, &lp);
382 if(line->fillstyle0 || line->fillstyle1)
383 add_line(dest, x1, y1, x3, y3, &p, 0);
385 if(DEBUG&4) printf("\n");
386 } else if(line->type == splineTo) {
388 transform_point(&mat, x, y, &x1, &y1);
389 transform_point(&mat, line->sx, line->sy, &x2, &y2);
390 transform_point(&mat, line->x, line->y, &x3, &y3);
392 int c = abs(x3-2*x2+x1) + abs(y3-2*y2+y1);
397 parts = (int)(sqrt(c)/3);
398 if(!parts) parts = 1;
402 printf("spline %.2f/%.2f -(%.2f/%.2f)-> %.2f/%.2f (c=%d, %d parts)",
405 x3/20.0, y3/20.0, c, parts);
408 for(t=1;t<=parts;t++) {
409 double nx = (double)(t*t*x3 + 2*t*(parts-t)*x2 + (parts-t)*(parts-t)*x1)/(double)(parts*parts);
410 double ny = (double)(t*t*y3 + 2*t*(parts-t)*y2 + (parts-t)*(parts-t)*y1)/(double)(parts*parts);
412 if(line->linestyle && ! p.clipdepth) {
413 lp.shapeline = &lshape->lines[line->linestyle-1];
414 add_solidline(dest, xx, yy, nx, ny, shape->linestyles[line->linestyle-1].width, &lp);
417 if(line->fillstyle0 || line->fillstyle1)
418 add_line(dest, (int)xx, (int)yy, (int)nx, (int)ny, &p, 0);
432 typedef struct _layer {
444 static RGBA color_red = {255,255,0,0};
445 static RGBA color_white = {255,255,255,255};
447 static void fill_plain(RGBA*line, int x1, int x2, RGBA col)
451 int ainv = 255-col.a;
452 col.r = (col.r*col.a)>>8;
453 col.g = (col.g*col.a)>>8;
454 col.b = (col.b*col.a)>>8;
457 line[x].r = ((line[x].r*ainv)>>8)+col.r;
458 line[x].g = ((line[x].g*ainv)>>8)+col.g;
459 line[x].b = ((line[x].b*ainv)>>8)+col.b;
469 static void fill_bitmap(RGBA*line, int y, int x1, int x2, MATRIX*m, bitmap_t*b, int clip)
472 double m11=m->sx/65536.0, m21=m->r1/65536.0;
473 double m12=m->r0/65536.0, m22=m->sy/65536.0;
474 double rx = m->tx/20.0;
475 double ry = m->ty/20.0;
476 double det = m11*m22 - m12*m21;
477 if(fabs(det) < 0.0005) {
478 /* x direction equals y direction- the image is invisible */
483 if(!b->width || !b->height) {
484 fill_plain(line, x1, x2, color_red);
489 int xx = (int)(( (x - rx) * m22 - (y - ry) * m21)*det);
490 int yy = (int)((- (x - rx) * m12 + (y - ry) * m11)*det);
494 if(xx>=b->width) xx = b->width-1;
496 if(yy>=b->height) yy = b->height-1;
502 RGBA col = b->data[yy*b->width+xx];
503 int ainv = 255-col.a;
505 line[x].r = ((line[x].r*ainv)>>8)+col.r;
506 line[x].g = ((line[x].g*ainv)>>8)+col.g;
507 line[x].b = ((line[x].b*ainv)>>8)+col.b;
512 static void fill(RENDERBUF*dest, RGBA*line, int y, int x1, int x2, state_t*state)
514 renderbuf_internal*i = (renderbuf_internal*)dest->internal;
516 layer_t*l = state->layers;
518 if(x1>=x2) //zero width? nothing to do.
523 if(l->p->depth < clipdepth) {
524 if(DEBUG&2) printf("(clipped)");
528 if(l->fillid < 0 /*clip*/) {
529 if(DEBUG&2) printf("(add clip %d)", l->clipdepth);
530 if(l->clipdepth > clipdepth)
531 clipdepth = l->clipdepth;
532 } else if(l->fillid == 0) {
533 /* not filled. TODO: we should never add those in the first place */
535 printf("(not filled)");
536 } else if(l->fillid > l->p->shape->numfillstyles) {
537 fprintf(stderr, "Fill style out of bounds (%d>%d)", l->fillid, l->p->shape->numlinestyles);
541 printf("(%d -> %d style %d)", x1, x2, l->fillid);
543 f = &l->p->shape->fillstyles[l->fillid-1];
545 if(f->type == FILL_SOLID) {
546 /* plain color fill */
547 fill_plain(line, x1, x2, f->color);
548 } else if(f->type == FILL_TILED || f->type == FILL_CLIPPED) {
549 /* TODO: optimize (do this in add_pixel()?) */
550 bitmap_t* b = i->bitmaps;
551 while(b && b->id != f->id_bitmap) {
555 fprintf(stderr, "Shape references unknown bitmap %d\n", f->id_bitmap);
556 fill_plain(line, x1, x2, color_red);
559 m.tx -= dest->posx*20;
560 m.ty -= dest->posy*20;
567 fill_bitmap(line, y, x1, x2, &m, b, FILL_CLIPPED?1:0);
575 static void search_layer(state_t*state, int depth, layer_t**before, layer_t**self, layer_t**after)
577 layer_t*last=0,*l = state->layers;
578 while(l && l->p->depth < depth) {
583 if(l && l->p->depth == depth)
588 static void delete_layer(state_t*state, layer_t*todel)
590 layer_t*before=todel->prev;
591 layer_t*next = todel->next;
594 state->layers = next;
600 before->next->prev = before;
603 static void add_layer(state_t*state, layer_t*before, layer_t*toadd)
606 toadd->next = state->layers;
610 toadd->next = before->next;
611 toadd->prev = before;
612 before->next = toadd;
615 toadd->next->prev = toadd;
617 static void free_layers(state_t* state)
619 layer_t*l = state->layers;
621 layer_t*next = l->next;
627 static void change_state(int y, state_t* state, renderpoint_t*p)
629 layer_t*before=0, *self=0, *after=0;
632 printf("[%s(%d,%d)/%d/%d-%d]", p->type==clip_type?"C":"F", p->x, y, p->depth, p->shapeline->fillstyle0, p->shapeline->fillstyle1);
635 search_layer(state, p->depth, &before, &self, &after);
639 if(self->fillid<0 || !p->shapeline->fillstyle0 || !p->shapeline->fillstyle1) {
640 /* filling/clipping ends */
641 if(DEBUG&2) printf("<D>");
643 delete_layer(state, self);
645 /*both fill0 and fill1 are set- exchange the two, updating the layer */
646 if(self->fillid == p->shapeline->fillstyle0) {
647 self->fillid = p->shapeline->fillstyle1;
650 if(DEBUG&2) printf("<X>");
651 } else if(self->fillid == p->shapeline->fillstyle1) {
652 self->fillid = p->shapeline->fillstyle0;
655 if(DEBUG&2) printf("<X>");
657 /* buggy shape. keep everything as-is. */
658 if(DEBUG&2) printf("<!>");
659 //fprintf(stderr, "<line %d: bad swap>\n", y);
665 if(p->shapeline && p->shapeline->fillstyle0 && p->shapeline->fillstyle1) {
666 /* this is a hack- a better way would be to make sure that
667 we always get (0,32), (32, 33), (33, 0) in the right order if
668 they happen to fall on the same pixel.
669 (not: (0,32), (33, 0), (32, 33))
671 fprintf(stderr, "<line %d: both fillstyles set while not inside shape>\n", y);
675 n = rfx_calloc(sizeof(layer_t));
677 if(DEBUG&2) printf("<+>");
679 if(p->type == clip_type) {
682 n->clipdepth = p->clipdepth;
685 n->fillid = p->shapeline->fillstyle0 ? p->shapeline->fillstyle0 : p->shapeline->fillstyle1;
690 add_layer(state, before, n);
694 RGBA* swf_Render(RENDERBUF*dest)
696 renderbuf_internal*i = (renderbuf_internal*)dest->internal;
697 RGBA* img = (RGBA*)rfx_alloc(sizeof(RGBA)*dest->width*dest->height);
700 RGBA * line1 = rfx_alloc(sizeof(RGBA)*i->width2);
701 RGBA * line2 = rfx_alloc(sizeof(RGBA)*i->width2);
703 for(y=0;y<i->height2;y++) {
704 TAG*tag = i->lines[y].points;
706 int size = sizeof(renderpoint_t);
707 int num = tag->len / size;
709 if((y&1) && i->antialize)
713 memset(&state, 0, sizeof(state_t));
715 memset(line, 0, sizeof(RGBA)*i->width2);
716 memory += tag->memsize;
717 qsort(tag->data, num, size, compare_renderpoints);
719 renderpoint_t*p = (renderpoint_t*)&tag->data[size*n];
720 renderpoint_t*next= n<num-1?(renderpoint_t*)&tag->data[size*(n+1)]:0;
722 int endx = next?next->x:i->width2;
728 change_state(y, &state, p);
730 fill(dest, line, y, startx, endx, &state);
731 if(endx == i->width2)
735 if(DEBUG&2) printf("\n");
738 memcpy(&img[y*dest->width], line, sizeof(RGBA)*dest->width);
742 RGBA* p = &img[(y/2)*dest->width];
743 for(x=0;x<dest->width;x++) {
744 RGBA*p1 = &line1[x*2];
745 RGBA*p2 = &line1[x*2+1];
746 RGBA*p3 = &line2[x*2];
747 RGBA*p4 = &line2[x*2+1];
748 p[x].r = (p1->r + p2->r + p3->r + p4->r)/4;
749 p[x].g = (p1->g + p2->g + p3->g + p4->g)/4;
750 p[x].b = (p1->b + p2->b + p3->b + p4->b)/4;
751 p[x].a = (p1->a + p2->a + p3->a + p4->a)/4;
759 if(DEBUG) printf("\nMemory used: %d\n", memory);
761 if(DEBUG) printf("Statistics:\n");
762 if(DEBUG) printf("Average layer depth: %f\n", (double)layers/layernum);