package com.xenoage.zong.musiclayout.stampings.bitmap; import static com.xenoage.utils.math.Units.mmToPx; import static com.xenoage.utils.math.Units.pxToMm; import com.xenoage.utils.annotations.Const; import com.xenoage.utils.math.Units; import com.xenoage.zong.musiclayout.stampings.StaffStamping; /** * This class helps drawing nice staves on a bitmap, * like the screen or a bitmap file. * * Because the bitmap works with integer * coordinates, a staff could look very ugly, * when it uses for example the interline spaces * 2-1-2-1-2. Just view a score in Adobe Reader 7 * for example and the need of a better display * is obvious. * * This class computes the best possible * display of staves on a bitmap: It ensures * that each interline space has the same size. * Unfortunately this also means that the displayed * size is not really the actual one, but an * approximation. But it's really much better * than just rounding to integer interline spaces. * * When the staff is too small, a rectangle should be drawn * (called a "simplified staff"). * In this case the interline space if a float value * and the height of the staff is just rounded * to the next integer value. * * @author Andreas Wenger */ @Const public class BitmapStaff { /** The width of a px in mm */ public final float pxMm; /** True, if a simplified staff (just a filled rectangle) should be drawn */ public final boolean isSimplifiedStaff; /** The adjusted height of the staff in mm, that fits best to the screen. * The half top line and half bottom line are, as always in a "staff height", not included! */ public final float heightMm; /** The height of a line in mm. This value may be less than 1. If you need the displayed height * of the line, use the {@link BitmapLine} class. */ public final float lineHeightMm; /** The height scaling factor of the staff, that fits best to the screen */ public final float heightScaling; /** The adjusted interline space of the staff in mm, that fits best to the screen */ public final float interlineSpaceMm; /** The additional vertical offset of the staff in mm, that fits best to the screen. */ public final float yOffsetMm; /** The vertical position in mm, relative to the normal y-position * of the {@link StaffStamping}, of line position 0 (bottom line). * This is the y-offset plus the adjusted staff height and is just stored * for performance reasons. */ public final float lp0Mm; /** * Creates a {@link BitmapStaff} with the given number of lines, * interline space in mm, line width (in interline spaces) and the given scaling. * @param scaling the current scaling factor. e.g. 1 means 72 dpi, 2 means 144 dpi. */ public BitmapStaff(int lines, float realInterlineSpaceMm, float lineWidthIS, float scaling) { this.pxMm = pxToMm(1, scaling); float realHeightMm = realInterlineSpaceMm * (lines - 1); float interlineSpacePxFloat = mmToPx(realInterlineSpaceMm, scaling); //height of a line (may be smaller than 1) this.lineHeightMm = pxToMm(lineWidthIS * interlineSpacePxFloat, scaling); //simplified staff? this.isSimplifiedStaff = (interlineSpacePxFloat < 2); if (this.isSimplifiedStaff) { //no place for lines, the staff is displayed too small //just round the height to the next integer value //explanation: Lines are only useful, if there is (at least) 1-pixel-line, then //a 1-pixel space, and so on. //if the normalHeightPxFloat (because of the //above explanation) is smaller than this value, there can be no //useful display with lines, but a rectangle should be drawn. //use real interline space this.interlineSpaceMm = realInterlineSpaceMm; this.heightMm = this.interlineSpaceMm * (lines - 1); //no vertical offset is needed this.yOffsetMm = 0; } else { //ensure equal interline spaces int interlineSpacePx = Math.round(interlineSpacePxFloat); this.interlineSpaceMm = pxToMm(interlineSpacePx, scaling); this.heightMm = this.interlineSpaceMm * (lines - 1); //compute vertical offset. if for example the staff is 0.4 mm "too high", //move it 0.2 mm up. Thanks for the idea to Andreas Schmid. float yOffsetPx = Math.round(Units.mmToPx((realHeightMm - this.heightMm) / 2, scaling)); this.yOffsetMm = Units.pxToMm(yOffsetPx, scaling); } //height scaling factor this.heightScaling = heightMm / realHeightMm; //cache this.lp0Mm = yOffsetMm + heightMm; } /** * Gets the vertical position in mm, relative to the normal y-position * of the {@link StaffStamping}, of the given line position. */ public float getYMm(float lp) { return lp0Mm - lp / 2 * interlineSpaceMm; //TODO: wrong?! is relative to LP0! } }