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