PageRenderTime 46ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/javafx-iio/src/com/sun/javafx/iio/gif/GIFImageLoader2.java

https://bitbucket.org/shemnon/openjfx-8-master-rt
Java | 473 lines | 352 code | 63 blank | 58 comment | 91 complexity | 1a0986452da1149915dc52ccfdc5ee80 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.0, BSD-3-Clause, LGPL-2.1
  1. /*
  2. * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
  3. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4. *
  5. * This code is free software; you can redistribute it and/or modify it
  6. * under the terms of the GNU General Public License version 2 only, as
  7. * published by the Free Software Foundation. Oracle designates this
  8. * particular file as subject to the "Classpath" exception as provided
  9. * by Oracle in the LICENSE file that accompanied this code.
  10. *
  11. * This code is distributed in the hope that it will be useful, but WITHOUT
  12. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13. * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
  14. * version 2 for more details (a copy is included in the LICENSE file that
  15. * accompanied this code).
  16. *
  17. * You should have received a copy of the GNU General Public License version
  18. * 2 along with this work; if not, write to the Free Software Foundation,
  19. * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20. *
  21. * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22. * or visit www.oracle.com if you need additional information or have any
  23. * questions.
  24. */
  25. package com.sun.javafx.iio.gif;
  26. import com.sun.javafx.iio.ImageFrame;
  27. import com.sun.javafx.iio.ImageMetadata;
  28. import com.sun.javafx.iio.ImageStorage;
  29. import com.sun.javafx.iio.common.ImageLoaderImpl;
  30. import com.sun.javafx.iio.common.ImageTools;
  31. import com.sun.javafx.iio.common.PushbroomScaler;
  32. import com.sun.javafx.iio.common.ScalerFactory;
  33. import java.io.EOFException;
  34. import java.io.IOException;
  35. import java.io.InputStream;
  36. import java.nio.ByteBuffer;
  37. import java.util.Arrays;
  38. /*
  39. * loader implementation for GIF89 file format
  40. */
  41. public class GIFImageLoader2 extends ImageLoaderImpl {
  42. static final byte FILE_SIG87[] = {'G', 'I', 'F', '8', '7', 'a'};
  43. static final byte FILE_SIG89[] = {'G', 'I', 'F', '8', '9', 'a'};
  44. static final int DEFAULT_FPS = 25;
  45. InputStream stream = null;
  46. int screenW, screenH, bgColor;
  47. byte globalPalette[][]; // r,g,b,a
  48. byte image[];
  49. public GIFImageLoader2(InputStream input) throws IOException {
  50. super(GIFDescriptor.getInstance());
  51. this.stream = input;
  52. readGlobalHeader();
  53. }
  54. // read GIF file header
  55. private void readGlobalHeader() throws IOException {
  56. byte signature[] = readBytes(new byte[6]);
  57. if (!Arrays.equals(FILE_SIG87, signature) && !Arrays.equals(FILE_SIG89, signature)) {
  58. throw new IOException("Bad GIF signature!");
  59. }
  60. screenW = readShort();
  61. screenH = readShort();
  62. int cInfo = readByte();
  63. bgColor = readByte();
  64. int aspectR = readByte();
  65. if ((cInfo & 0x80) != 0) {
  66. globalPalette = readPalete(2 << (cInfo & 7), -1);
  67. }
  68. image = new byte[screenW * screenH * 4];
  69. }
  70. // read palette data from the stream
  71. private byte[][] readPalete(int size, int trnsIndex) throws IOException {
  72. byte palette[][] = new byte[4][size];
  73. byte paletteData[] = readBytes(new byte[size*3]);
  74. for (int i = 0, idx = 0; i != size; ++i) {
  75. for (int k = 0; k != 3; ++k) {
  76. palette[k][i] = paletteData[idx++];
  77. }
  78. palette[3][i] = (i == trnsIndex) ? 0 : (byte)0xFF;
  79. }
  80. return palette;
  81. }
  82. // skip an extension
  83. private void consumeAnExtension() throws IOException {
  84. for (int blSize = readByte(); blSize != 0; blSize = readByte()) {
  85. skipBytes(blSize);
  86. }
  87. }
  88. // reads Image Control extension information
  89. // returns ((pField & 0x1F) << 24) + (trnsIndex << 16) + frameDelay;
  90. private int readControlCode() throws IOException {
  91. int size = readByte();
  92. int pField = readByte();
  93. int frameDelay = readShort();
  94. int trnsIndex = readByte();
  95. if (size != 4 || readByte() != 0) {
  96. throw new IOException("Bad GIF GraphicControlExtension");
  97. }
  98. return ((pField & 0x1F) << 24) + (trnsIndex << 16) + frameDelay;
  99. }
  100. // The method waits until image data in the stream
  101. // The method also reads and return Image Control extension information
  102. // returns -1 if EOF reached or the value of readControlCode
  103. private int waitForImageFrame() throws IOException {
  104. int controlData = 0;
  105. while (true) {
  106. int ch = stream.read();
  107. switch (ch) {
  108. case 0x2C:
  109. return controlData;
  110. case 0x21:
  111. switch (readByte()) {
  112. case 0xF9:
  113. controlData = readControlCode();
  114. break;
  115. default:
  116. consumeAnExtension();
  117. }
  118. break;
  119. case -1: case 0x3B: // EOF or end of GIF
  120. return -1;
  121. default:
  122. throw new IOException("Unexpected GIF control characher 0x"
  123. + String.format("%02X", ch));
  124. }
  125. }
  126. }
  127. // Decode the one frame of GIF form the input stread using internal LZWDecoder class
  128. private void decodeImage(byte image[], int w, int h, int interlace[]) throws IOException {
  129. LZWDecoder dec = new LZWDecoder();
  130. byte data[] = dec.getString();
  131. int y = 0, iPos = 0, xr = w;
  132. while (true) {
  133. int len = dec.readString();
  134. if (len == -1) { // end of stream
  135. dec.waitForTerminator();
  136. return;
  137. }
  138. for (int pos = 0; pos != len;) {
  139. int ax = xr < (len - pos) ? xr : (len - pos);
  140. System.arraycopy(data, pos, image, iPos, ax);
  141. iPos += ax;
  142. pos += ax;
  143. if ((xr -= ax) == 0) {
  144. if (++y == h) { // image is full
  145. dec.waitForTerminator();
  146. return;
  147. }
  148. int iY = interlace == null ? y : interlace[y];
  149. iPos = iY * w;
  150. xr = w;
  151. }
  152. }
  153. }
  154. }
  155. // computes row re-index for interlaced case
  156. private int[] computeInterlaceReIndex(int h) {
  157. int data[] = new int[h], pos = 0;
  158. for (int i = 0; i < h; i += 8) data[pos++] = i;
  159. for (int i = 4; i < h; i += 8) data[pos++] = i;
  160. for (int i = 2; i < h; i += 4) data[pos++] = i;
  161. for (int i = 1; i < h; i += 2) data[pos++] = i;
  162. return data;
  163. }
  164. // loads next image frame or null if no more
  165. public ImageFrame load(int imageIndex, int width, int height, boolean preserveAspectRatio, boolean smooth) throws IOException {
  166. int imageControlCode = waitForImageFrame();
  167. if (imageControlCode < 0) {
  168. return null;
  169. }
  170. int left = readShort(), top = readShort(), w = readShort(), h = readShort();
  171. // check if the image is in the virtual screen boundaries
  172. if (left + w > screenW || top + h > screenH) {
  173. throw new IOException("Wrong GIF image frame size");
  174. }
  175. int imgCtrl = readByte();
  176. boolean isTRNS = ((imageControlCode >>> 24) & 1) == 1;
  177. int trnsIndex = isTRNS ? (imageControlCode >>> 16) & 0xFF : -1;
  178. boolean localPalette = (imgCtrl & 0x80) != 0;
  179. boolean isInterlaced = (imgCtrl & 0x40) != 0;
  180. byte palette[][] = localPalette ? readPalete(2 << (imgCtrl & 7), trnsIndex) : globalPalette;
  181. ImageMetadata metadata = updateMetadata(screenW, screenH, imageControlCode & 0xFFFF);
  182. int disposalCode = (imageControlCode >>> 26) & 7;
  183. byte pImage[] = new byte[w * h];
  184. decodeImage(pImage, w, h, isInterlaced ? computeInterlaceReIndex(h) : null);
  185. ImageFrame imgGIF = decodePalette(pImage, palette, trnsIndex,
  186. left, top, w, h, disposalCode, metadata);
  187. // need to remove scaler from image decoder itself
  188. int[] outWH = ImageTools.computeDimensions(screenW, screenH, width, height, preserveAspectRatio);
  189. if (screenW != outWH[0] || screenH != outWH[1]) {
  190. imgGIF = scaleImage(imgGIF, outWH[0], outWH[1], smooth);
  191. }
  192. return imgGIF;
  193. }
  194. // IO helpers
  195. private int readByte() throws IOException {
  196. int ch = stream.read();
  197. if (ch < 0) {
  198. throw new EOFException();
  199. }
  200. return ch;
  201. }
  202. private int readShort() throws IOException {
  203. int lsb = readByte(), msb = readByte();
  204. return lsb + (msb << 8);
  205. }
  206. private byte[] readBytes(byte data[]) throws IOException {
  207. return readBytes(data, 0, data.length);
  208. }
  209. private byte[] readBytes(byte data[], int offs, int size) throws IOException {
  210. while (size > 0) {
  211. int sz = stream.read(data, offs, size);
  212. if (sz < 0) {
  213. throw new EOFException();
  214. }
  215. offs += sz;
  216. size -= sz;
  217. }
  218. return data;
  219. }
  220. private void skipBytes(int n) throws IOException {
  221. while (n > 0) {
  222. int lskip = (int) stream.skip(n);
  223. if (lskip <= 0) {
  224. throw new EOFException();
  225. }
  226. n -= lskip;
  227. }
  228. }
  229. public void dispose() {}
  230. // fill whole image with bk color
  231. private void fillBackground(int trnsIndex, byte img[]) {
  232. byte r = globalPalette[0][bgColor], g = globalPalette[1][bgColor];
  233. byte b = globalPalette[2][bgColor], a = (bgColor == trnsIndex) ? (byte)0 : (byte)0xFF;
  234. for (int i = 0, pos = 0, l = screenW * screenH; i != l; pos += 4, ++i) {
  235. img[pos + 0] = r;
  236. img[pos + 1] = g;
  237. img[pos + 2] = b;
  238. img[pos + 3] = a;
  239. }
  240. }
  241. // decode palletized image into RGBA
  242. private ImageFrame decodePalette(byte[] srcImage, byte[][] palette, int trnsIndex,
  243. int left, int top, int w, int h, int disposalCode, ImageMetadata metadata) {
  244. byte img[] = (disposalCode == 3) ? image.clone() : image;
  245. for (int y = 0; y != h; ++y) {
  246. int iPos = ((top + y) * screenW + left) * 4;
  247. int i = y * w;
  248. if (trnsIndex < 0) {
  249. for (int x = 0; x != w; iPos += 4, ++x) {
  250. int index = 0xFF & srcImage[i + x];
  251. img[iPos + 0] = palette[0][index];
  252. img[iPos + 1] = palette[1][index];
  253. img[iPos + 2] = palette[2][index];
  254. img[iPos + 3] = palette[3][index];
  255. }
  256. } else {
  257. for (int x = 0; x != w; iPos += 4, ++x) {
  258. int index = 0xFF & srcImage[i + x];
  259. if (index != trnsIndex) {
  260. img[iPos + 0] = palette[0][index];
  261. img[iPos + 1] = palette[1][index];
  262. img[iPos + 2] = palette[2][index];
  263. img[iPos + 3] = palette[3][index];
  264. }
  265. }
  266. }
  267. }
  268. if (disposalCode != 3) img = img.clone();
  269. if (disposalCode == 2) fillBackground(trnsIndex, image);
  270. return new ImageFrame(ImageStorage.ImageType.RGBA, ByteBuffer.wrap(img),
  271. screenW, screenH, screenW * 4, null, metadata);
  272. }
  273. // copy from PNG, needs exctract refactoring later
  274. // scales the image
  275. private ImageFrame scaleImage(ImageFrame imgPNG, int rWidth, int rHeight, boolean smooth) {
  276. byte image[] = ((ByteBuffer) imgPNG.getImageData()).array();
  277. int bpp = ImageStorage.getNumBands(imgPNG.getImageType());
  278. PushbroomScaler scaler = ScalerFactory.createScaler(screenW, screenH, bpp,
  279. rWidth, rHeight, smooth);
  280. for (int y = 0; y != screenH; ++y) {
  281. scaler.putSourceScanline(image, y * screenW * bpp);
  282. }
  283. return new ImageFrame(imgPNG.getImageType(), scaler.getDestination(),
  284. rWidth, rHeight, rWidth * bpp, null, imgPNG.getMetadata());
  285. }
  286. // fill metadata
  287. private ImageMetadata updateMetadata(int w, int h, int delayTime) {
  288. ImageMetadata metaData = new ImageMetadata(null, true, null, null, null,
  289. delayTime != 0 ? delayTime*10 : 1000/DEFAULT_FPS, w, h, null, null, null);
  290. updateImageMetadata(metaData);
  291. return metaData;
  292. }
  293. class LZWDecoder {
  294. private final int initCodeSize, clearCode, eofCode;
  295. private int codeSize, codeMask, tableIndex, oldCode;
  296. // input data buffer
  297. private int blockLength = 0, blockPos = 0;
  298. private byte block[] = new byte[255];
  299. private int inData = 0, inBits = 0;
  300. // table
  301. private int[] prefix = new int[4096];
  302. private byte[] suffix = new byte[4096];
  303. private byte[] initial = new byte[4096];
  304. private int[] length = new int[4096];
  305. private byte[] string = new byte[4096];
  306. public LZWDecoder() throws IOException {
  307. initCodeSize = readByte();
  308. clearCode = 1 << initCodeSize;
  309. eofCode = clearCode + 1;
  310. initTable();
  311. }
  312. // decode next string of data, which can be accessed by getString() method
  313. public final int readString() throws IOException {
  314. int code = getCode();
  315. if (code == eofCode) {
  316. return -1;
  317. } else if (code == clearCode) {
  318. initTable();
  319. code = getCode();
  320. if (code == eofCode) {
  321. return -1;
  322. }
  323. } else {
  324. int newSuffixIndex;
  325. int ti = tableIndex;
  326. if (code < ti) {
  327. newSuffixIndex = code;
  328. } else { // code == tableIndex
  329. newSuffixIndex = oldCode;
  330. if (code != ti) {
  331. throw new IOException("Bad GIF LZW: Out-of-sequence code!");
  332. }
  333. }
  334. int oc = oldCode;
  335. prefix[ti] = oc;
  336. suffix[ti] = initial[newSuffixIndex];
  337. initial[ti] = initial[oc];
  338. length[ti] = length[oc] + 1;
  339. ++tableIndex;
  340. if ((tableIndex == (1 << codeSize)) && (tableIndex < 4096)) {
  341. ++codeSize;
  342. codeMask = (1 << codeSize) - 1;
  343. }
  344. }
  345. // Reverse code
  346. int c = code;
  347. int len = length[c];
  348. for (int i = len - 1; i >= 0; i--) {
  349. string[i] = suffix[c];
  350. c = prefix[c];
  351. }
  352. oldCode = code;
  353. return len;
  354. }
  355. // data accessor, the data length returned by readString method
  356. public final byte[] getString() { return string; }
  357. // waits until data ends
  358. public final void waitForTerminator() throws IOException {
  359. consumeAnExtension();
  360. }
  361. // initialize LZW dctionary
  362. private void initTable() {
  363. int numEntries = 1 << initCodeSize;
  364. for (int i = 0; i < numEntries; i++) {
  365. prefix[i] = -1;
  366. suffix[i] = (byte) i;
  367. initial[i] = (byte) i;
  368. length[i] = 1;
  369. }
  370. // fill in the entire table for robustness against
  371. // out-of-sequence codes.
  372. for (int i = numEntries; i < 4096; i++) {
  373. prefix[i] = -1;
  374. length[i] = 1;
  375. }
  376. codeSize = initCodeSize + 1;
  377. codeMask = (1 << codeSize) - 1;
  378. tableIndex = numEntries + 2;
  379. oldCode = 0;
  380. }
  381. // reads codeSize bits from the stream
  382. private int getCode() throws IOException {
  383. while (inBits < codeSize) {
  384. inData |= nextByte() << inBits;
  385. inBits += 8;
  386. }
  387. int code = inData & codeMask;
  388. inBits -= codeSize;
  389. inData >>>= codeSize;
  390. return code;
  391. }
  392. // reads next in byte
  393. private int nextByte() throws IOException {
  394. if (blockPos == blockLength) {
  395. readData();
  396. }
  397. return (int)block[blockPos++] & 0xFF;
  398. }
  399. // reads next block if data
  400. private void readData() throws IOException {
  401. blockPos = 0;
  402. blockLength = readByte();
  403. if (blockLength > 0) {
  404. readBytes(block, 0, blockLength);
  405. } else {
  406. throw new EOFException();
  407. }
  408. }
  409. }
  410. }