Source code: com/anotherbigidea/flash/movie/Frame.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.movie;
35
36 import java.io.IOException;
37 import java.util.Enumeration;
38 import java.util.Vector;
39
40 import com.anotherbigidea.flash.SWFConstants;
41 import com.anotherbigidea.flash.interfaces.SWFActions;
42 import com.anotherbigidea.flash.interfaces.SWFTagTypes;
43 import com.anotherbigidea.flash.sound.SoundStreamHead;
44 import com.anotherbigidea.flash.structs.AlphaTransform;
45 import com.anotherbigidea.flash.structs.SoundInfo;
46
47 /**
48 * A Movie or Movie Clip frame
49 */
50 public class Frame
51 {
52 protected int frameNumber;
53 protected String label;
54 protected Vector placements = new Vector();
55 protected boolean stop;
56 protected TimeLine timeline;
57 protected Actions actions;
58 protected SoundStreamHead soundHeader;
59 protected byte[] soundData;
60 protected Sound soundToStart;
61 protected int customTag = -1;
62 protected byte[] customTagData;
63 protected boolean mAnchor; //true for an anchor frame (Flash MX+)
64
65 protected Frame( int number, TimeLine timeline )
66 {
67 frameNumber = number;
68 this.timeline = timeline;
69 }
70
71 public SoundStreamHead getSoundHeader() { return soundHeader; }
72 public void setSoundHeader( SoundStreamHead header ) { soundHeader = header; }
73
74 public byte[] getSoundData() { return soundData; }
75 public void setSoundData( byte[] data ) { soundData = data; }
76
77 public boolean isAnchor() { return mAnchor; }
78 public void setAnchor( boolean isAnchor ) { mAnchor = isAnchor; }
79
80 /**
81 * Set a custom tag to be written at the start of the frame
82 */
83 public void setCustomTag( int tagId, byte[] tagData )
84 {
85 this.customTag = tagId;
86 this.customTagData = tagData;
87 }
88
89 /**
90 * @return number of frames required for the sound
91 */
92 public int startSound( Sound soundToStart, int framesPerSec )
93 {
94 this.soundToStart = soundToStart;
95
96 int freq = soundToStart.getFrequency();
97 switch( freq )
98 {
99 case SWFConstants.SOUND_FREQ_5_5KHZ: freq = 5500; break;
100 case SWFConstants.SOUND_FREQ_11KHZ: freq = 11000; break;
101 case SWFConstants.SOUND_FREQ_22KHZ: freq = 22000; break;
102 case SWFConstants.SOUND_FREQ_44KHZ: freq = 44000; break;
103 default: freq = 22000; break;
104 }
105
106 int samples = soundToStart.getSampleCount();
107 int length = samples / freq;
108 return length * framesPerSec;
109 }
110
111 /**
112 * Get the frame actions
113 */
114 public Actions getActions() { return actions; }
115
116 /**
117 * Set the frame actions (or null them out)
118 */
119 public void setActions( Actions actions ) { this.actions = actions; }
120
121 /**
122 * Reset the frame actions (if any) and return the new empty Actions object
123 */
124 public Actions actions( int flashVersion )
125 {
126 actions = new Actions( 0, flashVersion );
127 return actions;
128 }
129
130 /**
131 * Get the frame number
132 */
133 public int getFrameNumber() { return frameNumber; }
134
135 /**
136 * Get the placements in this frame
137 */
138 public Placement[] getPlacements()
139 {
140 Placement[] p= new Placement[ placements.size() ];
141 placements.copyInto( p );
142 return p;
143 }
144
145 /**
146 * Get the frame label
147 * @return null if the frame has no label
148 */
149 public String getLabel() { return label; }
150
151 /**
152 * Set the frame label - set to null to clear any label
153 */
154 public void setLabel( String label ) { this.label = label; }
155
156 /**
157 * Set the stop flag - if true then the movie will stop at this frame.
158 * This can be set on the last frame to prevent the movie looping.
159 */
160 public void stop() { this.stop = true; }
161
162
163 /**
164 * Place a symbol at the given coordinates at the next available
165 * depth.
166 */
167 public Instance placeSymbol( Symbol symbol, int x, int y )
168 {
169 return placeSymbol( symbol, new Transform( x, y ), null, -1, -1 );
170 }
171
172 /**
173 * Place a symbol at the next available depth with the given
174 * matrix transform and color transform.
175 *
176 * @param matrix may be null to place the symbol at (0,0)
177 * @param cxform may be null if no color transform is required
178 */
179 public Instance placeSymbol( Symbol symbol, Transform matrix, AlphaTransform cxform )
180 {
181 return placeSymbol( symbol, matrix, cxform, -1, -1 );
182 }
183
184 /**
185 * Place a symbol at the next available depth with the given properties.
186 *
187 * @param matrix may be null to place the symbol at (0,0)
188 * @param cxform may be null if no color transform is required
189 * @param ratio only for a MorphShape - the morph ratio from 0 to 65535,
190 * should be -1 for a non-MorphShape
191 * @param clipDepth the top depth that will be clipped by the symbol, should
192 * be -1 if this is not a clipping symbol
193 */
194 public Instance placeSymbol( Symbol symbol, Transform matrix, AlphaTransform cxform,
195 int ratio, int clipDepth )
196 {
197 int depth = timeline.getAvailableDepth();
198 Instance inst = new Instance( symbol, depth );
199 timeline.setAvailableDepth( depth+1 );
200
201 if( matrix == null ) matrix = new Transform();
202
203 Placement placement = new Placement( inst, matrix, cxform, null,
204 ratio, clipDepth, frameNumber, false,
205 false, null );
206
207 placements.add( placement );
208 return inst;
209 }
210
211 /**
212 * Replace the symbol at the given depth with the new symbol
213 *
214 * @param matrix may be null to place the symbol at (0,0)
215 * @param cxform may be null if no color transform is required
216 * @param ratio only for a MorphShape - the morph ratio from 0 to 65535,
217 * should be -1 for a non-MorphShape
218 * @param clipDepth the top depth that will be clipped by the symbol, should
219 * be -1 if this is not a clipping symbol
220 */
221 public Instance replaceSymbol( Symbol symbol, int depth,
222 Transform matrix, AlphaTransform cxform,
223 int ratio, int clipDepth )
224 {
225 Instance inst = new Instance( symbol, depth );
226
227 if( matrix == null ) matrix = new Transform();
228
229 Placement placement = new Placement( inst, matrix, cxform, null,
230 ratio, clipDepth, frameNumber,
231 false, true, null );
232
233 placements.add( placement );
234 return inst;
235 }
236
237 /**
238 * Free up the given symbol so that it no longer takes up memory.
239 * All instances of the symbol must be removed first.
240 * This is useful with large images when they are no longer required.
241 */
242 public void undefineSymbol( Symbol symbol )
243 {
244 placements.add( new Placement( symbol ) );
245 }
246
247 /**
248 * Place a Movie Clip at the next available depth with the given properties.
249 *
250 * @param matrix may be null to place the symbol at (0,0)
251 * @param cxform may be null if no color transform is required
252 * @param name the instance name of a MovieClip - should be null if this is
253 * not a MovieClip
254 * @param clipAction an array of Actions (with clipAction conditions)
255 */
256 public Instance placeMovieClip( Symbol symbol, Transform matrix,
257 AlphaTransform cxform, String name,
258 Actions[] clipActions )
259 {
260 int depth = timeline.getAvailableDepth();
261 Instance inst = new Instance( symbol, depth );
262 timeline.setAvailableDepth( depth+1 );
263
264 if( matrix == null ) matrix = new Transform();
265
266 Placement placement = new Placement( inst, matrix, cxform, name,
267 -1, -1, frameNumber, false, false,
268 clipActions );
269
270 placements.add( placement );
271 return inst;
272 }
273
274 /**
275 * Replace the Symbol at the given depth with the new MovieClip
276 *
277 * @param matrix may be null to place the symbol at (0,0)
278 * @param cxform may be null if no color transform is required
279 * @param name the instance name of a MovieClip - should be null if this is
280 * not a MovieClip
281 * @param clipAction an array of Actions (with clipAction conditions)
282 */
283 public Instance replaceMovieClip( Symbol symbol, int depth,
284 Transform matrix,
285 AlphaTransform cxform, String name,
286 Actions[] clipActions )
287 {
288 Instance inst = new Instance( symbol, depth );
289
290 if( matrix == null ) matrix = new Transform();
291
292 Placement placement = new Placement( inst, matrix, cxform, name,
293 -1, -1, frameNumber, false, true,
294 clipActions );
295
296 placements.add( placement );
297 return inst;
298 }
299
300 /**
301 * Remove the symbol instance from the stage
302 */
303 public void remove( Instance instance )
304 {
305 placements.add( new Placement( instance, frameNumber ) );
306 }
307
308
309 /**
310 * Alter the symbol instance by moving it to the new coordinates.
311 * Only one alteration may be made to an Instance in any given frame.
312 */
313 public void alter( Instance instance, int x, int y )
314 {
315 alter( instance, new Transform( x, y ), null, -1 );
316 }
317
318 /**
319 * Alter the symbol instance by applying the given transform and/or
320 * color transform.
321 * Only one alteration may be made to an Instance in any given frame.
322 *
323 * @param matrix may be null if no positional change is to be made.
324 * @param cxform may be null if no color change is required.
325 */
326 public void alter( Instance instance, Transform matrix, AlphaTransform cxform )
327 {
328 alter( instance, matrix, cxform, -1 );
329 }
330
331 /**
332 * Alter the symbol instance by applying the given properties.
333 * Only one alteration may be made to an Instance in any given frame.
334 *
335 * @param matrix may be null if no positional change is to be made.
336 * @param cxform may be null if no color change is required.
337 * @param ratio only for a MorphShape - the morph ratio from 0 to 65535,
338 * should be -1 for a non-MorphShape
339 */
340 public void alter( Instance instance, Transform matrix,
341 AlphaTransform cxform, int ratio )
342 {
343 Placement placement = new Placement( instance, matrix, cxform, null,
344 ratio, -1, frameNumber, true, false, null );
345
346 placements.add( placement );
347 }
348
349 protected void flushDefinitions( Movie movie,
350 SWFTagTypes timelineWriter,
351 SWFTagTypes definitionWriter )
352 throws IOException
353 {
354 for( Enumeration enum = placements.elements(); enum.hasMoreElements(); )
355 {
356 Placement placement = (Placement)enum.nextElement();
357
358 placement.flushDefinitions( movie, timelineWriter, definitionWriter );
359 }
360 }
361
362 /**
363 * Write the frame
364 */
365 protected void write( Movie movie,
366 SWFTagTypes movieTagWriter,
367 SWFTagTypes timelineTagWriter )
368 throws IOException
369 {
370 if( customTag >= 0 )
371 {
372 timelineTagWriter.tag( customTag, false, customTagData );
373 }
374
375 if( actions != null )
376 {
377 SWFActions acts = timelineTagWriter.tagDoAction();
378 acts.start(0);
379 acts.blob( actions.bytes );
380 acts.done();
381 }
382
383 if( stop )
384 {
385 SWFActions actions = timelineTagWriter.tagDoAction();
386
387 actions.start(0);
388 actions.stop();
389 actions.end();
390 actions.done();
391 }
392
393 if( soundHeader != null ) soundHeader.write( timelineTagWriter );
394 if( soundData != null ) timelineTagWriter.tagSoundStreamBlock( soundData );
395 if( soundToStart != null ) timelineTagWriter.tagStartSound(
396 soundToStart.define( movie, movieTagWriter, timelineTagWriter ),
397 new SoundInfo( true, false, null, -1, -1, 0 ));
398
399 for( Enumeration enum = placements.elements(); enum.hasMoreElements(); )
400 {
401 Placement placement = (Placement)enum.nextElement();
402
403 placement.write( movie, movieTagWriter, timelineTagWriter );
404 }
405
406 if( label != null ) timelineTagWriter.tagFrameLabel( label, mAnchor );
407 timelineTagWriter.tagShowFrame();
408 }
409 }