/*
* JSwiff is an open source Java API for Macromedia Flash file generation
* and manipulation
*
* Copyright (C) 2004-2006 Ralf Terdic (contact@jswiff.com)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.jswiff.swfrecords.tags;
import com.jswiff.io.InputBitStream;
import com.jswiff.io.OutputBitStream;
import com.jswiff.swfrecords.CXformWithAlpha;
import com.jswiff.swfrecords.ClipActions;
import com.jswiff.swfrecords.Matrix;
import java.io.IOException;
/**
* <p>
* With this tag, an instance of a character can be added to the display list.
* Besides, unlike <code>PlaceObject</code>, it can modify the attributes of a
* character that is already on the display list.
* </p>
*
* <p>
* The only mandatory attribute is the character depth. A character that is
* already on the display list can be identified by its depth alone, as there
* can be only one character at a given depth. If a new character is added to
* the display list, the character ID is needed.
* </p>
*
* <p>
* If the <code>move</code> flag is set, the character at the given depth is
* removed. If no character ID is set, the removed character is redisplayed at
* the same depth with new attributes. If the character ID is set, the
* corresponding character replaces the removed one.
* </p>
*
* <p>
* A transform matrix and a color tranform can be specified in order to
* determine the position, rotation, scale and color of the character to be
* displayed.
* </p>
*
* <p>
* The morph ratio applies to characters defined with
* <code>DefineMorphShape</code> and specifies how far the morph has
* progressed.
* </p>
*
* <p>
* A (non-zero) clip depth indicates that the character is a clipping character
* which masks depths up to and including the specified value (e.g. if a
* character was placed at depth 1 with a clip depth of 4, all depths above 1,
* up to and including depth 4, will be masked by the shape placed at depth 1;
* characters placed at depths above 4 will not be masked).
* </p>
*
* <p>
* The character instance can be given a name which it can later be referenced
* by (e.g. within <code>With</code>).
* </p>
*
* <p>
* Finally, if the character to be placed is a sprite, one or more event
* handlers (clip actions) can be defined.
* </p>
*
* @see PlaceObject
* @see DefineMorphShape
* @since SWF 3
*/
public final class PlaceObject2 extends Tag {
private boolean move;
private int depth;
private int characterId;
private Matrix matrix;
private CXformWithAlpha colorTransform;
private int ratio;
private String name;
private int clipDepth;
private ClipActions clipActions;
private boolean hasClipActions;
private boolean hasClipDepth;
private boolean hasName;
private boolean hasRatio;
private boolean hasColorTransform;
private boolean hasMatrix;
private boolean hasCharacter;
/**
* Creates a new PlaceObject2 tag.
*
* @param depth depth the character is placed at
*/
public PlaceObject2(int depth) {
code = TagConstants.PLACE_OBJECT_2;
this.depth = depth;
}
PlaceObject2() {
// empty
}
/**
* Sets the character ID. If this ID is set, the corresponding character is
* displayed at the depth specified with the constructor.
*
* @param characterId The characterId to set.
*/
public void setCharacterId(int characterId) {
this.characterId = characterId;
hasCharacter = true;
}
/**
* Returns the character ID. If this ID is set, the corresponding character
* is displayed at the depth specified with the constructor. Check with
* <code>hasCharacter()</code> if set.
*
* @return Returns the characterId.
*/
public int getCharacterId() {
return characterId;
}
/**
* Sets the event handlers (only for sprite characters).
*
* @param clipActions event handlers
*/
public void setClipActions(ClipActions clipActions) {
this.clipActions = clipActions;
hasClipActions = (clipActions != null);
}
/**
* Returns the event handlers (only for sprite characters). Check with
* <code>hasClipActions()</code> if set.
*
* @return Returns the clipActions.
*/
public ClipActions getClipActions() {
return clipActions;
}
/**
* Sets the clip depth, indicating that the character is a clipping character
* which masks characters at depths up to and including the specified clip
* depth
*
* @param clipDepth the clip depth
*/
public void setClipDepth(int clipDepth) {
this.clipDepth = clipDepth;
hasClipDepth = true;
}
/**
* Returs the clip depth (which indicates that the character is a clipping
* character masking characters at depths up to and including the specified
* clip depth). Check with <code>hasclipDepth()</code> if set.
*
* @return clip depth of character
*/
public int getClipDepth() {
return clipDepth;
}
/**
* Sets the color transform, allowing color effects to be applied to the
* character to be displayed.
*
* @param colorTransform a color transform
*/
public void setColorTransform(CXformWithAlpha colorTransform) {
this.colorTransform = colorTransform;
hasColorTransform = (colorTransform != null);
}
/**
* Returns the color transform which allows color effects to be applied to
* the character to be displayed. Check with
* <code>hasColorTransform()</code> if set.
*
* @return Returns the colorTransform.
*/
public CXformWithAlpha getColorTransform() {
return colorTransform;
}
/**
* Sets the depth the character will be displayed at.
*
* @param depth display depth
*/
public void setDepth(int depth) {
this.depth = depth;
}
/**
* Returns the depth the character will be displayed at.
*
* @return display depth
*/
public int getDepth() {
return depth;
}
/**
* Sets the tranform matrix, specifying position, scale, rotation etc. of the
* character to be displayed.
*
* @param matrix transform matrix
*/
public void setMatrix(Matrix matrix) {
this.matrix = matrix;
hasMatrix = (matrix != null);
}
/**
* Returns the tranform matrix, which specifies position, scale, rotation
* etc. of the character to be displayed. Check with
* <code>hasMatrix()</code> if set.
*
* @return transform matrix
*/
public Matrix getMatrix() {
return matrix;
}
/**
* <p>
* Sets the move flag. If set, the character at the given depth is removed.
* It is replaced either by a modified instance of the removed character or
* (if a character ID is specified) by a new character.
* </p>
*
* <p>
* Mainly used for moving a character: first frame specifies depth, character
* ID and initial matrix. Subsequent frames have the move flag set for this
* depth and replace the translate values of the matrix with new ones.
* </p>
*/
public void setMove() {
move = true;
}
/**
* Sets or clears the move flag.
*
* @param move value of move flag
*
* @see PlaceObject2#setMove()
*/
public void setMove(boolean move) {
this.move = move;
}
/**
* Checks the move flag. If set, the character at the given depth is removed.
* It is replaced either by a modified instance of the removed character or
* (if a character ID is specified) by a new character.
*
* @return <code>true</code> if move flag set, else <code>false</code>
*/
public boolean isMove() {
return move;
}
/**
* Assigns a name to the instance of the character to be placed, in order to
* be able to reference this instance by the assigend name (e.g. within
* <code>With</code>).
*
* @param name instance name
*/
public void setName(String name) {
this.name = name;
hasName = (name != null);
}
/**
* Returns the name assigned to the instance of the character to be placed,
* in order to be able to reference this instance by the assigend name (e.g.
* within <code>With</code>). Check with <code>hasName()</code> if set.
*
* @return instance name
*/
public String getName() {
return name;
}
/**
* Sets the morph ratio which indicates how far the morph has progressed
* (only for characters defined with <code>DefineMorphShape</code>). Values
* between 0 and 65535 are permitted. A ratio of 0 displays the character at
* morph start, 65535 causes the character to be displayed at the end of the
* morph. Values between 0 and 65535 cause the Flash Player to interpolate
* between start and end shapes.
*
* @param ratio morph ratio
*/
public void setRatio(int ratio) {
if (ratio < 0) {
this.ratio = 0;
} else if (ratio > 65535) {
this.ratio = 65535;
} else {
this.ratio = ratio;
}
hasRatio = true;
}
/**
* <p>
* Sets the morph ratio which indicates how far the morph has progressed
* (only for characters defined with <code>DefineMorphShape</code>). Values
* between 0 and 65535 are returned. A ratio of 0 displays the character at
* morph start, 65535 causes the character to be displayed at the end of the
* morph. Values between 0 and 65535 cause the Flash Player to interpolate
* between start and end shapes.
* </p>
*
* <p>
* Check with <code>hasRatio()</code> if set.
* </p>
*
* @return Returns the ratio.
*/
public int getRatio() {
return ratio;
}
/**
* Checks whether the character ID is set.
*
* @return <code>true</code> if character ID set, else <code>false</code>
*/
public boolean hasCharacter() {
return hasCharacter;
}
/**
* Checks whether clip actions (sprite event handlers) have been specified.
*
* @return <code>true</code> if clip actions set, else <code>false</code>
*/
public boolean hasClipActions() {
return hasClipActions;
}
/**
* Checks if the value of the clip depth was set.
*
* @return <code>true</code> if clip depth set, else <code>false</code>
*/
public boolean hasClipDepth() {
return hasClipDepth;
}
/**
* Checks whether the color transform is specified.
*
* @return <code>true</code> if color transform set, else <code>false</code>
*/
public boolean hasColorTransform() {
return hasColorTransform;
}
/**
* Checks if a transform matrix is specified.
*
* @return <code>true</code> if transform matrix set, else <code>false</code>
*/
public boolean hasMatrix() {
return hasMatrix;
}
/**
* Checks if a name is assigned to the character instance to be displayed.
*
* @return <code>true</code> if character instance name set, else
* <code>false</code>
*/
public boolean hasName() {
return hasName;
}
/**
* Checks if the morph ratio is set.
*
* @return <code>true</code> if ratio set, else <code>false</code>
*/
public boolean hasRatio() {
return hasRatio;
}
protected void writeData(OutputBitStream outStream) throws IOException {
outStream.writeBooleanBit(hasClipActions);
outStream.writeBooleanBit(hasClipDepth);
outStream.writeBooleanBit(hasName);
outStream.writeBooleanBit(hasRatio);
outStream.writeBooleanBit(hasColorTransform);
outStream.writeBooleanBit(hasMatrix);
outStream.writeBooleanBit(hasCharacter);
outStream.writeBooleanBit(move);
outStream.writeUI16(depth);
if (hasCharacter) {
outStream.writeUI16(characterId);
}
if (hasMatrix) {
matrix.write(outStream);
}
if (hasColorTransform) {
colorTransform.write(outStream);
}
if (hasRatio) {
outStream.writeUI16(ratio);
}
if (hasName) {
outStream.writeString(name);
}
if (hasClipDepth) {
outStream.writeUI16(clipDepth);
}
if (hasClipActions) {
clipActions.write(outStream, getSWFVersion());
}
}
void setData(byte[] data) throws IOException {
InputBitStream inStream = new InputBitStream(data);
if (getSWFVersion() < 6) {
if (isJapanese()) {
inStream.setShiftJIS(true);
} else {
inStream.setANSI(true);
}
}
hasClipActions = inStream.readBooleanBit();
hasClipDepth = inStream.readBooleanBit();
hasName = inStream.readBooleanBit();
hasRatio = inStream.readBooleanBit();
hasColorTransform = inStream.readBooleanBit();
hasMatrix = inStream.readBooleanBit();
hasCharacter = inStream.readBooleanBit();
move = inStream.readBooleanBit();
depth = inStream.readUI16();
if (hasCharacter) {
characterId = inStream.readUI16();
}
if (hasMatrix) {
matrix = new Matrix(inStream);
}
if (hasColorTransform) {
colorTransform = new CXformWithAlpha(inStream);
}
if (hasRatio) {
ratio = inStream.readUI16();
}
if (hasName) {
name = inStream.readString();
}
if (hasClipDepth) {
clipDepth = inStream.readUI16();
}
if ((getSWFVersion() >= 5) && hasClipActions) {
clipActions = new ClipActions(inStream, getSWFVersion());
}
}
}