//----------------------------------------------------------------------------// // // // B a s i c S y m b o l // // // //----------------------------------------------------------------------------// // <editor-fold defaultstate="collapsed" desc="hdr"> // // Copyright © Hervé Bitteur and others 2000-2013. All rights reserved. // // This software is released under the GNU General Public License. // // Goto http://kenai.com/projects/audiveris to report bugs or suggestions. // //----------------------------------------------------------------------------// // </editor-fold> package omr.ui.symbol; import static omr.ui.symbol.Alignment.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.awt.Component; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Point; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.font.TextLayout; import java.awt.geom.AffineTransform; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.util.Arrays; /** * Class {@code BasicSymbol} is the base for implementing instances of * {@link Symbol} interface. * It does not handle a specific Shape as its subclass ShapeSymbol,but only * handles a sequence of MusicFont codes. * * @author Hervé Bitteur */ public class BasicSymbol implements Symbol { //~ Static fields/initializers --------------------------------------------- /** Usual logger utility */ protected static final Logger logger = LoggerFactory.getLogger( BasicSymbol.class); /** Painting origin for images */ protected static final Point ORIGIN = new Point(0, 0); /** A transformation to flip horizontally (x' = -x) */ protected static final AffineTransform horizontalFlip = new AffineTransform( -1, // m00 (x' = -x) 0, // m01 0, // m02 1, // m10 (y' = y) 0, // m11 0); // m12 /** A transformation to flip vertically (y' = -y) */ protected static final AffineTransform verticalFlip = new AffineTransform( 1, // m00 (x' = x) 0, // m01 0, // m02 -1, // m10 (y' = -y) 0, // m11 0); // m12 /** A transformation to turn 1 quadrant clockwise */ protected static final AffineTransform quadrantRotateOne = AffineTransform.getQuadrantRotateInstance( 1); /** A transformation to turn 2 quadrants clockwise */ protected static final AffineTransform quadrantRotateTwo = AffineTransform.getQuadrantRotateInstance( 2); /** A transformation for really small icon display */ protected static AffineTransform tiny = AffineTransform.getScaleInstance( 0.5, 0.5); //~ Instance fields -------------------------------------------------------- /** To flag an icon symbol */ protected final boolean isIcon; /** Sequence of point codes */ public final int[] codes; /** Related image, corresponding to standard interline */ private BufferedImage image; /** Pre-scaled symbol for icon display */ protected BasicSymbol icon; /** Image dimension corresponding to standard interline */ private Dimension dimension; //~ Constructors ----------------------------------------------------------- //-------------// // BasicSymbol // //-------------// /** * Creates a new BasicSymbol object. * * @param isIcon true for an icon * @param codes the codes for MusicFont characters */ public BasicSymbol (boolean isIcon, int... codes) { this.isIcon = isIcon; this.codes = shiftedCodesOf(codes); } //-------------// // BasicSymbol // //-------------// /** * Creates a new BasicSymbol object, standard size. * * @param codes the codes for MusicFont characters */ public BasicSymbol (int... codes) { this(false, codes); } //~ Methods ---------------------------------------------------------------- //------------// // buildImage // //------------// @Override public SymbolImage buildImage (MusicFont font) { // Params Params p = getParams(font); // Allocate image of proper size SymbolImage img = new SymbolImage( p.rect.width, p.rect.height, p.offset); // Paint the image Graphics2D g = (Graphics2D) img.getGraphics(); g.setColor(OmrFont.defaultImageColor); // Anti-aliasing g.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); paint(g, p, ORIGIN, TOP_LEFT); return img; } //-----------// // getString // //-----------// public final String getString () { return new String(codes, 0, codes.length); } //---------------// // getIconHeight // //---------------// /** * Report the icon height. * * @return the height of the icon image in pixels */ @Override public int getIconHeight () { return getIcon() .getHeight(); } //--------------// // getIconImage // //--------------// @Override public BufferedImage getIconImage () { return getIcon() .getImage(); } //--------------// // getIconWidth // //--------------// /** * Report the width of the icon (used by swing when painting). * * @return the icon image width in pixels */ @Override public int getIconWidth () { return getIcon() .getWidth(); } //-------------// // getRefPoint // //-------------// @Override public Point getRefPoint (Rectangle box) { return new Point(box.x + (box.width / 2), box.y + (box.height / 2)); } //-----------// // paintIcon // //-----------// /** * Implements Icon interface paintIcon() method. * * @param c containing component * @param g graphic context * @param x abscissa * @param y ordinate */ @Override public void paintIcon (Component c, Graphics g, int x, int y) { g.drawImage(getIconImage(), x, y, c); } //-------------// // paintSymbol // //-------------// @Override public void paintSymbol (Graphics2D g, MusicFont font, Point location, Alignment alignment) { paint(g, getParams(font), location, alignment); } //----------// // toString // //----------// @Override public String toString () { StringBuilder sb = new StringBuilder("{"); sb.append(getClass().getSimpleName()); sb.append(internalsString()); sb.append("}"); return sb.toString(); } //------------// // createIcon // //------------// /** * To be redefined by each subclass in order to create a icon symbol * using the subclass. * * @return the icon-sized instance of proper symbol class */ protected BasicSymbol createIcon () { return new BasicSymbol(true, codes); } //--------------// // getDimension // //--------------// /** * Report the normalized bounding dimension of the symbol. * * @return the size of the symbol */ protected Dimension getDimension () { if (dimension == null) { computeImage(); } return dimension; } //--------------// // getDimension // //--------------// /** * Report what would be the bounding dimension of the symbol, * if painted with the provided font. * * @return the potential size of the painted symbol */ protected Dimension getDimension (MusicFont font) { Dimension dim = getDimension(); double ratio = font.getSize2D() / MusicFont.baseMusicFont.getSize2D(); return new Dimension( (int) Math.ceil(dim.width * ratio), (int) Math.ceil(dim.height * ratio)); } //-----------// // getHeight // //-----------// /** * Report the height of the symbol, for a standard interline. * * @return the real image height in pixels */ protected int getHeight () { if (dimension == null) { computeImage(); } return dimension.height; } //----------// // getImage // //----------// /** * Report the underlying image, scaled for standard interline value. * * @return the underlying image */ protected BufferedImage getImage () { if (dimension == null) { computeImage(); } return image; } //-----------// // getParams // //-----------// protected Params getParams (MusicFont font) { Params p = new Params(); p.layout = layout(font); Rectangle2D r = p.layout.getBounds(); p.rect = new Rectangle( (int) Math.ceil(r.getWidth()), (int) Math.ceil(r.getHeight())); return p; } //----------// // getWidth // //----------// /** * Report the width of the symbol, for a standard interline. * * @return the real image width in pixels */ protected int getWidth () { if (dimension == null) { computeImage(); } return dimension.width; } //-----------------// // internalsString // //-----------------// protected String internalsString () { StringBuilder sb = new StringBuilder(); if (isIcon) { sb.append(" icon"); } // sb.append(" dim:") // .append(getWidth()) // .append("x") // .append(getHeight()); // if (codes != null) { sb.append(" codes:") .append(Arrays.toString(codes)); } return sb.toString(); } //--------// // layout // //--------// /** * Report a single layout, based on symbol codes if they exist. * This feature can work only with a single "line" of music codes. * * @param font the specifically-scaled font to use * @return the layout ready to be drawn, or null */ protected TextLayout layout (MusicFont font) { return font.layout(getString()); } //-------// // paint // //-------// /** * Actual painting, to be redefined by subclasses if needed. * * @param g graphics context * @param p the parameters fed by getParams() * @param location where to paint * @param alignment relative position of provided location wrt symbol */ protected void paint (Graphics2D g, Params p, Point location, Alignment alignment) { OmrFont.paint(g, p.layout, location, alignment); } //--------------// // computeImage // //--------------// private void computeImage () { image = buildImage( isIcon ? MusicFont.iconMusicFont : MusicFont.baseMusicFont); dimension = new Dimension(image.getWidth(), image.getHeight()); } //---------// // getIcon // //---------// private BasicSymbol getIcon () { if (isIcon) { return null; } else { if (icon == null) { icon = createIcon(); } return icon; } } //----------------// // shiftedCodesOf // //----------------// /** * Make sure the codes are above the CODE_OFFSET value. * * @param codes raw codes * @return codes suitable for font display */ private int[] shiftedCodesOf (int... codes) { int[] values = new int[codes.length]; for (int i = 0; i < codes.length; i++) { if (codes[i] < MusicFont.CODE_OFFSET) { values[i] = codes[i] + MusicFont.CODE_OFFSET; } else { values[i] = codes[i]; } } return values; } //~ Inner Classes ---------------------------------------------------------- //--------// // Params // //--------// /** * A set of parameters used for building an image and for painting * a symbol. */ protected class Params { //~ Instance fields ---------------------------------------------------- /** Specific offset, if any, from area center. */ Point offset; /** (Main) layout. */ TextLayout layout; /** Image bounds. */ Rectangle rect; } }