comparison 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
comparison
equal deleted inserted replaced
-1:000000000000 0:e0efe7848130
1 /*
2 * Represents a file being edited (rotated)
3 */
4 package name.blackcap.imageprep
5
6 import java.awt.Graphics2D
7 import java.awt.RenderingHints
8 import java.awt.image.BufferedImage
9 import java.io.File
10 import java.io.IOException
11 import javax.imageio.ImageIO
12 import javax.swing.*
13 import kotlin.math.*
14
15 class RotateDialog(val file: File, initialImage: BufferedImage) : JDialog(Application.mainFrame) {
16 private val BW = 9
17 private val BW2 = BW * 2
18 private val MINWIDTH = 512
19 private val MINHEIGHT = 384
20
21 private class DrawingPane() : JPanel() {
22 var image: BufferedImage = initialImage
23 set(value) {
24 field = value
25 revalidate()
26 repaint()
27 }
28
29 override val preferredSize
30 get { return image.preferredSize }
31
32 protected paintComponent(g: Graphics): Unit {
33 g.drawImage(image, 0, 0, null)
34 }
35 }
36 private val drawingPane = DrawingPane()
37
38 val image: BufferedImage
39 get() { return drawingPane.image }
40
41 private val r90cw = JButton("90° CW").apply {
42 it.addActionListener(ActionListener { doRotate(90) })
43 }
44
45 private val r180 = JButton("180°").apply {
46 it.addActionListener(ActionListener { doRotate(180) })
47 }
48
49 private val r90ccw = JButton("90° CCW").apply {
50 it.addActionListener(ActionListener { doRotate(270) })
51 }
52
53 /* Theoretically, this should do the rotation in a background thread.
54 Practically, that is fraught with difficulties, as it involves
55 manipulating data used by Swing itself. Since the size of the images
56 being rotated are small, we do it in the foreground. */
57 private fun doRotate(deg: Int) {
58 // https://stackoverflow.com/questions/15927014/rotating-an-image-90-degrees-in-java
59 val rad = java.lang.Math.toRadians(deg.toDouble())
60 val sinx = sin(rad)
61 val cosx = cos(rad)
62 val w = floor(image.width * cosx + image.height * sinx).toInt()
63 val h = floor(image.height * cosx + image.width * sinx).toInt()
64 val rotatedImage = BufferedImage(w, h, image.type)
65 rotatedImage.createGraphics().run {
66 translate((w - image.width) / 2, (h - image.height) / 2)
67 rotate(rad, image.width / 2, image.height / 2)
68 drawRenderedImage(image, null)
69 dispose()
70 }
71 drawingPane.image = rotatedImage
72 }
73
74 init {
75 defaultCloseOperation = JDialog.DISPOSE_ON_CLOSE
76 title = "Untitled"
77 contentPane.apply {
78 layout = BoxLayout(this, BoxLayout.Y_AXIS)
79 add(JScrollpane(drawingPane).apply {
80 alignmentX = JScrollPane.CENTER_ALIGNMENT
81 addBorder(BorderFactory.createEmptyBorder(BW2, BW2, BW, BW2))
82 verticalScrollBarPolicy = ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS
83 horizontalScrollBarPolicy = ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS
84 background = Application.mainFrame.background
85 })
86 add(Box(BoxLayout.X_AXIS).apply {
87 alignmentX = Box.CENTER_ALIGNMENT
88 addBorder(BorderFactory.createEmptyBorder(BW, BW2, BW2, BW2))
89 add(JLabel("Rotate:"))
90 add(Box.createHorizontalGlue())
91 add(r90cw)
92 add(Box.createHorizontalGlue())
93 add(r180)
94 add(Box.createHorizontalGlue())
95 add(r90ccw)
96 })
97 }
98 pack()
99 }
100
101 companion object Factory {
102 /**
103 * Make a dialog asynchronously.
104 *
105 * @param input java.io.File to read for image.
106 */
107 fun makeDialog(input: File): Unit {
108 Application.mainFrame.useWaitCursor()
109 swingWorker<Pair<BufferedImage?, IOException?> {
110 inBackground {
111 try {
112 val imageIn = ImageIO.read(input) /* IOException */
113 val ratio = Settings.maxDimension.toDouble() / max(imageIn.width, imageIn.height).toDouble()
114 if (ratio >= 1.0) {
115 null
116 } else {
117 val nWidth = (imageIn.width * ratio).toInt()
118 val nHeight = (imageIn.height * ratio).toInt()
119 val imageOut = BufferedImage(nWidth, nHeight, BufferedImage.TYPE_INT_RGB)
120 val graphics = imageOut.createGraphics().apply {
121 setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR)
122 setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY)
123 setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON)
124 }
125 graphics.drawImage(imageIn, 0, 0, nWidth, nHeight, null)
126 Pair(imageOut, null)
127 }
128 } catch (e: IOException) {
129 Pair(null, e)
130 }
131 }
132 whenDone {
133 Application.mainFrame.useNormalCursor()
134 val (image, error) = get()
135 if (error != null)
136 ioExceptionDialog(Application.mainFrame, input, "read", error)
137 if (image != null)
138 RotateDialog(input, image).title = input.getName()
139 }
140 }
141 }
142 }
143 }