1. /*
  2. * @(#)BMPImageReader.java 1.11 03/12/19 16:53:53
  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.bmp;
  8. import java.awt.Point;
  9. import java.awt.Rectangle;
  10. import java.awt.Transparency;
  11. import java.awt.color.ColorSpace;
  12. import java.awt.color.ICC_ColorSpace;
  13. import java.awt.color.ICC_Profile;
  14. import java.awt.image.BufferedImage;
  15. import java.awt.image.ColorModel;
  16. import java.awt.image.ComponentColorModel;
  17. import java.awt.image.ComponentSampleModel;
  18. import java.awt.image.DataBuffer;
  19. import java.awt.image.DataBufferByte;
  20. import java.awt.image.DataBufferInt;
  21. import java.awt.image.DataBufferUShort;
  22. import java.awt.image.DirectColorModel;
  23. import java.awt.image.IndexColorModel;
  24. import java.awt.image.MultiPixelPackedSampleModel;
  25. import java.awt.image.PixelInterleavedSampleModel;
  26. import java.awt.image.Raster;
  27. import java.awt.image.SampleModel;
  28. import java.awt.image.SinglePixelPackedSampleModel;
  29. import java.awt.image.WritableRaster;
  30. import javax.imageio.IIOException;
  31. import javax.imageio.ImageIO;
  32. import javax.imageio.ImageReader;
  33. import javax.imageio.ImageReadParam;
  34. import javax.imageio.ImageTypeSpecifier;
  35. import javax.imageio.metadata.IIOMetadata;
  36. import javax.imageio.spi.ImageReaderSpi;
  37. import javax.imageio.stream.ImageInputStream;
  38. import javax.imageio.event.IIOReadProgressListener;
  39. import javax.imageio.event.IIOReadUpdateListener;
  40. import javax.imageio.event.IIOReadWarningListener;
  41. import java.io.*;
  42. import java.nio.*;
  43. import java.util.ArrayList;
  44. import java.util.Iterator;
  45. import java.util.StringTokenizer;
  46. import com.sun.imageio.plugins.common.ImageUtil;
  47. import com.sun.imageio.plugins.common.I18N;
  48. /** This class is the Java Image IO plugin reader for BMP images.
  49. * It may subsample the image, clip the image, select sub-bands,
  50. * and shift the decoded image origin if the proper decoding parameter
  51. * are set in the provided <code>ImageReadParam</code>.
  52. *
  53. * This class supports Microsoft Windows Bitmap Version 3-5,
  54. * as well as OS/2 Bitmap Version 2.x (for single-image BMP file).
  55. */
  56. public class BMPImageReader extends ImageReader implements BMPConstants {
  57. // BMP Image types
  58. private static final int VERSION_2_1_BIT = 0;
  59. private static final int VERSION_2_4_BIT = 1;
  60. private static final int VERSION_2_8_BIT = 2;
  61. private static final int VERSION_2_24_BIT = 3;
  62. private static final int VERSION_3_1_BIT = 4;
  63. private static final int VERSION_3_4_BIT = 5;
  64. private static final int VERSION_3_8_BIT = 6;
  65. private static final int VERSION_3_24_BIT = 7;
  66. private static final int VERSION_3_NT_16_BIT = 8;
  67. private static final int VERSION_3_NT_32_BIT = 9;
  68. private static final int VERSION_4_1_BIT = 10;
  69. private static final int VERSION_4_4_BIT = 11;
  70. private static final int VERSION_4_8_BIT = 12;
  71. private static final int VERSION_4_16_BIT = 13;
  72. private static final int VERSION_4_24_BIT = 14;
  73. private static final int VERSION_4_32_BIT = 15;
  74. // BMP variables
  75. private long bitmapFileSize;
  76. private long bitmapOffset;
  77. private long compression;
  78. private long imageSize;
  79. private byte palette[];
  80. private int imageType;
  81. private int numBands;
  82. private boolean isBottomUp;
  83. private int bitsPerPixel;
  84. private int redMask, greenMask, blueMask, alphaMask;
  85. private SampleModel sampleModel, originalSampleModel;
  86. private ColorModel colorModel, originalColorModel;
  87. /** The input stream where reads from */
  88. private ImageInputStream iis = null;
  89. /** Indicates whether the header is read. */
  90. private boolean gotHeader = false;
  91. /** The original image width. */
  92. private int width;
  93. /** The original image height. */
  94. private int height;
  95. /** The destination region. */
  96. private Rectangle destinationRegion;
  97. /** The source region. */
  98. private Rectangle sourceRegion;
  99. /** The metadata from the stream. */
  100. private BMPMetadata metadata;
  101. /** The destination image. */
  102. private BufferedImage bi;
  103. /** Indicates whether subsampled, subregion is required, and offset is
  104. * defined
  105. */
  106. private boolean noTransform = true;
  107. /** Indicates whether subband is selected. */
  108. private boolean seleBand = false;
  109. /** The scaling factors. */
  110. private int scaleX, scaleY;
  111. /** source and destination bands. */
  112. private int[] sourceBands, destBands;
  113. /** Constructs <code>BMPImageReader</code> from the provided
  114. * <code>ImageReaderSpi</code>.
  115. */
  116. public BMPImageReader(ImageReaderSpi originator) {
  117. super(originator);
  118. }
  119. /** Overrides the method defined in the superclass. */
  120. public void setInput(Object input,
  121. boolean seekForwardOnly,
  122. boolean ignoreMetadata) {
  123. super.setInput(input, seekForwardOnly, ignoreMetadata);
  124. iis = (ImageInputStream) input; // Always works
  125. if(iis != null)
  126. iis.setByteOrder(ByteOrder.LITTLE_ENDIAN);
  127. resetHeaderInfo();
  128. }
  129. /** Overrides the method defined in the superclass. */
  130. public int getNumImages(boolean allowSearch) throws IOException {
  131. if (iis == null) {
  132. throw new IllegalStateException(I18N.getString("GetNumImages0"));
  133. }
  134. if (seekForwardOnly && allowSearch) {
  135. throw new IllegalStateException(I18N.getString("GetNumImages1"));
  136. }
  137. return 1;
  138. }
  139. public int getWidth(int imageIndex) throws IOException {
  140. checkIndex(imageIndex);
  141. readHeader();
  142. return width;
  143. }
  144. public int getHeight(int imageIndex) throws IOException {
  145. checkIndex(imageIndex);
  146. readHeader();
  147. return height;
  148. }
  149. private void checkIndex(int imageIndex) {
  150. if (imageIndex != 0) {
  151. throw new IndexOutOfBoundsException(I18N.getString("BMPImageReader0"));
  152. }
  153. }
  154. public void readHeader() throws IOException {
  155. if (gotHeader)
  156. return;
  157. if (iis == null) {
  158. throw new IllegalStateException("Input source not set!");
  159. }
  160. int profileData = 0, profileSize = 0;
  161. this.metadata = new BMPMetadata();
  162. iis.mark();
  163. // read and check the magic marker
  164. byte[] marker = new byte[2];
  165. iis.read(marker);
  166. if (marker[0] != 0x42 || marker[1] != 0x4d)
  167. throw new IllegalArgumentException(I18N.getString("BMPImageReader1"));
  168. // Read file size
  169. bitmapFileSize = iis.readUnsignedInt();
  170. // skip the two reserved fields
  171. iis.skipBytes(4);
  172. // Offset to the bitmap from the beginning
  173. bitmapOffset = iis.readUnsignedInt();
  174. // End File Header
  175. // Start BitmapCoreHeader
  176. long size = iis.readUnsignedInt();
  177. if (size == 12) {
  178. width = iis.readShort();
  179. height = iis.readShort();
  180. } else {
  181. width = iis.readInt();
  182. height = iis.readInt();
  183. }
  184. metadata.width = width;
  185. metadata.height = height;
  186. int planes = iis.readUnsignedShort();
  187. bitsPerPixel = iis.readUnsignedShort();
  188. //metadata.colorPlane = planes;
  189. metadata.bitsPerPixel = (short)bitsPerPixel;
  190. // As BMP always has 3 rgb bands, except for Version 5,
  191. // which is bgra
  192. numBands = 3;
  193. if (size == 12) {
  194. // Windows 2.x and OS/2 1.x
  195. metadata.bmpVersion = VERSION_2;
  196. // Classify the image type
  197. if (bitsPerPixel == 1) {
  198. imageType = VERSION_2_1_BIT;
  199. } else if (bitsPerPixel == 4) {
  200. imageType = VERSION_2_4_BIT;
  201. } else if (bitsPerPixel == 8) {
  202. imageType = VERSION_2_8_BIT;
  203. } else if (bitsPerPixel == 24) {
  204. imageType = VERSION_2_24_BIT;
  205. }
  206. // Read in the palette
  207. int numberOfEntries = (int)((bitmapOffset - 14 - size) / 3);
  208. int sizeOfPalette = numberOfEntries*3;
  209. palette = new byte[sizeOfPalette];
  210. iis.readFully(palette, 0, sizeOfPalette);
  211. metadata.palette = palette;
  212. metadata.paletteSize = numberOfEntries;
  213. } else {
  214. compression = iis.readUnsignedInt();
  215. imageSize = iis.readUnsignedInt();
  216. long xPelsPerMeter = iis.readInt();
  217. long yPelsPerMeter = iis.readInt();
  218. long colorsUsed = iis.readUnsignedInt();
  219. long colorsImportant = iis.readUnsignedInt();
  220. metadata.compression = (int)compression;
  221. metadata.xPixelsPerMeter = (int)xPelsPerMeter;
  222. metadata.yPixelsPerMeter = (int)yPelsPerMeter;
  223. metadata.colorsUsed = (int)colorsUsed;
  224. metadata.colorsImportant = (int)colorsImportant;
  225. if (size == 40) {
  226. // Windows 3.x and Windows NT
  227. switch((int)compression) {
  228. case BI_RGB: // No compression
  229. case BI_RLE8: // 8-bit RLE compression
  230. case BI_RLE4: // 4-bit RLE compression
  231. case BI_PNG:
  232. case BI_JPEG:
  233. // Read in the palette
  234. int numberOfEntries = (int)((bitmapOffset-14-size) / 4);
  235. int sizeOfPalette = numberOfEntries * 4;
  236. palette = new byte[sizeOfPalette];
  237. iis.readFully(palette, 0, sizeOfPalette);
  238. metadata.palette = palette;
  239. metadata.paletteSize = numberOfEntries;
  240. if (bitsPerPixel == 1) {
  241. imageType = VERSION_3_1_BIT;
  242. } else if (bitsPerPixel == 4) {
  243. imageType = VERSION_3_4_BIT;
  244. } else if (bitsPerPixel == 8) {
  245. imageType = VERSION_3_8_BIT;
  246. } else if (bitsPerPixel == 24) {
  247. imageType = VERSION_3_24_BIT;
  248. } else if (bitsPerPixel == 16) {
  249. imageType = VERSION_3_NT_16_BIT;
  250. redMask = 0x7C00;
  251. greenMask = 0x3E0;
  252. blueMask = (1 << 5) - 1;// 0x1F;
  253. metadata.redMask = redMask;
  254. metadata.greenMask = greenMask;
  255. metadata.blueMask = blueMask;
  256. } else if (bitsPerPixel == 32) {
  257. imageType = VERSION_3_NT_32_BIT;
  258. redMask = 0x00FF0000;
  259. greenMask = 0x0000FF00;
  260. blueMask = 0x000000FF;
  261. metadata.redMask = redMask;
  262. metadata.greenMask = greenMask;
  263. metadata.blueMask = blueMask;
  264. }
  265. metadata.bmpVersion = VERSION_3;
  266. break;
  267. case BI_BITFIELDS:
  268. if (bitsPerPixel == 16) {
  269. imageType = VERSION_3_NT_16_BIT;
  270. } else if (bitsPerPixel == 32) {
  271. imageType = VERSION_3_NT_32_BIT;
  272. }
  273. // BitsField encoding
  274. redMask = (int)iis.readUnsignedInt();
  275. greenMask = (int)iis.readUnsignedInt();
  276. blueMask = (int)iis.readUnsignedInt();
  277. metadata.redMask = redMask;
  278. metadata.greenMask = greenMask;
  279. metadata.blueMask = blueMask;
  280. if (colorsUsed != 0) {
  281. // there is a palette
  282. sizeOfPalette = (int)colorsUsed*4;
  283. palette = new byte[sizeOfPalette];
  284. iis.readFully(palette, 0, sizeOfPalette);
  285. metadata.palette = palette;
  286. metadata.paletteSize = (int)colorsUsed;
  287. }
  288. metadata.bmpVersion = VERSION_3_NT;
  289. break;
  290. default:
  291. throw new
  292. RuntimeException(I18N.getString("BMPImageReader2"));
  293. }
  294. } else if (size == 108 || size == 124) {
  295. // Windows 4.x BMP
  296. if (size == 108)
  297. metadata.bmpVersion = VERSION_4;
  298. else if (size == 124)
  299. metadata.bmpVersion = VERSION_5;
  300. // rgb masks, valid only if comp is BI_BITFIELDS
  301. redMask = (int)iis.readUnsignedInt();
  302. greenMask = (int)iis.readUnsignedInt();
  303. blueMask = (int)iis.readUnsignedInt();
  304. // Only supported for 32bpp BI_RGB argb
  305. alphaMask = (int)iis.readUnsignedInt();
  306. long csType = iis.readUnsignedInt();
  307. int redX = iis.readInt();
  308. int redY = iis.readInt();
  309. int redZ = iis.readInt();
  310. int greenX = iis.readInt();
  311. int greenY = iis.readInt();
  312. int greenZ = iis.readInt();
  313. int blueX = iis.readInt();
  314. int blueY = iis.readInt();
  315. int blueZ = iis.readInt();
  316. long gammaRed = iis.readUnsignedInt();
  317. long gammaGreen = iis.readUnsignedInt();
  318. long gammaBlue = iis.readUnsignedInt();
  319. if (size == 124) {
  320. metadata.intent = iis.readInt();
  321. profileData = iis.readInt();
  322. profileSize = iis.readInt();
  323. iis.skipBytes(4);
  324. }
  325. metadata.colorSpace = (int)csType;
  326. if (csType == LCS_CALIBRATED_RGB) {
  327. // All the new fields are valid only for this case
  328. metadata.redX = redX;
  329. metadata.redY = redY;
  330. metadata.redZ = redZ;
  331. metadata.greenX = greenX;
  332. metadata.greenY = greenY;
  333. metadata.greenZ = greenZ;
  334. metadata.blueX = blueX;
  335. metadata.blueY = blueY;
  336. metadata.blueZ = blueZ;
  337. metadata.gammaRed = (int)gammaRed;
  338. metadata.gammaGreen = (int)gammaGreen;
  339. metadata.gammaBlue = (int)gammaBlue;
  340. }
  341. // Read in the palette
  342. int numberOfEntries = (int)((bitmapOffset-14-size) / 4);
  343. int sizeOfPalette = numberOfEntries*4;
  344. palette = new byte[sizeOfPalette];
  345. iis.readFully(palette, 0, sizeOfPalette);
  346. metadata.palette = palette;
  347. metadata.paletteSize = numberOfEntries;
  348. if (bitsPerPixel == 1) {
  349. imageType = VERSION_4_1_BIT;
  350. } else if (bitsPerPixel == 4) {
  351. imageType = VERSION_4_4_BIT;
  352. } else if (bitsPerPixel == 8) {
  353. imageType = VERSION_4_8_BIT;
  354. } else if (bitsPerPixel == 16) {
  355. imageType = VERSION_4_16_BIT;
  356. if ((int)compression == BI_RGB) {
  357. redMask = 0x7C00;
  358. greenMask = 0x3E0;
  359. blueMask = 0x1F;
  360. }
  361. } else if (bitsPerPixel == 24) {
  362. imageType = VERSION_4_24_BIT;
  363. } else if (bitsPerPixel == 32) {
  364. imageType = VERSION_4_32_BIT;
  365. if ((int)compression == BI_RGB) {
  366. redMask = 0x00FF0000;
  367. greenMask = 0x0000FF00;
  368. blueMask = 0x000000FF;
  369. }
  370. }
  371. metadata.redMask = redMask;
  372. metadata.greenMask = greenMask;
  373. metadata.blueMask = blueMask;
  374. metadata.alphaMask = alphaMask;
  375. } else {
  376. throw new
  377. RuntimeException(I18N.getString("BMPImageReader3"));
  378. }
  379. }
  380. if (height > 0) {
  381. // bottom up image
  382. isBottomUp = true;
  383. } else {
  384. // top down image
  385. isBottomUp = false;
  386. height = Math.abs(height);
  387. }
  388. // Reset Image Layout so there's only one tile.
  389. //Define the color space
  390. ColorSpace colorSpace = ColorSpace.getInstance(ColorSpace.CS_sRGB);
  391. if (metadata.colorSpace == PROFILE_LINKED ||
  392. metadata.colorSpace == PROFILE_EMBEDDED) {
  393. iis.mark();
  394. iis.skipBytes(profileData - size);
  395. byte[] profile = new byte[profileSize];
  396. iis.readFully(profile, 0, profileSize);
  397. iis.reset();
  398. try {
  399. if (metadata.colorSpace == PROFILE_LINKED)
  400. colorSpace =
  401. new ICC_ColorSpace(ICC_Profile.getInstance(new String(profile)));
  402. else
  403. colorSpace =
  404. new ICC_ColorSpace(ICC_Profile.getInstance(profile));
  405. } catch (Exception e) {
  406. colorSpace = ColorSpace.getInstance(ColorSpace.CS_sRGB);
  407. }
  408. }
  409. // When number of bitsPerPixel is <= 8, we use IndexColorModel.
  410. if (bitsPerPixel == 1 || bitsPerPixel == 4 || bitsPerPixel == 8) {
  411. numBands = 1;
  412. if (bitsPerPixel == 8) {
  413. int[] bandOffsets = new int[numBands];
  414. for (int i = 0; i < numBands; i++) {
  415. bandOffsets[i] = numBands -1 -i;
  416. }
  417. sampleModel =
  418. new PixelInterleavedSampleModel(DataBuffer.TYPE_BYTE,
  419. width, height,
  420. numBands,
  421. numBands * width,
  422. bandOffsets);
  423. } else {
  424. // 1 and 4 bit pixels can be stored in a packed format.
  425. sampleModel =
  426. new MultiPixelPackedSampleModel(DataBuffer.TYPE_BYTE,
  427. width, height,
  428. bitsPerPixel);
  429. }
  430. // Create IndexColorModel from the palette.
  431. byte r[], g[], b[];
  432. if (imageType == VERSION_2_1_BIT ||
  433. imageType == VERSION_2_4_BIT ||
  434. imageType == VERSION_2_8_BIT) {
  435. size = palette.length3;
  436. if (size > 256) {
  437. size = 256;
  438. }
  439. int off;
  440. r = new byte[(int)size];
  441. g = new byte[(int)size];
  442. b = new byte[(int)size];
  443. for (int i=0; i<(int)size; i++) {
  444. off = 3 * i;
  445. b[i] = palette[off];
  446. g[i] = palette[off+1];
  447. r[i] = palette[off+2];
  448. }
  449. } else {
  450. size = palette.length4;
  451. if (size > 256) {
  452. size = 256;
  453. }
  454. int off;
  455. r = new byte[(int)size];
  456. g = new byte[(int)size];
  457. b = new byte[(int)size];
  458. for (int i=0; i<size; i++) {
  459. off = 4 * i;
  460. b[i] = palette[off];
  461. g[i] = palette[off+1];
  462. r[i] = palette[off+2];
  463. }
  464. }
  465. if (ImageUtil.isIndicesForGrayscale(r, g, b))
  466. colorModel =
  467. ImageUtil.createColorModel(null, sampleModel);
  468. else
  469. colorModel = new IndexColorModel(bitsPerPixel, (int)size, r, g, b);
  470. } else if (bitsPerPixel == 16) {
  471. numBands = 3;
  472. sampleModel =
  473. new SinglePixelPackedSampleModel(DataBuffer.TYPE_USHORT,
  474. width, height,
  475. new int[] {redMask, greenMask, blueMask});
  476. colorModel =
  477. new DirectColorModel(colorSpace,
  478. 16, redMask, greenMask, blueMask, 0,
  479. false, DataBuffer.TYPE_USHORT);
  480. } else if (bitsPerPixel == 32) {
  481. numBands = alphaMask == 0 ? 3 : 4;
  482. // The number of bands in the SampleModel is determined by
  483. // the length of the mask array passed in.
  484. int[] bitMasks = numBands == 3 ?
  485. new int[] {redMask, greenMask, blueMask} :
  486. new int[] {redMask, greenMask, blueMask, alphaMask};
  487. sampleModel =
  488. new SinglePixelPackedSampleModel(DataBuffer.TYPE_INT,
  489. width, height,
  490. bitMasks);
  491. colorModel =
  492. new DirectColorModel(colorSpace,
  493. 32, redMask, greenMask, blueMask, alphaMask,
  494. false, DataBuffer.TYPE_INT);
  495. } else {
  496. numBands = 3;
  497. // Create SampleModel
  498. int[] bandOffsets = new int[numBands];
  499. for (int i = 0; i < numBands; i++) {
  500. bandOffsets[i] = numBands -1 -i;
  501. }
  502. sampleModel =
  503. new PixelInterleavedSampleModel(DataBuffer.TYPE_BYTE,
  504. width, height,
  505. numBands,
  506. numBands * width,
  507. bandOffsets);
  508. colorModel =
  509. ImageUtil.createColorModel(colorSpace, sampleModel);
  510. }
  511. originalSampleModel = sampleModel;
  512. originalColorModel = colorModel;
  513. // Reset to the start of bitmap; then jump to the
  514. //start of image data
  515. iis.reset();
  516. iis.skipBytes(bitmapOffset);
  517. gotHeader = true;
  518. }
  519. public Iterator getImageTypes(int imageIndex)
  520. throws IOException {
  521. checkIndex(imageIndex);
  522. readHeader();
  523. ArrayList list = new ArrayList(1);
  524. list.add(new ImageTypeSpecifier(originalColorModel,
  525. originalSampleModel));
  526. return list.iterator();
  527. }
  528. public ImageReadParam getDefaultReadParam() {
  529. return new ImageReadParam();
  530. }
  531. public IIOMetadata getImageMetadata(int imageIndex)
  532. throws IOException {
  533. checkIndex(imageIndex);
  534. if (metadata == null) {
  535. readHeader();
  536. }
  537. return metadata;
  538. }
  539. public IIOMetadata getStreamMetadata() throws IOException {
  540. return null;
  541. }
  542. public boolean isRandomAccessEasy(int imageIndex) throws IOException {
  543. checkIndex(imageIndex);
  544. readHeader();
  545. return metadata.compression == BI_RGB;
  546. }
  547. public BufferedImage read(int imageIndex, ImageReadParam param)
  548. throws IOException {
  549. if (iis == null) {
  550. throw new IllegalStateException(I18N.getString("BMPImageReader5"));
  551. }
  552. checkIndex(imageIndex);
  553. clearAbortRequest();
  554. processImageStarted(imageIndex);
  555. if (param == null)
  556. param = getDefaultReadParam();
  557. //read header
  558. readHeader();
  559. sourceRegion = new Rectangle(0, 0, 0, 0);
  560. destinationRegion = new Rectangle(0, 0, 0, 0);
  561. computeRegions(param, this.width, this.height,
  562. param.getDestination(),
  563. sourceRegion,
  564. destinationRegion);
  565. scaleX = param.getSourceXSubsampling();
  566. scaleY = param.getSourceYSubsampling();
  567. // If the destination band is set used it
  568. sourceBands = param.getSourceBands();
  569. destBands = param.getDestinationBands();
  570. seleBand = (sourceBands != null) && (destBands != null);
  571. noTransform =
  572. destinationRegion.equals(new Rectangle(0, 0, width, height)) ||
  573. seleBand;
  574. if (!seleBand) {
  575. sourceBands = new int[numBands];
  576. destBands = new int[numBands];
  577. for (int i = 0; i < numBands; i++)
  578. destBands[i] = sourceBands[i] = i;
  579. }
  580. // If the destination is provided, then use it. Otherwise, create new one
  581. bi = param.getDestination();
  582. // Get the image data.
  583. WritableRaster raster = null;
  584. if (bi == null) {
  585. sampleModel =
  586. sampleModel.createCompatibleSampleModel(destinationRegion.x +
  587. destinationRegion.width,
  588. destinationRegion.y +
  589. destinationRegion.height);
  590. if (seleBand)
  591. sampleModel = sampleModel.createSubsetSampleModel(sourceBands);
  592. raster = Raster.createWritableRaster(sampleModel, new Point());
  593. bi = new BufferedImage(colorModel, raster, false, null);
  594. } else {
  595. raster = bi.getWritableTile(0, 0);
  596. sampleModel = bi.getSampleModel();
  597. colorModel = bi.getColorModel();
  598. noTransform &= destinationRegion.equals(raster.getBounds());
  599. }
  600. byte bdata[] = null; // buffer for byte data
  601. short sdata[] = null; // buffer for short data
  602. int idata[] = null; // buffer for int data
  603. if (sampleModel.getDataType() == DataBuffer.TYPE_BYTE)
  604. bdata = (byte[])((DataBufferByte)raster.getDataBuffer()).getData();
  605. else if (sampleModel.getDataType() == DataBuffer.TYPE_USHORT)
  606. sdata = (short[])((DataBufferUShort)raster.getDataBuffer()).getData();
  607. else if (sampleModel.getDataType() == DataBuffer.TYPE_INT)
  608. idata = (int[])((DataBufferInt)raster.getDataBuffer()).getData();
  609. // There should only be one tile.
  610. switch(imageType) {
  611. case VERSION_2_1_BIT:
  612. // no compression
  613. read1Bit(bdata);
  614. break;
  615. case VERSION_2_4_BIT:
  616. // no compression
  617. read4Bit(bdata);
  618. break;
  619. case VERSION_2_8_BIT:
  620. // no compression
  621. read8Bit(bdata);
  622. break;
  623. case VERSION_2_24_BIT:
  624. // no compression
  625. read24Bit(bdata);
  626. break;
  627. case VERSION_3_1_BIT:
  628. // 1-bit images cannot be compressed.
  629. read1Bit(bdata);
  630. break;
  631. case VERSION_3_4_BIT:
  632. switch((int)compression) {
  633. case BI_RGB:
  634. read4Bit(bdata);
  635. break;
  636. case BI_RLE4:
  637. readRLE4(bdata);
  638. break;
  639. case BI_JPEG:
  640. readEmbedded("JPEG", bi, param);
  641. break;
  642. case BI_PNG:
  643. readEmbedded("PNG", bi, param);
  644. break;
  645. default:
  646. throw new
  647. RuntimeException(I18N.getString("BMPImageReader1"));
  648. }
  649. break;
  650. case VERSION_3_8_BIT:
  651. switch((int)compression) {
  652. case BI_RGB:
  653. read8Bit(bdata);
  654. break;
  655. case BI_RLE8:
  656. readRLE8(bdata);
  657. break;
  658. case BI_JPEG:
  659. readEmbedded("JPEG", bi, param);
  660. break;
  661. case BI_PNG:
  662. readEmbedded("PNG", bi, param);
  663. break;
  664. default:
  665. throw new
  666. RuntimeException(I18N.getString("BMPImageReader1"));
  667. }
  668. break;
  669. case VERSION_3_24_BIT:
  670. // 24-bit images are not compressed
  671. read24Bit(bdata);
  672. break;
  673. case VERSION_3_NT_16_BIT:
  674. read16Bit(sdata);
  675. break;
  676. case VERSION_3_NT_32_BIT:
  677. switch((int)compression) {
  678. case BI_JPEG:
  679. iis.seek(54);
  680. readEmbedded("JPEG", bi, param);
  681. break;
  682. case BI_PNG:
  683. readEmbedded("PNG", bi, param);
  684. break;
  685. default:
  686. read32Bit(idata);
  687. }
  688. break;
  689. case VERSION_4_1_BIT:
  690. read1Bit(bdata);
  691. break;
  692. case VERSION_4_4_BIT:
  693. switch((int)compression) {
  694. case BI_RGB:
  695. read4Bit(bdata);
  696. break;
  697. case BI_RLE4:
  698. readRLE4(bdata);
  699. break;
  700. case BI_JPEG:
  701. readEmbedded("JPEG", bi, param);
  702. break;
  703. case BI_PNG:
  704. readEmbedded("PNG", bi, param);
  705. break;
  706. default:
  707. throw new
  708. RuntimeException(I18N.getString("BMPImageReader1"));
  709. }
  710. case VERSION_4_8_BIT:
  711. switch((int)compression) {
  712. case BI_RGB:
  713. read8Bit(bdata);
  714. break;
  715. case BI_RLE8:
  716. readRLE8(bdata);
  717. break;
  718. case BI_JPEG:
  719. readEmbedded("JPEG", bi, param);
  720. break;
  721. case BI_PNG:
  722. readEmbedded("PNG", bi, param);
  723. break;
  724. default:
  725. throw new
  726. RuntimeException(I18N.getString("BMPImageReader1"));
  727. }
  728. break;
  729. case VERSION_4_16_BIT:
  730. read16Bit(sdata);
  731. break;
  732. case VERSION_4_24_BIT:
  733. read24Bit(bdata);
  734. break;
  735. case VERSION_4_32_BIT:
  736. read32Bit(idata);
  737. break;
  738. }
  739. if (abortRequested())
  740. processReadAborted();
  741. else
  742. processImageComplete();
  743. return bi;
  744. }
  745. public boolean canReadRaster() {
  746. return true;
  747. }
  748. public Raster readRaster(int imageIndex,
  749. ImageReadParam param) throws IOException {
  750. BufferedImage bi = read(imageIndex, param);
  751. return bi.getData();
  752. }
  753. private void resetHeaderInfo() {
  754. gotHeader = false;
  755. bi = null;
  756. sampleModel = originalSampleModel = null;
  757. colorModel = originalColorModel = null;
  758. }
  759. public void reset() {
  760. super.reset();
  761. iis = null;
  762. resetHeaderInfo();
  763. }
  764. // Deal with 1 Bit images using IndexColorModels
  765. private void read1Bit(byte[] bdata) throws IOException {
  766. int bytesPerScanline = (width + 7) / 8;
  767. int padding = bytesPerScanline % 4;
  768. if (padding != 0) {
  769. padding = 4 - padding;
  770. }
  771. int lineLength = bytesPerScanline + padding;
  772. if (noTransform) {
  773. int j = isBottomUp ? (height -1)*bytesPerScanline : 0;
  774. for (int i=0; i<height; i++) {
  775. if (abortRequested()) {
  776. break;
  777. }
  778. iis.readFully(bdata, j, bytesPerScanline);
  779. iis.skipBytes(padding);
  780. j += isBottomUp ? -bytesPerScanline : bytesPerScanline;
  781. processImageUpdate(bi, 0, i,
  782. destinationRegion.width, 1, 1, 1,
  783. new int[]{0});
  784. processImageProgress(100.0F * idestinationRegion.height);
  785. }
  786. } else {
  787. byte[] buf = new byte[lineLength];
  788. int lineStride =
  789. ((MultiPixelPackedSampleModel)sampleModel).getScanlineStride();
  790. if (isBottomUp) {
  791. int lastLine =
  792. sourceRegion.y + (destinationRegion.height - 1) * scaleY;
  793. iis.skipBytes(lineLength * (height - 1 - lastLine));
  794. } else
  795. iis.skipBytes(lineLength * sourceRegion.y);
  796. int skipLength = lineLength * (scaleY - 1);
  797. // cache the values to avoid duplicated computation
  798. int[] srcOff = new int[destinationRegion.width];
  799. int[] destOff = new int[destinationRegion.width];
  800. int[] srcPos = new int[destinationRegion.width];
  801. int[] destPos = new int[destinationRegion.width];
  802. for (int i = destinationRegion.x, x = sourceRegion.x, j = 0;
  803. i < destinationRegion.x + destinationRegion.width;
  804. i++, j++, x += scaleX) {
  805. srcPos[j] = x >> 3;
  806. srcOff[j] = 7 - (x & 7);
  807. destPos[j] = i >> 3;
  808. destOff[j] = 7 - (i & 7);
  809. }
  810. int k = destinationRegion.y * lineStride;
  811. if (isBottomUp)
  812. k += (destinationRegion.height - 1) * lineStride;
  813. for (int j = 0, y = sourceRegion.y;
  814. j < destinationRegion.height; j++, y+=scaleY) {
  815. if (abortRequested())
  816. break;
  817. iis.read(buf, 0, lineLength);
  818. for (int i = 0; i < destinationRegion.width; i++) {
  819. //get the bit and assign to the data buffer of the raster
  820. int v = (buf[srcPos[i]] >> srcOff[i]) & 1;
  821. bdata[k + destPos[i]] |= v << destOff[i];
  822. }
  823. k += isBottomUp ? -lineStride : lineStride;
  824. iis.skipBytes(skipLength);
  825. processImageUpdate(bi, 0, j,
  826. destinationRegion.width, 1, 1, 1,
  827. new int[]{0});
  828. processImageProgress(100.0F*jdestinationRegion.height);
  829. }
  830. }
  831. }
  832. // Method to read a 4 bit BMP image data
  833. private void read4Bit(byte[] bdata) throws IOException {
  834. int bytesPerScanline = (width + 1) / 2;
  835. // Padding bytes at the end of each scanline
  836. int padding = bytesPerScanline % 4;
  837. if (padding != 0)
  838. padding = 4 - padding;
  839. int lineLength = bytesPerScanline + padding;
  840. if (noTransform) {
  841. int j = isBottomUp ? (height -1) * bytesPerScanline : 0;
  842. for (int i=0; i<height; i++) {
  843. if (abortRequested()) {
  844. break;
  845. }
  846. iis.readFully(bdata, j, bytesPerScanline);
  847. iis.skipBytes(padding);
  848. j += isBottomUp ? -bytesPerScanline : bytesPerScanline;
  849. processImageUpdate(bi, 0, i,
  850. destinationRegion.width, 1, 1, 1,
  851. new int[]{0});
  852. processImageProgress(100.0F * idestinationRegion.height);
  853. }
  854. } else {
  855. byte[] buf = new byte[lineLength];
  856. int lineStride =
  857. ((MultiPixelPackedSampleModel)sampleModel).getScanlineStride();
  858. if (isBottomUp) {
  859. int lastLine =
  860. sourceRegion.y + (destinationRegion.height - 1) * scaleY;
  861. iis.skipBytes(lineLength * (height - 1 - lastLine));
  862. } else
  863. iis.skipBytes(lineLength * sourceRegion.y);
  864. int skipLength = lineLength * (scaleY - 1);
  865. // cache the values to avoid duplicated computation
  866. int[] srcOff = new int[destinationRegion.width];
  867. int[] destOff = new int[destinationRegion.width];
  868. int[] srcPos = new int[destinationRegion.width];
  869. int[] destPos = new int[destinationRegion.width];
  870. for (int i = destinationRegion.x, x = sourceRegion.x, j = 0;
  871. i < destinationRegion.x + destinationRegion.width;
  872. i++, j++, x += scaleX) {
  873. srcPos[j] = x >> 1;
  874. srcOff[j] = (1 - (x & 1)) << 2;
  875. destPos[j] = i >> 1;
  876. destOff[j] = (1 - (i & 1)) << 2;
  877. }
  878. int k = destinationRegion.y * lineStride;
  879. if (isBottomUp)
  880. k += (destinationRegion.height - 1) * lineStride;
  881. for (int j = 0, y = sourceRegion.y;
  882. j < destinationRegion.height; j++, y+=scaleY) {
  883. if (abortRequested())
  884. break;
  885. iis.read(buf, 0, lineLength);
  886. for (int i = 0; i < destinationRegion.width; i++) {
  887. //get the bit and assign to the data buffer of the raster
  888. int v = (buf[srcPos[i]] >> srcOff[i]) & 0x0F;
  889. bdata[k + destPos[i]] |= v << destOff[i];
  890. }
  891. k += isBottomUp ? -lineStride : lineStride;
  892. iis.skipBytes(skipLength);
  893. processImageUpdate(bi, 0, j,
  894. destinationRegion.width, 1, 1, 1,
  895. new int[]{0});
  896. processImageProgress(100.0F*jdestinationRegion.height);
  897. }
  898. }
  899. }
  900. // Method to read 8 bit BMP image data
  901. private void read8Bit(byte[] bdata) throws IOException {
  902. // Padding bytes at the end of each scanline
  903. int padding = width % 4;
  904. if (padding != 0) {
  905. padding = 4 - padding;
  906. }
  907. int lineLength = width + padding;
  908. if (noTransform) {
  909. int j = isBottomUp ? (height -1) * width : 0;
  910. for (int i=0; i<height; i++) {
  911. if (abortRequested()) {
  912. break;
  913. }
  914. iis.readFully(bdata, j, width);
  915. iis.skipBytes(padding);
  916. j += isBottomUp ? -width : width;
  917. processImageUpdate(bi, 0, i,
  918. destinationRegion.width, 1, 1, 1,
  919. new int[]{0});
  920. processImageProgress(100.0F * idestinationRegion.height);
  921. }
  922. } else {
  923. byte[] buf = new byte[lineLength];
  924. int lineStride =
  925. ((ComponentSampleModel)sampleModel).getScanlineStride();
  926. if (isBottomUp) {
  927. int lastLine =
  928. sourceRegion.y + (destinationRegion.height - 1) * scaleY;
  929. iis.skipBytes(lineLength * (height - 1 - lastLine));
  930. } else
  931. iis.skipBytes(lineLength * sourceRegion.y);
  932. int skipLength = lineLength * (scaleY - 1);
  933. int k = destinationRegion.y * lineStride;
  934. if (isBottomUp)
  935. k += (destinationRegion.height - 1) * lineStride;
  936. k += destinationRegion.x;
  937. for (int j = 0, y = sourceRegion.y;
  938. j < destinationRegion.height; j++, y+=scaleY) {
  939. if (abortRequested())
  940. break;
  941. iis.read(buf, 0, lineLength);
  942. for (int i = 0, m = sourceRegion.x;
  943. i < destinationRegion.width; i++, m += scaleX) {
  944. //get the bit and assign to the data buffer of the raster
  945. bdata[k + i] = buf[m];
  946. }
  947. k += isBottomUp ? -lineStride : lineStride;
  948. iis.skipBytes(skipLength);
  949. processImageUpdate(bi, 0, j,
  950. destinationRegion.width, 1, 1, 1,
  951. new int[]{0});
  952. processImageProgress(100.0F*jdestinationRegion.height);
  953. }
  954. }
  955. }
  956. // Method to read 24 bit BMP image data
  957. private void read24Bit(byte[] bdata) throws IOException {
  958. // Padding bytes at the end of each scanline
  959. // width * bitsPerPixel should be divisible by 32
  960. int padding = width * 3 % 4;
  961. if ( padding != 0)
  962. padding = 4 - padding;
  963. int lineStride = width * 3;
  964. int lineLength = lineStride + padding;
  965. if (noTransform) {
  966. int j = isBottomUp ? (height -1) * width * 3 : 0;
  967. for (int i=0; i<height; i++) {
  968. if (abortRequested()) {
  969. break;
  970. }
  971. iis.readFully(bdata, j, lineStride);
  972. iis.skipBytes(padding);
  973. j += isBottomUp ? -lineStride : lineStride;
  974. processImageUpdate(bi, 0, i,
  975. destinationRegion.width, 1, 1, 1,
  976. new int[]{0});
  977. processImageProgress(100.0F * idestinationRegion.height);
  978. }
  979. } else {
  980. byte[] buf = new byte[lineLength];
  981. lineStride =
  982. ((ComponentSampleModel)sampleModel).getScanlineStride();
  983. if (isBottomUp) {
  984. int lastLine =
  985. sourceRegion.y + (destinationRegion.height - 1) * scaleY;
  986. iis.skipBytes(lineLength * (height - 1 - lastLine));
  987. } else
  988. iis.skipBytes(lineLength * sourceRegion.y);
  989. int skipLength = lineLength * (scaleY - 1);
  990. int k = destinationRegion.y * lineStride;
  991. if (isBottomUp)
  992. k += (destinationRegion.height - 1) * lineStride;
  993. k += destinationRegion.x * 3;
  994. for (int j = 0, y = sourceRegion.y;
  995. j < destinationRegion.height; j++, y+=scaleY) {
  996. if (abortRequested())
  997. break;
  998. iis.read(buf, 0, lineLength);
  999. for (int i = 0, m = 3 * sourceRegion.x;
  1000. i < destinationRegion.width; i++, m += 3 * scaleX) {
  1001. //get the bit and assign to the data buffer of the raster
  1002. int n = 3 * i + k;
  1003. for (int b = 0; b < destBands.length; b++)
  1004. bdata[n + destBands[b]] = buf[m + sourceBands[b]];
  1005. }
  1006. k += isBottomUp ? -lineStride : lineStride;
  1007. iis.skipBytes(skipLength);
  1008. processImageUpdate(bi, 0, j,
  1009. destinationRegion.width, 1, 1, 1,
  1010. new int[]{0});
  1011. processImageProgress(100.0F*jdestinationRegion.height);
  1012. }
  1013. }
  1014. }
  1015. private void read16Bit(short sdata[]) throws IOException {
  1016. // Padding bytes at the end of each scanline
  1017. // width * bitsPerPixel should be divisible by 32
  1018. int padding = width * 2 % 4;
  1019. if ( padding != 0)
  1020. padding = 4 - padding;
  1021. int lineLength = width + padding / 2;
  1022. if (noTransform) {
  1023. int j = isBottomUp ? (height -1) * width : 0;
  1024. for (int i=0; i<height; i++) {
  1025. if (abortRequested()) {
  1026. break;
  1027. }
  1028. iis.readFully(sdata, j, width);
  1029. iis.skipBytes(padding);
  1030. j += isBottomUp ? -width : width;
  1031. processImageUpdate(bi, 0, i,
  1032. destinationRegion.width, 1, 1, 1,
  1033. new int[]{0});
  1034. processImageProgress(100.0F * idestinationRegion.height);
  1035. }
  1036. } else {
  1037. short[] buf = new short[lineLength];
  1038. int lineStride =
  1039. ((SinglePixelPackedSampleModel)sampleModel).getScanlineStride();
  1040. if (isBottomUp) {
  1041. int lastLine =
  1042. sourceRegion.y + (destinationRegion.height - 1) * scaleY;
  1043. iis.skipBytes(lineLength * (height - 1 - lastLine) << 1);
  1044. } else
  1045. iis.skipBytes(lineLength * sourceRegion.y << 1);
  1046. int skipLength = lineLength * (scaleY - 1) << 1;
  1047. int k = destinationRegion.y * lineStride;
  1048. if (isBottomUp)
  1049. k += (destinationRegion.height - 1) * lineStride;
  1050. k += destinationRegion.x;
  1051. for (int j = 0, y = sourceRegion.y;
  1052. j < destinationRegion.height; j++, y+=scaleY) {
  1053. if (abortRequested())
  1054. break;
  1055. iis.readFully(buf, 0, lineLength);
  1056. for (int i = 0, m = sourceRegion.x;
  1057. i < destinationRegion.width; i++, m += scaleX) {
  1058. //get the bit and assign to the data buffer of the raster
  1059. sdata[k + i] = buf[m];
  1060. }
  1061. k += isBottomUp ? -lineStride : lineStride;
  1062. iis.skipBytes(skipLength);
  1063. processImageUpdate(bi, 0, j,
  1064. destinationRegion.width, 1, 1, 1,
  1065. new int[]{0});
  1066. processImageProgress(100.0F*jdestinationRegion.height);
  1067. }
  1068. }
  1069. }
  1070. private void read32Bit(int idata[]) throws IOException {
  1071. if (noTransform) {
  1072. int j = isBottomUp ? (height -1) * width : 0;
  1073. for (int i=0; i<height; i++) {
  1074. if (abortRequested()) {
  1075. break;
  1076. }
  1077. iis.readFully(idata, j, width);
  1078. j += isBottomUp ? -width : width;
  1079. processImageUpdate(bi, 0, i,
  1080. destinationRegion.width, 1, 1, 1,
  1081. new int[]{0});
  1082. processImageProgress(100.0F * idestinationRegion.height);
  1083. }
  1084. } else {
  1085. int[] buf = new int[width];
  1086. int lineStride =
  1087. ((SinglePixelPackedSampleModel)sampleModel).getScanlineStride();
  1088. if (isBottomUp) {
  1089. int lastLine =
  1090. sourceRegion.y + (destinationRegion.height - 1) * scaleY;
  1091. iis.skipBytes(width * (height - 1 - lastLine) << 2);
  1092. } else
  1093. iis.skipBytes(width * sourceRegion.y << 2);
  1094. int skipLength = width * (scaleY - 1) << 2;
  1095. int k = destinationRegion.y * lineStride;
  1096. if (isBottomUp)
  1097. k += (destinationRegion.height - 1) * lineStride;
  1098. k += destinationRegion.x;
  1099. for (int j = 0, y = sourceRegion.y;
  1100. j < destinationRegion.height; j++, y+=scaleY) {
  1101. if (abortRequested())
  1102. break;
  1103. iis.readFully(buf, 0, width);
  1104. for (int i = 0, m = sourceRegion.x;
  1105. i < destinationRegion.width; i++, m += scaleX) {
  1106. //get the bit and assign to the data buffer of the raster
  1107. idata[k + i] = buf[m];
  1108. }
  1109. k += isBottomUp ? -lineStride : lineStride;
  1110. iis.skipBytes(skipLength);
  1111. processImageUpdate(bi, 0, j,
  1112. destinationRegion.width, 1, 1, 1,
  1113. new int[]{0});
  1114. processImageProgress(100.0F*jdestinationRegion.height);
  1115. }
  1116. }
  1117. }
  1118. private void readRLE8(byte bdata[]) throws IOException {
  1119. // If imageSize field is not provided, calculate it.
  1120. int imSize = (int)imageSize;
  1121. if (imSize == 0) {
  1122. imSize = (int)(bitmapFileSize - bitmapOffset);
  1123. }
  1124. int padding = 0;
  1125. // If width is not 32 bit aligned, then while uncompressing each
  1126. // scanline will have padding bytes, calculate the amount of padding
  1127. int remainder = width % 4;
  1128. if (remainder != 0) {
  1129. padding = 4 - remainder;
  1130. }
  1131. // Read till we have the whole image
  1132. byte values[] = new byte[imSize];
  1133. int bytesRead = 0;
  1134. iis.readFully(values, 0, imSize);
  1135. // Since data is compressed, decompress it
  1136. decodeRLE8(imSize, padding, values, bdata);
  1137. }
  1138. private void decodeRLE8(int imSize,
  1139. int padding,
  1140. byte[] values,
  1141. byte[] bdata) throws IOException {
  1142. byte val[] = new byte[width * height];
  1143. int count = 0, l = 0;
  1144. int value;
  1145. boolean flag = false;
  1146. int lineNo = isBottomUp ? height - 1 : 0;
  1147. int lineStride =
  1148. ((ComponentSampleModel)sampleModel).getScanlineStride();
  1149. int finished = 0;
  1150. while (count != imSize) {
  1151. value = values[count++] & 0xff;
  1152. if (value == 0) {
  1153. switch(values[count++] & 0xff) {
  1154. case 0:
  1155. // End-of-scanline marker
  1156. if (lineNo >= sourceRegion.y &&
  1157. lineNo < sourceRegion.y + sourceRegion.width) {
  1158. if (noTransform) {
  1159. int pos = lineNo * width;
  1160. for(int i = 0; i < width; i++)
  1161. bdata[pos++] = val[i];
  1162. processImageUpdate(bi, 0, lineNo,
  1163. destinationRegion.width, 1, 1, 1,
  1164. new int[]{0});
  1165. finished++;
  1166. } else if ((lineNo - sourceRegion.y) % scaleY == 0) {
  1167. int currentLine = (lineNo - sourceRegion.y) / scaleY +
  1168. destinationRegion.y;
  1169. int pos = currentLine * lineStride;
  1170. pos += destinationRegion.x;
  1171. for (int i = sourceRegion.x;
  1172. i < sourceRegion.x + sourceRegion.width;
  1173. i += scaleX)
  1174. bdata[pos++] = val[i];
  1175. processImageUpdate(bi, 0, currentLine,
  1176. destinationRegion.width, 1, 1, 1,
  1177. new int[]{0});
  1178. finished++;
  1179. }
  1180. }
  1181. processImageProgress(100.0F * finished / destinationRegion.height);
  1182. lineNo += isBottomUp ? -1 : 1;
  1183. l = 0;
  1184. if (abortRequested()) {
  1185. flag = true;
  1186. }
  1187. break;
  1188. case 1:
  1189. // End-of-RLE marker
  1190. flag = true;
  1191. break;
  1192. case 2:
  1193. // delta or vector marker
  1194. int xoff = values[count++] & 0xff;
  1195. int yoff = values[count] & 0xff;
  1196. // Move to the position xoff, yoff down
  1197. l += xoff + yoff*width;
  1198. break;
  1199. default:
  1200. int end = values[count-1] & 0xff;
  1201. for (int i=0; i<end; i++) {
  1202. val[l++] = (byte)(values[count++] & 0xff);
  1203. }
  1204. // Whenever end pixels can fit into odd number of bytes,
  1205. // an extra padding byte will be present, so skip that.
  1206. if ((end & 1) == 1) {
  1207. count++;
  1208. }
  1209. }
  1210. } else {
  1211. for (int i=0; i<value; i++) {
  1212. val[l++] = (byte)(values[count] & 0xff);
  1213. }
  1214. count++;
  1215. }
  1216. // If End-of-RLE data, then exit the while loop
  1217. if (flag) {
  1218. break;
  1219. }
  1220. }
  1221. }
  1222. private void readRLE4(byte[] bdata) throws IOException {
  1223. // If imageSize field is not specified, calculate it.
  1224. int imSize = (int)imageSize;
  1225. if (imSize == 0) {
  1226. imSize = (int)(bitmapFileSize - bitmapOffset);
  1227. }
  1228. int padding = 0;
  1229. // If width is not 32 byte aligned, then while uncompressing each
  1230. // scanline will have padding bytes, calculate the amount of padding
  1231. int remainder = width % 4;
  1232. if (remainder != 0) {
  1233. padding = 4 - remainder;
  1234. }
  1235. // Read till we have the whole image
  1236. byte[] values = new byte[imSize];
  1237. iis.readFully(values, 0, imSize);
  1238. // Decompress the RLE4 compressed data.
  1239. decodeRLE4(imSize, padding, values, bdata);
  1240. }
  1241. private void decodeRLE4(int imSize,
  1242. int padding,
  1243. byte[] values,
  1244. byte[] bdata) throws IOException {
  1245. byte[] val = new byte[width];
  1246. int count = 0, l = 0;
  1247. int value;
  1248. boolean flag = false;
  1249. int lineNo = isBottomUp ? height - 1 : 0;
  1250. int lineStride =
  1251. ((MultiPixelPackedSampleModel)sampleModel).getScanlineStride();
  1252. int finished = 0;
  1253. while (count != imSize) {
  1254. value = values[count++] & 0xFF;
  1255. if (value == 0) {
  1256. // Absolute mode
  1257. switch(values[count++] & 0xFF) {
  1258. case 0:
  1259. // End-of-scanline marker
  1260. // End-of-scanline marker
  1261. if (lineNo >= sourceRegion.y &&
  1262. lineNo < sourceRegion.y + sourceRegion.width) {
  1263. if (noTransform) {
  1264. int pos = lineNo * (width + 1 >> 1);
  1265. for(int i = 0, j = 0; i < width >> 1; i++)
  1266. bdata[pos++] =
  1267. (byte)((val[j++] << 4) | val[j++]);
  1268. if ((width & 1) == 1)
  1269. bdata[pos] |= val[width - 1] << 4;
  1270. processImageUpdate(bi, 0, lineNo,
  1271. destinationRegion.width, 1, 1, 1,
  1272. new int[]{0});
  1273. finished++;
  1274. } else if ((lineNo - sourceRegion.y) % scaleY == 0) {
  1275. int currentLine = (lineNo - sourceRegion.y) / scaleY +
  1276. destinationRegion.y;
  1277. int pos = currentLine * lineStride;
  1278. pos += destinationRegion.x >> 1;
  1279. int shift = (1 - (destinationRegion.x & 1)) << 2;
  1280. for (int i = sourceRegion.x;
  1281. i < sourceRegion.x + sourceRegion.width;
  1282. i += scaleX) {
  1283. bdata[pos] |= val[i] << shift;
  1284. shift += 4;
  1285. if (shift == 4) {
  1286. pos++;
  1287. }
  1288. shift &= 7;
  1289. }
  1290. processImageUpdate(bi, 0, currentLine,
  1291. destinationRegion.width, 1, 1, 1,
  1292. new int[]{0});
  1293. finished++;
  1294. }
  1295. }
  1296. processImageProgress(100.0F * finished / destinationRegion.height);
  1297. lineNo += isBottomUp ? -1 : 1;
  1298. l = 0;
  1299. if (abortRequested()) {
  1300. flag = true;
  1301. }
  1302. break;
  1303. case 1:
  1304. // End-of-RLE marker
  1305. flag = true;
  1306. break;
  1307. case 2:
  1308. // delta or vector marker
  1309. int xoff = values[count++] & 0xFF;
  1310. int yoff = values[count] & 0xFF;
  1311. // Move to the position xoff, yoff down
  1312. l += xoff + yoff*width;
  1313. break;
  1314. default:
  1315. int end = values[count-1] & 0xFF;
  1316. for (int i=0; i<end; i++) {
  1317. val[l++] = (byte)(((i & 1) == 0) ? (values[count] & 0xf0) >> 4
  1318. : (values[count++] & 0x0f));
  1319. }
  1320. // When end is odd, the above for loop does not
  1321. // increment count, so do it now.
  1322. if ((end & 1) == 1) {
  1323. count++;
  1324. }
  1325. // Whenever end pixels can fit into odd number of bytes,
  1326. // an extra padding byte will be present, so skip that.
  1327. if ((((int)Math.ceil(end2)) & 1) ==1 ) {
  1328. count++;
  1329. }
  1330. break;
  1331. }
  1332. } else {
  1333. // Encoded mode
  1334. int alternate[] = { (values[count] & 0xf0) >> 4,
  1335. values[count] & 0x0f };
  1336. for (int i=0; (i < value) && (l < width); i++) {
  1337. val[l++] = (byte)alternate[i & 1];
  1338. }
  1339. count++;
  1340. }
  1341. // If End-of-RLE data, then exit the while loop
  1342. if (flag) {
  1343. break;
  1344. }
  1345. }
  1346. }
  1347. /** Decodes the jpeg/png image embedded in the bitmap using any jpeg
  1348. * ImageIO-style plugin.
  1349. *
  1350. * @param bi The destination <code>BufferedImage</code>.
  1351. * @param bmpParam The <code>ImageReadParam</code> for decoding this
  1352. * BMP image. The parameters for subregion, band selection and
  1353. * subsampling are used in decoding the jpeg image.
  1354. */
  1355. private void readEmbedded(String format,
  1356. BufferedImage bi, ImageReadParam bmpParam)
  1357. throws IOException {
  1358. Iterator iterator = ImageIO.getImageReadersByFormatName(format);
  1359. ImageReader reader = null;
  1360. if (iterator.hasNext())
  1361. reader = (ImageReader)iterator.next();
  1362. if (reader != null) {
  1363. // prepare input
  1364. byte[] buff = new byte[(int)imageSize];
  1365. iis.read(buff);
  1366. reader.setInput(ImageIO.createImageInputStream(new ByteArrayInputStream(buff)));
  1367. reader.addIIOReadProgressListener(new EmbeddedProgressAdapter() {
  1368. public void imageProgress(ImageReader source,
  1369. float percentageDone)
  1370. {
  1371. processImageProgress(percentageDone);
  1372. }
  1373. });
  1374. reader.addIIOReadUpdateListener(new IIOReadUpdateListener() {
  1375. public void imageUpdate(ImageReader source,
  1376. BufferedImage theImage,
  1377. int minX, int minY,
  1378. int width, int height,
  1379. int periodX, int periodY,
  1380. int[] bands)
  1381. {
  1382. processImageUpdate(theImage, minX, minY,
  1383. width, height,
  1384. periodX, periodY, bands);
  1385. }
  1386. public void passComplete(ImageReader source,
  1387. BufferedImage theImage)
  1388. {
  1389. processPassComplete(theImage);
  1390. }
  1391. public void passStarted(ImageReader source,
  1392. BufferedImage theImage,
  1393. int pass,
  1394. int minPass, int maxPass,
  1395. int minX, int minY,
  1396. int periodX, int periodY,
  1397. int[] bands)
  1398. {
  1399. processPassStarted(theImage, pass, minPass, maxPass,
  1400. minX, minY, periodX, periodY,
  1401. bands);
  1402. }
  1403. public void thumbnailPassComplete(ImageReader source,
  1404. BufferedImage thumb) {}
  1405. public void thumbnailPassStarted(ImageReader source,
  1406. BufferedImage thumb,
  1407. int pass,
  1408. int minPass, int maxPass,
  1409. int minX, int minY,
  1410. int periodX, int periodY,
  1411. int[] bands) {}
  1412. public void thumbnailUpdate(ImageReader source,
  1413. BufferedImage theThumbnail,
  1414. int minX, int minY,
  1415. int width, int height,
  1416. int periodX, int periodY,
  1417. int[] bands) {}
  1418. });
  1419. reader.addIIOReadWarningListener(new IIOReadWarningListener() {
  1420. public void warningOccurred(ImageReader source, String warning) {
  1421. processWarningOccurred(warning);
  1422. }
  1423. });
  1424. ImageReadParam param = reader.getDefaultReadParam();
  1425. param.setDestination(bi);
  1426. param.setDestinationBands(bmpParam.getDestinationBands());
  1427. param.setDestinationOffset(bmpParam.getDestinationOffset());
  1428. param.setSourceBands(bmpParam.getSourceBands());
  1429. param.setSourceRegion(bmpParam.getSourceRegion());
  1430. param.setSourceSubsampling(bmpParam.getSourceXSubsampling(),
  1431. bmpParam.getSourceYSubsampling(),
  1432. bmpParam.getSubsamplingXOffset(),
  1433. bmpParam.getSubsamplingYOffset());
  1434. reader.read(0, param);
  1435. } else
  1436. throw new RuntimeException(I18N.getString("BMPImageReader4") + " " + format);
  1437. }
  1438. private class EmbeddedProgressAdapter implements IIOReadProgressListener {
  1439. public void imageComplete(ImageReader src) {}
  1440. public void imageProgress(ImageReader src, float percentageDone) {}
  1441. public void imageStarted(ImageReader src, int imageIndex) {}
  1442. public void thumbnailComplete(ImageReader src) {}
  1443. public void thumbnailProgress(ImageReader src, float percentageDone) {}
  1444. public void thumbnailStarted(ImageReader src, int iIdx, int tIdx) {}
  1445. public void sequenceComplete(ImageReader src) {}
  1446. public void sequenceStarted(ImageReader src, int minIndex) {}
  1447. public void readAborted(ImageReader src) {}
  1448. }
  1449. }