Mercurial > projects > dwt2
annotate org.eclipse.swt.gtk.linux.x86/src/org/eclipse/swt/internal/image/GIFFileFormat.d @ 51:c01d033c633a
[swt lin]
author | Frank Benoit <benoit@tionex.de> |
---|---|
date | Fri, 27 Mar 2009 19:58:06 +0100 |
parents | 7a2dd761a8b2 |
children | fb3aa8075988 |
rev | line source |
---|---|
25 | 1 /******************************************************************************* |
2 * Copyright (c) 2000, 2008 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.GIFFileFormat; | |
14 | |
15 public import org.eclipse.swt.internal.image.FileFormat; | |
16 public import org.eclipse.swt.graphics.PaletteData; | |
17 import org.eclipse.swt.internal.image.LEDataInputStream; | |
18 import org.eclipse.swt.internal.image.LZWCodec; | |
19 import org.eclipse.swt.graphics.RGB; | |
20 import org.eclipse.swt.SWT; | |
21 import org.eclipse.swt.graphics.ImageData; | |
22 import org.eclipse.swt.graphics.ImageLoaderEvent; | |
23 import org.eclipse.swt.graphics.ImageLoader; | |
24 import java.lang.all; | |
25 | |
26 | |
27 final class GIFFileFormat : FileFormat { | |
28 String signature; | |
29 int screenWidth, screenHeight, backgroundPixel, bitsPerPixel, defaultDepth; | |
30 int disposalMethod = 0; | |
31 int delayTime = 0; | |
32 int transparentPixel = -1; | |
33 int repeatCount = 1; | |
34 | |
49
7a2dd761a8b2
more work until dmd 2.026 linux segfaults.
Frank Benoit <benoit@tionex.de>
parents:
48
diff
changeset
|
35 static const int GIF_APPLICATION_EXTENSION_BLOCK_ID = 0xFF; |
7a2dd761a8b2
more work until dmd 2.026 linux segfaults.
Frank Benoit <benoit@tionex.de>
parents:
48
diff
changeset
|
36 static const int GIF_GRAPHICS_CONTROL_BLOCK_ID = 0xF9; |
7a2dd761a8b2
more work until dmd 2.026 linux segfaults.
Frank Benoit <benoit@tionex.de>
parents:
48
diff
changeset
|
37 static const int GIF_PLAIN_TEXT_BLOCK_ID = 0x01; |
7a2dd761a8b2
more work until dmd 2.026 linux segfaults.
Frank Benoit <benoit@tionex.de>
parents:
48
diff
changeset
|
38 static const int GIF_COMMENT_BLOCK_ID = 0xFE; |
7a2dd761a8b2
more work until dmd 2.026 linux segfaults.
Frank Benoit <benoit@tionex.de>
parents:
48
diff
changeset
|
39 static const int GIF_EXTENSION_BLOCK_ID = 0x21; |
7a2dd761a8b2
more work until dmd 2.026 linux segfaults.
Frank Benoit <benoit@tionex.de>
parents:
48
diff
changeset
|
40 static const int GIF_IMAGE_BLOCK_ID = 0x2C; |
7a2dd761a8b2
more work until dmd 2.026 linux segfaults.
Frank Benoit <benoit@tionex.de>
parents:
48
diff
changeset
|
41 static const int GIF_TRAILER_ID = 0x3B; |
51 | 42 static /+const+/ byte[] GIF89a = cast(byte[])"GIF89a"; |
43 static /+const+/ byte[] NETSCAPE2_0 = cast(byte[])"NETSCAPE2.0"; | |
25 | 44 |
45 /** | |
46 * Answer a palette containing numGrays | |
47 * shades of gray, ranging from black to white. | |
48 */ | |
49 static PaletteData grayRamp(int numGrays) { | |
50 int n = numGrays - 1; | |
51 RGB[] colors = new RGB[numGrays]; | |
52 for (int i = 0; i < numGrays; i++) { | |
53 int intensity = cast(byte)((i * 3) * 256 / n); | |
54 colors[i] = new RGB(intensity, intensity, intensity); | |
55 } | |
56 return new PaletteData(colors); | |
57 } | |
58 | |
59 override bool isFileFormat(LEDataInputStream stream) { | |
60 try { | |
61 byte[3] signature; | |
62 stream.read(signature); | |
63 stream.unread(signature); | |
64 return signature[0] is 'G' && signature[1] is 'I' && signature[2] is 'F'; | |
65 } catch (Exception e) { | |
66 return false; | |
67 } | |
68 } | |
69 | |
70 /** | |
71 * Load the GIF image(s) stored in the input stream. | |
72 * Return an array of ImageData representing the image(s). | |
73 */ | |
74 override ImageData[] loadFromByteStream() { | |
75 byte[3] signature; | |
76 byte[3] versionBytes; | |
77 byte[7] block; | |
78 try { | |
79 inputStream.read(signature); | |
80 if (!(signature[0] is 'G' && signature[1] is 'I' && signature[2] is 'F')) | |
81 SWT.error(SWT.ERROR_INVALID_IMAGE); | |
82 | |
83 inputStream.read(versionBytes); | |
84 | |
85 inputStream.read(block); | |
86 } catch (IOException e) { | |
87 SWT.error(SWT.ERROR_IO, e); | |
88 } | |
89 screenWidth = (block[0] & 0xFF) | ((block[1] & 0xFF) << 8); | |
90 loader.logicalScreenWidth = screenWidth; | |
91 screenHeight = (block[2] & 0xFF) | ((block[3] & 0xFF) << 8); | |
92 loader.logicalScreenHeight = screenHeight; | |
93 byte bitField = block[4]; | |
94 backgroundPixel = block[5] & 0xFF; | |
95 //aspect = block[6] & 0xFF; | |
96 bitsPerPixel = ((bitField >> 4) & 0x07) + 1; | |
97 defaultDepth = (bitField & 0x7) + 1; | |
98 PaletteData palette = null; | |
99 if ((bitField & 0x80) !is 0) { | |
100 // Global palette. | |
101 //sorted = (bitField & 0x8) !is 0; | |
102 palette = readPalette(1 << defaultDepth); | |
103 } else { | |
104 // No global palette. | |
105 //sorted = false; | |
106 backgroundPixel = -1; | |
107 defaultDepth = bitsPerPixel; | |
108 } | |
109 loader.backgroundPixel = backgroundPixel; | |
110 | |
111 getExtensions(); | |
112 int id = readID(); | |
113 ImageData[] images = new ImageData[0]; | |
114 while (id is GIF_IMAGE_BLOCK_ID) { | |
115 ImageData image = readImageBlock(palette); | |
116 if (loader.hasListeners()) { | |
117 loader.notifyListeners(new ImageLoaderEvent(loader, image, 3, true)); | |
118 } | |
119 ImageData[] oldImages = images; | |
120 images = new ImageData[oldImages.length + 1]; | |
121 System.arraycopy(oldImages, 0, images, 0, oldImages.length); | |
122 images[images.length - 1] = image; | |
123 //images ~= image; | |
124 try { | |
125 /* Read the 0-byte terminator at the end of the image. */ | |
126 id = inputStream.read(); | |
127 if (id > 0) { | |
128 /* We read the terminator earlier. */ | |
129 byte[1] arr; | |
130 arr[0] = id; | |
131 inputStream.unread( arr ); | |
132 } | |
133 } catch (IOException e) { | |
134 SWT.error(SWT.ERROR_IO, e); | |
135 } | |
136 getExtensions(); | |
137 id = readID(); | |
138 } | |
139 return images; | |
140 } | |
141 | |
142 /** | |
143 * Read and return the next block or extension identifier from the file. | |
144 */ | |
145 int readID() { | |
146 try { | |
147 return inputStream.read(); | |
148 } catch (IOException e) { | |
149 SWT.error(SWT.ERROR_IO, e); | |
150 } | |
151 return -1; | |
152 } | |
153 | |
154 /** | |
155 * Read extensions until an image descriptor appears. | |
156 * In the future, if we care about the extensions, they | |
157 * should be properly grouped with the image data before | |
158 * which they appeared. Right now, the interesting parts | |
159 * of some extensions are kept, but the rest is discarded. | |
160 * Throw an error if an error occurs. | |
161 */ | |
162 void getExtensions() { | |
163 int id = readID(); | |
164 while (id !is GIF_IMAGE_BLOCK_ID && id !is GIF_TRAILER_ID && id > 0) { | |
165 if (id is GIF_EXTENSION_BLOCK_ID) { | |
166 readExtension(); | |
167 } else { | |
168 SWT.error(SWT.ERROR_INVALID_IMAGE); | |
169 } | |
170 id = readID(); | |
171 } | |
172 if (id is GIF_IMAGE_BLOCK_ID || id is GIF_TRAILER_ID) { | |
173 try { | |
174 byte[1] arr; | |
175 arr[0] = id; | |
176 inputStream.unread(arr); | |
177 } catch (IOException e) { | |
178 SWT.error(SWT.ERROR_IO, e); | |
179 } | |
180 } | |
181 } | |
182 | |
183 /** | |
184 * Read a control extension. | |
185 * Return the extension block data. | |
186 */ | |
187 byte[] readExtension() { | |
188 int extensionID = readID(); | |
189 if (extensionID is GIF_COMMENT_BLOCK_ID) | |
190 return readCommentExtension(); | |
191 if (extensionID is GIF_PLAIN_TEXT_BLOCK_ID) | |
192 return readPlainTextExtension(); | |
193 if (extensionID is GIF_GRAPHICS_CONTROL_BLOCK_ID) | |
194 return readGraphicsControlExtension(); | |
195 if (extensionID is GIF_APPLICATION_EXTENSION_BLOCK_ID) | |
196 return readApplicationExtension(); | |
197 // Otherwise, we don't recognize the block. If the | |
198 // field size is correct, we can just skip over | |
199 // the block contents. | |
200 try { | |
201 int extSize = inputStream.read(); | |
202 if (extSize < 0) { | |
203 SWT.error(SWT.ERROR_INVALID_IMAGE); | |
204 } | |
205 byte[] ext = new byte[extSize]; | |
206 inputStream.read(ext, 0, extSize); | |
207 return ext; | |
208 } catch (IOException e) { | |
209 SWT.error(SWT.ERROR_IO, e); | |
210 return null; | |
211 } | |
212 } | |
213 | |
214 /** | |
215 * We have just read the Comment extension identifier | |
216 * from the input stream. Read in the rest of the comment | |
217 * and return it. GIF comment blocks are variable size. | |
218 */ | |
219 byte[] readCommentExtension() { | |
220 try { | |
221 byte[] comment = new byte[0]; | |
222 byte[] block = new byte[255]; | |
223 int size = inputStream.read(); | |
224 while ((size > 0) && (inputStream.read(block, 0, size) !is -1)) { | |
225 byte[] oldComment = comment; | |
226 comment = new byte[oldComment.length + size]; | |
227 System.arraycopy(oldComment, 0, comment, 0, oldComment.length); | |
228 System.arraycopy(block, 0, comment, oldComment.length, size); | |
229 //comment ~= block[ 0 .. size ]; | |
230 size = inputStream.read(); | |
231 } | |
232 return comment; | |
233 } catch (Exception e) { | |
234 SWT.error(SWT.ERROR_IO, e); | |
235 return null; | |
236 } | |
237 } | |
238 | |
239 /** | |
240 * We have just read the PlainText extension identifier | |
241 * from the input stream. Read in the plain text info and text, | |
242 * and return the text. GIF plain text blocks are variable size. | |
243 */ | |
244 byte[] readPlainTextExtension() { | |
245 try { | |
246 // Read size of block = 0x0C. | |
247 inputStream.read(); | |
248 // Read the text information (x, y, width, height, colors). | |
249 byte[] info = new byte[12]; | |
250 inputStream.read(info); | |
251 // Read the text. | |
252 byte[] text = new byte[0]; | |
253 byte[] block = new byte[255]; | |
254 int size = inputStream.read(); | |
255 while ((size > 0) && (inputStream.read(block, 0, size) !is -1)) { | |
256 byte[] oldText = text; | |
257 text = new byte[oldText.length + size]; | |
258 System.arraycopy(oldText, 0, text, 0, oldText.length); | |
259 System.arraycopy(block, 0, text, oldText.length, size); | |
260 //text ~= block[ 0 .. size ]; | |
261 size = inputStream.read(); | |
262 } | |
263 return text; | |
264 } catch (Exception e) { | |
265 SWT.error(SWT.ERROR_IO, e); | |
266 return null; | |
267 } | |
268 } | |
269 | |
270 /** | |
271 * We have just read the GraphicsControl extension identifier | |
272 * from the input stream. Read in the control information, store | |
273 * it, and return it. | |
274 */ | |
275 byte[] readGraphicsControlExtension() { | |
276 try { | |
277 // Read size of block = 0x04. | |
278 inputStream.read(); | |
279 // Read the control block. | |
280 byte[] controlBlock = new byte[4]; | |
281 inputStream.read(controlBlock); | |
282 byte bitField = controlBlock[0]; | |
283 // Store the user input field. | |
284 //userInput = (bitField & 0x02) !is 0; | |
285 // Store the disposal method. | |
286 disposalMethod = (bitField >> 2) & 0x07; | |
287 // Store the delay time. | |
288 delayTime = (controlBlock[1] & 0xFF) | ((controlBlock[2] & 0xFF) << 8); | |
289 // Store the transparent color. | |
290 if ((bitField & 0x01) !is 0) { | |
291 transparentPixel = controlBlock[3] & 0xFF; | |
292 } else { | |
293 transparentPixel = -1; | |
294 } | |
295 // Read block terminator. | |
296 inputStream.read(); | |
297 return controlBlock; | |
298 } catch (Exception e) { | |
299 SWT.error(SWT.ERROR_IO, e); | |
300 return null; | |
301 } | |
302 } | |
303 | |
304 /** | |
305 * We have just read the Application extension identifier | |
306 * from the input stream. Read in the rest of the extension, | |
307 * look for and store 'number of repeats', and return the data. | |
308 */ | |
309 byte[] readApplicationExtension() { | |
310 try { | |
311 // Read size of block = 0x0B. | |
312 inputStream.read(); | |
313 // Read application identifier. | |
314 byte[] application = new byte[8]; | |
315 inputStream.read(application); | |
316 // Read authentication code. | |
317 byte[] authentication = new byte[3]; | |
318 inputStream.read(authentication); | |
319 // Read application data. | |
320 byte[] data = new byte[0]; | |
321 byte[] block = new byte[255]; | |
322 int size = inputStream.read(); | |
323 while ((size > 0) && (inputStream.read(block, 0, size) !is -1)) { | |
324 byte[] oldData = data; | |
325 data = new byte[oldData.length + size]; | |
326 System.arraycopy(oldData, 0, data, 0, oldData.length); | |
327 System.arraycopy(block, 0, data, oldData.length, size); | |
328 //data ~= block[ 0 .. size ]; | |
329 size = inputStream.read(); | |
330 } | |
331 // Look for the NETSCAPE 'repeat count' field for an animated GIF. | |
332 bool netscape = | |
333 application[0] is 'N' && | |
334 application[1] is 'E' && | |
335 application[2] is 'T' && | |
336 application[3] is 'S' && | |
337 application[4] is 'C' && | |
338 application[5] is 'A' && | |
339 application[6] is 'P' && | |
340 application[7] is 'E'; | |
341 bool authentic = | |
342 authentication[0] is '2' && | |
343 authentication[1] is '.' && | |
344 authentication[2] is '0'; | |
345 if (netscape && authentic && data[0] is 01) { //$NON-NLS-1$ //$NON-NLS-2$ | |
346 repeatCount = (data[1] & 0xFF) | ((data[2] & 0xFF) << 8); | |
347 loader.repeatCount = repeatCount; | |
348 } | |
349 return data; | |
350 } catch (Exception e) { | |
351 SWT.error(SWT.ERROR_IO, e); | |
352 return null; | |
353 } | |
354 } | |
355 | |
356 /** | |
357 * Return a DeviceIndependentImage representing the | |
358 * image block at the current position in the input stream. | |
359 * Throw an error if an error occurs. | |
360 */ | |
361 ImageData readImageBlock(PaletteData defaultPalette) { | |
362 int depth; | |
363 PaletteData palette; | |
364 byte[] block = new byte[9]; | |
365 try { | |
366 inputStream.read(block); | |
367 } catch (IOException e) { | |
368 SWT.error(SWT.ERROR_IO, e); | |
369 } | |
370 int left = (block[0] & 0xFF) | ((block[1] & 0xFF) << 8); | |
371 int top = (block[2] & 0xFF) | ((block[3] & 0xFF) << 8); | |
372 int width = (block[4] & 0xFF) | ((block[5] & 0xFF) << 8); | |
373 int height = (block[6] & 0xFF) | ((block[7] & 0xFF) << 8); | |
374 byte bitField = block[8]; | |
375 bool interlaced = (bitField & 0x40) !is 0; | |
376 //bool sorted = (bitField & 0x20) !is 0; | |
377 if ((bitField & 0x80) !is 0) { | |
378 // Local palette. | |
379 depth = (bitField & 0x7) + 1; | |
380 palette = readPalette(1 << depth); | |
381 } else { | |
382 // No local palette. | |
383 depth = defaultDepth; | |
384 palette = defaultPalette; | |
385 } | |
386 /* Work around: Ignore the case where a GIF specifies an | |
387 * invalid index for the transparent pixel that is larger | |
388 * than the number of entries in the palette. */ | |
389 if (transparentPixel > 1 << depth) { | |
390 transparentPixel = -1; | |
391 } | |
392 // Promote depth to next highest supported value. | |
393 if (!(depth is 1 || depth is 4 || depth is 8)) { | |
394 if (depth < 4) | |
395 depth = 4; | |
396 else | |
397 depth = 8; | |
398 } | |
399 if (palette is null) { | |
400 palette = grayRamp(1 << depth); | |
401 } | |
402 int initialCodeSize = -1; | |
403 try { | |
404 initialCodeSize = inputStream.read(); | |
405 } catch (IOException e) { | |
406 SWT.error(SWT.ERROR_IO, e); | |
407 } | |
408 if (initialCodeSize < 0) { | |
409 SWT.error(SWT.ERROR_INVALID_IMAGE); | |
410 } | |
411 ImageData image = ImageData.internal_new( | |
412 width, | |
413 height, | |
414 depth, | |
415 palette, | |
416 4, | |
417 null, | |
418 0, | |
419 null, | |
420 null, | |
421 -1, | |
422 transparentPixel, | |
423 SWT.IMAGE_GIF, | |
424 left, | |
425 top, | |
426 disposalMethod, | |
427 delayTime); | |
428 LZWCodec codec = new LZWCodec(); | |
429 codec.decode(inputStream, loader, image, interlaced, initialCodeSize); | |
430 return image; | |
431 } | |
432 | |
433 /** | |
434 * Read a palette from the input stream. | |
435 */ | |
436 PaletteData readPalette(int numColors) { | |
437 byte[] bytes = new byte[numColors * 3]; | |
438 try { | |
439 if (inputStream.read(bytes) !is bytes.length) | |
440 SWT.error(SWT.ERROR_INVALID_IMAGE); | |
441 } catch (IOException e) { | |
442 SWT.error(SWT.ERROR_IO, e); | |
443 } | |
444 RGB[] colors = new RGB[numColors]; | |
445 for (int i = 0; i < numColors; i++) | |
446 colors[i] = new RGB(bytes[i*3] & 0xFF, | |
447 bytes[i*3+1] & 0xFF, bytes[i*3+2] & 0xFF); | |
448 return new PaletteData(colors); | |
449 } | |
450 | |
451 override void unloadIntoByteStream(ImageLoader loader) { | |
452 | |
453 /* Step 1: Acquire GIF parameters. */ | |
454 ImageData[] data = loader.data; | |
455 int frameCount = data.length; | |
456 bool multi = frameCount > 1; | |
457 ImageData firstImage = data[0]; | |
458 int logicalScreenWidth = multi ? loader.logicalScreenWidth : firstImage.width; | |
459 int logicalScreenHeight = multi ? loader.logicalScreenHeight : firstImage.height; | |
460 int backgroundPixel = loader.backgroundPixel; | |
461 int depth = firstImage.depth; | |
462 PaletteData palette = firstImage.palette; | |
463 RGB[] colors = palette.getRGBs(); | |
464 short globalTable = 1; | |
465 | |
466 /* Step 2: Check for validity and global/local color map. */ | |
467 if (!(depth is 1 || depth is 4 || depth is 8)) { | |
468 SWT.error(SWT.ERROR_UNSUPPORTED_DEPTH); | |
469 } | |
470 for (int i=0; i<frameCount; i++) { | |
471 if (data[i].palette.isDirect) { | |
472 SWT.error(SWT.ERROR_INVALID_IMAGE); | |
473 } | |
474 if (multi) { | |
475 if (!(data[i].height <= logicalScreenHeight && data[i].width <= logicalScreenWidth && data[i].depth is depth)) { | |
476 SWT.error(SWT.ERROR_INVALID_IMAGE); | |
477 } | |
478 if (globalTable is 1) { | |
479 RGB rgbs[] = data[i].palette.getRGBs(); | |
480 if (rgbs.length !is colors.length) { | |
481 globalTable = 0; | |
482 } else { | |
483 for (int j=0; j<colors.length; j++) { | |
484 if (!(rgbs[j].red is colors[j].red && | |
485 rgbs[j].green is colors[j].green && | |
486 rgbs[j].blue is colors[j].blue)) | |
487 globalTable = 0; | |
488 } | |
489 } | |
490 } | |
491 } | |
492 } | |
493 | |
494 try { | |
495 /* Step 3: Write the GIF89a Header and Logical Screen Descriptor. */ | |
496 outputStream.write(GIF89a); | |
497 int bitField = globalTable*128 + (depth-1)*16 + depth-1; | |
498 outputStream.writeShort(cast(short)logicalScreenWidth); | |
499 outputStream.writeShort(cast(short)logicalScreenHeight); | |
500 outputStream.write(bitField); | |
501 outputStream.write(backgroundPixel); | |
502 outputStream.write(0); // Aspect ratio is 1:1 | |
503 } catch (IOException e) { | |
504 SWT.error(SWT.ERROR_IO, e); | |
505 } | |
506 | |
507 /* Step 4: Write Global Color Table if applicable. */ | |
508 if (globalTable is 1) { | |
509 writePalette(palette, depth); | |
510 } | |
511 | |
512 /* Step 5: Write Application Extension if applicable. */ | |
513 if (multi) { | |
514 int repeatCount = loader.repeatCount; | |
515 try { | |
516 outputStream.write(GIF_EXTENSION_BLOCK_ID); | |
517 outputStream.write(GIF_APPLICATION_EXTENSION_BLOCK_ID); | |
518 outputStream.write(NETSCAPE2_0.length); | |
519 outputStream.write(NETSCAPE2_0); | |
520 outputStream.write(3); // Three bytes follow | |
521 outputStream.write(1); // Extension type | |
522 outputStream.writeShort(cast(short) repeatCount); | |
523 outputStream.write(0); // Block terminator | |
524 } catch (IOException e) { | |
525 SWT.error(SWT.ERROR_IO, e); | |
526 } | |
527 } | |
528 | |
529 for (int frame=0; frame<frameCount; frame++) { | |
530 | |
531 /* Step 6: Write Graphics Control Block for each frame if applicable. */ | |
532 if (multi || data[frame].transparentPixel !is -1) { | |
533 writeGraphicsControlBlock(data[frame]); | |
534 } | |
535 | |
536 /* Step 7: Write Image Header for each frame. */ | |
537 int x = data[frame].x; | |
538 int y = data[frame].y; | |
539 int width = data[frame].width; | |
540 int height = data[frame].height; | |
541 try { | |
542 outputStream.write(GIF_IMAGE_BLOCK_ID); | |
543 byte[] block = new byte[9]; | |
544 block[0] = cast(byte)(x & 0xFF); | |
545 block[1] = cast(byte)((x >> 8) & 0xFF); | |
546 block[2] = cast(byte)(y & 0xFF); | |
547 block[3] = cast(byte)((y >> 8) & 0xFF); | |
548 block[4] = cast(byte)(width & 0xFF); | |
549 block[5] = cast(byte)((width >> 8) & 0xFF); | |
550 block[6] = cast(byte)(height & 0xFF); | |
551 block[7] = cast(byte)((height >> 8) & 0xFF); | |
552 block[8] = cast(byte)(globalTable is 0 ? (depth-1) | 0x80 : 0x00); | |
553 outputStream.write(block); | |
554 } catch (IOException e) { | |
555 SWT.error(SWT.ERROR_IO, e); | |
556 } | |
557 | |
558 /* Step 8: Write Local Color Table for each frame if applicable. */ | |
559 if (globalTable is 0) { | |
560 writePalette(data[frame].palette, depth); | |
561 } | |
562 | |
563 /* Step 9: Write the actual data for each frame. */ | |
564 try { | |
565 outputStream.write(depth); // Minimum LZW Code size | |
566 } catch (IOException e) { | |
567 SWT.error(SWT.ERROR_IO, e); | |
568 } | |
569 (new LZWCodec()).encode(outputStream, data[frame]); | |
570 } | |
571 | |
572 /* Step 10: Write GIF terminator. */ | |
573 try { | |
574 outputStream.write(0x3B); | |
575 } catch (IOException e) { | |
576 SWT.error(SWT.ERROR_IO, e); | |
577 } | |
578 } | |
579 | |
580 /** | |
581 * Write out a GraphicsControlBlock to describe | |
582 * the specified device independent image. | |
583 */ | |
584 void writeGraphicsControlBlock(ImageData image) { | |
585 try { | |
586 outputStream.write(GIF_EXTENSION_BLOCK_ID); | |
587 outputStream.write(GIF_GRAPHICS_CONTROL_BLOCK_ID); | |
588 byte[] gcBlock = new byte[4]; | |
589 gcBlock[0] = 0; | |
590 gcBlock[1] = 0; | |
591 gcBlock[2] = 0; | |
592 gcBlock[3] = 0; | |
593 if (image.transparentPixel !is -1) { | |
594 gcBlock[0] = cast(byte)0x01; | |
595 gcBlock[3] = cast(byte)image.transparentPixel; | |
596 } | |
597 if (image.disposalMethod !is 0) { | |
598 gcBlock[0] |= cast(byte)((image.disposalMethod & 0x07) << 2); | |
599 } | |
600 if (image.delayTime !is 0) { | |
601 gcBlock[1] = cast(byte)(image.delayTime & 0xFF); | |
602 gcBlock[2] = cast(byte)((image.delayTime >> 8) & 0xFF); | |
603 } | |
604 outputStream.write(cast(byte)gcBlock.length); | |
605 outputStream.write(gcBlock); | |
606 outputStream.write(0); // Block terminator | |
607 } catch (IOException e) { | |
608 SWT.error(SWT.ERROR_IO, e); | |
609 } | |
610 } | |
611 | |
612 /** | |
613 * Write the specified palette to the output stream. | |
614 */ | |
615 void writePalette(PaletteData palette, int depth) { | |
616 byte[] bytes = new byte[(1 << depth) * 3]; | |
617 int offset = 0; | |
618 for (int i = 0; i < palette.colors.length; i++) { | |
619 RGB color = palette.colors[i]; | |
620 bytes[offset] = cast(byte)color.red; | |
621 bytes[offset + 1] = cast(byte)color.green; | |
622 bytes[offset + 2] = cast(byte)color.blue; | |
623 offset += 3; | |
624 } | |
625 try { | |
626 outputStream.write(bytes); | |
627 } catch (IOException e) { | |
628 SWT.error(SWT.ERROR_IO, e); | |
629 } | |
630 } | |
631 } |