2022-03-18 23:57:35 +00:00
package prog8.vm
import java.awt.*
import java.awt.image.BufferedImage
import javax.swing.JFrame
import javax.swing.JPanel
import javax.swing.Timer
import kotlin.math.max
import kotlin.math.min
2022-03-23 00:52:01 +00:00
class GraphicsWindow ( val pixelWidth : Int , val pixelHeight : Int , val pixelScaling : Int ) : JFrame ( " Prog8 VM Graphics Screen $pixelWidth × $pixelHeight " ) , AutoCloseable {
2022-03-18 23:57:35 +00:00
private lateinit var repaintTimer : Timer
fun start ( ) {
val refreshRate = optimalRefreshRate ( this )
repaintTimer = Timer ( 1000 / refreshRate ) {
repaint ( )
}
repaintTimer . initialDelay = 0
repaintTimer . start ( )
}
override fun close ( ) {
repaintTimer . stop ( )
dispose ( )
}
val image : BufferedImage
init {
contentPane . layout = BorderLayout ( )
background = Color . BLACK
contentPane . background = Color . BLACK
isResizable = false
isLocationByPlatform = true
2022-04-03 15:33:50 +00:00
defaultCloseOperation = EXIT _ON _CLOSE
2022-03-18 23:57:35 +00:00
image = graphicsConfiguration . createCompatibleImage ( pixelWidth , pixelHeight , Transparency . OPAQUE )
contentPane . add ( BitmapScreenPanel ( image , pixelScaling ) , BorderLayout . CENTER )
pack ( )
requestFocusInWindow ( )
isVisible = true
}
private fun optimalRefreshRate ( frame : JFrame ) : Int {
var rate = frame . graphicsConfiguration . device . displayMode . refreshRate
if ( rate == 0 )
2022-03-19 00:08:10 +00:00
rate = GraphicsEnvironment . getLocalGraphicsEnvironment ( ) . screenDevices
. map { it . displayMode . refreshRate }
. firstOrNull { it > 0 } ?: 60
2022-03-18 23:57:35 +00:00
return max ( 30 , min ( 250 , rate ) )
}
fun clear ( color : Int ) {
val g2d = image . graphics as Graphics2D
g2d . background = Color ( color , color , color )
g2d . clearRect ( 0 , 0 , pixelWidth * pixelScaling , pixelHeight * pixelScaling )
g2d . dispose ( )
}
fun plot ( x : Int , y : Int , color : Int ) {
if ( x < 0 || x >= pixelWidth )
throw IllegalArgumentException ( " plot x outside of screen: $x " )
if ( y < 0 || y >= pixelHeight )
throw IllegalArgumentException ( " plot y outside of screen: $y " )
image . setRGB ( x , y , Color ( color , color , color , 255 ) . rgb )
}
2022-07-02 22:41:04 +00:00
fun getpixel ( x : Int , y : Int ) : Int {
if ( x < 0 || x >= pixelWidth )
2023-11-04 12:51:18 +00:00
throw IllegalArgumentException ( " getpixel x outside of screen: $x " )
2022-07-02 22:41:04 +00:00
if ( y < 0 || y >= pixelHeight )
2023-11-04 12:51:18 +00:00
throw IllegalArgumentException ( " getpixel y outside of screen: $y " )
2022-07-02 22:41:04 +00:00
return image . getRGB ( x , y )
}
2022-03-18 23:57:35 +00:00
}
2023-07-30 16:42:45 +00:00
internal class BitmapScreenPanel ( private val drawImage : BufferedImage , pixelScaling : Int ) : JPanel ( ) {
2022-03-18 23:57:35 +00:00
init {
val size = Dimension ( drawImage . width * pixelScaling , drawImage . height * pixelScaling )
minimumSize = size
maximumSize = size
preferredSize = size
isFocusable = true
isDoubleBuffered = false
requestFocusInWindow ( )
}
override fun paint ( graphics : Graphics ) {
val g2d = graphics as Graphics2D
2023-04-11 20:28:19 +00:00
g2d . setRenderingHint ( RenderingHints . KEY _INTERPOLATION , RenderingHints . VALUE _INTERPOLATION _NEAREST _NEIGHBOR )
2022-03-18 23:57:35 +00:00
g2d . drawImage ( drawImage , 0 , 0 , size . width , size . height , null )
Toolkit . getDefaultToolkit ( ) . sync ( )
}
}