1+ package org.polyfrost.polyplus.client.utils
2+
3+
4+ import dev.deftu.omnicore.api.client.OmniDesktop
5+ import java.awt.image.BufferedImage
6+ import java.io.File
7+ import java.io.IOException
8+ import java.io.InputStream
9+ import java.nio.ByteBuffer
10+ import javax.imageio.ImageIO
11+
12+
13+ /* ****************************************************************************
14+ * A convenience class for loading icons from images.
15+ *
16+ * Icons loaded from this class are formatted to fit within the required
17+ * dimension (16x16, 32x32, or 128x128). If the source image is larger than the
18+ * target dimension, it is shrunk down to the minimum size that will fit. If it
19+ * is smaller, then it is only scaled up if the new scale can be a per-pixel
20+ * linear scale (i.e., x2, x3, x4, etc). In both cases, the image's width/height
21+ * ratio is kept the same as the source image.
22+ *
23+ * @author Chris Molini
24+ */
25+ object IconLoader {
26+ /* ************************************************************************
27+ * Loads an icon in ByteBuffer form.
28+ *
29+ * @param filepath
30+ * The location of the Image to use as an icon.
31+ *
32+ * @return An array of ByteBuffers containing the pixel data for the icon in
33+ * varying sizes.
34+ */
35+ @JvmStatic
36+ fun load (filepath : String ): Array <ByteBuffer ?> {
37+ return load(File (filepath))
38+ }
39+
40+ /* ************************************************************************
41+ * Loads an icon in ByteBuffer form.
42+ *
43+ * @param fil
44+ * A File pointing to the image.
45+ *
46+ * @return An array of ByteBuffers containing the pixel data for the icon in
47+ * various sizes (as recommended by the OS).
48+ */
49+ @JvmStatic
50+ fun load (fil : File ): Array <ByteBuffer ?> {
51+ val image: BufferedImage
52+ try {
53+ image = ImageIO .read(fil)
54+ } catch (e: IOException ) {
55+ e.printStackTrace()
56+ return arrayOfNulls(2 )
57+ }
58+
59+ return load(image)
60+ }
61+
62+ @JvmStatic
63+ fun load (image : BufferedImage ): Array <ByteBuffer ?> {
64+ val buffers: Array <ByteBuffer ?>
65+ if (OmniDesktop .isWindows) {
66+ buffers = arrayOfNulls(2 )
67+ buffers[0 ] = loadInstance(image, 16 )
68+ buffers[1 ] = loadInstance(image, 32 )
69+ } else if (OmniDesktop .isMac) {
70+ buffers = arrayOfNulls(1 )
71+ buffers[0 ] = loadInstance(image, 128 )
72+ } else {
73+ buffers = arrayOfNulls(1 )
74+ buffers[0 ] = loadInstance(image, 32 )
75+ }
76+ return buffers
77+ }
78+
79+ /* ************************************************************************
80+ * Copies the supplied image into a square icon at the indicated size.
81+ *
82+ * @param image
83+ * The image to place onto the icon.
84+ * @param dimension
85+ * The desired size of the icon.
86+ *
87+ * @return A ByteBuffer of pixel data at the indicated size.
88+ */
89+ private fun loadInstance (image : BufferedImage , dimension : Int ): ByteBuffer {
90+ val scaledIcon = BufferedImage (dimension, dimension, BufferedImage .TYPE_INT_ARGB_PRE )
91+ val g = scaledIcon.createGraphics()
92+ val ratio = getIconRatio(image, scaledIcon)
93+ val width = image.width * ratio
94+ val height = image.height * ratio
95+ g.drawImage(
96+ image,
97+ ((scaledIcon.width - width) / 2 ).toInt(),
98+ ((scaledIcon.height - height) / 2 ).toInt(),
99+ (width).toInt(),
100+ (height).toInt(),
101+ null
102+ )
103+ g.dispose()
104+
105+ return convertToByteBuffer(scaledIcon)
106+ }
107+
108+ /* ************************************************************************
109+ * Gets the width/height ratio of the icon. This is meant to simplify
110+ * scaling the icon to a new dimension.
111+ *
112+ * @param src
113+ * The base image that will be placed onto the icon.
114+ * @param icon
115+ * The icon that will have the image placed on it.
116+ *
117+ * @return The amount to scale the source image to fit it onto the icon
118+ * appropriately.
119+ */
120+ private fun getIconRatio (src : BufferedImage , icon : BufferedImage ): Double {
121+ var ratio: Double
122+ if (src.width > icon.width) ratio = (icon.width).toDouble() / src.width
123+ else ratio = (icon.width.toFloat() / src.width).toDouble()
124+ if (src.height > icon.height) {
125+ val r2 = (icon.height).toDouble() / src.height
126+ if (r2 < ratio) ratio = r2
127+ } else {
128+ val r2 = (icon.height.toFloat() / src.height).toDouble()
129+ if (r2 < ratio) ratio = r2
130+ }
131+ return ratio
132+ }
133+
134+ /* ************************************************************************
135+ * Converts a BufferedImage into a ByteBuffer of pixel data.
136+ *
137+ * @param image
138+ * The image to convert.
139+ *
140+ * @return A ByteBuffer that contains the pixel data of the supplied image.
141+ */
142+ fun convertToByteBuffer (image : BufferedImage ): ByteBuffer {
143+ val buffer = ByteArray (image.width * image.height * 4 )
144+ var counter = 0
145+ for (i in 0 .. < image.height) for (j in 0 .. < image.width) {
146+ val colorSpace = image.getRGB(j, i)
147+ buffer[counter] = ((colorSpace shl 8 ) shr 24 ).toByte()
148+ buffer[counter + 1 ] = ((colorSpace shl 16 ) shr 24 ).toByte()
149+ buffer[counter + 2 ] = ((colorSpace shl 24 ) shr 24 ).toByte()
150+ buffer[counter + 3 ] = (colorSpace shr 24 ).toByte()
151+ counter + = 4
152+ }
153+ return ByteBuffer .wrap(buffer)
154+ }
155+ }
0 commit comments