Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: com/anotherbigidea/flash/writers/TagWriter.java


1   /****************************************************************
2    * Copyright (c) 2001, David N. Main, All rights reserved.
3    * 
4    * Redistribution and use in source and binary forms, with or
5    * without modification, are permitted provided that the 
6    * following conditions are met:
7    *
8    * 1. Redistributions of source code must retain the above 
9    * copyright notice, this list of conditions and the following 
10   * disclaimer. 
11   * 
12   * 2. Redistributions in binary form must reproduce the above 
13   * copyright notice, this list of conditions and the following 
14   * disclaimer in the documentation and/or other materials 
15   * provided with the distribution.
16   * 
17   * 3. The name of the author may not be used to endorse or 
18   * promote products derived from this software without specific 
19   * prior written permission. 
20   * 
21   * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY 
22   * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 
23   * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 
24   * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 
25   * AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
26   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 
27   * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
28   * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
29   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
30   * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 
31   * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 
32   * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33   ****************************************************************/
34  package com.anotherbigidea.flash.writers;
35  
36  import java.io.*;
37  import java.util.*;
38  import java.util.zip.*;
39  import com.anotherbigidea.io.*;
40  import com.anotherbigidea.flash.*;
41  import com.anotherbigidea.flash.structs.*;
42  import com.anotherbigidea.flash.interfaces.*;
43  
44  /**
45   * A writer that implements the SWFTagTypes interface and writes
46   * to a SWFTags interface
47   */
48  public class TagWriter implements SWFTagTypes, SWFConstants, SWFFileSignature 
49  {
50      protected SWFTags mTags;
51          
52      protected OutStream out;
53      protected ByteArrayOutputStream bytes;
54      protected int     tagType;
55      protected boolean longTag;
56      protected int version;
57      protected String mStringEncoding = SWFConstants.STRING_ENCODING_MX;
58      
59      public TagWriter( SWFTags tags )
60      {
61          mTags = tags;
62      }
63      
64      protected OutStream getOutStream() { return out; }
65      
66      protected SWFActions factorySWFActions()
67      {
68          return new ActionWriter( this, version );
69      }
70      
71      protected SWFShape factorySWFShape( boolean hasAlpha, boolean hasStyle )
72      {
73          return new SWFShapeImpl( this, hasAlpha, hasStyle );
74      }
75      
76    /**
77     * @see SWFFileSignature#signature(String)
78     */
79    public void signature( String sig ) {
80      if( mTags instanceof SWFFileSignature ) {
81        ((SWFFileSignature) mTags).signature( sig );
82      }
83    }    
84      
85      /**
86       * Start a new tag context
87       */
88      protected void startTag( int tagType, boolean longTag )
89      {
90          this.tagType = tagType;
91          this.longTag = longTag;
92          
93          bytes = new ByteArrayOutputStream( 10000 );
94          out   = new OutStream( bytes );
95      }
96      
97      /**
98       * Start a new definition tag context
99       */
100     protected void startTag( int tagType, int id, boolean longTag )
101         throws IOException 
102     {
103         startTag( tagType, longTag );        
104         out.writeUI16( id );
105     }
106         
107     /**
108      * Finish the tag context and write the tag
109      */
110     protected void completeTag() throws IOException 
111     {
112         out.flush();
113         byte[] contents = bytes.toByteArray();
114         
115         out = null;
116         bytes = null;
117         
118         mTags.tag( tagType, longTag, contents );        
119     }
120     
121     /**
122      * SWFTags interface
123      */    
124     public void tag( int tagType, boolean longTag, byte[] contents ) 
125         throws IOException
126     {
127         mTags.tag( tagType, longTag, contents );
128     }
129     
130     /**
131      * SWFHeader interface.
132      * Sets movie length to -1 to force a recalculation since the length
133      * cannot be guaranteed to be the same as the original.
134      */
135     public void header( int version, long length,
136                         int twipsWidth, int twipsHeight,
137                         int frameRate, int frameCount ) throws IOException
138     {
139         this.version = version;
140         mTags.header( version, -1, twipsWidth, twipsHeight, frameRate, frameCount );
141         
142         //set encoding to Ascii if pre-MX
143         if( version < SWFConstants.FLASH_MX_VERSION ) {
144           mStringEncoding = SWFConstants.STRING_ENCODING_PRE_MX; 
145         } 
146     }
147     
148     /**
149      * SWFTagTypes interface
150      */
151     public void tagEnd() throws IOException
152     {
153         mTags.tag( TAG_END, false, null );
154     }
155 
156     /**
157      * SWFTagTypes interface
158      */
159     public void tagShowFrame() throws IOException
160     {
161         mTags.tag( TAG_SHOWFRAME, false, null );
162     }
163     
164     /**
165      * SWFTagTypes interface
166      */
167     public void tagDefineSound( int id, int format, int frequency,
168                                 boolean bits16, boolean stereo,
169                                 int sampleCount, byte[] soundData ) 
170         throws IOException
171     {
172         startTag( TAG_DEFINESOUND, id, true );
173         out.writeUBits( 4, format );
174         out.writeUBits( 2, frequency );
175         out.writeUBits( 1, bits16 ? 1 : 0 );
176         out.writeUBits( 1, stereo ? 1 : 0 );
177         out.writeUI32 ( sampleCount );
178         out.write( soundData );
179         completeTag();
180     }
181     
182     /**
183      * SWFTagTypes interface
184      */
185     public void tagDefineButtonSound( int buttonId,
186                     int rollOverSoundId, SoundInfo rollOverSoundInfo,
187                     int rollOutSoundId,  SoundInfo rollOutSoundInfo,
188                     int pressSoundId,    SoundInfo pressSoundInfo,
189                     int releaseSoundId,  SoundInfo releaseSoundInfo )
190         throws IOException
191     {
192         startTag( TAG_DEFINEBUTTONSOUND, buttonId, true );
193         
194         out.writeUI16( rollOverSoundId );
195         if( rollOverSoundId != 0 ) rollOverSoundInfo.write( out );
196 
197         out.writeUI16( rollOutSoundId );
198         if( rollOutSoundId != 0 ) rollOutSoundInfo.write( out );
199         
200         out.writeUI16( pressSoundId );
201         if( pressSoundId != 0 ) pressSoundInfo.write( out );
202         out.writeUI16( releaseSoundId );
203         if( releaseSoundId != 0 ) releaseSoundInfo.write( out );
204         
205         completeTag();
206     }
207     
208     /**
209      * SWFTagTypes interface
210      */
211     public void tagStartSound( int soundId, SoundInfo info ) throws IOException
212     {
213         startTag( TAG_STARTSOUND, soundId, false );
214         info.write( out );
215         completeTag();
216     }
217     
218     /**
219      * SWFTagTypes interface
220      */
221     public void tagSoundStreamHead( 
222         int playbackFrequency, boolean playback16bit, boolean playbackStereo,
223         int streamFormat, int streamFrequency, boolean stream16bit, boolean streamStereo,
224         int averageSampleCount ) throws IOException
225     {
226         writeSoundStreamHead( TAG_SOUNDSTREAMHEAD,
227             playbackFrequency, playback16bit, playbackStereo,
228             streamFormat, streamFrequency, stream16bit, streamStereo,
229             averageSampleCount );
230     }
231     
232     /**
233      * SWFTagTypes interface
234      */
235     public void tagSoundStreamHead2( 
236         int playbackFrequency, boolean playback16bit, boolean playbackStereo,
237         int streamFormat, int streamFrequency, boolean stream16bit, boolean streamStereo,
238         int averageSampleCount ) throws IOException
239     {
240         writeSoundStreamHead( TAG_SOUNDSTREAMHEAD2,
241             playbackFrequency, playback16bit, playbackStereo,
242             streamFormat, streamFrequency, stream16bit, streamStereo,
243             averageSampleCount );
244     }
245     
246     public void writeSoundStreamHead( int tag,
247         int playbackFrequency, boolean playback16bits, boolean playbackStereo,
248         int streamFormat, int streamFrequency, boolean stream16bits, boolean streamStereo,
249         int averageSampleCount ) throws IOException    
250     {
251         startTag( tag, false );
252         
253         out.writeUBits(4,0);
254         out.writeUBits(2,playbackFrequency);
255         out.writeUBits(1, playback16bits ? 1 : 0 );
256         out.writeUBits(1, playbackStereo ? 1 : 0 );
257         
258         out.writeUBits(4,streamFormat);
259         out.writeUBits(2,streamFrequency);
260         out.writeUBits(1, stream16bits ? 1 : 0 );
261         out.writeUBits(1, streamStereo ? 1 : 0 );        
262         out.writeUI16 (averageSampleCount);
263         
264         if( streamFormat == SWFConstants.SOUND_FORMAT_MP3 )
265         {
266             out.writeUI16 (0); //unknown
267         }
268         
269         completeTag();
270     }
271     
272     /**
273      * SWFTagTypes interface
274      */
275     public void tagSoundStreamBlock( byte[] soundData ) throws IOException
276     {
277         startTag( TAG_SOUNDSTREAMBLOCK, true );
278         out.write( soundData );
279         completeTag();
280     }
281     
282     /**
283      * SWFTagTypes interface
284      */
285     public void tagSerialNumber( String serialNumber ) throws IOException
286     {
287         startTag( TAG_SERIALNUMBER, false );
288         out.writeString( serialNumber, mStringEncoding );
289         completeTag();
290     }
291     
292     /**
293      * SWFTagTypes interface
294      */
295     public void tagGenerator( byte[] data ) throws IOException
296     {
297         startTag( TAG_FLASHGENERATOR, false );
298         out.write( data );
299         completeTag();
300     }
301     
302     /**
303      * SWFTagTypes interface
304      */
305     public void tagGeneratorText( byte[] data ) throws IOException
306     {
307         startTag( TAG_GENERATOR_TEXT, false );
308         out.write( data );
309         completeTag();
310     }
311     
312     /**
313      * SWFTagTypes interface
314      */
315     public void tagGeneratorCommand( byte[] data ) throws IOException
316     {
317         startTag( TAG_TEMPLATECOMMAND, false );
318         out.write( data );
319         completeTag();
320     }
321     
322     /**
323      * SWFTagTypes interface
324      */
325     public void tagGeneratorFont( byte[] data ) throws IOException
326     {
327         startTag( TAG_GEN_EXTERNAL_FONT, false );
328         out.write( data );
329         completeTag();
330     }
331     
332     /**
333      * SWFTagTypes interface
334      */
335     public void tagNameCharacter( byte[] data ) throws IOException
336     {
337         startTag( TAG_NAMECHARACTER, false );
338         out.write( data );
339         completeTag();
340     }
341     
342     /**
343      * SWFTagTypes interface
344      */
345     public void tagDefineBits( int id, byte[] imageData ) throws IOException
346     {
347         startTag( TAG_DEFINEBITS, id, true );
348         out.write( imageData );
349         completeTag();
350     }  
351     
352     /**
353      * SWFTagTypes interface
354      */
355     public void tagJPEGTables( byte[] jpegEncodingData ) throws IOException
356     {
357         startTag( TAG_JPEGTABLES, true );
358         out.write( jpegEncodingData );
359         completeTag();
360     }
361 
362     /**
363      * SWFTagTypes interface
364      */
365     public void tagDefineBitsJPEG3( int id, byte[] imageData, byte[] alphaData ) throws IOException
366     {
367         startTag( TAG_DEFINEBITSJPEG3, id, true );
368         out.writeUI32( imageData.length );
369         out.write( imageData );
370         out.write( alphaData );
371         completeTag();        
372     }        
373     
374     /**
375      * SWFTagTypes interface
376      */
377     public SWFActions tagDoAction() throws IOException
378     {
379         startTag( TAG_DOACTION, true );
380         return factorySWFActions();
381     }
382     
383   /**
384    * SWFTagTypes interface
385    */
386   public SWFActions tagDoInitAction( int spriteId ) throws IOException {
387     startTag( TAG_DOINITACTION, spriteId, true );
388     return factorySWFActions();        
389   }
390     
391     /**
392      * SWFTagTypes interface
393      */
394     public SWFShape tagDefineShape( int id, Rect outline ) throws IOException
395     {
396         startShape( TAG_DEFINESHAPE, id, outline );
397         return factorySWFShape( false, true );
398     }
399     
400     /**
401      * SWFTagTypes interface
402      */
403     public SWFShape tagDefineShape2( int id, Rect outline ) throws IOException
404     {
405         startShape( TAG_DEFINESHAPE2, id, outline );
406         return factorySWFShape( false, true );
407     }
408     
409     /**
410      * SWFTagTypes interface
411      */
412     public SWFShape tagDefineShape3( int id, Rect outline ) throws IOException
413     {
414         startShape( TAG_DEFINESHAPE3, id, outline );
415         return factorySWFShape( true, true );        
416     }    
417     
418     /**
419      * SWFTagTypes interface
420      */    
421     public void tagFreeCharacter( int charId ) throws IOException 
422     {
423         startTag( TAG_FREECHARACTER, false );
424         out.writeUI16( charId );
425         completeTag();
426     }
427     
428     /**
429      * SWFTagTypes interface
430      */    
431     public void tagPlaceObject( int charId, int depth, 
432                                 Matrix matrix, AlphaTransform cxform ) 
433         throws IOException
434     {
435         startTag( TAG_PLACEOBJECT, false );
436         out.writeUI16( charId );        
437         out.writeUI16( depth );
438         matrix.write ( out );
439         if( cxform != null ) cxform.write( out );
440         completeTag();
441     }
442     
443     /**
444      * SWFTagTypes interface
445      */    
446     public SWFActions tagPlaceObject2( boolean isMove,
447                                        int clipDepth,
448                                        int depth,
449                                        int charId,
450                                        Matrix matrix,
451                                        AlphaTransform cxform,
452                                        int ratio,
453                                        String name,
454                                        int clipActionFlags )  
455         throws IOException    
456     {
457         boolean hasClipActions = ( clipActionFlags != 0 );
458         
459         startTag( TAG_PLACEOBJECT2, false );
460         
461         out.writeUBits( 1, hasClipActions   ? 1 : 0 );
462         out.writeUBits( 1, (clipDepth > 0)  ? 1 : 0 );
463         out.writeUBits( 1, (name != null)   ? 1 : 0 );
464         out.writeUBits( 1, (ratio >= 0)     ? 1 : 0 );
465         out.writeUBits( 1, (cxform != null) ? 1 : 0 );
466         out.writeUBits( 1, (matrix != null) ? 1 : 0 );
467         out.writeUBits( 1, (charId > 0)     ? 1 : 0 );
468         out.writeUBits( 1, isMove           ? 1 : 0 );
469     
470         out.writeUI16( depth );
471         
472         if( charId > 0     ) out   .writeUI16( charId );
473         if( matrix != null ) matrix.write    ( out );
474         if( cxform != null ) cxform.write    ( out );
475         if( ratio >= 0     ) out   .writeUI16( ratio );
476         if( clipDepth > 0  ) out   .writeUI16( clipDepth );
477         
478         if( name != null )
479         {
480             out.writeString( name, mStringEncoding );
481             longTag = true;
482         }
483         
484         if( hasClipActions )
485         {
486             out.writeUI16( 0 );  //unknown
487             out.writeUI16( clipActionFlags );
488 
489             return new ActionWriter( this, version )
490             {
491                 public void start( int conditions ) throws IOException
492                 {
493                     super.start( conditions );
494                     tagWriter.out.writeUI16( conditions );
495                 }      
496                 
497                 protected void writeBytes( byte[] bytes ) throws IOException
498                 {
499                     tagWriter.out.writeUI32( bytes.length );
500                     super.writeBytes( bytes );
501                 }
502                
503                 public void done() throws IOException
504                 {
505                     tagWriter.out.writeUI16(0);
506                     super.done();
507                 }                
508             };
509         }
510         
511         completeTag();
512         return null;
513     }
514         
515     /**
516      * SWFTagTypes interface
517      */ 
518     public void tagRemoveObject( int charId, int depth ) throws IOException
519     {
520         startTag( TAG_REMOVEOBJECT, false );
521         out.writeUI16( charId );
522         out.writeUI16( depth );
523         completeTag();
524     }
525         
526     /**
527      * SWFTagTypes interface
528      */ 
529     public void tagRemoveObject2(int depth ) throws IOException
530     {
531         startTag( TAG_REMOVEOBJECT2, false );
532         out.writeUI16( depth );
533         completeTag();
534     }
535 
536     /**
537      * SWFTagTypes interface
538      */ 
539     public void tagSetBackgroundColor( Color color ) throws IOException
540     {
541         startTag( TAG_SETBACKGROUNDCOLOR, false ); 
542         color.writeRGB( out );
543         completeTag();
544     }
545 
546     /**
547      * SWFTagTypes interface
548      */ 
549     public void tagFrameLabel( String label ) throws IOException
550     {
551         tagFrameLabel( label, false );
552     }
553     
554   /**
555    * SWFTagTypes interface
556    */     
557   public void tagFrameLabel( String label, boolean isAnchor ) throws IOException {    
558     startTag( TAG_FRAMELABEL, true ); 
559     out.writeString( label, mStringEncoding );
560     if( isAnchor ) out.writeUI8( 1 );    
561     completeTag();
562   }
563     
564     /**
565      * SWFTagTypes interface
566      */ 
567     public SWFTagTypes tagDefineSprite( int id ) throws IOException
568     {
569         startTag( TAG_DEFINESPRITE, id, true ); 
570         
571         out.writeUI16( 0 ); //framecount - to be filled in later
572         
573         TagWriter writer = new TagWriter( new SpriteTags() );
574         writer.version = version;
575         
576         return writer;
577     }
578     
579     /**
580      * SWFTagTypes interface
581      */ 
582     public void tagProtect( byte[] password ) throws IOException
583     {
584         mTags.tag( TAG_PROTECT, false, password ); 
585     }
586     
587     /**
588      * SWFTagTypes interface
589      */ 
590     public void tagEnableDebug( byte[] password ) throws IOException
591     {
592         mTags.tag( TAG_ENABLEDEBUG, false, password ); 
593     }
594   
595   /**
596    * SWFTagTypes interface
597    */ 
598   public void tagEnableDebug2( byte[] password ) throws IOException
599   {
600     startTag( TAG_ENABLEDEBUGGER2, 0, false );
601     out.write( password );
602     completeTag(); 
603   }        
604   
605     /**
606      * SWFTagTypes interface
607      */ 
608     public SWFVectors tagDefineFont( int id, int numGlyphs ) throws IOException
609     {
610         startTag( TAG_DEFINEFONT, id, true );
611             
612         return new SWFShapeImpl( this, numGlyphs );
613     }
614 
615     /**
616      * SWFTagTypes interface
617      */ 
618     public void tagDefineFontInfo( int fontId, String fontName, int flags, int[] codes )
619         throws IOException
620     {
621         startTag( TAG_DEFINEFONTINFO, true );
622         out.writeUI16( fontId );
623         
624         byte[] chars = fontName.getBytes();
625         out.writeUI8( chars.length );
626         out.write( chars );
627         
628         out.writeUI8( flags );
629         
630         boolean wide = (flags & FONT_WIDECHARS) != 0;
631         
632         for( int i = 0; i < codes.length; i++ )
633         {
634             if( wide ) out.writeUI16( codes[i] );
635             else       out.writeUI8 ( codes[i] );
636         }                             
637         
638         completeTag();
639     }
640 
641   /**
642    * SWFTagTypes interface
643    */ 
644   public void tagDefineFontInfo2( int fontId, String fontName, int flags, int[] codes, int languageCode )
645     throws IOException
646   {
647     startTag( TAG_DEFINEFONTINFO2, true );
648     out.writeUI16( fontId );
649         
650     byte[] chars = fontName.getBytes();
651     out.writeUI8( chars.length );
652     out.write( chars );
653         
654         flags |= SWFConstants.FONT_WIDECHARS; //always wide
655     out.writeUI8( flags );
656     out.writeUI8( languageCode );
657         
658     for( int i = 0; i < codes.length; i++ )
659     {
660       out.writeUI16( codes[i] );
661     }                             
662         
663     completeTag();
664   }
665     
666     /**
667      * SWFTagTypes interface
668      */ 
669     public SWFVectors tagDefineFont2( int id, int flags, String name, int numGlyphs,
670                                       int ascent, int descent, int leading,
671                                       int[] codes, int[] advances, Rect[] bounds,
672                                       int[] kernCodes1, int[] kernCodes2,
673                                       int[] kernAdjustments ) throws IOException
674     {
675         startTag( TAG_DEFINEFONT2, id, true );
676 
677         out.writeUI8( flags );
678         out.writeUI8( 0  );     //reserved flags
679         
680         byte[] nameBytes = name.getBytes();
681         out.writeUI8 ( nameBytes.length );
682         out.write    ( nameBytes );
683         out.writeUI16( numGlyphs );
684         
685         return new Font2ShapeImpl( this, flags, numGlyphs, ascent, descent, leading,
686                                    codes, advances, bounds, 
687                                    kernCodes1, kernCodes2, kernAdjustments );
688     }
689     
690     /**
691      * SWFTagTypes interface
692      */ 
693     public void tagDefineTextField( int fieldId, String fieldName,
694                     String initialText, Rect boundary, int flags,
695                     AlphaColor textColor, int alignment, int fontId, int fontSize, 
696                     int charLimit, int leftMargin, int rightMargin, int indentation,
697                     int lineSpacing ) 
698         throws IOException
699     {
700         flags |= 0x2005;
701         
702         startTag( TAG_DEFINETEXTFIELD, fieldId, true ); 
703                 
704         boundary.write( out );
705         
706         out.writeUI16( flags );
707         out.writeUI16( fontId );
708         out.writeUI16( fontSize );
709         textColor.write( out );
710         
711         if( (flags & TEXTFIELD_LIMIT_CHARS ) != 0 )
712         {
713             out.writeUI16( charLimit );
714         }
715         
716         out.writeUI8 ( alignment   );
717         out.writeUI16( leftMargin  );
718         out.writeUI16( rightMargin );
719         out.writeUI16( indentation );
720         out.writeUI16( lineSpacing );
721                 
722         out.writeString( fieldName, mStringEncoding );
723         
724         if( (flags & TEXTFIELD_HAS_TEXT ) != 0 )
725         {
726             out.writeString( initialText, mStringEncoding );
727         }                
728         
729         completeTag();
730     }
731 
732     /**
733      * SWFTagTypes interface
734      */ 
735     public SWFText tagDefineText( int id, Rect bounds, Matrix matrix )
736         throws IOException
737     { 
738         startTag( TAG_DEFINETEXT, id, true );
739         return defineText( bounds, matrix, false );
740     }
741 
742     /**
743      * SWFTagTypes interface
744      */ 
745     public SWFText tagDefineText2( int id, Rect bounds, Matrix matrix ) throws IOException
746     { 
747         startTag( TAG_DEFINETEXT2, id, true );
748         return defineText( bounds, matrix, true );
749     }
750     
751     /**
752      * SWFTagTypes interface
753      */ 
754     public SWFActions tagDefineButton( int id, Vector buttonRecords )
755         throws IOException
756     {
757         startTag( TAG_DEFINEBUTTON, id, true );
758         
759         ButtonRecord.write( out, buttonRecords );
760         System.out.println( "BUTTON" );
761         return new ActionWriter( this, version );
762     }
763     
764     /**
765      * SWFTagTypes interface
766      */ 
767     public void tagButtonCXForm( int buttonId, ColorTransform transform ) 
768         throws IOException
769     {
770         startTag( TAG_DEFINEBUTTONCXFORM, buttonId, false );
771         transform.writeWithoutAlpha( out );
772         completeTag();
773     }
774         
775     /**
776      * SWFTagTypes interface
777      */ 
778     public SWFActions tagDefineButton2( int id, 
779                                         boolean trackAsMenu, 
780                                         Vector buttonRecord2s )
781         throws IOException
782     {
783         startTag( TAG_DEFINEBUTTON2, id, true ); 
784         out.writeUI8( trackAsMenu ? 1 : 0 );
785 
786         return new ButtonActionWriter( this, version, buttonRecord2s );
787     }
788 
789     /**
790      * SWFTagTypes interface
791      */ 
792     public void tagExport( String[] names, int[] ids ) throws IOException
793     {
794         startTag( TAG_EXPORT, true );
795         
796         int count = ids.length;
797 
798         out.writeUI16( count );
799 
800         for( int i = 0; i < count; i++ )
801         {
802             //System.out.println( "Exporting " + ids[i] + " as " + names[i] );
803             out.writeUI16  ( ids  [i] );
804             out.writeString( names[i], mStringEncoding );
805         }        
806         
807         completeTag();
808     }
809     
810     /**
811      * SWFTagTypes interface
812      */ 
813     public void tagImport( String movieName, String[] names, int[] ids ) 
814         throws IOException
815     {
816         startTag( TAG_IMPORT, true );
817         
818         int count = ids.length;
819         
820         out.writeString( movieName, mStringEncoding );
821         out.writeUI16  ( count );
822 
823         for( int i = 0; i < count; i++ )
824         {
825             //System.out.println( "Importing " + names[i] + " as " + ids[i] + " from " + movieName );
826             out.writeUI16  ( ids  [i] );
827             out.writeString( names[i], mStringEncoding );
828         }
829         
830         completeTag();
831     }
832     
833     /**
834      * SWFTagTypes interface
835      */ 
836     public void tagDefineQuickTimeMovie( int id, String filename ) throws IOException
837     {
838         startTag( TAG_DEFINEQUICKTIMEMOVIE, id, true );
839         out.writeString( filename, mStringEncoding );
840         completeTag();
841     }
842     
843     /**
844      * SWFTagTypes interface
845      */ 
846     public void tagDefineBitsJPEG2( int id, byte[] data ) throws IOException
847     {
848         startTag( TAG_DEFINEBITSJPEG2, id, true );
849         out.write( data );
850         completeTag();
851     }
852     /**
853      * SWFTagTypes interface
854      */ 
855     public void tagDefineBitsLossless( int id, int format, int width, int height,
856                                        Color[] colors, byte[] imageData )
857         throws IOException
858     {
859         writeBitsLossless( id, format, width, height, colors, imageData, false );
860     }
861     
862     /**
863      * SWFTagTypes interface
864      */ 
865     public void tagDefineBitsLossless2( int id, int format, int width, int height,
866                                         Color[] colors, byte[] imageData )
867         throws IOException
868     {
869         writeBitsLossless( id, format, width, height, colors, imageData, true );
870     }
871     
872     
873     public void writeBitsLossless( int id, int format, int width, int height,
874                                    Color[] colors, byte[] imageData, boolean hasAlpha )
875         throws IOException
876     {
877         startTag( hasAlpha ? TAG_DEFINEBITSLOSSLESS2 : TAG_DEFINEBITSLOSSLESS,
878                   id, true );
879 
880         out.writeUI8 ( format );
881         out.writeUI16( width  );
882         out.writeUI16( height );
883         
884         switch( format )
885         {
886             case BITMAP_FORMAT_8_BIT:  out.writeUI8 ( colors.length - 1 ); break;
887             case BITMAP_FORMAT_16_BIT: out.writeUI16( colors.length - 1 ); break;
888             case BITMAP_FORMAT_32_BIT: break;
889             default: throw new IOException( "unknown bitmap format: " + format );
890         }                
891        
892         //--zip up the colors and the bitmap data
893         DeflaterOutputStream deflater = new DeflaterOutputStream( bytes );
894         OutStream zipOut = new OutStream( deflater );
895                 
896         if( format == BITMAP_FORMAT_8_BIT || format == BITMAP_FORMAT_16_BIT )
897         {
898             for( int i = 0; i < colors.length; i++ )
899             {
900                 if( hasAlpha ) colors[i].writeWithAlpha( zipOut ); 
901                 else           colors[i].writeRGB( zipOut );
902             }
903         }
904         zipOut.write( imageData );        
905         zipOut.flush();
906         deflater.finish();
907         
908         completeTag();
909     }
910     
911     /**
912      * SWFTagTypes interface
913      */ 
914     public void tagDefineBitsJPEG2( int id, InputStream jpegImage ) throws IOException
915     {
916         startTag( TAG_DEFINEBITSJPEG2, id, true );
917         
918         //--Write stream terminator/header
919         out.writeUI8( 0xff );
920         out.writeUI8( 0xd9 );
921         out.writeUI8( 0xff );
922         out.writeUI8( 0xd8 );
923         
924         int read = 0;
925         byte[] bytes = new byte[10000];
926         
927         while( (read = jpegImage.read( bytes )) >= 0 )
928         {
929             out.write( bytes, 0, read );
930         }
931         
932         jpegImage.close();        
933         completeTag();
934     }
935     
936     /**
937      * SWFTagTypes interface
938      */ 
939     public SWFShape tagDefineMorphShape( int id, Rect startBounds, Rect endBounds ) 
940         throws IOException
941     {
942         startTag( TAG_DEFINEMORPHSHAPE, id, true );
943         startBounds.write( out );
944         endBounds  .write( out );
945         return new MorphShapeImpl( this );
946     }
947     
948     //-----------------------------------------------------------------------
949 
950         
951     protected static class ButtonActionWriter extends ActionWriter
952     {
953         //offsets is a vector of int[]{ offsetPtr, offsetValue }
954         protected Vector offsets = new Vector();
955         protected int lastPtr;
956         protected OutStream tagout;
957         
958         public ButtonActionWriter( TagWriter tagWriter, 
959                                    int flashVersion, 
960                                    Vector buttonRecs )
961             throws IOException 
962         {
963             super( tagWriter, flashVersion );
964 
965             tagout = tagWriter.getOutStream();
966             
967             //--save ptr to first offset location
968             lastPtr = (int)tagout.getCount();
969             
970             tagout.writeUI16( 0 ); //will be calculated later
971             
972             //--write the button records
973             ButtonRecord2.write( tagout, buttonRecs );
974         }
975         
976         public void start( int conditions ) throws IOException
977         {
978             super.start( conditions );
979 
980             //--save ptr to offset location and offset value
981             int ptr    = (int)tagout.getCount();
982             int offset = ptr - lastPtr;
983             
984             offsets.addElement( new int[] { lastPtr, offset } );
985             
986             lastPtr = ptr;
987             
988             tagout.writeUI16( 0 );  //will be calculated later
989             tagout.writeUI16( conditions );            
990         }      
991                 
992         public void done() throws IOException
993         {
994             //--save last offset
995             offsets.addElement( new int[] { lastPtr, 0 } );
996             
997             tagout.flush();
998             byte[] contents = tagWriter.bytes.toByteArray();
999         
1000            tagWriter.out = null;
1001            tagWriter.bytes = null;
1002        
1003            //--fix the offsets
1004            for( Enumeration enum = offsets.elements(); enum.hasMoreElements(); )
1005            {
1006                int[] offInfo = (int[])enum.nextElement();
1007                int ptr = offInfo[0];
1008                int off = offInfo[1];
1009                
1010                byte[] offbytes = OutStream.uintTo2Bytes( off );
1011                contents[ ptr     ] = offbytes[0];
1012                contents[ ptr + 1 ] = offbytes[1];
1013            }
1014            
1015            tagWriter.mTags.tag( tagWriter.tagType, true, contents );
1016        }                
1017    }
1018    
1019    protected SWFText defineText( Rect bounds, Matrix matrix, boolean hasAlpha ) 
1020        throws IOException 
1021    {
1022        bounds.write( out );
1023        matrix.write( out );
1024
1025        return new SWFTextImpl( hasAlpha );
1026    }
1027    
1028    protected class SWFTextImpl implements SWFText 
1029    {
1030        protected boolean hasAlpha;
1031
1032        protected int maxGlyphIndex = 0;
1033        protected int maxAdvance    = 0;
1034        
1035        // Text records are stored as
1036        // Object[] { int[]{ font, height }, int[]{ x }, int[]{ y }, Color }, or
1037        // Object[] { int[] glyphs, int[] advances }
1038        protected Vector recs = new Vector();
1039        protected Object[] currentStyleRecord = null;
1040        
1041        
1042        public SWFTextImpl( boolean hasAlpha )
1043        {
1044            this.hasAlpha = hasAlpha;
1045        }
1046        
1047        protected Object[] getCurrentStyle()
1048        {
1049            if( currentStyleRecord == null )
1050            {
1051                currentStyleRecord = new Object[4];
1052                recs.addElement( currentStyleRecord );
1053            }
1054
1055            return currentStyleRecord;
1056        }
1057        
1058        /**
1059         * SWFText interface
1060         */
1061        public void font( int fontId, int textHeight ) throws IOException
1062        {
1063            getCurrentStyle()[0] = new int[] { fontId, textHeight };
1064        }
1065    
1066        /**
1067         * SWFText interface
1068         */
1069        public void color( Color color ) throws IOException
1070        {
1071            getCurrentStyle()[3] = color;
1072        }
1073    
1074        /**
1075         * SWFText interface
1076         */
1077        public void setX( int x ) throws IOException
1078        {
1079            getCurrentStyle()[1] = new int[] { x };
1080        }
1081
1082        /**
1083         * SWFText interface
1084         */
1085        public void setY( int y ) throws IOException
1086        {
1087            getCurrentStyle()[2] = new int[] { y };
1088        }
1089    
1090        /**
1091         * SWFText interface
1092         */
1093        public void text( int[] glyphIndices, int[] glyphAdvances ) throws IOException
1094        {
1095            currentStyleRecord = null;
1096            recs.addElement( new Object[] { glyphIndices, glyphAdvances } );
1097            
1098            for( int i = 0; i < glyphIndices.length; i++ )
1099            {
1100                if( glyphIndices[i] > maxGlyphIndex ) maxGlyphIndex = glyphIndices[i];
1101                if( glyphAdvances[i] > maxAdvance   ) maxAdvance    = glyphAdvances[i];
1102            }
1103        }
1104    
1105        /**
1106         * SWFText interface
1107         */
1108        public void done() throws IOException
1109        {
1110            int glyphBits   = OutStream.determineUnsignedBitSize( maxGlyphIndex );
1111            int advanceBits = OutStream.determineSignedBitSize  ( maxAdvance );               
1112               
1113            out.writeUI8( glyphBits );
1114            out.writeUI8( advanceBits );
1115
1116            for( Enumeration enum = recs.elements(); enum.hasMoreElements(); )
1117            {
1118                Object[] rec = (Object[])enum.nextElement();
1119
1120                if( rec.length == 4 ) //style record
1121                {
1122                    boolean hasFont  = rec[0] != null;
1123                    boolean hasX     = rec[1] != null;
1124                    boolean hasY     = rec[2] != null;
1125                    boolean hasColor = rec[3] != null;
1126                    
1127                    int flags = 0x80;
1128                    if( hasFont  ) flags |= TEXT_HAS_FONT;
1129                    if( hasX     ) flags |= TEXT_HAS_XOFFSET;
1130                    if( hasY     ) flags |= TEXT_HAS_YOFFSET;
1131                    if( hasColor ) flags |= TEXT_HAS_COLOR;
1132
1133                    out.writeUI8( flags );
1134            
1135                    if( hasFont )
1136                    {
1137                        out.writeUI16( ((int[])rec[0])[0] ); //fontId
1138                    }
1139            
1140                    if( hasColor )
1141                    {
1142                        Color color = (Color)rec[3];
1143                        
1144                        if( hasAlpha ) color.writeWithAlpha( out );
1145                        else           color.writeRGB( out );
1146                    }
1147
1148                    if( hasX )
1149                    {
1150                        int xOffset = ((int[])rec[1])[0];
1151                        out.writeSI16( (short)xOffset );
1152                    }
1153
1154                    if( hasY ) //x & y are in reverse order from flag bits
1155                    {
1156                        int yOffset = ((int[])rec[2])[0];
1157                        out.writeSI16( (short)yOffset );
1158                    }
1159
1160                    if( hasFont )
1161                    {
1162                        out.writeUI16( ((int[])rec[0])[1] ); //textHeight
1163                    }                        
1164                }
1165                else //glyph record
1166                {
1167                    int[] glyphs   = (int[])rec[0];
1168                    int[] advances = (int[])rec[1];
1169                    
1170                    out.writeUI8( glyphs.length );
1171            
1172                    for( int i = 0; i < glyphs.length; i++ )
1173                    {
1174                        out.writeUBits( glyphBits,   glyphs[i]   );
1175                        out.writeSBits( advanceBits, advances[i] );
1176                    }                                
1177                }
1178            }
1179
1180            out.writeUI8( 0 );  //record terminator
1181
1182            completeTag();
1183        }        
1184    }
1185    
1186    
1187    protected class SpriteTags implements SWFTags 
1188    {
1189        protected int frameCount = 0;
1190        
1191        public void tag( int tagType2, boolean longTag2, 
1192                         byte[] contents ) throws IOException
1193        {
1194            int length = (contents != null ) ? contents.length : 0;
1195            longTag2 = ( length > 62 ) || longTag2;
1196                
1197            int hdr = ( tagType2 << 6 ) + ( longTag2 ? 0x3f : length );
1198
1199            out.writeUI16( hdr );
1200                            
1201            if( longTag2 ) out.writeUI32( length );        
1202                
1203            if( contents != null ) out.write( contents );
1204                
1205            if( tagType2 == SWFConstants.TAG_SHOWFRAME ) frameCount++;
1206            
1207            if( tagType2 == SWFConstants.TAG_END )
1208            {
1209                out.flush();
1210                contents = bytes.toByteArray();
1211        
1212                out = null;
1213                bytes = null;
1214        
1215                byte[] fc = OutStream.uintTo2Bytes( frameCount );
1216                contents[2] = fc[0];
1217                contents[3] = fc[1];
1218                
1219                mTags.tag( tagType, longTag, contents );                  
1220            }
1221        }
1222        
1223        public void header( int version, long length,
1224                            int twipsWidth, int twipsHeight,
1225                            int frameRate, int frameCount ) throws IOException
1226        {
1227        }
1228    }    
1229    
1230    protected void startShape( int tagType, int id, Rect outline ) throws IOException
1231    {
1232        startTag( tagType, id, true );
1233        outline.write( out );
1234    }
1235    
1236    /** 
1237     * Implementation of the SWFShape interface
1238     */
1239    protected static class SWFShapeImpl implements SWFShape 
1240    {
1241        protected boolean hasAlpha;
1242        protected boolean hasStyle;
1243        protected boolean outstandingChanges = true;
1244        protected boolean initialStyles = false;
1245        
1246        protected int fill0Index = -1;
1247        protected int fill1Index = -1;
1248        protected int lineIndex  = -1;
1249        
1250        protected int[] moveXY;
1251        protected Vector lineStyles = new Vector();
1252        protected Vector fillStyles = new Vector();
1253        
1254        protected int fillBits;
1255        protected int lineBits;
1256        
1257        protected int    glyphCount = 0;
1258        protected Vector glyphByteArrays;
1259        
1260        protected OutStream out;
1261        protected TagWriter writer;
1262        protected ByteArrayOutputStream bout;
1263        
1264        /**
1265         * For shapes (other than glyphs)
1266         */
1267        public SWFShapeImpl( TagWriter writer, boolean hasAlpha, boolean hasStyle )
1268        {
1269            this.hasAlpha  = hasAlpha;
1270            this.hasStyle  = hasStyle;
1271            this.writer    = writer;
1272            out = writer.getOutStream();
1273        }
1274
1275        /**
1276         * For glyphs
1277         */
1278        public SWFShapeImpl( TagWriter writer, int glyphCount ) 
1279        {
1280            this( writer, false, false );
1281            this.glyphCount = glyphCount;
1282            bout = new ByteArrayOutputStream();
1283            out  = new OutStream( bout );
1284            glyphByteArrays = new Vector();
1285            
1286            fill1Index = 1;                
1287            lineIndex  = 0;            
1288        }
1289
1290       
1291        public void done() throws IOException
1292        {
1293            if( ! initialStyles )
1294            {
1295                writeInitialStyles();
1296                initialStyles = true;
1297            }
1298            
1299            out.writeUBits( 6, 0 );  //end record            
1300            out.flushBits();
1301         
1302            if( bout != null && glyphCount > 0 ) //capturing bytes internally
1303            {
1304                byte[] glyphBytes = bout.toByteArray();
1305                glyphByteArrays.addElement( glyphBytes );
1306            }
1307            
1308            if( glyphCount > 1 )
1309            {
1310                bout = new ByteArrayOutputStream();
1311                out  = new OutStream( bout );
1312                glyphCount--;
1313                
1314                fill1Index = 1;
1315                lineIndex  = 0;
1316                outstandingChanges = true;
1317                initialStyles = false;
1318            }
1319            else
1320            {
1321                if( bout != null ) finishFont();
1322                writer.completeTag();
1323            }
1324        }
1325    
1326        protected void finishFont() throws IOException
1327        {
1328            out = writer.getOutStream();
1329            
1330            int glyphCount = glyphByteArrays.size();
1331            
1332            //--Write first shape offset
1333            int offset = glyphCount * 2;
1334            out.writeUI16( offset );
1335        
1336            //--Write subsequent shape offsets
1337            for( int i = 0; i < glyphCount - 1; i++ )
1338            {
1339                offset += ((byte[])glyphByteArrays.elementAt(i)).length;
1340                out.writeUI16( offset );        
1341            }
1342
1343            //--Write shapes        
1344            for( int i = 0; i < glyphCount; i++ )
1345            {
1346                out.write( (byte[])glyphByteArrays.elementAt(i) );
1347            }                            
1348        }
1349        
1350        public void setFillStyle0( int styleIndex ) throws IOException
1351        {
1352            fill0Index = styleIndex;
1353            outstandingChanges = true;
1354        }
1355    
1356        public void setFillStyle1( int styleIndex ) throws IOException
1357        {
1358            fill1Index = styleIndex;
1359            outstandingChanges = true;
1360        }
1361    
1362        public void setLineStyle( int styleIndex ) throws IOException
1363        {
1364            lineIndex = styleIndex;
1365            outstandingChanges = true;
1366        }
1367
1368        public void defineFillStyle( Color color ) throws IOException
1369        {
1370            fillStyles.addElement( new FillStyle( color ) );
1371            outstandingChanges = true;
1372        }
1373
1374        public void defineFillStyle( Matrix matrix, int[] ratios,
1375                                     Color[] colors, boolean radial )
1376            throws IOException
1377        {
1378            fillStyles.addElement( new FillStyle( matrix, ratios, colors, radial ) );
1379            outstandingChanges = true;
1380        }
1381
1382        public void defineFillStyle( int bitmapId, Matrix matrix, boolean clipped )
1383            throws IOException
1384        {
1385            fillStyles.addElement( new FillStyle( bitmapId, matrix, clipped ) );
1386            outstandingChanges = true;
1387        }
1388    
1389        public void defineLineStyle( int width, Color color ) throws IOException
1390        {
1391            lineStyles.addElement( new LineStyle( width, color ) );
1392            outstandingChanges = true;            
1393        }
1394
1395        public void line( int deltaX, int deltaY ) throws IOException
1396        {
1397            if( outstandingChanges ) flushChangeRecords();
1398
1399            int numBits = OutStream.determineSignedBitSize( deltaX );
1400            int dyBits  = OutStream.determineSignedBitSize( deltaY );
1401            
1402            if( dyBits  > numBits ) numBits = dyBits;            
1403            if( numBits < 2 ) numBits = 2;
1404            
1405            out.writeUBits( 2, 3 );  //11b = line record
1406            
1407            out.writeUBits( 4, numBits - 2 );
1408            
1409            if( deltaX !=