1. /*
  2. * @(#)PNGImageReader.java 1.53 03/01/23
  3. *
  4. * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package com.sun.imageio.plugins.png;
  8. import java.awt.Point;
  9. import java.awt.Rectangle;
  10. import java.awt.color.ColorSpace;
  11. import java.awt.image.BufferedImage;
  12. import java.awt.image.DataBuffer;
  13. import java.awt.image.DataBufferByte;
  14. import java.awt.image.DataBufferUShort;
  15. import java.awt.image.Raster;
  16. import java.awt.image.WritableRaster;
  17. import java.io.BufferedInputStream;
  18. import java.io.ByteArrayInputStream;
  19. import java.io.DataInputStream;
  20. import java.io.InputStream;
  21. import java.io.IOException;
  22. import java.io.SequenceInputStream;
  23. import java.util.ArrayList;
  24. import java.util.Arrays;
  25. import java.util.Enumeration;
  26. import java.util.Iterator;
  27. import java.util.List;
  28. import java.util.zip.Inflater;
  29. import java.util.zip.InflaterInputStream;
  30. import javax.imageio.IIOException;
  31. import javax.imageio.ImageReader;
  32. import javax.imageio.ImageReadParam;
  33. import javax.imageio.ImageTypeSpecifier;
  34. import javax.imageio.metadata.IIOMetadata;
  35. import javax.imageio.spi.ImageReaderSpi;
  36. import javax.imageio.stream.ImageInputStream;
  37. import com.sun.imageio.plugins.common.InputStreamAdapter;
  38. import com.sun.imageio.plugins.common.SubImageInputStream;
  39. class PNGImageDataEnumeration implements Enumeration {
  40. boolean firstTime = true;
  41. ImageInputStream stream;
  42. int length;
  43. public PNGImageDataEnumeration(ImageInputStream stream)
  44. throws IOException {
  45. this.stream = stream;
  46. this.length = stream.readInt();
  47. int type = stream.readInt(); // skip chunk type
  48. }
  49. public Object nextElement() {
  50. try {
  51. firstTime = false;
  52. ImageInputStream iis = new SubImageInputStream(stream, length);
  53. return new InputStreamAdapter(iis);
  54. } catch (IOException e) {
  55. return null;
  56. }
  57. }
  58. public boolean hasMoreElements() {
  59. if (firstTime) {
  60. return true;
  61. }
  62. try {
  63. int crc = stream.readInt();
  64. this.length = stream.readInt();
  65. int type = stream.readInt();
  66. if (type == PNGImageReader.IDAT_TYPE) {
  67. return true;
  68. } else {
  69. return false;
  70. }
  71. } catch (IOException e) {
  72. return false;
  73. }
  74. }
  75. }
  76. /**
  77. * @version 0.5
  78. */
  79. public class PNGImageReader extends ImageReader {
  80. // Critical chunks
  81. static final int IHDR_TYPE = chunkType("IHDR");
  82. static final int PLTE_TYPE = chunkType("PLTE");
  83. static final int IDAT_TYPE = chunkType("IDAT");
  84. static final int IEND_TYPE = chunkType("IEND");
  85. // Ancillary chunks
  86. static final int bKGD_TYPE = chunkType("bKGD");
  87. static final int cHRM_TYPE = chunkType("cHRM");
  88. static final int gAMA_TYPE = chunkType("gAMA");
  89. static final int hIST_TYPE = chunkType("hIST");
  90. static final int iCCP_TYPE = chunkType("iCCP");
  91. static final int iTXt_TYPE = chunkType("iTXt");
  92. static final int pHYs_TYPE = chunkType("pHYs");
  93. static final int sBIT_TYPE = chunkType("sBIT");
  94. static final int sPLT_TYPE = chunkType("sPLT");
  95. static final int sRGB_TYPE = chunkType("sRGB");
  96. static final int tEXt_TYPE = chunkType("tEXt");
  97. static final int tIME_TYPE = chunkType("tIME");
  98. static final int tRNS_TYPE = chunkType("tRNS");
  99. static final int zTXt_TYPE = chunkType("zTXt");
  100. static final int PNG_COLOR_GRAY = 0;
  101. static final int PNG_COLOR_RGB = 2;
  102. static final int PNG_COLOR_PALETTE = 3;
  103. static final int PNG_COLOR_GRAY_ALPHA = 4;
  104. static final int PNG_COLOR_RGB_ALPHA = 6;
  105. // The number of bands by PNG color type
  106. static final int[] inputBandsForColorType = {
  107. 1, // gray
  108. -1, // unused
  109. 3, // rgb
  110. 1, // palette
  111. 2, // gray + alpha
  112. -1, // unused
  113. 4 // rgb + alpha
  114. };
  115. static final int PNG_FILTER_NONE = 0;
  116. static final int PNG_FILTER_SUB = 1;
  117. static final int PNG_FILTER_UP = 2;
  118. static final int PNG_FILTER_AVERAGE = 3;
  119. static final int PNG_FILTER_PAETH = 4;
  120. static final int[] adam7XOffset = { 0, 4, 0, 2, 0, 1, 0 };
  121. static final int[] adam7YOffset = { 0, 0, 4, 0, 2, 0, 1 };
  122. static final int[] adam7XSubsampling = { 8, 8, 4, 4, 2, 2, 1, 1 };
  123. static final int[] adam7YSubsampling = { 8, 8, 8, 4, 4, 2, 2, 1 };
  124. private static final boolean debug = true;
  125. ImageInputStream stream = null;
  126. boolean gotHeader = false;
  127. boolean gotMetadata = false;
  128. ImageReadParam lastParam = null;
  129. long imageStartPosition = -1L;
  130. Rectangle sourceRegion = null;
  131. int sourceXSubsampling = -1;
  132. int sourceYSubsampling = -1;
  133. int sourceMinProgressivePass = 0;
  134. int sourceMaxProgressivePass = 6;
  135. int[] sourceBands = null;
  136. int[] destinationBands = null;
  137. Point destinationOffset = new Point(0, 0);
  138. PNGMetadata metadata = new PNGMetadata();
  139. DataInputStream pixelStream = null;
  140. BufferedImage theImage = null;
  141. // The number of source pixels processed
  142. int pixelsDone = 0;
  143. // The total number of pixels in the source image
  144. int totalPixels;
  145. public PNGImageReader(ImageReaderSpi originatingProvider) {
  146. super(originatingProvider);
  147. }
  148. public void setInput(Object input,
  149. boolean seekForwardOnly,
  150. boolean ignoreMetadata) {
  151. super.setInput(input, seekForwardOnly, ignoreMetadata);
  152. this.stream = (ImageInputStream)input; // Always works
  153. // Clear all values based on the previous stream contents
  154. resetStreamSettings();
  155. }
  156. // Callable from ImageWriter
  157. static int chunkType(String typeString) {
  158. char c0 = typeString.charAt(0);
  159. char c1 = typeString.charAt(1);
  160. char c2 = typeString.charAt(2);
  161. char c3 = typeString.charAt(3);
  162. int type = (c0 << 24) | (c1 << 16) | (c2 << 8) | c3;
  163. return type;
  164. }
  165. private String readNullTerminatedString() throws IOException {
  166. StringBuffer b = new StringBuffer();
  167. int c;
  168. while ((c = stream.read()) != 0) {
  169. b.append((char)c);
  170. }
  171. return b.toString();
  172. }
  173. private void readHeader() throws IIOException {
  174. if (gotHeader) {
  175. return;
  176. }
  177. if (stream == null) {
  178. throw new IllegalStateException("Input source not set!");
  179. }
  180. try {
  181. byte[] signature = new byte[8];
  182. stream.readFully(signature);
  183. if (signature[0] != (byte)137 ||
  184. signature[1] != (byte)80 ||
  185. signature[2] != (byte)78 ||
  186. signature[3] != (byte)71 ||
  187. signature[4] != (byte)13 ||
  188. signature[5] != (byte)10 ||
  189. signature[6] != (byte)26 ||
  190. signature[7] != (byte)10) {
  191. throw new IIOException("Bad PNG signature!");
  192. }
  193. int IHDR_length = stream.readInt();
  194. if (IHDR_length != 13) {
  195. throw new IIOException("Bad length for IHDR chunk!");
  196. }
  197. int IHDR_type = stream.readInt();
  198. if (IHDR_type != IHDR_TYPE) {
  199. throw new IIOException("Bad type for IHDR chunk!");
  200. }
  201. this.metadata = new PNGMetadata();
  202. int width = stream.readInt();
  203. int height = stream.readInt();
  204. int bitDepth = stream.readUnsignedByte();
  205. int colorType = stream.readUnsignedByte();
  206. int compressionMethod = stream.readUnsignedByte();
  207. int filterMethod = stream.readUnsignedByte();
  208. int interlaceMethod = stream.readUnsignedByte();
  209. int IHDR_CRC = stream.readInt();
  210. stream.flushBefore(stream.getStreamPosition());
  211. if (width == 0) {
  212. throw new IIOException("Image width == 0!");
  213. }
  214. if (height == 0) {
  215. throw new IIOException("Image height == 0!");
  216. }
  217. if (bitDepth != 1 && bitDepth != 2 && bitDepth != 4 &&
  218. bitDepth != 8 && bitDepth != 16) {
  219. throw new IIOException("Bit depth must be 1, 2, 4, 8, or 16!");
  220. }
  221. if (colorType != 0 && colorType != 2 && colorType != 3 &&
  222. colorType != 4 && colorType != 6) {
  223. throw new IIOException("Color type must be 0, 2, 3, 4, or 6!");
  224. }
  225. if (colorType == PNG_COLOR_PALETTE && bitDepth == 16) {
  226. throw new IIOException("Bad color type/bit depth combination!");
  227. }
  228. if ((colorType == PNG_COLOR_RGB ||
  229. colorType == PNG_COLOR_RGB_ALPHA ||
  230. colorType == PNG_COLOR_GRAY_ALPHA) &&
  231. (bitDepth != 8 && bitDepth != 16)) {
  232. throw new IIOException("Bad color type/bit depth combination!");
  233. }
  234. if (compressionMethod != 0) {
  235. throw new IIOException("Unknown compression method (not 0)!");
  236. }
  237. if (filterMethod != 0) {
  238. throw new IIOException("Unknown filter method (not 0)!");
  239. }
  240. if (interlaceMethod != 0 && interlaceMethod != 1) {
  241. throw new IIOException("Unknown interlace method (not 0 or 1)!");
  242. }
  243. metadata.IHDR_present = true;
  244. metadata.IHDR_width = width;
  245. metadata.IHDR_height = height;
  246. metadata.IHDR_bitDepth = bitDepth;
  247. metadata.IHDR_colorType = colorType;
  248. metadata.IHDR_compressionMethod = compressionMethod;
  249. metadata.IHDR_filterMethod = filterMethod;
  250. metadata.IHDR_interlaceMethod = interlaceMethod;
  251. gotHeader = true;
  252. } catch (IOException e) {
  253. throw new IIOException("I/O error reading PNG header!", e);
  254. }
  255. }
  256. private void parse_PLTE_chunk(int chunkLength) throws IOException {
  257. if (metadata.PLTE_present) {
  258. processWarningOccurred(
  259. "A PNG image may not contain more than one PLTE chunk.\n" +
  260. "The chunk wil be ignored.");
  261. return;
  262. } else if (metadata.IHDR_colorType == PNG_COLOR_GRAY ||
  263. metadata.IHDR_colorType == PNG_COLOR_GRAY_ALPHA) {
  264. processWarningOccurred(
  265. "A PNG gray or gray alpha image cannot have a PLTE chunk.\n" +
  266. "The chunk wil be ignored.");
  267. return;
  268. }
  269. byte[] palette = new byte[chunkLength];
  270. stream.readFully(palette);
  271. int numEntries = chunkLength3;
  272. if (metadata.IHDR_colorType == PNG_COLOR_PALETTE) {
  273. int maxEntries = 1 << metadata.IHDR_bitDepth;
  274. if (numEntries > maxEntries) {
  275. processWarningOccurred(
  276. "PLTE chunk contains too many entries for bit depth, ignoring extras.");
  277. numEntries = maxEntries;
  278. }
  279. numEntries = Math.min(numEntries, maxEntries);
  280. }
  281. // Round array sizes up to 2^2^n
  282. int paletteEntries;
  283. if (numEntries > 16) {
  284. paletteEntries = 256;
  285. } else if (numEntries > 8) {
  286. paletteEntries = 16;
  287. } else if (numEntries > 2) {
  288. paletteEntries = 4;
  289. } else {
  290. paletteEntries = 2;
  291. }
  292. metadata.PLTE_present = true;
  293. metadata.PLTE_red = new byte[paletteEntries];
  294. metadata.PLTE_green = new byte[paletteEntries];
  295. metadata.PLTE_blue = new byte[paletteEntries];
  296. int index = 0;
  297. for (int i = 0; i < numEntries; i++) {
  298. metadata.PLTE_red[i] = palette[index++];
  299. metadata.PLTE_green[i] = palette[index++];
  300. metadata.PLTE_blue[i] = palette[index++];
  301. }
  302. }
  303. private void parse_bKGD_chunk() throws IOException {
  304. if (metadata.IHDR_colorType == PNG_COLOR_PALETTE) {
  305. metadata.bKGD_colorType = PNG_COLOR_PALETTE;
  306. metadata.bKGD_index = stream.readUnsignedByte();
  307. } else if (metadata.IHDR_colorType == PNG_COLOR_GRAY ||
  308. metadata.IHDR_colorType == PNG_COLOR_GRAY_ALPHA) {
  309. metadata.bKGD_colorType = PNG_COLOR_GRAY;
  310. metadata.bKGD_gray = stream.readUnsignedShort();
  311. } else { // RGB or RGB_ALPHA
  312. metadata.bKGD_colorType = PNG_COLOR_RGB;
  313. metadata.bKGD_red = stream.readUnsignedShort();
  314. metadata.bKGD_green = stream.readUnsignedShort();
  315. metadata.bKGD_blue = stream.readUnsignedShort();
  316. }
  317. metadata.bKGD_present = true;
  318. }
  319. private void parse_cHRM_chunk() throws IOException {
  320. metadata.cHRM_whitePointX = stream.readInt();
  321. metadata.cHRM_whitePointY = stream.readInt();
  322. metadata.cHRM_redX = stream.readInt();
  323. metadata.cHRM_redY = stream.readInt();
  324. metadata.cHRM_greenX = stream.readInt();
  325. metadata.cHRM_greenY = stream.readInt();
  326. metadata.cHRM_blueX = stream.readInt();
  327. metadata.cHRM_blueY = stream.readInt();
  328. metadata.cHRM_present = true;
  329. }
  330. private void parse_gAMA_chunk() throws IOException {
  331. int gamma = stream.readInt();
  332. metadata.gAMA_gamma = gamma;
  333. metadata.gAMA_present = true;
  334. }
  335. private void parse_hIST_chunk() throws IOException, IIOException {
  336. if (!metadata.PLTE_present) {
  337. throw new IIOException("hIST chunk without prior PLTE chunk!");
  338. }
  339. metadata.hIST_histogram = new char[metadata.PLTE_red.length];
  340. stream.readFully(metadata.hIST_histogram,
  341. 0, metadata.hIST_histogram.length);
  342. metadata.hIST_present = true;
  343. }
  344. private void parse_iCCP_chunk(int chunkLength) throws IOException {
  345. String keyword = readNullTerminatedString();
  346. metadata.iCCP_profileName = keyword;
  347. metadata.iCCP_compressionMethod = stream.readUnsignedByte();
  348. byte[] compressedProfile =
  349. new byte[chunkLength - keyword.length() - 2];
  350. stream.readFully(compressedProfile);
  351. metadata.iCCP_compressedProfile = compressedProfile;
  352. metadata.iCCP_present = true;
  353. }
  354. private void parse_iTXt_chunk(int chunkLength) throws IOException {
  355. long chunkStart = stream.getStreamPosition();
  356. String keyword = readNullTerminatedString();
  357. metadata.iTXt_keyword.add(keyword);
  358. int compressionFlag = stream.readUnsignedByte();
  359. metadata.iTXt_compressionFlag.add(new Integer(compressionFlag));
  360. int compressionMethod = stream.readUnsignedByte();
  361. metadata.iTXt_compressionMethod.add(new Integer(compressionMethod));
  362. String languageTag = readNullTerminatedString();
  363. metadata.iTXt_languageTag.add(languageTag);
  364. String translatedKeyword = stream.readUTF();
  365. metadata.iTXt_translatedKeyword.add(translatedKeyword);
  366. stream.skipBytes(1); // Null separator
  367. String text;
  368. if (compressionFlag == 1) { // Decompress the text
  369. long pos = stream.getStreamPosition();
  370. byte[] b = new byte[(int)(chunkStart + chunkLength - pos)];
  371. stream.readFully(b);
  372. text = inflate(b);
  373. } else {
  374. text = stream.readUTF();
  375. }
  376. metadata.iTXt_text.add(text);
  377. }
  378. private void parse_pHYs_chunk() throws IOException {
  379. metadata.pHYs_pixelsPerUnitXAxis = stream.readInt();
  380. metadata.pHYs_pixelsPerUnitYAxis = stream.readInt();
  381. metadata.pHYs_unitSpecifier = stream.readUnsignedByte();
  382. metadata.pHYs_present = true;
  383. }
  384. private void parse_sBIT_chunk() throws IOException {
  385. int colorType = metadata.IHDR_colorType;
  386. if (colorType == PNG_COLOR_GRAY ||
  387. colorType == PNG_COLOR_GRAY_ALPHA) {
  388. metadata.sBIT_grayBits = stream.readUnsignedByte();
  389. } else if (colorType == PNG_COLOR_RGB ||
  390. colorType == PNG_COLOR_PALETTE ||
  391. colorType == PNG_COLOR_RGB_ALPHA) {
  392. metadata.sBIT_redBits = stream.readUnsignedByte();
  393. metadata.sBIT_greenBits = stream.readUnsignedByte();
  394. metadata.sBIT_blueBits = stream.readUnsignedByte();
  395. }
  396. if (colorType == PNG_COLOR_GRAY_ALPHA ||
  397. colorType == PNG_COLOR_RGB_ALPHA) {
  398. metadata.sBIT_alphaBits = stream.readUnsignedByte();
  399. }
  400. metadata.sBIT_colorType = colorType;
  401. metadata.sBIT_present = true;
  402. }
  403. private void parse_sPLT_chunk(int chunkLength)
  404. throws IOException, IIOException {
  405. metadata.sPLT_paletteName = readNullTerminatedString();
  406. chunkLength -= metadata.sPLT_paletteName.length() + 1;
  407. int sampleDepth = stream.readUnsignedByte();
  408. metadata.sPLT_sampleDepth = sampleDepth;
  409. int numEntries = chunkLength(4*(sampleDepth8) + 2);
  410. metadata.sPLT_red = new int[numEntries];
  411. metadata.sPLT_green = new int[numEntries];
  412. metadata.sPLT_blue = new int[numEntries];
  413. metadata.sPLT_alpha = new int[numEntries];
  414. metadata.sPLT_frequency = new int[numEntries];
  415. if (sampleDepth == 8) {
  416. for (int i = 0; i < numEntries; i++) {
  417. metadata.sPLT_red[i] = stream.readUnsignedByte();
  418. metadata.sPLT_green[i] = stream.readUnsignedByte();
  419. metadata.sPLT_blue[i] = stream.readUnsignedByte();
  420. metadata.sPLT_alpha[i] = stream.readUnsignedByte();
  421. metadata.sPLT_frequency[i] = stream.readUnsignedShort();
  422. }
  423. } else if (sampleDepth == 16) {
  424. for (int i = 0; i < numEntries; i++) {
  425. metadata.sPLT_red[i] = stream.readUnsignedShort();
  426. metadata.sPLT_green[i] = stream.readUnsignedShort();
  427. metadata.sPLT_blue[i] = stream.readUnsignedShort();
  428. metadata.sPLT_alpha[i] = stream.readUnsignedShort();
  429. metadata.sPLT_frequency[i] = stream.readUnsignedShort();
  430. }
  431. } else {
  432. throw new IIOException("sPLT sample depth not 8 or 16!");
  433. }
  434. metadata.sPLT_present = true;
  435. }
  436. private void parse_sRGB_chunk() throws IOException {
  437. metadata.sRGB_renderingIntent = stream.readUnsignedByte();
  438. metadata.sRGB_present = true;
  439. }
  440. private void parse_tEXt_chunk(int chunkLength) throws IOException {
  441. String keyword = readNullTerminatedString();
  442. metadata.tEXt_keyword.add(keyword);
  443. byte[] b = new byte[chunkLength - keyword.length() - 1];
  444. stream.readFully(b);
  445. metadata.tEXt_text.add(new String(b));
  446. }
  447. private void parse_tIME_chunk() throws IOException {
  448. metadata.tIME_year = stream.readUnsignedShort();
  449. metadata.tIME_month = stream.readUnsignedByte();
  450. metadata.tIME_day = stream.readUnsignedByte();
  451. metadata.tIME_hour = stream.readUnsignedByte();
  452. metadata.tIME_minute = stream.readUnsignedByte();
  453. metadata.tIME_second = stream.readUnsignedByte();
  454. metadata.tIME_present = true;
  455. }
  456. private void parse_tRNS_chunk(int chunkLength) throws IOException {
  457. int colorType = metadata.IHDR_colorType;
  458. if (colorType == PNG_COLOR_PALETTE) {
  459. if (!metadata.PLTE_present) {
  460. processWarningOccurred(
  461. "tRNS chunk without prior PLTE chunk, ignoring it.");
  462. return;
  463. }
  464. // Alpha table may have fewer entries than RGB palette
  465. int maxEntries = metadata.PLTE_red.length;
  466. int numEntries = chunkLength;
  467. if (numEntries > maxEntries) {
  468. processWarningOccurred(
  469. "tRNS chunk has more entries than prior PLTE chunk, ignoring extras.");
  470. numEntries = maxEntries;
  471. }
  472. metadata.tRNS_alpha = new byte[numEntries];
  473. metadata.tRNS_colorType = PNG_COLOR_PALETTE;
  474. stream.read(metadata.tRNS_alpha, 0, numEntries);
  475. stream.skipBytes(chunkLength - numEntries);
  476. } else if (colorType == PNG_COLOR_GRAY) {
  477. if (chunkLength != 2) {
  478. processWarningOccurred(
  479. "tRNS chunk for gray image must have length 2, ignoring chunk.");
  480. stream.skipBytes(chunkLength);
  481. return;
  482. }
  483. metadata.tRNS_gray = stream.readUnsignedShort();
  484. metadata.tRNS_colorType = PNG_COLOR_GRAY;
  485. } else if (colorType == PNG_COLOR_RGB) {
  486. if (chunkLength != 6) {
  487. processWarningOccurred(
  488. "tRNS chunk for RGB image must have length 6, ignoring chunk.");
  489. stream.skipBytes(chunkLength);
  490. return;
  491. }
  492. metadata.tRNS_red = stream.readUnsignedShort();
  493. metadata.tRNS_green = stream.readUnsignedShort();
  494. metadata.tRNS_blue = stream.readUnsignedShort();
  495. metadata.tRNS_colorType = PNG_COLOR_RGB;
  496. } else {
  497. processWarningOccurred(
  498. "Gray+Alpha and RGBS images may not have a tRNS chunk, ignoring it.");
  499. return;
  500. }
  501. metadata.tRNS_present = true;
  502. }
  503. private static String inflate(byte[] b) throws IOException {
  504. InputStream bais = new ByteArrayInputStream(b);
  505. InputStream iis = new InflaterInputStream(bais);
  506. StringBuffer sb = new StringBuffer(80);
  507. int c;
  508. while ((c = iis.read()) != -1) {
  509. sb.append((char)c);
  510. }
  511. return sb.toString();
  512. }
  513. private void parse_zTXt_chunk(int chunkLength) throws IOException {
  514. String keyword = readNullTerminatedString();
  515. metadata.zTXt_keyword.add(keyword);
  516. int method = stream.readUnsignedByte();
  517. metadata.zTXt_compressionMethod.add(new Integer(method));
  518. byte[] b = new byte[chunkLength - keyword.length() - 2];
  519. stream.readFully(b);
  520. metadata.zTXt_text.add(inflate(b));
  521. }
  522. private void readMetadata() throws IIOException {
  523. if (gotMetadata) {
  524. return;
  525. }
  526. readHeader();
  527. try {
  528. while (true) {
  529. int chunkLength = stream.readInt();
  530. int chunkType = stream.readInt();
  531. // If chunk type is 'IDAT', we've reached the image data.
  532. if (chunkType == IDAT_TYPE) {
  533. stream.skipBytes(-8);
  534. imageStartPosition = stream.getStreamPosition();
  535. break;
  536. }
  537. if (chunkType == PLTE_TYPE) {
  538. parse_PLTE_chunk(chunkLength);
  539. } else if (chunkType == bKGD_TYPE) {
  540. parse_bKGD_chunk();
  541. } else if (chunkType == cHRM_TYPE) {
  542. parse_cHRM_chunk();
  543. } else if (chunkType == gAMA_TYPE) {
  544. parse_gAMA_chunk();
  545. } else if (chunkType == hIST_TYPE) {
  546. parse_hIST_chunk();
  547. } else if (chunkType == iCCP_TYPE) {
  548. parse_iCCP_chunk(chunkLength);
  549. } else if (chunkType == iTXt_TYPE) {
  550. parse_iTXt_chunk(chunkLength);
  551. } else if (chunkType == pHYs_TYPE) {
  552. parse_pHYs_chunk();
  553. } else if (chunkType == sBIT_TYPE) {
  554. parse_sBIT_chunk();
  555. } else if (chunkType == sPLT_TYPE) {
  556. parse_sPLT_chunk(chunkLength);
  557. } else if (chunkType == sRGB_TYPE) {
  558. parse_sRGB_chunk();
  559. } else if (chunkType == tEXt_TYPE) {
  560. parse_tEXt_chunk(chunkLength);
  561. } else if (chunkType == tIME_TYPE) {
  562. parse_tIME_chunk();
  563. } else if (chunkType == tRNS_TYPE) {
  564. parse_tRNS_chunk(chunkLength);
  565. } else if (chunkType == zTXt_TYPE) {
  566. parse_zTXt_chunk(chunkLength);
  567. } else {
  568. // Read an unknown chunk
  569. byte[] b = new byte[chunkLength];
  570. stream.readFully(b);
  571. StringBuffer chunkName = new StringBuffer(4);
  572. chunkName.append((char)(chunkType >>> 24));
  573. chunkName.append((char)((chunkType >> 16) & 0xff));
  574. chunkName.append((char)((chunkType >> 8) & 0xff));
  575. chunkName.append((char)(chunkType & 0xff));
  576. int ancillaryBit = chunkType >>> 28;
  577. if (ancillaryBit == 0) {
  578. processWarningOccurred(
  579. "Encountered unknown chunk with critical bit set!");
  580. }
  581. metadata.unknownChunkType.add(chunkName.toString());
  582. metadata.unknownChunkData.add(b);
  583. }
  584. int chunkCRC = stream.readInt();
  585. stream.flushBefore(stream.getStreamPosition());
  586. }
  587. } catch (IOException e) {
  588. e.printStackTrace();
  589. throw new IIOException("Error reading PNG metadata", e);
  590. }
  591. gotMetadata = true;
  592. }
  593. // Data filtering methods
  594. private static void decodeSubFilter(byte[] curr, int coff, int count,
  595. int bpp) {
  596. for (int i = bpp; i < count; i++) {
  597. int val;
  598. val = curr[i + coff] & 0xff;
  599. val += curr[i + coff - bpp] & 0xff;
  600. curr[i + coff] = (byte)val;
  601. }
  602. }
  603. private static void decodeUpFilter(byte[] curr, int coff,
  604. byte[] prev, int poff,
  605. int count) {
  606. for (int i = 0; i < count; i++) {
  607. int raw = curr[i + coff] & 0xff;
  608. int prior = prev[i + poff] & 0xff;
  609. curr[i + coff] = (byte)(raw + prior);
  610. }
  611. }
  612. private static void decodeAverageFilter(byte[] curr, int coff,
  613. byte[] prev, int poff,
  614. int count, int bpp) {
  615. int raw, priorPixel, priorRow;
  616. for (int i = 0; i < bpp; i++) {
  617. raw = curr[i + coff] & 0xff;
  618. priorRow = prev[i + poff] & 0xff;
  619. curr[i + coff] = (byte)(raw + priorRow2);
  620. }
  621. for (int i = bpp; i < count; i++) {
  622. raw = curr[i + coff] & 0xff;
  623. priorPixel = curr[i + coff - bpp] & 0xff;
  624. priorRow = prev[i + poff] & 0xff;
  625. curr[i + coff] = (byte)(raw + (priorPixel + priorRow)/2);
  626. }
  627. }
  628. private static int paethPredictor(int a, int b, int c) {
  629. int p = a + b - c;
  630. int pa = Math.abs(p - a);
  631. int pb = Math.abs(p - b);
  632. int pc = Math.abs(p - c);
  633. if ((pa <= pb) && (pa <= pc)) {
  634. return a;
  635. } else if (pb <= pc) {
  636. return b;
  637. } else {
  638. return c;
  639. }
  640. }
  641. private static void decodePaethFilter(byte[] curr, int coff,
  642. byte[] prev, int poff,
  643. int count, int bpp) {
  644. int raw, priorPixel, priorRow, priorRowPixel;
  645. for (int i = 0; i < bpp; i++) {
  646. raw = curr[i + coff] & 0xff;
  647. priorRow = prev[i + poff] & 0xff;
  648. curr[i + coff] = (byte)(raw + priorRow);
  649. }
  650. for (int i = bpp; i < count; i++) {
  651. raw = curr[i + coff] & 0xff;
  652. priorPixel = curr[i + coff - bpp] & 0xff;
  653. priorRow = prev[i + poff] & 0xff;
  654. priorRowPixel = prev[i + poff - bpp] & 0xff;
  655. curr[i + coff] = (byte)(raw + paethPredictor(priorPixel,
  656. priorRow,
  657. priorRowPixel));
  658. }
  659. }
  660. private static final int[][] bandOffsets = {
  661. null,
  662. { 0 }, // G
  663. { 0, 1 }, // GA in GA order
  664. { 0, 1, 2 }, // RGB in RGB order
  665. { 0, 1, 2, 3 } // RGBA in RGBA order
  666. };
  667. private WritableRaster createRaster(int width, int height, int bands,
  668. int scanlineStride,
  669. int bitDepth) {
  670. DataBuffer dataBuffer;
  671. WritableRaster ras = null;
  672. Point origin = new Point(0, 0);
  673. if ((bitDepth < 8) && (bands == 1)) {
  674. dataBuffer = new DataBufferByte(height*scanlineStride);
  675. ras = Raster.createPackedRaster(dataBuffer,
  676. width, height,
  677. bitDepth,
  678. origin);
  679. } else if (bitDepth <= 8) {
  680. dataBuffer = new DataBufferByte(height*scanlineStride);
  681. ras = Raster.createInterleavedRaster(dataBuffer,
  682. width, height,
  683. scanlineStride,
  684. bands,
  685. bandOffsets[bands],
  686. origin);
  687. } else {
  688. dataBuffer = new DataBufferUShort(height*scanlineStride);
  689. ras = Raster.createInterleavedRaster(dataBuffer,
  690. width, height,
  691. scanlineStride,
  692. bands,
  693. bandOffsets[bands],
  694. origin);
  695. }
  696. return ras;
  697. }
  698. private void skipPass(int passWidth, int passHeight)
  699. throws IOException, IIOException {
  700. if ((passWidth == 0) || (passHeight == 0)) {
  701. return;
  702. }
  703. int inputBands = inputBandsForColorType[metadata.IHDR_colorType];
  704. int bytesPerRow = (inputBands*passWidth*metadata.IHDR_bitDepth + 7)/8;
  705. byte[] curr = new byte[bytesPerRow];
  706. // Read the image row-by-row
  707. for (int srcY = 0; srcY < passHeight; srcY++) {
  708. // Read the filter type byte and a row of data
  709. int filter = pixelStream.read();
  710. pixelStream.readFully(curr, 0, bytesPerRow);
  711. // If read has been aborted, just return
  712. // processReadAborted will be called later
  713. if (abortRequested()) {
  714. return;
  715. }
  716. }
  717. }
  718. // Helper for protected computeUpdatedPixels method
  719. private static void computeUpdatedPixels(int sourceOffset,
  720. int sourceExtent,
  721. int destinationOffset,
  722. int dstMin,
  723. int dstMax,
  724. int sourceSubsampling,
  725. int passStart,
  726. int passExtent,
  727. int passPeriod,
  728. int[] vals,
  729. int offset) {
  730. // We need to satisfy the congruences:
  731. // dst = destinationOffset + (src - sourceOffset)/sourceSubsampling
  732. //
  733. // src - passStart == 0 (mod passPeriod)
  734. // src - sourceOffset == 0 (mod sourceSubsampling)
  735. //
  736. // subject to the inequalities:
  737. //
  738. // src >= passStart
  739. // src < passStart + passExtent
  740. // src >= sourceOffset
  741. // src < sourceOffset + sourceExtent
  742. // dst >= dstMin
  743. // dst <= dstmax
  744. //
  745. // where
  746. //
  747. // dst = destinationOffset + (src - sourceOffset)/sourceSubsampling
  748. //
  749. // For now we use a brute-force approach although we could
  750. // attempt to analyze the congruences. If passPeriod and
  751. // sourceSubsamling are relatively prime, the period will be
  752. // their product. If they share a common factor, either the
  753. // period will be equal to the larger value, or the sequences
  754. // will be completely disjoint, depending on the relationship
  755. // between passStart and sourceOffset. Since we only have to do this
  756. // twice per image (once each for X and Y), it seems cheap enough
  757. // to do it the straightforward way.
  758. boolean gotPixel = false;
  759. int firstDst = -1;
  760. int secondDst = -1;
  761. int lastDst = -1;
  762. for (int i = 0; i < passExtent; i++) {
  763. int src = passStart + i*passPeriod;
  764. if (src < sourceOffset) {
  765. continue;
  766. }
  767. if ((src - sourceOffset) % sourceSubsampling != 0) {
  768. continue;
  769. }
  770. if (src >= sourceOffset + sourceExtent) {
  771. break;
  772. }
  773. int dst = destinationOffset +
  774. (src - sourceOffset)/sourceSubsampling;
  775. if (dst < dstMin) {
  776. continue;
  777. }
  778. if (dst > dstMax) {
  779. break;
  780. }
  781. if (!gotPixel) {
  782. firstDst = dst; // Record smallest valid pixel
  783. gotPixel = true;
  784. } else if (secondDst == -1) {
  785. secondDst = dst; // Record second smallest valid pixel
  786. }
  787. lastDst = dst; // Record largest valid pixel
  788. }
  789. vals[offset] = firstDst;
  790. // If we never saw a valid pixel, set width to 0
  791. if (!gotPixel) {
  792. vals[offset + 2] = 0;
  793. } else {
  794. vals[offset + 2] = lastDst - firstDst + 1;
  795. }
  796. // The period is given by the difference of any two adjacent pixels
  797. vals[offset + 4] = Math.max(secondDst - firstDst, 1);
  798. }
  799. /**
  800. * A utility method that computes the exact set of destination
  801. * pixels that will be written during a particular decoding pass.
  802. * The intent is to simplify the work done by readers in combining
  803. * the source region, source subsampling, and destination offset
  804. * information obtained from the <code>ImageReadParam</code> with
  805. * the offsets and periods of a progressive or interlaced decoding
  806. * pass.
  807. *
  808. * @param sourceRegion a <code>Rectangle</code> containing the
  809. * source region being read, offset by the source subsampling
  810. * offsets, and clipped against the source bounds, as returned by
  811. * the <code>getSourceRegion</code> method.
  812. * @param destinationOffset a <code>Point</code> containing the
  813. * coordinates of the upper-left pixel to be written in the
  814. * destination.
  815. * @param dstMinX the smallest X coordinate (inclusive) of the
  816. * destination <code>Raster</code>.
  817. * @param dstMinY the smallest Y coordinate (inclusive) of the
  818. * destination <code>Raster</code>.
  819. * @param dstMaxX the largest X coordinate (inclusive) of the destination
  820. * <code>Raster</code>.
  821. * @param dstMaxY the largest Y coordinate (inclusive) of the destination
  822. * <code>Raster</code>.
  823. * @param sourceXSubsampling the X subsampling factor.
  824. * @param sourceYSubsampling the Y subsampling factor.
  825. * @param passXStart the smallest source X coordinate (inclusive)
  826. * of the current progressive pass.
  827. * @param passYStart the smallest source Y coordinate (inclusive)
  828. * of the current progressive pass.
  829. * @param passWidth the width in pixels of the current progressive
  830. * pass.
  831. * @param passHeight the height in pixels of the current progressive
  832. * pass.
  833. * @param passPeriodX the X period (horizontal spacing between
  834. * pixels) of the current progressive pass.
  835. * @param passPeriodY the Y period (vertical spacing between
  836. * pixels) of the current progressive pass.
  837. *
  838. * @return an array of 6 <code>int</code>s containing the
  839. * destination min X, min Y, width, height, X period and Y period
  840. * of the region that will be updated.
  841. */
  842. private static int[] computeUpdatedPixels(Rectangle sourceRegion,
  843. Point destinationOffset,
  844. int dstMinX,
  845. int dstMinY,
  846. int dstMaxX,
  847. int dstMaxY,
  848. int sourceXSubsampling,
  849. int sourceYSubsampling,
  850. int passXStart,
  851. int passYStart,
  852. int passWidth,
  853. int passHeight,
  854. int passPeriodX,
  855. int passPeriodY) {
  856. int[] vals = new int[6];
  857. computeUpdatedPixels(sourceRegion.x, sourceRegion.width,
  858. destinationOffset.x,
  859. dstMinX, dstMaxX, sourceXSubsampling,
  860. passXStart, passWidth, passPeriodX,
  861. vals, 0);
  862. computeUpdatedPixels(sourceRegion.y, sourceRegion.height,
  863. destinationOffset.y,
  864. dstMinY, dstMaxY, sourceYSubsampling,
  865. passYStart, passHeight, passPeriodY,
  866. vals, 1);
  867. return vals;
  868. }
  869. private void updateImageProgress(int newPixels) {
  870. pixelsDone += newPixels;
  871. processImageProgress(100.0F*pixelsDonetotalPixels);
  872. }
  873. private void decodePass(int passNum,
  874. int xStart, int yStart,
  875. int xStep, int yStep,
  876. int passWidth, int passHeight) throws IOException {
  877. if ((passWidth == 0) || (passHeight == 0)) {
  878. return;
  879. }
  880. WritableRaster imRas = theImage.getWritableTile(0, 0);
  881. int dstMinX = imRas.getMinX();
  882. int dstMaxX = dstMinX + imRas.getWidth() - 1;
  883. int dstMinY = imRas.getMinY();
  884. int dstMaxY = dstMinY + imRas.getHeight() - 1;
  885. // Determine which pixels will be updated in this pass
  886. int[] vals = computeUpdatedPixels(sourceRegion,
  887. destinationOffset,
  888. dstMinX, dstMinY,
  889. dstMaxX, dstMaxY,
  890. sourceXSubsampling,
  891. sourceYSubsampling,
  892. xStart, yStart,
  893. passWidth, passHeight,
  894. xStep, yStep);
  895. int updateMinX = vals[0];
  896. int updateMinY = vals[1];
  897. int updateWidth = vals[2];
  898. int updateXStep = vals[4];
  899. int updateYStep = vals[5];
  900. int bitDepth = metadata.IHDR_bitDepth;
  901. int inputBands = inputBandsForColorType[metadata.IHDR_colorType];
  902. int bytesPerPixel = (bitDepth == 16) ? 2 : 1;
  903. bytesPerPixel *= inputBands;
  904. int bytesPerRow = (inputBands*passWidth*bitDepth + 7)/8;
  905. int eltsPerRow = (bitDepth == 16) ? bytesPerRow2 : bytesPerRow;
  906. // If no pixels need updating, just skip the input data
  907. if (updateWidth == 0) {
  908. for (int srcY = 0; srcY < passHeight; srcY++) {
  909. // Update count of pixels read
  910. updateImageProgress(passWidth);
  911. pixelStream.skipBytes(1 + bytesPerRow);
  912. }
  913. return;
  914. }
  915. // Backwards map from destination pixels
  916. // (dstX = updateMinX + k*updateXStep)
  917. // to source pixels (sourceX), and then
  918. // to offset and skip in passRow (srcX and srcXStep)
  919. int sourceX =
  920. (updateMinX - destinationOffset.x)*sourceXSubsampling +
  921. sourceRegion.x;
  922. int srcX = (sourceX - xStart)/xStep;
  923. // Compute the step factor in the source
  924. int srcXStep = updateXStep*sourceXSubsamplingxStep;
  925. byte[] byteData = null;
  926. short[] shortData = null;
  927. byte[] curr = new byte[bytesPerRow];
  928. byte[] prior = new byte[bytesPerRow];
  929. // Create a 1-row tall Raster to hold the data
  930. WritableRaster passRow = createRaster(passWidth, 1, inputBands,
  931. eltsPerRow,
  932. bitDepth);
  933. // Create an array suitable for holding one pixel
  934. int[] ps = passRow.getPixel(0, 0, (int[])null);
  935. DataBuffer dataBuffer = passRow.getDataBuffer();
  936. int type = dataBuffer.getDataType();
  937. if (type == DataBuffer.TYPE_BYTE) {
  938. byteData = ((DataBufferByte)dataBuffer).getData();
  939. } else {
  940. shortData = ((DataBufferUShort)dataBuffer).getData();
  941. }
  942. processPassStarted(theImage,
  943. passNum,
  944. sourceMinProgressivePass,
  945. sourceMaxProgressivePass,
  946. updateMinX, updateMinY,
  947. updateXStep, updateYStep,
  948. destinationBands);
  949. // Handle source and destination bands
  950. if (sourceBands != null) {
  951. passRow = passRow.createWritableChild(0, 0,
  952. passRow.getWidth(), 1,
  953. 0, 0,
  954. sourceBands);
  955. }
  956. if (destinationBands != null) {
  957. imRas = imRas.createWritableChild(0, 0,
  958. imRas.getWidth(),
  959. imRas.getHeight(),
  960. 0, 0,
  961. destinationBands);
  962. }
  963. // Determine if all of the relevant output bands have the
  964. // same bit depth as the source data
  965. boolean adjustBitDepths = false;
  966. int[] outputSampleSize = imRas.getSampleModel().getSampleSize();
  967. int numBands = outputSampleSize.length;
  968. for (int b = 0; b < numBands; b++) {
  969. if (outputSampleSize[b] != bitDepth) {
  970. adjustBitDepths = true;
  971. break;
  972. }
  973. }
  974. // If the bit depths differ, create a lookup table per band to perform
  975. // the conversion
  976. int[][] scale = null;
  977. if (adjustBitDepths) {
  978. int maxInSample = (1 << bitDepth) - 1;
  979. int halfMaxInSample = maxInSample2;
  980. scale = new int[numBands][];
  981. for (int b = 0; b < numBands; b++) {
  982. int maxOutSample = (1 << outputSampleSize[b]) - 1;
  983. scale[b] = new int[maxInSample + 1];
  984. for (int s = 0; s <= maxInSample; s++) {
  985. scale[b][s] =
  986. (s*maxOutSample + halfMaxInSample)/maxInSample;
  987. }
  988. }
  989. }
  990. // Limit passRow to relevant area for the case where we
  991. // will can setRect to copy a contiguous span
  992. boolean useSetRect = srcXStep == 1 &&
  993. updateXStep == 1 &&
  994. !adjustBitDepths;
  995. if (useSetRect) {
  996. passRow = passRow.createWritableChild(srcX, 0,
  997. updateWidth, 1,
  998. 0, 0,
  999. null);
  1000. }
  1001. // Decode the (sub)image row-by-row
  1002. for (int srcY = 0; srcY < passHeight; srcY++) {
  1003. // Update count of pixels read
  1004. updateImageProgress(passWidth);
  1005. // Read the filter type byte and a row of data
  1006. int filter = pixelStream.read();
  1007. try {
  1008. // Swap curr and prior
  1009. byte[] tmp = prior;
  1010. prior = curr;
  1011. curr = tmp;
  1012. pixelStream.readFully(curr, 0, bytesPerRow);
  1013. } catch (java.util.zip.ZipException ze) {
  1014. // TODO - throw a more meaningful exception
  1015. throw ze;
  1016. }
  1017. switch (filter) {
  1018. case PNG_FILTER_NONE:
  1019. break;
  1020. case PNG_FILTER_SUB:
  1021. decodeSubFilter(curr, 0, bytesPerRow, bytesPerPixel);
  1022. break;
  1023. case PNG_FILTER_UP:
  1024. decodeUpFilter(curr, 0, prior, 0, bytesPerRow);
  1025. break;
  1026. case PNG_FILTER_AVERAGE:
  1027. decodeAverageFilter(curr, 0, prior, 0, bytesPerRow,
  1028. bytesPerPixel);
  1029. break;
  1030. case PNG_FILTER_PAETH:
  1031. decodePaethFilter(curr, 0, prior, 0, bytesPerRow,
  1032. bytesPerPixel);
  1033. break;
  1034. default:
  1035. throw new IIOException("Unknown row filter type (= " +
  1036. filter + ")!");
  1037. }
  1038. // Copy data into passRow byte by byte
  1039. if (bitDepth < 16) {
  1040. System.arraycopy(curr, 0, byteData, 0, bytesPerRow);
  1041. } else {
  1042. int idx = 0;
  1043. for (int j = 0; j < eltsPerRow; j++) {
  1044. shortData[j] =
  1045. (short)((curr[idx] << 8) | (curr[idx + 1] & 0xff));
  1046. idx += 2;
  1047. }
  1048. }
  1049. // True Y position in source
  1050. int sourceY = srcY*yStep + yStart;
  1051. if ((sourceY >= sourceRegion.y) &&
  1052. (sourceY < sourceRegion.y + sourceRegion.height) &&
  1053. (((sourceY - sourceRegion.y) %
  1054. sourceYSubsampling) == 0)) {
  1055. int dstY = destinationOffset.y +
  1056. (sourceY - sourceRegion.y)/sourceYSubsampling;
  1057. if (dstY < dstMinY) {
  1058. continue;
  1059. }
  1060. if (dstY > dstMaxY) {
  1061. break;
  1062. }
  1063. if (useSetRect) {
  1064. imRas.setRect(updateMinX, dstY, passRow);
  1065. } else {
  1066. int newSrcX = srcX;
  1067. for (int dstX = updateMinX;
  1068. dstX < updateMinX + updateWidth;
  1069. dstX += updateXStep) {
  1070. passRow.getPixel(newSrcX, 0, ps);
  1071. if (adjustBitDepths) {
  1072. for (int b = 0; b < numBands; b++) {
  1073. ps[b] = scale[b][ps[b]];
  1074. }
  1075. }
  1076. imRas.setPixel(dstX, dstY, ps);
  1077. newSrcX += srcXStep;
  1078. }
  1079. }
  1080. processImageUpdate(theImage,
  1081. updateMinX, dstY,
  1082. updateWidth, 1,
  1083. updateXStep, updateYStep,
  1084. destinationBands);
  1085. // If read has been aborted, just return
  1086. // processReadAborted will be called later
  1087. if (abortRequested()) {
  1088. return;
  1089. }
  1090. }
  1091. }
  1092. processPassComplete(theImage);
  1093. }
  1094. private void decodeImage()
  1095. throws IOException, IIOException {
  1096. int width = metadata.IHDR_width;
  1097. int height = metadata.IHDR_height;
  1098. this.pixelsDone = 0;
  1099. this.totalPixels = width*height;
  1100. clearAbortRequest();
  1101. if (metadata.IHDR_interlaceMethod == 0) {
  1102. decodePass(0, 0, 0, 1, 1, width, height);
  1103. } else {
  1104. for (int i = 0; i <= sourceMaxProgressivePass; i++) {
  1105. int XOffset = adam7XOffset[i];
  1106. int YOffset = adam7YOffset[i];
  1107. int XSubsampling = adam7XSubsampling[i];
  1108. int YSubsampling = adam7YSubsampling[i];
  1109. int xbump = adam7XSubsampling[i + 1] - 1;
  1110. int ybump = adam7YSubsampling[i + 1] - 1;
  1111. if (i >= sourceMinProgressivePass) {
  1112. decodePass(i,
  1113. XOffset,
  1114. YOffset,
  1115. XSubsampling,
  1116. YSubsampling,
  1117. (width + xbump)/XSubsampling,
  1118. (height + ybump)/YSubsampling);
  1119. } else {
  1120. skipPass((width + xbump)/XSubsampling,
  1121. (height + ybump)/YSubsampling);
  1122. }
  1123. // If read has been aborted, just return
  1124. // processReadAborted will be called later
  1125. if (abortRequested()) {
  1126. return;
  1127. }
  1128. }
  1129. }
  1130. }
  1131. private void readImage(ImageReadParam param) throws IIOException {
  1132. readMetadata();
  1133. int width = metadata.IHDR_width;
  1134. int height = metadata.IHDR_height;
  1135. // Init default values
  1136. sourceRegion = getSourceRegion(param, width, height);
  1137. sourceXSubsampling = 1;
  1138. sourceYSubsampling = 1;
  1139. sourceMinProgressivePass = 0;
  1140. sourceMaxProgressivePass = 6;
  1141. sourceBands = null;
  1142. destinationBands = null;
  1143. destinationOffset = new Point(0, 0);
  1144. // If an ImageReadParam is available, get values from it
  1145. if (param != null) {
  1146. sourceXSubsampling = param.getSourceXSubsampling();
  1147. sourceYSubsampling = param.getSourceYSubsampling();
  1148. sourceMinProgressivePass =
  1149. Math.max(param.getSourceMinProgressivePass(), 0);
  1150. sourceMaxProgressivePass =
  1151. Math.min(param.getSourceMaxProgressivePass(), 6);
  1152. sourceBands = param.getSourceBands();
  1153. destinationBands = param.getDestinationBands();
  1154. destinationOffset = param.getDestinationOffset();
  1155. }
  1156. try {
  1157. stream.seek(imageStartPosition);
  1158. Enumeration e = new PNGImageDataEnumeration(stream);
  1159. InputStream is = new SequenceInputStream(e);
  1160. is = new InflaterInputStream(is, new Inflater());
  1161. is = new BufferedInputStream(is);
  1162. this.pixelStream = new DataInputStream(is);
  1163. theImage = getDestination(param,
  1164. getImageTypes(0),
  1165. width,
  1166. height);
  1167. // At this point the header has been read and we know
  1168. // how many bands are in the image, so perform checking
  1169. // of the read param.
  1170. int colorType = metadata.IHDR_colorType;
  1171. checkReadParamBandSettings(param,
  1172. inputBandsForColorType[colorType],
  1173. theImage.getSampleModel().getNumBands());
  1174. processImageStarted(0);
  1175. decodeImage();
  1176. if (abortRequested()) {
  1177. processReadAborted();
  1178. } else {
  1179. processImageComplete();
  1180. }
  1181. } catch (IOException e) {
  1182. e.printStackTrace();
  1183. throw new IIOException("Error reading PNG image data", e);
  1184. }
  1185. }
  1186. public int getNumImages(boolean allowSearch) throws IIOException {
  1187. if (stream == null) {
  1188. throw new IllegalStateException("No input source set!");
  1189. }
  1190. if (seekForwardOnly && allowSearch) {
  1191. throw new IllegalStateException
  1192. ("seekForwardOnly and allowSearch can't both be true!");
  1193. }
  1194. return 1;
  1195. }
  1196. public int getWidth(int imageIndex) throws IIOException {
  1197. readHeader();
  1198. return metadata.IHDR_width;
  1199. }
  1200. public int getHeight(int imageIndex) throws IIOException {
  1201. readHeader();
  1202. return metadata.IHDR_height;
  1203. }
  1204. public Iterator getImageTypes(int imageIndex) throws IIOException {
  1205. if (imageIndex != 0) {
  1206. throw new IndexOutOfBoundsException("imageIndex != 0!");
  1207. }
  1208. readHeader();
  1209. ArrayList l = new ArrayList(1); // List of ImageTypeSpecifiers
  1210. ColorSpace rgb;
  1211. ColorSpace gray;
  1212. int[] bandOffsets;
  1213. int bitDepth = metadata.IHDR_bitDepth;
  1214. int colorType = metadata.IHDR_colorType;
  1215. int dataType;
  1216. if (bitDepth <= 8) {
  1217. dataType = DataBuffer.TYPE_BYTE;
  1218. } else {
  1219. dataType = DataBuffer.TYPE_USHORT;
  1220. }
  1221. switch (colorType) {
  1222. case PNG_COLOR_GRAY:
  1223. // Packed grayscale
  1224. l.add(ImageTypeSpecifier.createGrayscale(bitDepth,
  1225. dataType,
  1226. false));
  1227. break;
  1228. case PNG_COLOR_RGB:
  1229. // Component R, G, B
  1230. rgb = ColorSpace.getInstance(ColorSpace.CS_sRGB);
  1231. bandOffsets = new int[3];
  1232. bandOffsets[0] = 0;
  1233. bandOffsets[1] = 1;
  1234. bandOffsets[2] = 2;
  1235. l.add(ImageTypeSpecifier.createInterleaved(rgb,
  1236. bandOffsets,
  1237. dataType,
  1238. false,
  1239. false));
  1240. break;
  1241. case PNG_COLOR_PALETTE:
  1242. readMetadata(); // Need tRNS chunk
  1243. // Alpha from tRNS chunk may have fewer entries than
  1244. // the RGB LUTs from the PLTE chunk; if so, pad with
  1245. // 255.
  1246. byte[] alpha = null;
  1247. if (metadata.tRNS_present && (metadata.tRNS_alpha != null)) {
  1248. if (metadata.tRNS_alpha.length == metadata.PLTE_red.length) {
  1249. alpha = metadata.tRNS_alpha;
  1250. } else {
  1251. alpha = new byte[metadata.PLTE_red.length];
  1252. System.arraycopy(metadata.tRNS_alpha, 0,
  1253. alpha, 0,
  1254. metadata.tRNS_alpha.length);
  1255. Arrays.fill(alpha,
  1256. metadata.tRNS_alpha.length,
  1257. metadata.PLTE_red.length,
  1258. (byte)255);
  1259. }
  1260. }
  1261. l.add(ImageTypeSpecifier.createIndexed(metadata.PLTE_red,
  1262. metadata.PLTE_green,
  1263. metadata.PLTE_blue,
  1264. alpha,
  1265. bitDepth,
  1266. DataBuffer.TYPE_BYTE));
  1267. break;
  1268. case PNG_COLOR_GRAY_ALPHA:
  1269. // Component G, A
  1270. gray = ColorSpace.getInstance(ColorSpace.CS_GRAY);
  1271. bandOffsets = new int[2];
  1272. bandOffsets[0] = 0;
  1273. bandOffsets[1] = 1;
  1274. l.add(ImageTypeSpecifier.createInterleaved(gray,
  1275. bandOffsets,
  1276. dataType,
  1277. true,
  1278. false));
  1279. break;
  1280. case PNG_COLOR_RGB_ALPHA:
  1281. // Component R, G, B, A (non-premultiplied)
  1282. rgb = ColorSpace.getInstance(ColorSpace.CS_sRGB);
  1283. bandOffsets = new int[4];
  1284. bandOffsets[0] = 0;
  1285. bandOffsets[1] = 1;
  1286. bandOffsets[2] = 2;
  1287. bandOffsets[3] = 3;
  1288. l.add(ImageTypeSpecifier.createInterleaved(rgb,
  1289. bandOffsets,
  1290. dataType,
  1291. true,
  1292. false));
  1293. break;
  1294. default:
  1295. break;
  1296. }
  1297. return l.iterator();
  1298. }
  1299. public ImageReadParam getDefaultReadParam() {
  1300. return new ImageReadParam();
  1301. }
  1302. public IIOMetadata getStreamMetadata()
  1303. throws IIOException {
  1304. return null;
  1305. }
  1306. public IIOMetadata getImageMetadata(int imageIndex) throws IIOException {
  1307. if (imageIndex != 0) {
  1308. throw new IndexOutOfBoundsException("imageIndex != 0!");
  1309. }
  1310. readMetadata();
  1311. return metadata;
  1312. }
  1313. public BufferedImage read(int imageIndex, ImageReadParam param)
  1314. throws IIOException {
  1315. if (imageIndex != 0) {
  1316. throw new IndexOutOfBoundsException("imageIndex != 0!");
  1317. }
  1318. readImage(param);
  1319. return theImage;
  1320. }
  1321. public void reset() {
  1322. super.reset();
  1323. resetStreamSettings();
  1324. }
  1325. private void resetStreamSettings() {
  1326. gotHeader = false;
  1327. gotMetadata = false;
  1328. metadata = null;
  1329. pixelStream = null;
  1330. }
  1331. }