diff --git a/src/main/scala/hevs/graphics/FunGraphics.scala b/src/main/scala/hevs/graphics/FunGraphics.scala index b7b1bcc..3b228dc 100644 --- a/src/main/scala/hevs/graphics/FunGraphics.scala +++ b/src/main/scala/hevs/graphics/FunGraphics.scala @@ -8,9 +8,10 @@ import hevs.graphics.utils.RepeatingReleasedEventsFixer import javax.imageio.ImageIO import java.awt._ import java.awt.event._ -import java.awt.font.TextLayout -import java.awt.geom.AffineTransform +import java.awt.font.{LineMetrics, TextLayout} +import java.awt.geom.{AffineTransform, Rectangle2D} import java.io._ +import javax.swing.SwingConstants /** * Factory for [[hevs.graphics.FunGraphics]]. @@ -282,14 +283,19 @@ class FunGraphics(val width: Int, val height: Int, val xoffset: Int, val yoffset g2d.fillOval(posX, posY, width, height) } - override def drawString(posX: Int, posY: Int, str: String): Unit = { - g2d.drawString(str, posX, posY) + override def getStringSize(str: String, font: Font): Rectangle2D = { + val metrics: LineMetrics = font.getLineMetrics(str, g2d.getFontRenderContext) + val rect: Rectangle2D = font.getStringBounds(str, g2d.getFontRenderContext) + val height: Double = rect.getHeight - metrics.getDescent - metrics.getLeading + new Rectangle(rect.getWidth.toInt, height.toInt) } - override def drawString(posX: Int, posY: Int, str: String, color: Color, size: Int): Unit = { + override def getStringSize(str: String): Rectangle2D = getStringSize(str, g2d.getFont) + + override def drawString(posX: Int, posY: Int, str: String, font: Font, color: Color): Unit = { val oldFont = g2d.getFont val oldColor = g2d.getColor - val font = new Font("SansSerif", Font.PLAIN, size) + g2d.setFont(font) g2d.setColor(color) g2d.drawString(str, posX, posY) @@ -297,18 +303,118 @@ class FunGraphics(val width: Int, val height: Int, val xoffset: Int, val yoffset g2d.setColor(oldColor) } + override def drawString(posX: Int, + posY: Int, + str: String, + font: Font, + color: Color, + halign: Int, + valign: Int): Unit = { + + val bounds: Rectangle2D = getStringSize(str, font) + val w: Double = bounds.getWidth + val h: Double = bounds.getHeight + + var x: Double = posX + var y: Double = posY + + if (halign == SwingConstants.CENTER) { + x -= w / 2.0 + } else if (halign == SwingConstants.RIGHT) { + x -= w + } + + if (valign == SwingConstants.CENTER) { + y += h / 2.0 + } else if (valign == SwingConstants.TOP) { + y += h + } + + drawString(math.round(x).toInt, math.round(y).toInt, str, font, color) + } + + override def drawString(posX: Int, + posY: Int, + str: String, + fontFamily: String = "SansSerif", + fontStyle: Int = Font.PLAIN, + fontSize: Int = 20, + color: Color = Color.BLACK, + halign: Int = SwingConstants.LEFT, + valign: Int = SwingConstants.BOTTOM): Unit = { + + val font = new Font(fontFamily, fontStyle, fontSize) + drawString(posX, posY, str, font, color, halign, valign) + } + + override def drawString(posX: Int, posY: Int, str: String, color: Color, size: Int): Unit = { + drawString(posX, posY, str, fontSize = size, color = color) + } + override def drawFancyString(posX: Int, posY: Int, str: String, color: Color, size: Int): Unit = { - val g2 = g2d - val oldFont = g2d.getFont - val oldColor = g2d.getColor - val font = new Font("Georgia", Font.BOLD, size) - val textLayout = new TextLayout(str, font, g2.getFontRenderContext) - g2.setColor(Color.GRAY) - textLayout.draw(g2, (posX + 2).toFloat, (posY + 2).toFloat) - g2.setColor(color) - textLayout.draw(g2, posX.toFloat, posY.toFloat) - g2.setFont(oldFont) - g2.setColor(oldColor) + val font: Font = new Font("Georgia", Font.BOLD, size) + drawString(posX+2, posY+2, str, font, color = Color.GRAY) + drawString(posX, posY, str, fontSize = size, color = color) + } + + override def drawFancyString(posX: Int, + posY: Int, + str: String, + fontFamily: String = "Georgia", + fontStyle: Int = Font.BOLD, + fontSize: Int = 20, + color: Color = Color.BLACK, + halign: Int = SwingConstants.LEFT, + valign: Int = SwingConstants.BOTTOM, + shadowX: Int = 0, + shadowY: Int = 0, + shadowColor: Color = Color.GRAY, + shadowThickness: Int = 0, + outlineColor: Color = Color.WHITE, + outlineThickness: Int = 0): Unit = { + + val font: Font = new Font(fontFamily, fontStyle, fontSize) + + if (shadowThickness > 0) { + val bounds: Rectangle2D = getStringSize(str, font) + val w: Double = bounds.getWidth + val h: Double = bounds.getHeight + + var cx: Double = posX + var cy: Double = posY + + if (halign == SwingConstants.LEFT) { + cx += w / 2.0 + } else if (halign == SwingConstants.RIGHT) { + cx -= w / 2.0 + } + + if (valign == SwingConstants.TOP) { + cy += h / 2.0 + } else if (valign == SwingConstants.BOTTOM) { + cy -= h / 2.0 + } + + val font2: Font = new Font(fontFamily, fontStyle, fontSize+shadowThickness) + drawString( + math.round(cx + shadowX).toInt, + math.round(cy + shadowY).toInt, + str, + font2, + shadowColor, + SwingConstants.CENTER, + SwingConstants.CENTER) + } + + if (outlineThickness > 0) { + for (dy: Int <- -outlineThickness to outlineThickness) { + for (dx: Int <- -outlineThickness to outlineThickness) { + drawString(posX + dx, posY + dy, str, font, outlineColor, halign, valign) + } + } + } + + drawString(posX, posY, str, font, color, halign, valign) } override def drawPicture(posX: Int, posY: Int, bitmap: GraphicsBitmap): Unit = { @@ -347,6 +453,8 @@ class FunGraphics(val width: Int, val height: Int, val xoffset: Int, val yoffset override def getFrameWidth(): Int = fWidth override def getFrameHeight(): Int = fHeight + override def getAvailableFonts(): Array[String] = GraphicsEnvironment.getLocalGraphicsEnvironment.getAvailableFontFamilyNames + /** * A sample game loop using explicit synchronization (if display flickers) */ diff --git a/src/main/scala/hevs/graphics/interfaces/Graphics.scala b/src/main/scala/hevs/graphics/interfaces/Graphics.scala index 2fbb719..70ab847 100644 --- a/src/main/scala/hevs/graphics/interfaces/Graphics.scala +++ b/src/main/scala/hevs/graphics/interfaces/Graphics.scala @@ -2,9 +2,9 @@ package hevs.graphics.interfaces import hevs.graphics.FunGraphics import hevs.graphics.utils.GraphicsBitmap -import java.awt.Color -import java.awt.Polygon -import java.awt.Rectangle + +import java.awt.geom.Rectangle2D +import java.awt.{Color, Font, Polygon, Rectangle} /** @@ -181,6 +181,71 @@ trait Graphics { */ def drawFilledOval(posX: Int, posY: Int, width: Int, height: Int): Unit + /** + * Computes the size necessary to render a string with the given font + * @param str + * the string + * @param font + * the font + * @return the bounding box of the rendered string + */ + def getStringSize(str: String, font: Font): Rectangle2D + + + /** + * Computes the size necessary to render a string with the current font + * @param str + * the string + * @return the bounding box of the rendered string + */ + def getStringSize(str: String): Rectangle2D + + /** + * Draws a string at a given location with the given font and color. Note that the boundaries are not + * checked and text may be painted outside the window + * @param posX + * X position of string + * @param posY + * Y position of string + * @param str + * the string to write + * @param font + * the font + * @param color + * the text color + */ + def drawString(posX: Int, + posY: Int, + str: String, + font: Font, + color: Color): Unit + + /** + * Draws a string at a given location with the given font, color and alignments. Note that the boundaries are not + * checked and text may be painted outside the window + * @param posX + * X position of string + * @param posY + * Y position of string + * @param str + * the string to write + * @param font + * the font + * @param color + * the text color + * @param halign + * the horizontal alignment (see {@link javax.swing.SwingConstants}) + * @param valign + * the vertical alignment (see {@link javax.swing.SwingConstants}) + */ + def drawString(posX: Int, + posY: Int, + str: String, + font: Font, + color: Color, + halign: Int, + valign: Int): Unit + /** * Draws a string at a given location. Note that the boundaries are not * checked and text may be painted outside the window @@ -191,8 +256,28 @@ trait Graphics { * Y position of string * @param str * the string to write + * @param fontFamily + * the font family + * @param fontStyle + * the font style (plain, bold, italics, ...) + * @param fontSize + * the font size + * @param color + * the text color + * @param halign + * the horizontal alignment (see {@link javax.swing.SwingConstants}) + * @param valign + * the vertical alignment (see {@link javax.swing.SwingConstants}) */ - def drawString(posX: Int, posY: Int, str: String): Unit + def drawString(posX: Int, + posY: Int, + str: String, + fontFamily: String, + fontStyle: Int, + fontSize: Int, + color: Color, + halign: Int, + valign: Int): Unit /** * Write the given string at posX, posY @@ -221,6 +306,41 @@ trait Graphics { */ def drawFancyString(posX: Int, posY: Int, str: String, color: Color, size: Int): Unit + /** + * Draws a text with a shadow or outline + * @param posX + * X position of the string + * @param posY + * Y position of the string + * @param str + * the string to draw + * @param fontFamily + * the font family + * @param fontStyle + * the font style (plain, bold, italics, ...) + * @param fontSize + * the font size + * @param color + * the text color + * @param halign + * the horizontal alignment (see {@link javax.swing.SwingConstants}) + * @param valign + * the vertical alignment (see {@link javax.swing.SwingConstants}) + * @param shadowX + * the shadow's X offset + * @param shadowY + * the shadow's Y offset + * @param shadowColor + * the shadow color + * @param shadowThickness + * the shadow thickness + * @param outlineColor + * the outline color + * @param outlineThickness + * the outline thickness + */ + def drawFancyString(posX: Int, posY: Int, str: String, fontFamily: String, fontStyle: Int, fontSize: Int, color: Color, halign: Int, valign: Int, shadowX: Int, shadowY: Int, shadowColor: Color, shadowThickness: Int, outlineColor: Color, outlineThickness: Int): Unit + /** * Draw a centered picture from a file (gif, jpg, png) to (posX, posY) * @@ -289,4 +409,10 @@ trait Graphics { * @return the frame height */ def getFrameHeight(): Int + + /** + * Returns a list of available font names on the device + * @return the list of available font names + */ + def getAvailableFonts(): Array[String] } \ No newline at end of file diff --git a/src/main/scala/hevs/graphics/samples/TestDrawString.scala b/src/main/scala/hevs/graphics/samples/TestDrawString.scala new file mode 100644 index 0000000..4d0a755 --- /dev/null +++ b/src/main/scala/hevs/graphics/samples/TestDrawString.scala @@ -0,0 +1,99 @@ +package hevs.graphics.samples + +import hevs.graphics.FunGraphics + +import java.awt.{Color, Font} +import java.awt.geom.Rectangle2D +import javax.swing.SwingConstants + +/** + * Sample for drawString methods + * + * @author Louis Heredero + */ +object TestDrawString extends App { + val HALIGNMENTS: Array[Int] = Array( + SwingConstants.LEFT, + SwingConstants.CENTER, + SwingConstants.RIGHT + ) + val VALIGNMENTS: Array[Int] = Array( + SwingConstants.TOP, + SwingConstants.CENTER, + SwingConstants.BOTTOM + ) + val fg: FunGraphics = new FunGraphics(600, 600, "Test of drawString") + + for (y: Int <- 0 until 3) { + for (x: Int <- 0 until 3) { + val posX: Int = 25 + x * 100 + val posY: Int = 25 + y * 100 + + fg.setColor(Color.BLACK) + fg.drawString(posX, posY, "Test", halign = HALIGNMENTS(x), valign = VALIGNMENTS(y)) + fg.setColor(Color.RED) + fg.drawFilledCircle(posX-2, posY-2, 2) + } + } + + fg.setColor(Color.BLACK) + for (y: Int <- 0 until 3) { + for (x: Int <- 0 until 3) { + val posX: Int = 350 + x * 100 + val posY: Int = 25 + y * 100 + + fg.drawFancyString( + posX, + posY, + "Test", + halign = SwingConstants.CENTER, + valign = SwingConstants.CENTER, + shadowX = (x-1)*2, + shadowY = (y-1)*2, + shadowThickness = 1, + fontFamily = "Arial", + fontStyle = Font.BOLD + ) + } + } + + + for (y: Int <- 0 until 3) { + for (x: Int <- 0 until 3) { + val posX: Int = 25 + x * 100 + val posY: Int = 350 + y * 100 + + fg.drawFancyString( + posX, + posY, + "Test", + halign = SwingConstants.CENTER, + valign = SwingConstants.CENTER, + shadowThickness = x+y*3, + fontFamily = "Arial", + fontStyle = Font.BOLD + ) + } + } + + + for (y: Int <- 0 until 3) { + for (x: Int <- 0 until 3) { + val posX: Int = 350 + x * 100 + val posY: Int = 350 + y * 100 + + fg.drawFancyString( + posX, + posY, + "Test", + color = Color.WHITE, + halign = SwingConstants.CENTER, + valign = SwingConstants.CENTER, + outlineThickness = x+y*3, + outlineColor = Color.BLACK, + fontFamily = "Arial", + fontStyle = Font.BOLD + ) + } + } +}