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 */ 75 public WBMPImageReader(ImageReaderSpi originator) { 76 super(originator); 77 } 78 79 @Override 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 @Override 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 99 @Override 100 public int getWidth(int imageIndex) throws IOException { 101 checkIndex(imageIndex); 102 readHeader(); 103 return width; 104 } 105 106 @Override 107 public int getHeight(int imageIndex) throws IOException { 108 checkIndex(imageIndex); 109 readHeader(); 110 return height; 111 } 112 113 @Override 114 public boolean isRandomAccessEasy(int imageIndex) throws IOException { 115 checkIndex(imageIndex); 116 return true; 117 } 118 119 private void checkIndex(int imageIndex) { 120 if (imageIndex != 0) { 121 throw new IndexOutOfBoundsException(I18N.getString("WBMPImageReader0")); 122 } 123 } 124 125 public void readHeader() throws IOException { 126 if (gotHeader) 127 return; 128 129 if (iis == null) { 130 throw new IllegalStateException("Input source not set!"); 131 } 132 133 metadata = new WBMPMetadata(); 134 135 wbmpType = iis.readByte(); // TypeField 136 byte fixHeaderField = iis.readByte(); 137 138 // check for valid wbmp image 139 if (fixHeaderField != 0 140 || !isValidWbmpType(wbmpType)) 141 { 142 throw new IIOException(I18N.getString("WBMPImageReader2")); 143 } 144 145 metadata.wbmpType = wbmpType; 146 147 // Read image width 148 width = ReaderUtil.readMultiByteInteger(iis); 149 metadata.width = width; 150 151 // Read image height 152 height = ReaderUtil.readMultiByteInteger(iis); 153 metadata.height = height; 154 155 gotHeader = true; 156 } 157 158 @Override 159 public Iterator<ImageTypeSpecifier> getImageTypes(int imageIndex) 160 throws IOException { 161 checkIndex(imageIndex); 162 readHeader(); 163 164 BufferedImage bi = 165 new BufferedImage(1, 1, BufferedImage.TYPE_BYTE_BINARY); 166 ArrayList<ImageTypeSpecifier> list = new ArrayList<>(1); 167 list.add(new ImageTypeSpecifier(bi)); 168 return list.iterator(); 169 } 170 171 @Override 172 public ImageReadParam getDefaultReadParam() { 173 return new ImageReadParam(); 174 } 175 176 @Override 177 public IIOMetadata getImageMetadata(int imageIndex) 178 throws IOException { 179 checkIndex(imageIndex); 180 if (metadata == null) { 181 readHeader(); 182 } 183 return metadata; 184 } 185 186 @Override 187 public IIOMetadata getStreamMetadata() throws IOException { 188 return null; 189 } 190 191 @Override 192 public BufferedImage read(int imageIndex, ImageReadParam param) 193 throws IOException { 194 195 if (iis == null) { 196 throw new IllegalStateException(I18N.getString("WBMPImageReader1")); 197 } 198 199 checkIndex(imageIndex); 200 clearAbortRequest(); 201 processImageStarted(imageIndex); 202 if (param == null) 203 param = getDefaultReadParam(); 204 205 //read header 206 readHeader(); 207 208 Rectangle sourceRegion = new Rectangle(0, 0, 0, 0); 209 Rectangle destinationRegion = new Rectangle(0, 0, 0, 0); 210 211 computeRegions(param, this.width, this.height, 212 param.getDestination(), 213 sourceRegion, 214 destinationRegion); 215 216 int scaleX = param.getSourceXSubsampling(); 217 int scaleY = param.getSourceYSubsampling(); 218 int xOffset = param.getSubsamplingXOffset(); 219 int yOffset = param.getSubsamplingYOffset(); 220 221 // If the destination is provided, then use it. Otherwise, create new one 222 BufferedImage bi = param.getDestination(); 223 224 if (bi == null) 225 bi = new BufferedImage(destinationRegion.x + destinationRegion.width, 226 destinationRegion.y + destinationRegion.height, 227 BufferedImage.TYPE_BYTE_BINARY); 228 229 boolean noTransform = 230 destinationRegion.equals(new Rectangle(0, 0, width, height)) && 231 destinationRegion.equals(new Rectangle(0, 0, bi.getWidth(), bi.getHeight())); 232 233 // Get the image data. 234 WritableRaster tile = bi.getWritableTile(0, 0); 235 236 // Get the SampleModel. 237 MultiPixelPackedSampleModel sm = 238 (MultiPixelPackedSampleModel)bi.getSampleModel(); 239 240 if (noTransform) { 241 if (abortRequested()) { 242 processReadAborted(); 243 return bi; 244 } 245 246 // If noTransform is necessary, read the data. 247 iis.read(((DataBufferByte)tile.getDataBuffer()).getData(), 248 0, height*sm.getScanlineStride()); 249 processImageUpdate(bi, 250 0, 0, 251 width, height, 1, 1, 252 new int[]{0}); 253 processImageProgress(100.0F); 254 } else { 255 int len = (this.width + 7) / 8; 256 byte[] buf = new byte[len]; 257 byte[] data = ((DataBufferByte)tile.getDataBuffer()).getData(); 258 int lineStride = sm.getScanlineStride(); 259 iis.skipBytes(len * sourceRegion.y); 260 int skipLength = len * (scaleY - 1); 261 262 // cache the values to avoid duplicated computation 263 int[] srcOff = new int[destinationRegion.width]; 264 int[] destOff = new int[destinationRegion.width]; 265 int[] srcPos = new int[destinationRegion.width]; 266 int[] destPos = new int[destinationRegion.width]; 267 268 for (int i = destinationRegion.x, x = sourceRegion.x, j = 0; 269 i < destinationRegion.x + destinationRegion.width; 270 i++, j++, x += scaleX) { 271 srcPos[j] = x >> 3; 272 srcOff[j] = 7 - (x & 7); 273 destPos[j] = i >> 3; 274 destOff[j] = 7 - (i & 7); 275 } 276 277 for (int j = 0, y = sourceRegion.y, 278 k = destinationRegion.y * lineStride; 279 j < destinationRegion.height; j++, y+=scaleY) { 280 281 if (abortRequested()) 282 break; 283 iis.read(buf, 0, len); 284 for (int i = 0; i < destinationRegion.width; i++) { 285 //get the bit and assign to the data buffer of the raster 286 int v = (buf[srcPos[i]] >> srcOff[i]) & 1; 287 data[k + destPos[i]] |= v << destOff[i]; 288 } 289 290 k += lineStride; 291 iis.skipBytes(skipLength); 292 processImageUpdate(bi, 293 0, j, 294 destinationRegion.width, 1, 1, 1, 295 new int[]{0}); 296 processImageProgress(100.0F*j/destinationRegion.height); 297 } 298 } 299 300 if (abortRequested()) 301 processReadAborted(); 302 else 303 processImageComplete(); 304 return bi; 305 } 306 307 @Override 308 public boolean canReadRaster() { 309 return true; 310 } 311 312 @Override 313 public Raster readRaster(int imageIndex, 314 ImageReadParam param) throws IOException { 315 BufferedImage bi = read(imageIndex, param); 316 return bi.getData(); 317 } 318 319 @Override 320 public void reset() { 321 super.reset(); 322 iis = null; 323 gotHeader = false; 324 } 325 326 /* 327 * This method verifies that given byte is valid wbmp type marker. 328 * At the moment only 0x0 marker is described by wbmp spec. 329 */ 330 boolean isValidWbmpType(int type) { 331 return type == 0; 332 } 333 }