1/*
2 * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23
24/**
25 * @test
26 * @bug     8149028
27 * @author  a.stepanov
28 * @summary a simple write-read test for TIFFDirectory
29 * @run     main TIFFDirectoryWriteReadTest
30 */
31
32import java.awt.*;
33import java.awt.color.*;
34import java.awt.image.BufferedImage;
35import java.io.*;
36import javax.imageio.*;
37import javax.imageio.metadata.*;
38import javax.imageio.stream.*;
39import javax.imageio.plugins.tiff.*;
40
41
42public class TIFFDirectoryWriteReadTest {
43
44    private final static String FILENAME = "test.tiff";
45    private final static int SZ = 100;
46    private final static Color C = Color.RED;
47
48    private static final String COPYRIGHT[] = {"Copyright 123ABC.."};
49    private static final String DESCRIPTION[] = {"Test Image", "Description"};
50    private static final String SOFTWARE[] = {"test", "software", "123"};
51
52    private static final long RES_X[][] = {{2, 1}}, RES_Y[][] = {{1, 1}};
53
54    private static final byte[] ICC_PROFILE =
55        ICC_ProfileRGB.getInstance(ColorSpace.CS_sRGB).getData();
56
57
58    private ImageWriter getTIFFWriter() {
59
60        java.util.Iterator<ImageWriter> writers =
61            ImageIO.getImageWritersByFormatName("TIFF");
62        if (!writers.hasNext()) {
63            throw new RuntimeException("No writers available for TIFF format");
64        }
65        return writers.next();
66    }
67
68    private ImageReader getTIFFReader() {
69
70        java.util.Iterator<ImageReader> readers =
71            ImageIO.getImageReadersByFormatName("TIFF");
72        if (!readers.hasNext()) {
73            throw new RuntimeException("No readers available for TIFF format");
74        }
75        return readers.next();
76    }
77
78    private void addASCIIField(TIFFDirectory d,
79                               String        name,
80                               String        data[],
81                               int           num) {
82
83        d.addTIFFField(new TIFFField(
84            new TIFFTag(name, num, 1 << TIFFTag.TIFF_ASCII),
85                TIFFTag.TIFF_ASCII, data.length, data));
86    }
87
88    private void checkASCIIField(TIFFDirectory d,
89                                 String        what,
90                                 String        data[],
91                                 int           num) {
92
93        String notFound = what + " field was not found";
94        check(d.containsTIFFField(num), notFound);
95        TIFFField f = d.getTIFFField(num);
96        check(f.getType() == TIFFTag.TIFF_ASCII, "field type != ASCII");
97        check(f.getCount() == data.length, "invalid " + what + " data count");
98        for (int i = 0; i < data.length; i++) {
99            check(f.getValueAsString(i).equals(data[i]),
100                "invalid " + what + " data");
101        }
102    }
103
104    private void writeImage() throws Exception {
105
106        OutputStream s = new BufferedOutputStream(new FileOutputStream(FILENAME));
107        try (ImageOutputStream ios = ImageIO.createImageOutputStream(s)) {
108
109            ImageWriter writer = getTIFFWriter();
110            writer.setOutput(ios);
111
112            BufferedImage img = new BufferedImage(
113                SZ, SZ, BufferedImage.TYPE_INT_RGB);
114            Graphics g = img.getGraphics();
115            g.setColor(C);
116            g.fillRect(0, 0, SZ, SZ);
117            g.dispose();
118
119            IIOMetadata metadata = writer.getDefaultImageMetadata(
120                new ImageTypeSpecifier(img), writer.getDefaultWriteParam());
121
122            TIFFDirectory dir = TIFFDirectory.createFromMetadata(metadata);
123
124            addASCIIField(dir, "Copyright",
125                COPYRIGHT, BaselineTIFFTagSet.TAG_COPYRIGHT);
126
127            addASCIIField(dir, "ImageDescription",
128                DESCRIPTION, BaselineTIFFTagSet.TAG_IMAGE_DESCRIPTION);
129
130            addASCIIField(dir, "Software",
131                SOFTWARE, BaselineTIFFTagSet.TAG_SOFTWARE);
132
133            dir.addTIFFField(new TIFFField(
134                new TIFFTag("XResolution", BaselineTIFFTagSet.TAG_X_RESOLUTION,
135                1 << TIFFTag.TIFF_RATIONAL), TIFFTag.TIFF_RATIONAL, 1, RES_X));
136            dir.addTIFFField(new TIFFField(
137                new TIFFTag("YResolution", BaselineTIFFTagSet.TAG_Y_RESOLUTION,
138                1 << TIFFTag.TIFF_RATIONAL), TIFFTag.TIFF_RATIONAL, 1, RES_Y));
139
140            dir.addTIFFField(new TIFFField(
141            new TIFFTag("ICC Profile", BaselineTIFFTagSet.TAG_ICC_PROFILE,
142                1 << TIFFTag.TIFF_UNDEFINED),
143                TIFFTag.TIFF_UNDEFINED, ICC_PROFILE.length, ICC_PROFILE));
144
145            IIOMetadata data = dir.getAsMetadata();
146            writer.write(new IIOImage(img, null, data));
147
148            ios.flush();
149            writer.dispose();
150        }
151        s.close();
152    }
153
154
155
156    private void readAndCheckImage() throws Exception {
157
158        ImageReader reader = getTIFFReader();
159
160        ImageInputStream s = ImageIO.createImageInputStream(new File(FILENAME));
161        reader.setInput(s);
162
163        int ni = reader.getNumImages(true);
164        check(ni == 1, "invalid number of images");
165
166        // check image
167        BufferedImage img = reader.read(0);
168        check(img.getWidth() == SZ && img.getHeight() == SZ,
169            "invalid image size");
170
171        Color c = new Color(img.getRGB(SZ / 2, SZ / 2));
172        check(C.equals(c), "invalid image color");
173
174        IIOMetadata metadata = reader.readAll(0, null).getMetadata();
175        TIFFDirectory dir = TIFFDirectory.createFromMetadata(metadata);
176
177        reader.dispose();
178        s.close();
179
180        // ===== perform tag checks =====
181
182        checkASCIIField(dir, "copyright", COPYRIGHT,
183            BaselineTIFFTagSet.TAG_COPYRIGHT);
184
185        checkASCIIField(dir, "description", DESCRIPTION,
186            BaselineTIFFTagSet.TAG_IMAGE_DESCRIPTION);
187
188        checkASCIIField(dir, "software", SOFTWARE,
189            BaselineTIFFTagSet.TAG_SOFTWARE);
190
191        TIFFField f = dir.getTIFFField(BaselineTIFFTagSet.TAG_IMAGE_WIDTH);
192        check(f.getCount() == 1, "invalid width field count");
193        int w = f.getAsInt(0);
194        check(w == SZ, "invalid width");
195
196        f = dir.getTIFFField(BaselineTIFFTagSet.TAG_IMAGE_LENGTH);
197        check(f.getCount() == 1, "invalid height field count");
198        int h = f.getAsInt(0);
199        check(h == SZ, "invalid height");
200
201        f = dir.getTIFFField(BaselineTIFFTagSet.TAG_BITS_PER_SAMPLE);
202        // RGB: 3 x 8 bits for R, G and B components
203        int bps[] = f.getAsInts();
204        check((f.getCount() == 3) && (bps.length == 3), "invalid BPS count");
205        for (int b: bps) { check(b == 8, "invalid bits per sample"); }
206
207        // RGB: PhotometricInterpretation = 2
208        f = dir.getTIFFField(BaselineTIFFTagSet.TAG_PHOTOMETRIC_INTERPRETATION);
209        check(f.getCount() == 1, "invalid count");
210        check(f.getAsInt(0) == BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_RGB,
211            "invalid photometric interpretation value");
212
213        String rat = " resolution must be rational";
214        f = dir.getTIFFField(BaselineTIFFTagSet.TAG_X_RESOLUTION);
215        check(f.getType() == TIFFTag.TIFF_RATIONAL, "x" + rat);
216        check(f.getCount() == 1 &&
217              f.getAsInt(0) == (int) (RES_X[0][0] / RES_X[0][1]),
218              "invalid x resolution");
219
220        f = dir.getTIFFField(BaselineTIFFTagSet.TAG_Y_RESOLUTION);
221        check(f.getType() == TIFFTag.TIFF_RATIONAL, "y" + rat);
222        check(f.getCount() == 1 &&
223              f.getAsInt(0) == (int) (RES_Y[0][0] / RES_Y[0][1]),
224              "invalid y resolution");
225
226        f = dir.getTIFFField(BaselineTIFFTagSet.TAG_ICC_PROFILE);
227        check(f.getType() == TIFFTag.TIFF_UNDEFINED,
228            "invalid ICC profile field type");
229        int cnt = f.getCount();
230        byte icc[] = f.getAsBytes();
231        check((cnt == ICC_PROFILE.length) && (cnt == icc.length),
232                "invalid ICC profile");
233        for (int i = 0; i < cnt; i++) {
234            check(icc[i] == ICC_PROFILE[i], "invalid ICC profile");
235        }
236    }
237
238    public void run() {
239
240        try {
241            writeImage();
242            readAndCheckImage();
243        } catch (Exception e) {
244            throw new RuntimeException(e);
245        }
246
247    }
248
249    private void check(boolean ok, String msg) {
250        if (!ok) { throw new RuntimeException(msg); }
251    }
252
253    public static void main(String[] args) {
254        (new TIFFDirectoryWriteReadTest()).run();
255    }
256}
257