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));
69 static void add_line(RENDERBUF*buf, double x1, int y1, double x2, int y2, renderpoint_t*p, char thin)
71 renderbuf_internal*i = (renderbuf_internal*)buf->internal;
73 int l = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
74 printf(" l[%d - %.2f/%.2f -> %.2f/%.2f]", l, x1/20.0, y1/20.0, x2/20.0, y2/20.0);
89 double diffx = x2 - x1;
108 stepx = diffx / diffy;
115 float xx = (float)((startx + posx)/20.0);
116 int yy = starty + posy;
118 add_pixel(buf, xx ,yy, p);
121 float x2 = (float)((startx + posx)/20.0);
129 add_pixel(buf, x2, yy, p);
135 #define PI 3.14159265358979
136 static void add_solidline(RENDERBUF*buf, double x1, double y1, double x2, double y2, int width, renderpoint_t*p)
138 renderbuf_internal*i = (renderbuf_internal*)buf->internal;
154 /* The Flash Player does this, too. This means every line is always at least
159 add_line(buf, x1, y1, x2, y2, p, 1);
163 sd = (double)dx*(double)dx+(double)dy*(double)dy;
185 add_line(buf, x1+vx, (int)(y1+vy), xx, yy, p, 0);
188 for(t=1;t<segments;t++) {
189 double s = sin(t*PI/segments);
190 double c = cos(t*PI/segments);
191 xx = (x2 + vx*c - vy*s);
192 yy = (int)(y2 + vx*s + vy*c);
193 add_line(buf, lastx, lasty, xx, yy, p, 0);
200 add_line(buf, lastx, lasty, xx, yy, p, 0);
205 add_line(buf, lastx, lasty, xx, yy, p, 0);
208 for(t=1;t<segments;t++) {
209 double s = sin(t*PI/segments);
210 double c = cos(t*PI/segments);
211 xx = (x1 - vx*c + vy*s);
212 yy = (int)(y1 - vx*s - vy*c);
213 add_line(buf, lastx, lasty, xx, yy, p, 0);
217 add_line(buf, lastx, lasty, (x1+vx), (int)(y1+vy), p, 0);
220 static inline void transform_point(MATRIX*m, int x, int y, int*dx, int*dy)
225 d = swf_TurnPoint(p, m);
230 static int compare_renderpoints(const void * _a, const void * _b)
232 renderpoint_t*a = (renderpoint_t*)_a;
233 renderpoint_t*b = (renderpoint_t*)_b;
234 if(a->fx < b->fx) return -1;
235 if(a->fx > b->fx) return 1;
239 void swf_Render_Init(RENDERBUF*buf, int posx, int posy, int width, int height, char antialize, int multiply)
241 renderbuf_internal*i;
243 memset(buf, 0, sizeof(RENDERBUF));
244 buf->width = width*multiply;
245 buf->height = height*multiply;
248 buf->internal = (renderbuf_internal*)rfx_calloc(sizeof(renderbuf_internal));
249 i = (renderbuf_internal*)buf->internal;
250 i->antialize = antialize;
251 i->multiply = antialize?multiply*2:multiply;
252 i->height2 = antialize?2*buf->height:buf->height;
253 i->width2 = antialize?2*buf->width:buf->width;
254 i->lines = (renderline_t*)rfx_alloc(i->height2*sizeof(renderline_t));
255 for(y=0;y<i->height2;y++) {
256 i->lines[y].points = swf_InsertTag(0, 0);
259 void swf_Render_AddImage(RENDERBUF*buf, U16 id, RGBA*img, int width, int height)
261 renderbuf_internal*i = (renderbuf_internal*)buf->internal;
263 bitmap_t*bm = rfx_calloc(sizeof(bitmap_t));
269 bm->next = i->bitmaps;
272 void swf_Render_ClearCanvas(RENDERBUF*dest)
274 renderbuf_internal*i = (renderbuf_internal*)dest->internal;
276 for(y=0;y<i->height2;y++) {
277 swf_ClearTag(i->lines[y].points);
280 void swf_Render_Delete(RENDERBUF*dest)
282 renderbuf_internal*i = (renderbuf_internal*)dest->internal;
284 bitmap_t*b = i->bitmaps;
286 /* delete line buffers */
287 for(y=0;y<i->height2;y++) {
288 swf_DeleteTag(i->lines[y].points);
289 i->lines[y].points = 0;
294 bitmap_t*next = b->next;
295 //free(b->data);b->data=0;
300 rfx_free(i->lines); i->lines = 0;
301 rfx_free(dest->internal); dest->internal = 0;
304 void swf_RenderShape(RENDERBUF*dest, SHAPE2*shape, MATRIX*m, CXFORM*c, U16 _depth,U16 _clipdepth)
306 renderbuf_internal*i = (renderbuf_internal*)dest->internal;
308 SHAPELINE*line = shape->lines;
314 memset(&p, 0, sizeof(renderpoint_t));
315 memset(&lp, 0, sizeof(renderpoint_t));
316 p.type = _clipdepth?clip_type:fill_type;
318 p.depth = _depth << 16;
319 p.clipdepth = _clipdepth << 16;
320 mat.tx -= dest->posx*20;
321 mat.ty -= dest->posy*20;
323 if(shape->numlinestyles) {
324 /* TODO: free this again */
325 lshape = rfx_calloc(sizeof(SHAPE2));
327 lshape->numfillstyles = shape->numlinestyles;
328 lshape->fillstyles = (FILLSTYLE*)rfx_calloc(sizeof(FILLSTYLE)*shape->numlinestyles);
329 lshape->lines = (SHAPELINE*)rfx_calloc(sizeof(SHAPELINE)*shape->numlinestyles);
330 for(t=0;t<shape->numlinestyles;t++) {
331 lshape->lines[t].fillstyle0 = t+1;
332 lshape->fillstyles[t].type = FILL_SOLID;
333 lshape->fillstyles[t].color = shape->linestyles[t].color;
337 lp.depth = p.depth+1;
343 add_line(dest, -20, 0, -20, i->height2*20, &p, 0);
348 int x1,y1,x2,y2,x3,y3;
352 if(line->type == moveTo) {
353 } else if(line->type == lineTo) {
359 int l = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
360 printf("%d - %.2f/%.2f -> %.2f/%.2f ", l, x1/20.0, y1/20.0, x2/20.0, y2/20.0);
363 transform_point(&mat, x, y, &x1, &y1);
364 transform_point(&mat, line->x, line->y, &x3, &y3);
366 if(line->linestyle && ! p.clipdepth) {
367 lp.shapeline = &lshape->lines[line->linestyle-1];
368 add_solidline(dest, x1, y1, x3, y3, shape->linestyles[line->linestyle-1].width, &lp);
371 if(line->fillstyle0 || line->fillstyle1)
372 add_line(dest, x1, y1, x3, y3, &p, 0);
374 if(DEBUG&4) printf("\n");
375 } else if(line->type == splineTo) {
377 transform_point(&mat, x, y, &x1, &y1);
378 transform_point(&mat, line->sx, line->sy, &x2, &y2);
379 transform_point(&mat, line->x, line->y, &x3, &y3);
381 int c = abs(x3-2*x2+x1) + abs(y3-2*y2+y1);
386 parts = (int)(sqrt(c)/2);
387 if(!parts) parts = 1;
391 printf("spline %.2f/%.2f -(%.2f/%.2f)-> %.2f/%.2f (c=%d, %d parts)",
394 x3/20.0, y3/20.0, c, parts);
397 for(t=1;t<=parts;t++) {
398 double nx = (double)(t*t*x3 + 2*t*(parts-t)*x2 + (parts-t)*(parts-t)*x1)/(double)(parts*parts);
399 double ny = (double)(t*t*y3 + 2*t*(parts-t)*y2 + (parts-t)*(parts-t)*y1)/(double)(parts*parts);
401 if(line->linestyle && ! p.clipdepth) {
402 lp.shapeline = &lshape->lines[line->linestyle-1];
403 add_solidline(dest, xx, yy, nx, ny, shape->linestyles[line->linestyle-1].width, &lp);
406 if(line->fillstyle0 || line->fillstyle1)
407 add_line(dest, (int)xx, (int)yy, (int)nx, (int)ny, &p, 0);
421 typedef struct _layer {
433 static RGBA color_red = {255,255,0,0};
434 static RGBA color_white = {255,255,255,255};
436 static void fill_plain(RGBA*line, int x1, int x2, RGBA col)
440 int ainv = 255-col.a;
441 col.r = (col.r*col.a)>>8;
442 col.g = (col.g*col.a)>>8;
443 col.b = (col.b*col.a)>>8;
446 line[x].r = ((line[x].r*ainv)>>8)+col.r;
447 line[x].g = ((line[x].g*ainv)>>8)+col.g;
448 line[x].b = ((line[x].b*ainv)>>8)+col.b;
458 static void fill_bitmap(RGBA*line, int y, int x1, int x2, MATRIX*m, bitmap_t*b, int clip)
461 double m11=m->sx/65536.0, m21=m->r1/65536.0;
462 double m12=m->r0/65536.0, m22=m->sy/65536.0;
463 double rx = m->tx/20.0;
464 double ry = m->ty/20.0;
465 double det = m11*m22 - m12*m21;
466 if(fabs(det) < 0.0005) {
467 /* x direction equals y direction- the image is invisible */
473 int xx = (int)(( (x - rx) * m22 - (y - ry) * m21)*det);
474 int yy = (int)((- (x - rx) * m12 + (y - ry) * m11)*det);
477 if(xx<0 || xx>=b->width || yy<0 || yy>=b->height) {
478 //line[x] = color_red;
486 RGBA col = b->data[yy*b->width+xx];
487 int ainv = 255-col.a;
489 line[x].r = ((line[x].r*ainv)>>8)+col.r;
490 line[x].g = ((line[x].g*ainv)>>8)+col.g;
491 line[x].b = ((line[x].b*ainv)>>8)+col.b;
496 static void fill(RENDERBUF*dest, RGBA*line, int y, int x1, int x2, state_t*state)
498 renderbuf_internal*i = (renderbuf_internal*)dest->internal;
500 layer_t*l = state->layers;
502 if(x1>=x2) //zero width? nothing to do.
507 if(l->p->depth < clipdepth) {
508 if(DEBUG&2) printf("(clipped)");
512 if(l->fillid < 0 /*clip*/) {
513 if(DEBUG&2) printf("(add clip %d)", l->clipdepth);
514 if(l->clipdepth > clipdepth)
515 clipdepth = l->clipdepth;
516 } else if(l->fillid == 0) {
517 /* not filled. TODO: we should never add those in the first place */
519 printf("(not filled)");
520 } else if(l->fillid > l->p->shape->numfillstyles) {
521 fprintf(stderr, "Fill style out of bounds (%d>%d)", l->fillid, l->p->shape->numlinestyles);
525 printf("(%d -> %d style %d)", x1, x2, l->fillid);
527 f = &l->p->shape->fillstyles[l->fillid-1];
529 if(f->type == FILL_SOLID) {
530 /* plain color fill */
531 fill_plain(line, x1, x2, f->color);
532 } else if(f->type == FILL_TILED || f->type == FILL_CLIPPED) {
533 /* TODO: optimize (do this in add_pixel()?) */
534 bitmap_t* b = i->bitmaps;
535 while(b && b->id != f->id_bitmap) {
539 fprintf(stderr, "Shape references unknown bitmap %d\n", f->id_bitmap);
540 fill_plain(line, x1, x2, color_red);
543 m.tx -= dest->posx*20;
544 m.ty -= dest->posy*20;
551 fill_bitmap(line, y, x1, x2, &m, b, FILL_CLIPPED?1:0);
559 static void search_layer(state_t*state, int depth, layer_t**before, layer_t**self, layer_t**after)
561 layer_t*last=0,*l = state->layers;
562 while(l && l->p->depth < depth) {
567 if(l && l->p->depth == depth)
572 static void delete_layer(state_t*state, layer_t*todel)
574 layer_t*before=todel->prev;
575 layer_t*next = todel->next;
578 state->layers = next;
584 before->next->prev = before;
587 static void add_layer(state_t*state, layer_t*before, layer_t*toadd)
590 toadd->next = state->layers;
594 toadd->next = before->next;
595 toadd->prev = before;
596 before->next = toadd;
599 toadd->next->prev = toadd;
601 static void free_layers(state_t* state)
603 layer_t*l = state->layers;
605 layer_t*next = l->next;
611 static void change_state(int y, state_t* state, renderpoint_t*p)
613 layer_t*before=0, *self=0, *after=0;
616 printf("[%s(%d,%d)/%d/%d-%d]", p->type==clip_type?"C":"F", p->x, y, p->depth, p->shapeline->fillstyle0, p->shapeline->fillstyle1);
619 search_layer(state, p->depth, &before, &self, &after);
623 if(self->fillid<0 || !p->shapeline->fillstyle0 || !p->shapeline->fillstyle1) {
624 /* filling/clipping ends */
625 if(DEBUG&2) printf("<D>");
627 delete_layer(state, self);
629 /*both fill0 and fill1 are set- exchange the two, updating the layer */
630 if(self->fillid == p->shapeline->fillstyle0) {
631 self->fillid = p->shapeline->fillstyle1;
634 if(DEBUG&2) printf("<X>");
635 } else if(self->fillid == p->shapeline->fillstyle1) {
636 self->fillid = p->shapeline->fillstyle0;
639 if(DEBUG&2) printf("<X>");
641 /* buggy shape. keep everything as-is. */
642 if(DEBUG&2) printf("<!>");
643 //fprintf(stderr, "<line %d: bad swap>\n", y);
649 if(p->shapeline && p->shapeline->fillstyle0 && p->shapeline->fillstyle1) {
650 /* this is a hack- a better way would be to make sure that
651 we always get (0,32), (32, 33), (33, 0) in the right order if
652 they happen to fall on the same pixel.
653 (not: (0,32), (33, 0), (32, 33))
655 fprintf(stderr, "<line %d: both fillstyles set while not inside shape>\n", y);
659 n = rfx_calloc(sizeof(layer_t));
661 if(DEBUG&2) printf("<+>");
663 if(p->type == clip_type) {
666 n->clipdepth = p->clipdepth;
669 n->fillid = p->shapeline->fillstyle0 ? p->shapeline->fillstyle0 : p->shapeline->fillstyle1;
674 add_layer(state, before, n);
678 RGBA* swf_Render(RENDERBUF*dest)
680 renderbuf_internal*i = (renderbuf_internal*)dest->internal;
681 RGBA* img = (RGBA*)rfx_alloc(sizeof(RGBA)*dest->width*dest->height);
684 RGBA * line1 = rfx_alloc(sizeof(RGBA)*i->width2);
685 RGBA * line2 = rfx_alloc(sizeof(RGBA)*i->width2);
687 for(y=0;y<i->height2;y++) {
688 TAG*tag = i->lines[y].points;
690 int size = sizeof(renderpoint_t);
691 int num = tag->len / size;
693 if((y&1) && i->antialize)
697 memset(&state, 0, sizeof(state_t));
699 memset(line, 0, sizeof(RGBA)*i->width2);
700 memory += tag->memsize;
701 qsort(tag->data, num, size, compare_renderpoints);
703 renderpoint_t*p = (renderpoint_t*)&tag->data[size*n];
704 renderpoint_t*next= n<num-1?(renderpoint_t*)&tag->data[size*(n+1)]:0;
706 int endx = next?next->x:i->width2;
712 change_state(y, &state, p);
714 fill(dest, line, y, startx, endx, &state);
715 if(endx == i->width2)
719 if(DEBUG&2) printf("\n");
722 memcpy(&img[y*dest->width], line, sizeof(RGBA)*dest->width);
726 RGBA* p = &img[(y/2)*dest->width];
727 for(x=0;x<dest->width;x++) {
728 RGBA*p1 = &line1[x*2];
729 RGBA*p2 = &line1[x*2+1];
730 RGBA*p3 = &line2[x*2];
731 RGBA*p4 = &line2[x*2+1];
732 p[x].r = (p1->r + p2->r + p3->r + p4->r)/4;
733 p[x].g = (p1->g + p2->g + p3->g + p4->g)/4;
734 p[x].b = (p1->b + p2->b + p3->b + p4->b)/4;
735 p[x].a = (p1->a + p2->a + p3->a + p4->a)/4;
743 if(DEBUG) printf("\nMemory used: %d\n", memory);
745 if(DEBUG) printf("Statistics:\n");
746 if(DEBUG) printf("Average layer depth: %f\n", (double)layers/layernum);