PageRenderTime 56ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/src/share/classes/com/sun/imageio/plugins/bmp/BMPImageReader.java

https://bitbucket.org/infinitekind/openjdk7u-jdk
Java | 1821 lines | 1383 code | 267 blank | 171 comment | 328 complexity | 04f154ffd4bb4090cbe68e473914bacd MD5 | raw file
Possible License(s): GPL-2.0, BSD-3-Clause-No-Nuclear-License-2014, LGPL-3.0

Large files files are truncated, but you can click here to view the full file

  1. /*
  2. * Copyright (c) 2003, 2005, 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. package com.sun.imageio.plugins.bmp;
  26. import java.awt.Point;
  27. import java.awt.Rectangle;
  28. import java.awt.Transparency;
  29. import java.awt.color.ColorSpace;
  30. import java.awt.color.ICC_ColorSpace;
  31. import java.awt.color.ICC_Profile;
  32. import java.awt.image.BufferedImage;
  33. import java.awt.image.ColorModel;
  34. import java.awt.image.ComponentColorModel;
  35. import java.awt.image.ComponentSampleModel;
  36. import java.awt.image.DataBuffer;
  37. import java.awt.image.DataBufferByte;
  38. import java.awt.image.DataBufferInt;
  39. import java.awt.image.DataBufferUShort;
  40. import java.awt.image.DirectColorModel;
  41. import java.awt.image.IndexColorModel;
  42. import java.awt.image.MultiPixelPackedSampleModel;
  43. import java.awt.image.PixelInterleavedSampleModel;
  44. import java.awt.image.Raster;
  45. import java.awt.image.SampleModel;
  46. import java.awt.image.SinglePixelPackedSampleModel;
  47. import java.awt.image.WritableRaster;
  48. import javax.imageio.IIOException;
  49. import javax.imageio.ImageIO;
  50. import javax.imageio.ImageReader;
  51. import javax.imageio.ImageReadParam;
  52. import javax.imageio.ImageTypeSpecifier;
  53. import javax.imageio.metadata.IIOMetadata;
  54. import javax.imageio.spi.ImageReaderSpi;
  55. import javax.imageio.stream.ImageInputStream;
  56. import javax.imageio.event.IIOReadProgressListener;
  57. import javax.imageio.event.IIOReadUpdateListener;
  58. import javax.imageio.event.IIOReadWarningListener;
  59. import java.io.*;
  60. import java.nio.*;
  61. import java.security.AccessController;
  62. import java.security.PrivilegedAction;
  63. import java.util.ArrayList;
  64. import java.util.Iterator;
  65. import java.util.StringTokenizer;
  66. import com.sun.imageio.plugins.common.ImageUtil;
  67. import com.sun.imageio.plugins.common.I18N;
  68. /** This class is the Java Image IO plugin reader for BMP images.
  69. * It may subsample the image, clip the image, select sub-bands,
  70. * and shift the decoded image origin if the proper decoding parameter
  71. * are set in the provided <code>ImageReadParam</code>.
  72. *
  73. * This class supports Microsoft Windows Bitmap Version 3-5,
  74. * as well as OS/2 Bitmap Version 2.x (for single-image BMP file).
  75. */
  76. public class BMPImageReader extends ImageReader implements BMPConstants {
  77. // BMP Image types
  78. private static final int VERSION_2_1_BIT = 0;
  79. private static final int VERSION_2_4_BIT = 1;
  80. private static final int VERSION_2_8_BIT = 2;
  81. private static final int VERSION_2_24_BIT = 3;
  82. private static final int VERSION_3_1_BIT = 4;
  83. private static final int VERSION_3_4_BIT = 5;
  84. private static final int VERSION_3_8_BIT = 6;
  85. private static final int VERSION_3_24_BIT = 7;
  86. private static final int VERSION_3_NT_16_BIT = 8;
  87. private static final int VERSION_3_NT_32_BIT = 9;
  88. private static final int VERSION_4_1_BIT = 10;
  89. private static final int VERSION_4_4_BIT = 11;
  90. private static final int VERSION_4_8_BIT = 12;
  91. private static final int VERSION_4_16_BIT = 13;
  92. private static final int VERSION_4_24_BIT = 14;
  93. private static final int VERSION_4_32_BIT = 15;
  94. private static final int VERSION_3_XP_EMBEDDED = 16;
  95. private static final int VERSION_4_XP_EMBEDDED = 17;
  96. private static final int VERSION_5_XP_EMBEDDED = 18;
  97. // BMP variables
  98. private long bitmapFileSize;
  99. private long bitmapOffset;
  100. private long compression;
  101. private long imageSize;
  102. private byte palette[];
  103. private int imageType;
  104. private int numBands;
  105. private boolean isBottomUp;
  106. private int bitsPerPixel;
  107. private int redMask, greenMask, blueMask, alphaMask;
  108. private SampleModel sampleModel, originalSampleModel;
  109. private ColorModel colorModel, originalColorModel;
  110. /** The input stream where reads from */
  111. private ImageInputStream iis = null;
  112. /** Indicates whether the header is read. */
  113. private boolean gotHeader = false;
  114. /** The original image width. */
  115. private int width;
  116. /** The original image height. */
  117. private int height;
  118. /** The destination region. */
  119. private Rectangle destinationRegion;
  120. /** The source region. */
  121. private Rectangle sourceRegion;
  122. /** The metadata from the stream. */
  123. private BMPMetadata metadata;
  124. /** The destination image. */
  125. private BufferedImage bi;
  126. /** Indicates whether subsampled, subregion is required, and offset is
  127. * defined
  128. */
  129. private boolean noTransform = true;
  130. /** Indicates whether subband is selected. */
  131. private boolean seleBand = false;
  132. /** The scaling factors. */
  133. private int scaleX, scaleY;
  134. /** source and destination bands. */
  135. private int[] sourceBands, destBands;
  136. /** Constructs <code>BMPImageReader</code> from the provided
  137. * <code>ImageReaderSpi</code>.
  138. */
  139. public BMPImageReader(ImageReaderSpi originator) {
  140. super(originator);
  141. }
  142. /** Overrides the method defined in the superclass. */
  143. public void setInput(Object input,
  144. boolean seekForwardOnly,
  145. boolean ignoreMetadata) {
  146. super.setInput(input, seekForwardOnly, ignoreMetadata);
  147. iis = (ImageInputStream) input; // Always works
  148. if(iis != null)
  149. iis.setByteOrder(ByteOrder.LITTLE_ENDIAN);
  150. resetHeaderInfo();
  151. }
  152. /** Overrides the method defined in the superclass. */
  153. public int getNumImages(boolean allowSearch) throws IOException {
  154. if (iis == null) {
  155. throw new IllegalStateException(I18N.getString("GetNumImages0"));
  156. }
  157. if (seekForwardOnly && allowSearch) {
  158. throw new IllegalStateException(I18N.getString("GetNumImages1"));
  159. }
  160. return 1;
  161. }
  162. public int getWidth(int imageIndex) throws IOException {
  163. checkIndex(imageIndex);
  164. readHeader();
  165. return width;
  166. }
  167. public int getHeight(int imageIndex) throws IOException {
  168. checkIndex(imageIndex);
  169. readHeader();
  170. return height;
  171. }
  172. private void checkIndex(int imageIndex) {
  173. if (imageIndex != 0) {
  174. throw new IndexOutOfBoundsException(I18N.getString("BMPImageReader0"));
  175. }
  176. }
  177. public void readHeader() throws IOException {
  178. if (gotHeader)
  179. return;
  180. if (iis == null) {
  181. throw new IllegalStateException("Input source not set!");
  182. }
  183. int profileData = 0, profileSize = 0;
  184. this.metadata = new BMPMetadata();
  185. iis.mark();
  186. // read and check the magic marker
  187. byte[] marker = new byte[2];
  188. iis.read(marker);
  189. if (marker[0] != 0x42 || marker[1] != 0x4d)
  190. throw new IllegalArgumentException(I18N.getString("BMPImageReader1"));
  191. // Read file size
  192. bitmapFileSize = iis.readUnsignedInt();
  193. // skip the two reserved fields
  194. iis.skipBytes(4);
  195. // Offset to the bitmap from the beginning
  196. bitmapOffset = iis.readUnsignedInt();
  197. // End File Header
  198. // Start BitmapCoreHeader
  199. long size = iis.readUnsignedInt();
  200. if (size == 12) {
  201. width = iis.readShort();
  202. height = iis.readShort();
  203. } else {
  204. width = iis.readInt();
  205. height = iis.readInt();
  206. }
  207. metadata.width = width;
  208. metadata.height = height;
  209. int planes = iis.readUnsignedShort();
  210. bitsPerPixel = iis.readUnsignedShort();
  211. //metadata.colorPlane = planes;
  212. metadata.bitsPerPixel = (short)bitsPerPixel;
  213. // As BMP always has 3 rgb bands, except for Version 5,
  214. // which is bgra
  215. numBands = 3;
  216. if (size == 12) {
  217. // Windows 2.x and OS/2 1.x
  218. metadata.bmpVersion = VERSION_2;
  219. // Classify the image type
  220. if (bitsPerPixel == 1) {
  221. imageType = VERSION_2_1_BIT;
  222. } else if (bitsPerPixel == 4) {
  223. imageType = VERSION_2_4_BIT;
  224. } else if (bitsPerPixel == 8) {
  225. imageType = VERSION_2_8_BIT;
  226. } else if (bitsPerPixel == 24) {
  227. imageType = VERSION_2_24_BIT;
  228. }
  229. // Read in the palette
  230. int numberOfEntries = (int)((bitmapOffset - 14 - size) / 3);
  231. int sizeOfPalette = numberOfEntries*3;
  232. palette = new byte[sizeOfPalette];
  233. iis.readFully(palette, 0, sizeOfPalette);
  234. metadata.palette = palette;
  235. metadata.paletteSize = numberOfEntries;
  236. } else {
  237. compression = iis.readUnsignedInt();
  238. imageSize = iis.readUnsignedInt();
  239. long xPelsPerMeter = iis.readInt();
  240. long yPelsPerMeter = iis.readInt();
  241. long colorsUsed = iis.readUnsignedInt();
  242. long colorsImportant = iis.readUnsignedInt();
  243. metadata.compression = (int)compression;
  244. metadata.xPixelsPerMeter = (int)xPelsPerMeter;
  245. metadata.yPixelsPerMeter = (int)yPelsPerMeter;
  246. metadata.colorsUsed = (int)colorsUsed;
  247. metadata.colorsImportant = (int)colorsImportant;
  248. if (size == 40) {
  249. // Windows 3.x and Windows NT
  250. switch((int)compression) {
  251. case BI_JPEG:
  252. case BI_PNG:
  253. metadata.bmpVersion = VERSION_3;
  254. imageType = VERSION_3_XP_EMBEDDED;
  255. break;
  256. case BI_RGB: // No compression
  257. case BI_RLE8: // 8-bit RLE compression
  258. case BI_RLE4: // 4-bit RLE compression
  259. // Read in the palette
  260. int numberOfEntries = (int)((bitmapOffset-14-size) / 4);
  261. int sizeOfPalette = numberOfEntries * 4;
  262. palette = new byte[sizeOfPalette];
  263. iis.readFully(palette, 0, sizeOfPalette);
  264. metadata.palette = palette;
  265. metadata.paletteSize = numberOfEntries;
  266. if (bitsPerPixel == 1) {
  267. imageType = VERSION_3_1_BIT;
  268. } else if (bitsPerPixel == 4) {
  269. imageType = VERSION_3_4_BIT;
  270. } else if (bitsPerPixel == 8) {
  271. imageType = VERSION_3_8_BIT;
  272. } else if (bitsPerPixel == 24) {
  273. imageType = VERSION_3_24_BIT;
  274. } else if (bitsPerPixel == 16) {
  275. imageType = VERSION_3_NT_16_BIT;
  276. redMask = 0x7C00;
  277. greenMask = 0x3E0;
  278. blueMask = (1 << 5) - 1;// 0x1F;
  279. metadata.redMask = redMask;
  280. metadata.greenMask = greenMask;
  281. metadata.blueMask = blueMask;
  282. } else if (bitsPerPixel == 32) {
  283. imageType = VERSION_3_NT_32_BIT;
  284. redMask = 0x00FF0000;
  285. greenMask = 0x0000FF00;
  286. blueMask = 0x000000FF;
  287. metadata.redMask = redMask;
  288. metadata.greenMask = greenMask;
  289. metadata.blueMask = blueMask;
  290. }
  291. metadata.bmpVersion = VERSION_3;
  292. break;
  293. case BI_BITFIELDS:
  294. if (bitsPerPixel == 16) {
  295. imageType = VERSION_3_NT_16_BIT;
  296. } else if (bitsPerPixel == 32) {
  297. imageType = VERSION_3_NT_32_BIT;
  298. }
  299. // BitsField encoding
  300. redMask = (int)iis.readUnsignedInt();
  301. greenMask = (int)iis.readUnsignedInt();
  302. blueMask = (int)iis.readUnsignedInt();
  303. metadata.redMask = redMask;
  304. metadata.greenMask = greenMask;
  305. metadata.blueMask = blueMask;
  306. if (colorsUsed != 0) {
  307. // there is a palette
  308. sizeOfPalette = (int)colorsUsed*4;
  309. palette = new byte[sizeOfPalette];
  310. iis.readFully(palette, 0, sizeOfPalette);
  311. metadata.palette = palette;
  312. metadata.paletteSize = (int)colorsUsed;
  313. }
  314. metadata.bmpVersion = VERSION_3_NT;
  315. break;
  316. default:
  317. throw new
  318. RuntimeException(I18N.getString("BMPImageReader2"));
  319. }
  320. } else if (size == 108 || size == 124) {
  321. // Windows 4.x BMP
  322. if (size == 108)
  323. metadata.bmpVersion = VERSION_4;
  324. else if (size == 124)
  325. metadata.bmpVersion = VERSION_5;
  326. // rgb masks, valid only if comp is BI_BITFIELDS
  327. redMask = (int)iis.readUnsignedInt();
  328. greenMask = (int)iis.readUnsignedInt();
  329. blueMask = (int)iis.readUnsignedInt();
  330. // Only supported for 32bpp BI_RGB argb
  331. alphaMask = (int)iis.readUnsignedInt();
  332. long csType = iis.readUnsignedInt();
  333. int redX = iis.readInt();
  334. int redY = iis.readInt();
  335. int redZ = iis.readInt();
  336. int greenX = iis.readInt();
  337. int greenY = iis.readInt();
  338. int greenZ = iis.readInt();
  339. int blueX = iis.readInt();
  340. int blueY = iis.readInt();
  341. int blueZ = iis.readInt();
  342. long gammaRed = iis.readUnsignedInt();
  343. long gammaGreen = iis.readUnsignedInt();
  344. long gammaBlue = iis.readUnsignedInt();
  345. if (size == 124) {
  346. metadata.intent = iis.readInt();
  347. profileData = iis.readInt();
  348. profileSize = iis.readInt();
  349. iis.skipBytes(4);
  350. }
  351. metadata.colorSpace = (int)csType;
  352. if (csType == LCS_CALIBRATED_RGB) {
  353. // All the new fields are valid only for this case
  354. metadata.redX = redX;
  355. metadata.redY = redY;
  356. metadata.redZ = redZ;
  357. metadata.greenX = greenX;
  358. metadata.greenY = greenY;
  359. metadata.greenZ = greenZ;
  360. metadata.blueX = blueX;
  361. metadata.blueY = blueY;
  362. metadata.blueZ = blueZ;
  363. metadata.gammaRed = (int)gammaRed;
  364. metadata.gammaGreen = (int)gammaGreen;
  365. metadata.gammaBlue = (int)gammaBlue;
  366. }
  367. // Read in the palette
  368. int numberOfEntries = (int)((bitmapOffset-14-size) / 4);
  369. int sizeOfPalette = numberOfEntries*4;
  370. palette = new byte[sizeOfPalette];
  371. iis.readFully(palette, 0, sizeOfPalette);
  372. metadata.palette = palette;
  373. metadata.paletteSize = numberOfEntries;
  374. switch ((int)compression) {
  375. case BI_JPEG:
  376. case BI_PNG:
  377. if (size == 108) {
  378. imageType = VERSION_4_XP_EMBEDDED;
  379. } else if (size == 124) {
  380. imageType = VERSION_5_XP_EMBEDDED;
  381. }
  382. break;
  383. default:
  384. if (bitsPerPixel == 1) {
  385. imageType = VERSION_4_1_BIT;
  386. } else if (bitsPerPixel == 4) {
  387. imageType = VERSION_4_4_BIT;
  388. } else if (bitsPerPixel == 8) {
  389. imageType = VERSION_4_8_BIT;
  390. } else if (bitsPerPixel == 16) {
  391. imageType = VERSION_4_16_BIT;
  392. if ((int)compression == BI_RGB) {
  393. redMask = 0x7C00;
  394. greenMask = 0x3E0;
  395. blueMask = 0x1F;
  396. }
  397. } else if (bitsPerPixel == 24) {
  398. imageType = VERSION_4_24_BIT;
  399. } else if (bitsPerPixel == 32) {
  400. imageType = VERSION_4_32_BIT;
  401. if ((int)compression == BI_RGB) {
  402. redMask = 0x00FF0000;
  403. greenMask = 0x0000FF00;
  404. blueMask = 0x000000FF;
  405. }
  406. }
  407. metadata.redMask = redMask;
  408. metadata.greenMask = greenMask;
  409. metadata.blueMask = blueMask;
  410. metadata.alphaMask = alphaMask;
  411. }
  412. } else {
  413. throw new
  414. RuntimeException(I18N.getString("BMPImageReader3"));
  415. }
  416. }
  417. if (height > 0) {
  418. // bottom up image
  419. isBottomUp = true;
  420. } else {
  421. // top down image
  422. isBottomUp = false;
  423. height = Math.abs(height);
  424. }
  425. // Reset Image Layout so there's only one tile.
  426. //Define the color space
  427. ColorSpace colorSpace = ColorSpace.getInstance(ColorSpace.CS_sRGB);
  428. if (metadata.colorSpace == PROFILE_LINKED ||
  429. metadata.colorSpace == PROFILE_EMBEDDED) {
  430. iis.mark();
  431. iis.skipBytes(profileData - size);
  432. byte[] profile = new byte[profileSize];
  433. iis.readFully(profile, 0, profileSize);
  434. iis.reset();
  435. try {
  436. if (metadata.colorSpace == PROFILE_LINKED &&
  437. isLinkedProfileAllowed() &&
  438. !isUncOrDevicePath(profile))
  439. {
  440. String path = new String(profile, "windows-1252");
  441. colorSpace =
  442. new ICC_ColorSpace(ICC_Profile.getInstance(path));
  443. } else {
  444. colorSpace =
  445. new ICC_ColorSpace(ICC_Profile.getInstance(profile));
  446. }
  447. } catch (Exception e) {
  448. colorSpace = ColorSpace.getInstance(ColorSpace.CS_sRGB);
  449. }
  450. }
  451. if (bitsPerPixel == 0 ||
  452. compression == BI_JPEG || compression == BI_PNG )
  453. {
  454. // the colorModel and sampleModel will be initialzed
  455. // by the reader of embedded image
  456. colorModel = null;
  457. sampleModel = null;
  458. } else if (bitsPerPixel == 1 || bitsPerPixel == 4 || bitsPerPixel == 8) {
  459. // When number of bitsPerPixel is <= 8, we use IndexColorModel.
  460. numBands = 1;
  461. if (bitsPerPixel == 8) {
  462. int[] bandOffsets = new int[numBands];
  463. for (int i = 0; i < numBands; i++) {
  464. bandOffsets[i] = numBands -1 -i;
  465. }
  466. sampleModel =
  467. new PixelInterleavedSampleModel(DataBuffer.TYPE_BYTE,
  468. width, height,
  469. numBands,
  470. numBands * width,
  471. bandOffsets);
  472. } else {
  473. // 1 and 4 bit pixels can be stored in a packed format.
  474. sampleModel =
  475. new MultiPixelPackedSampleModel(DataBuffer.TYPE_BYTE,
  476. width, height,
  477. bitsPerPixel);
  478. }
  479. // Create IndexColorModel from the palette.
  480. byte r[], g[], b[];
  481. if (imageType == VERSION_2_1_BIT ||
  482. imageType == VERSION_2_4_BIT ||
  483. imageType == VERSION_2_8_BIT) {
  484. size = palette.length/3;
  485. if (size > 256) {
  486. size = 256;
  487. }
  488. int off;
  489. r = new byte[(int)size];
  490. g = new byte[(int)size];
  491. b = new byte[(int)size];
  492. for (int i=0; i<(int)size; i++) {
  493. off = 3 * i;
  494. b[i] = palette[off];
  495. g[i] = palette[off+1];
  496. r[i] = palette[off+2];
  497. }
  498. } else {
  499. size = palette.length/4;
  500. if (size > 256) {
  501. size = 256;
  502. }
  503. int off;
  504. r = new byte[(int)size];
  505. g = new byte[(int)size];
  506. b = new byte[(int)size];
  507. for (int i=0; i<size; i++) {
  508. off = 4 * i;
  509. b[i] = palette[off];
  510. g[i] = palette[off+1];
  511. r[i] = palette[off+2];
  512. }
  513. }
  514. if (ImageUtil.isIndicesForGrayscale(r, g, b))
  515. colorModel =
  516. ImageUtil.createColorModel(null, sampleModel);
  517. else
  518. colorModel = new IndexColorModel(bitsPerPixel, (int)size, r, g, b);
  519. } else if (bitsPerPixel == 16) {
  520. numBands = 3;
  521. sampleModel =
  522. new SinglePixelPackedSampleModel(DataBuffer.TYPE_USHORT,
  523. width, height,
  524. new int[] {redMask, greenMask, blueMask});
  525. colorModel =
  526. new DirectColorModel(colorSpace,
  527. 16, redMask, greenMask, blueMask, 0,
  528. false, DataBuffer.TYPE_USHORT);
  529. } else if (bitsPerPixel == 32) {
  530. numBands = alphaMask == 0 ? 3 : 4;
  531. // The number of bands in the SampleModel is determined by
  532. // the length of the mask array passed in.
  533. int[] bitMasks = numBands == 3 ?
  534. new int[] {redMask, greenMask, blueMask} :
  535. new int[] {redMask, greenMask, blueMask, alphaMask};
  536. sampleModel =
  537. new SinglePixelPackedSampleModel(DataBuffer.TYPE_INT,
  538. width, height,
  539. bitMasks);
  540. colorModel =
  541. new DirectColorModel(colorSpace,
  542. 32, redMask, greenMask, blueMask, alphaMask,
  543. false, DataBuffer.TYPE_INT);
  544. } else {
  545. numBands = 3;
  546. // Create SampleModel
  547. int[] bandOffsets = new int[numBands];
  548. for (int i = 0; i < numBands; i++) {
  549. bandOffsets[i] = numBands -1 -i;
  550. }
  551. sampleModel =
  552. new PixelInterleavedSampleModel(DataBuffer.TYPE_BYTE,
  553. width, height,
  554. numBands,
  555. numBands * width,
  556. bandOffsets);
  557. colorModel =
  558. ImageUtil.createColorModel(colorSpace, sampleModel);
  559. }
  560. originalSampleModel = sampleModel;
  561. originalColorModel = colorModel;
  562. // Reset to the start of bitmap; then jump to the
  563. //start of image data
  564. iis.reset();
  565. iis.skipBytes(bitmapOffset);
  566. gotHeader = true;
  567. }
  568. public Iterator getImageTypes(int imageIndex)
  569. throws IOException {
  570. checkIndex(imageIndex);
  571. readHeader();
  572. ArrayList list = new ArrayList(1);
  573. list.add(new ImageTypeSpecifier(originalColorModel,
  574. originalSampleModel));
  575. return list.iterator();
  576. }
  577. public ImageReadParam getDefaultReadParam() {
  578. return new ImageReadParam();
  579. }
  580. public IIOMetadata getImageMetadata(int imageIndex)
  581. throws IOException {
  582. checkIndex(imageIndex);
  583. if (metadata == null) {
  584. readHeader();
  585. }
  586. return metadata;
  587. }
  588. public IIOMetadata getStreamMetadata() throws IOException {
  589. return null;
  590. }
  591. public boolean isRandomAccessEasy(int imageIndex) throws IOException {
  592. checkIndex(imageIndex);
  593. readHeader();
  594. return metadata.compression == BI_RGB;
  595. }
  596. public BufferedImage read(int imageIndex, ImageReadParam param)
  597. throws IOException {
  598. if (iis == null) {
  599. throw new IllegalStateException(I18N.getString("BMPImageReader5"));
  600. }
  601. checkIndex(imageIndex);
  602. clearAbortRequest();
  603. processImageStarted(imageIndex);
  604. if (param == null)
  605. param = getDefaultReadParam();
  606. //read header
  607. readHeader();
  608. sourceRegion = new Rectangle(0, 0, 0, 0);
  609. destinationRegion = new Rectangle(0, 0, 0, 0);
  610. computeRegions(param, this.width, this.height,
  611. param.getDestination(),
  612. sourceRegion,
  613. destinationRegion);
  614. scaleX = param.getSourceXSubsampling();
  615. scaleY = param.getSourceYSubsampling();
  616. // If the destination band is set used it
  617. sourceBands = param.getSourceBands();
  618. destBands = param.getDestinationBands();
  619. seleBand = (sourceBands != null) && (destBands != null);
  620. noTransform =
  621. destinationRegion.equals(new Rectangle(0, 0, width, height)) ||
  622. seleBand;
  623. if (!seleBand) {
  624. sourceBands = new int[numBands];
  625. destBands = new int[numBands];
  626. for (int i = 0; i < numBands; i++)
  627. destBands[i] = sourceBands[i] = i;
  628. }
  629. // If the destination is provided, then use it. Otherwise, create new one
  630. bi = param.getDestination();
  631. // Get the image data.
  632. WritableRaster raster = null;
  633. if (bi == null) {
  634. if (sampleModel != null && colorModel != null) {
  635. sampleModel =
  636. sampleModel.createCompatibleSampleModel(destinationRegion.x +
  637. destinationRegion.width,
  638. destinationRegion.y +
  639. destinationRegion.height);
  640. if (seleBand)
  641. sampleModel = sampleModel.createSubsetSampleModel(sourceBands);
  642. raster = Raster.createWritableRaster(sampleModel, new Point());
  643. bi = new BufferedImage(colorModel, raster, false, null);
  644. }
  645. } else {
  646. raster = bi.getWritableTile(0, 0);
  647. sampleModel = bi.getSampleModel();
  648. colorModel = bi.getColorModel();
  649. noTransform &= destinationRegion.equals(raster.getBounds());
  650. }
  651. byte bdata[] = null; // buffer for byte data
  652. short sdata[] = null; // buffer for short data
  653. int idata[] = null; // buffer for int data
  654. // the sampleModel can be null in case of embedded image
  655. if (sampleModel != null) {
  656. if (sampleModel.getDataType() == DataBuffer.TYPE_BYTE)
  657. bdata = (byte[])
  658. ((DataBufferByte)raster.getDataBuffer()).getData();
  659. else if (sampleModel.getDataType() == DataBuffer.TYPE_USHORT)
  660. sdata = (short[])
  661. ((DataBufferUShort)raster.getDataBuffer()).getData();
  662. else if (sampleModel.getDataType() == DataBuffer.TYPE_INT)
  663. idata = (int[])
  664. ((DataBufferInt)raster.getDataBuffer()).getData();
  665. }
  666. // There should only be one tile.
  667. switch(imageType) {
  668. case VERSION_2_1_BIT:
  669. // no compression
  670. read1Bit(bdata);
  671. break;
  672. case VERSION_2_4_BIT:
  673. // no compression
  674. read4Bit(bdata);
  675. break;
  676. case VERSION_2_8_BIT:
  677. // no compression
  678. read8Bit(bdata);
  679. break;
  680. case VERSION_2_24_BIT:
  681. // no compression
  682. read24Bit(bdata);
  683. break;
  684. case VERSION_3_1_BIT:
  685. // 1-bit images cannot be compressed.
  686. read1Bit(bdata);
  687. break;
  688. case VERSION_3_4_BIT:
  689. switch((int)compression) {
  690. case BI_RGB:
  691. read4Bit(bdata);
  692. break;
  693. case BI_RLE4:
  694. readRLE4(bdata);
  695. break;
  696. default:
  697. throw new
  698. RuntimeException(I18N.getString("BMPImageReader1"));
  699. }
  700. break;
  701. case VERSION_3_8_BIT:
  702. switch((int)compression) {
  703. case BI_RGB:
  704. read8Bit(bdata);
  705. break;
  706. case BI_RLE8:
  707. readRLE8(bdata);
  708. break;
  709. default:
  710. throw new
  711. RuntimeException(I18N.getString("BMPImageReader1"));
  712. }
  713. break;
  714. case VERSION_3_24_BIT:
  715. // 24-bit images are not compressed
  716. read24Bit(bdata);
  717. break;
  718. case VERSION_3_NT_16_BIT:
  719. read16Bit(sdata);
  720. break;
  721. case VERSION_3_NT_32_BIT:
  722. read32Bit(idata);
  723. break;
  724. case VERSION_3_XP_EMBEDDED:
  725. case VERSION_4_XP_EMBEDDED:
  726. case VERSION_5_XP_EMBEDDED:
  727. bi = readEmbedded((int)compression, bi, param);
  728. break;
  729. case VERSION_4_1_BIT:
  730. read1Bit(bdata);
  731. break;
  732. case VERSION_4_4_BIT:
  733. switch((int)compression) {
  734. case BI_RGB:
  735. read4Bit(bdata);
  736. break;
  737. case BI_RLE4:
  738. readRLE4(bdata);
  739. break;
  740. default:
  741. throw new
  742. RuntimeException(I18N.getString("BMPImageReader1"));
  743. }
  744. case VERSION_4_8_BIT:
  745. switch((int)compression) {
  746. case BI_RGB:
  747. read8Bit(bdata);
  748. break;
  749. case BI_RLE8:
  750. readRLE8(bdata);
  751. break;
  752. default:
  753. throw new
  754. RuntimeException(I18N.getString("BMPImageReader1"));
  755. }
  756. break;
  757. case VERSION_4_16_BIT:
  758. read16Bit(sdata);
  759. break;
  760. case VERSION_4_24_BIT:
  761. read24Bit(bdata);
  762. break;
  763. case VERSION_4_32_BIT:
  764. read32Bit(idata);
  765. break;
  766. }
  767. if (abortRequested())
  768. processReadAborted();
  769. else
  770. processImageComplete();
  771. return bi;
  772. }
  773. public boolean canReadRaster() {
  774. return true;
  775. }
  776. public Raster readRaster(int imageIndex,
  777. ImageReadParam param) throws IOException {
  778. BufferedImage bi = read(imageIndex, param);
  779. return bi.getData();
  780. }
  781. private void resetHeaderInfo() {
  782. gotHeader = false;
  783. bi = null;
  784. sampleModel = originalSampleModel = null;
  785. colorModel = originalColorModel = null;
  786. }
  787. public void reset() {
  788. super.reset();
  789. iis = null;
  790. resetHeaderInfo();
  791. }
  792. // Deal with 1 Bit images using IndexColorModels
  793. private void read1Bit(byte[] bdata) throws IOException {
  794. int bytesPerScanline = (width + 7) / 8;
  795. int padding = bytesPerScanline % 4;
  796. if (padding != 0) {
  797. padding = 4 - padding;
  798. }
  799. int lineLength = bytesPerScanline + padding;
  800. if (noTransform) {
  801. int j = isBottomUp ? (height -1)*bytesPerScanline : 0;
  802. for (int i=0; i<height; i++) {
  803. if (abortRequested()) {
  804. break;
  805. }
  806. iis.readFully(bdata, j, bytesPerScanline);
  807. iis.skipBytes(padding);
  808. j += isBottomUp ? -bytesPerScanline : bytesPerScanline;
  809. processImageUpdate(bi, 0, i,
  810. destinationRegion.width, 1, 1, 1,
  811. new int[]{0});
  812. processImageProgress(100.0F * i/destinationRegion.height);
  813. }
  814. } else {
  815. byte[] buf = new byte[lineLength];
  816. int lineStride =
  817. ((MultiPixelPackedSampleModel)sampleModel).getScanlineStride();
  818. if (isBottomUp) {
  819. int lastLine =
  820. sourceRegion.y + (destinationRegion.height - 1) * scaleY;
  821. iis.skipBytes(lineLength * (height - 1 - lastLine));
  822. } else
  823. iis.skipBytes(lineLength * sourceRegion.y);
  824. int skipLength = lineLength * (scaleY - 1);
  825. // cache the values to avoid duplicated computation
  826. int[] srcOff = new int[destinationRegion.width];
  827. int[] destOff = new int[destinationRegion.width];
  828. int[] srcPos = new int[destinationRegion.width];
  829. int[] destPos = new int[destinationRegion.width];
  830. for (int i = destinationRegion.x, x = sourceRegion.x, j = 0;
  831. i < destinationRegion.x + destinationRegion.width;
  832. i++, j++, x += scaleX) {
  833. srcPos[j] = x >> 3;
  834. srcOff[j] = 7 - (x & 7);
  835. destPos[j] = i >> 3;
  836. destOff[j] = 7 - (i & 7);
  837. }
  838. int k = destinationRegion.y * lineStride;
  839. if (isBottomUp)
  840. k += (destinationRegion.height - 1) * lineStride;
  841. for (int j = 0, y = sourceRegion.y;
  842. j < destinationRegion.height; j++, y+=scaleY) {
  843. if (abortRequested())
  844. break;
  845. iis.read(buf, 0, lineLength);
  846. for (int i = 0; i < destinationRegion.width; i++) {
  847. //get the bit and assign to the data buffer of the raster
  848. int v = (buf[srcPos[i]] >> srcOff[i]) & 1;
  849. bdata[k + destPos[i]] |= v << destOff[i];
  850. }
  851. k += isBottomUp ? -lineStride : lineStride;
  852. iis.skipBytes(skipLength);
  853. processImageUpdate(bi, 0, j,
  854. destinationRegion.width, 1, 1, 1,
  855. new int[]{0});
  856. processImageProgress(100.0F*j/destinationRegion.height);
  857. }
  858. }
  859. }
  860. // Method to read a 4 bit BMP image data
  861. private void read4Bit(byte[] bdata) throws IOException {
  862. int bytesPerScanline = (width + 1) / 2;
  863. // Padding bytes at the end of each scanline
  864. int padding = bytesPerScanline % 4;
  865. if (padding != 0)
  866. padding = 4 - padding;
  867. int lineLength = bytesPerScanline + padding;
  868. if (noTransform) {
  869. int j = isBottomUp ? (height -1) * bytesPerScanline : 0;
  870. for (int i=0; i<height; i++) {
  871. if (abortRequested()) {
  872. break;
  873. }
  874. iis.readFully(bdata, j, bytesPerScanline);
  875. iis.skipBytes(padding);
  876. j += isBottomUp ? -bytesPerScanline : bytesPerScanline;
  877. processImageUpdate(bi, 0, i,
  878. destinationRegion.width, 1, 1, 1,
  879. new int[]{0});
  880. processImageProgress(100.0F * i/destinationRegion.height);
  881. }
  882. } else {
  883. byte[] buf = new byte[lineLength];
  884. int lineStride =
  885. ((MultiPixelPackedSampleModel)sampleModel).getScanlineStride();
  886. if (isBottomUp) {
  887. int lastLine =
  888. sourceRegion.y + (destinationRegion.height - 1) * scaleY;
  889. iis.skipBytes(lineLength * (height - 1 - lastLine));
  890. } else
  891. iis.skipBytes(lineLength * sourceRegion.y);
  892. int skipLength = lineLength * (scaleY - 1);
  893. // cache the values to avoid duplicated computation
  894. int[] srcOff = new int[destinationRegion.width];
  895. int[] destOff = new int[destinationRegion.width];
  896. int[] srcPos = new int[destinationRegion.width];
  897. int[] destPos = new int[destinationRegion.width];
  898. for (int i = destinationRegion.x, x = sourceRegion.x, j = 0;
  899. i < destinationRegion.x + destinationRegion.width;
  900. i++, j++, x += scaleX) {
  901. srcPos[j] = x >> 1;
  902. srcOff[j] = (1 - (x & 1)) << 2;
  903. destPos[j] = i >> 1;
  904. destOff[j] = (1 - (i & 1)) << 2;
  905. }
  906. int k = destinationRegion.y * lineStride;
  907. if (isBottomUp)
  908. k += (destinationRegion.height - 1) * lineStride;
  909. for (int j = 0, y = sourceRegion.y;
  910. j < destinationRegion.height; j++, y+=scaleY) {
  911. if (abortRequested())
  912. break;
  913. iis.read(buf, 0, lineLength);
  914. for (int i = 0; i < destinationRegion.width; i++) {
  915. //get the bit and assign to the data buffer of the raster
  916. int v = (buf[srcPos[i]] >> srcOff[i]) & 0x0F;
  917. bdata[k + destPos[i]] |= v << destOff[i];
  918. }
  919. k += isBottomUp ? -lineStride : lineStride;
  920. iis.skipBytes(skipLength);
  921. processImageUpdate(bi, 0, j,
  922. destinationRegion.width, 1, 1, 1,
  923. new int[]{0});
  924. processImageProgress(100.0F*j/destinationRegion.height);
  925. }
  926. }
  927. }
  928. // Method to read 8 bit BMP image data
  929. private void read8Bit(byte[] bdata) throws IOException {
  930. // Padding bytes at the end of each scanline
  931. int padding = width % 4;
  932. if (padding != 0) {
  933. padding = 4 - padding;
  934. }
  935. int lineLength = width + padding;
  936. if (noTransform) {
  937. int j = isBottomUp ? (height -1) * width : 0;
  938. for (int i=0; i<height; i++) {
  939. if (abortRequested()) {
  940. break;
  941. }
  942. iis.readFully(bdata, j, width);
  943. iis.skipBytes(padding);
  944. j += isBottomUp ? -width : width;
  945. processImageUpdate(bi, 0, i,
  946. destinationRegion.width, 1, 1, 1,
  947. new int[]{0});
  948. processImageProgress(100.0F * i/destinationRegion.height);
  949. }
  950. } else {
  951. byte[] buf = new byte[lineLength];
  952. int lineStride =
  953. ((ComponentSampleModel)sampleModel).getScanlineStride();
  954. if (isBottomUp) {
  955. int lastLine =
  956. sourceRegion.y + (destinationRegion.height - 1) * scaleY;
  957. iis.skipBytes(lineLength * (height - 1 - lastLine));
  958. } else
  959. iis.skipBytes(lineLength * sourceRegion.y);
  960. int skipLength = lineLength * (scaleY - 1);
  961. int k = destinationRegion.y * lineStride;
  962. if (isBottomUp)
  963. k += (destinationRegion.height - 1) * lineStride;
  964. k += destinationRegion.x;
  965. for (int j = 0, y = sourceRegion.y;
  966. j < destinationRegion.height; j++, y+=scaleY) {
  967. if (abortRequested())
  968. break;
  969. iis.read(buf, 0, lineLength);
  970. for (int i = 0, m = sourceRegion.x;
  971. i < destinationRegion.width; i++, m += scaleX) {
  972. //get the bit and assign to the data buffer of the raster
  973. bdata[k + i] = buf[m];
  974. }
  975. k += isBottomUp ? -lineStride : lineStride;
  976. iis.skipBytes(skipLength);
  977. processImageUpdate(bi, 0, j,
  978. destinationRegion.width, 1, 1, 1,
  979. new int[]{0});
  980. processImageProgress(100.0F*j/destinationRegion.height);
  981. }
  982. }
  983. }
  984. // Method to read 24 bit BMP image data
  985. private void read24Bit(byte[] bdata) throws IOException {
  986. // Padding bytes at the end of each scanline
  987. // width * bitsPerPixel should be divisible by 32
  988. int padding = width * 3 % 4;
  989. if ( padding != 0)
  990. padding = 4 - padding;
  991. int lineStride = width * 3;
  992. int lineLength = lineStride + padding;
  993. if (noTransform) {
  994. int j = isBottomUp ? (height -1) * width * 3 : 0;
  995. for (int i=0; i<height; i++) {
  996. if (abortRequested()) {
  997. break;
  998. }
  999. iis.readFully(bdata, j, lineStride);
  1000. iis.skipBytes(padding);
  1001. j += isBottomUp ? -lineStride : lineStride;
  1002. processImageUpdate(bi, 0, i,
  1003. destinationRegion.width, 1, 1, 1,
  1004. new int[]{0});
  1005. processImageProgress(100.0F * i/destinationRegion.height);
  1006. }
  1007. } else {
  1008. byte[] buf = new byte[lineLength];
  1009. lineStride =
  1010. ((ComponentSampleModel)sampleModel).getScanlineStride();
  1011. if (isBottomUp) {
  1012. int lastLine =
  1013. sourceRegion.y + (destinationRegion.height - 1) * scaleY;
  1014. iis.skipBytes(lineLength * (height - 1 - lastLine));
  1015. } else
  1016. iis.skipBytes(lineLength * sourceRegion.y);
  1017. int skipLength = lineLength * (scaleY - 1);
  1018. int k = destinationRegion.y * lineStride;
  1019. if (isBottomUp)
  1020. k += (destinationRegion.height - 1) * lineStride;
  1021. k += destinationRegion.x * 3;
  1022. for (int j = 0, y = sourceRegion.y;
  1023. j < destinationRegion.height; j++, y+=scaleY) {
  1024. if (abortRequested())
  1025. break;
  1026. iis.read(buf, 0, lineLength);
  1027. for (int i = 0, m = 3 * sourceRegion.x;
  1028. i < destinationRegion.width; i++, m += 3 * scaleX) {
  1029. //get the bit and assign to the data buffer of the raster
  1030. int n = 3 * i + k;
  1031. for (int b = 0; b < destBands.length; b++)
  1032. bdata[n + destBands[b]] = buf[m + sourceBands[b]];
  1033. }
  1034. k += isBottomUp ? -lineStride : lineStride;
  1035. iis.skipBytes(skipLength);
  1036. processImageUpdate(bi, 0, j,
  1037. destinationRegion.width, 1, 1, 1,
  1038. new int[]{0});
  1039. processImageProgress(100.0F*j/destinationRegion.height);
  1040. }
  1041. }
  1042. }
  1043. private void read16Bit(short sdata[]) throws IOException {
  1044. // Padding bytes at the end of each scanline
  1045. // width * bitsPerPixel should be divisible by 32
  1046. int padding = width * 2 % 4;
  1047. if ( padding != 0)
  1048. padding = 4 - padding;
  1049. int lineLength = width + padding / 2;
  1050. if (noTransform) {
  1051. int j = isBottomUp ? (height -1) * width : 0;
  1052. for (int i=0; i<height; i++) {
  1053. if (abortRequested()) {
  1054. break;
  1055. }
  1056. iis.readFully(sdata, j, width);
  1057. iis.skipBytes(padding);
  1058. j += isBottomUp ? -width : width;
  1059. processImageUpdate(bi, 0, i,
  1060. destinationRegion.width, 1, 1, 1,
  1061. new int[]{0});
  1062. processImageProgress(100.0F * i/destinationRegion.height);
  1063. }
  1064. } else {
  1065. short[] buf = new short[lineLength];
  1066. int lineStride =
  1067. ((SinglePixelPackedSampleModel)sampleModel).getScanlineStride();
  1068. if (isBottomUp) {
  1069. int lastLine =
  1070. sourceRegion.y + (destinationRegion.height - 1) * scaleY;
  1071. iis.skipBytes(lineLength * (height - 1 - lastLine) << 1);
  1072. } else
  1073. iis.skipBytes(lineLength * sourceRegion.y << 1);
  1074. int skipLength = lineLength * (scaleY - 1) << 1;
  1075. int k = destinationRegion.y * lineStride;
  1076. if (isBottomUp)
  1077. k += (destinationRegion.height - 1) * lineStride;
  1078. k += destinationRegion.x;
  1079. for (int j = 0, y = sourceRegion.y;
  1080. j < destinationRegion.height; j++, y+=scaleY) {
  1081. if (abortRequested())
  1082. break;
  1083. iis.readFully(buf, 0, lineLength);
  1084. for (int i = 0, m = sourceRegion.x;
  1085. i < destinationRegion.width; i++, m += scaleX) {
  1086. //get the bit and assign to the data buffer of the raster
  1087. sdata[k + i] = buf[m];
  1088. }
  1089. k += isBottomUp ? -lineStride : lineStride;
  1090. iis.skipBytes(skipLength);
  1091. processImageUpdate(bi, 0, j,
  1092. destinationRegion.width, 1, 1, 1,
  1093. new int[]{0});
  1094. processImageProgress(100.0F*j/destinationRegion.height);
  1095. }
  1096. }
  1097. }
  1098. private void read32Bit(int idata[]) throws IOException {
  1099. if (noTransform) {
  1100. int j = isBottomUp ? (height -1) * width : 0;
  1101. for (int i=0; i<height; i++) {
  1102. if (abortRequested()) {
  1103. break;
  1104. }
  1105. iis.readFully(idata, j, width);
  1106. j += isBottomUp ? -width : width;
  1107. processImageUpdate(bi, 0, i,
  1108. destinationRegion.width, 1, 1, 1,
  1109. new int[]{0});
  1110. processImageProgress(100.0F * i/destinationRegion.height);
  1111. }
  1112. } else {
  1113. int[] buf = new int[width];
  1114. int lineStride =
  1115. ((SinglePixelPackedSampleModel)sampleModel).getScanlineStride();
  1116. if (isBottomUp) {
  1117. int lastLine =
  1118. sourceRegion.y + (destinationRegion.height - 1) * scaleY;
  1119. iis.skipBytes(width * (height - 1 - lastLine) << 2);
  1120. } else
  1121. iis.skipBytes(width * sourceRegion.y << 2);
  1122. int skipLength = width * (scaleY - 1) << 2;
  1123. int k = destinationRegion.y * lineStride;
  1124. if (isBottomUp)
  1125. k += (destinationRegion.height - 1) * lineStride;
  1126. k += destinationRegion.x;
  1127. for (int j = 0, y = sourceRegion.y;
  1128. j < destinationRegion.height; j++, y+=scaleY) {
  1129. if (abortRequested())
  1130. break;
  1131. iis.readFully(buf, 0, width);
  1132. for (int i = 0, m = sourceRegion.x;
  1133. i < destinationRegion.width; i++, m += scaleX) {
  1134. //get the bit and assign to the data buffer of the raster
  1135. idata[k + i] = buf[m];
  1136. }
  1137. k += isBottomUp ? -lineStride : lineStride;
  1138. iis.skipBytes(skipLength);
  1139. processImageUpdate(bi, 0, j,
  1140. destinationRegion.width, 1, 1, 1,
  1141. new int[]{0});
  1142. processImageProgress(100.0F*j/destinationRegion.height);
  1143. }
  1144. }
  1145. }
  1146. private void readRLE8(byte bdata[]) throws IOException {
  1147. // If imageSize field is not provided, calculate it.
  1148. int imSize = (int)imageSize;
  1149. if (imSize == 0) {
  1150. imSize = (int)(bitmapFileSize - bitmapOffset);
  1151. }
  1152. int padding = 0;
  1153. // If width is not 32 bit aligned, then while uncompressing each
  1154. // scanline will have padding bytes, calculate the amount of padding
  1155. int remainder = width % 4;
  1156. if (remainder != 0) {
  1157. padding = 4 - remainder;
  1158. }
  1159. // Read till we have the whole image
  1160. byte values[] = new byte[imSize];
  1161. int bytesRead = 0;
  1162. iis.readFully(values, 0, imSize);
  1163. // Since data is compressed, decompress it
  1164. decodeRLE8(imSize, padding, values, bdata);
  1165. }
  1166. private void decodeRLE8(int imSize,
  1167. int pad

Large files files are truncated, but you can click here to view the full file