root/dwt/internal/image/LZWCodec.d

Revision 213:36f5cb12e1a2, 13.8 kB (checked in by Frank Benoit <benoit@tionex.de>, 8 months ago)

Update to SWT 3.4M7

Line 
1 /*******************************************************************************
2  * Copyright (c) 2000, 2005 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  * Port to the D programming language:
11  *     Frank Benoit <benoit@tionex.de>
12  *******************************************************************************/
13 module dwt.internal.image.LZWCodec;
14
15
16 import dwt.internal.image.LZWNode;
17 import dwt.internal.image.LEDataInputStream;
18 import dwt.internal.image.LEDataOutputStream;
19 import dwt.DWT;
20 import dwt.graphics.ImageData;
21 import dwt.graphics.ImageLoader;
22 import dwt.graphics.ImageLoaderEvent;
23 import dwt.dwthelper.utils;
24
25 import tango.core.Exception;
26
27 final class LZWCodec {
28     int bitsPerPixel, blockSize, blockIndex, currentByte, bitsLeft,
29         codeSize, clearCode, endCode, newCodes, topSlot, currentSlot,
30         imageWidth, imageHeight, imageX, imageY, pass, line, codeMask;
31     byte[] block, lineArray;
32     int[] stack, suffix, prefix;
33     LZWNode[] nodeStack;
34     LEDataInputStream inputStream;
35     LEDataOutputStream outputStream;
36     ImageData image;
37     ImageLoader loader;
38     bool interlaced;
39     static final int[] MASK_TABLE = [
40         0x1, 0x3, 0x7, 0xF, 0x1F, 0x3F, 0x7F,
41         0xFF, 0x1FF, 0x3FF, 0x7FF, 0xFFF
42     ];
43
44 /**
45  * Decode the input.
46  */
47 void decode() {
48     int code;
49     int oc = 0;
50     int fc = 0;
51     byte[] buf = new byte[imageWidth];
52     int stackIndex = 0;
53     int bufIndex = 0;
54     int c;
55     while ((c = nextCode()) !is endCode) {
56         if (c is clearCode) {
57             codeSize = bitsPerPixel + 1;
58             codeMask = MASK_TABLE[bitsPerPixel];
59             currentSlot = newCodes;
60             topSlot = 1 << codeSize;
61             while ((c = nextCode()) is clearCode) {}
62             if (c !is endCode) {
63                 oc = fc = c;
64                 buf[bufIndex] = cast(byte)c;
65                 bufIndex++;
66                 if (bufIndex is imageWidth) {
67                     nextPutPixels(buf);
68                     bufIndex = 0;
69                 }
70             }
71         } else {
72             code = c;
73             if (code >= currentSlot) {
74                 code = oc;
75                 stack[stackIndex] = fc;
76                 stackIndex++;
77             }
78             while (code >= newCodes) {
79                 stack[stackIndex] = suffix[code];
80                 stackIndex++;
81                 code = prefix[code];
82             }
83             stack[stackIndex] = code;
84             stackIndex++;
85             if (currentSlot < topSlot) {
86                 fc = code;
87                 suffix[currentSlot] = fc;
88                 prefix[currentSlot] = oc;
89                 currentSlot++;
90                 oc = c;
91             }
92             if (currentSlot >= topSlot) {
93                 if (codeSize < 12) {
94                     codeMask = MASK_TABLE[codeSize];
95                     codeSize++;
96                     topSlot = topSlot + topSlot;
97                 }
98             }
99             while (stackIndex > 0) {
100                 stackIndex--;
101                 buf[bufIndex] = cast(byte)stack[stackIndex];
102                 bufIndex++;
103                 if (bufIndex is imageWidth) {
104                     nextPutPixels(buf);
105                     bufIndex = 0;
106                 }
107             }
108         }
109     }
110     if (bufIndex !is 0 && line < imageHeight) {
111         nextPutPixels(buf);
112     }
113 }
114 /**
115  * Decode the LZW-encoded bytes in the given byte stream
116  * into the given DeviceIndependentImage.
117  */
118 public void decode(LEDataInputStream inputStream, ImageLoader loader, ImageData image, bool interlaced, int depth) {
119     this.inputStream = inputStream;
120     this.loader = loader;
121     this.image = image;
122     this.interlaced = interlaced;
123     this.bitsPerPixel = depth;
124     initializeForDecoding();
125     decode();
126 }
127 /**
128  * Encode the image.
129  */
130 void encode() {
131     nextPutCode(clearCode);
132     int lastPrefix = encodeLoop();
133     nextPutCode(lastPrefix);
134     nextPutCode(endCode);
135
136     // Write out last partial block
137     if (bitsLeft is 8) {
138         block[0] = cast(byte)(blockIndex - 1); // Nothing in last byte
139     } else {
140         block[0] = cast(byte)(blockIndex); // Last byte has data
141     }
142     writeBlock();
143
144     // Write out empty block to indicate the end (if needed)
145     if (block[0] !is 0) {
146         block[0] = 0;
147         writeBlock();
148     }
149 }
150 /**
151  * Encode the bytes into the given byte stream
152  * from the given DeviceIndependentImage.
153  */
154 public void encode(LEDataOutputStream byteStream, ImageData image) {
155     this.outputStream = byteStream;
156     this.image = image;
157     initializeForEncoding();
158     encode();
159 }
160 /**
161  * Encoding loop broken out to allow early return.
162  */
163 int encodeLoop() {
164     int pixel = nextPixel();
165     bool found;
166     LZWNode node;
167     while (true) {
168         int currentPrefix = pixel;
169         node = nodeStack[currentPrefix];
170         found = true;
171         pixel = nextPixel();
172         if (pixel < 0)
173             return currentPrefix;
174         while (found && (node.children !is null)) {
175             node = node.children;
176             while (found && (node.suffix !is pixel)) {
177                 if (pixel < node.suffix) {
178                     if (node.left is null) {
179                         node.left = new LZWNode();
180                         found = false;
181                     }
182                     node = node.left;
183                 } else {
184                     if (node.right is null) {
185                         node.right = new LZWNode();
186                         found = false;
187                     }
188                     node = node.right;
189                 }
190             }
191             if (found) {
192                 currentPrefix = node.code;
193                 pixel = nextPixel();
194                 if (pixel < 0)
195                     return currentPrefix;
196             }
197         }
198         if (found) {
199             node.children = new LZWNode();
200             node = node.children;
201         }
202         node.children = null;
203         node.left = null;
204         node.right = null;
205         node.code = currentSlot;
206         node.prefix = currentPrefix;
207         node.suffix = pixel;
208         nextPutCode(currentPrefix);
209         currentSlot++;
210         // Off by one?
211         if (currentSlot < 4096) {
212             if (currentSlot > topSlot) {
213                 codeSize++;
214                 codeMask = MASK_TABLE[codeSize - 1];
215                 topSlot *= 2;
216             }
217         } else {
218             nextPutCode(clearCode);
219             for (int i = 0; i < nodeStack.length; i++)
220                 nodeStack[i].children = null;
221             codeSize = bitsPerPixel + 1;
222             codeMask = MASK_TABLE[codeSize - 1];
223             currentSlot = newCodes;
224             topSlot = 1 << codeSize;
225         }
226     }
227 }
228 /**
229  * Initialize the receiver for decoding the given
230  * byte array.
231  */
232 void initializeForDecoding() {
233     pass = 1;
234     line = 0;
235     codeSize = bitsPerPixel + 1;
236     topSlot = 1 << codeSize;
237     clearCode = 1 << bitsPerPixel;
238     endCode = clearCode + 1;
239     newCodes = currentSlot = endCode + 1;
240     currentByte = -1;
241     blockSize = bitsLeft = 0;
242     blockIndex = 0;
243     codeMask = MASK_TABLE[codeSize - 1];
244     stack = new int[4096];
245     suffix = new int[4096];
246     prefix = new int[4096];
247     block = new byte[256];
248     imageWidth = image.width;
249     imageHeight = image.height;
250 }
251 /**
252  * Initialize the receiver for encoding the given
253  * byte array.
254  */
255 void initializeForEncoding() {
256     interlaced = false;
257     bitsPerPixel = image.depth;
258     codeSize = bitsPerPixel + 1;
259     topSlot = 1 << codeSize;
260     clearCode = 1 << bitsPerPixel;
261     endCode = clearCode + 1;
262     newCodes = currentSlot = endCode + 1;
263     bitsLeft = 8;
264     currentByte = 0;
265     blockIndex = 1;
266     blockSize = 255;
267     block = new byte[blockSize];
268     block[0] = cast(byte)(blockSize - 1);
269     nodeStack = new LZWNode[1 << bitsPerPixel];
270     for (int i = 0; i < nodeStack.length; i++) {
271         LZWNode node = new LZWNode();
272         node.code = i + 1;
273         node.prefix = -1;
274         node.suffix = i + 1;
275         nodeStack[i] = node;
276     }
277     imageWidth = image.width;
278     imageHeight = image.height;
279     imageY = -1;
280     lineArray = new byte[imageWidth];
281     imageX = imageWidth + 1; // Force a read
282 }
283 /**
284  * Answer the next code from the input byte array.
285  */
286 int nextCode() {
287     int code;
288     if (bitsLeft is 0) {
289         if (blockIndex >= blockSize) {
290             blockSize = readBlock();
291             blockIndex = 0;
292             if (blockSize is 0) return endCode;
293         }
294         blockIndex++;
295         currentByte = block[blockIndex] & 0xFF;
296         bitsLeft = 8;
297         code = currentByte;
298     } else {
299         int shift = bitsLeft - 8;
300         if (shift < 0)
301             code = currentByte >> (0 - shift);
302         else
303             code = currentByte << shift;
304     }
305     while (codeSize > bitsLeft) {
306         if (blockIndex >= blockSize) {
307             blockSize = readBlock();
308             blockIndex = 0;
309             if (blockSize is 0) return endCode;
310         }
311         blockIndex++;
312         currentByte = block[blockIndex] & 0xFF;
313         code += currentByte << bitsLeft;
314         bitsLeft += 8;
315     }
316     bitsLeft -= codeSize;
317     return code & codeMask;
318 }
319 /**
320  * Answer the next pixel to encode in the image
321  */
322 int nextPixel() {
323     imageX++;
324     if (imageX > imageWidth) {
325         imageY++;
326         if (imageY >= imageHeight) {
327             return -1;
328         } else {
329             nextPixels(lineArray, imageWidth);
330         }
331         imageX = 1;
332     }
333     return this.lineArray[imageX - 1] & 0xFF;
334 }
335 /**
336  * Copy a row of pixel values from the image.
337  */
338 void nextPixels(byte[] buf, int lineWidth) {
339     if (image.depth is 8) {
340         System.arraycopy(image.data, imageY * image.bytesPerLine, buf, 0, lineWidth);
341     } else {
342         image.getPixels(0, imageY, lineWidth, buf, 0);
343     }
344 }
345 /**
346  * Output aCode to the output stream.
347  */
348 void nextPutCode(int aCode) {
349     int codeToDo = aCode;
350     int codeBitsToDo = codeSize;
351     // Fill in the remainder of the current byte with the
352     // *high-order* bits of the code.
353     int c = codeToDo & MASK_TABLE[bitsLeft - 1];
354     currentByte = currentByte | (c << (8 - bitsLeft));
355     block[blockIndex] = cast(byte)currentByte;
356     codeBitsToDo -= bitsLeft;
357     if (codeBitsToDo < 1) {
358         // The whole code fit in the first byte, so we are done.
359         bitsLeft -= codeSize;
360         if (bitsLeft is 0) {
361             // We used the whole last byte, so get ready
362             // for the next one.
363             bitsLeft = 8;
364             blockIndex++;
365             if (blockIndex >= blockSize) {
366                 writeBlock();
367                 blockIndex = 1;
368             }
369             currentByte = 0;
370         }
371         return;
372     }
373     codeToDo = codeToDo >> bitsLeft;
374
375     // Fill in any remaining whole bytes (i.e. not the last one!)
376     blockIndex++;
377     if (blockIndex >= blockSize) {
378         writeBlock();
379         blockIndex = 1;
380     }
381     while (codeBitsToDo >= 8) {
382         currentByte = codeToDo & 0xFF;
383         block[blockIndex] = cast(byte)currentByte;
384         codeToDo = codeToDo >> 8;
385         codeBitsToDo -= 8;
386         blockIndex++;
387         if (blockIndex >= blockSize) {
388             writeBlock();
389             blockIndex = 1;
390         }
391     }
392     // Fill the *low-order* bits of the last byte with the remainder
393     bitsLeft = 8 - codeBitsToDo;
394     currentByte = codeToDo;
395     block[blockIndex] = cast(byte)currentByte;
396 }
397 /**
398  * Copy a row of pixel values to the image.
399  */
400 void nextPutPixels(byte[] buf) {
401     if (image.depth is 8) {
402         // Slight optimization for depth = 8.
403         int start = line * image.bytesPerLine;
404         for (int i = 0; i < imageWidth; i++)
405             image.data[start + i] = buf[i];
406     } else {
407         image.setPixels(0, line, imageWidth, buf, 0);
408     }
409     if (interlaced) {
410         if (pass is 1) {
411             copyRow(buf, 7);
412             line += 8;
413         } else if (pass is 2) {
414             copyRow(buf, 3);
415             line += 8;
416         } else if (pass is 3) {
417             copyRow(buf, 1);
418             line += 4;
419         } else if (pass is 4) {
420             line += 2;
421         } else if (pass is 5) {
422             line += 0;
423         }
424         if (line >= imageHeight) {
425             pass++;
426             if (pass is 2) line = 4;
427             else if (pass is 3) line = 2;
428             else if (pass is 4) line = 1;
429             else if (pass is 5) line = 0;
430             if (pass < 5) {
431                 if (loader.hasListeners()) {
432                     ImageData imageCopy = cast(ImageData) image.clone();
433                     loader.notifyListeners(
434                         new ImageLoaderEvent(loader, imageCopy, pass - 2, false));
435                 }
436             }
437         }
438         if (line >= imageHeight) line = 0;
439     } else {
440         line++;
441     }
442 }
443 /**
444  * Copy duplicate rows of pixel values to the image.
445  * This is to fill in rows if the image is interlaced.
446  */
447 void copyRow(byte[] buf, int copies) {
448     for (int i = 1; i <= copies; i++) {
449         if (line + i < imageHeight) {
450             image.setPixels(0, line + i, imageWidth, buf, 0);
451         }
452     }
453 }
454 /**
455  * Read a block from the byte stream.
456  * Return the number of bytes read.
457  * Throw an exception if the block could not be read.
458  */
459 int readBlock() {
460     int size = -1;
461     try {
462         size = inputStream.read();
463         if (size is -1) {
464             DWT.error(DWT.ERROR_INVALID_IMAGE);
465         }
466         block[0] = cast(byte)size;
467         size = inputStream.read(block, 1, size);
468         if (size is -1) {
469             DWT.error(DWT.ERROR_INVALID_IMAGE);
470         }
471     } catch (Exception e) {
472         DWT.error(DWT.ERROR_IO, e);
473     }
474     return size;
475 }
476 /**
477  * Write a block to the byte stream.
478  * Throw an exception if the block could not be written.
479  */
480 void writeBlock() {
481     try {
482         outputStream.write(block, 0, (block[0] & 0xFF) + 1);
483     } catch (Exception e) {
484         DWT.error(DWT.ERROR_IO, e);
485     }
486 }
487 }
Note: See TracBrowser for help on using the browser.