From c7e835c2211f0eaa8a3a05983594289408a851d8 Mon Sep 17 00:00:00 2001 From: LordBaryhobal Date: Tue, 9 Jan 2024 14:59:49 +0100 Subject: [PATCH 1/5] added text alignments to drawString methods + improved drawFancyString --- .../scala/hevs/graphics/FunGraphics.scala | 105 ++++++++++++--- .../hevs/graphics/interfaces/Graphics.scala | 124 +++++++++++++++++- 2 files changed, 208 insertions(+), 21 deletions(-) diff --git a/src/main/scala/hevs/graphics/FunGraphics.scala b/src/main/scala/hevs/graphics/FunGraphics.scala index b7b1bcc..0e656c7 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,16 @@ 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 = { + font.getStringBounds(str, g2d.getFontRenderContext) } - 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 +300,86 @@ 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 = Color.BLACK, + halign: Int = SwingConstants.LEFT, + valign: Int = SwingConstants.BOTTOM): Unit = { + + val bounds: Rectangle2D = getStringSize(str, font) + val w: Double = bounds.getWidth + val h: Double = bounds.getHeight + + var x: Int = posX + var y: Int = posY + + if (halign == SwingConstants.CENTER) { + x -= w/2 + } else if (halign == SwingConstants.RIGHT) { + x -= w + } + + if (valign == SwingConstants.CENTER) { + y += h/2 + } else if (valign == SwingConstants.TOP) { + y += h + } + + drawString(x, y, 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): 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 + + val font2: Font = new Font(fontFamily, fontStyle, fontSize+shadowThickness) + drawString(math.round(posX + w/2 + shadowX).toInt, math.round(posY + h/2 + shadowY).toInt, str, font2, shadowColor, SwingConstants.CENTER, SwingConstants.CENTER) + } + + drawString(posX, posY, str, font, color, halign, valign) } override def drawPicture(posX: Int, posY: Int, bitmap: GraphicsBitmap): Unit = { diff --git a/src/main/scala/hevs/graphics/interfaces/Graphics.scala b/src/main/scala/hevs/graphics/interfaces/Graphics.scala index 2fbb719..075ce9f 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,37 @@ trait Graphics { */ def drawFancyString(posX: Int, posY: Int, str: String, color: Color, size: Int): Unit + /** + * Draws a text with a shadow + * @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 + */ + 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): Unit + /** * Draw a centered picture from a file (gif, jpg, png) to (posX, posY) * From 3a28c8f3523899765159e13febf6ce72e9950cc1 Mon Sep 17 00:00:00 2001 From: LordBaryhobal Date: Tue, 9 Jan 2024 18:34:59 +0100 Subject: [PATCH 2/5] fixed text positioning and conflicting overloads --- .../scala/hevs/graphics/FunGraphics.scala | 45 ++++++++++++++----- 1 file changed, 35 insertions(+), 10 deletions(-) diff --git a/src/main/scala/hevs/graphics/FunGraphics.scala b/src/main/scala/hevs/graphics/FunGraphics.scala index 0e656c7..21d1721 100644 --- a/src/main/scala/hevs/graphics/FunGraphics.scala +++ b/src/main/scala/hevs/graphics/FunGraphics.scala @@ -284,7 +284,10 @@ class FunGraphics(val width: Int, val height: Int, val xoffset: Int, val yoffset } override def getStringSize(str: String, font: Font): Rectangle2D = { - font.getStringBounds(str, g2d.getFontRenderContext) + 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 getStringSize(str: String): Rectangle2D = getStringSize(str, g2d.getFont) @@ -304,30 +307,30 @@ class FunGraphics(val width: Int, val height: Int, val xoffset: Int, val yoffset posY: Int, str: String, font: Font, - color: Color = Color.BLACK, - halign: Int = SwingConstants.LEFT, - valign: Int = SwingConstants.BOTTOM): Unit = { + 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: Int = posX - var y: Int = posY + var x: Double = posX + var y: Double = posY if (halign == SwingConstants.CENTER) { - x -= w/2 + x -= w / 2.0 } else if (halign == SwingConstants.RIGHT) { x -= w } if (valign == SwingConstants.CENTER) { - y += h/2 + y += h / 2.0 } else if (valign == SwingConstants.TOP) { y += h } - drawString(x, y, str, font, color) + drawString(math.round(x).toInt, math.round(y).toInt, str, font, color) } override def drawString(posX: Int, @@ -375,8 +378,30 @@ class FunGraphics(val width: Int, val height: Int, val xoffset: Int, val yoffset 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(posX + w/2 + shadowX).toInt, math.round(posY + h/2 + shadowY).toInt, str, font2, shadowColor, SwingConstants.CENTER, SwingConstants.CENTER) + drawString( + math.round(cx + shadowX).toInt, + math.round(cy + shadowY).toInt, + str, + font2, + shadowColor, + SwingConstants.CENTER, + SwingConstants.CENTER) } drawString(posX, posY, str, font, color, halign, valign) From 4ff9e8d612cf8ee63d3ada87d2faaa838b779051 Mon Sep 17 00:00:00 2001 From: LordBaryhobal Date: Tue, 9 Jan 2024 18:35:30 +0100 Subject: [PATCH 3/5] added samples for string drawing methods --- .../graphics/samples/TestDrawString.scala | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 src/main/scala/hevs/graphics/samples/TestDrawString.scala 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..1a1599e --- /dev/null +++ b/src/main/scala/hevs/graphics/samples/TestDrawString.scala @@ -0,0 +1,78 @@ +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 + ) + } + } +} From 79807706de3f2c7759697114ce92f9373cb14d9b Mon Sep 17 00:00:00 2001 From: LordBaryhobal Date: Tue, 9 Jan 2024 21:08:41 +0100 Subject: [PATCH 4/5] added outlines to drawFancyString --- .../scala/hevs/graphics/FunGraphics.scala | 12 ++++++++++- .../hevs/graphics/interfaces/Graphics.scala | 8 +++++-- .../graphics/samples/TestDrawString.scala | 21 +++++++++++++++++++ 3 files changed, 38 insertions(+), 3 deletions(-) diff --git a/src/main/scala/hevs/graphics/FunGraphics.scala b/src/main/scala/hevs/graphics/FunGraphics.scala index 21d1721..0b70e15 100644 --- a/src/main/scala/hevs/graphics/FunGraphics.scala +++ b/src/main/scala/hevs/graphics/FunGraphics.scala @@ -369,7 +369,9 @@ class FunGraphics(val width: Int, val height: Int, val xoffset: Int, val yoffset shadowX: Int = 0, shadowY: Int = 0, shadowColor: Color = Color.GRAY, - shadowThickness: Int = 0): Unit = { + shadowThickness: Int = 0, + outlineColor: Color = Color.WHITE, + outlineThickness: Int = 0): Unit = { val font: Font = new Font(fontFamily, fontStyle, fontSize) @@ -404,6 +406,14 @@ class FunGraphics(val width: Int, val height: Int, val xoffset: Int, val yoffset 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) } diff --git a/src/main/scala/hevs/graphics/interfaces/Graphics.scala b/src/main/scala/hevs/graphics/interfaces/Graphics.scala index 075ce9f..c4c5fa5 100644 --- a/src/main/scala/hevs/graphics/interfaces/Graphics.scala +++ b/src/main/scala/hevs/graphics/interfaces/Graphics.scala @@ -307,7 +307,7 @@ trait Graphics { def drawFancyString(posX: Int, posY: Int, str: String, color: Color, size: Int): Unit /** - * Draws a text with a shadow + * Draws a text with a shadow or outline * @param posX * X position of the string * @param posY @@ -334,8 +334,12 @@ trait Graphics { * 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): Unit + 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) diff --git a/src/main/scala/hevs/graphics/samples/TestDrawString.scala b/src/main/scala/hevs/graphics/samples/TestDrawString.scala index 1a1599e..4d0a755 100644 --- a/src/main/scala/hevs/graphics/samples/TestDrawString.scala +++ b/src/main/scala/hevs/graphics/samples/TestDrawString.scala @@ -75,4 +75,25 @@ object TestDrawString extends App { ) } } + + + 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 + ) + } + } } From e0009f00fde06aa657662295089a6b4382d22c4e Mon Sep 17 00:00:00 2001 From: LordBaryhobal Date: Thu, 11 Jan 2024 10:25:05 +0100 Subject: [PATCH 5/5] added getAvailableFonts --- src/main/scala/hevs/graphics/FunGraphics.scala | 2 ++ src/main/scala/hevs/graphics/interfaces/Graphics.scala | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/src/main/scala/hevs/graphics/FunGraphics.scala b/src/main/scala/hevs/graphics/FunGraphics.scala index 0b70e15..3b228dc 100644 --- a/src/main/scala/hevs/graphics/FunGraphics.scala +++ b/src/main/scala/hevs/graphics/FunGraphics.scala @@ -453,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 c4c5fa5..70ab847 100644 --- a/src/main/scala/hevs/graphics/interfaces/Graphics.scala +++ b/src/main/scala/hevs/graphics/interfaces/Graphics.scala @@ -409,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