package KondoKit import KondoKit.plugin.Companion.FIXED_HEIGHT import KondoKit.plugin.Companion.FIXED_WIDTH import plugin.api.API.IsHD import rt4.GameShell.canvas import rt4.GlRenderer import rt4.SoftwareRaster import java.awt.* import java.awt.event.* import java.awt.geom.AffineTransform import java.awt.image.AffineTransformOp import java.awt.image.BufferedImage import java.awt.image.VolatileImage class AltCanvas : Canvas() { private var gameImage: VolatileImage? = null private var op: AffineTransformOp? = null private var transform: AffineTransform? = null private var flippedImage: BufferedImage? = null private var bufferImage = BufferedImage(FIXED_WIDTH, FIXED_HEIGHT, BufferedImage.TYPE_INT_BGR) private var lastImageWidth = -1 private var lastImageHeight = -1 init { isFocusable = true requestFocusInWindow() addMouseListener(object : MouseAdapter() { override fun mousePressed(e: MouseEvent) = relayMouseEvent(e) override fun mouseReleased(e: MouseEvent) = relayMouseEvent(e) override fun mouseClicked(e: MouseEvent) = relayMouseEvent(e) }) addMouseMotionListener(object : MouseMotionAdapter() { override fun mouseMoved(e: MouseEvent) = relayMouseEvent(e) override fun mouseDragged(e: MouseEvent) = relayMouseEvent(e) }) addKeyListener(object : KeyAdapter() { override fun keyPressed(e: KeyEvent) = relayKeyEvent { it.keyPressed(e) } override fun keyReleased(e: KeyEvent) = relayKeyEvent { it.keyReleased(e) } override fun keyTyped(e: KeyEvent) = relayKeyEvent { it.keyTyped(e) } }) addMouseWheelListener(MouseWheelListener { e -> relayMouseWheelEvent(e) }) } override fun update(g: Graphics) = paint(g) override fun addNotify() { super.addNotify() createBufferStrategy(2) // Double-buffering validateGameImage() } private fun validateGameImage() { val gc = GraphicsEnvironment.getLocalGraphicsEnvironment().defaultScreenDevice.defaultConfiguration gameImage?.let { when (it.validate(gc)) { VolatileImage.IMAGE_INCOMPATIBLE -> createGameImage(gc) VolatileImage.IMAGE_RESTORED -> renderGameImage() } } ?: createGameImage(gc) } private fun createGameImage(gc: GraphicsConfiguration) { gameImage = gc.createCompatibleVolatileImage(FIXED_WIDTH, FIXED_HEIGHT, Transparency.OPAQUE) renderGameImage() } private fun renderGameImage() { gameImage?.createGraphics()?.apply { color = Color.DARK_GRAY fillRect(0, 0, gameImage!!.width, gameImage!!.height) color = Color.BLACK drawString("KondoKit Scaled Fixed Canvas", 20, 20) dispose() } } override fun paint(g: Graphics) { bufferStrategy?.let { strategy -> val g2d = strategy.drawGraphics as? Graphics2D ?: return g2d.color = Color.BLACK g2d.fillRect(0, 0, width, height) gameImage?.let { image -> val scale = minOf(width.toDouble() / image.width, height.toDouble() / image.height) val x = ((width - image.width * scale) / 2).toInt() val y = ((height - image.height * scale) / 2).toInt() g2d.drawImage(image, x, y, (image.width * scale).toInt(), (image.height * scale).toInt(), null) } g2d.dispose() // Release the graphics context strategy.show() // Display the buffer } } private fun relayMouseEvent(e: MouseEvent) { requestFocusInWindow() val scale = minOf(width.toDouble() / gameImage!!.width, height.toDouble() / gameImage!!.height) val xOffset = ((width - gameImage!!.width * scale) / 2) val yOffset = ((height - gameImage!!.height * scale) / 2) val adjustedX = ((e.x - xOffset) / scale).toInt().coerceIn(0, gameImage!!.width - 1) val adjustedY = ((e.y - yOffset) / scale).toInt().coerceIn(0, gameImage!!.height - 1) canvas.dispatchEvent(MouseEvent(this, e.id, e.`when`, e.modifiersEx, adjustedX, adjustedY, e.clickCount, e.isPopupTrigger, e.button)) } private fun relayKeyEvent(action: (KeyListener) -> Unit) { for (listener in canvas.keyListeners) action(listener) } private fun relayMouseWheelEvent(e: MouseWheelEvent) { val scale = minOf(width.toDouble() / gameImage!!.width, height.toDouble() / gameImage!!.height) val xOffset = ((width - gameImage!!.width * scale) / 2) val yOffset = ((height - gameImage!!.height * scale) / 2) val adjustedX = ((e.x - xOffset) / scale).toInt().coerceIn(0, gameImage!!.width - 1) val adjustedY = ((e.y - yOffset) / scale).toInt().coerceIn(0, gameImage!!.height - 1) canvas.dispatchEvent(MouseWheelEvent(this, e.id, e.`when`, e.modifiersEx, adjustedX, adjustedY, e.clickCount, e.isPopupTrigger, e.scrollType, e.scrollAmount, e.wheelRotation)) } fun updateGameImage() { if (IsHD()) renderGlRaster() else renderSoftwareRaster() repaint() } private fun renderGlRaster() { val width = gameImage!!.width val height = gameImage!!.height bufferImage.setRGB(0, 0, width, height, GlRenderer.pixelData, 0, width) if (width != lastImageWidth || height != lastImageHeight) { transform = AffineTransform.getScaleInstance(1.0, -1.0).apply { translate(0.0, -height.toDouble()) } op = AffineTransformOp(transform, AffineTransformOp.TYPE_NEAREST_NEIGHBOR) flippedImage = BufferedImage(width, height, bufferImage.type) lastImageWidth = width lastImageHeight = height } op!!.filter(bufferImage, flippedImage) gameImage?.createGraphics()?.apply { drawImage(flippedImage, 0, 0, null) dispose() } } private fun renderSoftwareRaster() { gameImage?.createGraphics()?.apply { SoftwareRaster.frameBuffer.draw(this) dispose() } } }