more horizontal refactoring
[swftools.git] / lib / modules / swfshape.c
1 /* swfshape.c
2
3    shape functions
4       
5    Extension module for the rfxswf library.
6    Part of the swftools package.
7
8    Copyright (c) 2001 Rainer Böhme <rfxswf@reflex-studio.de>
9  
10    This program is free software; you can redistribute it and/or modify
11    it under the terms of the GNU General Public License as published by
12    the Free Software Foundation; either version 2 of the License, or
13    (at your option) any later version.
14
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License for more details.
19
20    You should have received a copy of the GNU General Public License
21    along with this program; if not, write to the Free Software
22    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
23
24 #include "../rfxswf.h"
25
26 #define SF_MOVETO       0x01
27 #define SF_FILL0        0x02
28 #define SF_FILL1        0x04
29 #define SF_LINE         0x08
30 #define SF_NEWSTYLE     0x10
31
32 void swf_ShapeFree(SHAPE * s)
33
34     if(!s)
35         return;
36     if (s->linestyle.data) rfx_free(s->linestyle.data);
37     s->linestyle.data = NULL;
38     s->linestyle.n    = 0;
39     if (s->fillstyle.data) rfx_free(s->fillstyle.data);
40     s->fillstyle.data = NULL;
41     s->fillstyle.n    = 0;
42     if (s->data) rfx_free(s->data);
43     s->data = NULL;
44     rfx_free(s);
45 }
46
47 int swf_ShapeNew(SHAPE * * s)
48
49     SHAPE * sh;
50     if (!s) return -1;
51     sh = (SHAPE *)rfx_calloc(sizeof(SHAPE)); 
52     *s = sh;
53     return 0;
54 }
55
56 int swf_GetSimpleShape(TAG * t,SHAPE * * s) // without Linestyle/Fillstyle Record
57 { SHAPE * sh;
58   int bitl, len;
59   int end;
60   U32 pos;
61   
62   if (FAILED(swf_ShapeNew(s))) return -1;
63   sh = s[0];
64
65   swf_ResetReadBits(t); 
66   sh->bits.fill = (U16)swf_GetBits(t,4);
67   sh->bits.line = (U16)swf_GetBits(t,4);
68   bitl = 0; end = 0; pos = swf_GetTagPos(t);
69
70   while (!end)
71   { int edge = swf_GetBits(t,1); bitl+=1;
72     if (edge)
73     { bitl+=1;
74       if (swf_GetBits(t,1))                 // Line
75       { U16 nbits = swf_GetBits(t,4)+2;
76         bitl+=5;
77
78         if (swf_GetBits(t,1))               // x/y Line
79         { swf_GetBits(t,nbits);
80           swf_GetBits(t,nbits);
81           bitl+=nbits*2;
82         }
83         else                            // hline/vline
84         { swf_GetBits(t,nbits+1);
85           bitl+=nbits+1;
86         }
87       }
88       else                              // Curve
89       { U16 nbits = swf_GetBits(t,4)+2;
90         bitl+=4;
91
92         swf_GetBits(t,nbits);
93         swf_GetBits(t,nbits);
94         swf_GetBits(t,nbits);
95         swf_GetBits(t,nbits);
96
97         bitl+=4*nbits;
98       }
99     }
100     else
101     { U16 flags = swf_GetBits(t,5); bitl+=5;
102       if (flags)
103       {
104         if (flags&SF_MOVETO)
105         { U16 nbits = swf_GetBits(t,5); bitl+=5;
106           swf_GetBits(t,nbits);
107           swf_GetBits(t,nbits);
108           bitl+=2*nbits;
109         }
110         
111         if (flags&SF_FILL0)
112         { swf_GetBits(t,sh->bits.fill);
113           bitl+=sh->bits.fill;
114         }
115         
116         if (flags&SF_FILL1)
117         { swf_GetBits(t,sh->bits.fill);
118           bitl+=sh->bits.fill;
119         }
120
121         if (flags&SF_LINE)
122         { swf_GetBits(t,sh->bits.line);
123           bitl+=sh->bits.line;
124         }
125
126         if (flags&SF_NEWSTYLE)
127         { fprintf(stderr,"RFXSWF: Can't process extended styles in shape.\n");
128         }
129       }
130       else end = 1;
131     }
132   }
133   swf_SetTagPos(t,pos);
134   len = (bitl+7)/8;
135   
136   if (sh->data) rfx_free(sh->data);
137   sh->data = (U8*)rfx_alloc(len);
138   
139   if (sh->data)
140   { sh->bitlen = bitl;
141     swf_GetBlock(t,sh->data,len);
142   }
143   else return -1;
144   
145   return len;
146 }
147
148 int swf_SetSimpleShape(TAG * t,SHAPE * s) // without Linestyle/Fillstyle Record
149 { int l;
150
151   if (!s) return -1;
152   l = (s->bitlen+7)/8;
153
154   if (t)
155   { swf_ResetWriteBits(t);
156
157     swf_SetBits(t,s->bits.fill,4);
158     swf_SetBits(t,s->bits.line,4);
159     swf_SetBlock(t,s->data,l);
160
161     swf_ResetWriteBits(t);
162   }
163   return l+1;
164 }
165
166 int swf_SetFillStyle(TAG * t,FILLSTYLE * f)
167 { if ((!t)||(!f)) return -1;
168   swf_SetU8(t,f->type);
169   
170   switch (f->type)
171   { case FILL_SOLID:
172       if (swf_GetTagID(t)!=ST_DEFINESHAPE3) swf_SetRGB(t,&f->color);
173       else swf_SetRGBA(t,&f->color);
174       break;
175
176     case FILL_TILED:
177     case FILL_CLIPPED:
178       swf_SetU16(t,f->id_bitmap);
179       swf_SetMatrix(t,&f->m);
180       break;
181     case FILL_LINEAR:
182     case FILL_RADIAL:
183       swf_SetMatrix(t,&f->m);
184       swf_SetGradient(t,&f->gradient,/*alpha?*/t->id==ST_DEFINESHAPE3?1:0);
185       break;
186   }
187   
188   return 0;
189 }
190
191 int swf_SetLineStyle(TAG * t,LINESTYLE * l)
192 { if ((!l)||(!t)) return -1;
193   swf_SetU16(t,l->width);
194
195   if (swf_GetTagID(t)!=ST_DEFINESHAPE3) swf_SetRGB(t,&l->color);
196   else swf_SetRGBA(t,&l->color);
197   
198   return 0;
199 }
200
201 int swf_SetShapeStyleCount(TAG * t,U16 n)
202 { if (n>254)
203   { swf_SetU8(t,0xff);
204     swf_SetU16(t,n);
205     return 3;
206   }
207   else
208   { swf_SetU8(t,(U8)n);
209     return 1;
210   }
211 }
212
213 int swf_SetShapeStyles(TAG * t,SHAPE * s)
214 { int i,l;
215   if (!s) return -1;
216
217   l = 0;
218   l += swf_SetShapeStyleCount(t,s->fillstyle.n);
219
220   for (i=0;i<s->fillstyle.n;i++)
221     l+=swf_SetFillStyle(t,&s->fillstyle.data[i]);
222
223   l += swf_SetShapeStyleCount(t,s->linestyle.n);
224
225   for (i=0;i<s->linestyle.n;i++)
226     l+=swf_SetLineStyle(t,&s->linestyle.data[i]);
227
228   return l;
229 }
230
231 int swf_ShapeCountBits(SHAPE * s,U8 * fbits,U8 * lbits)
232 { if (!s) return -1;
233   s->bits.fill = swf_CountUBits(s->fillstyle.n, 0);
234   s->bits.line = swf_CountUBits(s->linestyle.n, 0);
235   if (fbits) fbits[0] = s->bits.fill;
236   if (lbits) lbits[0] = s->bits.line;
237   return 0;    
238 }
239
240 int swf_SetShapeBits(TAG * t,SHAPE * s)
241 { if ((!t)||(!s)) return -1;
242   swf_ResetWriteBits(t);
243   swf_SetBits(t,s->bits.fill,4);
244   swf_SetBits(t,s->bits.line,4);
245   return 0;
246 }
247
248 int swf_SetShapeHeader(TAG * t,SHAPE * s)
249 { int res;
250   res = swf_SetShapeStyles(t,s);
251   if (res>=0) res = swf_ShapeCountBits(s,NULL,NULL);
252   if (res>=0) res = swf_SetShapeBits(t,s);
253   return res;
254 }
255
256 int swf_ShapeAddFillStyle(SHAPE * s,U8 type,MATRIX * m,RGBA * color,U16 id_bitmap, GRADIENT*gradient)
257 { RGBA def_c;
258   MATRIX def_m;    
259   GRADIENT def_g;
260
261   // handle defaults
262   
263   if (!s) return -1;
264   if (!color)
265   { color = &def_c;
266     def_c.a = 0xff;
267     def_c.r = def_c.g = def_c.b = 0;
268   }
269   if (!m)
270   { m = &def_m;
271     swf_GetMatrix(NULL,m);
272   }
273   if(!gradient)
274   {
275     gradient = &def_g;
276     swf_GetGradient(NULL, gradient, 1);
277   }
278
279   // handle memory
280   
281   if (s->fillstyle.data)
282   { FILLSTYLE * xnew = (FILLSTYLE *)rfx_realloc(s->fillstyle.data,(s->fillstyle.n+1)*sizeof(FILLSTYLE));
283     if (!xnew) return -1;
284     s->fillstyle.data = xnew;
285   }
286   else
287   { s->fillstyle.data = (FILLSTYLE *)rfx_alloc(sizeof(FILLSTYLE));
288     s->fillstyle.n = 0;
289     if (!s->fillstyle.data) return -1;
290   }
291
292   // set fillstyle
293   
294   s->fillstyle.data[s->fillstyle.n].type = type; 
295   s->fillstyle.data[s->fillstyle.n].id_bitmap = id_bitmap;
296   memcpy(&s->fillstyle.data[s->fillstyle.n].m,m,sizeof(MATRIX));
297   memcpy(&s->fillstyle.data[s->fillstyle.n].color,color,sizeof(RGBA));
298   memcpy(&s->fillstyle.data[s->fillstyle.n].gradient,gradient,sizeof(GRADIENT));
299           
300   return (++s->fillstyle.n);
301 }
302 int swf_ShapeAddFillStyle2(SHAPE * s,FILLSTYLE*fs)
303 {
304     return swf_ShapeAddFillStyle(s, fs->type, &fs->m, &fs->color, fs->id_bitmap, &fs->gradient);
305 }
306
307 int swf_ShapeAddSolidFillStyle(SHAPE * s,RGBA * color)
308 { return swf_ShapeAddFillStyle(s,FILL_SOLID,NULL,color,0,0);
309 }
310
311 int swf_ShapeAddBitmapFillStyle(SHAPE * s,MATRIX * m,U16 id_bitmap,int clip)
312 { return swf_ShapeAddFillStyle(s,clip?FILL_CLIPPED:FILL_TILED,m,NULL,id_bitmap,0);
313 }
314
315 int swf_ShapeAddGradientFillStyle(SHAPE * s,MATRIX * m,GRADIENT* gradient,int radial)
316 { return swf_ShapeAddFillStyle(s,radial?FILL_RADIAL:FILL_LINEAR,m,NULL,0,gradient);
317 }
318
319 int swf_ShapeAddLineStyle(SHAPE * s,U16 width,RGBA * color)
320 { RGBA def;
321   if (!s) return -1;
322   if (!color)
323   { color = &def;
324     def.a = 0xff;
325     def.r = def.g = def.b = 0; 
326   }
327   if (s->linestyle.data)
328   { LINESTYLE * xnew = (LINESTYLE *)rfx_realloc(s->linestyle.data,(s->linestyle.n+1)*sizeof(LINESTYLE));
329     if (!xnew) return -1;
330     s->linestyle.data = xnew;
331   }
332   else
333   { s->linestyle.data = (LINESTYLE *)rfx_alloc(sizeof(LINESTYLE));
334     s->linestyle.n = 0;
335     if (!s->linestyle.data) return -1;
336   }
337   
338   s->linestyle.data[s->linestyle.n].width = width;
339   memcpy(&s->linestyle.data[s->linestyle.n].color,color,sizeof(RGBA));
340
341   return (++s->linestyle.n);
342 }
343
344 int swf_ShapeSetMove(TAG * t,SHAPE * s,S32 x,S32 y)
345 { U8 b;
346   if (!t) return -1;
347   swf_SetBits(t,0,1);
348   swf_SetBits(t,SF_MOVETO,5);
349   
350   b = swf_CountBits(x,0);
351   b = swf_CountBits(y,b);
352
353   if(b>31) {
354       fprintf(stderr, "Warning: bad moveTo (%f,%f)\n", x/20.0, y/20.0);
355       b=31;
356   }
357     
358   swf_SetBits(t,b,5);
359   swf_SetBits(t,x,b);
360   swf_SetBits(t,y,b);
361
362   return 0;
363 }
364
365 int swf_ShapeSetStyle(TAG * t,SHAPE * s,int line,int fill0,int fill1)
366 { if ((!t)||(!s)) return -1;
367     
368   swf_SetBits(t,0,1);
369   swf_SetBits(t,(line?SF_LINE:0)|(fill0?SF_FILL0:0)|(fill1?SF_FILL1:0),5);
370
371   if (fill0) swf_SetBits(t,fill0,s->bits.fill);
372   if (fill1) swf_SetBits(t,fill1,s->bits.fill);
373   if (line)  swf_SetBits(t,line ,s->bits.line);
374   
375   return 0;
376 }
377
378 /* TODO: sometimes we want to set fillstyle 0, as that's the empty fill
379    used for line drawings. At the moment, we can't, as 0 fill be considered
380    nonexistent and therefore not set.
381    these defines are a workaround (they also reduce the maximal number of
382    fill styles to 32768)
383  */
384 #define UNDEFINED_COORD 0x7fffffff
385
386 int swf_ShapeSetAll(TAG * t,SHAPE * s,S32 x,S32 y,int line,int fill0,int fill1)
387 { U8 b;
388   U8 hasmove = 0;
389   if ((!t)||(!s)) return -1;
390
391   if(x!=UNDEFINED_COORD || y!=UNDEFINED_COORD)
392       hasmove=1;
393
394   swf_SetBits(t,0,1);
395   swf_SetBits(t,(hasmove?SF_MOVETO:0)|(line?SF_LINE:0)|(fill0?SF_FILL0:0)|(fill1?SF_FILL1:0),5);
396
397   if(hasmove) {
398     b = swf_CountBits(x,0);
399     b = swf_CountBits(y,b);
400     swf_SetBits(t,b,5);
401     swf_SetBits(t,x,b);
402     swf_SetBits(t,y,b);
403   }
404
405   if (fill0) swf_SetBits(t,fill0,s->bits.fill);
406   if (fill1) swf_SetBits(t,fill1,s->bits.fill);
407   if (line)  swf_SetBits(t,line ,s->bits.line);
408   
409   return 0;
410 }
411
412 int swf_ShapeSetEnd(TAG * t)
413 { if (!t) return -1;
414   swf_SetBits(t,0,6);
415   swf_ResetWriteBits(t);
416   return 0;
417 }
418
419 int swf_ShapeSetLine(TAG * t,SHAPE * s,S32 x,S32 y)
420
421     U8 b;
422     if (!t) return -1;
423    
424     b = swf_CountBits(x,2);
425     b = swf_CountBits(y,b);
426     if (b<2) b=2;
427     if(b >= 18) {
428         if(b > 18 + 6) {
429             /* do not split into more than 64 segments. If the line is *that* long, something's broken */
430             fprintf(stderr, "Warning: Line to %.2f,%.2f is too long (%d bits)\n", (double)x,(double)y, b);
431             return -1;
432         } else {
433             /* split line */
434             int x1,y1,x2,y2;
435             if(x>=0) { x1 = x/2;x2 = (x+1)/2;} 
436             else     { x1 = x/2;x2 = (x-1)/2;}
437             if(y>=0) { y1 = y/2;y2 = (y+1)/2;} 
438             else     { y1 = y/2;y2 = (y-1)/2;}
439             swf_ShapeSetLine(t, s, x1,y1);
440             swf_ShapeSetLine(t, s, x2,y2);
441             return 0;
442         }
443     }
444
445     if(x!=0 && y!=0) { //(!s)||((x!=0)&&(y!=0)))
446         swf_SetBits(t,3,2); // Straight Edge
447         swf_SetBits(t, b-2, 4); //Number of Bits in x/y
448         swf_SetBits(t,1,1); // Diagonal
449         swf_SetBits(t,x,b);
450         swf_SetBits(t,y,b);
451     } else if (x==0) {
452         swf_SetBits(t,3,2); // Straight Edge
453         swf_SetBits(t, b-2, 4); //Number of Bits in y
454         swf_SetBits(t,1,2); // Vertical
455         swf_SetBits(t,y,b);
456     } else {
457         swf_SetBits(t,3,2); // Straight Edge
458         swf_SetBits(t, b-2, 4); //Number of Bits in x
459         swf_SetBits(t,0,2); // Horizontal
460         swf_SetBits(t,x,b);
461     }
462     return 0;
463 }
464
465 int swf_ShapeSetCurve(TAG * t,SHAPE * s,S32 x,S32 y,S32 ax,S32 ay)
466
467     U8 b;
468     if (!t) return -1;
469
470     b = swf_CountBits(ax,2);
471     b = swf_CountBits(ay,b);
472     b = swf_CountBits(x,b);
473     b = swf_CountBits(y,b);
474
475     if(b >= 18) {
476           fprintf(stderr, "Bit overflow in swf_ShapeSetCurve- %d (%d,%d,%d,%d)\n", b, ax,ay,x,y);
477           return swf_ShapeSetLine(t, s, x+ax, y+ay);
478     }
479
480     swf_SetBits(t,2,2);
481     swf_SetBits(t,b-2,4);
482     swf_SetBits(t,x,b);
483     swf_SetBits(t,y,b);
484     swf_SetBits(t,ax,b);
485     swf_SetBits(t,ay,b);
486     return 0;
487 }
488
489 int swf_ShapeSetCircle(TAG * t,SHAPE * s,S32 x,S32 y,S32 rx,S32 ry)
490 { double C1 = 0.2930;    
491   double C2 = 0.4140;   
492   double begin = 0.7070; 
493
494   if (!t) return -1;
495   
496   swf_ShapeSetMove(t,s,x+begin*rx,y+begin*ry);
497   swf_ShapeSetCurve(t,s, -C1*rx,  C1*ry, -C2*rx,      0);
498   swf_ShapeSetCurve(t,s, -C2*rx,      0, -C1*rx, -C1*ry);
499   swf_ShapeSetCurve(t,s, -C1*rx, -C1*ry,      0, -C2*ry);
500   swf_ShapeSetCurve(t,s,      0, -C2*ry,  C1*rx, -C1*ry);
501   swf_ShapeSetCurve(t,s,  C1*rx, -C1*ry,  C2*rx,      0);
502   swf_ShapeSetCurve(t,s,  C2*rx,      0,  C1*rx,  C1*ry);
503   swf_ShapeSetCurve(t,s,  C1*rx,  C1*ry,      0,  C2*ry);
504   swf_ShapeSetCurve(t,s,      0,  C2*ry, -C1*rx,  C1*ry);
505   
506   return 0;
507 }
508
509 void dummycallback1(TAG*tag, int x, void*y)
510 {
511 }
512
513 // from swftools.c:
514 void enumerateUsedIDs_styles(TAG * tag, void (*callback)(TAG*, int, void*), void*callback_data, int num, int morph);
515
516 static void parseFillStyle(FILLSTYLE*dest, TAG*tag, int num)
517 {
518     int type = swf_GetU8(tag); //type
519     dest->type = type;
520     if(type == 0) {
521         /* plain color */
522         if(num >= 3)
523             swf_GetRGBA(tag, &dest->color);
524         else 
525             swf_GetRGB(tag, &dest->color);
526     }
527     else if(type == 0x10 || type == 0x11 || type == 0x12 || type == 0x13)
528     {
529         /* linear/radial gradient fill */
530         swf_ResetReadBits(tag);
531         swf_GetMatrix(tag, &dest->m);
532         swf_ResetReadBits(tag);
533         swf_GetGradient(tag, &dest->gradient, num>=3?1:0);
534         if(type == 0x13)
535             swf_GetU16(tag);
536     }
537     else if(type == 0x40 || type == 0x41 || type == 0x42 || type == 0x43)
538     {
539         /* bitmap fill */
540         swf_ResetReadBits(tag);
541         dest->id_bitmap = swf_GetU16(tag); //id
542         swf_ResetReadBits(tag); //?
543         swf_GetMatrix(tag, &dest->m);
544     }
545     else {
546         fprintf(stderr, "rfxswf:swfshape.c Unknown fillstyle:0x%02x in tag %02d\n",type, tag->id);
547     }
548 }
549 static int parseFillStyleArray(TAG*tag, SHAPE2*shape)
550 {
551     U16 count;
552     int t;
553     int num=0;
554     int fillstylestart = shape->numfillstyles;
555     int linestylestart = shape->numlinestyles;
556
557     if(tag->id == ST_DEFINESHAPE)
558         num = 1;
559     else if(tag->id == ST_DEFINESHAPE2)
560         num = 2;
561     else if(tag->id == ST_DEFINESHAPE3)
562         num = 3;
563     else if(tag->id == ST_DEFINESHAPE4)
564         num = 4;
565
566     count = swf_GetU8(tag);
567     if(count == 0xff && num>1) // defineshape2,3 only
568         count = swf_GetU16(tag);
569
570     shape->numfillstyles += count;
571     if(shape->numfillstyles) {
572         shape->fillstyles = (FILLSTYLE*)rfx_realloc(shape->fillstyles, sizeof(FILLSTYLE)*shape->numfillstyles);
573
574         for(t=fillstylestart;t<shape->numfillstyles;t++) {
575             parseFillStyle(&shape->fillstyles[t], tag, num);
576         }
577     }
578
579     swf_ResetReadBits(tag);
580     count = swf_GetU8(tag); // line style array
581     if(count == 0xff)
582         count = swf_GetU16(tag);
583
584     shape->numlinestyles += count;
585     if(count) {
586         shape->linestyles = (LINESTYLE*)rfx_realloc(shape->linestyles, sizeof(LINESTYLE)*shape->numlinestyles);
587         /* TODO: should we start with 1 and insert a correct definition of the
588            "built in" linestyle 0? */
589         for(t=linestylestart;t<shape->numlinestyles;t++) 
590         {
591             char fill = 0;
592             shape->linestyles[t].width = swf_GetU16(tag);
593
594             if(num >= 4) {
595                 U16 flags = swf_GetU16(tag);
596                 if((flags & 0x30) == 0x20)
597                     swf_GetU16(tag); // miter limit
598                 if(flags & 0x08) {
599                     fprintf(stderr, "Warning: Filled strokes parsing not yet fully supported\n");
600                     fill = 1;
601                 }
602             }
603
604             if(fill) {
605                 FILLSTYLE f;
606                 parseFillStyle(&f, tag, num);
607                 shape->linestyles[t].color = f.color;
608             } else {
609                 if(num >= 3)
610                     swf_GetRGBA(tag, &shape->linestyles[t].color);
611                 else
612                     swf_GetRGB(tag, &shape->linestyles[t].color);
613             }
614         }
615     }
616     return 1;
617 }
618
619 char swf_ShapeIsEmpty(SHAPE*s)
620 {
621     if(!s || !s->data) return 1;
622     TAG _tag;
623     TAG* tag = &_tag;
624     memset(tag, 0, sizeof(TAG));
625     tag->data = s->data;
626     tag->len = tag->memsize = (s->bitlen+7)/8;
627     tag->pos = 0;
628     
629     while(1) {
630         if(!swf_GetBits(tag, 1)) {
631             U16 flags = swf_GetBits(tag, 5);
632             if(!flags) break;
633             if(flags&1) { //move
634                 int n = swf_GetBits(tag, 5); 
635                 swf_GetSBits(tag, n); //x
636                 swf_GetSBits(tag, n); //y
637             }
638             if(flags&2) swf_GetBits(tag, s->bits.fill);
639             if(flags&4) swf_GetBits(tag, s->bits.fill);
640             if(flags&8) swf_GetBits(tag, s->bits.line);
641             if(flags&16) {return 0;}
642         } else {
643             return 0;
644         }
645     }
646     return 1;
647 }
648
649 /* todo: merge this with swf_GetSimpleShape */
650 static SHAPELINE* swf_ParseShapeData(U8*data, int bits, int fillbits, int linebits, int version, SHAPE2*shape2)
651 {
652     SHAPELINE _lines;
653     SHAPELINE*lines = &_lines;
654
655     TAG _tag;
656     TAG* tag = &_tag;
657     int fill0 = 0;
658     int fill1 = 0;
659     int line = 0;
660     int x=0,y=0;
661     int linestyleadd=0;
662     int fillstyleadd=0;
663     
664     memset(tag, 0, sizeof(TAG));
665     tag->data = data;
666     tag->len = tag->memsize = (bits+7)/8;
667     tag->pos = 0;
668     tag->id = version==1?ST_DEFINESHAPE:(version==2?ST_DEFINESHAPE2:(version==3?ST_DEFINESHAPE3:ST_DEFINESHAPE4));
669
670     lines->next = 0;
671     while(1) {
672         int flags;
673         flags = swf_GetBits(tag, 1);
674         if(!flags) { //style change
675             flags = swf_GetBits(tag, 5);
676             if(!flags)
677                 break;
678             if(flags&1) { //move
679                 int n = swf_GetBits(tag, 5); 
680                 x = swf_GetSBits(tag, n); //x
681                 y = swf_GetSBits(tag, n); //y
682             }
683             if(flags&2)
684                 fill0 = swf_GetBits(tag, fillbits) + fillstyleadd; 
685             if(flags&4)
686                 fill1 = swf_GetBits(tag, fillbits) + fillstyleadd; 
687             if(flags&8)
688                 line = swf_GetBits(tag, linebits) + linestyleadd; 
689             if(flags&16) {
690                 if(!shape2) {
691                     fprintf(stderr, "rfxswf: Error: Additional fillstyles not supported\n");fflush(stderr);
692                     enumerateUsedIDs_styles(tag, dummycallback1, 0, version, 0);
693                 } else {
694                     linestyleadd = shape2->numlinestyles;
695                     fillstyleadd = shape2->numfillstyles;
696                     if(!parseFillStyleArray(tag, shape2))
697                         return 0;
698                 }
699                 fillbits = swf_GetBits(tag, 4);
700                 linebits = swf_GetBits(tag, 4);
701             }
702             if(flags&1) { //move
703                 lines->next = (SHAPELINE*)rfx_alloc(sizeof(SHAPELINE));
704                 lines = lines->next;
705                 lines->type = moveTo;
706                 lines->x = x; 
707                 lines->y = y; 
708                 lines->sx = lines->sy = 0;
709                 lines->fillstyle0 = fill0;
710                 lines->fillstyle1 = fill1;
711                 lines->linestyle = line;
712                 lines->next = 0;
713             }
714         } else {
715             flags = swf_GetBits(tag, 1);
716             if(flags) { //straight edge
717                 int n = swf_GetBits(tag, 4) + 2;
718                 if(swf_GetBits(tag, 1)) { //line flag
719                     x += swf_GetSBits(tag, n); //delta x
720                     y += swf_GetSBits(tag, n); //delta y
721                 } else {
722                     int v=swf_GetBits(tag, 1);
723                     int d;
724                     d = swf_GetSBits(tag, n); //vert/horz
725                     if(v) y += d;
726                     else  x += d;
727                 }
728                 lines->next = (SHAPELINE*)rfx_alloc(sizeof(SHAPELINE));
729                 lines = lines->next;
730                 lines->type = lineTo;
731                 lines->x = x; 
732                 lines->y = y; 
733                 lines->sx = lines->sy = 0;
734                 lines->fillstyle0 = fill0;
735                 lines->fillstyle1 = fill1;
736                 lines->linestyle = line;
737                 lines->next = 0;
738             } else { //curved edge
739                 int n = swf_GetBits(tag, 4) + 2;
740                 int x1,y1;
741                 x += swf_GetSBits(tag, n);
742                 y += swf_GetSBits(tag, n);
743                 x1 = x;
744                 y1 = y;
745                 x += swf_GetSBits(tag, n);
746                 y += swf_GetSBits(tag, n);
747
748                 lines->next = (SHAPELINE*)rfx_alloc(sizeof(SHAPELINE));
749                 lines = lines->next;
750                 lines->type = splineTo;
751                 lines->sx = x1; 
752                 lines->sy = y1; 
753                 lines->x = x; 
754                 lines->y = y; 
755                 lines->fillstyle0 = fill0;
756                 lines->fillstyle1 = fill1;
757                 lines->linestyle = line;
758                 lines->next = 0;
759             }
760         }
761     }
762     return _lines.next;
763 }
764
765 SRECT swf_GetShapeBoundingBox(SHAPE2*shape2)
766 {
767     SRECT r;
768     SHAPELINE*l = shape2->lines;
769     int lastx=0,lasty=0;
770     int valid = 0;
771     r.xmin = r.ymin = SCOORD_MAX;
772     r.xmax = r.ymax = SCOORD_MIN;
773
774     while(l) {
775         int t1;
776         if(l->linestyle>0) {
777             t1 = shape2->linestyles[l->linestyle - 1].width*3/2;
778         } else {
779             t1 = 0;
780         }
781
782         if(l->type == lineTo || l->type == splineTo)
783         {
784             valid = 1;
785             if(lastx - t1 < r.xmin) r.xmin = lastx - t1;
786             if(lasty - t1 < r.ymin) r.ymin = lasty - t1;
787             if(lastx + t1 > r.xmax) r.xmax = lastx + t1;
788             if(lasty + t1 > r.ymax) r.ymax = lasty + t1;
789             if(l->x - t1 < r.xmin) r.xmin = l->x - t1;
790             if(l->y - t1 < r.ymin) r.ymin = l->y - t1;
791             if(l->x + t1 > r.xmax) r.xmax = l->x + t1;
792             if(l->y + t1 > r.ymax) r.ymax = l->y + t1;
793             if(l->type == splineTo) {
794                 if(l->sx - t1 < r.xmin) r.xmin = l->sx - t1;
795                 if(l->sy - t1 < r.ymin) r.ymin = l->sy - t1;
796                 if(l->sx + t1 > r.xmax) r.xmax = l->sx + t1;
797                 if(l->sy + t1 > r.ymax) r.ymax = l->sy + t1;
798             }
799         }
800         lastx = l->x;
801         lasty = l->y;
802         l = l->next;
803     }
804     if(!valid) memset(&r, 0, sizeof(SRECT));
805     return r;
806 }
807
808 void swf_Shape2Free(SHAPE2 * s)
809 {
810     SHAPELINE*line = s->lines;
811     s->lines = 0;
812     while(line) {
813         SHAPELINE*next = line->next;
814         line->next = 0;
815         rfx_free(line);
816         line = next;
817     }
818
819     if(s->linestyles) {
820         rfx_free(s->linestyles);
821         s->linestyles = 0;
822     }
823     if(s->fillstyles) {
824         rfx_free(s->fillstyles);
825         s->fillstyles = 0;
826     }
827     if(s->bbox) {
828         rfx_free(s->bbox);
829         s->bbox = 0;
830     }
831 }
832
833 SHAPE2* swf_Shape2Clone(SHAPE2 * s)
834 {
835     SHAPELINE*line = s->lines;
836     SHAPELINE*prev = 0;
837     SHAPE2*s2 = (SHAPE2*)rfx_alloc(sizeof(SHAPE2));
838     memcpy(s2,s,sizeof(SHAPE2));
839     s2->linestyles = (LINESTYLE*)rfx_alloc(sizeof(LINESTYLE)*s->numlinestyles);
840     memcpy(s2->linestyles, s->linestyles, sizeof(LINESTYLE)*s->numlinestyles);
841     s2->fillstyles = (FILLSTYLE*)rfx_alloc(sizeof(FILLSTYLE)*s->numfillstyles);
842     memcpy(s2->fillstyles, s->fillstyles, sizeof(FILLSTYLE)*s->numfillstyles);
843
844     while(line) {
845         SHAPELINE*line2 = (SHAPELINE*)rfx_alloc(sizeof(SHAPELINE));
846         memcpy(line2, line, sizeof(SHAPELINE));
847         line2->next = 0;
848         if(prev)
849             prev->next = line2;
850         else
851             s2->lines = line2;
852         prev = line2;
853         line = line->next;
854     }
855     if(s->bbox) {
856         s2->bbox = (SRECT*)rfx_alloc(sizeof(SRECT));
857         memcpy(s2->bbox, s->bbox, sizeof(SRECT));
858     }
859     return s2;
860 }
861
862 SHAPE2* swf_ShapeToShape2(SHAPE*shape) {
863
864     SHAPE2*shape2 = (SHAPE2*)rfx_calloc(sizeof(SHAPE2));
865     
866     shape2->numlinestyles = shape->linestyle.n;
867     if(shape2->numlinestyles) {
868         shape2->linestyles = (LINESTYLE*)rfx_alloc(sizeof(LINESTYLE)*shape->linestyle.n);
869         memcpy(shape2->linestyles, shape->linestyle.data, sizeof(LINESTYLE)*shape->linestyle.n);
870     }
871     
872     shape2->numfillstyles = shape->fillstyle.n;
873     if(shape2->numfillstyles) {
874         shape2->fillstyles = (FILLSTYLE*)rfx_alloc(sizeof(FILLSTYLE)*shape->fillstyle.n);
875         memcpy(shape2->fillstyles, shape->fillstyle.data, sizeof(FILLSTYLE)*shape->fillstyle.n);
876     }
877
878     shape2->lines = swf_ParseShapeData(shape->data, shape->bitlen, shape->bits.fill, shape->bits.line, 1, 0);
879     shape2->bbox = 0;
880     return shape2;
881 };
882
883 void swf_ShapeSetBitmapRect(TAG*tag, U16 gfxid, int width, int height)
884 {
885     SHAPE*shape;
886     MATRIX m;
887     RGBA rgb;
888     SRECT r;
889     int lines = 0;
890     int ls=0,fs;
891     swf_ShapeNew(&shape);
892     rgb.b = rgb.g = rgb.r = 0xff;
893     if(lines)
894         ls = swf_ShapeAddLineStyle(shape,20,&rgb);  
895     swf_GetMatrix(NULL,&m);
896     m.sx = 20*65536;
897     m.sy = 20*65536;
898
899     fs = swf_ShapeAddBitmapFillStyle(shape,&m,gfxid,0);
900     r.xmin = 0;
901     r.ymin = 0;
902     r.xmax = width*20;
903     r.ymax = height*20;
904     swf_SetRect(tag,&r);
905
906     swf_SetShapeStyles(tag,shape);
907     swf_ShapeCountBits(shape,NULL,NULL);
908     swf_SetShapeBits(tag,shape);
909
910     swf_ShapeSetAll(tag,shape,0,0,lines?ls:0,fs,0);
911
912     swf_ShapeSetLine(tag,shape,width*20,0);
913     swf_ShapeSetLine(tag,shape,0,height*20);
914     swf_ShapeSetLine(tag,shape,-width*20,0);
915     swf_ShapeSetLine(tag,shape,0,-height*20);
916     swf_ShapeSetEnd(tag);
917     swf_ShapeFree(shape);
918 }
919
920 void swf_ShapeSetRectangle(TAG*tag, U16 shapeid, int width, int height, RGBA*rgba)
921 {
922     RGBA white={255,255,255,255};
923     if(!rgba) {
924         rgba = &white;
925     }
926     SHAPE* s;
927     swf_ShapeNew(&s);
928     int fs = swf_ShapeAddSolidFillStyle(s, rgba);
929     swf_SetU16(tag,shapeid);
930     SRECT r;
931     r.xmin = 0;
932     r.xmax = 0;
933     r.ymin = width;
934     r.ymax = height;
935     swf_SetRect(tag,&r);
936     swf_SetShapeHeader(tag,s);
937     swf_ShapeSetAll(tag,s,0,0,0,fs,0);
938     swf_ShapeSetLine(tag,s,width,0);
939     swf_ShapeSetLine(tag,s,0,height);
940     swf_ShapeSetLine(tag,s,-width,0);
941     swf_ShapeSetLine(tag,s,0,-height);
942     swf_ShapeSetEnd(tag);
943     swf_ShapeFree(s);
944 }
945
946 void swf_ShapeSetRectangleWithBorder(TAG*tag, U16 shapeid, int width, int height, RGBA*rgba, int linewidth, RGBA*linecolor)
947 {
948     RGBA white={255,255,255,255};
949     if(!rgba) {
950         rgba = &white;
951     }
952     SHAPE* s;
953     swf_ShapeNew(&s);
954     int fs = swf_ShapeAddSolidFillStyle(s, rgba);
955     int ls = swf_ShapeAddLineStyle(s, linewidth, linecolor);
956     swf_SetU16(tag,shapeid);
957     SRECT r;
958     r.xmin = 0;
959     r.xmax = 0;
960     r.ymin = width;
961     r.ymax = height;
962     swf_SetRect(tag,&r);
963     swf_SetShapeHeader(tag,s);
964     swf_ShapeSetAll(tag,s,0,0,ls,fs,0);
965     swf_ShapeSetLine(tag,s,width,0);
966     swf_ShapeSetLine(tag,s,0,height);
967     swf_ShapeSetLine(tag,s,-width,0);
968     swf_ShapeSetLine(tag,s,0,-height);
969     swf_ShapeSetEnd(tag);
970     swf_ShapeFree(s);
971 }
972
973 void swf_Shape2ToShape(SHAPE2*shape2, SHAPE*shape)
974 {
975     TAG*tag = swf_InsertTag(0,0);
976     SHAPELINE*l;
977     int newx=0,newy=0,lastx=0,lasty=0,oldls=0,oldfs0=0,oldfs1=0;
978
979     memset(shape, 0, sizeof(SHAPE));
980
981     shape->linestyle.n = shape2->numlinestyles;
982     shape->linestyle.data = (LINESTYLE*)rfx_alloc(sizeof(LINESTYLE)*shape->linestyle.n);
983     memcpy(shape->linestyle.data, shape2->linestyles, sizeof(LINESTYLE)*shape->linestyle.n);
984     
985     shape->fillstyle.n =  shape2->numfillstyles;
986     shape->fillstyle.data = (FILLSTYLE*)rfx_alloc(sizeof(FILLSTYLE)*shape->fillstyle.n);
987     memcpy(shape->fillstyle.data, shape2->fillstyles, sizeof(FILLSTYLE)*shape->fillstyle.n);
988
989     swf_ShapeCountBits(shape,NULL,NULL);
990
991     l = shape2->lines;
992
993     while(l) {
994         int ls=0,fs0=0,fs1=0;
995
996         if(l->type != moveTo) {
997             if(oldls != l->linestyle) {oldls = ls = l->linestyle;if(!ls) ls=0x8000;}
998             if(oldfs0 != l->fillstyle0) {oldfs0 = fs0 = l->fillstyle0;if(!fs0) fs0=0x8000;}
999             if(oldfs1 != l->fillstyle1) {oldfs1 = fs1 = l->fillstyle1;if(!fs1) fs1=0x8000;}
1000
1001             if(ls || fs0 || fs1 || newx!=0x7fffffff || newy!=0x7fffffff) {
1002                 swf_ShapeSetAll(tag,shape,newx,newy,ls,fs0,fs1);
1003                 newx = 0x7fffffff;
1004                 newy = 0x7fffffff;
1005             }
1006         }
1007
1008         if(l->type == lineTo) {
1009             swf_ShapeSetLine(tag,shape,l->x-lastx,l->y-lasty);
1010         } else if(l->type == splineTo) {
1011             swf_ShapeSetCurve(tag,shape, l->sx-lastx,l->sy-lasty, l->x-l->sx,l->y-l->sy);
1012         }
1013         if(l->type == moveTo) {
1014             newx = l->x;
1015             newy = l->y;
1016         }
1017
1018         lastx = l->x;
1019         lasty = l->y;
1020         l = l->next;
1021     }
1022     swf_ShapeSetEnd(tag);
1023     shape->data = tag->data;
1024     shape->bitlen = tag->len*8;
1025     free(tag);
1026 }
1027
1028 void swf_SetShape2(TAG*tag, SHAPE2*shape2)
1029 {
1030     SHAPE shape;
1031     swf_Shape2ToShape(shape2, &shape);
1032
1033     swf_SetRect(tag,shape2->bbox);
1034     swf_SetShapeStyles(tag, &shape);
1035     //swf_ShapeCountBits(&shape,NULL,NULL); // done in swf_Shape2ToShape()
1036     swf_SetShapeBits(tag,&shape);
1037
1038     swf_SetBlock(tag, shape.data, (shape.bitlen+7)/8);
1039 }
1040
1041 void swf_ParseDefineShape(TAG*tag, SHAPE2*shape)
1042 {
1043     int num = 0, id;
1044     U16 fill,line;
1045     SHAPELINE*l;
1046     if(tag->id == ST_DEFINESHAPE)
1047         num = 1;
1048     else if(tag->id == ST_DEFINESHAPE2)
1049         num = 2;
1050     else if(tag->id == ST_DEFINESHAPE3)
1051         num = 3;
1052     else if(tag->id == ST_DEFINESHAPE4)
1053         num = 4;
1054     else {
1055         fprintf(stderr, "parseDefineShape must be called with a shape tag");
1056     }
1057     swf_SetTagPos(tag, 0);
1058
1059     id = swf_GetU16(tag); //id
1060     memset(shape, 0, sizeof(SHAPE2));
1061     shape->bbox = (SRECT*)rfx_alloc(sizeof(SRECT));
1062     swf_GetRect(tag, shape->bbox);
1063     if(num>=4) {
1064         SRECT r2;
1065         swf_ResetReadBits(tag);
1066         swf_GetRect(tag, &r2); // edge bounds
1067         U8 flags = swf_GetU8(tag); // flags, &1: contains scaling stroke, &2: contains non-scaling stroke
1068     }
1069
1070     if(!parseFillStyleArray(tag, shape)) {
1071         return;
1072     }
1073
1074     swf_ResetReadBits(tag); 
1075     fill = (U16)swf_GetBits(tag,4);
1076     line = (U16)swf_GetBits(tag,4);
1077     if(!fill && !line) {
1078         fprintf(stderr, "fill/line bits are both zero\n");
1079     }
1080
1081     shape->lines = swf_ParseShapeData(&tag->data[tag->pos], (tag->len - tag->pos)*8, fill, line, num, shape);
1082
1083     l = shape->lines;
1084 }
1085
1086 static void free_lines(SHAPELINE* lines)
1087 {
1088     if (lines->next)
1089         free_lines(lines->next);
1090     free(lines);
1091 }
1092
1093 void swf_RecodeShapeData(U8*data, int bitlen, int in_bits_fill, int in_bits_line, 
1094                          U8**destdata, U32*destbitlen, int out_bits_fill, int out_bits_line)
1095 {
1096     SHAPE2 s2;
1097     SHAPE s;
1098     SHAPELINE*line;
1099     memset(&s2, 0, sizeof(s2));
1100     s2.lines = swf_ParseShapeData(data, bitlen, in_bits_fill, in_bits_line, 1, 0);
1101     s2.numfillstyles = out_bits_fill?1<<(out_bits_fill-1):0;
1102     s2.numlinestyles = out_bits_line?1<<(out_bits_line-1):0;
1103     s2.fillstyles = (FILLSTYLE*)rfx_calloc(sizeof(FILLSTYLE)*s2.numfillstyles);
1104     s2.linestyles = (LINESTYLE*)rfx_calloc(sizeof(LINESTYLE)*s2.numlinestyles);
1105
1106     line = s2.lines;
1107     while(line) {
1108         if(line->fillstyle0 > s2.numfillstyles) line->fillstyle0 = 0;
1109         if(line->fillstyle1 > s2.numfillstyles) line->fillstyle1 = 0;
1110         if(line->linestyle > s2.numlinestyles) line->linestyle = 0;
1111         line = line->next;
1112     }
1113
1114     swf_Shape2ToShape(&s2,&s);
1115
1116     free_lines(s2.lines);
1117     free(s2.fillstyles);
1118     free(s2.linestyles);
1119     free(s.fillstyle.data);
1120     free(s.linestyle.data);
1121     *destdata = s.data;
1122     *destbitlen = s.bitlen;
1123 }
1124