0001: /*
0002: * $Id: Script.java,v 1.13 2002/08/08 23:26:54 skavish Exp $
0003: *
0004: * ==========================================================================
0005: *
0006: * The JGenerator Software License, Version 1.0
0007: *
0008: * Copyright (c) 2000 Dmitry Skavish (skavish@usa.net). All rights reserved.
0009: *
0010: * Redistribution and use in source and binary forms, with or without
0011: * modification, are permitted provided that the following conditions are met:
0012: *
0013: * 1. Redistributions of source code must retain the above copyright
0014: * notice, this list of conditions and the following disclaimer.
0015: *
0016: * 2. Redistributions in binary form must reproduce the above copyright
0017: * notice, this list of conditions and the following disclaimer in
0018: * the documentation and/or other materials provided with the
0019: * distribution.
0020: *
0021: * 3. The end-user documentation included with the redistribution, if
0022: * any, must include the following acknowlegement:
0023: * "This product includes software developed by Dmitry Skavish
0024: * (skavish@usa.net, http://www.flashgap.com/)."
0025: * Alternately, this acknowlegement may appear in the software itself,
0026: * if and wherever such third-party acknowlegements normally appear.
0027: *
0028: * 4. The name "The JGenerator" must not be used to endorse or promote
0029: * products derived from this software without prior written permission.
0030: * For written permission, please contact skavish@usa.net.
0031: *
0032: * 5. Products derived from this software may not be called "The JGenerator"
0033: * nor may "The JGenerator" appear in their names without prior written
0034: * permission of Dmitry Skavish.
0035: *
0036: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
0037: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
0038: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
0039: * DISCLAIMED. IN NO EVENT SHALL DMITRY SKAVISH OR THE OTHER
0040: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
0041: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
0042: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
0043: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
0044: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
0045: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
0046: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
0047: * SUCH DAMAGE.
0048: *
0049: */
0050:
0051: /*
0052: * 12/11/2001 fixed bug in getBounds(), now it understands masks
0053: *
0054: */
0055:
0056: package org.openlaszlo.iv.flash.api;
0057:
0058: import java.io.*;
0059: import java.util.*;
0060: import java.awt.geom.Rectangle2D;
0061: import java.awt.geom.AffineTransform;
0062:
0063: import org.openlaszlo.iv.flash.util.*;
0064:
0065: import org.openlaszlo.iv.flash.parser.*;
0066: import org.openlaszlo.iv.flash.commands.*;
0067: import org.openlaszlo.iv.flash.cache.*;
0068: import org.openlaszlo.iv.flash.api.text.*;
0069: import org.openlaszlo.iv.flash.api.image.*;
0070: import org.openlaszlo.iv.flash.api.sound.*;
0071: import org.openlaszlo.iv.flash.api.shape.*;
0072: import org.openlaszlo.iv.flash.api.action.*;
0073: import org.openlaszlo.iv.flash.api.button.*;
0074: import org.openlaszlo.iv.flash.context.Context;
0075: import org.openlaszlo.iv.flash.context.FakeContext;
0076: import org.openlaszlo.iv.flash.context.StandardContext;
0077:
0078: /**
0079: * Flash movie clip
0080: * <p>
0081: * Contains a timeline, a background color and generator commands of script level.
0082: * <P>
0083: * Excerpt from flash file format specs:
0084: * <P>
0085: * A script corresponds to a 'movie clip' in the Flash user-interface.
0086: * It is a movie contained within a movie, and supports many of the features of a regular Flash movie, including:<br>
0087: * <ul>
0088: * <li>Most of the control tags that can be used in the main movie.
0089: * <li>A timeline that can stop, start and play independently of the main movie.
0090: * <li>A streaming sound track that is automatically mixed with the main sound track.
0091: * </ul>
0092: * <p>
0093: * A script object is defined with a DefineSprite tag. It consists of a character ID, a frame count, and a
0094: * series of control tags. Definition tags (such as DefineShape) are not allowed in the DefineSprite tag.
0095: * All the characters referred to by control tags in the sprite must be defined outside the sprite,
0096: * and before the DefineSprite tag.
0097: * <p>
0098: * Once defined, a script is displayed with a PlaceObject2 tag in the main movie. The transform (specified in
0099: * PlaceObject) is concatenated with the transforms of objects placed inside the script. These objects behave
0100: * like 'children' of the script, so when the script is moved, the objects inside the script move also.
0101: * Similarly, when the script is scaled or rotated, the child objects are also scaled or rotated.
0102: * A script object stops playing automatically when it is removed from the display list.
0103: *
0104: * @author Dmitry Skavish
0105: */
0106: public final class Script extends FlashDef {
0107:
0108: private Timeline timeline; // a timeline of the script
0109: private boolean isProcessed = false; // true if this script is already processed by jgenerator
0110: private IVVector gl_commands; // global generator commands, like SetEnvironment etc
0111: private IVVector zero_frame; // zero frame, it usually contains some sound heads etc
0112: private SetBackgroundColor bkgColor; // background color of the script
0113:
0114: /**
0115: * Creates empty script
0116: */
0117: public Script() {
0118: }
0119:
0120: /**
0121: * Creates empty script with specified initial capacity
0122: *
0123: * @param frameNumber initial capacity of this script in frames
0124: * @see #Script(int, int)
0125: */
0126: public Script(int frameNumber) {
0127: this (0, frameNumber);
0128: }
0129:
0130: /**
0131: * Creates empty script with specified initial capacity and ID
0132: * <P>
0133: * The capacity specifies the capacity of this script's
0134: * timeline (in frames). It does not specify how many frames
0135: * this script will eventually have.
0136: *
0137: * @param objID ID of this script, if it's -1, then this is a main script
0138: * @param frameNumber initial capacity of this script in frames
0139: */
0140: public Script(int objID, int frameNumber) {
0141: timeline = new Timeline(frameNumber + 1);
0142: setID(objID);
0143: }
0144:
0145: /**
0146: * Returns true if this script is a main script
0147: * <P>
0148: * Main script is a script which represents main flash timeline
0149: *
0150: * @return true if this is a main script
0151: */
0152: public boolean isMain() {
0153: return getID() == -1;
0154: }
0155:
0156: /**
0157: * Sets this script to be main
0158: */
0159: public void setMain() {
0160: setID(-1);
0161: }
0162:
0163: /**
0164: * Sets this script to be NOT main script
0165: */
0166: public void resetMain() {
0167: setID(0);
0168: }
0169:
0170: /**
0171: * Returns this script's timeline
0172: *
0173: * @return script's timeline
0174: */
0175: public Timeline getTimeline() {
0176: return timeline;
0177: }
0178:
0179: /**
0180: * Writes content of this script to flash buffer
0181: * <P>
0182: * This method has to be used only if this script is main script.
0183: *
0184: * @param fob flash buffer to write
0185: * @see #isMain
0186: * @see #write
0187: */
0188: public void generate(FlashOutput fob) {
0189: int frameCount = timeline.getFrameCount();
0190: fob.writeWord(frameCount);
0191:
0192: // collect fonts and make sure there is only one instance of each font
0193: FontsCollector fc = new FontsCollector();
0194: collectFonts(fc);
0195:
0196: // Create a slot for license key first
0197: int licenseLen = 32;
0198: fob.writeTag(Tag.SERIALNUMBER, licenseLen);
0199: for (int i = 0; i < licenseLen; i++)
0200: fob.writeByte(0x78); // x
0201:
0202: // write background color
0203: if (bkgColor != null)
0204: bkgColor.write(fob);
0205:
0206: mergeFonts(fc.getFonts());
0207:
0208: // generate timeline in right order (definitions first)
0209: timeline.generate(fob, new DepsCollector(fc));
0210:
0211: Tag.END_TAG.write(fob);
0212: }
0213:
0214: /**
0215: * Writes content of this script to flash buffer
0216: * <P>
0217: * This method has to be used only if this script is main script.
0218: *
0219: * @param fob flash buffer to write
0220: * @param fc1
0221: * @param pfc preloader fonts
0222: * @param hasPreloader true if preloader exists
0223: * @see #isMain
0224: * @see #write
0225: */
0226: public void generate(FlashOutput fob, FontsCollector fc1,
0227: FontsCollector pfc, boolean hasPreloader) {
0228: int frameCount = timeline.getFrameCount();
0229: fob.writeWord(frameCount);
0230:
0231: // write background color first
0232: if (bkgColor != null)
0233: bkgColor.write(fob);
0234:
0235: // Fonts in fc1 includes fonts declared in the Laszlo app as well as fonts that
0236: // are used in SWF assets that are imported.
0237: //
0238: // Fonts in pfc are fonts that are used in SWF assets that are imported in the preloader/splash.
0239: // FIXME: [2004-03-09 bloch] If a font name is used in a SWF asset that's in the preloader *and*
0240: // a laszlo font name, then there might be a prob
0241: // here since only the preloader version will show up and if the font needed merging, some
0242: // text will show up blank.
0243: mergeFonts(fc1.getFonts());
0244: if (hasPreloader) {
0245: mergeFonts(pfc.getFonts());
0246: }
0247:
0248: IVVector fonts;
0249: int frame = 0;
0250: // Assume that preloader is only one frame long in the main timeline
0251: // starting on frame 0.
0252: if (hasPreloader) {
0253: // add preloader fonts here
0254: fonts = pfc.getFonts();
0255: for (int i = 0; i < fonts.size(); i++) {
0256: FontDef fontDef = (FontDef) fonts.elementAt(i);
0257: fontDef.write(fob);
0258: }
0259:
0260: // write out preloader at frame 0 and place the rest of the app in
0261: // frame 1.
0262: Frame fo = (Frame) timeline.elementAt(frame++);
0263: fo.generate(fob, new DepsCollector(new FontsCollector()));
0264: }
0265:
0266: // Place the rest of the fonts
0267: fonts = fc1.getFonts();
0268: for (int i = 0; i < fonts.size(); i++) {
0269: FontDef fontDef = (FontDef) fonts.elementAt(i);
0270: fontDef.write(fob);
0271: }
0272:
0273: // generate timeline in right order, but starting with
0274: // frame 1
0275: timeline.generate(fob, new DepsCollector(new FontsCollector()),
0276: frame);
0277:
0278: Tag.END_TAG.write(fob);
0279: }
0280:
0281: /**
0282: * Writes content of this script to flash buffer
0283: * <P>
0284: * This method has to be used only if this script is NOT a main script.
0285: *
0286: * @param fob flash buffer to write
0287: * @see #isMain
0288: * @see #generate
0289: */
0290: public void write(FlashOutput fob) {
0291: int start = fob.getPos(); // save for length calculating
0292: fob.skip(6); // 6 - long tag
0293: fob.writeDefID(this );
0294: int frameCount = timeline.getFrameCount();
0295: fob.writeWord(frameCount);
0296:
0297: // write background color first
0298: if (bkgColor != null)
0299: bkgColor.write(fob);
0300:
0301: // write "zero frame"
0302: if (zero_frame != null)
0303: zero_frame.write(fob);
0304:
0305: // write timeline
0306: timeline.write(fob);
0307:
0308: // write end tag
0309: Tag.END_TAG.write(fob);
0310:
0311: int size = fob.getPos() - start - 6; // 6 - long tag
0312: fob.writeLongTagAt(Tag.DEFINESPRITE, size, start);
0313: }
0314:
0315: /**
0316: * Parses script
0317: *
0318: * @param p parser
0319: * @param isMain true if script to be parsed is main
0320: * @return new parsed script
0321: * @exception IVException
0322: */
0323: public static Script parse(Parser p, boolean isMain)
0324: throws IVException {
0325: int objID = -1;
0326: if (!isMain) {
0327: objID = p.getUWord();
0328: }
0329:
0330: // number of frames
0331: int frameNum = p.getUWord();
0332: // create script
0333: Script sc = new Script(objID, frameNum);
0334:
0335: Timeline tl = sc.getTimeline();
0336: IVVector tFrame = new IVVector();
0337: sc.zero_frame = tFrame;
0338: String frameName = null;
0339: boolean is_anchor = false;
0340:
0341: for (;;) {
0342: int code = p.getTag();
0343:
0344: // check for tag
0345: switch (code) {
0346: /* Control tags */
0347: case Tag.SHOWFRAME: {
0348: Frame f = new Frame(tFrame);
0349: f.setName(frameName);
0350: f.setAnchor(is_anchor);
0351: frameName = null;
0352: is_anchor = false;
0353: tl.addFrame(f);
0354: tFrame.reset();
0355: break;
0356: }
0357: case Tag.END:
0358: return sc;
0359: case Tag.PLACEOBJECT:
0360: tFrame.addElement(Instance.parse(p));
0361: break;
0362: case Tag.PLACEOBJECT2: {
0363: Instance instance = Instance.parse2(p);
0364: GenericCommand cmd = GenericCommand
0365: .checkAndParseMX(instance);
0366: if (cmd != null && cmd.isGlobal()) {
0367: sc.addGlobalCommand(cmd);
0368: } else {
0369: tFrame.addElement(instance);
0370: }
0371: break;
0372: }
0373: case Tag.REMOVEOBJECT:
0374: tFrame.addElement(RemoveObject.parse(p));
0375: break;
0376: case Tag.REMOVEOBJECT2:
0377: tFrame.addElement(RemoveObject.parse2(p));
0378: break;
0379: case Tag.SETBKGCOLOR:
0380: sc.setBackgroundColor(SetBackgroundColor.parse(p));
0381: break;
0382: case Tag.STARTSOUND:
0383: tFrame.addElement(StartSound.parse(p));
0384: break;
0385: case Tag.DOACTION:
0386: tFrame.addElement(DoAction.parse(p));
0387: break;
0388: case Tag.INITCLIPACTION:
0389: tFrame.addElement(InitClipAction.parse(p));
0390: break;
0391: case Tag.TEMPLATECOMMAND: {
0392: GenericCommand cmd = GenericCommand.parse(p);
0393: if (cmd == null)
0394: break;
0395: if (!cmd.isGlobal()) {
0396: // this is not a global command, search backward for corresponding instance
0397: int j = tl.getFrameCount();
0398: IVVector myFrame = tFrame;
0399: timelineLoop: for (;;) {
0400: for (int i = myFrame.size(); --i >= 0;) {
0401: Object o = myFrame.elementAt(i);
0402: if (o instanceof Instance) {
0403: Instance inst = (Instance) o;
0404: if (inst.depth != cmd.getDepth())
0405: continue;
0406: cmd.setInstance(inst);
0407: inst.setCommand(cmd);
0408: break timelineLoop;
0409: }
0410: }
0411: if (--j < 0)
0412: break;
0413: myFrame = tl.getFrameAt(j);
0414: }
0415: ;
0416: if (j < 0) {
0417: Log.logRB(Resource.GENCMDERR,
0418: new Object[] { cmd.getCommandName() });
0419: }
0420: } else {
0421: sc.addGlobalCommand(cmd);
0422: }
0423: break;
0424: }
0425: /* Info tags */
0426: case Tag.FLASHGENERATOR:
0427: p.getFile().setTemplate(true);
0428: break;
0429: case Tag.PROTECT:
0430: tFrame.addElement(p.newUnknownTag());
0431: break;
0432: case Tag.SERIALNUMBER:
0433: break;
0434: case Tag.FRAMELABEL:
0435: //Util.dump(p.getBuf(), p.getTagDataPos(), p.getTagEndPos()-p.getTagDataPos(), System.out);
0436: frameName = p.getString();
0437: if (p.getPos() < p.getTagEndPos()) {
0438: is_anchor = p.getUByte() == 1;
0439: }
0440: break;
0441: case Tag.GENERATORTEXT: {
0442: int id = p.getUWord();
0443: Text text = (Text) p.getDef(id);
0444: text.parseGenText(p);
0445: break;
0446: }
0447: case Tag.NAMECHARACTER: {
0448: int id = p.getUWord();
0449: String name = p.getString();
0450: FlashDef def = p.getDef(id);
0451: def.setName(name);
0452: p.addDefToLibrary(name, def);
0453: break;
0454: }
0455: case Tag.FREECHARACTER:
0456: tFrame.addElement(FreeCharacter.parse(p));
0457: break;
0458: /* Streaming sound tags */
0459: case Tag.SOUNDSTREAMHEAD:
0460: case Tag.SOUNDSTREAMHEAD2:
0461: tFrame.addElement(SoundStreamHead.parse(p));
0462: break;
0463: case Tag.SOUNDSTREAMBLOCK:
0464: tFrame.addElement(SoundStreamBlock.parse(p));
0465: break;
0466: /* Definitions tags */
0467: case Tag.DEFINESPRITE:
0468: p.addDef(Script.parse(p, false));
0469: break;
0470: case Tag.DEFINEMOVIE:
0471: p.addDef(QTMovie.parse(p));
0472: break;
0473: case Tag.DEFINESHAPE:
0474: case Tag.DEFINESHAPE2:
0475: case Tag.DEFINESHAPE3:
0476: p.addDef(LazyShape.parse(p));
0477: break;
0478: case Tag.DEFINEMORPHSHAPE:
0479: p.addDef(LazyMorphShape.parse(p));
0480: break;
0481: case Tag.DEFINESOUND:
0482: p.addDef(LazySound.parse(p));
0483: break;
0484: case Tag.DEFINEFONT:
0485: p.addDef(FontDef.parse(p));
0486: break;
0487: case Tag.DEFINEFONTINFO:
0488: FontDef.parseFontInfoTag(p);
0489: break;
0490: case Tag.DEFINEFONTINFO2:
0491: FontDef.parseFontInfoTag2(p);
0492: break;
0493: case Tag.DEFINEFONT2:
0494: p.addDef(FontDef.parse2(p));
0495: break;
0496: case Tag.EXTERNALFONT: {
0497: FlashDef def = FontDef.parseExternalFontTag(p);
0498: if (def != null)
0499: p.addDef(def);
0500: break;
0501: }
0502: case Tag.DEFINETEXT:
0503: p.addDef(Text.parse(p, false));
0504: break;
0505: case Tag.DEFINETEXT2:
0506: p.addDef(Text.parse(p, true));
0507: break;
0508: case Tag.DEFINEEDITTEXT:
0509: p.addDef(TextField.parse(p));
0510: break;
0511: case Tag.DEFINEBUTTON:
0512: p.addDef(Button.parse(p));
0513: break;
0514: case Tag.DEFINEBUTTON2:
0515: p.addDef(Button2.parse2(p));
0516: break;
0517: case Tag.DEFINEBUTTONCXFORM:
0518: ButtonCXForm.parse(p); // this guy will add itself to a button, we need to do nothing
0519: break;
0520: case Tag.DEFINEBUTTONSOUND:
0521: ButtonSound.parse(p); // this guy will add itself to a button, we need to do nothing
0522: break;
0523: case Tag.DEFINEBITS:
0524: case Tag.DEFINEBITSJPEG2:
0525: case Tag.DEFINEBITSJPEG3:
0526: p.addDef(JPEGBitmap.parse(p));
0527: break;
0528: case Tag.DEFINEBITSLOSSLESS:
0529: case Tag.DEFINEBITSLOSSLESS2:
0530: p.addDef(LLBitmap.parse(p));
0531: break;
0532: case Tag.JPEGTABLES:
0533: JPEGBitmap.parseJPegTables(p);
0534: break;
0535: /* Flash 5 tags */
0536: case Tag.ENABLEDEBUGGER:
0537: tFrame.addElement(p.newUnknownTag());
0538: break;
0539: case Tag.EXPORTASSETS: {
0540: ExportAssets ea = ExportAssets.parse(p);
0541: if (ea != null)
0542: tFrame.addElement(ea);
0543: break;
0544: }
0545: case Tag.PATHSAREPOSTSCRIPT:
0546: break;
0547: case Tag.IMPORTASSETS:
0548: tFrame.addElement(ImportAssets.parse(p));
0549: break;
0550: /* Unknown tags parsed here */
0551: default:
0552: Log.logRB(Resource.UNKNOWNTAG, new Object[] { Util
0553: .b2h(code) });
0554: tFrame.addElement(p.newUnknownTag());
0555: break;
0556: }
0557: p.skipLastTag();
0558: }
0559: }
0560:
0561: /**
0562: * Returns background color of this script
0563: *
0564: * @return background color of this script
0565: */
0566: public SetBackgroundColor getBackgroundColor() {
0567: return bkgColor;
0568: }
0569:
0570: /**
0571: * Sets new background color
0572: *
0573: * @param bkgColor new color
0574: */
0575: public void setBackgroundColor(SetBackgroundColor bkgColor) {
0576: this .bkgColor = bkgColor;
0577: }
0578:
0579: protected void addGlobalCommand(GenericCommand cmd) {
0580: if (gl_commands == null)
0581: gl_commands = new IVVector();
0582: gl_commands.addElement(cmd);
0583: }
0584:
0585: /**
0586: * Removes global commands from this script which have to appear
0587: * only once in a file. For example: MovieSetCommand
0588: * <p>
0589: * Usually all templates loaded into another one need to have these global
0590: * commands stripped out
0591: */
0592: public void removeFileDepGlobalCommands() {
0593: if (gl_commands == null)
0594: return;
0595: for (int i = 0; i < gl_commands.size(); i++) {
0596: GenericCommand cmd = (GenericCommand) gl_commands
0597: .elementAt(i);
0598: if (cmd instanceof MovieSetCommand /*||*/) {
0599: gl_commands.removeElementAt(i);
0600: i--;
0601: }
0602: }
0603: }
0604:
0605: /**
0606: * Finds nested script's instance by name
0607: * <P>
0608: * Searches instance of a script in this script
0609: * and in all nested scripts by instance's name.
0610: *
0611: * @param name name of the instance to be searched
0612: * @return found Script or null
0613: */
0614: public Instance findInstance(String name) {
0615: for (int i = 0; i < timeline.getFrameCount(); i++) {
0616: Frame frame = timeline.getFrameAt(i);
0617: for (int j = 0; j < frame.size(); j++) {
0618: FlashObject fo = frame.getFlashObjectAt(j);
0619: if (!(fo instanceof Instance))
0620: continue;
0621: Instance inst = (Instance) fo;
0622: if (inst.name != null && inst.name.equals(name))
0623: return inst;
0624: if (inst.isScript()) {
0625: inst = inst.getScript().findInstance(name);
0626: if (inst != null)
0627: return inst;
0628: }
0629: }
0630: }
0631: return null;
0632: }
0633:
0634: /**
0635: * Processes this script in the specified context and flash file
0636: *
0637: * @param file file to be used when processing this script
0638: * @param context context to be used when processing this script
0639: * @exception IVException
0640: */
0641: public void process(FlashFile file, Context context)
0642: throws IVException {
0643: if (isProcessed())
0644: return;
0645:
0646: FakeContext fakeContext = null;
0647: Context localContext = null;
0648:
0649: // execute global commands (if any)
0650: if (gl_commands != null) {
0651: fakeContext = new FakeContext(context);
0652: for (int i = 0; i < gl_commands.size(); i++) {
0653: GenericCommand cmd = (GenericCommand) gl_commands
0654: .elementAt(i);
0655: try {
0656: cmd.doCommand(file,
0657: localContext != null ? localContext
0658: : fakeContext, this , 0);
0659: localContext = fakeContext.getContext();
0660: } catch (Exception e) {
0661: Log.logRB(Resource.ERRDOCMD, new Object[] {
0662: file.getFullName(), getName(), "0",
0663: cmd.getCommandName() }, e);
0664: }
0665: }
0666: }
0667:
0668: if (localContext == null) {
0669: localContext = new StandardContext();
0670: }
0671: localContext.setParent(context);
0672:
0673: // perform generator commands
0674: timeline.doCommand(file, localContext, this );
0675:
0676: // process inner scripts
0677: timeline.process(file, localContext);
0678:
0679: // apply context to the rest
0680: apply(localContext);
0681: }
0682:
0683: /**
0684: * Returns true if this script was already processed
0685: *
0686: * @return true if this script was already processed
0687: */
0688: public boolean isProcessed() {
0689: return isProcessed;
0690: }
0691:
0692: /**
0693: * Marks this script as processed
0694: */
0695: public void setProcessed() {
0696: isProcessed = true;
0697: }
0698:
0699: /**
0700: * Creates a copy of this script
0701: *
0702: * @return copy of this script
0703: */
0704: public Script copyScript() {
0705: return (Script) getCopy(new ScriptCopier());
0706: }
0707:
0708: /**
0709: * Appends specified script to the end of this script
0710: * <p>
0711: * Removes all hanging instances of this script, i.e. for every instance
0712: * which is still on the timeline by the of this script put RemoveObject
0713: * tag in the last frame of the timeline. Then adds all frames of the specified
0714: * script to the end of this one.
0715: *
0716: * @param sc script to be appended
0717: */
0718: public void appendScript(Script sc) {
0719: Timeline scTm = sc.getTimeline();
0720:
0721: Frame lastFrame = newFrame();
0722: removeAllInstances(lastFrame);
0723: Frame firstFrame = scTm.getFrameAt(0);
0724: lastFrame.setName(firstFrame.getName());
0725: lastFrame.append(firstFrame);
0726:
0727: for (int i = 1; i < scTm.getFrameCount(); i++) {
0728: Frame frame = scTm.getFrameAt(i);
0729: timeline.addFrame(frame);
0730: }
0731: }
0732:
0733: /**
0734: * Removes all hanging instances of the timeline in the specified frame
0735: * <p>
0736: * Traverses this script and for every hanging instance (instance which is
0737: * still on the timeline by the last frame) puts RemoveObject tag in the specified frame.
0738: *
0739: * @param lastFrame frame to put RemoveObject tags in
0740: */
0741: public void removeAllInstances(Frame lastFrame) {
0742: IVVector layers = getOccupiedLayers();
0743: for (int i = 0; i < layers.size(); i++) {
0744: Instance inst = (Instance) layers.elementAt(i);
0745: if (inst == null)
0746: continue;
0747: lastFrame.removeInstance(i);
0748: }
0749: }
0750:
0751: /**
0752: * Calculates bounds of this script
0753: * <p>
0754: * Takes masks into account too
0755: *
0756: * @return bounds of this script
0757: */
0758: public Rectangle2D getBounds() {
0759: Rectangle2D rect = null;
0760: IVVector layers = new IVVector();
0761: int[] masks = null;
0762: for (int i = 0; i < timeline.getFrameCount(); i++) {
0763: Frame frame = timeline.getFrameAt(i);
0764: for (int k = 0; k < frame.size(); k++) {
0765: FlashObject fo = frame.getFlashObjectAt(k);
0766: if (fo instanceof Instance) {
0767: Instance inst = (Instance) fo;
0768: int layer = inst.depth;
0769: Rectangle2D bounds = null;
0770: FlashDef def = inst.def;
0771: if (def != null) {
0772: bounds = def.getBounds();
0773: layers.setElementAt(bounds, layer);
0774: } else if (inst.matrix != null) {
0775: bounds = (Rectangle2D) layers.elementAt(layer);
0776: }
0777:
0778: // check for mask
0779: if (masks != null && masks.length > layer
0780: && masks[layer] != 0)
0781: continue;
0782:
0783: if (inst.matrix != null && bounds != null) {
0784: bounds = GeomHelper.calcBounds(inst.matrix,
0785: bounds);
0786: }
0787: rect = GeomHelper.add(rect, bounds);
0788:
0789: if (inst.clip > 0) {
0790: int clip = inst.clip;
0791: if (masks == null) {
0792: masks = new int[clip + 10];
0793: } else if (masks.length <= clip) {
0794: int[] masks1 = new int[clip + 10];
0795: System.arraycopy(masks, 0, masks1, 0,
0796: masks.length);
0797: masks = masks1;
0798: }
0799: masks[layer] = clip;
0800: for (int m = layer + 1; m <= clip; m++) {
0801: masks[m] = -1;
0802: }
0803: }
0804: } else if (fo instanceof RemoveObject) {
0805: RemoveObject ro = (RemoveObject) fo;
0806: int layer = ro.depth;
0807: if (masks != null && masks.length > layer
0808: && masks[layer] > 0) {
0809: // mask is to be removed, clear it
0810: int clip = masks[layer];
0811: for (int m = layer; m <= clip; m++) {
0812: masks[m] = 0;
0813: }
0814: }
0815: layers.setElementAt(null, layer);
0816: } else {
0817: rect = GeomHelper.add(rect, fo.getBounds());
0818: }
0819: }
0820: }
0821: if (rect == null) {
0822: rect = GeomHelper.newRectangle();
0823: }
0824: return rect;
0825: }
0826:
0827: /**
0828: * Reserves specified number of layers beginning from the specified one
0829: * <p>
0830: * Traverses the timeline and moves all the instances which are on layers
0831: * greater than the specified one by the specified number.
0832: * <P>
0833: * For example if we have instances A, B, C, D on layers 2,3,4,5,
0834: * then if we call reserveLayers(3,2) we will have:
0835: * A on 2, B on 5, C on 6, D on 7, so there will be two available layers depths: 3 and 4.
0836: *
0837: * @param from reserve layers beginning from this one
0838: * @param num number of layers to be reserved
0839: * @return 'from' layer
0840: */
0841: public int reserveLayers(int from, int num) {
0842: int cnt = timeline.getFrameCount();
0843: for (int i = 0; i < cnt; i++) {
0844: Frame frame = timeline.getFrameAt(i);
0845: int fsz = frame.size();
0846: for (int k = 0; k < fsz; k++) {
0847: FlashObject fo = frame.getFlashObjectAt(k);
0848: if (fo instanceof Instance) {
0849: Instance inst = (Instance) fo;
0850: if (inst.depth >= from)
0851: inst.depth += num;
0852: if (inst.clip >= from)
0853: inst.clip += num;
0854: } else if (fo instanceof RemoveObject) {
0855: RemoveObject ro = (RemoveObject) fo;
0856: if (ro.depth >= from)
0857: ro.depth += num;
0858: }
0859: }
0860: }
0861: return from;
0862: }
0863:
0864: /**
0865: * Returns maximum layer's depth of this script
0866: *
0867: * @return maximum depth
0868: */
0869: public int getMaxDepth() {
0870: int max = 0;
0871: for (int i = 0; i < timeline.getFrameCount(); i++) {
0872: Frame frame = timeline.getFrameAt(i);
0873: for (int k = 0; k < frame.size(); k++) {
0874: FlashObject fo = frame.getFlashObjectAt(k);
0875: if (fo instanceof Instance) {
0876: Instance inst = (Instance) fo;
0877: if (inst.depth > max)
0878: max = inst.depth;
0879: if (inst.clip > max)
0880: max = inst.clip;
0881: }
0882: }
0883: }
0884: return max;
0885: }
0886:
0887: /**
0888: * Creates linear interpolation of the two specified matrixes
0889: *
0890: * @param t coefficient of the interpolation [0..1]
0891: * @param startMatrix first matrix
0892: * @param endMatrix second matrix
0893: * @return interpolation of the two specified matrixes
0894: */
0895: public static AffineTransform interLinear(double t,
0896: AffineTransform startMatrix, AffineTransform endMatrix) {
0897: double t1 = 1.0 - t;
0898: double m00 = startMatrix.getScaleX() * t1
0899: + endMatrix.getScaleX() * t;
0900: double m11 = startMatrix.getScaleY() * t1
0901: + endMatrix.getScaleY() * t;
0902: double m01 = startMatrix.getShearX() * t1
0903: + endMatrix.getShearX() * t;
0904: double m10 = startMatrix.getShearY() * t1
0905: + endMatrix.getShearY() * t;
0906: double m02 = startMatrix.getTranslateX() * t1
0907: + endMatrix.getTranslateX() * t;
0908: double m12 = startMatrix.getTranslateY() * t1
0909: + endMatrix.getTranslateY() * t;
0910: return new AffineTransform(m00, m10, m01, m11, m02, m12);
0911: }
0912:
0913: /**
0914: * Adds simple motion and color tweening to this script
0915: *
0916: * @param def symbol to be tweened
0917: * @param layer layer on which to place the symbol and do the tweening
0918: * @param startFrame start frame
0919: * @param startMatrix start transformation matrix
0920: * @param startCXF start color matrix (optional)
0921: * @param endFrame end frame (included)
0922: * @param endMatrix end transformation matrix
0923: * @param endCXF end color matrix (optional)
0924: * @return last frame
0925: */
0926: public Frame addTweening(FlashDef def, int layer, int startFrame,
0927: AffineTransform startMatrix, CXForm startCXF, int endFrame,
0928: AffineTransform endMatrix, CXForm endCXF) {
0929: return addTweening(def, layer, getFrameAt(startFrame), endFrame
0930: - startFrame, startMatrix, startCXF, endMatrix, endCXF);
0931: }
0932:
0933: /**
0934: * Adds simple motion and color tweening to this script
0935: *
0936: * @param def symbol to be tweened
0937: * @param layer layer on which to place the symbol and do the tweening
0938: * @param startFrame start frame
0939: * @param startMatrix
0940: * start transformation matrix
0941: * @param startCXF start color matrix (optional)
0942: * @param endFrame end frame (included)
0943: * @param endMatrix end transformation matrix
0944: * @param endCXF end color matrix (optional)
0945: * @param name instance name
0946: * @return last frame
0947: */
0948: public Frame addTweening(FlashDef def, int layer, int startFrame,
0949: AffineTransform startMatrix, CXForm startCXF, int endFrame,
0950: AffineTransform endMatrix, CXForm endCXF, String name) {
0951: return addTweening(def, layer, getFrameAt(startFrame), endFrame
0952: - startFrame, startMatrix, startCXF, endMatrix, endCXF,
0953: name);
0954: }
0955:
0956: /**
0957: * Adds simple motion and color tweening to this script
0958: *
0959: * @param def symbol to be tweened
0960: * @param layer layer on which to place the symbol and do the tweening
0961: * @param frame first frame to start the tweening
0962: * @param num number of frames for the tweening (in addition to the first frame)
0963: * @param startMatrix start transformation matrix
0964: * @param startCXF start color matrix (optional)
0965: * @param endMatrix end transformation matrix
0966: * @param endCXF end color matrix (optional)
0967: * @return last frame
0968: */
0969: public Frame addTweening(FlashDef def, int layer, Frame frame,
0970: int num, AffineTransform startMatrix, CXForm startCXF,
0971: AffineTransform endMatrix, CXForm endCXF) {
0972: return addTweening(def, layer, frame, num, startMatrix,
0973: startCXF, endMatrix, endCXF, null);
0974: }
0975:
0976: /**
0977: * Adds simple motion and color tweening to this script
0978: *
0979: * @param def symbol to be tweened
0980: * @param layer layer on which to place the symbol and do the tweening
0981: * @param frame first frame to start the tweening
0982: * @param num number of frames for the tweening (in addition to the first frame)
0983: * @param startMatrix start transformation matrix
0984: * @param startCXF start color matrix (optional)
0985: * @param endMatrix end transformation matrix
0986: * @param endCXF end color matrix (optional)
0987: * @param name instance name (optional), is set on first instance
0988: * @return last frame
0989: */
0990: public Frame addTweening(FlashDef def, int layer, Frame frame,
0991: int num, AffineTransform startMatrix, CXForm startCXF,
0992: AffineTransform endMatrix, CXForm endCXF, String name) {
0993: int startFrame = getFrameIndex(frame);
0994: int endFrame = startFrame + num;
0995: frame.addInstance(def, layer, startMatrix, startCXF, name);
0996: for (int i = 1; i < num; i++) {
0997: double t = (double) i / num;
0998: AffineTransform matrix = interLinear(t, startMatrix,
0999: endMatrix);
1000: CXForm cxform = startCXF != null ? CXForm.interLinear(t,
1001: startCXF, endCXF) : null;
1002: frame = getFrameAt(startFrame + i);
1003: frame.addInstance(layer, matrix, cxform);
1004: }
1005: if (startFrame != endFrame) {
1006: frame = getFrameAt(endFrame);
1007: frame.addInstance(layer, endMatrix, endCXF);
1008: }
1009: return frame;
1010: }
1011:
1012: /**
1013: * Fades out everything on the timeline during the specified number of frames
1014: *
1015: * @param num number of frames
1016: */
1017: public void fadeOut(int num) {
1018: IVVector layers = getOccupiedLayers();
1019: int startFrame = getFrameCount() - 1;
1020: ;
1021: CXForm startcx = CXForm.newAlpha(256);
1022: CXForm endcx = CXForm.newAlpha(0);
1023: for (int i = 0; i < layers.size(); i++) {
1024: Instance inst = (Instance) layers.elementAt(i);
1025: if (inst == null)
1026: continue;
1027: for (int j = 1; j <= num; j++) {
1028: double t = (double) j / num;
1029: CXForm cxform = CXForm.interLinear(t, startcx, endcx);
1030: Frame frame = getFrameAt(startFrame + j);
1031: frame.addInstance(i, null, cxform);
1032: }
1033: }
1034: }
1035:
1036: /**
1037: * Returns vector of all occupied layers by the end of this script
1038: *
1039: * @return vector which contains Instances at 'layer' indexes
1040: */
1041: public IVVector getOccupiedLayers() {
1042: IVVector layers = new IVVector();
1043: for (int i = 0; i < timeline.getFrameCount(); i++) {
1044: Frame frame = timeline.getFrameAt(i);
1045: for (int k = 0; k < frame.size(); k++) {
1046: FlashObject fo = frame.getFlashObjectAt(k);
1047: if (fo instanceof Instance) {
1048: Instance inst = (Instance) fo;
1049: int layer = inst.depth;
1050: layers.setElementAt(inst, layer);
1051: } else if (fo instanceof RemoveObject) {
1052: RemoveObject ro = (RemoveObject) fo;
1053: int layer = ro.depth;
1054: layers.setElementAt(null, layer);
1055: }
1056: }
1057: }
1058: return layers;
1059: }
1060:
1061: /**
1062: * Returns number of frames in this script
1063: *
1064: * @return number of frames
1065: */
1066: public int getFrameCount() {
1067: return timeline.getFrameCount();
1068: }
1069:
1070: /**
1071: * Returns frame at specified index
1072: * <p>
1073: * If frame does not exist, creates it at specified index and fills everything
1074: * in between with empty frames
1075: *
1076: * @param frameNum frame number
1077: * @return frame at specified index
1078: */
1079: public Frame getFrameAt(int frameNum) {
1080: if (timeline.getFrameCount() <= frameNum) {
1081: for (int i = timeline.getFrameCount(); i <= frameNum; i++)
1082: newFrame();
1083: }
1084: return timeline.getFrameAt(frameNum);
1085: }
1086:
1087: /**
1088: * Returns index of specified frame in the timeline or -1
1089: *
1090: * @param frame specified frame
1091: * @return index of specified frame in the timeline or -1
1092: */
1093: public int getFrameIndex(Frame frame) {
1094: return timeline.getFrameIndex(frame);
1095: }
1096:
1097: /**
1098: * Creates new frame and adds it to the end of the timeline
1099: *
1100: * @return new added frame
1101: */
1102: public Frame newFrame() {
1103: return timeline.newFrame();
1104: }
1105:
1106: /**
1107: * Returns last frame of this script
1108: *
1109: * @return last frame of this script
1110: */
1111: public Frame getLastFrame() {
1112: return timeline.getFrameAt(getFrameCount() - 1);
1113: }
1114:
1115: public void apply(Context context) {
1116: if (isConstant() || isProcessed())
1117: return;
1118: timeline.apply(context);
1119: }
1120:
1121: public boolean isConstant() {
1122: return timeline.isConstant();
1123: }
1124:
1125: protected FlashItem copyInto(FlashItem item, ScriptCopier copier) {
1126: super .copyInto(item, copier);
1127: ((Script) item).zero_frame = zero_frame != null ? zero_frame
1128: .getCopy(copier) : null;
1129: ((Script) item).timeline = (Timeline) timeline.getCopy(copier);
1130: ((Script) item).isProcessed = isProcessed;
1131: if (gl_commands != null) {
1132: IVVector v = new IVVector(gl_commands.size());
1133: for (int i = 0; i < gl_commands.size(); i++) {
1134: GenericCommand cmd = (GenericCommand) gl_commands
1135: .elementAt(i);
1136: v.addElement(cmd.getCopy(null));
1137: }
1138: ((Script) item).gl_commands = v;
1139: }
1140: ((Script) item).bkgColor = bkgColor != null ? (SetBackgroundColor) bkgColor
1141: .getCopy(copier)
1142: : null;
1143: return item;
1144: }
1145:
1146: public FlashItem getCopy(ScriptCopier copier) {
1147: return copyInto(new Script(), copier);
1148: }
1149:
1150: public void collectDeps(DepsCollector dc) {
1151: //System.out.println( "Script.collectDeps" );
1152: timeline.collectDeps(dc);
1153: }
1154:
1155: public void collectFonts(FontsCollector fc) {
1156: for (int i = 0; i < timeline.getFrameCount(); i++) {
1157: Frame frame = timeline.getFrameAt(i);
1158: for (int k = 0; k < frame.size(); k++) {
1159: FlashObject fo = frame.getFlashObjectAt(k);
1160: fo.collectFonts(fc);
1161: }
1162: }
1163: }
1164:
1165: public int getTag() {
1166: return Tag.DEFINESPRITE;
1167: }
1168:
1169: public void printContent(PrintStream out, String indent) {
1170: String id = isMain() ? "main" : Integer.toString(getID());
1171: out.println(indent + "Script: id=" + id + " frames="
1172: + timeline.getFrameCount() + " name='" + getName()
1173: + "'");
1174: if (zero_frame != null)
1175: zero_frame.printContent(out, indent + " ");
1176: timeline.printContent(out, indent + " ");
1177: out.println(indent + "End Script(" + id + ") name='"
1178: + getName() + "'");
1179: }
1180:
1181: /**
1182: * Merges the same fonts into one
1183: *
1184: * @param fonts vector of fonts (FontDefs)
1185: */
1186: protected void mergeFonts(IVVector fonts) {
1187: // vector of Object[] {(String)font_key, (IVVector) text_blocks}
1188: IVVector fonts_blocks = new IVVector();
1189:
1190: // hashtable of FontDef by their font key
1191: Hashtable fonts_map = new Hashtable();
1192:
1193: // iterate all the fonts and merge the same ones
1194: for (int i = 0; i < fonts.size(); i++) {
1195: FontDef fdef = (FontDef) fonts.elementAt(i);
1196: Font font = fdef.getFont();
1197: //System.out.println( "Script.mergeFonts: font="+font.fontKey );
1198:
1199: // check for the same font in font cache, if found and smaller then remove from the cache
1200: Font font2 = FontCache.getFont(font.fontKey);
1201: if (font2 != null && font2 != font) {
1202: if (font.isLargeThan(font2)) {
1203: //System.out.println( "Script.mergeFonts: removing smaller font from cache" );
1204: // remove old font from cache
1205: FontCache.removeFont(font2.fontKey);
1206: // add new one
1207: FontCache.addFont(font.fontKey, font);
1208: }
1209: }
1210:
1211: FontDef prev_fdef = (FontDef) fonts_map.get(font.fontKey);
1212: if (prev_fdef == null) {
1213: //System.out.println( "Script.mergeFonts: there is no previous font" );
1214: fonts_map.put(font.fontKey, fdef);
1215: } else {
1216: Font font_prev = prev_fdef.getFont();
1217: //System.out.println( "Script.mergeFonts: there is previous font and they are "+(font_prev==font?"":"not ")+"equal" );
1218: if (font_prev != font) {
1219: //System.out.println( "Script.mergeFonts: merge fonts" );
1220: Font font3 = FontDef.mergeFonts(font_prev, font);
1221: if (font3 == font) {
1222: //System.out.println( "Script.mergeFonts: font3==font" );
1223: prev_fdef.setFont(font);
1224: fonts_blocks.addElement(new Object[] {
1225: font_prev, prev_fdef.getTextBlocks() });
1226: fonts_map.remove(font.fontKey);
1227: fonts_map.put(font.fontKey, fdef);
1228: fonts.removeElement(prev_fdef);
1229: } else {
1230: //System.out.println( "Script.mergeFonts: font3==font_prev" );
1231: fdef.setFont(font_prev);
1232: fonts_blocks.addElement(new Object[] { font,
1233: fdef.getTextBlocks() });
1234: fonts.removeElement(fdef);
1235: }
1236: i--;
1237: }
1238: }
1239: }
1240:
1241: for (int i = 0; i < fonts_blocks.size(); i++) {
1242: Object[] objs = (Object[]) fonts_blocks.elementAt(i);
1243: Font old_font = (Font) objs[0];
1244: String fontKey = old_font.fontKey;
1245: IVVector text_blocks = (IVVector) objs[1];
1246: FontDef fontDef = (FontDef) fonts_map.get(fontKey);
1247: fontDef.addTextBlocks(text_blocks);
1248: Font new_font = fontDef.getFont();
1249: //System.out.println( "updating textblocks for font "+fontKey );
1250: for (int j = 0; j < text_blocks.size(); j++) {
1251: TextBlock tblock = (TextBlock) text_blocks.elementAt(j);
1252: tblock.layout();
1253: tblock.changeFont(old_font, new_font);
1254: }
1255: }
1256: }
1257:
1258: /**
1259: * zero_frame IVVector accessor
1260: *
1261: * @return IVVector
1262: */
1263: public IVVector getZero_frame() {
1264: return zero_frame;
1265: }
1266:
1267: }
|