0001: /*
0002: * $Id: Shape.java,v 1.2 2002/02/15 23:44:28 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: package org.openlaszlo.iv.flash.api.shape;
0052:
0053: import java.io.PrintStream;
0054: import java.awt.geom.*;
0055: import java.util.*;
0056:
0057: import org.openlaszlo.iv.flash.parser.*;
0058: import org.openlaszlo.iv.flash.util.*;
0059: import org.openlaszlo.iv.flash.api.*;
0060:
0061: /**
0062: * Shape.
0063: * <P>
0064: * Shapes are defined by a list of edges called a path. A path may be closed - where the start
0065: * and end of the path meet to close the figure, or open - where the path forms an
0066: * open-ended stroke. A path may contain a mixture of straight edges, curved edges, and
0067: * "pen up and move" commands. The latter allows multiple disconnected figures to be
0068: * described by a single shape structure. (see MoveTo flag)
0069: * <P>
0070: * A fill style defines the appearance of an area enclosed by a path. Fill styles supported
0071: * by SWF include a color, a gradient, or a bitmapped image.<BR>
0072: * A line style defines the appearance of the outline of a path. The line style may be a stroke
0073: * of any thickness and color.
0074: * <P>
0075: * SWF format allows each edge to have its own line and fill style.
0076: * This can have unpredictable results when fill styles change in the middle of a path.
0077: * <P>
0078: * Flash also supports two fill styles per edge, one for each side of the edge:
0079: * FillStyle0 and FillStyle1. FillStyle0 should always be used first and then
0080: * FillStyle1 if the shape is filled on both sides of the edge.
0081: * <P>
0082: * A shape is comprised of the following elements:
0083: * <UL>
0084: * <LI>CharacterId - A 16-bit value that uniquely identifies this shape as a
0085: * 'character' in the dictionary. The CharacterId can be referred to in
0086: * control tags such as PlaceObject. Characters can be re-used and combined
0087: * with other characters to make more complex shapes.
0088: * <LI>Bounding box - The rectangle that completely encloses the shape.
0089: * <LI>Fill style array - A list of all the fill styles used in a shape.
0090: * <LI>Line style array - A list of all the line styles used in a shape.
0091: * <LI>Shape-record array - A list of shape-records. Shape-records can define straight
0092: * or curved edges, style changes, or move the drawing position.
0093: * </UL>
0094: * Note: Line and fill styles are defined once only, and may be used (and re-used) by
0095: * any of the edges in the shape.
0096: * <p>
0097: * Note that objects of this class will always be generated as DefineShape3 tags which
0098: * means that all the colors used in the shape (in line and fillstyles) have to be AlphaColors!
0099: *
0100: * @author Dmitry Skavish
0101: */
0102: public final class Shape extends FlashDef /*implements Drawable*/{
0103:
0104: private StyleBlock style_block;
0105: private Rectangle2D bounds; // bounding box of this shape
0106: private int tagcode; // tag of this shape
0107:
0108: /**
0109: * Creates new empty shape (Shape3 - all colors with alpha)
0110: */
0111: public Shape() {
0112: this (Tag.DEFINESHAPE3);
0113: newStyleBlock();
0114: }
0115:
0116: /**
0117: * Returns shape styles
0118: *
0119: * @return object representing array of shape styles
0120: */
0121: public ShapeStyles getShapeStyles() {
0122: return style_block.shapeStyles;
0123: }
0124:
0125: /**
0126: * Returns shape records
0127: *
0128: * @return object representing array of shape records
0129: */
0130: public ShapeRecords getShapeRecords() {
0131: return style_block.shapeRecords;
0132: }
0133:
0134: /**
0135: * Adds specified fill style to the array of shape styles.
0136: * <p>
0137: * Note: colors used in the specified fillstyle have to be AlphaColors
0138: *
0139: * @param fillStyle specified fill style
0140: * @return index of added fill style in the array
0141: */
0142: public int addFillStyle(FillStyle fillStyle) {
0143: return getShapeStyles().addFillStyle(fillStyle);
0144: }
0145:
0146: /**
0147: * Adds specified line style to the array of shape styles
0148: * <p>
0149: * Note: colors used in the specified linestyle have to be AlphaColors
0150: *
0151: * @param lineStyle specified line style
0152: * @return index of added line style in the array
0153: */
0154: public int addLineStyle(LineStyle lineStyle) {
0155: return getShapeStyles().addLineStyle(lineStyle);
0156: }
0157:
0158: /**
0159: * Sets specified fillstyle as current fillstyle0.
0160: * <P>
0161: * If the specified fillstyle is not in the shapestyle array
0162: * then it's added, if it's already there it's reused.
0163: * <p>
0164: * Note: colors used in the specified fillstyle have to be AlphaColors
0165: *
0166: * @param fillStyle specified fillstyle
0167: * @return index of specified fillstyle in the shapestyle array
0168: */
0169: public int setFillStyle0(FillStyle fillStyle) {
0170: int fs = getShapeStyles().getFillStyleIndex(fillStyle);
0171: if (fs == -1)
0172: fs = addFillStyle(fillStyle);
0173: setFillStyle0(fs);
0174: return fs;
0175: }
0176:
0177: /**
0178: * Sets specified fillstyle as current fillstyle1.
0179: * <P>
0180: * If the specified fillstyle is not in the shapestyle array
0181: * then it's added, if it's already there it's reused.
0182: * <p>
0183: * Note: colors used in the specified fillstyle have to be AlphaColors
0184: *
0185: * @param fillStyle specified fillstyle
0186: * @return index of specified fillstyle in the shapestyle array
0187: */
0188: public int setFillStyle1(FillStyle fillStyle) {
0189: int fs = getShapeStyles().getFillStyleIndex(fillStyle);
0190: if (fs == -1)
0191: fs = addFillStyle(fillStyle);
0192: setFillStyle1(fs);
0193: return fs;
0194: }
0195:
0196: /**
0197: * Sets specified linestyle as current linestyle.
0198: * <P>
0199: * If the specified linestyle is not in the shapestyle array
0200: * then it's added, if it's already there it's reused.
0201: * <p>
0202: * Note: color used in the specified linestyle has to be AlphaColor
0203: *
0204: * @param lineStyle specified linestyle
0205: * @return index of specified linestyle in the shapestyle array
0206: */
0207: public int setLineStyle(LineStyle lineStyle) {
0208: int ls = getShapeStyles().getLineStyleIndex(lineStyle);
0209: if (ls == -1)
0210: ls = addLineStyle(lineStyle);
0211: setLineStyle(ls);
0212: return ls;
0213: }
0214:
0215: /**
0216: * Sets current fillstyle0 by its index in shapestyle array.
0217: *
0218: * @param fillStyle index of fillstyle in shapestyle array to be set as current fillstyle0
0219: */
0220: public void setFillStyle0(int fillStyle) {
0221: StyleChangeRecord sc = getStyleChange();
0222: sc.addFlags(StyleChangeRecord.FILLSTYLE0);
0223: sc.setFillStyle0(fillStyle);
0224: }
0225:
0226: /**
0227: * Sets current fillstyle1 by its index in shapestyle array.
0228: *
0229: * @param fillStyle index of fillstyle in shapestyle array to be set as current fillstyle1
0230: */
0231: public void setFillStyle1(int fillStyle) {
0232: StyleChangeRecord sc = getStyleChange();
0233: sc.addFlags(StyleChangeRecord.FILLSTYLE1);
0234: sc.setFillStyle1(fillStyle);
0235: }
0236:
0237: /**
0238: * Sets current linestyle by its index in shapestyle array.
0239: *
0240: * @param lineStyle index of linestyle in shapestyle array to be set as current linestyle
0241: */
0242: public void setLineStyle(int lineStyle) {
0243: StyleChangeRecord sc = getStyleChange();
0244: sc.addFlags(StyleChangeRecord.LINESTYLE);
0245: sc.setLineStyle(lineStyle);
0246: }
0247:
0248: /**
0249: * Returns index of current line style
0250: *
0251: * @return index of currently used line style
0252: */
0253: public int getLineStyleIndex() {
0254: StyleChangeRecord sc = getStyleChange();
0255: return sc.getLineStyle();
0256: }
0257:
0258: /**
0259: * Returns current line style
0260: *
0261: * @return currently used line style
0262: */
0263: public LineStyle getLineStyle() {
0264: int idx = getLineStyleIndex();
0265: return getShapeStyles().getLineStyle(idx);
0266: }
0267:
0268: /**
0269: * Returns index of current fill style 0
0270: *
0271: * @return index of currently used fill style 0
0272: */
0273: public int getFillStyle0Index() {
0274: StyleChangeRecord sc = getStyleChange();
0275: return sc.getFillStyle0();
0276: }
0277:
0278: /**
0279: * Returns current fill style 0
0280: *
0281: * @return currently used fill style 0
0282: */
0283: public FillStyle getFillStyle0() {
0284: int idx = getFillStyle0Index();
0285: return getShapeStyles().getFillStyle(idx);
0286: }
0287:
0288: /**
0289: * Returns index of current fill style 1
0290: *
0291: * @return index of currently used fill style 1
0292: */
0293: public int getFillStyle1Index() {
0294: StyleChangeRecord sc = getStyleChange();
0295: return sc.getFillStyle1();
0296: }
0297:
0298: /**
0299: * Returns current fill style 1
0300: *
0301: * @return currently used fill style 1
0302: */
0303: public FillStyle getFillStyle1() {
0304: int idx = getFillStyle1Index();
0305: return getShapeStyles().getFillStyle(idx);
0306: }
0307:
0308: /**
0309: * Creates new style block
0310: * <P>
0311: * Each style block contains styles and records which use
0312: * these styles. Records cannot use styles from different
0313: * style blocks. It means that after this call one cannot
0314: * use styles (and style indexes) from previous style blocks.
0315: */
0316: public void newStyleBlock() {
0317: StyleBlock sb = new StyleBlock();
0318: sb.prev = style_block;
0319:
0320: // check whether last style block has stylechange with flag NEW_STYLES
0321: // add this record if there is no such stylechange
0322: if (style_block != null) {
0323: IVVector shape_records = style_block.shapeRecords
0324: .getShapeRecords();
0325: if (shape_records.size() > 0) {
0326: Object o = shape_records
0327: .elementAt(shape_records.size() - 1);
0328: if (o instanceof StyleChangeRecord) {
0329: StyleChangeRecord sr = (StyleChangeRecord) o;
0330: sr.addFlags(StyleChangeRecord.NEW_STYLES);
0331: } else {
0332: StyleChangeRecord sr = new StyleChangeRecord();
0333: sr.addFlags(StyleChangeRecord.NEW_STYLES);
0334: shape_records.addElement(sr);
0335: }
0336: }
0337: }
0338:
0339: style_block = sb;
0340: }
0341:
0342: /**
0343: * Creates new Shape defined by tag DEFINESHAPE
0344: *
0345: * @return new Shape
0346: */
0347: public static Shape newShape1() {
0348: Shape shape = new Shape(Tag.DEFINESHAPE);
0349: shape.newStyleBlock();
0350: return shape;
0351: }
0352:
0353: /**
0354: * Creates new Shape defined by tag DEFINESHAPE2
0355: *
0356: * @return new Shape
0357: */
0358: public static Shape newShape2() {
0359: Shape shape = new Shape(Tag.DEFINESHAPE2);
0360: shape.newStyleBlock();
0361: return shape;
0362: }
0363:
0364: /**
0365: * Creates new Shape defined by tag DEFINESHAPE3
0366: *
0367: * @return new Shape
0368: */
0369: public static Shape newShape3() {
0370: Shape shape = new Shape(Tag.DEFINESHAPE3);
0371: shape.newStyleBlock();
0372: return shape;
0373: }
0374:
0375: /**
0376: * Creates new Shape3 from provided shape styles and records
0377: *
0378: * @return new Shape
0379: */
0380: public static Shape newShape3(ShapeStyles styles,
0381: ShapeRecords records) {
0382: Shape shape = new Shape(Tag.DEFINESHAPE3);
0383: shape.style_block = new StyleBlock(styles, records);
0384: return shape;
0385: }
0386:
0387: /**
0388: * Creates empty Shape (DEFINESHAPE tag)
0389: *
0390: * @return empty shape
0391: */
0392: public static Shape newEmptyShape1() {
0393: Shape emptyShape = newShape1();
0394: emptyShape.setLineStyle(new LineStyle(1, new Color(0, 0, 0)));
0395: emptyShape.movePenTo(0, 0);
0396: emptyShape.setBounds(GeomHelper.newRectangle(0, 0, 0, 0));
0397: return emptyShape;
0398: }
0399:
0400: public int getTag() {
0401: return tagcode;
0402: }
0403:
0404: public void collectDeps(DepsCollector dc) {
0405: StyleBlock sb = style_block;
0406: while (sb != null) {
0407: sb.shapeStyles.collectDeps(dc);
0408: sb = sb.prev;
0409: }
0410: }
0411:
0412: protected StyleChangeRecord getStyleChange() {
0413: return getShapeRecords().getStyleChange();
0414: }
0415:
0416: /**
0417: * Parses Shape
0418: *
0419: * @param p Parser
0420: * @return parsed shape
0421: */
0422: public static Shape parse(Parser p) {
0423:
0424: int tagCode = p.getTagCode();
0425: Shape shape = new Shape(tagCode);
0426: shape.setID(p.getUWord());
0427: shape.bounds = p.getRect();
0428:
0429: boolean withAlpha = shape.isWithAlpha();
0430:
0431: // parse first styles
0432: ShapeStyles shape_styles = ShapeStyles.parse(p, withAlpha);
0433:
0434: IVVector shape_records = new IVVector();
0435:
0436: // parse shape records
0437: int nBits = p.getUByte();
0438: int nFillBits = (nBits & 0xf0) >> 4;
0439: int nLineBits = nBits & 0x0f;
0440: p.initBits();
0441: for (;;) {
0442: if (p.getBool()) { // edge record
0443: if (p.getBool()) { // stright edge
0444: int nb = p.getBits(4) + 2;
0445: if (p.getBool()) { // general line
0446: int deltaX = p.getSBits(nb);
0447: int deltaY = p.getSBits(nb);
0448: shape_records.addElement(StrightEdgeRecord
0449: .newLine(deltaX, deltaY));
0450: } else if (p.getBool()) { // vertical line
0451: int deltaY = p.getSBits(nb);
0452: shape_records.addElement(StrightEdgeRecord
0453: .newVLine(deltaY));
0454: } else { // horizontal line
0455: int deltaX = p.getSBits(nb);
0456: shape_records.addElement(StrightEdgeRecord
0457: .newHLine(deltaX));
0458: }
0459: } else { // curved edge
0460: int nb = p.getBits(4) + 2;
0461: int cx = p.getSBits(nb);
0462: int cy = p.getSBits(nb);
0463: int ax = p.getSBits(nb);
0464: int ay = p.getSBits(nb);
0465: shape_records.addElement(new CurvedEdgeRecord(cx,
0466: cy, ax, ay));
0467: }
0468: } else { // style-change record (non-edge)
0469: int flags = p.getBits(5);
0470: if (flags == 0)
0471: break; // end record
0472: StyleChangeRecord scr = new StyleChangeRecord();
0473: scr.setFlags(flags);
0474:
0475: if ((flags & StyleChangeRecord.MOVETO) != 0) {
0476: int nMoveBits = p.getBits(5);
0477: scr.setDeltaX(p.getSBits(nMoveBits));
0478: scr.setDeltaY(p.getSBits(nMoveBits));
0479: }
0480:
0481: if ((flags & StyleChangeRecord.FILLSTYLE0) != 0) {
0482: scr.setFillStyle0(p.getBits(nFillBits));
0483: }
0484:
0485: if ((flags & StyleChangeRecord.FILLSTYLE1) != 0) {
0486: scr.setFillStyle1(p.getBits(nFillBits));
0487: }
0488:
0489: if ((flags & StyleChangeRecord.LINESTYLE) != 0) {
0490: scr.setLineStyle(p.getBits(nLineBits));
0491: }
0492:
0493: if ((flags & StyleChangeRecord.NEW_STYLES) != 0) {
0494:
0495: // add this style change as first record to new block
0496: shape_records.addElement(scr);
0497:
0498: // new styles are to be defined
0499: // save all already parsed records and styles in style block
0500: // and reset data to parse next style blosk
0501:
0502: StyleBlock new_style_block = new StyleBlock(
0503: shape_styles, shape_records);
0504: new_style_block.prev = shape.style_block;
0505: shape.style_block = new_style_block;
0506:
0507: // parse new styles
0508: shape_styles = ShapeStyles.parse(p, withAlpha);
0509: // create new records vector
0510: shape_records = new IVVector();
0511:
0512: // parse new fill and line number of bits
0513: nBits = p.getUByte();
0514: nFillBits = (nBits & 0xf0) >> 4;
0515: nLineBits = nBits & 0x0f;
0516: p.initBits();
0517:
0518: } else {
0519: shape_records.addElement(scr);
0520: }
0521:
0522: if ((flags & 0x80) != 0) {
0523: break;
0524: }
0525: }
0526: }
0527:
0528: // save parsed records in style block
0529: StyleBlock new_style_block = new StyleBlock(shape_styles,
0530: shape_records);
0531: new_style_block.prev = shape.style_block;
0532: shape.style_block = new_style_block;
0533:
0534: return shape;
0535: }
0536:
0537: public boolean isWithAlpha() {
0538: return getTag() == Tag.DEFINESHAPE3;
0539: }
0540:
0541: /**
0542: * This class represents style block: set of styles and set of corresponding
0543: * records which use these styles. It also holds reference to previous
0544: * style block. Shape may contain several different style blocks.
0545: * This class is not supposed to be used externally and reflects inner
0546: * Shape structure.
0547: */
0548: private static class StyleBlock {
0549:
0550: StyleBlock prev;
0551: ShapeStyles shapeStyles; // collection of fill and line styles
0552: ShapeRecords shapeRecords; // collection of shape records
0553:
0554: StyleBlock() {
0555: this .shapeStyles = new ShapeStyles();
0556: this .shapeRecords = new ShapeRecords();
0557: }
0558:
0559: StyleBlock(ShapeStyles styles, IVVector records) {
0560: this (styles, new ShapeRecords(records));
0561: }
0562:
0563: StyleBlock(ShapeStyles styles, ShapeRecords records) {
0564: this .shapeStyles = styles;
0565: this .shapeRecords = records;
0566: }
0567:
0568: private void write_block(FlashOutput fob) {
0569: if (prev != null) {
0570: prev.write_block(fob);
0571: fob.flushBits();
0572: }
0573:
0574: shapeStyles.write(fob);
0575:
0576: int nFillBits = shapeStyles.calcNFillBits();
0577: int nLineBits = shapeStyles.calcNLineBits();
0578: fob.writeByte((nFillBits << 4) | nLineBits);
0579: shapeRecords.write(fob, nFillBits, nLineBits);
0580: }
0581:
0582: public void write(FlashOutput fob) {
0583: write_block(fob);
0584: fob.writeBits(0, 6);
0585: fob.flushBits();
0586: }
0587:
0588: public void printContent(PrintStream out, String indent) {
0589: if (prev != null) {
0590: prev.printContent(out, indent);
0591: out.println(indent + " new styleblock:");
0592: }
0593: shapeStyles.printContent(out, indent + " ");
0594: shapeRecords.printContent(out, indent + " ");
0595: }
0596:
0597: public StyleBlock getCopy(ScriptCopier copier) {
0598: StyleBlock my_prev = null;
0599: if (prev != null) {
0600: my_prev = prev.getCopy(copier);
0601: }
0602: StyleBlock my_block = new StyleBlock(
0603: (ShapeStyles) shapeStyles.getCopy(copier),
0604: (ShapeRecords) shapeRecords.getCopy(copier));
0605: my_block.prev = my_prev;
0606: return my_block;
0607: }
0608: }
0609:
0610: public void write(FlashOutput main) {
0611: FlashOutput fob = new FlashOutput(main, 100);
0612:
0613: // set userdata to this shape, so that styles know which color to write
0614: fob.setUserData(this );
0615: fob.write(bounds);
0616:
0617: style_block.write(fob);
0618:
0619: main.writeTag(getTag(), 2 + fob.getSize());
0620: main.writeDefID(this );
0621: main.writeFOB(fob);
0622: }
0623:
0624: public void printContent(PrintStream out, String indent) {
0625: out.println(indent + "Shape(" + Tag.tagNames[getTag()]
0626: + "): id=" + getID() + ", name='" + getName() + "'");
0627: out.println(indent + " " + bounds);
0628: style_block.printContent(out, indent);
0629: }
0630:
0631: public boolean isConstant() {
0632: return true;
0633: }
0634:
0635: /**
0636: * Returns Shape bounds
0637: *
0638: * @return shape bounds
0639: */
0640: public Rectangle2D getBounds() {
0641: return bounds;
0642: }
0643:
0644: /**
0645: * Sets bounding box for this shape.
0646: *
0647: * @param bounds new bounding box
0648: */
0649: public void setBounds(Rectangle2D bounds) {
0650: this .bounds = bounds;
0651: }
0652:
0653: /**
0654: * Sets bounding box for this shape.
0655: *
0656: */
0657: public void setBounds(int x, int y, int width, int height) {
0658: setBounds(GeomHelper.newRectangle(x, y, width, height));
0659: }
0660:
0661: protected FlashItem copyInto(FlashItem item, ScriptCopier copier) {
0662: super .copyInto(item, copier);
0663: ((Shape) item).bounds = (Rectangle2D) bounds.clone();
0664: ((Shape) item).style_block = (StyleBlock) style_block
0665: .getCopy(copier);
0666: ((Shape) item).tagcode = tagcode;
0667: return item;
0668: }
0669:
0670: public FlashItem getCopy(ScriptCopier copier) {
0671: return copyInto(new Shape(), copier);
0672: }
0673:
0674: private Shape(int tagcode) {
0675: this .tagcode = tagcode;
0676: }
0677:
0678: /* ----------------------------------------------------------------------------------- */
0679: /* D R A W I N G */
0680: /* ----------------------------------------------------------------------------------- */
0681:
0682: /**
0683: * Draws curve record.<p>
0684: * All coordinates are in twixels.
0685: *
0686: * @param cx X control point
0687: * @param cy Y control point
0688: * @param ax X anchor point
0689: * @param ay Y anchor point
0690: */
0691: public void drawCurveTo(int cx, int cy, int ax, int ay) {
0692: getShapeRecords().drawCurveTo(cx, cy, ax, ay);
0693: }
0694:
0695: /**
0696: * Draws curve record.<p>
0697: * All coordinates are in twixels.
0698: *
0699: * @param ax1 X anchor point 1
0700: * @param ay1 Y anchor point 1
0701: * @param cx X control point
0702: * @param cy Y control point
0703: * @param ax2 X anchor point 2
0704: * @param ay2 Y anchor point 2
0705: */
0706: public void drawCurve(int ax1, int ay1, int cx, int cy, int ax2,
0707: int ay2) {
0708: getShapeRecords().drawCurve(ax1, ay1, cx, cy, ax2, ay2);
0709: }
0710:
0711: /**
0712: * Draws curve record.<p>
0713: * All coordinates are in twixels.
0714: *
0715: * @param anchor0 first anchor point
0716: * @param control control point
0717: * @param anchor1 second anchor point
0718: */
0719: public void drawCurve(Point2D anchor1, Point2D control,
0720: Point2D anchor2) {
0721: getShapeRecords().drawCurve(anchor1, control, anchor2);
0722: }
0723:
0724: /**
0725: * Draws a straight line from current position to the specified one.<p>
0726: * All coordinates are in twixels.
0727: *
0728: * @param x X of end of line
0729: * @param y Y of end of line
0730: */
0731: public void drawLineTo(int x, int y) {
0732: getShapeRecords().drawLineTo(x, y);
0733: }
0734:
0735: /**
0736: * Draws a straight line from current position to the specified one.<p>
0737: * All coordinates are in twixels.
0738: *
0739: * @param p1 end of line
0740: */
0741: public void drawLineTo(Point2D p1) {
0742: getShapeRecords().drawLineTo(p1);
0743: }
0744:
0745: /**
0746: * Draws a straight line specified by two points.
0747: * <P>
0748: * All coordinates are in twixels.
0749: *
0750: * @param x1 X of the beginning of the line
0751: * @param y1 Y of the beginning of the line
0752: * @param x2 X of the end of the line
0753: * @param y2 Y of the end of the line
0754: */
0755: public void drawLine(int x1, int y1, int x2, int y2) {
0756: getShapeRecords().drawLine(x1, y1, x2, y2);
0757: }
0758:
0759: /**
0760: * Draws a straight line specified by two points.
0761: * <P>
0762: * All coordinates are in twixels.
0763: *
0764: * @param p0 first point
0765: * @param p1 second point
0766: */
0767: public void drawLine(Point2D p0, Point2D p1) {
0768: getShapeRecords().drawLine(p0, p1);
0769: }
0770:
0771: /**
0772: * Draws a rectangle specified by its top-left corner and width and height
0773: * <p>
0774: * All coordinates are in twixels.
0775: *
0776: * @param x x coordinates of top-left corner of the rectangle
0777: * @param y y coordinates of top-left corner of the rectangle
0778: * @param width width of the rectangle
0779: * @param height height of the rectangle
0780: */
0781: public void drawRectangle(int x, int y, int width, int height) {
0782: getShapeRecords().drawRectangle(x, y, width, height);
0783: }
0784:
0785: /**
0786: * Draws a rectangle specified by {@link java.awt.geom.Rectangle2D}
0787: * <p>
0788: * All coordinates are in twixels.
0789: *
0790: * @param r specified rectangle
0791: */
0792: public void drawRectangle(Rectangle2D r) {
0793: getShapeRecords().drawRectangle(r);
0794: }
0795:
0796: /**
0797: * Moves pen to the specified position.<p>
0798: * All coordinates are ABSOLUTE and are in twixels.
0799: *
0800: * @param x new current X
0801: * @param y new current Y
0802: */
0803: public void movePenTo(int x, int y) {
0804: getShapeRecords().movePenTo(x, y);
0805: }
0806:
0807: /**
0808: * Moves pen to the specified point.<p>
0809: * All coordinates are ABSOLUTE and are in twixels!
0810: *
0811: * @param p new pen position
0812: */
0813: public void movePenTo(Point2D p) {
0814: getShapeRecords().movePenTo(p);
0815: }
0816:
0817: /**
0818: * Draw AWT Shape
0819: * <P>
0820: * All shape coordinates are in twixels!
0821: *
0822: * @param shape AWT shape
0823: */
0824: public void drawAWTShape(java.awt.Shape shape) {
0825: getShapeRecords().drawAWTShape(shape);
0826: }
0827:
0828: /**
0829: * Draw AWT Shape
0830: * <P>
0831: * All shape coordinates are in twixels!
0832: *
0833: * @param shape AWT shape
0834: */
0835: public void drawAWTShape(java.awt.Shape shape,
0836: AffineTransform matrix) {
0837: getShapeRecords().drawAWTShape(shape, matrix);
0838: }
0839:
0840: /**
0841: * Draw AWT PathIterator
0842: * <P>
0843: * All coordinates are in twixels!
0844: *
0845: * @param pi AWT PathIterator
0846: */
0847: public void drawAWTPathIterator(java.awt.geom.PathIterator pi) {
0848: getShapeRecords().drawAWTPathIterator(pi);
0849: }
0850:
0851: /**
0852: * Returns current pen position
0853: *
0854: * @return current pen position
0855: */
0856: public Point2D getCurrentPos() {
0857: return getShapeRecords().getCurrentPos();
0858: }
0859:
0860: /**
0861: * Returns first pen position (first moveTo)
0862: *
0863: * @return first pen position
0864: */
0865: public Point2D getFirstPos() {
0866: return getShapeRecords().getFirstPos();
0867: }
0868:
0869: /* --------------------------------------------------------------------------------------- */
0870: /* AWT Stuff */
0871: /* --------------------------------------------------------------------------------------- */
0872:
0873: //
0874: // THE FOLLOWING CODE IS JUST AN EXPERIMENT!
0875: // IT IS NOT INDENDED TO BE USED IN ANY WAY!
0876: // IT IS IN A VERY FIRST STAGE OF DEVELOPMENT
0877: //
0878:
0879: private static class Painter {
0880: java.awt.Graphics2D g2;
0881: GeneralPath gp_line;
0882: GeneralPath gp_fill;
0883: java.awt.Paint[] lstyle_paints;
0884: java.awt.Stroke[] lstyle_strokes;
0885: java.awt.Paint[] fstyle_paints;
0886: ShapeStyles ss;
0887: int line_idx;
0888: int fill_idx;
0889:
0890: Painter(java.awt.Graphics2D g2, GeneralPath gp_fill,
0891: GeneralPath gp_line, ShapeStyles ss) {
0892: this .g2 = g2;
0893: this .ss = ss;
0894: this .gp_line = gp_line;
0895: this .gp_fill = gp_fill;
0896:
0897: line_idx = -1;
0898: fill_idx = -1;
0899: lstyle_paints = new java.awt.Paint[ss.lineStyles.size()];
0900: lstyle_strokes = new java.awt.Stroke[ss.lineStyles.size()];
0901: fstyle_paints = new java.awt.Paint[ss.fillStyles.size()];
0902: }
0903:
0904: void paint_line() {
0905: if (line_idx >= 0 && gp_line != null) {
0906: java.awt.Paint paint = lstyle_paints[line_idx];
0907: java.awt.Stroke stroke = lstyle_strokes[line_idx];
0908: if (paint == null) {
0909: LineStyle lstyle = ss.getLineStyle(line_idx);
0910:
0911: int width = lstyle.getWidth();
0912: //if( width < 20 ) width = 20;
0913: stroke = new java.awt.BasicStroke(width);//, java.awt.BasicStroke.CAP_ROUND,
0914: paint = lstyle.getColor().getAWTColor();
0915:
0916: lstyle_paints[line_idx] = paint;
0917: lstyle_strokes[line_idx] = stroke;
0918: }
0919:
0920: g2.setPaint(paint);
0921: g2.setStroke(stroke);
0922:
0923: g2.draw(gp_line);
0924: gp_line.reset();
0925: }
0926: }
0927:
0928: void paint_fill() {
0929: if (fill_idx >= 0 && gp_fill != null) {
0930: java.awt.Paint paint = fstyle_paints[fill_idx];
0931: if (paint == null) {
0932: FillStyle fstyle = ss.getFillStyle(fill_idx);
0933:
0934: switch (fstyle.getType()) {
0935: case FillStyle.SOLID: {
0936: paint = fstyle.getColor().getAWTColor();
0937: break;
0938: }
0939: case FillStyle.LINEAR_GRADIENT: {
0940: Gradient grad = fstyle.getGraduent();
0941: paint = grad.getColors()[0].getAWTColor();
0942: break;
0943: }
0944: }
0945: fstyle_paints[fill_idx] = paint;
0946: }
0947:
0948: if (paint != null)
0949: g2.setPaint(paint);
0950:
0951: g2.fill(gp_fill);
0952: gp_fill.reset();
0953: }
0954: }
0955:
0956: }
0957:
0958: /**
0959: * Draws itself into specified graphics
0960: *
0961: * @param g graphics object
0962: */
0963: public void draw(java.awt.Graphics2D g2) {
0964: IVVector blocks = new IVVector();
0965:
0966: StyleBlock sb = style_block;
0967: while (sb.prev != null) {
0968: blocks.addElement(sb);
0969: sb = sb.prev;
0970: }
0971: blocks.addElement(sb);
0972:
0973: float x = 0f;
0974: float y = 0f;
0975:
0976: GeneralPath gp_line = null;
0977: GeneralPath gp_fill = null;
0978: for (int blk = blocks.size(); --blk >= 0;) {
0979: sb = (StyleBlock) blocks.elementAt(blk);
0980: ShapeRecords shaperecords = sb.shapeRecords;
0981:
0982: boolean isFills = sb.shapeStyles.fillStyles.size() > 0;
0983: boolean isLines = sb.shapeStyles.lineStyles.size() > 0;
0984:
0985: if (isFills && gp_fill == null)
0986: gp_fill = new GeneralPath(GeneralPath.WIND_EVEN_ODD);
0987: if (isLines && gp_line == null)
0988: gp_line = new GeneralPath(GeneralPath.WIND_EVEN_ODD);
0989:
0990: Painter painter = new Painter(g2, gp_fill, gp_line,
0991: sb.shapeStyles);
0992:
0993: IVVector records = shaperecords.getShapeRecords();
0994: boolean isGpEmpty = true;
0995: for (int r = 0; r < records.size(); r++) {
0996: Object o = records.elementAt(r);
0997: if (o instanceof StyleChangeRecord) {
0998: StyleChangeRecord sr = (StyleChangeRecord) o;
0999: int f = sr.getFlags();
1000:
1001: boolean isGpEmpty1 = isGpEmpty;
1002:
1003: if ((f & (StyleChangeRecord.FILLSTYLE1 | StyleChangeRecord.FILLSTYLE0)) != 0) {
1004: if (!isGpEmpty)
1005: painter.paint_fill();
1006: int idx = sr.getFillStyle0() - 1;
1007: if (idx < 0)
1008: idx = sr.getFillStyle1() - 1;
1009: painter.fill_idx = idx;
1010: isGpEmpty1 = true;
1011: }
1012:
1013: if ((f & StyleChangeRecord.LINESTYLE) != 0) {
1014: if (!isGpEmpty)
1015: painter.paint_line();
1016: painter.line_idx = sr.getLineStyle() - 1;
1017: isGpEmpty1 = true;
1018: }
1019: isGpEmpty = isGpEmpty1;
1020:
1021: if ((f & StyleChangeRecord.MOVETO) != 0) {
1022: x = sr.getDeltaX();
1023: y = sr.getDeltaY();
1024: }
1025:
1026: if (isFills)
1027: gp_fill.moveTo(x, y);
1028: if (isLines)
1029: gp_line.moveTo(x, y);
1030:
1031: } else if (o instanceof StrightEdgeRecord) {
1032: StrightEdgeRecord se = (StrightEdgeRecord) o;
1033: x += se.getDeltaX();
1034: y += se.getDeltaY();
1035: if (isFills)
1036: gp_fill.lineTo(x, y);
1037: if (isLines)
1038: gp_line.lineTo(x, y);
1039: isGpEmpty = false;
1040: } else {
1041: CurvedEdgeRecord ce = (CurvedEdgeRecord) o;
1042: float xx = x + ce.getControlDeltaX();
1043: float yy = y + ce.getControlDeltaY();
1044: x = xx + ce.getAnchorDeltaX();
1045: y = yy + ce.getAnchorDeltaY();
1046: if (isFills)
1047: gp_fill.quadTo(xx, yy, x, y);
1048: if (isLines)
1049: gp_line.quadTo(xx, yy, x, y);
1050: isGpEmpty = false;
1051: }
1052: }
1053: if (!isGpEmpty) {
1054: painter.paint_fill();
1055: painter.paint_line();
1056: }
1057: }
1058: }
1059: }
|