/* * 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.EdgeRecord; import com.jswiff.swfrecords.LineStyle2; import com.jswiff.swfrecords.MorphFillStyles; import com.jswiff.swfrecords.MorphLineStyle2; import com.jswiff.swfrecords.MorphLineStyles; import com.jswiff.swfrecords.Rect; import com.jswiff.swfrecords.Shape; import com.jswiff.swfrecords.ShapeRecord; import java.io.IOException; /** * DOCUMENT ME! * * @since SWF 8 */ public final class DefineMorphShape2 extends DefinitionTag { private Rect startShapeBounds; private Rect endShapeBounds; private Rect startEdgeBounds; private Rect endEdgeBounds; private MorphFillStyles morphFillStyles; private MorphLineStyles morphLineStyles; private Shape startShape; private Shape endShape; private boolean hasNonscalingStrokes; private boolean hasScalingStrokes; /** * Creates a new DefineMorphShape2 instance. * * @param characterId TODO: Comments * @param startShapeBounds TODO: Comments * @param endShapeBounds TODO: Comments * @param startEdgeBounds TODO: Comments * @param endEdgeBounds TODO: Comments * @param morphFillStyles TODO: Comments * @param morphLineStyles TODO: Comments * @param startShape TODO: Comments * @param endShape TODO: Comments * * @throws IllegalArgumentException TODO: Comments */ public DefineMorphShape2( int characterId, Rect startShapeBounds, Rect endShapeBounds, Rect startEdgeBounds, Rect endEdgeBounds, MorphFillStyles morphFillStyles, MorphLineStyles morphLineStyles, Shape startShape, Shape endShape) throws IllegalArgumentException { code = TagConstants.DEFINE_MORPH_SHAPE_2; this.characterId = characterId; this.startShapeBounds = startShapeBounds; this.endShapeBounds = endShapeBounds; this.startEdgeBounds = startEdgeBounds; this.endEdgeBounds = endEdgeBounds; this.morphFillStyles = morphFillStyles; this.morphLineStyles = morphLineStyles; checkEdges(startShape, endShape); this.startShape = startShape; this.endShape = endShape; } DefineMorphShape2() { // empty } /** * TODO: Comments * * @param endEdgeBounds TODO: Comments */ public void setEndEdgeBounds(Rect endEdgeBounds) { this.endEdgeBounds = endEdgeBounds; } /** * TODO: Comments * * @return TODO: Comments */ public Rect getEndEdgeBounds() { return endEdgeBounds; } /** * 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 bounding box of the end shape. * * @param endBounds end shape bounds */ public void setEndShapeBounds(Rect endBounds) { this.endShapeBounds = endBounds; } /** * Returns the bounding box of the end shape. * * @return end shape bounds */ public Rect getEndShapeBounds() { return endShapeBounds; } /** * 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; } /** * TODO: Comments * * @param startEdgeBounds TODO: Comments */ public void setStartEdgeBounds(Rect startEdgeBounds) { this.startEdgeBounds = startEdgeBounds; } /** * TODO: Comments * * @return TODO: Comments */ public Rect getStartEdgeBounds() { return startEdgeBounds; } /** * 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; } /** * Sets the bounding box of the start shape. * * @param startBounds start shape bounds */ public void setStartShapeBounds(Rect startBounds) { this.startShapeBounds = startBounds; } /** * Returns the bounding box of the start shape. * * @return start shape bounds */ public Rect getStartShapeBounds() { return startShapeBounds; } protected void writeData(OutputBitStream outStream) throws IOException { outStream.writeUI16(characterId); startShapeBounds.write(outStream); endShapeBounds.write(outStream); startEdgeBounds.write(outStream); endEdgeBounds.write(outStream); outStream.writeUnsignedBits(0, 6); checkStrokeScaling(); outStream.writeBooleanBit(hasNonscalingStrokes); outStream.writeBooleanBit(hasScalingStrokes); 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(); startShapeBounds = new Rect(inStream); endShapeBounds = new Rect(inStream); startEdgeBounds = new Rect(inStream); endEdgeBounds = new Rect(inStream); inStream.readUI8(); // ignore stroke scaling flags 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, true); 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!"); } } private void checkStrokeScaling() { hasNonscalingStrokes = false; hasScalingStrokes = false; if (morphLineStyles == null) { return; } for (int i = 1; i <= morphLineStyles.getSize(); i++) { MorphLineStyle2 style = (MorphLineStyle2) morphLineStyles.getStyle(i); if (style.getScaleStroke() == LineStyle2.SCALE_NONE) { hasNonscalingStrokes = true; } else { hasScalingStrokes = true; } if (hasNonscalingStrokes && hasScalingStrokes) { break; } } } }