1. /*
  2. * @(#)WBMPImageReader.java 1.10 03/12/19 16:54:40
  3. *
  4. * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package com.sun.imageio.plugins.wbmp;
  8. import java.awt.Rectangle;
  9. import java.awt.image.BufferedImage;
  10. import java.awt.image.DataBufferByte;
  11. import java.awt.image.MultiPixelPackedSampleModel;
  12. import java.awt.image.Raster;
  13. import java.awt.image.WritableRaster;
  14. import javax.imageio.IIOException;
  15. import javax.imageio.ImageReader;
  16. import javax.imageio.ImageReadParam;
  17. import javax.imageio.ImageTypeSpecifier;
  18. import javax.imageio.metadata.IIOMetadata;
  19. import javax.imageio.spi.ImageReaderSpi;
  20. import javax.imageio.stream.ImageInputStream;
  21. import java.io.*;
  22. import java.util.ArrayList;
  23. import java.util.Iterator;
  24. import com.sun.imageio.plugins.common.I18N;
  25. /** This class is the Java Image IO plugin reader for WBMP images.
  26. * It may subsample the image, clip the image,
  27. * and shift the decoded image origin if the proper decoding parameter
  28. * are set in the provided <code>WBMPImageReadParam</code>.
  29. */
  30. public class WBMPImageReader extends ImageReader {
  31. /** The input stream where reads from */
  32. private ImageInputStream iis = null;
  33. /** Indicates whether the header is read. */
  34. private boolean gotHeader = false;
  35. /** The original image width. */
  36. private int width;
  37. /** The original image height. */
  38. private int height;
  39. private int wbmpType;
  40. private WBMPMetadata metadata;
  41. /** Constructs <code>WBMPImageReader</code> from the provided
  42. * <code>ImageReaderSpi</code>.
  43. */
  44. public WBMPImageReader(ImageReaderSpi originator) {
  45. super(originator);
  46. }
  47. /** Overrides the method defined in the superclass. */
  48. public void setInput(Object input,
  49. boolean seekForwardOnly,
  50. boolean ignoreMetadata) {
  51. super.setInput(input, seekForwardOnly, ignoreMetadata);
  52. iis = (ImageInputStream) input; // Always works
  53. gotHeader = false;
  54. }
  55. /** Overrides the method defined in the superclass. */
  56. public int getNumImages(boolean allowSearch) throws IOException {
  57. if (iis == null) {
  58. throw new IllegalStateException(I18N.getString("GetNumImages0"));
  59. }
  60. if (seekForwardOnly && allowSearch) {
  61. throw new IllegalStateException(I18N.getString("GetNumImages1"));
  62. }
  63. return 1;
  64. }
  65. public int getWidth(int imageIndex) throws IOException {
  66. checkIndex(imageIndex);
  67. readHeader();
  68. return width;
  69. }
  70. public int getHeight(int imageIndex) throws IOException {
  71. checkIndex(imageIndex);
  72. readHeader();
  73. return height;
  74. }
  75. public boolean isRandomAccessEasy(int imageIndex) throws IOException {
  76. checkIndex(imageIndex);
  77. return true;
  78. }
  79. private void checkIndex(int imageIndex) {
  80. if (imageIndex != 0) {
  81. throw new IndexOutOfBoundsException(I18N.getString("WBMPImageReader0"));
  82. }
  83. }
  84. public void readHeader() throws IOException {
  85. if (gotHeader)
  86. return;
  87. if (iis == null) {
  88. throw new IllegalStateException("Input source not set!");
  89. }
  90. metadata = new WBMPMetadata();
  91. wbmpType = iis.readByte(); // TypeField
  92. byte fixHeaderField = iis.readByte();
  93. // check for valid wbmp image
  94. if (fixHeaderField != 0
  95. || !isValidWbmpType(wbmpType))
  96. {
  97. throw new IIOException(I18N.getString("WBMPImageReader2"));
  98. }
  99. metadata.wbmpType = wbmpType;
  100. // Read image width
  101. width = readMultiByteInteger();
  102. metadata.width = width;
  103. // Read image height
  104. height = readMultiByteInteger();
  105. metadata.height = height;
  106. gotHeader = true;
  107. }
  108. public Iterator getImageTypes(int imageIndex)
  109. throws IOException {
  110. checkIndex(imageIndex);
  111. readHeader();
  112. BufferedImage bi =
  113. new BufferedImage(1, 1, BufferedImage.TYPE_BYTE_BINARY);
  114. ArrayList list = new ArrayList(1);
  115. list.add(new ImageTypeSpecifier(bi));
  116. return list.iterator();
  117. }
  118. public ImageReadParam getDefaultReadParam() {
  119. return new ImageReadParam();
  120. }
  121. public IIOMetadata getImageMetadata(int imageIndex)
  122. throws IOException {
  123. checkIndex(imageIndex);
  124. if (metadata == null) {
  125. readHeader();
  126. }
  127. return metadata;
  128. }
  129. public IIOMetadata getStreamMetadata() throws IOException {
  130. return null;
  131. }
  132. public BufferedImage read(int imageIndex, ImageReadParam param)
  133. throws IOException {
  134. if (iis == null) {
  135. throw new IllegalStateException(I18N.getString("WBMPImageReader1"));
  136. }
  137. checkIndex(imageIndex);
  138. clearAbortRequest();
  139. processImageStarted(imageIndex);
  140. if (param == null)
  141. param = getDefaultReadParam();
  142. //read header
  143. readHeader();
  144. Rectangle sourceRegion = new Rectangle(0, 0, 0, 0);
  145. Rectangle destinationRegion = new Rectangle(0, 0, 0, 0);
  146. computeRegions(param, this.width, this.height,
  147. param.getDestination(),
  148. sourceRegion,
  149. destinationRegion);
  150. int scaleX = param.getSourceXSubsampling();
  151. int scaleY = param.getSourceYSubsampling();
  152. int xOffset = param.getSubsamplingXOffset();
  153. int yOffset = param.getSubsamplingYOffset();
  154. // If the destination is provided, then use it. Otherwise, create new one
  155. BufferedImage bi = param.getDestination();
  156. if (bi == null)
  157. bi = new BufferedImage(destinationRegion.x + destinationRegion.width,
  158. destinationRegion.y + destinationRegion.height,
  159. BufferedImage.TYPE_BYTE_BINARY);
  160. boolean noTransform =
  161. destinationRegion.equals(new Rectangle(0, 0, width, height)) &&
  162. destinationRegion.equals(new Rectangle(0, 0, bi.getWidth(), bi.getHeight()));
  163. // Get the image data.
  164. WritableRaster tile = bi.getWritableTile(0, 0);
  165. // Get the SampleModel.
  166. MultiPixelPackedSampleModel sm =
  167. (MultiPixelPackedSampleModel)bi.getSampleModel();
  168. if (noTransform) {
  169. if (abortRequested()) {
  170. processReadAborted();
  171. return bi;
  172. }
  173. // If noTransform is necessary, read the data.
  174. iis.read(((DataBufferByte)tile.getDataBuffer()).getData(),
  175. 0, height*sm.getScanlineStride());
  176. processImageUpdate(bi,
  177. 0, 0,
  178. width, height, 1, 1,
  179. new int[]{0});
  180. processImageProgress(100.0F);
  181. } else {
  182. int len = (this.width + 7) / 8;
  183. byte[] buf = new byte[len];
  184. byte[] data = ((DataBufferByte)tile.getDataBuffer()).getData();
  185. int lineStride = sm.getScanlineStride();
  186. iis.skipBytes(len * sourceRegion.y);
  187. int skipLength = len * (scaleY - 1);
  188. // cache the values to avoid duplicated computation
  189. int[] srcOff = new int[destinationRegion.width];
  190. int[] destOff = new int[destinationRegion.width];
  191. int[] srcPos = new int[destinationRegion.width];
  192. int[] destPos = new int[destinationRegion.width];
  193. for (int i = destinationRegion.x, x = sourceRegion.x, j = 0;
  194. i < destinationRegion.x + destinationRegion.width;
  195. i++, j++, x += scaleX) {
  196. srcPos[j] = x >> 3;
  197. srcOff[j] = 7 - (x & 7);
  198. destPos[j] = i >> 3;
  199. destOff[j] = 7 - (i & 7);
  200. }
  201. for (int j = 0, y = sourceRegion.y,
  202. k = destinationRegion.y * lineStride;
  203. j < destinationRegion.height; j++, y+=scaleY) {
  204. if (abortRequested())
  205. break;
  206. iis.read(buf, 0, len);
  207. for (int i = 0; i < destinationRegion.width; i++) {
  208. //get the bit and assign to the data buffer of the raster
  209. int v = (buf[srcPos[i]] >> srcOff[i]) & 1;
  210. data[k + destPos[i]] |= v << destOff[i];
  211. }
  212. k += lineStride;
  213. iis.skipBytes(skipLength);
  214. processImageUpdate(bi,
  215. 0, j,
  216. destinationRegion.width, 1, 1, 1,
  217. new int[]{0});
  218. processImageProgress(100.0F*jdestinationRegion.height);
  219. }
  220. }
  221. if (abortRequested())
  222. processReadAborted();
  223. else
  224. processImageComplete();
  225. return bi;
  226. }
  227. public boolean canReadRaster() {
  228. return true;
  229. }
  230. public Raster readRaster(int imageIndex,
  231. ImageReadParam param) throws IOException {
  232. BufferedImage bi = read(imageIndex, param);
  233. return bi.getData();
  234. }
  235. public void reset() {
  236. super.reset();
  237. iis = null;
  238. gotHeader = false;
  239. }
  240. private int readMultiByteInteger() throws IOException {
  241. int value = iis.readByte();
  242. int result = value & 0x7f;
  243. while((value & 0x80) == 0x80) {
  244. result <<= 7;
  245. value = iis.readByte();
  246. result |= (value & 0x7f);
  247. }
  248. return result;
  249. }
  250. /*
  251. * This method verifies that given byte is valid wbmp type marker.
  252. * At the moment only 0x0 marker is described by wbmp spec.
  253. */
  254. boolean isValidWbmpType(int type) {
  255. return type == 0;
  256. }
  257. }