1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.commons.imaging.formats.icns;
19
20 import static org.junit.jupiter.api.Assertions.assertEquals;
21 import static org.junit.jupiter.api.Assertions.assertNotNull;
22 import static org.junit.jupiter.api.Assertions.assertTrue;
23
24 import java.awt.image.BufferedImage;
25 import java.io.ByteArrayOutputStream;
26 import java.io.File;
27 import java.io.IOException;
28 import java.nio.ByteOrder;
29
30 import org.apache.commons.imaging.ImageReadException;
31 import org.apache.commons.imaging.Imaging;
32 import org.apache.commons.imaging.common.BinaryOutputStream;
33 import org.apache.commons.imaging.internal.Debug;
34 import org.apache.commons.io.FileUtils;
35 import org.junit.jupiter.api.Test;
36
37 public class IcnsRoundTripTest extends IcnsBaseTest {
38
39 private static final int[][] IMAGE = {
40 {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
41 {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
42 {0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0},
43 {0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0},
44 {0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0},
45 {0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0},
46 {0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0},
47 {0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0},
48 {0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0},
49 {0,0,0,0,0,1,1,1,0,1,0,0,0,0,0,0},
50 {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
51 {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
52 {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
53 {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
54 {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
55 {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
56 };
57
58 @Test
59 public void test1BPPIconMaskVersus8BPPMask() throws Exception {
60 final int foreground = 0xff000000;
61 final int background = 0xff000000;
62 try (final ByteArrayOutputStream baos = new ByteArrayOutputStream();
63 final BinaryOutputStream bos = new BinaryOutputStream(baos, ByteOrder.BIG_ENDIAN)) {
64 bos.write4Bytes(IcnsImageParser.ICNS_MAGIC);
65 bos.write4Bytes(4 + 4 + 4 + 4 + 2 * 16 * 16 / 8 + 4 + 4 + 16 * 16);
66 bos.write4Bytes(IcnsType.ICNS_16x16_1BIT_IMAGE_AND_MASK.getType());
67 bos.write4Bytes(4 + 4 + 2 * 16 * 16 / 8);
68
69 for (int y = 0; y < 16; y++) {
70 bos.write(0xff);
71 bos.write(0xff);
72 }
73
74 for (int y = 0; y < 16; y++) {
75 bos.write(0xff);
76 bos.write(0xff);
77 }
78
79 bos.write4Bytes(IcnsType.ICNS_16x16_8BIT_MASK.getType());
80 bos.write4Bytes(4 + 4 + 16 * 16);
81 for (int y = 0; y < 16; y++) {
82 for (int x = 0; x < 16; x++) {
83 if (IMAGE[y][x] != 0) {
84 bos.write(0xff);
85 } else {
86 bos.write(0x00);
87 }
88 }
89 }
90 bos.flush();
91 writeAndReadImageData("1bpp-image-mask-versus-8bpp-mask", baos.toByteArray(), foreground, background);
92 }
93 }
94
95 @Test
96 public void test8BPPIcon8BPPMask() throws Exception {
97 final int foreground = 0xff000000;
98 final int background = 0x00cccccc;
99 try (final ByteArrayOutputStream baos = new ByteArrayOutputStream();
100 final BinaryOutputStream bos = new BinaryOutputStream(baos, ByteOrder.BIG_ENDIAN)) {
101 bos.write4Bytes(IcnsImageParser.ICNS_MAGIC);
102 bos.write4Bytes(4 + 4 + 4 + 4 + 16 * 16 + 4 + 4 + 16 * 16);
103 bos.write4Bytes(IcnsType.ICNS_16x16_8BIT_IMAGE.getType());
104 bos.write4Bytes(4 + 4 + 16 * 16);
105
106 for (int y = 0; y < 16; y++) {
107 for (int x = 0; x < 16; x++) {
108 if (IMAGE[y][x] != 0) {
109 bos.write(0xff);
110 } else {
111 bos.write(43);
112 }
113 }
114 }
115
116 bos.write4Bytes(IcnsType.ICNS_16x16_8BIT_MASK.getType());
117 bos.write4Bytes(4 + 4 + 16 * 16);
118 for (int y = 0; y < 16; y++) {
119 for (int x = 0; x < 16; x++) {
120 if (IMAGE[y][x] != 0) {
121 bos.write(0xff);
122 } else {
123 bos.write(0x00);
124 }
125 }
126 }
127 bos.flush();
128 writeAndReadImageData("8bpp-image-8bpp-mask", baos.toByteArray(), foreground, background);
129 }
130 }
131
132 @Test
133 public void test8BPPIcon8BPPMaskVersus1BPPMask() throws Exception {
134 final int foreground = 0xff000000;
135 final int background = 0x00cccccc;
136 try (final ByteArrayOutputStream baos = new ByteArrayOutputStream();
137 final BinaryOutputStream bos = new BinaryOutputStream(baos, ByteOrder.BIG_ENDIAN)) {
138 bos.write4Bytes(IcnsImageParser.ICNS_MAGIC);
139 bos.write4Bytes(4 + 4 + 4 + 4 + 16 * 16 + 4 + 4 + 16 * 16 + 4 + 4 + 2 * 16 * 16 / 8);
140 bos.write4Bytes(IcnsType.ICNS_16x16_8BIT_IMAGE.getType());
141 bos.write4Bytes(4 + 4 + 16 * 16);
142
143 for (int y = 0; y < 16; y++) {
144 for (int x = 0; x < 16; x++) {
145 if (IMAGE[y][x] != 0) {
146 bos.write(0xff);
147 } else {
148 bos.write(43);
149 }
150 }
151 }
152
153 bos.write4Bytes(IcnsType.ICNS_16x16_8BIT_MASK.getType());
154 bos.write4Bytes(4 + 4 + 16 * 16);
155 for (int y = 0; y < 16; y++) {
156 for (int x = 0; x < 16; x++) {
157 if (IMAGE[y][x] != 0) {
158 bos.write(0xff);
159 } else {
160 bos.write(0x00);
161 }
162 }
163 }
164
165 bos.write4Bytes(IcnsType.ICNS_16x16_1BIT_IMAGE_AND_MASK.getType());
166 bos.write4Bytes(4 + 4 + 2 * 16 * 16 / 8);
167
168 for (int y = 0; y < 16; y++) {
169 for (int x = 0; x < 16; x += 8) {
170 int eightBits = 0;
171 for (int pos = 0; pos < 8; pos++) {
172 if (IMAGE[y][x + pos] != 0) {
173 eightBits |= (1 << (7 - pos));
174 }
175 }
176 bos.write(eightBits);
177 }
178 }
179
180 for (int y = 0; y < 16; y++) {
181 bos.write(0xff);
182 bos.write(0xff);
183 }
184 bos.flush();
185 writeAndReadImageData("8bpp-image-8bpp-mask-vs-1bpp-mask", baos.toByteArray(), foreground, background);
186 }
187 }
188
189 @Test
190 public void test8BPPIcon1BPPMaskVersus8BPPMask() throws Exception {
191 final int foreground = 0xff000000;
192 final int background = 0x00cccccc;
193 try (final ByteArrayOutputStream baos = new ByteArrayOutputStream();
194 final BinaryOutputStream bos = new BinaryOutputStream(baos, ByteOrder.BIG_ENDIAN)) {
195 bos.write4Bytes(IcnsImageParser.ICNS_MAGIC);
196 bos.write4Bytes(4 + 4 + 4 + 4 + 16 * 16 + 4 + 4 + 16 * 16 + 4 + 4 + 2 * 16 * 16 / 8);
197 bos.write4Bytes(IcnsType.ICNS_16x16_8BIT_IMAGE.getType());
198 bos.write4Bytes(4 + 4 + 16 * 16);
199
200 for (int y = 0; y < 16; y++) {
201 for (int x = 0; x < 16; x++) {
202 if (IMAGE[y][x] != 0) {
203 bos.write(0xff);
204 } else {
205 bos.write(43);
206 }
207 }
208 }
209
210 bos.write4Bytes(IcnsType.ICNS_16x16_1BIT_IMAGE_AND_MASK.getType());
211 bos.write4Bytes(4 + 4 + 2 * 16 * 16 / 8);
212
213 for (int y = 0; y < 16; y++) {
214 for (int x = 0; x < 16; x += 8) {
215 int eightBits = 0;
216 for (int pos = 0; pos < 8; pos++) {
217 if (IMAGE[y][x + pos] != 0) {
218 eightBits |= (1 << (7 - pos));
219 }
220 }
221 bos.write(eightBits);
222 }
223 }
224
225 for (int y = 0; y < 16; y++) {
226 bos.write(0xff);
227 bos.write(0xff);
228 }
229
230 bos.write4Bytes(IcnsType.ICNS_16x16_8BIT_MASK.getType());
231 bos.write4Bytes(4 + 4 + 16 * 16);
232 for (int y = 0; y < 16; y++) {
233 for (int x = 0; x < 16; x++) {
234 if (IMAGE[y][x] != 0) {
235 bos.write(0xff);
236 } else {
237 bos.write(0x00);
238 }
239 }
240 }
241 bos.flush();
242 writeAndReadImageData("8bpp-image-1bpp-mask-vs-8bpp-mask", baos.toByteArray(), foreground, background);
243 }
244 }
245
246 @Test
247 public void test8BPPIconNoMask() throws Exception {
248 final int foreground = 0xff000000;
249 final int background = 0xffcccccc;
250 try (final ByteArrayOutputStream baos = new ByteArrayOutputStream();
251 final BinaryOutputStream bos = new BinaryOutputStream(baos, ByteOrder.BIG_ENDIAN)) {
252 bos.write4Bytes(IcnsImageParser.ICNS_MAGIC);
253 bos.write4Bytes(4 + 4 + 4 + 4 + 16 * 16);
254 bos.write4Bytes(IcnsType.ICNS_16x16_8BIT_IMAGE.getType());
255 bos.write4Bytes(4 + 4 + 16 * 16);
256
257 for (int y = 0; y < 16; y++) {
258 for (int x = 0; x < 16; x++) {
259 if (IMAGE[y][x] != 0) {
260 bos.write(0xff);
261 } else {
262 bos.write(43);
263 }
264 }
265 }
266 bos.flush();
267 writeAndReadImageData("8bpp-image-no-mask", baos.toByteArray(), foreground, background);
268 }}
269
270 @Test
271 public void test32BPPMaskedIcon() throws Exception {
272 final int foreground = 0xff000000;
273 final int background = 0x000000ff;
274 try (final ByteArrayOutputStream baos = new ByteArrayOutputStream();
275 final BinaryOutputStream bos = new BinaryOutputStream(baos, ByteOrder.BIG_ENDIAN)) {
276 bos.write4Bytes(IcnsImageParser.ICNS_MAGIC);
277 bos.write4Bytes(4 + 4 + 4 + 4 + 4 * 16 * 16 + 4 + 4 + 2 * 16 * 16 / 8);
278 bos.write4Bytes(IcnsType.ICNS_16x16_32BIT_IMAGE.getType());
279 bos.write4Bytes(4 + 4 + 4 * 16 * 16);
280 for (int y = 0; y < 16; y++) {
281 for (int x = 0; x < 16; x++) {
282
283 bos.write(0);
284 final int pixel;
285 if (IMAGE[y][x] != 0) {
286 pixel = foreground;
287 } else {
288 pixel = background;
289 }
290 bos.write(0xff & (pixel >> 16));
291 bos.write(0xff & (pixel >> 8));
292 bos.write(0xff & pixel);
293 }
294 }
295 bos.write4Bytes(IcnsType.ICNS_16x16_1BIT_IMAGE_AND_MASK.getType());
296 bos.write4Bytes(4 + 4 + 2 * 16 * 16 / 8);
297
298 for (int y = 0; y < 16; y++) {
299 for (int x = 0; x < 16; x += 8) {
300 int eightBits = 0;
301 for (int pos = 0; pos < 8; pos++) {
302 if (IMAGE[y][x + pos] != 0) {
303 eightBits |= (1 << (7 - pos));
304 }
305 }
306 bos.write(eightBits);
307 }
308 }
309
310 for (int y = 0; y < 16; y++) {
311 for (int x = 0; x < 16; x += 8) {
312 int eightBits = 0;
313 for (int pos = 0; pos < 8; pos++) {
314 if (IMAGE[y][x + pos] != 0) {
315 eightBits |= (1 << (7 - pos));
316 }
317 }
318 bos.write(eightBits);
319 }
320 }
321 bos.flush();
322 writeAndReadImageData("32bpp-image-1bpp-mask", baos.toByteArray(), foreground, background);
323 }
324 }
325
326 @Test
327 public void test32BPPHalfMaskedIcon() throws Exception {
328 final int foreground = 0xff000000;
329 final int background = 0xff0000ff;
330 try (final ByteArrayOutputStream baos = new ByteArrayOutputStream();
331 final BinaryOutputStream bos = new BinaryOutputStream(baos, ByteOrder.BIG_ENDIAN)) {
332 bos.write4Bytes(IcnsImageParser.ICNS_MAGIC);
333 bos.write4Bytes(4 + 4 + 4 + 4 + 4 * 16 * 16 + 4 + 4 + 16 * 16 / 8);
334 bos.write4Bytes(IcnsType.ICNS_16x16_32BIT_IMAGE.getType());
335 bos.write4Bytes(4 + 4 + 4 * 16 * 16);
336 for (int y = 0; y < 16; y++) {
337 for (int x = 0; x < 16; x++) {
338
339 bos.write(0);
340 final int pixel;
341 if (IMAGE[y][x] != 0) {
342 pixel = foreground;
343 } else {
344 pixel = background;
345 }
346 bos.write(0xff & (pixel >> 16));
347 bos.write(0xff & (pixel >> 8));
348 bos.write(0xff & pixel);
349 }
350 }
351 bos.write4Bytes(IcnsType.ICNS_16x16_1BIT_IMAGE_AND_MASK.getType());
352 bos.write4Bytes(4 + 4 + 16 * 16 / 8);
353
354 for (int y = 0; y < 16; y++) {
355 for (int x = 0; x < 16; x += 8) {
356 int eightBits = 0;
357 for (int pos = 0; pos < 8; pos++) {
358 if (IMAGE[y][x + pos] != 0) {
359 eightBits |= (1 << (7 - pos));
360 }
361 }
362 bos.write(eightBits);
363 }
364 }
365
366 bos.flush();
367
368 boolean threw = false;
369 try {
370 writeAndReadImageData("32bpp-half-masked-CORRUPT", baos.toByteArray(), foreground, background);
371 } catch (final ImageReadException imageReadException) {
372 threw = true;
373 }
374 assertTrue(threw);
375 }
376 }
377
378 @Test
379 public void test32BPPMaskMissingIcon() throws Exception {
380 final int foreground = 0xff000000;
381 final int background = 0xff0000ff;
382 try (final ByteArrayOutputStream baos = new ByteArrayOutputStream();
383 final BinaryOutputStream bos = new BinaryOutputStream(baos, ByteOrder.BIG_ENDIAN)) {
384 bos.write4Bytes(IcnsImageParser.ICNS_MAGIC);
385 bos.write4Bytes(4 + 4 + 4 + 4 + 4 * 16 * 16);
386 bos.write4Bytes(IcnsType.ICNS_16x16_32BIT_IMAGE.getType());
387 bos.write4Bytes(4 + 4 + 4 * 16 * 16);
388 for (int y = 0; y < 16; y++) {
389 for (int x = 0; x < 16; x++) {
390
391 bos.write(0);
392 final int pixel;
393 if (IMAGE[y][x] != 0) {
394 pixel = foreground;
395 } else {
396 pixel = background;
397 }
398 bos.write(0xff & (pixel >> 16));
399 bos.write(0xff & (pixel >> 8));
400 bos.write(0xff & pixel);
401 }
402 }
403 bos.flush();
404 writeAndReadImageData("32bpp-mask-missing", baos.toByteArray(), foreground, background);
405 }
406 }
407
408 private void writeAndReadImageData(final String description, final byte[] rawData,
409 final int foreground, final int background) throws IOException,
410 ImageReadException {
411 final File exportFile = File.createTempFile(description, ".icns");
412 FileUtils.writeByteArrayToFile(exportFile, rawData);
413 final BufferedImage dstImage = Imaging.getBufferedImage(exportFile);
414
415 assertNotNull(dstImage);
416 assertEquals(dstImage.getWidth(), IMAGE[0].length);
417 assertEquals(dstImage.getHeight(), IMAGE.length);
418
419 verify(dstImage, foreground, background);
420 }
421
422 private void verify(final BufferedImage data, final int foreground, final int background) {
423 assertNotNull(data);
424 assertEquals(data.getHeight(), IMAGE.length);
425
426 for (int y = 0; y < data.getHeight(); y++) {
427 assertEquals(data.getWidth(), IMAGE[y].length);
428 for (int x = 0; x < data.getWidth(); x++) {
429 final int imageARGB = (IMAGE[y][x] == 1) ? foreground : background;
430 final int dataARGB = data.getRGB(x, y);
431
432 if (imageARGB != dataARGB) {
433 Debug.debug("x: " + x + ", y: " + y + ", image: "
434 + imageARGB + " (0x"
435 + Integer.toHexString(imageARGB) + ")" + ", data: "
436 + dataARGB + " (0x" + Integer.toHexString(dataARGB)
437 + ")");
438 }
439 assertEquals(imageARGB, dataARGB);
440 }
441 }
442 }
443 }