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