001: /*
002: * Copyright 1990-2007 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
004: *
005: * This program is free software; you can redistribute it and/or
006: * modify it under the terms of the GNU General Public License version
007: * 2 only, as published by the Free Software Foundation.
008: *
009: * This program is distributed in the hope that it will be useful, but
010: * WITHOUT ANY WARRANTY; without even the implied warranty of
011: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012: * General Public License version 2 for more details (a copy is
013: * included at /legal/license.txt).
014: *
015: * You should have received a copy of the GNU General Public License
016: * version 2 along with this work; if not, write to the Free Software
017: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
018: * 02110-1301 USA
019: *
020: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
021: * Clara, CA 95054 or visit www.sun.com if you need additional
022: * information or have any questions.
023: */
024: package com.sun.mmedia;
025:
026: /**
027: * Decodes single images after parsing headers. Helper for GIFPlayer.
028: * Performs LZW decoding, palette mapping, interlacing, tranparency follow-up
029: */
030: class GIFImageDecoder {
031:
032: final static int UNDRAW_LEAVE = 1;
033: final static int UNDRAW_RESTORE_BACKGROUND = 2;
034: final static int UNDRAW_RESTORE_PREVIOUS = 3;
035:
036: private int width, height, colorDepth, globalColorDepth,
037: curColorDepth;
038: private byte[] globalPalette;
039: private byte[] curPalette;
040: private int[] argb;
041: private int[] curArgb;
042: private int backgroundIndex;
043:
044: private int transparentColorIndex = -1;
045: private int undrawFlag = UNDRAW_LEAVE;
046:
047: private int framePosX, framePosY, frameWidth, frameHeight;
048: private boolean interlace;
049:
050: /// Initialize with Global Descriptor data
051: GIFImageDecoder(int width, int height, int colorDepth) {
052: this .width = width;
053: this .height = height;
054: this .colorDepth = colorDepth;
055:
056: globalColorDepth = colorDepth;
057: globalPalette = new byte[3 * (1 << globalColorDepth)];
058:
059: // default global color map - increasing grays
060: globalPalette[0] = 0;
061: globalPalette[1] = 0;
062: globalPalette[2] = 0;
063: int shift = 8 - globalColorDepth;
064: int add = (1 << shift) - 1;
065: for (int i = 1; i < (1 << globalColorDepth); ++i) {
066: byte gray = (byte) ((i << shift) | add);
067: globalPalette[3 * i + 0] = gray;
068: globalPalette[3 * i + 1] = gray;
069: globalPalette[3 * i + 2] = gray;
070: }
071:
072: backgroundIndex = -1;
073:
074: curColorDepth = globalColorDepth;
075: curPalette = globalPalette;
076:
077: argb = new int[width * height];
078: for (int i = 0; i < width * height; ++i)
079: argb[i] = 0x00badbad; // transparent
080: }
081:
082: /// Set Global Descriptor's palette data (optional)
083: void setGlobalPalette(int colorDepth, byte[] palette,
084: int backgroundIndex) {
085: if (curPalette == globalPalette) {
086: curPalette = palette;
087: curColorDepth = colorDepth;
088: }
089: globalPalette = palette;
090: globalColorDepth = colorDepth;
091:
092: this .backgroundIndex = backgroundIndex;
093: }
094:
095: /// Clear current image to background color
096: void clearImage() {
097: if (globalPalette != null && backgroundIndex >= 0) {
098: int bkg = getColor(backgroundIndex, globalPalette);
099: for (int i = 0; i < width * height; ++i)
100: argb[i] = bkg;
101: }
102: }
103:
104: /// Get current image (after undraw)
105: //int [] getCurrentARGB() {
106: // return argb;
107: //}
108:
109: /// Get color from palette
110: private int getColor(int index, byte[] palette) {
111: index *= 3;
112: return 0xff000000 | ((palette[index + 0] & 0xff) << 16)
113: | ((palette[index + 1] & 0xff) << 8)
114: | (palette[index + 2] & 0xff);
115: }
116:
117: /// Prepare for next frame decoding (set Local Descriptor data)
118: void newFrame(int relx, int rely, int width, int height,
119: boolean interlaceFlag) {
120: if (relx + width > this .width)
121: framePosX = this .width - width;
122: else
123: framePosX = relx;
124:
125: if (rely + height > this .height)
126: framePosY = this .height - height;
127: else
128: framePosY = rely;
129:
130: // Relying that frameWidth < this.width && frameHeight << this.height
131: frameWidth = width;
132: frameHeight = height;
133: interlace = interlaceFlag;
134: }
135:
136: /// Set Local Descriptor's palette data (optional)
137: void setLocalPalette(int colorDepth, byte[] palette) {
138: curColorDepth = colorDepth;
139: curPalette = palette;
140: }
141:
142: /**
143: * Set Local Description Extension data (optional)
144: *
145: * udrawFlag: see UNDRAW_* constants
146: * tranparentColor: -1 - no transparent color, otherwise - index from color table
147: */
148: void setGraphicsControl(int undrawFlag, int transparentColorIndex) {
149: this .undrawFlag = undrawFlag;
150: this .transparentColorIndex = transparentColorIndex;
151: }
152:
153: /// Decode frame data from dataBlocks to out
154: boolean decodeImage(int lzwCodeSize, int dataSize, byte data[],
155: int out[]) {
156: curArgb = out;
157:
158: // Get saved image
159: System.arraycopy(argb, 0, curArgb, 0, width * height);
160:
161: // LZW decode
162: boolean result = lzwImage(lzwCodeSize, dataSize, data);
163:
164: // Undraw
165: switch (undrawFlag) {
166: case UNDRAW_LEAVE: {
167: int pixel = framePosX + framePosY * width;
168: for (int i = 0; i < frameHeight; ++i, pixel += width)
169: System.arraycopy(curArgb, pixel, argb, pixel,
170: frameWidth);
171: break;
172: }
173: case UNDRAW_RESTORE_BACKGROUND:
174: if (globalPalette != null && backgroundIndex >= 0) {
175: int pixel = framePosX + framePosY * width;
176: int wdif = width - frameWidth;
177: int bkg = getColor(backgroundIndex, globalPalette);
178:
179: for (int i = 0; i < frameHeight; ++i, pixel += wdif)
180: for (int j = 0; j < frameWidth; ++j, ++pixel)
181: argb[pixel] = bkg;
182: break;
183: }
184: case UNDRAW_RESTORE_PREVIOUS:
185: // Do nothing
186: break;
187: }
188: resetLocalData();
189:
190: return result;
191: }
192:
193: // Should be called at the end of the image parsing
194: private void resetLocalData() {
195: undrawFlag = UNDRAW_LEAVE;
196: transparentColorIndex = -1;
197: curColorDepth = globalColorDepth;
198: curPalette = globalPalette;
199: }
200:
201: private void drawLine(byte[] line, int y/*, int multiply*/) {
202: if (y > frameHeight)
203: return;
204: int pixel = (framePosY + y) * width + framePosX;
205:
206: for (int i = 0; i < frameWidth; ++i, ++pixel) {
207: int idx = line[i] & 0xff;
208: if (idx != transparentColorIndex)
209: curArgb[pixel] = getColor(idx, curPalette);
210: }
211: }
212:
213: // ------------------------- LZW decoder ------------------------
214: // This is converted from the native version. The java version is
215: // much faster when we have JIT.
216:
217: private short prefix[] = new short[4096];
218: private byte suffix[] = new byte[4096];
219: private byte outCode[] = new byte[4097];
220:
221: /**
222: * Parses image, using current palette and graphics control.
223: * Returns true if the image has been wholly parsed,
224: * false if it was truncated.
225: */
226: private boolean lzwImage(int initCodeSize, int dataSize,
227: byte block[]) {
228:
229: byte[] rasline = new byte[frameWidth];
230:
231: int OUTCODELENGTH = 4097;
232:
233: int clearCode = (1 << initCodeSize);
234: int eofCode = clearCode + 1;
235: int bitMask;
236: int curCode;
237: int outCount = OUTCODELENGTH;
238:
239: /* Variables used to walk read data */
240: int remain = dataSize;
241: int byteoff = 0;
242: int accumbits = 0;
243: int accumdata = 0;
244:
245: /* Variables used to decompress the data */
246: int codeSize = initCodeSize + 1;
247: int maxCode = 1 << codeSize;
248: int codeMask = maxCode - 1;
249: int freeCode = clearCode + 2;
250: int code = 0;
251: int oldCode = 0;
252: char prevChar = 0;
253:
254: /* Variables used for writing pixels */
255: int x = frameWidth;
256: int y = 0;
257: int off = 0;
258: int passinc = interlace ? 8 : 1;
259: int passht = passinc;
260:
261: bitMask = (1 << curColorDepth) - 1;
262:
263: /* Read codes until the eofCode is encountered */
264: for (;;) {
265: if (accumbits < codeSize) {
266: while (remain < 2) {
267: /* Sometimes we have one last byte to process... */
268: if (remain == 1 && accumbits + 8 >= codeSize)
269: break;
270:
271: if (off > 0)
272: drawLine(rasline, y/*, passht*/);
273:
274: /* quietly accept truncated GIF images */
275: return false;
276: }
277:
278: /* 2 bytes at a time saves checking for accumbits < codeSize.
279: * We know we'll get enough and also that we can't overflow
280: * since codeSize <= 12.
281: */
282: if (remain >= 2) {
283: remain -= 2;
284: accumdata += (block[byteoff++] & 0xff) << accumbits;
285: accumbits += 8;
286: accumdata += (block[byteoff++] & 0xff) << accumbits;
287: accumbits += 8;
288: } else {
289: remain--;
290: accumdata += (block[byteoff++] & 0xff) << accumbits;
291: accumbits += 8;
292: }
293: }
294:
295: /* Compute the code */
296: code = accumdata & codeMask;
297: accumdata >>= codeSize;
298: accumbits -= codeSize;
299:
300: /*
301: * Interpret the code
302: */
303: if (code == clearCode) {
304: /* Clear code sets everything back to its initial value, then
305: * reads the immediately subsequent code as uncompressed data.
306: */
307:
308: /* Note that freeCode is one less than it is supposed to be,
309: * this is because it will be incremented next time round the
310: * loop
311: */
312: freeCode = clearCode + 1;
313: codeSize = initCodeSize + 1;
314: maxCode = 1 << codeSize;
315: codeMask = maxCode - 1;
316:
317: /* Continue if we've NOT reached the end, some Gif images
318: * contain bogus codes after the last clear code.
319: */
320: if (y < height)
321: continue;
322:
323: return true;
324: }
325:
326: if (code == eofCode)
327: return true;
328:
329: /* It must be data: save code in CurCode */
330: curCode = code;
331: /* Whenever it gets here outCount is always equal to
332: OUTCODELENGTH, so no need to reset outCount. */
333: //outCount = OUTCODELENGTH;
334: /* If greater or equal to freeCode, not in the hash table
335: * yet; repeat the last character decoded
336: */
337: if (curCode >= freeCode) {
338: if (curCode > freeCode)
339: /*
340: * if we get a code too far outside our range, it
341: * could case the parser to start traversing parts
342: * of our data structure that are out of range...
343: */
344: return true;
345:
346: curCode = oldCode;
347: outCode[--outCount] = (byte) prevChar;
348: }
349:
350: /* Unless this code is raw data, pursue the chain pointed
351: * to by curCode through the hash table to its end; each
352: * code in the chain puts its associated output code on
353: * the output queue.
354: */
355: while (curCode > bitMask) {
356: outCode[--outCount] = suffix[curCode];
357: if (outCount == 0)
358: /*
359: * In theory this should never happen since our
360: * prefix and suffix arrays are monotonically
361: * decreasing and so outCode will only be filled
362: * as much as those arrays, but I don't want to
363: * take that chance and the test is probably
364: * cheap compared to the read and write operations.
365: * If we ever do overflow the array, we will just
366: * flush the rest of the data and quietly accept
367: * the GIF as truncated here.
368: */
369: return true;
370:
371: curCode = prefix[curCode];
372: }
373:
374: /* The last code in the chain is treated as raw data. */
375: prevChar = (char) curCode;
376: outCode[--outCount] = (byte) prevChar;
377:
378: /* Now we put the data out to the Output routine. It's
379: * been stacked LIFO, so deal with it that way...
380: */
381: int len = OUTCODELENGTH - outCount; /* This is why I commented out
382: the code that resets outCount. */
383: while (--len >= 0) {
384: rasline[off++] = outCode[outCount++];
385:
386: /* Update the X-coordinate, and if it overflows, update the
387: * Y-coordinate
388: */
389: if (--x == 0) {
390: /* If a non-interlaced picture, just increment y to the next
391: * scan line. If it's interlaced, deal with the interlace as
392: * described in the GIF spec. Put the decoded scan line out
393: * to the screen if we haven't gone past the bottom of it
394: */
395: drawLine(rasline, y/*, passht*/);
396:
397: x = frameWidth;
398: off = 0;
399: /* pass inc ht ystart */
400: /* 0 8 8 0 */
401: /* 1 8 4 4 */
402: /* 2 4 2 2 */
403: /* 3 2 1 1 */
404: y += passinc;
405: while (y >= height) {
406: passinc = passht;
407: passht >>= 1;
408: y = passht;
409: if (passht == 0)
410: return true;
411: }
412: }
413: }
414:
415: /* Build the hash table on-the-fly. No table is stored in the file. */
416: prefix[freeCode] = (short) oldCode;
417: suffix[freeCode] = (byte) prevChar;
418: oldCode = code;
419: /* Point to the next slot in the table. If we exceed the
420: * maxCode, increment the code size unless
421: * it's already 12. If it is, do nothing: the next code
422: * decompressed better be CLEAR
423: */
424: if (++freeCode >= maxCode) {
425: if (codeSize < 12) {
426: codeSize++;
427: maxCode <<= 1;
428: codeMask = maxCode - 1;
429: } else
430: /* Just in case */
431: freeCode = maxCode - 1;
432: }
433: }
434: }
435: }
|