Mercurial > cgi-bin > hgweb.cgi > ImagePrep
diff src/name/blackcap/imageprep/RotateDialog.kt @ 0:e0efe7848130
Initial commit. Untested!
author | David Barts <davidb@stashtea.com> |
---|---|
date | Thu, 16 Jul 2020 19:57:23 -0700 |
parents | |
children | 0bded24f746e |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/name/blackcap/imageprep/RotateDialog.kt Thu Jul 16 19:57:23 2020 -0700 @@ -0,0 +1,143 @@ +/* + * Represents a file being edited (rotated) + */ +package name.blackcap.imageprep + +import java.awt.Graphics2D +import java.awt.RenderingHints +import java.awt.image.BufferedImage +import java.io.File +import java.io.IOException +import javax.imageio.ImageIO +import javax.swing.* +import kotlin.math.* + +class RotateDialog(val file: File, initialImage: BufferedImage) : JDialog(Application.mainFrame) { + private val BW = 9 + private val BW2 = BW * 2 + private val MINWIDTH = 512 + private val MINHEIGHT = 384 + + private class DrawingPane() : JPanel() { + var image: BufferedImage = initialImage + set(value) { + field = value + revalidate() + repaint() + } + + override val preferredSize + get { return image.preferredSize } + + protected paintComponent(g: Graphics): Unit { + g.drawImage(image, 0, 0, null) + } + } + private val drawingPane = DrawingPane() + + val image: BufferedImage + get() { return drawingPane.image } + + private val r90cw = JButton("90° CW").apply { + it.addActionListener(ActionListener { doRotate(90) }) + } + + private val r180 = JButton("180°").apply { + it.addActionListener(ActionListener { doRotate(180) }) + } + + private val r90ccw = JButton("90° CCW").apply { + it.addActionListener(ActionListener { doRotate(270) }) + } + + /* Theoretically, this should do the rotation in a background thread. + Practically, that is fraught with difficulties, as it involves + manipulating data used by Swing itself. Since the size of the images + being rotated are small, we do it in the foreground. */ + private fun doRotate(deg: Int) { + // https://stackoverflow.com/questions/15927014/rotating-an-image-90-degrees-in-java + val rad = java.lang.Math.toRadians(deg.toDouble()) + val sinx = sin(rad) + val cosx = cos(rad) + val w = floor(image.width * cosx + image.height * sinx).toInt() + val h = floor(image.height * cosx + image.width * sinx).toInt() + val rotatedImage = BufferedImage(w, h, image.type) + rotatedImage.createGraphics().run { + translate((w - image.width) / 2, (h - image.height) / 2) + rotate(rad, image.width / 2, image.height / 2) + drawRenderedImage(image, null) + dispose() + } + drawingPane.image = rotatedImage + } + + init { + defaultCloseOperation = JDialog.DISPOSE_ON_CLOSE + title = "Untitled" + contentPane.apply { + layout = BoxLayout(this, BoxLayout.Y_AXIS) + add(JScrollpane(drawingPane).apply { + alignmentX = JScrollPane.CENTER_ALIGNMENT + addBorder(BorderFactory.createEmptyBorder(BW2, BW2, BW, BW2)) + verticalScrollBarPolicy = ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS + horizontalScrollBarPolicy = ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS + background = Application.mainFrame.background + }) + add(Box(BoxLayout.X_AXIS).apply { + alignmentX = Box.CENTER_ALIGNMENT + addBorder(BorderFactory.createEmptyBorder(BW, BW2, BW2, BW2)) + add(JLabel("Rotate:")) + add(Box.createHorizontalGlue()) + add(r90cw) + add(Box.createHorizontalGlue()) + add(r180) + add(Box.createHorizontalGlue()) + add(r90ccw) + }) + } + pack() + } + + companion object Factory { + /** + * Make a dialog asynchronously. + * + * @param input java.io.File to read for image. + */ + fun makeDialog(input: File): Unit { + Application.mainFrame.useWaitCursor() + swingWorker<Pair<BufferedImage?, IOException?> { + inBackground { + try { + val imageIn = ImageIO.read(input) /* IOException */ + val ratio = Settings.maxDimension.toDouble() / max(imageIn.width, imageIn.height).toDouble() + if (ratio >= 1.0) { + null + } else { + val nWidth = (imageIn.width * ratio).toInt() + val nHeight = (imageIn.height * ratio).toInt() + val imageOut = BufferedImage(nWidth, nHeight, BufferedImage.TYPE_INT_RGB) + val graphics = imageOut.createGraphics().apply { + setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR) + setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY) + setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON) + } + graphics.drawImage(imageIn, 0, 0, nWidth, nHeight, null) + Pair(imageOut, null) + } + } catch (e: IOException) { + Pair(null, e) + } + } + whenDone { + Application.mainFrame.useNormalCursor() + val (image, error) = get() + if (error != null) + ioExceptionDialog(Application.mainFrame, input, "read", error) + if (image != null) + RotateDialog(input, image).title = input.getName() + } + } + } + } +}