X-Git-Url: http://git.asbjorn.biz/?p=swftools.git;a=blobdiff_plain;f=lib%2Freaders%2Fswf.c;h=7cb3580933b7e541902fc5215ac62b62628f9448;hp=783d5699d4d674cd4870b4555de15679130f6ec0;hb=2391d7ae5d8a145a250a8b80ab8c93ba74eba030;hpb=c8f6470122c3fa0bd990df2d1597ae60023d4df1 diff --git a/lib/readers/swf.c b/lib/readers/swf.c index 783d569..7cb3580 100644 --- a/lib/readers/swf.c +++ b/lib/readers/swf.c @@ -1,8 +1,11 @@ +#include +#include #include "../gfxdevice.h" #include "../gfxsource.h" #include "../gfxtools.h" #include "../log.h" #include "../mem.h" +#include "../png.h" #include "../rfxswf.h" #include "swf.h" @@ -19,7 +22,6 @@ typedef struct _swf_page_internal typedef struct _swf_doc_internal { map16_t*id2char; - int clips; SWF swf; int width,height; MATRIX m; @@ -51,6 +53,17 @@ typedef struct _sprite int frameCount; } sprite_t; +struct _render +{ + map16_t*id2char; + gfxdevice_t*device; + MATRIX m; + int clips; + int*clips_waiting; +}; +typedef struct _render render_t; + + static void placement_free(placement_t*p) { swf_PlaceObjectFree(&p->po); @@ -79,7 +92,7 @@ void map16_free(map16_t*map) void map16_add_id(map16_t*map, int nr, void*id) { if(map->ids[nr]) - fprintf(stderr, "Warning: ID %d defined more than once\n"); + fprintf(stderr, "Warning: ID %d defined more than once\n", nr); map->ids[nr] = id; } void map16_remove_id(map16_t*map, int nr) @@ -95,32 +108,213 @@ void map16_enumerate(map16_t*map, void (*f)(void*self, int id, void*data), void* } } } +//---- conversion stuff ---- -//---- bitmap handling ---- +static void convertMatrix(MATRIX*from, gfxmatrix_t*to) +{ + to->m00 = from->sx / 65536.0; to->m10 = from->r1 / 65536.0; + to->m01 = from->r0 / 65536.0; to->m11 = from->sy / 65536.0; + to->tx = from->tx/20.0; + to->ty = from->ty/20.0; +} -typedef struct _bitmap +static void convertCXForm(CXFORM*from, gfxcxform_t*to) { - int width, height; - RGBA*data; -} bitmap_t; + memset(to, 0, sizeof(gfxcxform_t)); + to->aa = from->a0 / 256.0; + to->rr = from->r0 / 256.0; + to->gg = from->g0 / 256.0; + to->bb = from->b0 / 256.0; + to->ta = from->a1; + to->tr = from->r1; + to->tg = from->g1; + to->tb = from->b1; +} + +static gfxgradient_t* convertGradient(GRADIENT*from) +{ + gfxgradient_t*g = rfx_calloc(from->num * sizeof(gfxgradient_t)); + int t; + for(t=0;tnum;t++) { + g[t].pos = from->ratios[t] / 255.0; + g[t].color = *(gfxcolor_t*)&from->rgba[t]; + if(tnum-1) + g[t].next = &g[t+1]; + else + g[t].next = 0; + } + return g; +} + +gfxline_t* swfline_to_gfxline(SHAPELINE*line, int linestyle, int fillstyle0) +{ + gfxdrawer_t d; + SCOORD x=0,y=0,xx=0,yy=0; + gfxline_t*l; + gfxdrawer_target_gfxline(&d); + if(line && line->type != moveTo) { + fprintf(stderr, "Warning: Shape doesn't start with a moveTo\n"); + } + xx = line?line->x+1:0; + while(line) { + if(line->fillstyle0 == fillstyle0 || + line->fillstyle1 == fillstyle0 || + line->linestyle == linestyle) { + if(line->type == lineTo) { + if(xx!=x || yy!=y) d.moveTo(&d, x/20.0,y/20.0); + d.lineTo(&d, line->x/20.0,line->y/20.0); + xx = line->x; + yy = line->y; + } else if(line->type == splineTo) { + if(xx!=x || yy!=y) d.moveTo(&d, x/20.0,y/20.0); + d.splineTo(&d, line->sx/20.0, line->sy/20.0, line->x/20.0,line->y/20.0); + xx = line->x; + yy = line->y; + } + } + x = line->x; + y = line->y; + line = line->next; + } + l = d.result(&d); + return l; +} -bitmap_t* bitmap_new(RGBA*data, int width, int height) + +//---- bitmap handling ---- + +gfximage_t* gfximage_new(RGBA*data, int width, int height) { - bitmap_t* b = (bitmap_t*)rfx_calloc(sizeof(bitmap_t)); - b->data = data; + gfximage_t* b = (gfximage_t*)rfx_calloc(sizeof(gfximage_t)); + b->data = (gfxcolor_t*)data; b->width = width; b->height = height; return b; } -void bitmap_free(bitmap_t*b) +void gfximage_free(gfximage_t*b) { free(b->data); //! b->data = 0; free(b); } -//---- handling ---- +static gfximage_t* findimage(render_t*r, U16 id) +{ + character_t*c = (character_t*)map16_get_id(r->id2char, id); + assert(c && c->type == TYPE_BITMAP); + gfximage_t*img = (gfximage_t*)c->data; + + /*char filename[80]; + sprintf(filename, "bitmap%d.png", id); + writePNG(filename, (unsigned char*)img->data, img->width, img->height); + printf("saving bitmap %d to %s\n", id, filename);*/ + + return c->data; +} +//---- shape handling ---- + +static void renderFilled(render_t*r, gfxline_t*line, FILLSTYLE*f, CXFORM*cx, MATRIX*po_m) +{ + if(f->type == FILL_SOLID) { + gfxcolor_t c = *(gfxcolor_t*)&f->color; + r->device->fill(r->device, line, &c); + } else if(f->type == FILL_TILED || f->type == FILL_CLIPPED) { + gfximage_t* img = findimage(r, f->id_bitmap); + gfxmatrix_t m; + gfxcxform_t gfxcx; + convertCXForm(cx, &gfxcx); + MATRIX m2; + swf_MatrixJoin(&m2, po_m, &f->m); + convertMatrix(&m2, &m); + m.m00/=20.0; m.m10/=20.0; + m.m01/=20.0; m.m11/=20.0; + /* TODO: handle clipped */ + r->device->fillbitmap(r->device, line, img, &m, &gfxcx); + } else if(f->type == FILL_LINEAR || f->type == FILL_RADIAL) { + gfxmatrix_t m; + gfxgradient_t* g; + MATRIX* m2 = &f->m; + //swf_MatrixJoin(&m2, po_m, &f->m); + + double z = f->type==FILL_RADIAL?4:4; + m.m00 = m2->sx/z/20.0; m.m10 = m2->r1/z/20.0; + m.m01 = m2->r0/z/20.0; m.m11 = m2->sy/z/20.0; + m.tx = m2->tx/20.0; + m.ty = m2->ty/20.0; + + g = convertGradient(&f->gradient); + r->device->fillgradient(r->device, line, g, f->type == FILL_LINEAR ? gfxgradient_linear : gfxgradient_radial, &m); + free(g); + } +} + +//---- font handling ---- + +typedef struct +{ + int numchars; + gfxline_t**glyphs; +} font_t; + +typedef struct textcallbackblock +{ + render_t*r; + MATRIX m; +} textcallbackblock_t; + +static void textcallback(void*self, int*chars, int*xpos, int nr, int fontid, int fontsize, + int xstart, int ystart, RGBA* color) +{ + textcallbackblock_t * info = (textcallbackblock_t*)self; + font_t*font = 0; + int t; + character_t*cfont = map16_get_id(info->r->id2char, fontid); + if(!cfont) { + fprintf(stderr, "Font %d unknown\n", fontid); + return; + } + if(cfont->type != TYPE_FONT) { + fprintf(stderr, "ID %d is not a font\n", fontid); + return; + } + font = cfont->data; + + for(t=0;tm; + SPOINT p; + + p.x = x; p.y = y; + p = swf_TurnPoint(p, &m); + + m.sx = (m.sx * fontsize) / 1024; + m.sy = (m.sy * fontsize) / 1024; + m.r0 = (m.r0 * fontsize) / 1024; + m.r1 = (m.r1 * fontsize) / 1024; + m.tx = p.x; + m.ty = p.y; + + gfxmatrix_t gm; + convertMatrix(&m, &gm); + + if(chars[t]<0 || chars[t]>= font->numchars) { + fprintf(stderr, "Character out of range: %d\n", chars[t]); + } else { + gfxline_t*line = gfxline_clone(font->glyphs[chars[t]]); + gfxline_transform(line, &gm); + FILLSTYLE f; + f.type = FILL_SOLID; + f.color = *color; + renderFilled(info->r, line, &f, 0, 0); + gfxline_free(line); + } + } +} + + +//---- tag handling ---- static map16_t* extractDefinitions(SWF*swf) { @@ -155,13 +349,36 @@ static map16_t* extractDefinitions(SWF*swf) else if(tag->id == ST_DEFINEFONT || tag->id == ST_DEFINEFONT2) { character_t*c = rfx_calloc(sizeof(character_t)); - SWFFONT*font = 0; - swf_FontExtract(swf, id, &font); + SWFFONT*swffont = 0; + font_t*font = (font_t*)rfx_calloc(sizeof(font_t)); + swf_FontExtract(swf, id, &swffont); + font->numchars = swffont->numchars; + font->glyphs = (gfxline_t**)rfx_calloc(sizeof(gfxline_t*)*font->numchars); + int t; + RGBA color_white = {255,255,255,255}; + for(t=0;tnumchars;t++) { + if(!swffont->glyph[t].shape->fillstyle.n) { + swf_ShapeAddSolidFillStyle(swffont->glyph[t].shape, &color_white); + } + SHAPE2*s2 = swf_ShapeToShape2(swffont->glyph[t].shape); + font->glyphs[t] = swfline_to_gfxline(s2->lines, 0, 1); + swf_Shape2Free(s2); + } + swf_FontFree(swffont); + c->tag = tag; c->type = TYPE_FONT; c->data = font; map16_add_id(map, id, c); } + else if(tag->id == ST_DEFINETEXT || + tag->id == ST_DEFINETEXT2) { + character_t*c = rfx_calloc(sizeof(character_t)); + c->tag = tag; + c->type = TYPE_TEXT; + c->data = 0; + map16_add_id(map, id, c); + } else if(tag->id == ST_DEFINEBITSJPEG || tag->id == ST_DEFINEBITSJPEG2 || tag->id == ST_DEFINEBITSJPEG3 || @@ -170,7 +387,7 @@ static map16_t* extractDefinitions(SWF*swf) character_t*c = rfx_calloc(sizeof(character_t)); int width, height; void*data = swf_ExtractImage(tag, &width, &height); - bitmap_t*b = bitmap_new(data, width, height); + gfximage_t*b = gfximage_new(data, width, height); c->tag = tag; c->type = TYPE_BITMAP; c->data = b; @@ -206,9 +423,11 @@ static map16_t* extractFrame(TAG*startTag, int frame_to_extract) TAG*tag = startTag; int frame = 1; int insprite = 0; + + SWF*swf = rfx_calloc(sizeof(SWF)); + swf->firstTag = startTag; + for(;tag;tag = tag->next) { - if(tag->id == ST_END) - break; if(tag->id == ST_DEFINESPRITE) { while(tag->id != ST_END) tag = tag->next; @@ -234,7 +453,7 @@ static map16_t* extractFrame(TAG*startTag, int frame_to_extract) U16 depth = swf_GetDepth(tag); map16_remove_id(depthmap, depth); } - if(tag->id == ST_SHOWFRAME || tag->id == ST_END) { + if(tag->id == ST_SHOWFRAME || tag->id == ST_END || !tag->next) { if(frame == frame_to_extract) { return depthmap; } @@ -243,125 +462,18 @@ static map16_t* extractFrame(TAG*startTag, int frame_to_extract) map16_enumerate(depthmap, increaseAge, 0); } } + if(tag->id == ST_END) + break; } - fprintf(stderr, "gfxsource_swf: frame %d not found\n", frame_to_extract); return depthmap; } -// ---- render handling ---- - -typedef struct _render -{ - map16_t*id2char; - gfxdevice_t*device; - MATRIX m; - int clips; -} render_t; - -static void stopClippings(int from, render_t*r) -{ - int t; - for(t=from;tclips;t++) - r->device->endclip(r->device); - r->clips = from; -} - -gfxline_t* swfline_to_gfxline(SHAPELINE*line, int linestyle, int fillstyle0) -{ - gfxdrawer_t d; - SCOORD x=0,y=0; - gfxline_t*l; - gfxdrawer_target_gfxline(&d); - if(line->type != moveTo) { - fprintf(stderr, "Warning: Shape doesn't start with a moveTo\n"); - } - while(line) { - if(line->fillstyle0 == fillstyle0 || line->fillstyle1 == fillstyle0 || - line->linestyle == linestyle) { - if(line->type == lineTo) { - d.moveTo(&d, x/20.0,y/20.0); - d.lineTo(&d, line->x/20.0,line->y/20.0); - } else if(line->type == splineTo) { - d.moveTo(&d, x/20.0,y/20.0); - d.splineTo(&d, line->sx/20.0, line->sy/20.0, line->x/20.0,line->y/20.0); - } - } - x = line->x; - y = line->y; - line = line->next; - } - l = d.result(&d); - return l; -} +// ---- rendering ---- void swf_ShapeApplyMatrix(SHAPE2*shape, MATRIX*m) { } -static void convertMatrix(MATRIX*from, gfxmatrix_t*to) -{ - to->m00 = from->sx / 65536.0; to->m10 = from->r1 / 65536.0; - to->m01 = from->r0 / 65536.0; to->m11 = from->sy / 65536.0; - to->tx = from->tx/20.0; - to->ty = from->ty/20.0; -} - -static void convertCXForm(CXFORM*from, gfxcxform_t*to) -{ - memset(to, 0, sizeof(gfxcxform_t)); - to->aa = from->a0 / 256.0; - to->rr = from->r0 / 256.0; - to->gg = from->g0 / 256.0; - to->bb = from->b0 / 256.0; - to->ta = from->a1; - to->tr = from->r1; - to->tg = from->g1; - to->tb = from->b1; -} - -static gfxgradient_t* convertGradient(GRADIENT*from) -{ - gfxgradient_t*g = rfx_calloc(from->num * sizeof(gfxgradient_t)); - int t; - for(t=0;tnum;t++) { - g[t].pos = from->ratios[t] / 255.0; - g[t].color = *(gfxcolor_t*)&from->rgba[t]; - if(tnum-1) - g[t].next = &g[t+1]; - else - g[t].next = 0; - } - return g; -} - -gfximage_t* findimage(render_t*r) -{ - return 0; -} - -static void renderFilled(render_t*r, gfxline_t*line, FILLSTYLE*f, CXFORM*cx) -{ - if(f->type == FILL_SOLID) { - gfxcolor_t c = *(gfxcolor_t*)&f->color; - r->device->fill(r->device, line, &c); - } else if(f->type == FILL_TILED || f->type == FILL_CLIPPED) { - gfximage_t* img = findimage(r); - gfxmatrix_t m; - gfxcxform_t gfxcx; - convertCXForm(cx, &gfxcx); - convertMatrix(&f->m, &m); - /* TODO: handle clipped */ - r->device->fillbitmap(r->device, line, img, &m, &gfxcx); - } else if(f->type == FILL_LINEAR || f->type == FILL_RADIAL) { - gfxmatrix_t m; - gfxgradient_t* g; - convertMatrix(&f->m, &m); - g = convertGradient(&f->gradient); - r->device->fillgradient(r->device, line, g, f->type == FILL_LINEAR ? gfxgradient_linear : gfxgradient_radial, &m); - free(g); - } -} - RGBA swf_ColorTransform(RGBA*color, CXFORM*cx) { RGBA dest; @@ -406,10 +518,10 @@ static void renderCharacter(render_t*r, placement_t*p, character_t*c) line = swfline_to_gfxline(shape.lines, -1, t); if(line) { if(!p->po.clipdepth) { - renderFilled(r, line, &shape.fillstyles[t-1], &p->po.cxform); + renderFilled(r, line, &shape.fillstyles[t-1], &p->po.cxform, &p->po.matrix); } else { r->device->startclip(r->device, line); - r->clips++; + r->clips_waiting[p->po.clipdepth]++; } } gfxline_free(line); @@ -422,6 +534,19 @@ static void renderCharacter(render_t*r, placement_t*p, character_t*c) if(line) renderOutline(r, line, &shape.linestyles[t-1], &p->po.cxform); gfxline_free(line); } + } else if(c->type == TYPE_TEXT) { + TAG* tag = c->tag; + textcallbackblock_t info; + MATRIX mt,mt2; + swf_SetTagPos(tag, 0); + swf_GetU16(tag); + swf_GetRect(tag,0); + swf_GetMatrix(tag,&mt); + + swf_MatrixJoin(&mt2, &r->m, &mt); + swf_MatrixJoin(&info.m, &mt2, &p->po.matrix); + info.r = r; + swf_ParseDefineText(tag, textcallback, &info); } } @@ -433,15 +558,28 @@ static void placeObject(void*self, int id, void*data) placement_t*p = (placement_t*)data; character_t*c = map16_get_id(r->id2char, p->po.id); if(!c) { - fprintf(stderr, "Error: ID %d unknown\n", p->po.id); + fprintf(stderr, "Error: ID %d unknown\n", p->po.id); + return; } if(c->type == TYPE_SPRITE) { - int oldclip = r->clips; - sprite_t* s = (sprite_t*)c->data; - map16_t* depths = extractFrame(c->tag, p->age % s->frameCount); - map16_enumerate(depths, placeObject, r); - stopClippings(oldclip, r); - return; + int*old_clips_waiting = r->clips_waiting; + r->clips_waiting = rfx_calloc(sizeof(r->clips_waiting[0])*65536); + + sprite_t* s = (sprite_t*)c->data; + + map16_t* depths = extractFrame(c->tag->next, p->age % s->frameCount); + map16_enumerate(depths, placeObject, r); + + int t; + for(t=0;t<65536;t++) { + int i; + for(i=0; iclips_waiting[t]; i++) { + r->device->endclip(r->device); + } + } + free(r->clips_waiting); + r->clips_waiting = old_clips_waiting; + return; } renderCharacter(r, p, c); } @@ -463,7 +601,20 @@ void swfpage_render(gfxpage_t*page, gfxdevice_t*output) r.clips = 0; r.device = output; r.m = pi->m; - map16_enumerate(depths, placeObject, &r); + r.clips_waiting = malloc(sizeof(r.clips_waiting[0])*65536); + memset(r.clips_waiting, 0, sizeof(r.clips_waiting[0])*65536); + + int t; + for(t=0;t<65536;t++) { + if(depths->ids[t]) { + placeObject(&r, t, depths->ids[t]); + } + int i; + for(i=0; iendclip(output); + } + } + free(r.clips_waiting); } void swfpage_rendersection(gfxpage_t*page, gfxdevice_t*output, gfxcoord_t x, gfxcoord_t y, gfxcoord_t _x1, gfxcoord_t _y1, gfxcoord_t _x2, gfxcoord_t _y2) @@ -481,7 +632,7 @@ void swf_doc_destroy(gfxdocument_t*gfx) free(gfx);gfx=0; } -void swf_doc_set_parameter(gfxdocument_t*gfx, const char*name, const char*value) +void swf_doc_setparameter(gfxdocument_t*gfx, const char*name, const char*value) { swf_doc_internal_t*i= (swf_doc_internal_t*)gfx->internal; } @@ -509,7 +660,7 @@ gfxpage_t* swf_doc_getpage(gfxdocument_t*doc, int page) return swf_page; } -void swf_set_parameter(gfxsource_t*src, const char*name, const char*value) +void swf_setparameter(gfxsource_t*src, const char*name, const char*value) { msg(" setting parameter %s to \"%s\"", name, value); } @@ -533,12 +684,12 @@ gfxdocument_t*swf_open(gfxsource_t*src, const char*filename) f = open(filename,O_RDONLY|O_BINARY); if (f<0) { perror("Couldn't open file: "); - return 0; + return 0; } if FAILED(swf_ReadSWF(f,&i->swf)) { fprintf(stderr, "%s is not a valid SWF file or contains errors.\n",filename); close(f); - return 0; + return 0; } swf_UnFoldAll(&i->swf); @@ -554,17 +705,26 @@ gfxdocument_t*swf_open(gfxsource_t*src, const char*filename) swf_doc->internal = i; swf_doc->get = 0; swf_doc->destroy = swf_doc_destroy; - swf_doc->set_parameter = swf_doc_set_parameter; + swf_doc->setparameter = swf_doc_setparameter; swf_doc->getpage = swf_doc_getpage; return swf_doc; } +static void swf_destroy(gfxsource_t*src) +{ + memset(src, 0, sizeof(*src)); + free(src); +} + + gfxsource_t*gfxsource_swf_create() { gfxsource_t*src = (gfxsource_t*)malloc(sizeof(gfxsource_t)); memset(src, 0, sizeof(gfxsource_t)); - src->set_parameter = swf_set_parameter; + src->setparameter = swf_setparameter; src->open = swf_open; + src->destroy = swf_destroy; return src; } +