KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > openlaszlo > iv > flash > api > Script


1 /*
2  * $Id: Script.java,v 1.13 2002/08/08 23:26:54 skavish Exp $
3  *
4  * ==========================================================================
5  *
6  * The JGenerator Software License, Version 1.0
7  *
8  * Copyright (c) 2000 Dmitry Skavish (skavish@usa.net). All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions are met:
12  *
13  * 1. Redistributions of source code must retain the above copyright
14  * notice, this list of conditions and the following disclaimer.
15  *
16  * 2. Redistributions in binary form must reproduce the above copyright
17  * notice, this list of conditions and the following disclaimer in
18  * the documentation and/or other materials provided with the
19  * distribution.
20  *
21  * 3. The end-user documentation included with the redistribution, if
22  * any, must include the following acknowlegement:
23  * "This product includes software developed by Dmitry Skavish
24  * (skavish@usa.net, http://www.flashgap.com/)."
25  * Alternately, this acknowlegement may appear in the software itself,
26  * if and wherever such third-party acknowlegements normally appear.
27  *
28  * 4. The name "The JGenerator" must not be used to endorse or promote
29  * products derived from this software without prior written permission.
30  * For written permission, please contact skavish@usa.net.
31  *
32  * 5. Products derived from this software may not be called "The JGenerator"
33  * nor may "The JGenerator" appear in their names without prior written
34  * permission of Dmitry Skavish.
35  *
36  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
37  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
38  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
39  * DISCLAIMED. IN NO EVENT SHALL DMITRY SKAVISH OR THE OTHER
40  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
41  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
42  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
43  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
44  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
45  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
46  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
47  * SUCH DAMAGE.
48  *
49  */

50
51 /*
52  * 12/11/2001 fixed bug in getBounds(), now it understands masks
53  *
54  */

55
56 package org.openlaszlo.iv.flash.api;
57
58 import java.io.*;
59 import java.util.*;
60 import java.awt.geom.Rectangle2D JavaDoc;
61 import java.awt.geom.AffineTransform JavaDoc;
62
63 import org.openlaszlo.iv.flash.util.*;
64
65 import org.openlaszlo.iv.flash.parser.*;
66 import org.openlaszlo.iv.flash.commands.*;
67 import org.openlaszlo.iv.flash.cache.*;
68 import org.openlaszlo.iv.flash.api.text.*;
69 import org.openlaszlo.iv.flash.api.image.*;
70 import org.openlaszlo.iv.flash.api.sound.*;
71 import org.openlaszlo.iv.flash.api.shape.*;
72 import org.openlaszlo.iv.flash.api.action.*;
73 import org.openlaszlo.iv.flash.api.button.*;
74 import org.openlaszlo.iv.flash.context.Context;
75 import org.openlaszlo.iv.flash.context.FakeContext;
76 import org.openlaszlo.iv.flash.context.StandardContext;
77
78 /**
79  * Flash movie clip
80  * <p>
81  * Contains a timeline, a background color and generator commands of script level.
82  * <P>
83  * Excerpt from flash file format specs:
84  * <P>
85  * A script corresponds to a 'movie clip' in the Flash user-interface.
86  * It is a movie contained within a movie, and supports many of the features of a regular Flash movie, including:<br>
87  * <ul>
88  * <li>Most of the control tags that can be used in the main movie.
89  * <li>A timeline that can stop, start and play independently of the main movie.
90  * <li>A streaming sound track that is automatically mixed with the main sound track.
91  * </ul>
92  * <p>
93  * A script object is defined with a DefineSprite tag. It consists of a character ID, a frame count, and a
94  * series of control tags. Definition tags (such as DefineShape) are not allowed in the DefineSprite tag.
95  * All the characters referred to by control tags in the sprite must be defined outside the sprite,
96  * and before the DefineSprite tag.
97  * <p>
98  * Once defined, a script is displayed with a PlaceObject2 tag in the main movie. The transform (specified in
99  * PlaceObject) is concatenated with the transforms of objects placed inside the script. These objects behave
100  * like 'children' of the script, so when the script is moved, the objects inside the script move also.
101  * Similarly, when the script is scaled or rotated, the child objects are also scaled or rotated.
102  * A script object stops playing automatically when it is removed from the display list.
103  *
104  * @author Dmitry Skavish
105  */

106 public final class Script extends FlashDef {
107
108     private Timeline timeline; // a timeline of the script
109
private boolean isProcessed = false; // true if this script is already processed by jgenerator
110
private IVVector gl_commands; // global generator commands, like SetEnvironment etc
111
private IVVector zero_frame; // zero frame, it usually contains some sound heads etc
112
private SetBackgroundColor bkgColor; // background color of the script
113

114     /**
115      * Creates empty script
116      */

117     public Script() {}
118
119     /**
120      * Creates empty script with specified initial capacity
121      *
122      * @param frameNumber initial capacity of this script in frames
123      * @see #Script(int, int)
124      */

125     public Script( int frameNumber ) {
126         this( 0, frameNumber );
127     }
128
129     /**
130      * Creates empty script with specified initial capacity and ID
131      * <P>
132      * The capacity specifies the capacity of this script's
133      * timeline (in frames). It does not specify how many frames
134      * this script will eventually have.
135      *
136      * @param objID ID of this script, if it's -1, then this is a main script
137      * @param frameNumber initial capacity of this script in frames
138      */

139     public Script( int objID, int frameNumber ) {
140         timeline = new Timeline( frameNumber+1 );
141         setID( objID );
142     }
143
144     /**
145      * Returns true if this script is a main script
146      * <P>
147      * Main script is a script which represents main flash timeline
148      *
149      * @return true if this is a main script
150      */

151     public boolean isMain() {
152         return getID() == -1;
153     }
154
155     /**
156      * Sets this script to be main
157      */

158     public void setMain() {
159         setID( -1 );
160     }
161
162     /**
163      * Sets this script to be NOT main script
164      */

165     public void resetMain() {
166         setID( 0 );
167     }
168
169     /**
170      * Returns this script's timeline
171      *
172      * @return script's timeline
173      */

174     public Timeline getTimeline() {
175         return timeline;
176     }
177
178     /**
179      * Writes content of this script to flash buffer
180      * <P>
181      * This method has to be used only if this script is main script.
182      *
183      * @param fob flash buffer to write
184      * @see #isMain
185      * @see #write
186      */

187     public void generate( FlashOutput fob ) {
188         int frameCount = timeline.getFrameCount();
189         fob.writeWord( frameCount );
190
191         // collect fonts and make sure there is only one instance of each font
192
FontsCollector fc = new FontsCollector();
193         collectFonts( fc );
194
195         // Create a slot for license key first
196
int licenseLen = 32;
197         fob.writeTag( Tag.SERIALNUMBER, licenseLen );
198         for (int i=0; i < licenseLen; i++)
199             fob.writeByte( 0x78 ); // x
200

201         // write background color
202
if( bkgColor != null ) bkgColor.write( fob );
203
204         mergeFonts( fc.getFonts() );
205
206         // generate timeline in right order (definitions first)
207
timeline.generate(fob, new DepsCollector(fc));
208
209         Tag.END_TAG.write( fob );
210     }
211
212     /**
213      * Writes content of this script to flash buffer
214      * <P>
215      * This method has to be used only if this script is main script.
216      *
217      * @param fob flash buffer to write
218      * @param fc1
219      * @param pfc preloader fonts
220      * @param hasPreloader true if preloader exists
221      * @see #isMain
222      * @see #write
223      */

224     public void generate( FlashOutput fob, FontsCollector fc1,
225                           FontsCollector pfc, boolean hasPreloader ) {
226         int frameCount = timeline.getFrameCount();
227         fob.writeWord( frameCount );
228
229         // write background color first
230
if( bkgColor != null ) bkgColor.write( fob );
231
232         // Fonts in fc1 includes fonts declared in the Laszlo app as well as fonts that
233
// are used in SWF assets that are imported.
234
//
235
// Fonts in pfc are fonts that are used in SWF assets that are imported in the preloader/splash.
236
// FIXME: [2004-03-09 bloch] If a font name is used in a SWF asset that's in the preloader *and*
237
// a laszlo font name, then there might be a prob
238
// here since only the preloader version will show up and if the font needed merging, some
239
// text will show up blank.
240
mergeFonts( fc1.getFonts() );
241         if (hasPreloader) {
242             mergeFonts( pfc.getFonts() );
243         }
244
245         IVVector fonts;
246         int frame = 0;
247         // Assume that preloader is only one frame long in the main timeline
248
// starting on frame 0.
249
if (hasPreloader) {
250             // add preloader fonts here
251
fonts = pfc.getFonts();
252             for(int i = 0; i < fonts.size(); i++) {
253                 FontDef fontDef = (FontDef)fonts.elementAt(i);
254                 fontDef.write( fob );
255             }
256
257             // write out preloader at frame 0 and place the rest of the app in
258
// frame 1.
259
Frame fo = (Frame) timeline.elementAt(frame++);
260             fo.generate( fob, new DepsCollector(new FontsCollector()) );
261         }
262
263         // Place the rest of the fonts
264
fonts = fc1.getFonts();
265         for(int i = 0; i < fonts.size(); i++) {
266             FontDef fontDef = (FontDef)fonts.elementAt(i);
267             fontDef.write( fob );
268         }
269
270         // generate timeline in right order, but starting with
271
// frame 1
272
timeline.generate(fob, new DepsCollector(new FontsCollector()), frame);
273
274         Tag.END_TAG.write( fob );
275     }
276
277     /**
278      * Writes content of this script to flash buffer
279      * <P>
280      * This method has to be used only if this script is NOT a main script.
281      *
282      * @param fob flash buffer to write
283      * @see #isMain
284      * @see #generate
285      */

286     public void write( FlashOutput fob ) {
287         int start = fob.getPos(); // save for length calculating
288
fob.skip( 6 ); // 6 - long tag
289
fob.writeDefID( this );
290         int frameCount = timeline.getFrameCount();
291         fob.writeWord( frameCount );
292
293         // write background color first
294
if( bkgColor != null ) bkgColor.write( fob );
295
296         // write "zero frame"
297
if( zero_frame != null ) zero_frame.write( fob );
298
299         // write timeline
300
timeline.write( fob );
301
302         // write end tag
303
Tag.END_TAG.write( fob );
304
305         int size = fob.getPos()-start-6; // 6 - long tag
306
fob.writeLongTagAt( Tag.DEFINESPRITE, size, start );
307     }
308
309     /**
310      * Parses script
311      *
312      * @param p parser
313      * @param isMain true if script to be parsed is main
314      * @return new parsed script
315      * @exception IVException
316      */

317     public static Script parse( Parser p, boolean isMain ) throws IVException {
318         int objID = -1;
319         if( !isMain ) {
320             objID = p.getUWord();
321         }
322
323         // number of frames
324
int frameNum = p.getUWord();
325         // create script
326
Script sc = new Script( objID, frameNum );
327
328         Timeline tl = sc.getTimeline();
329         IVVector tFrame = new IVVector();
330         sc.zero_frame = tFrame;
331         String JavaDoc frameName = null;
332         boolean is_anchor = false;
333
334         for(;;) {
335             int code = p.getTag();
336
337             // check for tag
338
switch( code ) {
339                 /* Control tags */
340                 case Tag.SHOWFRAME: {
341                     Frame f = new Frame( tFrame );
342                     f.setName( frameName );
343                     f.setAnchor(is_anchor);
344                     frameName = null;
345                     is_anchor = false;
346                     tl.addFrame( f );
347                     tFrame.reset();
348                     break;
349                 }
350                 case Tag.END:
351                     return sc;
352                 case Tag.PLACEOBJECT:
353                     tFrame.addElement( Instance.parse( p ) );
354                     break;
355                 case Tag.PLACEOBJECT2: {
356                     Instance instance = Instance.parse2(p);
357                     GenericCommand cmd = GenericCommand.checkAndParseMX(instance);
358                     if( cmd != null && cmd.isGlobal() ) {
359                         sc.addGlobalCommand(cmd);
360                     } else {
361                         tFrame.addElement(instance);
362                     }
363                     break;
364                 }
365                 case Tag.REMOVEOBJECT:
366                     tFrame.addElement( RemoveObject.parse( p ) );
367                     break;
368                 case Tag.REMOVEOBJECT2:
369                     tFrame.addElement( RemoveObject.parse2( p ) );
370                     break;
371                 case Tag.SETBKGCOLOR:
372                     sc.setBackgroundColor( SetBackgroundColor.parse(p) );
373                     break;
374                 case Tag.STARTSOUND:
375                     tFrame.addElement( StartSound.parse(p) );
376                     break;
377                 case Tag.DOACTION:
378                     tFrame.addElement( DoAction.parse(p) );
379                     break;
380                 case Tag.INITCLIPACTION:
381                     tFrame.addElement( InitClipAction.parse(p) );
382                     break;
383                 case Tag.TEMPLATECOMMAND: {
384                     GenericCommand cmd = GenericCommand.parse(p);
385                     if( cmd == null ) break;
386                     if( !cmd.isGlobal() ) {
387                         // this is not a global command, search backward for corresponding instance
388
int j=tl.getFrameCount();
389                         IVVector myFrame = tFrame;
390                         timelineLoop:
391                         for(;;) {
392                             for( int i=myFrame.size(); --i>=0; ) {
393                                 Object JavaDoc o = myFrame.elementAt(i);
394                                 if( o instanceof Instance ) {
395                                     Instance inst = (Instance) o;
396                                     if( inst.depth != cmd.getDepth() ) continue;
397                                     cmd.setInstance(inst);
398                                     inst.setCommand(cmd);
399                                     break timelineLoop;
400                                 }
401                             }
402                             if( --j < 0 ) break;
403                             myFrame = tl.getFrameAt(j);
404                         };
405                         if( j<0 ) {
406                             Log.logRB(Resource.GENCMDERR, new Object JavaDoc[] {cmd.getCommandName()});
407                         }
408                     } else {
409                         sc.addGlobalCommand( cmd );
410                     }
411                     break;
412                 }
413                 /* Info tags */
414                 case Tag.FLASHGENERATOR:
415                     p.getFile().setTemplate(true);
416                     break;
417                 case Tag.PROTECT:
418                     tFrame.addElement( p.newUnknownTag() );
419                     break;
420                 case Tag.SERIALNUMBER:
421                     break;
422                 case Tag.FRAMELABEL:
423                     //Util.dump(p.getBuf(), p.getTagDataPos(), p.getTagEndPos()-p.getTagDataPos(), System.out);
424
frameName = p.getString();
425                     if( p.getPos() < p.getTagEndPos() ) {
426                         is_anchor = p.getUByte()==1;
427                     }
428                     break;
429                 case Tag.GENERATORTEXT: {
430                     int id = p.getUWord();
431                     Text text = (Text) p.getDef(id);
432                     text.parseGenText(p);
433                     break;
434                 }
435                 case Tag.NAMECHARACTER: {
436                     int id = p.getUWord();
437                     String JavaDoc name = p.getString();
438                     FlashDef def = p.getDef(id);
439                     def.setName(name);
440                     p.addDefToLibrary( name, def );
441                     break;
442                 }
443                 case Tag.FREECHARACTER:
444                     tFrame.addElement( FreeCharacter.parse( p ) );
445                     break;
446                 /* Streaming sound tags */
447                 case Tag.SOUNDSTREAMHEAD:
448                 case Tag.SOUNDSTREAMHEAD2:
449                     tFrame.addElement( SoundStreamHead.parse( p ) );
450                     break;
451                 case Tag.SOUNDSTREAMBLOCK:
452                     tFrame.addElement( SoundStreamBlock.parse( p ) );
453                     break;
454                 /* Definitions tags */
455                 case Tag.DEFINESPRITE:
456                     p.addDef( Script.parse( p, false ) );
457                     break;
458                 case Tag.DEFINEMOVIE:
459                     p.addDef( QTMovie.parse(p) );
460                     break;
461                 case Tag.DEFINESHAPE:
462                 case Tag.DEFINESHAPE2:
463                 case Tag.DEFINESHAPE3:
464                     p.addDef( LazyShape.parse( p ) );
465                     break;
466                 case Tag.DEFINEMORPHSHAPE:
467                     p.addDef( LazyMorphShape.parse( p ) );
468                     break;
469                 case Tag.DEFINESOUND:
470                     p.addDef( LazySound.parse( p ) );
471                     break;
472                 case Tag.DEFINEFONT:
473                     p.addDef( FontDef.parse( p ) );
474                     break;
475                 case Tag.DEFINEFONTINFO:
476                     FontDef.parseFontInfoTag( p );
477                     break;
478                 case Tag.DEFINEFONTINFO2:
479                     FontDef.parseFontInfoTag2( p );
480                     break;
481                 case Tag.DEFINEFONT2:
482                     p.addDef( FontDef.parse2( p ) );
483                     break;
484                 case Tag.EXTERNALFONT: {
485                     FlashDef def = FontDef.parseExternalFontTag( p );
486                     if( def != null ) p.addDef( def );
487                     break;
488                 }
489                 case Tag.DEFINETEXT:
490                     p.addDef( Text.parse( p, false ) );
491                     break;
492                 case Tag.DEFINETEXT2:
493                     p.addDef( Text.parse( p, true ) );
494                     break;
495                 case Tag.DEFINEEDITTEXT:
496                     p.addDef( TextField.parse( p ) );
497                     break;
498                 case Tag.DEFINEBUTTON:
499                     p.addDef( Button.parse( p ) );
500                     break;
501                 case Tag.DEFINEBUTTON2:
502                     p.addDef( Button2.parse2( p ) );
503                     break;
504                 case Tag.DEFINEBUTTONCXFORM:
505                     ButtonCXForm.parse(p); // this guy will add itself to a button, we need to do nothing
506
break;
507                 case Tag.DEFINEBUTTONSOUND:
508                     ButtonSound.parse(p); // this guy will add itself to a button, we need to do nothing
509
break;
510                 case Tag.DEFINEBITS:
511                 case Tag.DEFINEBITSJPEG2:
512                 case Tag.DEFINEBITSJPEG3:
513                     p.addDef( JPEGBitmap.parse(p) );
514                     break;
515                 case Tag.DEFINEBITSLOSSLESS:
516                 case Tag.DEFINEBITSLOSSLESS2:
517                     p.addDef( LLBitmap.parse(p) );
518                     break;
519                 case Tag.JPEGTABLES:
520                     JPEGBitmap.parseJPegTables(p);
521                     break;
522                     /* Flash 5 tags */
523                 case Tag.ENABLEDEBUGGER:
524                     tFrame.addElement( p.newUnknownTag() );
525                     break;
526                 case Tag.EXPORTASSETS: {
527                     ExportAssets ea = ExportAssets.parse(p);
528                     if( ea != null ) tFrame.addElement(ea);
529                     break;
530                 }
531                 case Tag.PATHSAREPOSTSCRIPT:
532                     break;
533                 case Tag.IMPORTASSETS:
534                     tFrame.addElement( ImportAssets.parse(p) );
535                     break;
536                     /* Unknown tags parsed here */
537                 default:
538                     Log.logRB(Resource.UNKNOWNTAG, new Object JavaDoc[] {Util.b2h(code)});
539                     tFrame.addElement( p.newUnknownTag() );
540                     break;
541             }
542             p.skipLastTag();
543         }
544     }
545
546     /**
547      * Returns background color of this script
548      *
549      * @return background color of this script
550      */

551     public SetBackgroundColor getBackgroundColor() {
552         return bkgColor;
553     }
554
555     /**
556      * Sets new background color
557      *
558      * @param bkgColor new color
559      */

560     public void setBackgroundColor( SetBackgroundColor bkgColor ) {
561         this.bkgColor = bkgColor;
562     }
563
564     protected void addGlobalCommand( GenericCommand cmd ) {
565         if( gl_commands == null ) gl_commands = new IVVector();
566         gl_commands.addElement( cmd );
567     }
568
569     /**
570      * Removes global commands from this script which have to appear
571      * only once in a file. For example: MovieSetCommand
572      * <p>
573      * Usually all templates loaded into another one need to have these global
574      * commands stripped out
575      */

576     public void removeFileDepGlobalCommands() {
577         if( gl_commands == null ) return;
578         for( int i=0; i<gl_commands.size(); i++ ) {
579             GenericCommand cmd = (GenericCommand) gl_commands.elementAt(i);
580             if( cmd instanceof MovieSetCommand /*||*/ ) {
581                 gl_commands.removeElementAt(i);
582                 i--;
583             }
584         }
585     }
586
587     /**
588      * Finds nested script's instance by name
589      * <P>
590      * Searches instance of a script in this script
591      * and in all nested scripts by instance's name.
592      *
593      * @param name name of the instance to be searched
594      * @return found Script or null
595      */

596     public Instance findInstance( String JavaDoc name ) {
597         for( int i=0; i<timeline.getFrameCount(); i++ ) {
598             Frame frame = timeline.getFrameAt(i);
599             for( int j=0; j<frame.size(); j++ ) {
600                 FlashObject fo = frame.getFlashObjectAt(j);
601                 if( !(fo instanceof Instance) ) continue;
602                 Instance inst = (Instance) fo;
603                 if( inst.name != null && inst.name.equals( name ) ) return inst;
604                 if( inst.isScript() ) {
605                     inst = inst.getScript().findInstance(name);
606                     if( inst != null ) return inst;
607                 }
608             }
609         }
610         return null;
611     }
612
613     /**
614      * Processes this script in the specified context and flash file
615      *
616      * @param file file to be used when processing this script
617      * @param context context to be used when processing this script
618      * @exception IVException
619      */

620     public void process( FlashFile file, Context context ) throws IVException {
621         if( isProcessed() ) return;
622
623         FakeContext fakeContext = null;
624         Context localContext = null;
625
626         // execute global commands (if any)
627
if( gl_commands != null ) {
628             fakeContext = new FakeContext(context);
629             for( int i=0; i<gl_commands.size(); i++ ) {
630                 GenericCommand cmd = (GenericCommand) gl_commands.elementAt(i);
631                 try {
632                     cmd.doCommand(file, localContext!=null?localContext:fakeContext, this, 0);
633                     localContext = fakeContext.getContext();
634                 } catch( Exception JavaDoc e ) {
635                     Log.logRB(Resource.ERRDOCMD, new Object JavaDoc[] {file.getFullName(), getName(), "0", cmd.getCommandName()}, e);
636                 }
637             }
638         }
639
640         if( localContext == null ) {
641             localContext = new StandardContext();
642         }
643         localContext.setParent(context);
644
645         // perform generator commands
646
timeline.doCommand( file, localContext, this );
647
648         // process inner scripts
649
timeline.process( file, localContext );
650
651         // apply context to the rest
652
apply( localContext );
653     }
654
655     /**
656      * Returns true if this script was already processed
657      *
658      * @return true if this script was already processed
659      */

660     public boolean isProcessed() {
661         return isProcessed;
662     }
663
664     /**
665      * Marks this script as processed
666      */

667     public void setProcessed() {
668         isProcessed = true;
669     }
670
671     /**
672      * Creates a copy of this script
673      *
674      * @return copy of this script
675      */

676     public Script copyScript() {
677         return (Script) getCopy( new ScriptCopier() );
678     }
679
680     /**
681      * Appends specified script to the end of this script
682      * <p>
683      * Removes all hanging instances of this script, i.e. for every instance
684      * which is still on the timeline by the of this script put RemoveObject
685      * tag in the last frame of the timeline. Then adds all frames of the specified
686      * script to the end of this one.
687      *
688      * @param sc script to be appended
689      */

690     public void appendScript( Script sc ) {
691         Timeline scTm = sc.getTimeline();
692
693         Frame lastFrame = newFrame();
694         removeAllInstances(lastFrame);
695         Frame firstFrame = scTm.getFrameAt(0);
696         lastFrame.setName( firstFrame.getName() );
697         lastFrame.append( firstFrame );
698
699         for( int i=1; i<scTm.getFrameCount(); i++ ) {
700             Frame frame = scTm.getFrameAt(i);
701             timeline.addFrame(frame);
702         }
703     }
704
705     /**
706      * Removes all hanging instances of the timeline in the specified frame
707      * <p>
708      * Traverses this script and for every hanging instance (instance which is
709      * still on the timeline by the last frame) puts RemoveObject tag in the specified frame.
710      *
711      * @param lastFrame frame to put RemoveObject tags in
712      */

713     public void removeAllInstances( Frame lastFrame ) {
714         IVVector layers = getOccupiedLayers();
715         for( int i=0; i<layers.size(); i++ ) {
716             Instance inst = (Instance) layers.elementAt(i);
717             if( inst == null ) continue;
718             lastFrame.removeInstance(i);
719         }
720     }
721
722     /**
723      * Calculates bounds of this script
724      * <p>
725      * Takes masks into account too
726      *
727      * @return bounds of this script
728      */

729     public Rectangle2D JavaDoc getBounds() {
730         Rectangle2D JavaDoc rect = null;
731         IVVector layers = new IVVector();
732         int[] masks = null;
733         for( int i=0; i<timeline.getFrameCount(); i++ ) {
734             Frame frame = timeline.getFrameAt(i);
735             for( int k=0; k<frame.size(); k++ ) {
736                 FlashObject fo = frame.getFlashObjectAt(k);
737                 if( fo instanceof Instance ) {
738                     Instance inst = (Instance) fo;
739                     int layer = inst.depth;
740                     Rectangle2D JavaDoc bounds = null;
741                     FlashDef def = inst.def;
742                     if( def != null ) {
743                         bounds = def.getBounds();
744                         layers.setElementAt(bounds, layer);
745                     } else if( inst.matrix != null ) {
746                         bounds = (Rectangle2D JavaDoc) layers.elementAt(layer);
747                     }
748
749                     // check for mask
750
if( masks!=null && masks.length>layer && masks[layer]!=0 ) continue;
751
752                     if( inst.matrix != null && bounds != null ) {
753                         bounds = GeomHelper.calcBounds( inst.matrix, bounds );
754                     }
755                     rect = GeomHelper.add( rect, bounds );
756
757                     if( inst.clip > 0 ) {
758                         int clip = inst.clip;
759                         if( masks == null ) {
760                             masks = new int[clip+10];
761                         } else if( masks.length <= clip ) {
762                             int[] masks1 = new int[clip+10];
763                             System.arraycopy(masks, 0, masks1, 0, masks.length);
764                             masks = masks1;
765                         }
766                         masks[layer] = clip;
767                         for( int m=layer+1; m<=clip; m++ ) {
768                             masks[m] = -1;
769                         }
770                     }
771                 } else if( fo instanceof RemoveObject ) {
772                     RemoveObject ro = (RemoveObject) fo;
773                     int layer = ro.depth;
774                     if( masks!=null && masks.length>layer && masks[layer]>0 ) {
775                         // mask is to be removed, clear it
776
int clip = masks[layer];
777                         for( int m=layer; m<=clip; m++ ) {
778                             masks[m] = 0;
779                         }
780                     }
781                     layers.setElementAt(null, layer);
782                 } else {
783                     rect = GeomHelper.add( rect, fo.getBounds() );
784                 }
785             }
786         }
787         if( rect == null ) {
788             rect = GeomHelper.newRectangle();
789         }
790         return rect;
791     }
792
793     /**
794      * Reserves specified number of layers beginning from the specified one
795      * <p>
796      * Traverses the timeline and moves all the instances which are on layers
797      * greater than the specified one by the specified number.
798      * <P>
799      * For example if we have instances A, B, C, D on layers 2,3,4,5,
800      * then if we call reserveLayers(3,2) we will have:
801      * A on 2, B on 5, C on 6, D on 7, so there will be two available layers depths: 3 and 4.
802      *
803      * @param from reserve layers beginning from this one
804      * @param num number of layers to be reserved
805      * @return 'from' layer
806      */

807     public int reserveLayers( int from, int num ) {
808         int cnt = timeline.getFrameCount();
809         for( int i=0; i<cnt; i++ ) {
810             Frame frame = timeline.getFrameAt(i);
811             int fsz = frame.size();
812             for( int k=0; k<fsz; k++ ) {
813                 FlashObject fo = frame.getFlashObjectAt(k);
814                 if( fo instanceof Instance ) {
815                     Instance inst = (Instance) fo;
816                     if( inst.depth >= from ) inst.depth += num;
817                     if( inst.clip >= from ) inst.clip += num;
818                 } else if( fo instanceof RemoveObject ) {
819                     RemoveObject ro = (RemoveObject) fo;
820                     if( ro.depth >= from ) ro.depth += num;
821                 }
822             }
823         }
824         return from;
825     }
826
827     /**
828      * Returns maximum layer's depth of this script
829      *
830      * @return maximum depth
831      */

832     public int getMaxDepth() {
833         int max = 0;
834         for( int i=0; i<timeline.getFrameCount(); i++ ) {
835             Frame frame = timeline.getFrameAt(i);
836             for( int k=0; k<frame.size(); k++ ) {
837                 FlashObject fo = frame.getFlashObjectAt(k);
838                 if( fo instanceof Instance ) {
839                     Instance inst = (Instance) fo;
840                     if( inst.depth > max ) max = inst.depth;
841                     if( inst.clip > max ) max = inst.clip;
842                 }
843             }
844         }
845         return max;
846     }
847
848     /**
849      * Creates linear interpolation of the two specified matrixes
850      *
851      * @param t coefficient of the interpolation [0..1]
852      * @param startMatrix first matrix
853      * @param endMatrix second matrix
854      * @return interpolation of the two specified matrixes
855      */

856     public static AffineTransform JavaDoc interLinear(
857                 double t, AffineTransform JavaDoc startMatrix, AffineTransform JavaDoc endMatrix )
858     {
859         double t1 = 1.0-t;
860         double m00 = startMatrix.getScaleX()*t1 + endMatrix.getScaleX()*t;
861         double m11 = startMatrix.getScaleY()*t1 + endMatrix.getScaleY()*t;
862         double m01 = startMatrix.getShearX()*t1 + endMatrix.getShearX()*t;
863         double m10 = startMatrix.getShearY()*t1 + endMatrix.getShearY()*t;
864         double m02 = startMatrix.getTranslateX()*t1 + endMatrix.getTranslateX()*t;
865         double m12 = startMatrix.getTranslateY()*t1 + endMatrix.getTranslateY()*t;
866         return new AffineTransform JavaDoc(m00, m10, m01, m11, m02, m12);
867     }
868
869     /**
870      * Adds simple motion and color tweening to this script
871      *
872      * @param def symbol to be tweened
873      * @param layer layer on which to place the symbol and do the tweening
874      * @param startFrame start frame
875      * @param startMatrix start transformation matrix
876      * @param startCXF start color matrix (optional)
877      * @param endFrame end frame (included)
878      * @param endMatrix end transformation matrix
879      * @param endCXF end color matrix (optional)
880      * @return last frame
881      */

882     public Frame addTweening( FlashDef def, int layer,
883                              int startFrame, AffineTransform JavaDoc startMatrix, CXForm startCXF,
884                              int endFrame, AffineTransform JavaDoc endMatrix, CXForm endCXF )
885     {
886         return addTweening(def, layer, getFrameAt(startFrame), endFrame-startFrame,
887                            startMatrix, startCXF, endMatrix, endCXF);
888     }
889
890     /**
891      * Adds simple motion and color tweening to this script
892      *
893      * @param def symbol to be tweened
894      * @param layer layer on which to place the symbol and do the tweening
895      * @param startFrame start frame
896      * @param startMatrix
897      * start transformation matrix
898      * @param startCXF start color matrix (optional)
899      * @param endFrame end frame (included)
900      * @param endMatrix end transformation matrix
901      * @param endCXF end color matrix (optional)
902      * @param name instance name
903      * @return last frame
904      */

905     public Frame addTweening( FlashDef def, int layer,
906                              int startFrame, AffineTransform JavaDoc startMatrix, CXForm startCXF,
907                              int endFrame, AffineTransform JavaDoc endMatrix, CXForm endCXF, String JavaDoc name )
908     {
909         return addTweening(def, layer, getFrameAt(startFrame), endFrame-startFrame,
910                            startMatrix, startCXF, endMatrix, endCXF, name);
911     }
912
913     /**
914      * Adds simple motion and color tweening to this script
915      *
916      * @param def symbol to be tweened
917      * @param layer layer on which to place the symbol and do the tweening
918      * @param frame first frame to start the tweening
919      * @param num number of frames for the tweening (in addition to the first frame)
920      * @param startMatrix start transformation matrix
921      * @param startCXF start color matrix (optional)
922      * @param endMatrix end transformation matrix
923      * @param endCXF end color matrix (optional)
924      * @return last frame
925      */

926     public Frame addTweening( FlashDef def, int layer, Frame frame, int num,
927                              AffineTransform JavaDoc startMatrix, CXForm startCXF,
928                              AffineTransform JavaDoc endMatrix, CXForm endCXF )
929     {
930         return addTweening(def, layer, frame, num, startMatrix, startCXF, endMatrix, endCXF, null);
931     }
932
933     /**
934      * Adds simple motion and color tweening to this script
935      *
936      * @param def symbol to be tweened
937      * @param layer layer on which to place the symbol and do the tweening
938      * @param frame first frame to start the tweening
939      * @param num number of frames for the tweening (in addition to the first frame)
940      * @param startMatrix start transformation matrix
941      * @param startCXF start color matrix (optional)
942      * @param endMatrix end transformation matrix
943      * @param endCXF end color matrix (optional)
944      * @param name instance name (optional), is set on first instance
945      * @return last frame
946      */

947     public Frame addTweening( FlashDef def, int layer, Frame frame, int num,
948                              AffineTransform JavaDoc startMatrix, CXForm startCXF,
949                              AffineTransform JavaDoc endMatrix, CXForm endCXF, String JavaDoc name )
950     {
951         int startFrame = getFrameIndex(frame);
952         int endFrame = startFrame+num;
953         frame.addInstance(def, layer, startMatrix, startCXF, name);
954         for( int i=1; i<num; i++ ) {
955             double t = (double)i/num;
956             AffineTransform JavaDoc matrix = interLinear(t, startMatrix, endMatrix);
957             CXForm cxform = startCXF!=null? CXForm.interLinear(t, startCXF, endCXF): null;
958             frame = getFrameAt(startFrame+i);
959             frame.addInstance(layer, matrix, cxform);
960         }
961         if( startFrame != endFrame ) {
962             frame = getFrameAt(endFrame);
963             frame.addInstance(layer, endMatrix, endCXF);
964         }
965         return frame;
966     }
967
968     /**
969      * Fades out everything on the timeline during the specified number of frames
970      *
971      * @param num number of frames
972      */

973     public void fadeOut( int num ) {
974         IVVector layers = getOccupiedLayers();
975         int startFrame = getFrameCount()-1;;
976         CXForm startcx = CXForm.newAlpha(256);
977         CXForm endcx = CXForm.newAlpha(0);
978         for( int i=0; i<layers.size(); i++ ) {
979             Instance inst = (Instance) layers.elementAt(i);
980             if( inst == null ) continue;
981             for( int j=1; j<=num; j++ ) {
982                 double t = (double)j/num;
983                 CXForm cxform = CXForm.interLinear(t, startcx, endcx);
984                 Frame frame = getFrameAt(startFrame+j);
985                 frame.addInstance(i, null, cxform);
986             }
987         }
988     }
989
990     /**
991      * Returns vector of all occupied layers by the end of this script
992      *
993      * @return vector which contains Instances at 'layer' indexes
994      */

995     public IVVector getOccupiedLayers() {
996         IVVector layers = new IVVector();
997         for( int i=0; i<timeline.getFrameCount(); i++ ) {
998             Frame frame = timeline.getFrameAt(i);
999             for( int k=0; k<frame.size(); k++ ) {
1000                FlashObject fo = frame.getFlashObjectAt(k);
1001                if( fo instanceof Instance ) {
1002                    Instance inst = (Instance) fo;
1003                    int layer = inst.depth;
1004                    layers.setElementAt(inst, layer);
1005                } else if( fo instanceof RemoveObject ) {
1006                    RemoveObject ro = (RemoveObject) fo;
1007                    int layer = ro.depth;
1008                    layers.setElementAt(null, layer);
1009                }
1010            }
1011        }
1012        return layers;
1013    }
1014
1015    /**
1016     * Returns number of frames in this script
1017     *
1018     * @return number of frames
1019     */

1020    public int getFrameCount() {
1021        return timeline.getFrameCount();
1022    }
1023
1024    /**
1025     * Returns frame at specified index
1026     * <p>
1027     * If frame does not exist, creates it at specified index and fills everything
1028     * in between with empty frames
1029     *
1030     * @param frameNum frame number
1031     * @return frame at specified index
1032     */

1033    public Frame getFrameAt( int frameNum ) {
1034        if( timeline.getFrameCount() <= frameNum ) {
1035            for( int i=timeline.getFrameCount(); i<=frameNum; i++ ) newFrame();
1036        }
1037        return timeline.getFrameAt(frameNum);
1038    }
1039
1040    /**
1041     * Returns index of specified frame in the timeline or -1
1042     *
1043     * @param frame specified frame
1044     * @return index of specified frame in the timeline or -1
1045     */

1046    public int getFrameIndex( Frame frame ) {
1047        return timeline.getFrameIndex(frame);
1048    }
1049
1050    /**
1051     * Creates new frame and adds it to the end of the timeline
1052     *
1053     * @return new added frame
1054     */

1055    public Frame newFrame() {
1056        return timeline.newFrame();
1057    }
1058
1059    /**
1060     * Returns last frame of this script
1061     *
1062     * @return last frame of this script
1063     */

1064    public Frame getLastFrame() {
1065        return timeline.getFrameAt( getFrameCount()-1 );
1066    }
1067
1068    public void apply( Context context ) {
1069        if( isConstant() || isProcessed() ) return;
1070        timeline.apply(context);
1071    }
1072
1073    public boolean isConstant() {
1074        return timeline.isConstant();
1075    }
1076
1077    protected FlashItem copyInto( FlashItem item, ScriptCopier copier ) {
1078        super.copyInto( item, copier );
1079        ((Script)item).zero_frame = zero_frame!=null? zero_frame.getCopy(copier): null;
1080        ((Script)item).timeline = (Timeline) timeline.getCopy(copier);
1081        ((Script)item).isProcessed = isProcessed;
1082        if( gl_commands != null ) {
1083            IVVector v = new IVVector( gl_commands.size() );
1084            for( int i=0; i<gl_commands.size(); i++ ) {
1085                GenericCommand cmd = (GenericCommand) gl_commands.elementAt(i);
1086                v.addElement( cmd.getCopy(null) );
1087            }
1088            ((Script)item).gl_commands = v;
1089        }
1090        ((Script)item).bkgColor = bkgColor!=null? (SetBackgroundColor)bkgColor.getCopy(copier): null;
1091        return item;
1092    }
1093
1094    public FlashItem getCopy( ScriptCopier copier ) {
1095        return copyInto( new Script(), copier );
1096    }
1097
1098    public void collectDeps( DepsCollector dc ) {
1099        //System.out.println( "Script.collectDeps" );
1100
timeline.collectDeps(dc);
1101    }
1102
1103    public void collectFonts( FontsCollector fc ) {
1104        for( int i=0; i<timeline.getFrameCount(); i++ ) {
1105            Frame frame = timeline.getFrameAt(i);
1106            for( int k=0; k<frame.size(); k++ ) {
1107                FlashObject fo = frame.getFlashObjectAt(k);
1108                fo.collectFonts( fc );
1109            }
1110        }
1111    }
1112
1113    public int getTag() {
1114        return Tag.DEFINESPRITE;
1115    }
1116
1117    public void printContent( PrintStream out, String JavaDoc indent ) {
1118        String JavaDoc id = isMain()? "main": Integer.toString(getID());
1119        out.println( indent+"Script: id="+id+" frames="+timeline.getFrameCount()+" name='"+getName()+"'" );
1120        if( zero_frame != null ) zero_frame.printContent( out, indent+" " );
1121        timeline.printContent( out, indent+" " );
1122        out.println( indent+"End Script("+id+") name='"+getName()+"'" );
1123    }
1124
1125    /**
1126     * Merges the same fonts into one
1127     *
1128     * @param fonts vector of fonts (FontDefs)
1129     */

1130    protected void mergeFonts( IVVector fonts ) {
1131        // vector of Object[] {(String)font_key, (IVVector) text_blocks}
1132
IVVector fonts_blocks = new IVVector();
1133
1134        // hashtable of FontDef by their font key
1135
Hashtable fonts_map = new Hashtable();
1136
1137        // iterate all the fonts and merge the same ones
1138
for( int i=0; i<fonts.size(); i++ ) {
1139            FontDef fdef = (FontDef) fonts.elementAt(i);
1140            Font font = fdef.getFont();
1141            //System.out.println( "Script.mergeFonts: font="+font.fontKey );
1142

1143            // check for the same font in font cache, if found and smaller then remove from the cache
1144
Font font2 = FontCache.getFont(font.fontKey);
1145            if( font2 != null && font2 != font ) {
1146                if( font.isLargeThan(font2) ) {
1147                    //System.out.println( "Script.mergeFonts: removing smaller font from cache" );
1148
// remove old font from cache
1149
FontCache.removeFont(font2.fontKey);
1150                    // add new one
1151
FontCache.addFont(font.fontKey, font);
1152                }
1153            }
1154
1155            FontDef prev_fdef = (FontDef) fonts_map.get(font.fontKey);
1156            if( prev_fdef == null ) {
1157                //System.out.println( "Script.mergeFonts: there is no previous font" );
1158
fonts_map.put(font.fontKey, fdef);
1159            } else {
1160                Font font_prev = prev_fdef.getFont();
1161                //System.out.println( "Script.mergeFonts: there is previous font and they are "+(font_prev==font?"":"not ")+"equal" );
1162
if( font_prev != font ) {
1163                    //System.out.println( "Script.mergeFonts: merge fonts" );
1164
Font font3 = FontDef.mergeFonts(font_prev, font);
1165                    if( font3 == font ) {
1166                        //System.out.println( "Script.mergeFonts: font3==font" );
1167
prev_fdef.setFont(font);
1168                        fonts_blocks.addElement(new Object JavaDoc[] {font_prev, prev_fdef.getTextBlocks()});
1169                        fonts_map.remove(font.fontKey);
1170                        fonts_map.put(font.fontKey, fdef);
1171                        fonts.removeElement(prev_fdef);
1172                    } else {
1173                        //System.out.println( "Script.mergeFonts: font3==font_prev" );
1174
fdef.setFont(font_prev);
1175                        fonts_blocks.addElement(new Object JavaDoc[] {font, fdef.getTextBlocks()});
1176                        fonts.removeElement(fdef);
1177                    }
1178                    i--;
1179                }
1180            }
1181        }
1182
1183        for( int i=0; i<fonts_blocks.size(); i++ ) {
1184            Object JavaDoc[] objs = (Object JavaDoc[]) fonts_blocks.elementAt(i);
1185            Font old_font = (Font) objs[0];
1186            String JavaDoc fontKey = old_font.fontKey;
1187            IVVector text_blocks = (IVVector) objs[1];
1188            FontDef fontDef = (FontDef) fonts_map.get(fontKey);
1189            fontDef.addTextBlocks( text_blocks );
1190            Font new_font = fontDef.getFont();
1191            //System.out.println( "updating textblocks for font "+fontKey );
1192
for( int j=0; j<text_blocks.size(); j++ ) {
1193                TextBlock tblock = (TextBlock) text_blocks.elementAt(j);
1194                tblock.layout();
1195                tblock.changeFont(old_font, new_font);
1196            }
1197        }
1198    }
1199
1200    /**
1201     * zero_frame IVVector accessor
1202     *
1203     * @return IVVector
1204     */

1205    public IVVector getZero_frame() {
1206        return zero_frame;
1207    }
1208
1209}
1210
Popular Tags