/* * JSwiff is an open source Java API for Macromedia Flash file generation * and manipulation * * Copyright (C) 2004-2005 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.EdgeRecord; import com.jswiff.swfrecords.MorphFillStyles; import com.jswiff.swfrecords.MorphLineStyles; import com.jswiff.swfrecords.Rect; import com.jswiff.swfrecords.Shape; import com.jswiff.swfrecords.ShapeRecord; import java.io.IOException; /** * <p> * This tag is used to define the start and end states of a morph sequence. * After definition, a snapshot of the sequence can be displayed with the * <code>PlaceObject2</code> tag. Use several <code>PlaceObject2</code> tags * with <code>ratio</code> value increasing from 0 to 65535 to achieve a * smooth rendering of the morph. This is all you have to do to define and * render a morph. Flash Player is resposible for generating intermediary * states through interpolation. * </p> * * <p> * Shapes belonging to a morph sequence are defined within a single * <code>DefineMorphShape</code> tag and are independent of previously defined * shapes. Accordingly, character definitions preceding this tag cannot be * used. * </p> * * @see PlaceObject2 * @since SWF 3 */ public final class DefineMorphShape extends DefinitionTag { private Rect startBounds; private Rect endBounds; private MorphFillStyles morphFillStyles; private MorphLineStyles morphLineStyles; private Shape startShape; private Shape endShape; /** * <p> * Creates a new DefineMorphShape tag. Supply the character ID of the morph * sequence, bounding boxes for the shapes at start and end of morph, and * morph fill and line styles. Finally, provide the start and the end * shape. * </p> * * <p> * The shapes must have identical structures, i.e. a style change record in * the start shape must have a corresponding style change record in the * end shape. Edge records in the start shape must have matching edge * records in the end shape. The edge record type does not matter, since * straight edge records can be regarded as special cases of curved edge * records. * </p> * * @param characterId character ID if morph sequence * @param startBounds bounding box at morph start * @param endBounds bounding box at morph end * @param morphFillStyles array of fill styles used in morph sequence * @param morphLineStyles array of line styles used in morph sequence * @param startShape start shape * @param endShape end shape * * @throws IllegalArgumentException if start and end shapes are differently * structured */ public DefineMorphShape( int characterId, Rect startBounds, Rect endBounds, MorphFillStyles morphFillStyles, MorphLineStyles morphLineStyles, Shape startShape, Shape endShape) throws IllegalArgumentException { code = TagConstants.DEFINE_MORPH_SHAPE; this.characterId = characterId; this.startBounds = startBounds; this.endBounds = endBounds; this.morphFillStyles = morphFillStyles; this.morphLineStyles = morphLineStyles; checkEdges(startShape, endShape); this.startShape = startShape; this.endShape = endShape; } DefineMorphShape() { // empty } /** * Sets the bounding box of the end shape. * * @param endBounds end shape bounds */ public void setEndBounds(Rect endBounds) { this.endBounds = endBounds; } /** * Returns the bounding box of the end shape. * * @return end shape bounds */ public Rect getEndBounds() { return endBounds; } /** * Sets the shape displayed in the final state of the morph sequence. * * @param endShape end shape */ public void setEndShape(Shape endShape) { this.endShape = endShape; } /** * Returns the shape displayed in the final state of the morph sequence. * * @return end shape */ public Shape getEndShape() { return endShape; } /** * Sets the fill styles of the morph sequence. * * @param morphFillStyles morph fill styles */ public void setMorphFillStyles(MorphFillStyles morphFillStyles) { this.morphFillStyles = morphFillStyles; } /** * Returns the fill styles of the morph sequence. * * @return morph fill styles */ public MorphFillStyles getMorphFillStyles() { return morphFillStyles; } /** * Sets the line styles of the morph sequence. * * @param morphLineStyles morph line styles */ public void setMorphLineStyles(MorphLineStyles morphLineStyles) { this.morphLineStyles = morphLineStyles; } /** * Returns the line styles of the morph sequence. * * @return morph line styles */ public MorphLineStyles getMorphLineStyles() { return morphLineStyles; } /** * Sets the bounding box of the start shape. * * @param startBounds start shape bounds */ public void setStartBounds(Rect startBounds) { this.startBounds = startBounds; } /** * Returns the bounding box of the start shape. * * @return start shape bounds */ public Rect getStartBounds() { return startBounds; } /** * Sets the shape displayed in the initial state of the morph sequence. * * @param startShape start shape */ public void setStartShape(Shape startShape) { this.startShape = startShape; } /** * Returns the shape displayed in the initial state of the morph sequence. * * @return start shape */ public Shape getStartShape() { return startShape; } protected void writeData(OutputBitStream outStream) throws IOException { outStream.writeUI16(characterId); startBounds.write(outStream); endBounds.write(outStream); if ( (startShape == null) && (endShape == null) && (morphFillStyles == null) && (morphLineStyles == null)) { // zero offset "feature" outStream.writeUI32(0); // zero offset outStream.writeUI16(0); // two zeroes for empty styles outStream.writeUI32(0); // four zeroes for empty shapes return; } OutputBitStream bitStream = new OutputBitStream(); morphFillStyles.write(bitStream); morphLineStyles.write(bitStream); startShape.write(bitStream); byte[] bitStreamData = bitStream.getData(); outStream.writeUI32(bitStreamData.length); // offset to endShape outStream.writeBytes(bitStreamData); endShape.write(outStream); } void setData(byte[] data) throws IOException { InputBitStream inStream = new InputBitStream(data); characterId = inStream.readUI16(); startBounds = new Rect(inStream); endBounds = new Rect(inStream); long endEdgesOffset = inStream.readUI32(); if (endEdgesOffset == 0) { // the Flash authoring tool sometimes generates such morphs return; } endEdgesOffset += inStream.getOffset(); morphFillStyles = new MorphFillStyles(inStream); morphLineStyles = new MorphLineStyles(inStream, false); long startEdgesOffset = inStream.getOffset(); byte[] startEdgesBuffer = new byte[(int) (endEdgesOffset - startEdgesOffset)]; System.arraycopy( data, (int) startEdgesOffset, startEdgesBuffer, 0, startEdgesBuffer.length); startShape = new Shape(new InputBitStream(startEdgesBuffer)); byte[] endEdgesBuffer = new byte[(int) (data.length - endEdgesOffset)]; System.arraycopy( data, (int) endEdgesOffset, endEdgesBuffer, 0, endEdgesBuffer.length); endShape = new Shape(new InputBitStream(endEdgesBuffer)); } private void checkEdges(Shape edges1, Shape edges2) { if ((edges1 == null) || (edges2 == null)) { return; // zero offset bug } ShapeRecord[] startShapeRecs = edges1.getShapeRecords(); ShapeRecord[] endShapeRecs = edges1.getShapeRecords(); if (startShapeRecs.length != endShapeRecs.length) { throw new IllegalArgumentException( "Start and end shapes must have the same number of shape records!"); } for (int i = 0; i < startShapeRecs.length; i++) { ShapeRecord startRec = startShapeRecs[i]; ShapeRecord endRec = endShapeRecs[i]; if (startRec instanceof EdgeRecord) { if (endRec instanceof EdgeRecord) { continue; } throw new IllegalArgumentException( "Edge record in start shape must have corresponding record in end shape!"); } if (!(endRec instanceof EdgeRecord)) { continue; } throw new IllegalArgumentException( "Style change record in start shape must have corresponding record in end shape!"); } } }