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
26 package com.sun.javafx.iio.gif;
27
28 import com.sun.javafx.iio.ImageFrame;
29 import com.sun.javafx.iio.ImageMetadata;
30 import com.sun.javafx.iio.ImageStorage;
31 import com.sun.javafx.iio.common.ImageLoaderImpl;
32 import com.sun.javafx.iio.common.ImageTools;
33 import com.sun.javafx.iio.common.PushbroomScaler;
34 import com.sun.javafx.iio.common.ScalerFactory;
35 import java.io.EOFException;
36 import java.io.IOException;
37 import java.io.InputStream;
38 import java.nio.ByteBuffer;
39 import java.util.Arrays;
40
41 /*
42 * loader implementation for GIF89 file format
43 */
44
45 public class GIFImageLoader2 extends ImageLoaderImpl {
46
47 static final byte FILE_SIG87[] = {'G', 'I', 'F', '8', '7', 'a'};
48 static final byte FILE_SIG89[] = {'G', 'I', 'F', '8', '9', 'a'};
49 static final byte NETSCAPE_SIG[] = {'N', 'E', 'T', 'S', 'C', 'A', 'P', 'E', '2', '.', '0'};
50 static final int DEFAULT_FPS = 25;
51
52 InputStream stream = null;
53 int screenW, screenH, bgColor;
54 byte globalPalette[][]; // r,g,b,a
206 if (imageControlCode < 0) {
207 return null;
208 }
209
210 int left = readShort(), top = readShort(), w = readShort(), h = readShort();
211
212 // check if the image is in the virtual screen boundaries
213 if (left + w > screenW || top + h > screenH) {
214 throw new IOException("Wrong GIF image frame size");
215 }
216
217 int imgCtrl = readByte();
218
219 boolean isTRNS = ((imageControlCode >>> 24) & 1) == 1;
220 int trnsIndex = isTRNS ? (imageControlCode >>> 16) & 0xFF : -1;
221 boolean localPalette = (imgCtrl & 0x80) != 0;
222 boolean isInterlaced = (imgCtrl & 0x40) != 0;
223
224 byte palette[][] = localPalette ? readPalete(2 << (imgCtrl & 7), trnsIndex) : globalPalette;
225
226 ImageMetadata metadata = updateMetadata(screenW, screenH, imageControlCode & 0xFFFF);
227
228 int disposalCode = (imageControlCode >>> 26) & 7;
229 byte pImage[] = new byte[w * h];
230 decodeImage(pImage, w, h, isInterlaced ? computeInterlaceReIndex(h) : null);
231
232 ImageFrame imgGIF = decodePalette(pImage, palette, trnsIndex,
233 left, top, w, h, disposalCode, metadata);
234
235 // need to remove scaler from image decoder itself
236 int[] outWH = ImageTools.computeDimensions(screenW, screenH, width, height, preserveAspectRatio);
237
238 if (screenW != outWH[0] || screenH != outWH[1]) {
239 imgGIF = scaleImage(imgGIF, outWH[0], outWH[1], smooth);
240 }
241
242 return imgGIF;
243 }
244
245 // IO helpers
246 private int readByte() throws IOException {
247 int ch = stream.read();
248 if (ch < 0) {
249 throw new EOFException();
250 }
251 return ch;
252 }
253
254 private int readShort() throws IOException {
255 int lsb = readByte(), msb = readByte();
256 return lsb + (msb << 8);
257 }
258
259 private byte[] readBytes(byte data[]) throws IOException {
260 return readBytes(data, 0, data.length);
261 }
262
273 }
274
275 private void skipBytes(int n) throws IOException {
276 ImageTools.skipFully(stream, n);
277 }
278
279 public void dispose() {}
280
281 // GIF specification states that restore to background should fill the frame
282 // with background color, but actually all modern programs fill with transparent color.
283 private void restoreToBackground(byte img[], int left, int top, int w, int h) {
284 for (int y = 0; y != h; ++y) {
285 int iPos = ((top + y) * screenW + left) * 4;
286 for (int x = 0; x != w; iPos += 4, ++x) {
287 img[iPos + 3] = 0;
288 }
289 }
290 }
291
292 // decode palletized image into RGBA
293 private ImageFrame decodePalette(byte[] srcImage, byte[][] palette, int trnsIndex,
294 int left, int top, int w, int h, int disposalCode, ImageMetadata metadata) {
295
296 byte img[] = (disposalCode == 3) ? image.clone() : image;
297
298 for (int y = 0; y != h; ++y) {
299 int iPos = ((top + y) * screenW + left) * 4;
300 int i = y * w;
301 if (trnsIndex < 0) {
302 for (int x = 0; x != w; iPos += 4, ++x) {
303 int index = 0xFF & srcImage[i + x];
304 img[iPos + 0] = palette[0][index];
305 img[iPos + 1] = palette[1][index];
306 img[iPos + 2] = palette[2][index];
307 img[iPos + 3] = palette[3][index];
308 }
309 } else {
310 for (int x = 0; x != w; iPos += 4, ++x) {
311 int index = 0xFF & srcImage[i + x];
312 if (index != trnsIndex) {
313 img[iPos + 0] = palette[0][index];
314 img[iPos + 1] = palette[1][index];
315 img[iPos + 2] = palette[2][index];
316 img[iPos + 3] = palette[3][index];
317 }
318 }
319 }
320 }
321
322 if (disposalCode != 3) img = img.clone();
323 if (disposalCode == 2) restoreToBackground(image, left, top, w, h);
324
325 return new ImageFrame(ImageStorage.ImageType.RGBA, ByteBuffer.wrap(img),
326 screenW, screenH, screenW * 4, null, metadata);
327 }
328
329 // copy from PNG, needs exctract refactoring later
330 // scales the image
331 private ImageFrame scaleImage(ImageFrame imgPNG, int rWidth, int rHeight, boolean smooth) {
332 byte image[] = ((ByteBuffer) imgPNG.getImageData()).array();
333 int bpp = ImageStorage.getNumBands(imgPNG.getImageType());
334
335 PushbroomScaler scaler = ScalerFactory.createScaler(screenW, screenH, bpp,
336 rWidth, rHeight, smooth);
337
338 for (int y = 0; y != screenH; ++y) {
339 scaler.putSourceScanline(image, y * screenW * bpp);
340 }
341
342 return new ImageFrame(imgPNG.getImageType(), scaler.getDestination(),
343 rWidth, rHeight, rWidth * bpp, null, imgPNG.getMetadata());
344 }
345
346 // fill metadata
347 private ImageMetadata updateMetadata(int w, int h, int delayTime) {
348 ImageMetadata metaData = new ImageMetadata(null, true, null, null, null,
349 delayTime != 0 ? delayTime*10 : 1000/DEFAULT_FPS, loopCount, w, h, null, null, null);
350 updateImageMetadata(metaData);
351 return metaData;
352 }
353
354 class LZWDecoder {
355 private final int initCodeSize, clearCode, eofCode;
356 private int codeSize, codeMask, tableIndex, oldCode;
357
358 // input data buffer
359 private int blockLength = 0, blockPos = 0;
360 private byte block[] = new byte[255];
361 private int inData = 0, inBits = 0;
362
363 // table
|
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
26 package com.sun.javafx.iio.gif;
27
28 import com.sun.javafx.iio.ImageFrame;
29 import com.sun.javafx.iio.ImageMetadata;
30 import com.sun.javafx.iio.ImageStorage;
31 import com.sun.javafx.iio.common.ImageLoaderImpl;
32 import com.sun.javafx.iio.common.ImageTools;
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 /*
40 * loader implementation for GIF89 file format
41 */
42
43 public class GIFImageLoader2 extends ImageLoaderImpl {
44
45 static final byte FILE_SIG87[] = {'G', 'I', 'F', '8', '7', 'a'};
46 static final byte FILE_SIG89[] = {'G', 'I', 'F', '8', '9', 'a'};
47 static final byte NETSCAPE_SIG[] = {'N', 'E', 'T', 'S', 'C', 'A', 'P', 'E', '2', '.', '0'};
48 static final int DEFAULT_FPS = 25;
49
50 InputStream stream = null;
51 int screenW, screenH, bgColor;
52 byte globalPalette[][]; // r,g,b,a
204 if (imageControlCode < 0) {
205 return null;
206 }
207
208 int left = readShort(), top = readShort(), w = readShort(), h = readShort();
209
210 // check if the image is in the virtual screen boundaries
211 if (left + w > screenW || top + h > screenH) {
212 throw new IOException("Wrong GIF image frame size");
213 }
214
215 int imgCtrl = readByte();
216
217 boolean isTRNS = ((imageControlCode >>> 24) & 1) == 1;
218 int trnsIndex = isTRNS ? (imageControlCode >>> 16) & 0xFF : -1;
219 boolean localPalette = (imgCtrl & 0x80) != 0;
220 boolean isInterlaced = (imgCtrl & 0x40) != 0;
221
222 byte palette[][] = localPalette ? readPalete(2 << (imgCtrl & 7), trnsIndex) : globalPalette;
223
224 int[] outWH = ImageTools.computeDimensions(screenW, screenH, width, height, preserveAspectRatio);
225 width = outWH[0];
226 height = outWH[1];
227
228 ImageMetadata metadata = updateMetadata(width, height, imageControlCode & 0xFFFF);
229
230 int disposalCode = (imageControlCode >>> 26) & 7;
231 byte pImage[] = new byte[w * h];
232 decodeImage(pImage, w, h, isInterlaced ? computeInterlaceReIndex(h) : null);
233
234 ByteBuffer img = decodePalette(pImage, palette, trnsIndex,
235 left, top, w, h, disposalCode);
236
237 if (screenW != width || screenH != height) {
238 img = ImageTools.scaleImage(img, screenW, screenH, 4,
239 width, height, smooth);
240 }
241
242 return new ImageFrame(ImageStorage.ImageType.RGBA, img,
243 width, height, width * 4, null, metadata);
244 }
245
246 // IO helpers
247 private int readByte() throws IOException {
248 int ch = stream.read();
249 if (ch < 0) {
250 throw new EOFException();
251 }
252 return ch;
253 }
254
255 private int readShort() throws IOException {
256 int lsb = readByte(), msb = readByte();
257 return lsb + (msb << 8);
258 }
259
260 private byte[] readBytes(byte data[]) throws IOException {
261 return readBytes(data, 0, data.length);
262 }
263
274 }
275
276 private void skipBytes(int n) throws IOException {
277 ImageTools.skipFully(stream, n);
278 }
279
280 public void dispose() {}
281
282 // GIF specification states that restore to background should fill the frame
283 // with background color, but actually all modern programs fill with transparent color.
284 private void restoreToBackground(byte img[], int left, int top, int w, int h) {
285 for (int y = 0; y != h; ++y) {
286 int iPos = ((top + y) * screenW + left) * 4;
287 for (int x = 0; x != w; iPos += 4, ++x) {
288 img[iPos + 3] = 0;
289 }
290 }
291 }
292
293 // decode palletized image into RGBA
294 private ByteBuffer decodePalette(byte[] srcImage, byte[][] palette, int trnsIndex,
295 int left, int top, int w, int h, int disposalCode) {
296
297 byte img[] = (disposalCode == 3) ? image.clone() : image;
298
299 for (int y = 0; y != h; ++y) {
300 int iPos = ((top + y) * screenW + left) * 4;
301 int i = y * w;
302 if (trnsIndex < 0) {
303 for (int x = 0; x != w; iPos += 4, ++x) {
304 int index = 0xFF & srcImage[i + x];
305 img[iPos + 0] = palette[0][index];
306 img[iPos + 1] = palette[1][index];
307 img[iPos + 2] = palette[2][index];
308 img[iPos + 3] = palette[3][index];
309 }
310 } else {
311 for (int x = 0; x != w; iPos += 4, ++x) {
312 int index = 0xFF & srcImage[i + x];
313 if (index != trnsIndex) {
314 img[iPos + 0] = palette[0][index];
315 img[iPos + 1] = palette[1][index];
316 img[iPos + 2] = palette[2][index];
317 img[iPos + 3] = palette[3][index];
318 }
319 }
320 }
321 }
322
323 if (disposalCode != 3) img = img.clone();
324 if (disposalCode == 2) restoreToBackground(image, left, top, w, h);
325
326 return ByteBuffer.wrap(img);
327 }
328
329 // fill metadata
330 private ImageMetadata updateMetadata(int w, int h, int delayTime) {
331 ImageMetadata metaData = new ImageMetadata(null, true, null, null, null,
332 delayTime != 0 ? delayTime*10 : 1000/DEFAULT_FPS, loopCount, w, h, null, null, null);
333 updateImageMetadata(metaData);
334 return metaData;
335 }
336
337 class LZWDecoder {
338 private final int initCodeSize, clearCode, eofCode;
339 private int codeSize, codeMask, tableIndex, oldCode;
340
341 // input data buffer
342 private int blockLength = 0, blockPos = 0;
343 private byte block[] = new byte[255];
344 private int inData = 0, inBits = 0;
345
346 // table
|