001: /*
002: * $Id: Instance.java,v 1.2 2002/02/15 23:44:27 skavish Exp $
003: *
004: * ==========================================================================
005: *
006: * The JGenerator Software License, Version 1.0
007: *
008: * Copyright (c) 2000 Dmitry Skavish (skavish@usa.net). All rights reserved.
009: *
010: * Redistribution and use in source and binary forms, with or without
011: * modification, are permitted provided that the following conditions are met:
012: *
013: * 1. Redistributions of source code must retain the above copyright
014: * notice, this list of conditions and the following disclaimer.
015: *
016: * 2. Redistributions in binary form must reproduce the above copyright
017: * notice, this list of conditions and the following disclaimer in
018: * the documentation and/or other materials provided with the
019: * distribution.
020: *
021: * 3. The end-user documentation included with the redistribution, if
022: * any, must include the following acknowlegement:
023: * "This product includes software developed by Dmitry Skavish
024: * (skavish@usa.net, http://www.flashgap.com/)."
025: * Alternately, this acknowlegement may appear in the software itself,
026: * if and wherever such third-party acknowlegements normally appear.
027: *
028: * 4. The name "The JGenerator" must not be used to endorse or promote
029: * products derived from this software without prior written permission.
030: * For written permission, please contact skavish@usa.net.
031: *
032: * 5. Products derived from this software may not be called "The JGenerator"
033: * nor may "The JGenerator" appear in their names without prior written
034: * permission of Dmitry Skavish.
035: *
036: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
037: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
038: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
039: * DISCLAIMED. IN NO EVENT SHALL DMITRY SKAVISH OR THE OTHER
040: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
041: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
042: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
043: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
044: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
045: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
046: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
047: * SUCH DAMAGE.
048: *
049: */
050:
051: package org.openlaszlo.iv.flash.api;
052:
053: import java.awt.geom.*;
054:
055: import java.io.PrintStream;
056: import org.openlaszlo.iv.flash.parser.*;
057: import org.openlaszlo.iv.flash.util.*;
058: import org.openlaszlo.iv.flash.api.action.*;
059: import org.openlaszlo.iv.flash.commands.*;
060: import org.openlaszlo.iv.flash.context.Context;
061:
062: /**
063: * Instance of flash definition
064: * <P>
065: * This object represents flash file format's PlaceObject2 as well as PlaceObject tags.
066: * <P>
067: * PlaceObject2 can both add a character to the display list, and modify the attributes of a
068: * character that is already on the display list.
069: * <p>
070: * The tag begins with a group of flags that indicate which fields are present in the tag.
071: * The optional fields are <code>def</code>, <code>matrix</code>, <code>cxform</code>,
072: * <code>ratio</code>, <code>clip</code> and <code>name</code>.
073: * The <code>depth</code> field is the only field that is always required.
074: * <p>
075: * The depth value determines the stacking order of the character. Characters with lower depth values
076: * are displayed underneath characters with higher depth values. A depth value of 1 means the character
077: * is displayed at the bottom of the stack. There can be only one character at any given depth.
078: * This means a character that is already on the display list can be identified by its depth alone.
079: * (i.e. a <code>def</code> is not required).
080: * <p>
081: * Flag Move and flag HasCharacter indicate whether a new character is being added to the display list,
082: * or a character already on the display list is being modified. The meaning of the flags is as follows:<br>
083: * <ul>
084: * <li> PlaceFlagMove = 0 and PlaceFlagHasCharacter = 1<br>
085: * A new character (with ID of CharacterId) is placed on the display list at the specified Depth.
086: * Other fields set the attributes of this new character.
087: * <li> PlaceFlagMove = 1 and PlaceFlagHasCharacter = 0<br>
088: * The character at the specified Depth is modified. Other fields modify the attributes of this character.
089: * Because there can be only one character at any given depth, no CharacterId is required.
090: * <li> PlaceFlagMove = 1 and PlaceFlagHasCharacter = 1<br>
091: * The character at the specified Depth is removed, and a new character (with ID of CharacterId) is
092: * placed at that depth. Other fields set the attributes of this new character.
093: * </ul>
094: * <p>
095: * The optional fields in PlaceObject2 have the following meaning:<br>
096: * <ul>
097: * <li>The <code>def</code> field specifies the character to be added to the display list.
098: * It only used only when a new character is being added. If a character that is already on
099: * the display list is being modified, the <code>def</code> field is absent.
100: * <li>The <code>matrix</code> field specifies the position, scale and rotation of the character being added or modified.
101: * <li>The <code>cxform</code> field specifies the color effect applied to the character being added or modified.
102: * <li>The <code>ratio</code> field specifies a morph ratio for the character being added or modified.
103: * This field applies only to characters defined with DefineMorphShape,
104: * and controls how far the morph has progressed.
105: * A ratio of zero displays the character at the start of the morph. A ratio of 65535 displays the character
106: * at the end of the morph. For values between zero and 65535 the player interpolates between the start and end
107: * shapes, and displays an 'in-between' shape.
108: * <li>The <code>clip</code> field specifies the top-most depth that will be masked by the character being added.
109: * A clip of zero indicates no clipping.
110: * <li>The <code>name</code> field specifies a name for the character being added or modified. This field is typically used
111: * with sprite characters, and is used to identify the sprite for SetTarget actions.
112: * It allows the main movie (or other sprites) to perform actions inside the sprite.
113: * </ul>
114: *
115: * @author Dmitry Skavish
116: * @see RemoveObject
117: */
118: public class Instance extends FlashObject {
119:
120: public static final int HAS_CLIPACTIONS = 0x80;
121: public static final int HAS_CLIP = 0x40;
122: public static final int HAS_NAME = 0x20;
123: public static final int HAS_RATIO = 0x10;
124: public static final int HAS_COLOR_TRANSF = 0x08;
125: public static final int HAS_MATRIX = 0x04;
126: public static final int HAS_CHARACTER = 0x02;
127: public static final int MOVE = 0x01;
128:
129: /**
130: * true if this instance just for changing an object properties,
131: * such as transformation etc.
132: */
133: public boolean isMove;
134:
135: /**
136: * Transformation matrix
137: */
138: public AffineTransform matrix;
139:
140: /**
141: * Color transformation matrix or null
142: */
143: public CXForm cxform;
144:
145: /**
146: * Morphing ratio or -1
147: */
148: public int ratio = -1;
149:
150: /**
151: * Clip layer number or -1
152: */
153: public int clip = -1;
154:
155: /**
156: * Layer depth
157: */
158: public int depth;
159:
160: /**
161: * Instance name or null
162: */
163: public String name;
164:
165: /**
166: * Flash definition or null
167: */
168: public FlashDef def;
169:
170: /**
171: * Clip actions or null
172: */
173: public ClipActions actions;
174:
175: /**
176: * Generator command applied to this instance or null
177: */
178: public GenericCommand command;
179:
180: public Instance() {
181: }
182:
183: public int getTag() {
184: return Tag.PLACEOBJECT2;
185: }
186:
187: /**
188: * Parses PlaceObject tag
189: *
190: * @param p parser
191: * @return parser tag
192: */
193: public static Instance parse(Parser p) {
194: Instance o = new Instance();
195: o.def = p.getDef(p.getUWord());
196: o.depth = p.getUWord();
197: o.matrix = p.getMatrix();
198: if (p.getPos() < p.getTagEndPos()) {
199: o.cxform = CXForm.parse(p, false);
200: }
201: return o;
202: }
203:
204: /**
205: * Parses PlaceObject2 tag
206: *
207: * @param p parser
208: * @return parser tag
209: */
210: public static Instance parse2(Parser p) {
211: Instance o = new Instance();
212: int flags = p.getUByte();
213: o.depth = p.getUWord();
214: if ((flags & HAS_CHARACTER) != 0) {
215: o.def = p.getDef(p.getUWord());
216: }
217: if ((flags & HAS_MATRIX) != 0) {
218: o.matrix = p.getMatrix();
219: }
220: if ((flags & HAS_COLOR_TRANSF) != 0) {
221: o.cxform = CXForm.parse(p, true);
222: }
223: if ((flags & HAS_RATIO) != 0) {
224: o.ratio = p.getUWord();
225: }
226: if ((flags & HAS_CLIP) != 0) {
227: o.clip = p.getUWord();
228: }
229: if ((flags & HAS_NAME) != 0) {
230: o.name = p.getString();
231: }
232: if ((flags & HAS_CLIPACTIONS) != 0) {
233: o.actions = ClipActions.parse(p);
234: }
235: o.isMove = (flags & MOVE) != 0;
236: return o;
237: }
238:
239: /**
240: * Returns true if this is instance of a script
241: *
242: * @return true if this is instance of a script
243: */
244: public boolean isScript() {
245: return def instanceof Script;
246: }
247:
248: /**
249: * Returns flash definition as a Script
250: *
251: * @return definition as a Script
252: */
253: public Script getScript() {
254: return (Script) def;
255: }
256:
257: /**
258: * Sets specified script as flash definition of this instance
259: *
260: * @param script specified script
261: * @return specified script
262: */
263: public Script setScript(Script script) {
264: def = script;
265: return script;
266: }
267:
268: /**
269: * Makes a copy of a script which is the flash definition of this instance
270: *
271: * @return copy of the script
272: */
273: public Script copyScript() {
274: def = ((Script) def).copyScript();
275: return (Script) def;
276: }
277:
278: /**
279: * Return true if there is generator command attached to this instance
280: *
281: * @return true if there is generator command attached to this instance
282: */
283: public boolean isCommand() {
284: return command != null;
285: }
286:
287: /**
288: * Attaches generator command to this instance
289: *
290: * @param command generator command to be attached
291: */
292: public void setCommand(GenericCommand command) {
293: this .command = command;
294: }
295:
296: /**
297: * Returns attached generator command
298: *
299: * @return attached generator command
300: */
301: public GenericCommand getCommand() {
302: return command;
303: }
304:
305: /**
306: * Returns first nested non-script of this instance
307: * and its transformation matrix beginning from this instance
308: *
309: * @param m collect transformations in this matrix
310: * @return first nested non-script
311: */
312: public FlashDef getFirstNestedFlashDef(AffineTransform m) {
313: if (def == null)
314: return null;
315:
316: if (matrix != null)
317: m.concatenate(matrix);
318:
319: if (def instanceof Script) {
320: Script script = (Script) def;
321: if (script.getFrameCount() > 0) {
322: Frame frame = script.getFrameAt(0);
323: for (int i = 0; i < frame.size(); i++) {
324: FlashObject fo = frame.getFlashObjectAt(i);
325: if (fo instanceof Instance) {
326: FlashDef df = ((Instance) fo)
327: .getFirstNestedFlashDef(m);
328: if (df != null)
329: return df;
330: } else if (fo instanceof FlashDef) {
331: return (FlashDef) fo;
332: }
333: }
334: }
335: return null;
336: } else {
337: return def;
338: }
339: }
340:
341: public void collectDeps(DepsCollector dc) {
342: // if this instance is mask (clip!=-1), then we need to get first nested
343: // FlashDef, preserve its transformation and discard everything else
344: if (clip != -1 && def instanceof Script) {
345: AffineTransform m = new AffineTransform();
346: def = getFirstNestedFlashDef(m);
347: matrix = m;
348: }
349: if (def != null) {
350: dc.addDep(def);
351: //System.out.println( "Instance.collectDeps: def="+def.getID() );
352: }
353: }
354:
355: public void collectFonts(FontsCollector fc) {
356: if (def != null)
357: def.collectFonts(fc);
358: }
359:
360: public void write(FlashOutput fob) {
361: int tagPos = fob.getPos();
362: // quick guess: if name's length more than 8 or there are clip actions, we generate long tag
363: boolean longtag = (name != null && name.length() > 8)
364: || actions != null;
365: if (longtag) {
366: fob.skip(6);
367: } else {
368: fob.skip(2);
369: }
370: int flags = (actions != null ? HAS_CLIPACTIONS : 0)
371: | (def != null ? HAS_CHARACTER : 0)
372: | (matrix != null ? HAS_MATRIX : 0)
373: | (cxform != null ? HAS_COLOR_TRANSF : 0)
374: | (name != null ? HAS_NAME : 0)
375: | (ratio != -1 ? HAS_RATIO : 0)
376: | (clip != -1 ? HAS_CLIP : 0) | (isMove ? MOVE : 0);
377: fob.writeByte(flags);
378: fob.writeWord(depth);
379: if (def != null)
380: fob.writeDefID(def);
381: if (matrix != null)
382: fob.write(matrix);
383: if (cxform != null)
384: cxform.write(fob);
385: if (ratio != -1)
386: fob.writeWord(ratio);
387: if (clip != -1)
388: fob.writeWord(clip);
389: if (name != null)
390: fob.writeStringZ(name);
391: if (actions != null)
392: actions.write(fob);
393:
394: if (longtag) {
395: fob.writeLongTagAt(Tag.PLACEOBJECT2, fob.getPos() - tagPos
396: - 6, tagPos);
397: } else {
398: fob.writeShortTagAt(Tag.PLACEOBJECT2, fob.getPos() - tagPos
399: - 2, tagPos);
400: }
401: }
402:
403: public void printContent(PrintStream out, String indent) {
404: out.println(indent + "Instance: depth=" + depth);
405: if (matrix != null)
406: out.println(indent + " " + matrix.toString());
407: if (cxform != null)
408: cxform.printContent(out, indent + " ");
409: if (def != null)
410: out.println(indent + " charID=" + def.getID());
411: if (ratio != -1)
412: out.println(indent + " ratio=" + ratio);
413: if (clip != -1)
414: out.println(indent + " clip=" + clip);
415: if (name != null)
416: out.println(indent + " name=" + name);
417: if (actions != null)
418: actions.printContent(out, indent + " ");
419: if (command != null)
420: command.printContent(out, indent);
421: }
422:
423: protected boolean _isConstant() {
424: if (isCommand())
425: return false;
426: if (name != null && Util.hasVar(name))
427: return false;
428: if (actions != null && !actions.isConstant())
429: return false;
430: if (def != null)
431: return def.isConstant();
432: return true;
433: }
434:
435: public void process(FlashFile file, Context context)
436: throws IVException {
437: if (def != null && !isCommand()) {
438: def.process(file, context);
439: }
440: }
441:
442: public boolean isProcessed() {
443: if (def != null && !isCommand()) {
444: return def.isProcessed();
445: }
446: return true;
447: }
448:
449: public void setProcessed() {
450: if (def != null && !isCommand()) {
451: def.setProcessed();
452: }
453: }
454:
455: public void apply(Context context) {
456: if (isCommand())
457: return;
458: super .apply(context);
459: name = context.apply(name);
460: if (actions != null)
461: actions.apply(context);
462: if (def != null)
463: def.apply(context);
464: }
465:
466: public Rectangle2D getBounds() {
467: if (def == null)
468: return null;
469: if (matrix == null)
470: return def.getBounds();
471:
472: return GeomHelper.calcBounds(matrix, def.getBounds());
473: }
474:
475: protected FlashItem copyInto(FlashItem item, ScriptCopier copier) {
476: super .copyInto(item, copier);
477: ((Instance) item).matrix = matrix != null ? (AffineTransform) matrix
478: .clone()
479: : null;
480: ((Instance) item).cxform = cxform != null ? (CXForm) cxform
481: .getCopy(copier) : null;
482: ((Instance) item).isMove = isMove;
483: ((Instance) item).ratio = ratio;
484: ((Instance) item).clip = clip;
485: ((Instance) item).depth = depth;
486: ((Instance) item).name = name;
487: ((Instance) item).actions = actions != null ? (ClipActions) actions
488: .getCopy(copier)
489: : null;
490: ((Instance) item).def = copier.copy(def);
491: GenericCommand myCommand = command != null ? (GenericCommand) command
492: .getCopy(copier)
493: : null;
494: if (myCommand != null)
495: myCommand.setInstance((Instance) item);
496: ((Instance) item).command = myCommand;
497: return item;
498: }
499:
500: public FlashItem getCopy(ScriptCopier copier) {
501: return copyInto(new Instance(), copier);
502: }
503: }
|