JPEGDecoder.java 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626
  1. /* JPEGDecoder.java --
  2. Copyright (C) 2006 Free Software Foundation, Inc.
  3. This file is part of GNU Classpath.
  4. GNU Classpath is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 2, or (at your option)
  7. any later version.
  8. GNU Classpath is distributed in the hope that it will be useful, but
  9. WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with GNU Classpath; see the file COPYING. If not, write to the
  14. Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  15. 02110-1301 USA.
  16. Linking this library statically or dynamically with other modules is
  17. making a combined work based on this library. Thus, the terms and
  18. conditions of the GNU General Public License cover the whole
  19. combination.
  20. As a special exception, the copyright holders of this library give you
  21. permission to link this library with independent modules to produce an
  22. executable, regardless of the license terms of these independent
  23. modules, and to copy and distribute the resulting executable under
  24. terms of your choice, provided that you also meet, for each linked
  25. independent module, the terms and conditions of the license of that
  26. module. An independent module is a module which is not derived from
  27. or based on this library. If you modify this library, you may extend
  28. this exception to your version of the library, but you are not
  29. obligated to do so. If you do not wish to do so, delete this
  30. exception statement from your version. */
  31. package gnu.javax.imageio.jpeg;
  32. import java.io.IOException;
  33. import java.nio.ByteOrder;
  34. import javax.imageio.plugins.jpeg.JPEGHuffmanTable;
  35. import javax.imageio.plugins.jpeg.JPEGQTable;
  36. import javax.imageio.stream.ImageInputStream;
  37. import java.util.ArrayList;
  38. import java.util.Hashtable;
  39. import java.awt.Point;
  40. import java.awt.Transparency;
  41. import java.awt.color.ColorSpace;
  42. import java.awt.image.BufferedImage;
  43. import java.awt.image.ComponentColorModel;
  44. import java.awt.image.DataBuffer;
  45. import java.awt.image.Raster;
  46. import java.awt.image.WritableRaster;
  47. public class JPEGDecoder
  48. {
  49. byte majorVersion;
  50. byte minorVersion;
  51. byte units;
  52. short Xdensity;
  53. short Ydensity;
  54. byte Xthumbnail;
  55. byte Ythumbnail;
  56. byte[] thumbnail;
  57. BufferedImage image;
  58. int width;
  59. int height;
  60. byte marker;
  61. /**
  62. * This decoder expects JFIF 1.02 encoding.
  63. */
  64. public static final byte MAJOR_VERSION = (byte) 1;
  65. public static final byte MINOR_VERSION = (byte) 2;
  66. /**
  67. * The length of the JFIF field not including thumbnail data.
  68. */
  69. public static final short JFIF_FIXED_LENGTH = 16;
  70. /**
  71. * The length of the JFIF extension field not including extension
  72. * data.
  73. */
  74. public static final short JFXX_FIXED_LENGTH = 8;
  75. private JPEGImageInputStream jpegStream;
  76. ArrayList jpegFrames = new ArrayList();
  77. JPEGHuffmanTable[] dcTables = new JPEGHuffmanTable[4];
  78. JPEGHuffmanTable[] acTables = new JPEGHuffmanTable[4];
  79. JPEGQTable[] qTables = new JPEGQTable[4];
  80. public int getHeight()
  81. {
  82. return height;
  83. }
  84. public int getWidth()
  85. {
  86. return width;
  87. }
  88. public JPEGDecoder(ImageInputStream in)
  89. throws IOException, JPEGException
  90. {
  91. jpegStream = new JPEGImageInputStream(in);
  92. jpegStream.setByteOrder(ByteOrder.LITTLE_ENDIAN);
  93. if (jpegStream.findNextMarker() != JPEGMarker.SOI)
  94. throw new JPEGException("Failed to find SOI marker.");
  95. if (jpegStream.findNextMarker() != JPEGMarker.APP0)
  96. throw new JPEGException("Failed to find APP0 marker.");
  97. int length = jpegStream.readShort();
  98. if (!(length >= JFIF_FIXED_LENGTH))
  99. throw new JPEGException("Failed to find JFIF field.");
  100. byte[] identifier = new byte[5];
  101. jpegStream.read(identifier);
  102. if (identifier[0] != JPEGMarker.JFIF_J
  103. || identifier[1] != JPEGMarker.JFIF_F
  104. || identifier[2] != JPEGMarker.JFIF_I
  105. || identifier[3] != JPEGMarker.JFIF_F
  106. || identifier[4] != JPEGMarker.X00)
  107. throw new JPEGException("Failed to read JFIF identifier.");
  108. majorVersion = jpegStream.readByte();
  109. minorVersion = jpegStream.readByte();
  110. if (majorVersion != MAJOR_VERSION
  111. || (majorVersion == MAJOR_VERSION
  112. && minorVersion < MINOR_VERSION))
  113. throw new JPEGException("Unsupported JFIF version.");
  114. units = jpegStream.readByte();
  115. if (units > (byte) 2)
  116. throw new JPEGException("Units field is out of range.");
  117. Xdensity = jpegStream.readShort();
  118. Ydensity = jpegStream.readShort();
  119. Xthumbnail = jpegStream.readByte();
  120. Ythumbnail = jpegStream.readByte();
  121. // 3 * for RGB data
  122. int thumbnailLength = 3 * Xthumbnail * Ythumbnail;
  123. if (length > JFIF_FIXED_LENGTH
  124. && thumbnailLength != length - JFIF_FIXED_LENGTH)
  125. throw new JPEGException("Invalid length, Xthumbnail"
  126. + " or Ythumbnail field.");
  127. if (thumbnailLength > 0)
  128. {
  129. thumbnail = new byte[thumbnailLength];
  130. if (jpegStream.read(thumbnail) != thumbnailLength)
  131. throw new IOException("Failed to read thumbnail.");
  132. }
  133. }
  134. public void decode()
  135. throws IOException
  136. {
  137. System.out.println ("DECODE!!!");
  138. // The frames in this jpeg are loaded into a list. There is
  139. // usually just one frame except in heirarchial progression where
  140. // there are multiple frames.
  141. JPEGFrame frame = null;
  142. // The restart interval defines how many MCU's we should have
  143. // between the 8-modulo restart marker. The restart markers allow
  144. // us to tell whether or not our decoding process is working
  145. // correctly, also if there is corruption in the image we can
  146. // recover with these restart intervals. (See RSTm DRI).
  147. int resetInterval = 0;
  148. // The JPEGDecoder constructor parses the JFIF field. At this
  149. // point jpegStream points to the first byte after the JFIF field.
  150. // Find the first marker after the JFIF field.
  151. byte marker = jpegStream.findNextMarker();
  152. // Check for a JFIF extension field directly following the JFIF
  153. // header and advance the current marker to the next marker in the
  154. // stream, if necessary.
  155. decodeJFIFExtension();
  156. // Loop through until there are no more markers to read in, at
  157. // that point everything is loaded into the jpegFrames array and
  158. // can be processed.
  159. while (true)
  160. {
  161. switch (marker)
  162. {
  163. // APPn Application Reserved Information - Just throw this
  164. // information away because we wont be using it.
  165. case JPEGMarker.APP0:
  166. case JPEGMarker.APP1:
  167. case JPEGMarker.APP2:
  168. case JPEGMarker.APP3:
  169. case JPEGMarker.APP4:
  170. case JPEGMarker.APP5:
  171. case JPEGMarker.APP6:
  172. case JPEGMarker.APP7:
  173. case JPEGMarker.APP8:
  174. case JPEGMarker.APP9:
  175. case JPEGMarker.APP10:
  176. case JPEGMarker.APP11:
  177. case JPEGMarker.APP12:
  178. case JPEGMarker.APP13:
  179. case JPEGMarker.APP14:
  180. case JPEGMarker.APP15:
  181. jpegStream.skipBytes(jpegStream.readShort() - 2);
  182. break;
  183. case JPEGMarker.SOF0:
  184. // SOFn Start of Frame Marker, Baseline DCT - This is the start
  185. // of the frame header that defines certain variables that will
  186. // be carried out through the rest of the encoding. Multiple
  187. // frames are used in a heirarchiel system, however most JPEG's
  188. // only contain a single frame.
  189. jpegFrames.add(new JPEGFrame());
  190. frame = (JPEGFrame) jpegFrames.get(jpegFrames.size() - 1);
  191. // Skip the frame length.
  192. jpegStream.readShort();
  193. // Bits percision, either 8 or 12.
  194. frame.setPrecision(jpegStream.readByte());
  195. // Scan lines = to the height of the frame.
  196. frame.setScanLines(jpegStream.readShort());
  197. // Scan samples per line = to the width of the frame.
  198. frame.setSamplesPerLine(jpegStream.readShort());
  199. // Number of Color Components (or channels).
  200. frame.setComponentCount(jpegStream.readByte());
  201. // Set the color mode for this frame, so far only 2 color
  202. // modes are supported.
  203. if (frame.getComponentCount() == 1)
  204. frame.setColorMode(JPEGFrame.JPEG_COLOR_GRAY);
  205. else
  206. frame.setColorMode(JPEGFrame.JPEG_COLOR_YCbCr);
  207. // Add all of the necessary components to the frame.
  208. for (int i = 0; i < frame.getComponentCount(); i++)
  209. frame.addComponent(jpegStream.readByte(), jpegStream.readByte(),
  210. jpegStream.readByte());
  211. break;
  212. case JPEGMarker.SOF2:
  213. jpegFrames.add(new JPEGFrame());
  214. frame = (JPEGFrame) jpegFrames.get(jpegFrames.size() - 1);
  215. // Skip the frame length.
  216. jpegStream.readShort();
  217. // Bits percision, either 8 or 12.
  218. frame.setPrecision(jpegStream.readByte());
  219. // Scan lines = to the height of the frame.
  220. frame.setScanLines(jpegStream.readShort());
  221. // Scan samples per line = to the width of the frame.
  222. frame.setSamplesPerLine(jpegStream.readShort());
  223. // Number of Color Components (or channels).
  224. frame.setComponentCount(jpegStream.readByte());
  225. // Set the color mode for this frame, so far only 2 color
  226. // modes are supported.
  227. if (frame.getComponentCount() == 1)
  228. frame.setColorMode(JPEGFrame.JPEG_COLOR_GRAY);
  229. else
  230. frame.setColorMode(JPEGFrame.JPEG_COLOR_YCbCr);
  231. // Add all of the necessary components to the frame.
  232. for (int i = 0; i < frame.getComponentCount(); i++)
  233. frame.addComponent(jpegStream.readByte(), jpegStream.readByte(),
  234. jpegStream.readByte());
  235. break;
  236. case JPEGMarker.DHT:
  237. // DHT non-SOF Marker - Huffman Table is required for decoding
  238. // the JPEG stream, when we receive a marker we load in first
  239. // the table length (16 bits), the table class (4 bits), table
  240. // identifier (4 bits), then we load in 16 bytes and each byte
  241. // represents the count of bytes to load in for each of the 16
  242. // bytes. We load this into an array to use later and move on 4
  243. // huffman tables can only be used in an image.
  244. int huffmanLength = (jpegStream.readShort() - 2);
  245. // Keep looping until we are out of length.
  246. int index = huffmanLength;
  247. // Multiple tables may be defined within a DHT marker. This
  248. // will keep reading until there are no tables left, most
  249. // of the time there are just one tables.
  250. while (index > 0)
  251. {
  252. // Read the identifier information and class
  253. // information about the Huffman table, then read the
  254. // 16 byte codelength in and read in the Huffman values
  255. // and put it into table info.
  256. byte huffmanInfo = jpegStream.readByte();
  257. byte tableClass = (byte) (huffmanInfo >> 4);
  258. byte huffmanIndex = (byte) (huffmanInfo & 0x0f);
  259. short[] codeLength = new short[16];
  260. jpegStream.readFully(codeLength, 0, codeLength.length);
  261. int huffmanValueLen = 0;
  262. for (int i = 0; i < 16; i++)
  263. huffmanValueLen += codeLength[i];
  264. index -= (huffmanValueLen + 17);
  265. short[] huffmanVal = new short[huffmanValueLen];
  266. for (int i = 0; i < huffmanVal.length; i++)
  267. huffmanVal[i] = jpegStream.readByte();
  268. // Assign DC Huffman Table.
  269. if (tableClass == HuffmanTable.JPEG_DC_TABLE)
  270. dcTables[(int) huffmanIndex] = new JPEGHuffmanTable(codeLength,
  271. huffmanVal);
  272. // Assign AC Huffman Table.
  273. else if (tableClass == HuffmanTable.JPEG_AC_TABLE)
  274. acTables[(int) huffmanIndex] = new JPEGHuffmanTable(codeLength,
  275. huffmanVal);
  276. }
  277. break;
  278. case JPEGMarker.DQT:
  279. // DQT non-SOF Marker - This defines the quantization
  280. // coeffecients, this allows us to figure out the quality of
  281. // compression and unencode the data. The data is loaded and
  282. // then stored in to an array.
  283. short quantizationLength = (short) (jpegStream.readShort() - 2);
  284. for (int j = 0; j < quantizationLength / 65; j++)
  285. {
  286. byte quantSpecs = jpegStream.readByte();
  287. int[] quantData = new int[64];
  288. if ((byte) (quantSpecs >> 4) == 0)
  289. // Precision 8 bit.
  290. {
  291. for (int i = 0; i < 64; i++)
  292. quantData[i] = jpegStream.readByte();
  293. }
  294. else if ((byte) (quantSpecs >> 4) == 1)
  295. // Precision 16 bit.
  296. {
  297. for (int i = 0; i < 64; i++)
  298. quantData[i] = jpegStream.readShort();
  299. }
  300. qTables[(int) (quantSpecs & 0x0f)] = new JPEGQTable (quantData);
  301. }
  302. break;
  303. case JPEGMarker.SOS:
  304. // SOS non-SOF Marker - Start Of Scan Marker, this is where the
  305. // actual data is stored in a interlaced or non-interlaced with
  306. // from 1-4 components of color data, if three components most
  307. // likely a YCrCb model, this is a fairly complex process.
  308. // Read in the scan length.
  309. jpegStream.readShort();
  310. // Number of components in the scan.
  311. byte numberOfComponents = jpegStream.readByte();
  312. byte[] componentSelector = new byte[numberOfComponents];
  313. for (int i = 0; i < numberOfComponents; i++)
  314. {
  315. // Component ID, packed byte containing the Id for the
  316. // AC table and DC table.
  317. byte componentID = jpegStream.readByte();
  318. byte tableInfo = jpegStream.readByte();
  319. frame.setHuffmanTables(componentID,
  320. acTables[(byte) (tableInfo >> 4)],
  321. dcTables[(byte) (tableInfo & 0x0f)]);
  322. componentSelector[i] = componentID;
  323. }
  324. byte startSpectralSelection = jpegStream.readByte();
  325. byte endSpectralSelection = jpegStream.readByte();
  326. byte successiveApproximation = jpegStream.readByte();
  327. int mcuIndex = 0;
  328. int mcuTotalIndex = 0;
  329. // This loops through until a MarkerTagFound exception is
  330. // found, if the marker tag is a RST (Restart Marker) it
  331. // simply skips it and moves on this system does not handle
  332. // corrupt data streams very well, it could be improved by
  333. // handling misplaced restart markers.
  334. while (true)
  335. {
  336. try
  337. {
  338. // Loop though capturing MCU, instruct each
  339. // component to read in its necessary count, for
  340. // scaling factors the components automatically
  341. // read in how much they need
  342. for (int compIndex = 0; compIndex < numberOfComponents; compIndex++)
  343. {
  344. JPEGComponent comp = frame.components.getComponentByID(componentSelector[compIndex]);
  345. comp.readComponentMCU(jpegStream);
  346. }
  347. mcuIndex++;
  348. mcuTotalIndex++;
  349. }
  350. // We've found a marker, see if the marker is a restart
  351. // marker or just the next marker in the stream. If
  352. // it's the next marker in the stream break out of the
  353. // while loop, if it's just a restart marker skip it
  354. catch (JPEGMarkerFoundException bse)
  355. {
  356. // Handle JPEG Restart Markers, this is where the
  357. // count of MCU's per interval is compared with
  358. // the count actually obtained, if it's short then
  359. // pad on some MCU's ONLY for components that are
  360. // greater than one. Also restart the DC prediction
  361. // to zero.
  362. if (marker == JPEGMarker.RST0
  363. || marker == JPEGMarker.RST1
  364. || marker == JPEGMarker.RST2
  365. || marker == JPEGMarker.RST3
  366. || marker == JPEGMarker.RST4
  367. || marker == JPEGMarker.RST5
  368. || marker == JPEGMarker.RST6
  369. || marker == JPEGMarker.RST7)
  370. {
  371. for (int compIndex = 0; compIndex < numberOfComponents; compIndex++)
  372. {
  373. JPEGComponent comp = frame.components.getComponentByID(componentSelector[compIndex]);
  374. if (compIndex > 1)
  375. comp.padMCU(mcuTotalIndex, resetInterval - mcuIndex);
  376. comp.resetInterval();
  377. }
  378. mcuTotalIndex += (resetInterval - mcuIndex);
  379. mcuIndex = 0;
  380. }
  381. else
  382. {
  383. // We're at the end of our scan, exit out.
  384. break;
  385. }
  386. }
  387. }
  388. break;
  389. case JPEGMarker.DRI:
  390. // DRI - This defines the restart interval, if we have a
  391. // restart interval when we reach our restart modulo calculate
  392. // whether the count of MCU's specified in the restart
  393. // interval have been reached, if they havent then pad with
  394. // whatever MCU was last used, this is supposed to be a form of
  395. // error recovery but it turns out that some JPEG encoders
  396. // purposely cause missing MCU's on repeating MCU's to compress
  397. // data even more (even though it adds an extra layer of
  398. // complexity.. But since when is JPEG easy?
  399. jpegStream.skipBytes(2);
  400. resetInterval = jpegStream.readShort();
  401. break;
  402. case JPEGMarker.COM:
  403. // COM - This is a comment that was inserted into the JPEG, we
  404. // simply skip over the comment because it's really of no
  405. // importance, usually contains a verbal description of the
  406. // application or author who created the JPEG.
  407. jpegStream.skipBytes(jpegStream.readShort() - 2);
  408. break;
  409. case JPEGMarker.DNL:
  410. // DNL - This sets the height of the image. This is the Define
  411. // Number Lines for the image, I'm not sure exactly why we need
  412. // this but, whatever we'll abide.
  413. frame.setScanLines(jpegStream.readShort());
  414. break;
  415. case JPEGMarker.EOI:
  416. // EOI - End of Image, this processes the frames and turns the
  417. // frames into a buffered image.
  418. if (jpegFrames.size() == 0)
  419. {
  420. return;
  421. }
  422. else if (jpegFrames.size() == 1)
  423. {
  424. // Only one frame, JPEG Non-Heirarchial Frame.
  425. DCT myDCT = new DCT();
  426. WritableRaster raster =
  427. Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE,
  428. frame.width,
  429. frame.height,
  430. frame.getComponentCount(),
  431. new Point(0, 0));
  432. // Unencode the data.
  433. for (int i = 0; i < frame.getComponentCount(); i++)
  434. {
  435. JPEGComponent comp = frame.components.get(i);
  436. comp.setQuantizationTable(qTables[comp.quant_id].getTable());
  437. comp.quantitizeData();
  438. comp.idctData(myDCT);
  439. }
  440. // Scale the image and write the data to the raster.
  441. for (int i = 0; i < frame.getComponentCount(); i++)
  442. {
  443. JPEGComponent comp = frame.components.get(i);
  444. comp.scaleByFactors();
  445. comp.writeData(raster, i);
  446. // Ensure garbage collection.
  447. comp = null;
  448. }
  449. // Grayscale Color Image (1 Component).
  450. if (frame.getComponentCount() == 1)
  451. {
  452. ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_GRAY);
  453. ComponentColorModel ccm =
  454. new ComponentColorModel(cs, false, false,
  455. Transparency.OPAQUE,
  456. DataBuffer.TYPE_BYTE);
  457. image = new BufferedImage(ccm, raster, false,
  458. new Hashtable());
  459. }
  460. // YCbCr Color Image (3 Components).
  461. else if (frame.getComponentCount() == 3)
  462. {
  463. ComponentColorModel ccm =
  464. new ComponentColorModel(new YCbCr_ColorSpace(), false,
  465. false, Transparency.OPAQUE,
  466. DataBuffer.TYPE_BYTE);
  467. image = new BufferedImage(ccm, raster, false,
  468. new Hashtable());
  469. }
  470. // Possibly CMYK or RGBA ?
  471. else
  472. {
  473. throw new JPEGException("Unsupported Color Mode: 4 "
  474. + "Component Color Mode found.");
  475. }
  476. height = frame.height;
  477. width = frame.width;
  478. }
  479. else
  480. {
  481. //JPEG Heirarchial Frame (progressive or baseline).
  482. throw new JPEGException("Unsupported Codec Type:"
  483. + " Hierarchial JPEG");
  484. }
  485. break;
  486. case JPEGMarker.SOF1:
  487. // ERROR - If we encounter any of the following marker codes
  488. // error out with a codec exception, progressive, heirarchial,
  489. // differential, arithmetic, lossless JPEG's are not supported.
  490. // This is where enhancements can be made for future versions.
  491. // Thankfully 99% of all JPEG's are baseline DCT.
  492. throw new JPEGException("Unsupported Codec Type: Extended "
  493. + "Sequential DCT JPEG's Not-Supported");
  494. //case JPEGMarker.SOF2:
  495. // throw new JPEGException("Unsupported Codec Type: Progressive DCT JPEG's Not-Supported");
  496. case JPEGMarker.SOF3:
  497. throw new JPEGException("Unsupported Codec Type:"
  498. + " Lossless (sequential)");
  499. case JPEGMarker.SOF5:
  500. throw new JPEGException("Unsupported Codec Type:"
  501. + " Differential sequential DCT");
  502. case JPEGMarker.SOF6:
  503. throw new JPEGException("Unsupported Codec Type:"
  504. + " Differential progressive DCT");
  505. case JPEGMarker.SOF7:
  506. throw new JPEGException("Unsupported Codec Type:"
  507. + " Differential lossless");
  508. case JPEGMarker.SOF9:
  509. case JPEGMarker.SOF10:
  510. case JPEGMarker.SOF11:
  511. case JPEGMarker.SOF13:
  512. case JPEGMarker.SOF14:
  513. case JPEGMarker.SOF15:
  514. throw new JPEGException("Unsupported Codec Type:"
  515. + " Arithmetic Coding Frame");
  516. default:
  517. // Unknown marker found, ignore it.
  518. }
  519. marker = jpegStream.findNextMarker();
  520. }
  521. }
  522. // If the current marker is APP0, tries to decode a JFIF extension
  523. // and advances the current marker to the next marker in the stream.
  524. private void decodeJFIFExtension() throws IOException
  525. {
  526. if (marker == JPEGMarker.APP0)
  527. {
  528. int length = jpegStream.readShort();
  529. if (length >= JFXX_FIXED_LENGTH)
  530. {
  531. byte[] identifier = new byte[5];
  532. jpegStream.read(identifier);
  533. if (identifier[0] != JPEGMarker.JFIF_J
  534. || identifier[1] != JPEGMarker.JFIF_F
  535. || identifier[2] != JPEGMarker.JFIF_X
  536. || identifier[3] != JPEGMarker.JFIF_X
  537. || identifier[4] != JPEGMarker.X00)
  538. // Not a JFXX field. Ignore it and continue.
  539. jpegStream.skipBytes(length - 7);
  540. else
  541. {
  542. byte extension_code = jpegStream.readByte();
  543. switch (extension_code)
  544. {
  545. case JPEGMarker.JFXX_JPEG:
  546. // FIXME: add support for JFIF Extension:
  547. // Thumbnail coded using JPEG.
  548. jpegStream.skipBytes(length - 8);
  549. case JPEGMarker.JFXX_ONE_BPP:
  550. // FIXME: add support for JFIF Extension:
  551. // Thumbnail stored using 1 byte/pixel.
  552. jpegStream.skipBytes(length - 8);
  553. case JPEGMarker.JFXX_THREE_BPP:
  554. // FIXME: add support for JFIF Extension:
  555. // Thumbnail stored using 3 bytes/pixel.
  556. jpegStream.skipBytes(length - 8);
  557. }
  558. }
  559. }
  560. else
  561. {
  562. // Unknown APP0 marker. Ignore it and continue.
  563. jpegStream.skipBytes(length - 2);
  564. }
  565. marker = jpegStream.findNextMarker();
  566. }
  567. }
  568. public BufferedImage getImage()
  569. {
  570. return image;
  571. }
  572. }