001/* 002 * $RCSfile: WBMPImageReader.java,v $ 003 * 004 * 005 * Copyright (c) 2005 Sun Microsystems, Inc. All Rights Reserved. 006 * 007 * Redistribution and use in source and binary forms, with or without 008 * modification, are permitted provided that the following conditions 009 * are met: 010 * 011 * - Redistribution of source code must retain the above copyright 012 * notice, this list of conditions and the following disclaimer. 013 * 014 * - Redistribution in binary form must reproduce the above copyright 015 * notice, this list of conditions and the following disclaimer in 016 * the documentation and/or other materials provided with the 017 * distribution. 018 * 019 * Neither the name of Sun Microsystems, Inc. or the names of 020 * contributors may be used to endorse or promote products derived 021 * from this software without specific prior written permission. 022 * 023 * This software is provided "AS IS," without a warranty of any 024 * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND 025 * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, 026 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY 027 * EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL 028 * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF 029 * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS 030 * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR 031 * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, 032 * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND 033 * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR 034 * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE 035 * POSSIBILITY OF SUCH DAMAGES. 036 * 037 * You acknowledge that this software is not designed or intended for 038 * use in the design, construction, operation or maintenance of any 039 * nuclear facility. 040 * 041 * $Revision: 1.2 $ 042 * $Date: 2006/04/21 00:02:26 $ 043 * $State: Exp $ 044 */ 045package com.github.jaiimageio.impl.plugins.wbmp; 046 047import java.awt.Rectangle; 048import java.awt.image.BufferedImage; 049import java.awt.image.DataBufferByte; 050import java.awt.image.MultiPixelPackedSampleModel; 051import java.awt.image.Raster; 052import java.awt.image.WritableRaster; 053import java.io.IOException; 054import java.util.ArrayList; 055import java.util.Iterator; 056 057import javax.imageio.IIOException; 058import javax.imageio.ImageReadParam; 059import javax.imageio.ImageReader; 060import javax.imageio.ImageTypeSpecifier; 061import javax.imageio.metadata.IIOMetadata; 062import javax.imageio.spi.ImageReaderSpi; 063import javax.imageio.stream.ImageInputStream; 064 065import com.github.jaiimageio.impl.common.ImageUtil; 066 067/** This class is the Java Image IO plugin reader for WBMP images. 068 * It may subsample the image, clip the image, 069 * and shift the decoded image origin if the proper decoding parameter 070 * are set in the provided <code>WBMPImageReadParam</code>. 071 */ 072public class WBMPImageReader extends ImageReader { 073 /** The input stream where reads from */ 074 private ImageInputStream iis = null; 075 076 /** Indicates whether the header is read. */ 077 private boolean gotHeader = false; 078 079 /** The stream position where the image data starts. */ 080 private long imageDataOffset; 081 082 /** The original image width. */ 083 private int width; 084 085 /** The original image height. */ 086 private int height; 087 088 private int wbmpType; 089 090 private WBMPMetadata metadata; 091 092 /** Constructs <code>WBMPImageReader</code> from the provided 093 * <code>ImageReaderSpi</code>. 094 */ 095 public WBMPImageReader(ImageReaderSpi originator) { 096 super(originator); 097 } 098 099 /** Overrides the method defined in the superclass. */ 100 public void setInput(Object input, 101 boolean seekForwardOnly, 102 boolean ignoreMetadata) { 103 super.setInput(input, seekForwardOnly, ignoreMetadata); 104 iis = (ImageInputStream) input; // Always works 105 gotHeader = false; 106 } 107 108 /** Overrides the method defined in the superclass. */ 109 public int getNumImages(boolean allowSearch) throws IOException { 110 if (iis == null) { 111 throw new IllegalStateException(I18N.getString("GetNumImages0")); 112 } 113 if (seekForwardOnly && allowSearch) { 114 throw new IllegalStateException(I18N.getString("GetNumImages1")); 115 } 116 return 1; 117 } 118 119 public int getWidth(int imageIndex) throws IOException { 120 checkIndex(imageIndex); 121 readHeader(); 122 return width; 123 } 124 125 public int getHeight(int imageIndex) throws IOException { 126 checkIndex(imageIndex); 127 readHeader(); 128 return height; 129 } 130 131 public boolean isRandomAccessEasy(int imageIndex) throws IOException { 132 checkIndex(imageIndex); 133 return true; 134 } 135 136 private void checkIndex(int imageIndex) { 137 if (imageIndex != 0) { 138 throw new IndexOutOfBoundsException(I18N.getString("WBMPImageReader0")); 139 } 140 } 141 142 public void readHeader() throws IOException { 143 if (gotHeader) { 144 // Seek to where the image data starts, since that is where 145 // the stream pointer should be after header is read 146 iis.seek(imageDataOffset); 147 return; 148 } 149 150 if (iis == null) { 151 throw new IllegalStateException(I18N.getString("WBMPImageReader1")); 152 } 153 154 metadata = new WBMPMetadata(); 155 wbmpType = iis.readByte(); // TypeField 156 byte fixHeaderField = iis.readByte(); 157 158 // check for valid wbmp image 159 if (fixHeaderField != 0 160 || !isValidWbmpType(wbmpType)) { 161 throw new IIOException(I18N.getString("WBMPImageReader2")); 162 } 163 164 metadata.wbmpType = wbmpType; 165 166 // Read image width 167 width = ImageUtil.readMultiByteInteger(iis); 168 metadata.width = width; 169 170 // Read image height 171 height = ImageUtil.readMultiByteInteger(iis); 172 metadata.height = height; 173 174 gotHeader = true; 175 // Store the stream position where the image data starts 176 imageDataOffset = iis.getStreamPosition(); 177 } 178 179 public Iterator getImageTypes(int imageIndex) 180 throws IOException { 181 checkIndex(imageIndex); 182 readHeader(); 183 184 BufferedImage bi = 185 new BufferedImage(1, 1, BufferedImage.TYPE_BYTE_BINARY); 186 ArrayList list = new ArrayList(1); 187 list.add(new ImageTypeSpecifier(bi)); 188 return list.iterator(); 189 } 190 191 public ImageReadParam getDefaultReadParam() { 192 return new ImageReadParam(); 193 } 194 195 public IIOMetadata getImageMetadata(int imageIndex) 196 throws IOException { 197 checkIndex(imageIndex); 198 if (metadata == null) { 199 readHeader(); 200 } 201 return metadata; 202 } 203 204 public IIOMetadata getStreamMetadata() throws IOException { 205 return null; 206 } 207 208 public BufferedImage read(int imageIndex, ImageReadParam param) 209 throws IOException { 210 211 if (iis == null) { 212 throw new IllegalStateException(I18N.getString("WBMPImageReader1")); 213 } 214 215 checkIndex(imageIndex); 216 clearAbortRequest(); 217 processImageStarted(imageIndex); 218 if (param == null) 219 param = getDefaultReadParam(); 220 221 //read header 222 readHeader(); 223 224 Rectangle sourceRegion = new Rectangle(0, 0, 0, 0); 225 Rectangle destinationRegion = new Rectangle(0, 0, 0, 0); 226 227 computeRegions(param, this.width, this.height, 228 param.getDestination(), 229 sourceRegion, 230 destinationRegion); 231 232 int scaleX = param.getSourceXSubsampling(); 233 int scaleY = param.getSourceYSubsampling(); 234 int xOffset = param.getSubsamplingXOffset(); 235 int yOffset = param.getSubsamplingYOffset(); 236 237 // If the destination is provided, then use it. Otherwise, create new one 238 BufferedImage bi = param.getDestination(); 239 240 if (bi == null) 241 bi = new BufferedImage(destinationRegion.x + destinationRegion.width, 242 destinationRegion.y + destinationRegion.height, 243 BufferedImage.TYPE_BYTE_BINARY); 244 245 boolean noTransform = 246 destinationRegion.equals(new Rectangle(0, 0, width, height)) && 247 destinationRegion.equals(new Rectangle(0, 0, bi.getWidth(), bi.getHeight())); 248 249 // Get the image data. 250 WritableRaster tile = bi.getWritableTile(0, 0); 251 252 // Get the SampleModel. 253 MultiPixelPackedSampleModel sm = 254 (MultiPixelPackedSampleModel)bi.getSampleModel(); 255 256 if (noTransform) { 257 if (abortRequested()) { 258 processReadAborted(); 259 return bi; 260 } 261 262 // If noTransform is necessary, read the data. 263 iis.read(((DataBufferByte)tile.getDataBuffer()).getData(), 264 0, height*sm.getScanlineStride()); 265 processImageUpdate(bi, 266 0, 0, 267 width, height, 1, 1, 268 new int[]{0}); 269 processImageProgress(100.0F); 270 } else { 271 int len = (this.width + 7) / 8; 272 byte[] buf = new byte[len]; 273 byte[] data = ((DataBufferByte)tile.getDataBuffer()).getData(); 274 int lineStride = sm.getScanlineStride(); 275 iis.skipBytes(len * sourceRegion.y); 276 int skipLength = len * (scaleY - 1); 277 278 // cache the values to avoid duplicated computation 279 int[] srcOff = new int[destinationRegion.width]; 280 int[] destOff = new int[destinationRegion.width]; 281 int[] srcPos = new int[destinationRegion.width]; 282 int[] destPos = new int[destinationRegion.width]; 283 284 for (int i = destinationRegion.x, x = sourceRegion.x, j = 0; 285 i < destinationRegion.x + destinationRegion.width; 286 i++, j++, x += scaleX) { 287 srcPos[j] = x >> 3; 288 srcOff[j] = 7 - (x & 7); 289 destPos[j] = i >> 3; 290 destOff[j] = 7 - (i & 7); 291 } 292 293 for (int j = 0, y = sourceRegion.y, 294 k = destinationRegion.y * lineStride; 295 j < destinationRegion.height; j++, y+=scaleY) { 296 297 if (abortRequested()) 298 break; 299 iis.read(buf, 0, len); 300 for (int i = 0; i < destinationRegion.width; i++) { 301 //get the bit and assign to the data buffer of the raster 302 int v = (buf[srcPos[i]] >> srcOff[i]) & 1; 303 data[k + destPos[i]] |= v << destOff[i]; 304 } 305 306 k += lineStride; 307 iis.skipBytes(skipLength); 308 processImageUpdate(bi, 309 0, j, 310 destinationRegion.width, 1, 1, 1, 311 new int[]{0}); 312 processImageProgress(100.0F*j/destinationRegion.height); 313 } 314 } 315 316 if (abortRequested()) 317 processReadAborted(); 318 else 319 processImageComplete(); 320 return bi; 321 } 322 323 public boolean canReadRaster() { 324 return true; 325 } 326 327 public Raster readRaster(int imageIndex, 328 ImageReadParam param) throws IOException { 329 BufferedImage bi = read(imageIndex, param); 330 return bi.getData(); 331 } 332 333 public void reset() { 334 super.reset(); 335 iis = null; 336 gotHeader = false; 337 } 338 339 /* 340 * This method verifies that given byte is valid wbmp type marker. 341 * At the moment only 0x0 marker is described by wbmp spec. 342 */ 343 boolean isValidWbmpType(int type) { 344 return type == 0; 345 } 346}