1 /*
2  * Copyright (c) 2003, 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.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 package com.sun.imageio.plugins.wbmp;
27 
28 import java.awt.Rectangle;
29 import java.awt.image.BufferedImage;
30 import java.awt.image.DataBufferByte;
31 import java.awt.image.MultiPixelPackedSampleModel;
32 import java.awt.image.Raster;
33 import java.awt.image.WritableRaster;
34 
35 import javax.imageio.IIOException;
36 import javax.imageio.ImageReader;
37 import javax.imageio.ImageReadParam;
38 import javax.imageio.ImageTypeSpecifier;
39 import javax.imageio.metadata.IIOMetadata;
40 import javax.imageio.spi.ImageReaderSpi;
41 import javax.imageio.stream.ImageInputStream;
42 
43 import java.io.*;
44 import java.util.ArrayList;
45 import java.util.Iterator;
46 
47 import com.sun.imageio.plugins.common.I18N;
48 import com.sun.imageio.plugins.common.ReaderUtil;
49 
50 /** This class is the Java Image IO plugin reader for WBMP images.
51  *  It may subsample the image, clip the image,
52  *  and shift the decoded image origin if the proper decoding parameter
53  *  are set in the provided {@code WBMPImageReadParam}.
54  */
55 public class WBMPImageReader extends ImageReader {
56     /** The input stream where reads from */
57     private ImageInputStream iis = null;
58 
59     /** Indicates whether the header is read. */
60     private boolean gotHeader = false;
61 
62     /** The original image width. */
63     private int width;
64 
65     /** The original image height. */
66     private int height;
67 
68     private int wbmpType;
69 
70     private WBMPMetadata metadata;
71 
72     /** Constructs {@code WBMPImageReader} from the provided
73      *  {@code ImageReaderSpi}.
74      */
WBMPImageReader(ImageReaderSpi originator)75     public WBMPImageReader(ImageReaderSpi originator) {
76         super(originator);
77     }
78 
79     /** Overrides the method defined in the superclass. */
setInput(Object input, boolean seekForwardOnly, boolean ignoreMetadata)80     public void setInput(Object input,
81                          boolean seekForwardOnly,
82                          boolean ignoreMetadata) {
83         super.setInput(input, seekForwardOnly, ignoreMetadata);
84         iis = (ImageInputStream) input; // Always works
85         gotHeader = false;
86     }
87 
88     /** Overrides the method defined in the superclass. */
getNumImages(boolean allowSearch)89     public int getNumImages(boolean allowSearch) throws IOException {
90         if (iis == null) {
91             throw new IllegalStateException(I18N.getString("GetNumImages0"));
92         }
93         if (seekForwardOnly && allowSearch) {
94             throw new IllegalStateException(I18N.getString("GetNumImages1"));
95         }
96         return 1;
97     }
98 
getWidth(int imageIndex)99     public int getWidth(int imageIndex) throws IOException {
100         checkIndex(imageIndex);
101         readHeader();
102         return width;
103     }
104 
getHeight(int imageIndex)105     public int getHeight(int imageIndex) throws IOException {
106         checkIndex(imageIndex);
107         readHeader();
108         return height;
109     }
110 
isRandomAccessEasy(int imageIndex)111     public boolean isRandomAccessEasy(int imageIndex) throws IOException {
112         checkIndex(imageIndex);
113         return true;
114     }
115 
checkIndex(int imageIndex)116     private void checkIndex(int imageIndex) {
117         if (imageIndex != 0) {
118             throw new IndexOutOfBoundsException(I18N.getString("WBMPImageReader0"));
119         }
120     }
121 
readHeader()122     public void readHeader() throws IOException {
123         if (gotHeader)
124             return;
125 
126         if (iis == null) {
127             throw new IllegalStateException("Input source not set!");
128         }
129 
130         metadata = new WBMPMetadata();
131 
132         wbmpType = iis.readByte();   // TypeField
133         byte fixHeaderField = iis.readByte();
134 
135         // check for valid wbmp image
136         if (fixHeaderField != 0
137             || !isValidWbmpType(wbmpType))
138         {
139             throw new IIOException(I18N.getString("WBMPImageReader2"));
140         }
141 
142         metadata.wbmpType = wbmpType;
143 
144         // Read image width
145         width = ReaderUtil.readMultiByteInteger(iis);
146         metadata.width = width;
147 
148         // Read image height
149         height = ReaderUtil.readMultiByteInteger(iis);
150         metadata.height = height;
151 
152         gotHeader = true;
153     }
154 
getImageTypes(int imageIndex)155     public Iterator<ImageTypeSpecifier> getImageTypes(int imageIndex)
156         throws IOException {
157         checkIndex(imageIndex);
158         readHeader();
159 
160         BufferedImage bi =
161             new BufferedImage(1, 1, BufferedImage.TYPE_BYTE_BINARY);
162         ArrayList<ImageTypeSpecifier> list = new ArrayList<>(1);
163         list.add(new ImageTypeSpecifier(bi));
164         return list.iterator();
165     }
166 
getDefaultReadParam()167     public ImageReadParam getDefaultReadParam() {
168         return new ImageReadParam();
169     }
170 
getImageMetadata(int imageIndex)171     public IIOMetadata getImageMetadata(int imageIndex)
172         throws IOException {
173         checkIndex(imageIndex);
174         if (metadata == null) {
175             readHeader();
176         }
177         return metadata;
178     }
179 
getStreamMetadata()180     public IIOMetadata getStreamMetadata() throws IOException {
181         return null;
182     }
183 
read(int imageIndex, ImageReadParam param)184     public BufferedImage read(int imageIndex, ImageReadParam param)
185         throws IOException {
186 
187         if (iis == null) {
188             throw new IllegalStateException(I18N.getString("WBMPImageReader1"));
189         }
190 
191         checkIndex(imageIndex);
192         clearAbortRequest();
193         processImageStarted(imageIndex);
194         if (param == null)
195             param = getDefaultReadParam();
196 
197         //read header
198         readHeader();
199 
200         Rectangle sourceRegion = new Rectangle(0, 0, 0, 0);
201         Rectangle destinationRegion = new Rectangle(0, 0, 0, 0);
202 
203         computeRegions(param, this.width, this.height,
204                        param.getDestination(),
205                        sourceRegion,
206                        destinationRegion);
207 
208         int scaleX = param.getSourceXSubsampling();
209         int scaleY = param.getSourceYSubsampling();
210         int xOffset = param.getSubsamplingXOffset();
211         int yOffset = param.getSubsamplingYOffset();
212 
213         // If the destination is provided, then use it.  Otherwise, create new one
214         BufferedImage bi = param.getDestination();
215 
216         if (bi == null)
217             bi = new BufferedImage(destinationRegion.x + destinationRegion.width,
218                               destinationRegion.y + destinationRegion.height,
219                               BufferedImage.TYPE_BYTE_BINARY);
220 
221         boolean noTransform =
222             destinationRegion.equals(new Rectangle(0, 0, width, height)) &&
223             destinationRegion.equals(new Rectangle(0, 0, bi.getWidth(), bi.getHeight()));
224 
225         // Get the image data.
226         WritableRaster tile = bi.getWritableTile(0, 0);
227 
228         // Get the SampleModel.
229         MultiPixelPackedSampleModel sm =
230             (MultiPixelPackedSampleModel)bi.getSampleModel();
231 
232         if (noTransform) {
233             if (abortRequested()) {
234                 processReadAborted();
235                 return bi;
236             }
237 
238             // If noTransform is necessary, read the data.
239             iis.read(((DataBufferByte)tile.getDataBuffer()).getData(),
240                      0, height*sm.getScanlineStride());
241             processImageUpdate(bi,
242                                0, 0,
243                                width, height, 1, 1,
244                                new int[]{0});
245             processImageProgress(100.0F);
246         } else {
247             int len = (this.width + 7) / 8;
248             byte[] buf = new byte[len];
249             byte[] data = ((DataBufferByte)tile.getDataBuffer()).getData();
250             int lineStride = sm.getScanlineStride();
251             iis.skipBytes(len * sourceRegion.y);
252             int skipLength = len * (scaleY - 1);
253 
254             // cache the values to avoid duplicated computation
255             int[] srcOff = new int[destinationRegion.width];
256             int[] destOff = new int[destinationRegion.width];
257             int[] srcPos = new int[destinationRegion.width];
258             int[] destPos = new int[destinationRegion.width];
259 
260             for (int i = destinationRegion.x, x = sourceRegion.x, j = 0;
261                 i < destinationRegion.x + destinationRegion.width;
262                     i++, j++, x += scaleX) {
263                 srcPos[j] = x >> 3;
264                 srcOff[j] = 7 - (x & 7);
265                 destPos[j] = i >> 3;
266                 destOff[j] = 7 - (i & 7);
267             }
268 
269             for (int j = 0, y = sourceRegion.y,
270                 k = destinationRegion.y * lineStride;
271                 j < destinationRegion.height; j++, y+=scaleY) {
272 
273                 if (abortRequested())
274                     break;
275                 iis.read(buf, 0, len);
276                 for (int i = 0; i < destinationRegion.width; i++) {
277                     //get the bit and assign to the data buffer of the raster
278                     int v = (buf[srcPos[i]] >> srcOff[i]) & 1;
279                     data[k + destPos[i]] |= v << destOff[i];
280                 }
281 
282                 k += lineStride;
283                 iis.skipBytes(skipLength);
284                         processImageUpdate(bi,
285                                            0, j,
286                                            destinationRegion.width, 1, 1, 1,
287                                            new int[]{0});
288                         processImageProgress(100.0F*j/destinationRegion.height);
289             }
290         }
291 
292         if (abortRequested())
293             processReadAborted();
294         else
295             processImageComplete();
296         return bi;
297     }
298 
canReadRaster()299     public boolean canReadRaster() {
300         return true;
301     }
302 
readRaster(int imageIndex, ImageReadParam param)303     public Raster readRaster(int imageIndex,
304                              ImageReadParam param) throws IOException {
305         BufferedImage bi = read(imageIndex, param);
306         return bi.getData();
307     }
308 
reset()309     public void reset() {
310         super.reset();
311         iis = null;
312         gotHeader = false;
313     }
314 
315     /*
316      * This method verifies that given byte is valid wbmp type marker.
317      * At the moment only 0x0 marker is described by wbmp spec.
318      */
isValidWbmpType(int type)319     boolean isValidWbmpType(int type) {
320         return type == 0;
321     }
322 }
323