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 */
31 typedef struct _renderpoint
42 enum {clip_type, solidfill_type, texturefill_type, gradientfill_type} type;
57 // texture- & gradientfill;
63 typedef struct _renderline
65 TAG*points; //incremented in 128 byte steps
67 U32 pending_clipdepth;
70 typedef struct _bitmap {
78 typedef struct _renderbuf_internal
94 static inline void add_pixel(RENDERBUF*dest, float x, int y, renderpoint_t*p)
96 renderbuf_internal*i = (renderbuf_internal*)dest->internal;
97 if(x >= i->width2 || y >= i->height2 || y<0) return;
99 if(y<i->ymin) i->ymin = y;
100 if(y>i->ymax) i->ymax = y;
103 swf_SetBlock(i->lines[y].points, (U8*)p, sizeof(renderpoint_t));
106 /* set this to 0.777777 or something if the "both fillstyles set while not inside shape"
107 problem appears to often */
108 #define CUT 0.77887789
110 #define INT(x) ((int)((x)+16)-16)
112 static void add_line(RENDERBUF*buf, double x1, double y1, double x2, double y2, renderpoint_t*p)
114 renderbuf_internal*i = (renderbuf_internal*)buf->internal;
116 double ny1, ny2, stepx;
118 int l = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
119 printf(" l[%d - %.2f/%.2f -> %.2f/%.2f]\n", l, x1/20.0, y1/20.0, x2/20.0, y2/20.0);
121 assert(p->shapeline);
147 ny1 = INT(y1) + 1.0 + CUT;
150 ny2 = INT(y2) - 1.0 + CUT;
157 x1 = x1 + (ny1-y1)*stepx;
158 x2 = x2 + (ny2-y2)*stepx;
167 float xx = (float)(startx + posx);
168 add_pixel(buf, xx ,posy, p);
174 #define PI 3.14159265358979
175 static void add_solidline(RENDERBUF*buf, double x1, double y1, double x2, double y2, double width, renderpoint_t*p)
177 renderbuf_internal*i = (renderbuf_internal*)buf->internal;
190 /* Make sure the line is always at least one pixel wide */
192 /* That's what Macromedia's Player does at least at zoom level >= 1. */
195 /* That's what Macromedia's Player seems to do at zoom level 0. */
196 /* TODO: needs testing */
198 /* TODO: how does this interact with scaling? */
199 if(width * i->multiply < 20)
200 width = 20 / i->multiply;
203 sd = (double)dx*(double)dx+(double)dy*(double)dy;
214 segments = (int)(width/2);
225 add_line(buf, x1+vx, y1+vy, xx, yy, p);
228 for(t=1;t<segments;t++) {
229 double s = sin(t*PI/segments);
230 double c = cos(t*PI/segments);
231 xx = (x2 + vx*c - vy*s);
232 yy = (y2 + vx*s + vy*c);
233 add_line(buf, lastx, lasty, xx, yy, p);
240 add_line(buf, lastx, lasty, xx, yy, p);
245 add_line(buf, lastx, lasty, xx, yy, p);
248 for(t=1;t<segments;t++) {
249 double s = sin(t*PI/segments);
250 double c = cos(t*PI/segments);
251 xx = (x1 - vx*c + vy*s);
252 yy = (y1 - vx*s - vy*c);
253 add_line(buf, lastx, lasty, xx, yy, p);
257 add_line(buf, lastx, lasty, (x1+vx), (y1+vy), p);
260 static inline void transform_point(MATRIX*m, int x, int y, int*dx, int*dy)
265 d = swf_TurnPoint(p, m);
270 static int compare_renderpoints(const void * _a, const void * _b)
272 renderpoint_t*a = (renderpoint_t*)_a;
273 renderpoint_t*b = (renderpoint_t*)_b;
274 if(a->x < b->x) return -1;
275 if(a->x > b->x) return 1;
279 void swf_Render_Init(RENDERBUF*buf, int posx, int posy, int width, int height, int antialize, int multiply)
281 renderbuf_internal*i;
283 memset(buf, 0, sizeof(RENDERBUF));
284 buf->width = width*multiply;
285 buf->height = height*multiply;
288 buf->internal = (renderbuf_internal*)rfx_calloc(sizeof(renderbuf_internal));
289 i = (renderbuf_internal*)buf->internal;
292 i->antialize = antialize;
293 i->multiply = multiply*antialize;
294 i->height2 = antialize*buf->height;
295 i->width2 = antialize*buf->width;
296 i->lines = (renderline_t*)rfx_alloc(i->height2*sizeof(renderline_t));
297 for(y=0;y<i->height2;y++) {
298 memset(&i->lines[y], 0, sizeof(renderline_t));
299 i->lines[y].points = swf_InsertTag(0, 0);
302 i->zbuf = (int*)rfx_calloc(sizeof(int)*i->width2*i->height2);
303 i->img = (RGBA*)rfx_calloc(sizeof(RGBA)*i->width2*i->height2);
305 i->ymin = 0x7fffffff;
306 i->ymax = -0x80000000;
308 void swf_Render_SetBackground(RENDERBUF*buf, RGBA*img, int width, int height)
310 renderbuf_internal*i = (renderbuf_internal*)buf->internal;
312 int xstep=width*65536/i->width2;
313 int ystep=height*65536/i->height2;
315 fprintf(stderr, "rfxswf: Warning: swf_Render_SetBackground() called after drawing shapes\n");
317 for(y=0,yy=0;y<i->height2;y++,yy+=ystep) {
318 RGBA*src = &img[(yy>>16) * width];
319 RGBA*line = &i->img[y * i->width2];
320 for(x=0,xx=0;x<i->width2;x++,xx+=xstep) {
321 line[x] = src[xx>>16];
325 void swf_Render_SetBackgroundColor(RENDERBUF*buf, RGBA color)
327 swf_Render_SetBackground(buf, &color, 1, 1);
329 void swf_Render_AddImage(RENDERBUF*buf, U16 id, RGBA*img, int width, int height)
331 renderbuf_internal*i = (renderbuf_internal*)buf->internal;
333 bitmap_t*bm = (bitmap_t*)rfx_calloc(sizeof(bitmap_t));
337 bm->data = (RGBA*)rfx_alloc(width*height*4);
338 memcpy(bm->data, img, width*height*4);
340 bm->next = i->bitmaps;
343 void swf_Render_ClearCanvas(RENDERBUF*dest)
345 renderbuf_internal*i = (renderbuf_internal*)dest->internal;
347 for(y=0;y<i->height2;y++) {
348 swf_ClearTag(i->lines[y].points);
350 memset(i->zbuf, 0, sizeof(int)*i->width2*i->height2);
351 memset(i->img, 0, sizeof(RGBA)*i->width2*i->height2);
353 void swf_Render_Delete(RENDERBUF*dest)
355 renderbuf_internal*i = (renderbuf_internal*)dest->internal;
357 bitmap_t*b = i->bitmaps;
363 /* delete line buffers */
364 for(y=0;y<i->height2;y++) {
365 swf_DeleteTag(i->lines[y].points);
366 i->lines[y].points = 0;
371 bitmap_t*next = b->next;
372 free(b->data);b->data=0;
377 rfx_free(i->lines); i->lines = 0;
378 rfx_free(dest->internal); dest->internal = 0;
381 static SHAPE2* linestyle2fillstyle(SHAPE2*shape)
383 SHAPE2*s = (SHAPE2*)rfx_calloc(sizeof(SHAPE2));
385 s->numfillstyles = shape->numlinestyles;
386 s->fillstyles = (FILLSTYLE*)rfx_calloc(sizeof(FILLSTYLE)*shape->numlinestyles);
387 s->lines = (SHAPELINE*)rfx_calloc(sizeof(SHAPELINE)*shape->numlinestyles);
388 for(t=0;t<shape->numlinestyles;t++) {
389 s->lines[t].fillstyle0 = t+1;
390 s->fillstyles[t].type = FILL_SOLID;
391 s->fillstyles[t].color = shape->linestyles[t].color;
396 void swf_Process(RENDERBUF*dest, U32 clipdepth);
398 double matrixsize(MATRIX*m)
400 double l1 = sqrt((m->sx /65536.0) * (m->sx /65536.0) + (m->r0 /65536.0) * (m->r0/65536.0) );
401 double l2 = sqrt((m->r1 /65536.0) * (m->r1 /65536.0) + (m->sy /65536.0) * (m->sy/65536.0) );
405 void swf_RenderShape(RENDERBUF*dest, SHAPE2*shape, MATRIX*m, CXFORM*c, U16 _depth,U16 _clipdepth)
407 renderbuf_internal*i = (renderbuf_internal*)dest->internal;
416 double widthmultiply = matrixsize(m);
418 memset(&p, 0, sizeof(renderpoint_t));
419 memset(&lp, 0, sizeof(renderpoint_t));
421 clipdepth = _clipdepth? _clipdepth << 16 | 0xffff : 0;
422 p.depth = _depth << 16;
424 mat.tx -= dest->posx*20;
425 mat.ty -= dest->posy*20;
427 s2 = swf_Shape2Clone(shape);
429 if(shape->numfillstyles) {
432 /* multiply fillstyles matrices with placement matrix-
433 important for texture and gradient fill */
434 for(t=0;t<s2->numfillstyles;t++) {
436 swf_MatrixJoin(&nm, &mat, &s2->fillstyles[t].m);
437 /*nm.sx *= i->multiply;
438 nm.sy *= i->multiply;
439 nm.r0 *= i->multiply;
440 nm.r1 *= i->multiply;
441 nm.tx *= i->multiply;
442 nm.ty *= i->multiply;*/
443 s2->fillstyles[t].m = nm;
447 if(shape->numlinestyles) {
448 lshape = linestyle2fillstyle(shape);
450 lp.depth = (_depth << 16)+1;
456 int x1,y1,x2,y2,x3,y3;
458 if(line->type == moveTo) {
459 } else if(line->type == lineTo) {
460 transform_point(&mat, x, y, &x1, &y1);
461 transform_point(&mat, line->x, line->y, &x3, &y3);
463 if(line->linestyle && ! clipdepth) {
464 lp.shapeline = &lshape->lines[line->linestyle-1];
465 add_solidline(dest, x1, y1, x3, y3, shape->linestyles[line->linestyle-1].width * widthmultiply, &lp);
468 if(line->fillstyle0 || line->fillstyle1) {
469 assert(shape->numfillstyles);
471 add_line(dest, x1, y1, x3, y3, &p);
473 } else if(line->type == splineTo) {
474 int c,t,parts,qparts;
477 transform_point(&mat, x, y, &x1, &y1);
478 transform_point(&mat, line->sx, line->sy, &x2, &y2);
479 transform_point(&mat, line->x, line->y, &x3, &y3);
481 c = abs(x3-2*x2+x1) + abs(y3-2*y2+y1);
485 parts = (int)(sqrt((float)c)/3);
486 if(!parts) parts = 1;
488 for(t=1;t<=parts;t++) {
489 double nx = (double)(t*t*x3 + 2*t*(parts-t)*x2 + (parts-t)*(parts-t)*x1)/(double)(parts*parts);
490 double ny = (double)(t*t*y3 + 2*t*(parts-t)*y2 + (parts-t)*(parts-t)*y1)/(double)(parts*parts);
492 if(line->linestyle && ! clipdepth) {
493 lp.shapeline = &lshape->lines[line->linestyle-1];
494 add_solidline(dest, xx, yy, nx, ny, shape->linestyles[line->linestyle-1].width * widthmultiply, &lp);
497 if(line->fillstyle0 || line->fillstyle1) {
498 assert(shape->numfillstyles);
500 add_line(dest, xx, yy, nx, ny, &p);
512 swf_Process(dest, clipdepth);
515 swf_Shape2Free(s2);rfx_free(s2);s2=0;
518 swf_Shape2Free(lshape);rfx_free(lshape);lshape=0;
523 static RGBA color_red = {255,255,0,0};
524 static RGBA color_white = {255,255,255,255};
525 static RGBA color_black = {255,0,0,0};
527 static void fill_clip(RGBA*line, int*z, int y, int x1, int x2, U32 depth)
540 static void fill_solid(RGBA*line, int*z, int y, int x1, int x2, RGBA col, U32 depth)
545 int ainv = 255-col.a;
546 col.r = (col.r*col.a)>>8;
547 col.g = (col.g*col.a)>>8;
548 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;
569 static int inline clamp(int v)
571 if(v>255) return 255;
575 static void fill_bitmap(RGBA*line, int*z, int y, int x1, int x2, MATRIX*m, bitmap_t*b, int clipbitmap, U32 depth, double fmultiply)
579 double m11= m->sx*fmultiply/65536.0, m21= m->r1*fmultiply/65536.0;
580 double m12= m->r0*fmultiply/65536.0, m22= m->sy*fmultiply/65536.0;
581 double rx = m->tx*fmultiply/20.0;
582 double ry = m->ty*fmultiply/20.0;
584 double det = m11*m22 - m12*m21;
585 if(fabs(det) < 0.0005) {
586 /* x direction equals y direction- the image is invisible */
591 if(!b->width || !b->height) {
592 fill_solid(line, z, y, x1, x2, color_red, depth);
599 int xx = (int)(( (x - rx) * m22 - (y - ry) * m21)*det);
600 int yy = (int)((- (x - rx) * m12 + (y - ry) * m11)*det);
605 if(xx>=b->width) xx = b->width-1;
607 if(yy>=b->height) yy = b->height-1;
611 if(xx<0) xx += b->width;
612 if(yy<0) yy += b->height;
615 col = b->data[yy*b->width+xx];
618 line[x].r = clamp(((line[x].r*ainv)>>8)+col.r);
619 line[x].g = clamp(((line[x].g*ainv)>>8)+col.g);
620 line[x].b = clamp(((line[x].b*ainv)>>8)+col.b);
628 typedef struct _layer {
640 static void fill(RENDERBUF*dest, RGBA*line, int*zline, int y, int x1, int x2, state_t*fillstate, U32 clipdepth)
642 renderbuf_internal*i = (renderbuf_internal*)dest->internal;
645 layer_t*l = fillstate->layers;
647 if(x1>=x2) //zero width? nothing to do.
652 /* not filled. TODO: we should never add those in the first place */
654 printf("(not filled)");
655 } else if(l->fillid > l->p->s->numfillstyles) {
656 fprintf(stderr, "Fill style out of bounds (%d>%d)", l->fillid, l->p->s->numlinestyles);
657 } else if(clipdepth) {
658 /* filled region- not used for clipping */
663 printf("(%d -> %d style %d)", x1, x2, l->fillid);
665 f = &l->p->s->fillstyles[l->fillid-1];
667 if(f->type == FILL_SOLID) {
668 /* plain color fill */
669 fill_solid(line, zline, y, x1, x2, f->color, l->p->depth);
670 } else if(f->type == FILL_TILED || f->type == FILL_CLIPPED || f->type == (FILL_TILED|2) || f->type == (FILL_CLIPPED|2)) {
671 /* TODO: optimize (do this in add_pixel()?) */
672 bitmap_t* b = i->bitmaps;
673 while(b && b->id != f->id_bitmap) {
677 fprintf(stderr, "Shape references unknown bitmap %d\n", f->id_bitmap);
678 fill_solid(line, zline, y, x1, x2, color_red, l->p->depth);
680 fill_bitmap(line, zline, y, x1, x2, &f->m, b, /*clipped?*/f->type&1, l->p->depth, i->multiply);
683 fprintf(stderr, "Undefined fillmode: %02x\n", f->type);
688 if(clip && clipdepth) {
689 fill_clip(line, zline, y, x1, x2, clipdepth);
693 static void search_layer(state_t*state, int depth, layer_t**before, layer_t**self, layer_t**after)
695 layer_t*last=0,*l = state->layers;
696 while(l && l->p->depth < depth) {
701 if(l && l->p->depth == depth)
706 static void delete_layer(state_t*state, layer_t*todel)
708 layer_t*before=todel->prev;
709 layer_t*next = todel->next;
712 state->layers = next;
718 before->next->prev = before;
721 static void add_layer(state_t*state, layer_t*before, layer_t*toadd)
724 toadd->next = state->layers;
728 toadd->next = before->next;
729 toadd->prev = before;
730 before->next = toadd;
733 toadd->next->prev = toadd;
735 static void free_layers(state_t* state)
737 layer_t*l = state->layers;
739 layer_t*next = l->next;
745 static void change_state(int y, state_t* state, renderpoint_t*p)
747 layer_t*before=0, *self=0, *after=0;
750 printf("[(%d,%d)/%d/%d-%d]", p->x, y, p->depth, p->shapeline->fillstyle0, p->shapeline->fillstyle1);
753 search_layer(state, p->depth, &before, &self, &after);
757 if(self->fillid<0/*??*/ || !p->shapeline->fillstyle0 || !p->shapeline->fillstyle1) {
759 if(DEBUG&2) printf("<D>");
761 delete_layer(state, self);
763 /*both fill0 and fill1 are set- exchange the two, updating the layer */
764 if(self->fillid == p->shapeline->fillstyle0) {
765 self->fillid = p->shapeline->fillstyle1;
767 if(DEBUG&2) printf("<X>");
768 } else if(self->fillid == p->shapeline->fillstyle1) {
769 self->fillid = p->shapeline->fillstyle0;
771 if(DEBUG&2) printf("<X>");
773 /* buggy shape. keep everything as-is. */
774 if(DEBUG&2) printf("<!>");
775 //fprintf(stderr, "<line %d: bad swap>\n", y);
781 if(p->shapeline && p->shapeline->fillstyle0 && p->shapeline->fillstyle1) {
782 /* this is a hack- a better way would be to make sure that
783 we always get (0,32), (32, 33), (33, 0) in the right order if
784 they happen to fall on the same pixel.
785 (not: (0,32), (33, 0), (32, 33))
786 Notice: Weird fill styles appear if linestyles are involved, too.
788 fprintf(stderr, "<line %d: both fillstyles set while not inside shape>\n", y);
792 n = (layer_t*)rfx_calloc(sizeof(layer_t));
794 if(DEBUG&2) printf("<+>");
796 n->fillid = p->shapeline->fillstyle0 ? p->shapeline->fillstyle0 : p->shapeline->fillstyle1;
799 add_layer(state, before, n);
803 void swf_Process(RENDERBUF*dest, U32 clipdepth)
805 renderbuf_internal*i = (renderbuf_internal*)dest->internal;
808 if(i->ymax < i->ymin) {
809 /* shape is empty. return.
810 only, if it's a clipshape, remember the clipdepth */
812 for(y=0;y<i->height2;y++) {
813 if(clipdepth > i->lines[y].pending_clipdepth)
814 i->lines[y].pending_clipdepth = clipdepth;
817 return; //nothing (else) to do
821 /* lines outside the clip shape are not filled
822 immediately, only the highest clipdepth so far is
823 stored there. They will be clipfilled once there's
824 actually something about to happen in that line */
825 for(y=0;y<i->ymin;y++) {
826 if(clipdepth > i->lines[y].pending_clipdepth)
827 i->lines[y].pending_clipdepth = clipdepth;
829 for(y=i->ymax+1;y<i->height2;y++) {
830 if(clipdepth > i->lines[y].pending_clipdepth)
831 i->lines[y].pending_clipdepth = clipdepth;
835 for(y=i->ymin;y<=i->ymax;y++) {
837 TAG*tag = i->lines[y].points;
838 int num = i->lines[y].num;
839 renderpoint_t*points = (renderpoint_t*)tag->data;
840 RGBA*line = &i->img[i->width2*y];
841 int*zline = &i->zbuf[i->width2*y];
844 memset(&fillstate, 0, sizeof(state_t));
845 qsort(points, num, sizeof(renderpoint_t), compare_renderpoints);
849 printf("%f (%d/%d) %d\n", points[n].x,
850 points[n].shapeline->fillstyle0,
851 points[n].shapeline->fillstyle1,
852 points[n].shapeline->linestyle);
856 if(i->lines[y].pending_clipdepth && !clipdepth) {
857 fill_clip(line, zline, y, 0, i->width2, i->lines[y].pending_clipdepth);
858 i->lines[y].pending_clipdepth=0;
862 renderpoint_t*p = &points[n];
863 renderpoint_t*next= n<num-1?&points[n+1]:0;
864 int startx = (int)p->x;
865 int endx = (int)(next?next->x:i->width2);
874 /* for clipping, the inverse is filled
875 TODO: lastx!=startx only at the start of the loop,
876 so this might be moved up
878 fill_clip(line, zline, y, lastx, startx, clipdepth);
880 change_state(y, &fillstate, p);
882 fill(dest, line, zline, y, startx, endx, &fillstate, clipdepth);
883 /* if(y == 0 && startx == 232 && endx == 418) {
884 printf("ymin=%d ymax=%d\n", i->ymin, i->ymax);
886 renderpoint_t*p = &points[n];
887 printf("x=%f depth=%08x\n", p->x, p->depth);
892 if(endx == i->width2)
896 /* TODO: is lastx *ever* != i->width2 here? */
897 fill_clip(line, zline, y, lastx, i->width2, clipdepth);
899 free_layers(&fillstate);
902 swf_ClearTag(i->lines[y].points);
904 i->ymin = 0x7fffffff;
905 i->ymax = -0x80000000;
908 RGBA* swf_Render(RENDERBUF*dest)
910 renderbuf_internal*i = (renderbuf_internal*)dest->internal;
911 RGBA* img = (RGBA*)rfx_alloc(sizeof(RGBA)*dest->width*dest->height);
913 int antialize = i->antialize;
915 if(antialize <= 1) /* no antializing */ {
916 for(y=0;y<i->height2;y++) {
917 RGBA*line = &i->img[y*i->width2];
918 memcpy(&img[y*dest->width], line, sizeof(RGBA)*dest->width);
921 RGBA**lines = (RGBA**)rfx_calloc(sizeof(RGBA*)*antialize);
922 int q = antialize*antialize;
924 for(y=0;y<i->height2;y++) {
926 ypos = y % antialize;
927 lines[ypos] = &i->img[y*i->width2];
928 if(ypos == antialize-1) {
929 RGBA*out = &img[(y / antialize)*dest->width];
932 for(x=0;x<dest->width;x++) {
933 int xpos = x*antialize;
936 for(yp=0;yp<antialize;yp++) {
937 RGBA*lp = &lines[yp][xpos];
939 for(xp=0;xp<antialize;xp++) {
965 enum CHARACTER_TYPE {none_type, shape_type, image_type, text_type, font_type, sprite_type};
970 enum CHARACTER_TYPE type;
977 int compare_placements(const void *v1, const void *v2)
979 SWFPLACEOBJECT*p1 = (SWFPLACEOBJECT*)v1;
980 SWFPLACEOBJECT*p2 = (SWFPLACEOBJECT*)v2;
981 if(p1->depth != p2->depth)
982 return (int)p1->depth - (int)p2->depth;
985 return 1; // do the clip first
989 /* if(!p1->clipdepth) {
991 // !p1->clipdepth && !p2->clipdepth
992 return (int)p1->depth - (int)p2->depth;
994 // !p1->clipdepth && p2->clipdepth
995 if(p1->depth != p2->clipdepth)
996 return (int)p1->depth - (int)p2->clipdepth;
998 return 1; // do the clip first
1001 if(!p2->clipdepth) {
1002 // p1->clipdepth && !p2->clipdepth
1003 if(p1->clipdepth != p2->depth)
1004 return (int)p1->clipdepth - (int)p2->depth;
1006 return -1;// do the clip first
1008 if(p1->clipdepth != p2->clipdepth)
1009 return (int)p1->clipdepth - (int)p2->clipdepth;
1011 return (int)p1->depth - (int)p2->depth;
1016 typedef struct textcallbackblock
1018 character_t*idtable;
1024 } textcallbackblock_t;
1026 static void textcallback(void*self, int*chars, int*xpos, int nr, int fontid, int fontsize,
1027 int xstart, int ystart, RGBA* color)
1029 textcallbackblock_t * info = (textcallbackblock_t*)self;
1032 if(info->idtable[fontid].type != font_type) {
1033 fprintf(stderr, "ID %d is not a font\n", fontid);
1035 } else if(!info->idtable[fontid].obj.font) {
1036 fprintf(stderr, "Font %d unknown\n", fontid);
1039 font = info->idtable[fontid].obj.font;
1042 int x = xstart + xpos[t];
1048 p = swf_TurnPoint(p, &m);
1050 m.sx = (m.sx * fontsize) / 1024;
1051 m.sy = (m.sy * fontsize) / 1024;
1052 m.r0 = (m.r0 * fontsize) / 1024;
1053 m.r1 = (m.r1 * fontsize) / 1024;
1057 if(chars[t]<0 || chars[t]>= font->numchars) {
1058 fprintf(stderr, "Character out of range: %d\n", chars[t]);
1060 SHAPE2*shape = font->glyphs[chars[t]];
1061 shape->fillstyles[0].color = *color; //q&d
1062 /*printf("Rendering char %d (size %d, x:%d, y:%d) color:%02x%02x%02x%02x\n", chars[t], fontsize, x, y,
1063 color->a, color->r, color->g, color->b);
1064 swf_DumpMatrix(stdout, &m);
1065 swf_DumpShape(shape);*/
1066 swf_RenderShape(info->buf, shape, &m, info->cxform, info->depth, info->clipdepth);
1071 static void renderFromTag(RENDERBUF*buf, character_t*idtable, TAG*firstTag, MATRIX*m)
1074 int numplacements = 0;
1075 SWFPLACEOBJECT* placements;
1080 if(tag->id == ST_PLACEOBJECT ||
1081 tag->id == ST_PLACEOBJECT2) {
1084 if(tag->id == ST_SHOWFRAME || tag->id == ST_END)
1088 placements = (SWFPLACEOBJECT*)rfx_calloc(sizeof(SWFPLACEOBJECT)*numplacements);
1093 if(swf_isPlaceTag(tag)) {
1095 swf_GetPlaceObject(tag, &p);
1096 /* TODO: add move and deletion */
1097 placements[numplacements++] = p;
1098 swf_PlaceObjectFree(&p); //dirty! but it only frees fields we don't use
1100 if(tag->id == ST_SHOWFRAME || tag->id == ST_END)
1105 qsort(placements, numplacements, sizeof(SWFPLACEOBJECT), compare_placements);
1108 for(t=0;t<numplacements;t++) {
1109 SWFPLACEOBJECT*p = &placements[t];
1112 swf_MatrixJoin(&m2, m, &p->matrix);
1114 if(!idtable[id].tag) {
1115 fprintf(stderr, "rfxswf: Id %d is unknown\n", id);
1119 if(idtable[id].type == shape_type) {
1120 //SRECT sbbox = swf_TurnRect(*idtable[id].bbox, &p->matrix);
1121 swf_RenderShape(buf, idtable[id].obj.shape, &m2, &p->cxform, p->depth, p->clipdepth);
1122 } else if(idtable[id].type == sprite_type) {
1123 swf_UnFoldSprite(idtable[id].tag);
1124 renderFromTag(buf, idtable, idtable[id].tag->next, &m2);
1125 swf_FoldSprite(idtable[id].tag);
1126 } else if(idtable[id].type == text_type) {
1127 TAG* tag = idtable[id].tag;
1128 textcallbackblock_t info;
1131 swf_SetTagPos(tag, 0);
1134 swf_GetMatrix(tag,&mt);
1135 swf_MatrixJoin(&info.m, &m2, &mt);
1136 /*printf("Text matrix:\n");
1137 swf_DumpMatrix(stdout, &m);
1138 printf("Placement matrix:\n");
1139 swf_DumpMatrix(stdout, &p->matrix);
1140 printf("Final matrix:\n");
1141 swf_DumpMatrix(stdout, &info.m);*/
1143 info.idtable = idtable;
1144 info.depth = p->depth;
1145 info.cxform = &p->cxform;
1146 info.clipdepth = p->clipdepth;
1149 swf_ParseDefineText(tag, textcallback, &info);
1151 fprintf(stderr, "Unknown/Unsupported Object Type for id %d: %s\n", id, swf_TagGetName(idtable[id].tag));
1158 void swf_RenderSWF(RENDERBUF*buf, SWF*swf)
1166 character_t* idtable = (character_t*)rfx_calloc(sizeof(character_t)*65536); // id to character mapping
1168 /* set background color */
1169 color = swf_GetSWFBackgroundColor(swf);
1170 swf_Render_SetBackgroundColor(buf, color);
1172 /* parse definitions */
1173 tag = swf->firstTag;
1175 if(swf_isDefiningTag(tag)) {
1176 int id = swf_GetDefineID(tag);
1177 idtable[id].tag = tag;
1178 idtable[id].bbox = (SRECT*)rfx_alloc(sizeof(SRECT));
1179 *idtable[id].bbox = swf_GetDefineBBox(tag);
1181 if(swf_isShapeTag(tag)) {
1182 SHAPE2* shape = (SHAPE2*)rfx_calloc(sizeof(SHAPE2));
1183 swf_ParseDefineShape(tag, shape);
1184 idtable[id].type = shape_type;
1185 idtable[id].obj.shape = shape;
1186 } else if(swf_isImageTag(tag)) {
1188 RGBA*data = swf_ExtractImage(tag, &width, &height);
1189 idtable[id].type = image_type;
1190 swf_Render_AddImage(buf, id, data, width, height);
1192 } else if(tag->id == ST_DEFINEFONT ||
1193 tag->id == ST_DEFINEFONT2) {
1196 font_t*font = (font_t*)rfx_calloc(sizeof(font_t));
1197 idtable[id].obj.font = font;
1198 swf_FontExtract(swf,id,&swffont);
1199 font->numchars = swffont->numchars;
1200 font->glyphs = (SHAPE2**)rfx_calloc(sizeof(SHAPE2*)*font->numchars);
1201 for(t=0;t<font->numchars;t++) {
1202 if(!swffont->glyph[t].shape->fillstyle.n) {
1203 /* the actual fill color will be overwritten while rendering */
1204 swf_ShapeAddSolidFillStyle(swffont->glyph[t].shape, &color_white);
1206 font->glyphs[t] = swf_ShapeToShape2(swffont->glyph[t].shape);
1208 swf_FontFree(swffont);
1209 idtable[id].type = font_type;
1211 } else if(tag->id == ST_DEFINEFONTINFO ||
1212 tag->id == ST_DEFINEFONTINFO2) {
1213 idtable[id].type = font_type;
1214 } else if(tag->id == ST_DEFINETEXT ||
1215 tag->id == ST_DEFINETEXT2) {
1216 idtable[id].type = text_type;
1217 } else if(tag->id == ST_DEFINESPRITE) {
1218 idtable[id].type = sprite_type;
1224 swf_GetMatrix(0, &m);
1225 renderFromTag(buf, idtable, swf->firstTag, &m);
1227 /* free id and depth tables again */
1228 for(t=0;t<65536;t++) {
1229 if(idtable[t].bbox) {
1230 free(idtable[t].bbox);
1233 if(idtable[t].type == shape_type) {
1234 SHAPE2* shape = idtable[t].obj.shape;
1236 swf_Shape2Free(shape); // FIXME
1237 free(idtable[t].obj.shape);idtable[t].obj.shape = 0;
1239 } else if(idtable[t].type == font_type) {
1240 font_t* font = idtable[t].obj.font;
1244 for(t=0;t<font->numchars;t++) {
1245 swf_Shape2Free(font->glyphs[t]);
1246 free(font->glyphs[t]); font->glyphs[t] = 0;
1251 free(idtable[t].obj.font); idtable[t].obj.font = 0;