1- package net.perfectdreams.gabrielaimageserver.webserver.utils
2-
31/*
42 * SimpleImageInfo.java
53 *
@@ -29,131 +27,165 @@ package net.perfectdreams.gabrielaimageserver.webserver.utils
2927 * -------------------------------------------------------------------------------
3028 */
3129
30+ package net.perfectdreams.gabrielaimageserver.webserver.utils
31+
3232import java.io.*
3333
3434class SimpleImageInfo {
35- var height: Int = 0
36- var width: Int = 0
37- var mimeType: String? = null
38-
39- private constructor ()
40-
41- @Throws(IOException ::class )
42- constructor (file: File ) {
43- val `is ` = FileInputStream (file)
44- try {
45- processStream(`is `)
46- } finally {
47- `is `.close()
48- }
49- }
50-
51- @Throws(IOException ::class )
52- constructor (`is `: InputStream ) {
53- processStream(`is `)
54- }
35+ var height: Int = 0
36+ var width: Int = 0
37+ var mimeType: String? = null
5538
56- @Throws(IOException ::class )
57- constructor (bytes: ByteArray ) {
58- val `is ` = ByteArrayInputStream (bytes)
59- try {
60- processStream(`is `)
61- } finally {
62- `is `.close()
63- }
64- }
65-
66- @Throws(IOException ::class )
67- private fun processStream (`is `: InputStream ) {
68- val c1 = `is `.read()
69- val c2 = `is `.read()
70- var c3 = `is `.read()
71-
72- mimeType = null
73- height = - 1
74- width = height
75-
76- if (c1 == ' G' .toInt() && c2 == ' I' .toInt() && c3 == ' F' .toInt()) { // GIF
77- `is `.skip(3 )
78- width = readInt(`is `, 2 , false )
79- height = readInt(`is `, 2 , false )
80- mimeType = " image/gif"
81- } else if (c1 == 0xFF && c2 == 0xD8 ) { // JPG
82- while (c3 == 255 ) {
83- val marker = `is `.read()
84- val len = readInt(`is `, 2 , true )
85- if (marker == 192 || marker == 193 || marker == 194 ) {
86- `is `.skip(1 )
87- height = readInt(`is `, 2 , true )
88- width = readInt(`is `, 2 , true )
89- mimeType = " image/jpeg"
90- break
91- }
92- `is `.skip((len - 2 ).toLong())
93- c3 = `is `.read()
94- }
95- } else if (c1 == 137 && c2 == 80 && c3 == 78 ) { // PNG
96- `is `.skip(15 )
97- width = readInt(`is `, 2 , true )
98- `is `.skip(2 )
99- height = readInt(`is `, 2 , true )
100- mimeType = " image/png"
101- } else if (c1 == 66 && c2 == 77 ) { // BMP
102- `is `.skip(15 )
103- width = readInt(`is `, 2 , false )
104- `is `.skip(2 )
105- height = readInt(`is `, 2 , false )
106- mimeType = " image/bmp"
107- } else {
108- val c4 = `is `.read()
109- if (c1 == ' M' .toInt() && c2 == ' M' .toInt() && c3 == 0 && c4 == 42 || c1 == ' I' .toInt() && c2 == ' I' .toInt() && c3 == 42 && c4 == 0 ) { // TIFF
110- val bigEndian = c1 == ' M' .toInt()
111- var ifd = 0
112- val entries: Int
113- ifd = readInt(`is `, 4 , bigEndian)
114- `is `.skip((ifd - 8 ).toLong())
115- entries = readInt(`is `, 2 , bigEndian)
116- for (i in 1 .. entries) {
117- val tag = readInt(`is `, 2 , bigEndian)
118- val fieldType = readInt(`is `, 2 , bigEndian)
119- val count = readInt(`is `, 4 , bigEndian).toLong()
120- val valOffset: Int
121- if (fieldType == 3 || fieldType == 8 ) {
122- valOffset = readInt(`is `, 2 , bigEndian)
123- `is `.skip(2 )
124- } else {
125- valOffset = readInt(`is `, 4 , bigEndian)
126- }
127- if (tag == 256 ) {
128- width = valOffset
129- } else if (tag == 257 ) {
130- height = valOffset
131- }
132- if (width != - 1 && height != - 1 ) {
133- mimeType = " image/tiff"
134- break
135- }
136- }
137- }
138- }
139- if (mimeType == null ) {
140- throw IOException (" Unsupported image type" )
141- }
142- }
39+ private constructor ()
14340
14441 @Throws(IOException ::class )
145- private fun readInt (`is `: InputStream , noOfBytes : Int , bigEndian : Boolean ): Int {
146- var ret = 0
147- var sv = if (bigEndian) (noOfBytes - 1 ) * 8 else 0
148- val cnt = if (bigEndian) - 8 else 8
149- for (i in 0 until noOfBytes) {
150- ret = ret or (`is `.read() shl sv)
151- sv + = cnt
152- }
153- return ret
154- }
155-
156- override fun toString (): String {
157- return " MIME Type : $mimeType \t Width : $width \t Height : $height "
158- }
42+ constructor (file: File ) {
43+ val `is ` = FileInputStream (file)
44+ try {
45+ processStream(`is `)
46+ } finally {
47+ `is `.close()
48+ }
49+ }
50+
51+ @Throws(IOException ::class )
52+ constructor (`is `: InputStream ) {
53+ processStream(`is `)
54+ }
55+
56+ @Throws(IOException ::class )
57+ constructor (bytes: ByteArray ) {
58+ val `is ` = ByteArrayInputStream (bytes)
59+ try {
60+ processStream(`is `)
61+ } finally {
62+ `is `.close()
63+ }
64+ }
65+
66+ @Throws(IOException ::class )
67+ private fun processStream (`is `: InputStream ) {
68+ val c1 = `is `.read()
69+ val c2 = `is `.read()
70+ var c3 = `is `.read()
71+
72+ mimeType = null
73+ height = - 1
74+ width = height
75+
76+ if (c1 == ' G' .toInt() && c2 == ' I' .toInt() && c3 == ' F' .toInt()) { // GIF
77+ `is `.skip(3 )
78+ width = readInt(`is `, 2 , false )
79+ height = readInt(`is `, 2 , false )
80+ mimeType = " image/gif"
81+ } else if (c1 == 0xFF && c2 == 0xD8 ) { // JPG
82+ while (c3 == 255 ) {
83+ val marker = `is `.read()
84+ val len = readInt(`is `, 2 , true )
85+ if (marker == 192 || marker == 193 || marker == 194 ) {
86+ `is `.skip(1 )
87+ height = readInt(`is `, 2 , true )
88+ width = readInt(`is `, 2 , true )
89+ mimeType = " image/jpeg"
90+ break
91+ }
92+ `is `.skip((len - 2 ).toLong())
93+ c3 = `is `.read()
94+ }
95+ } else if (c1 == 137 && c2 == 80 && c3 == 78 ) { // PNG
96+ `is `.skip(15 )
97+ width = readInt(`is `, 2 , true )
98+ `is `.skip(2 )
99+ height = readInt(`is `, 2 , true )
100+ mimeType = " image/png"
101+ } else if (c1 == 66 && c2 == 77 ) { // BMP
102+ `is `.skip(15 )
103+ width = readInt(`is `, 2 , false )
104+ `is `.skip(2 )
105+ height = readInt(`is `, 2 , false )
106+ mimeType = " image/bmp"
107+ } else {
108+ val c4 = `is `.read()
109+ if (c1 == ' R' .code && c2 == ' I' .code && c3 == ' F' .code && c4 == ' F' .code) {
110+ // Image in RIFF format
111+ val fileSize = `is `.readNBytes(4 )
112+ val header = `is `.readNBytes(4 )
113+ if (header[0 ].toInt() == ' W' .code && header[1 ].toInt() == ' E' .code && header[2 ].toInt() == ' B' .code && header[3 ].toInt() == ' P' .code) {
114+ mimeType = " image/webp"
115+ val chunkFourCC = `is `.readNBytes(4 )
116+ val chunkSize = readInt(`is `, 4 , false ) // little-endian, not used
117+ val fourCC = String (chunkFourCC.map { it.toInt().toChar() }.toCharArray())
118+ when (fourCC) {
119+ " VP8 " -> {
120+ // Lossy format
121+ `is `.skip(3 ) // frame tag
122+ `is `.skip(3 ) // signature 0x9D 0x01 0x2A
123+ width = readInt(`is `, 2 , false ) and 0x3FFF
124+ height = readInt(`is `, 2 , false ) and 0x3FFF
125+ }
126+ " VP8L" -> {
127+ // Lossless format
128+ `is `.skip(1 ) // signature 0x2F
129+ val bits = readInt(`is `, 4 , false )
130+ width = (bits and 0x3FFF ) + 1
131+ height = ((bits shr 14 ) and 0x3FFF ) + 1
132+ }
133+ " VP8X" -> {
134+ // Extended format
135+ `is `.skip(4 ) // flags
136+ width = readInt(`is `, 3 , false ) + 1
137+ height = readInt(`is `, 3 , false ) + 1
138+ }
139+ }
140+ }
141+ } else if (c1 == ' M' .toInt() && c2 == ' M' .toInt() && c3 == 0 && c4 == 42 || c1 == ' I' .toInt() && c2 == ' I' .toInt() && c3 == 42 && c4 == 0 ) { // TIFF
142+ val bigEndian = c1 == ' M' .toInt()
143+ var ifd = 0
144+ val entries: Int
145+ ifd = readInt(`is `, 4 , bigEndian)
146+ `is `.skip((ifd - 8 ).toLong())
147+ entries = readInt(`is `, 2 , bigEndian)
148+ for (i in 1 .. entries) {
149+ val tag = readInt(`is `, 2 , bigEndian)
150+ val fieldType = readInt(`is `, 2 , bigEndian)
151+ val count = readInt(`is `, 4 , bigEndian).toLong()
152+ val valOffset: Int
153+ if (fieldType == 3 || fieldType == 8 ) {
154+ valOffset = readInt(`is `, 2 , bigEndian)
155+ `is `.skip(2 )
156+ } else {
157+ valOffset = readInt(`is `, 4 , bigEndian)
158+ }
159+ if (tag == 256 ) {
160+ width = valOffset
161+ } else if (tag == 257 ) {
162+ height = valOffset
163+ }
164+ if (width != - 1 && height != - 1 ) {
165+ mimeType = " image/tiff"
166+ break
167+ }
168+ }
169+ }
170+ }
171+ if (mimeType == null ) {
172+ throw IOException (" Unsupported image type" )
173+ }
174+ }
175+
176+ @Throws(IOException ::class )
177+ private fun readInt (`is `: InputStream , noOfBytes : Int , bigEndian : Boolean ): Int {
178+ var ret = 0
179+ var sv = if (bigEndian) (noOfBytes - 1 ) * 8 else 0
180+ val cnt = if (bigEndian) - 8 else 8
181+ for (i in 0 until noOfBytes) {
182+ ret = ret or (`is `.read() shl sv)
183+ sv + = cnt
184+ }
185+ return ret
186+ }
187+
188+ override fun toString (): String {
189+ return " MIME Type : $mimeType \t Width : $width \t Height : $height "
190+ }
159191}
0 commit comments