1 11 package org.eclipse.swt.internal.image; 12 13 14 import org.eclipse.swt.*; 15 import org.eclipse.swt.graphics.*; 16 import java.io.*; 17 18 final class GIFFileFormat extends FileFormat { 19 String signature; 20 int screenWidth, screenHeight, backgroundPixel, bitsPerPixel, defaultDepth; 21 int disposalMethod = 0; 22 int delayTime = 0; 23 int transparentPixel = -1; 24 int repeatCount = 1; 25 26 static final int GIF_APPLICATION_EXTENSION_BLOCK_ID = 0xFF; 27 static final int GIF_GRAPHICS_CONTROL_BLOCK_ID = 0xF9; 28 static final int GIF_PLAIN_TEXT_BLOCK_ID = 0x01; 29 static final int GIF_COMMENT_BLOCK_ID = 0xFE; 30 static final int GIF_EXTENSION_BLOCK_ID = 0x21; 31 static final int GIF_IMAGE_BLOCK_ID = 0x2C; 32 static final int GIF_TRAILER_ID = 0x3B; 33 static final byte [] GIF89a = new byte[] { (byte)'G', (byte)'I', (byte)'F', (byte)'8', (byte)'9', (byte)'a' }; 34 static final byte [] NETSCAPE2_0 = new byte[] { (byte)'N', (byte)'E', (byte)'T', (byte)'S', (byte)'C', (byte)'A', (byte)'P', (byte)'E', (byte)'2', (byte)'.', (byte)'0' }; 35 36 40 static PaletteData grayRamp(int numGrays) { 41 int n = numGrays - 1; 42 RGB[] colors = new RGB[numGrays]; 43 for (int i = 0; i < numGrays; i++) { 44 int intensity = (byte)((i * 3) * 256 / n); 45 colors[i] = new RGB(intensity, intensity, intensity); 46 } 47 return new PaletteData(colors); 48 } 49 50 boolean isFileFormat(LEDataInputStream stream) { 51 try { 52 byte[] signature = new byte[3]; 53 stream.read(signature); 54 stream.unread(signature); 55 return new String (signature).equals("GIF"); } catch (Exception e) { 57 return false; 58 } 59 } 60 61 65 ImageData[] loadFromByteStream() { 66 byte[] signatureBytes = new byte[3]; 67 byte[] versionBytes = new byte[3]; 68 byte[] block = new byte[7]; 69 try { 70 inputStream.read(signatureBytes); 71 signature = new String (signatureBytes); 72 if (!signature.equals("GIF")) SWT.error(SWT.ERROR_INVALID_IMAGE); 74 75 inputStream.read(versionBytes); 76 77 inputStream.read(block); 78 } catch (IOException e) { 79 SWT.error(SWT.ERROR_IO, e); 80 } 81 screenWidth = (block[0] & 0xFF) | ((block[1] & 0xFF) << 8); 82 loader.logicalScreenWidth = screenWidth; 83 screenHeight = (block[2] & 0xFF) | ((block[3] & 0xFF) << 8); 84 loader.logicalScreenHeight = screenHeight; 85 byte bitField = block[4]; 86 backgroundPixel = block[5] & 0xFF; 87 bitsPerPixel = ((bitField >> 4) & 0x07) + 1; 89 defaultDepth = (bitField & 0x7) + 1; 90 PaletteData palette = null; 91 if ((bitField & 0x80) != 0) { 92 palette = readPalette(1 << defaultDepth); 95 } else { 96 backgroundPixel = -1; 99 defaultDepth = bitsPerPixel; 100 } 101 loader.backgroundPixel = backgroundPixel; 102 103 getExtensions(); 104 int id = readID(); 105 ImageData[] images = new ImageData[0]; 106 while (id == GIF_IMAGE_BLOCK_ID) { 107 ImageData image = readImageBlock(palette); 108 if (loader.hasListeners()) { 109 loader.notifyListeners(new ImageLoaderEvent(loader, image, 3, true)); 110 } 111 ImageData[] oldImages = images; 112 images = new ImageData[oldImages.length + 1]; 113 System.arraycopy(oldImages, 0, images, 0, oldImages.length); 114 images[images.length - 1] = image; 115 try { 116 117 id = inputStream.read(); 118 if (id > 0) { 119 120 inputStream.unread(new byte[] {(byte)id}); 121 } 122 } catch (IOException e) { 123 SWT.error(SWT.ERROR_IO, e); 124 } 125 getExtensions(); 126 id = readID(); 127 } 128 return images; 129 } 130 131 134 int readID() { 135 try { 136 return inputStream.read(); 137 } catch (IOException e) { 138 SWT.error(SWT.ERROR_IO, e); 139 } 140 return -1; 141 } 142 143 151 void getExtensions() { 152 int id = readID(); 153 while (id != GIF_IMAGE_BLOCK_ID && id != GIF_TRAILER_ID && id > 0) { 154 if (id == GIF_EXTENSION_BLOCK_ID) { 155 readExtension(); 156 } else { 157 SWT.error(SWT.ERROR_INVALID_IMAGE); 158 } 159 id = readID(); 160 } 161 if (id == GIF_IMAGE_BLOCK_ID || id == GIF_TRAILER_ID) { 162 try { 163 inputStream.unread(new byte[] {(byte)id}); 164 } catch (IOException e) { 165 SWT.error(SWT.ERROR_IO, e); 166 } 167 } 168 } 169 170 174 byte[] readExtension() { 175 int extensionID = readID(); 176 if (extensionID == GIF_COMMENT_BLOCK_ID) 177 return readCommentExtension(); 178 if (extensionID == GIF_PLAIN_TEXT_BLOCK_ID) 179 return readPlainTextExtension(); 180 if (extensionID == GIF_GRAPHICS_CONTROL_BLOCK_ID) 181 return readGraphicsControlExtension(); 182 if (extensionID == GIF_APPLICATION_EXTENSION_BLOCK_ID) 183 return readApplicationExtension(); 184 try { 188 int extSize = inputStream.read(); 189 if (extSize < 0) { 190 SWT.error(SWT.ERROR_INVALID_IMAGE); 191 } 192 byte[] ext = new byte[extSize]; 193 inputStream.read(ext, 0, extSize); 194 return ext; 195 } catch (IOException e) { 196 SWT.error(SWT.ERROR_IO, e); 197 return null; 198 } 199 } 200 201 206 byte[] readCommentExtension() { 207 try { 208 byte[] comment = new byte[0]; 209 byte[] block = new byte[255]; 210 int size = inputStream.read(); 211 while ((size > 0) && (inputStream.read(block, 0, size) != -1)) { 212 byte[] oldComment = comment; 213 comment = new byte[oldComment.length + size]; 214 System.arraycopy(oldComment, 0, comment, 0, oldComment.length); 215 System.arraycopy(block, 0, comment, oldComment.length, size); 216 size = inputStream.read(); 217 } 218 return comment; 219 } catch (Exception e) { 220 SWT.error(SWT.ERROR_IO, e); 221 return null; 222 } 223 } 224 225 230 byte[] readPlainTextExtension() { 231 try { 232 inputStream.read(); 234 byte[] info = new byte[12]; 236 inputStream.read(info); 237 byte[] text = new byte[0]; 239 byte[] block = new byte[255]; 240 int size = inputStream.read(); 241 while ((size > 0) && (inputStream.read(block, 0, size) != -1)) { 242 byte[] oldText = text; 243 text = new byte[oldText.length + size]; 244 System.arraycopy(oldText, 0, text, 0, oldText.length); 245 System.arraycopy(block, 0, text, oldText.length, size); 246 size = inputStream.read(); 247 } 248 return text; 249 } catch (Exception e) { 250 SWT.error(SWT.ERROR_IO, e); 251 return null; 252 } 253 } 254 255 260 byte[] readGraphicsControlExtension() { 261 try { 262 inputStream.read(); 264 byte[] controlBlock = new byte[4]; 266 inputStream.read(controlBlock); 267 byte bitField = controlBlock[0]; 268 disposalMethod = (bitField >> 2) & 0x07; 272 delayTime = (controlBlock[1] & 0xFF) | ((controlBlock[2] & 0xFF) << 8); 274 if ((bitField & 0x01) != 0) { 276 transparentPixel = controlBlock[3] & 0xFF; 277 } else { 278 transparentPixel = -1; 279 } 280 inputStream.read(); 282 return controlBlock; 283 } catch (Exception e) { 284 SWT.error(SWT.ERROR_IO, e); 285 return null; 286 } 287 } 288 289 294 byte[] readApplicationExtension() { 295 try { 296 inputStream.read(); 298 byte[] applicationBytes = new byte[8]; 300 inputStream.read(applicationBytes); 301 String application = new String (applicationBytes); 302 byte[] authenticationBytes = new byte[3]; 304 inputStream.read(authenticationBytes); 305 String authentication = new String (authenticationBytes); 306 byte[] data = new byte[0]; 308 byte[] block = new byte[255]; 309 int size = inputStream.read(); 310 while ((size > 0) && (inputStream.read(block, 0, size) != -1)) { 311 byte[] oldData = data; 312 data = new byte[oldData.length + size]; 313 System.arraycopy(oldData, 0, data, 0, oldData.length); 314 System.arraycopy(block, 0, data, oldData.length, size); 315 size = inputStream.read(); 316 } 317 if (application.equals("NETSCAPE") && authentication.equals("2.0") && data[0] == 01) { repeatCount = (data[1] & 0xFF) | ((data[2] & 0xFF) << 8); 320 loader.repeatCount = repeatCount; 321 } 322 return data; 323 } catch (Exception e) { 324 SWT.error(SWT.ERROR_IO, e); 325 return null; 326 } 327 } 328 329 334 ImageData readImageBlock(PaletteData defaultPalette) { 335 int depth; 336 PaletteData palette; 337 byte[] block = new byte[9]; 338 try { 339 inputStream.read(block); 340 } catch (IOException e) { 341 SWT.error(SWT.ERROR_IO, e); 342 } 343 int left = (block[0] & 0xFF) | ((block[1] & 0xFF) << 8); 344 int top = (block[2] & 0xFF) | ((block[3] & 0xFF) << 8); 345 int width = (block[4] & 0xFF) | ((block[5] & 0xFF) << 8); 346 int height = (block[6] & 0xFF) | ((block[7] & 0xFF) << 8); 347 byte bitField = block[8]; 348 boolean interlaced = (bitField & 0x40) != 0; 349 if ((bitField & 0x80) != 0) { 351 depth = (bitField & 0x7) + 1; 353 palette = readPalette(1 << depth); 354 } else { 355 depth = defaultDepth; 357 palette = defaultPalette; 358 } 359 362 if (transparentPixel > 1 << depth) { 363 transparentPixel = -1; 364 } 365 if (!(depth == 1 || depth == 4 || depth == 8)) { 367 if (depth < 4) 368 depth = 4; 369 else 370 depth = 8; 371 } 372 if (palette == null) { 373 palette = grayRamp(1 << depth); 374 } 375 int initialCodeSize = -1; 376 try { 377 initialCodeSize = inputStream.read(); 378 } catch (IOException e) { 379 SWT.error(SWT.ERROR_IO, e); 380 } 381 if (initialCodeSize < 0) { 382 SWT.error(SWT.ERROR_INVALID_IMAGE); 383 } 384 ImageData image = ImageData.internal_new( 385 width, 386 height, 387 depth, 388 palette, 389 4, 390 null, 391 0, 392 null, 393 null, 394 -1, 395 transparentPixel, 396 SWT.IMAGE_GIF, 397 left, 398 top, 399 disposalMethod, 400 delayTime); 401 LZWCodec codec = new LZWCodec(); 402 codec.decode(inputStream, loader, image, interlaced, initialCodeSize); 403 return image; 404 } 405 406 409 PaletteData readPalette(int numColors) { 410 byte[] bytes = new byte[numColors * 3]; 411 try { 412 if (inputStream.read(bytes) != bytes.length) 413 SWT.error(SWT.ERROR_INVALID_IMAGE); 414 } catch (IOException e) { 415 SWT.error(SWT.ERROR_IO, e); 416 } 417 RGB[] colors = new RGB[numColors]; 418 for (int i = 0; i < numColors; i++) 419 colors[i] = new RGB(bytes[i*3] & 0xFF, 420 bytes[i*3+1] & 0xFF, bytes[i*3+2] & 0xFF); 421 return new PaletteData(colors); 422 } 423 424 void unloadIntoByteStream(ImageLoader loader) { 425 426 427 ImageData[] data = loader.data; 428 int frameCount = data.length; 429 boolean multi = frameCount > 1; 430 ImageData firstImage = data[0]; 431 int logicalScreenWidth = multi ? loader.logicalScreenWidth : firstImage.width; 432 int logicalScreenHeight = multi ? loader.logicalScreenHeight : firstImage.height; 433 int backgroundPixel = loader.backgroundPixel; 434 int depth = firstImage.depth; 435 PaletteData palette = firstImage.palette; 436 RGB[] colors = palette.getRGBs(); 437 short globalTable = 1; 438 439 440 if (!(depth == 1 || depth == 4 || depth == 8)) { 441 SWT.error(SWT.ERROR_UNSUPPORTED_DEPTH); 442 } 443 for (int i=0; i<frameCount; i++) { 444 if (data[i].palette.isDirect) { 445 SWT.error(SWT.ERROR_INVALID_IMAGE); 446 } 447 if (multi) { 448 if (!(data[i].height <= logicalScreenHeight && data[i].width <= logicalScreenWidth && data[i].depth == depth)) { 449 SWT.error(SWT.ERROR_INVALID_IMAGE); 450 } 451 if (globalTable == 1) { 452 RGB rgbs[] = data[i].palette.getRGBs(); 453 if (rgbs.length != colors.length) { 454 globalTable = 0; 455 } else { 456 for (int j=0; j<colors.length; j++) { 457 if (!(rgbs[j].red == colors[j].red && 458 rgbs[j].green == colors[j].green && 459 rgbs[j].blue == colors[j].blue)) 460 globalTable = 0; 461 } 462 } 463 } 464 } 465 } 466 467 try { 468 469 outputStream.write(GIF89a); 470 int bitField = globalTable*128 + (depth-1)*16 + depth-1; 471 outputStream.writeShort((short)logicalScreenWidth); 472 outputStream.writeShort((short)logicalScreenHeight); 473 outputStream.write(bitField); 474 outputStream.write(backgroundPixel); 475 outputStream.write(0); } catch (IOException e) { 477 SWT.error(SWT.ERROR_IO, e); 478 } 479 480 481 if (globalTable == 1) { 482 writePalette(palette, depth); 483 } 484 485 486 if (multi) { 487 int repeatCount = loader.repeatCount; 488 try { 489 outputStream.write(GIF_EXTENSION_BLOCK_ID); 490 outputStream.write(GIF_APPLICATION_EXTENSION_BLOCK_ID); 491 outputStream.write(NETSCAPE2_0.length); 492 outputStream.write(NETSCAPE2_0); 493 outputStream.write(3); outputStream.write(1); outputStream.writeShort((short) repeatCount); 496 outputStream.write(0); } catch (IOException e) { 498 SWT.error(SWT.ERROR_IO, e); 499 } 500 } 501 502 for (int frame=0; frame<frameCount; frame++) { 503 504 505 if (multi || data[frame].transparentPixel != -1) { 506 writeGraphicsControlBlock(data[frame]); 507 } 508 509 510 int x = data[frame].x; 511 int y = data[frame].y; 512 int width = data[frame].width; 513 int height = data[frame].height; 514 try { 515 outputStream.write(GIF_IMAGE_BLOCK_ID); 516 byte[] block = new byte[9]; 517 block[0] = (byte)(x & 0xFF); 518 block[1] = (byte)((x >> 8) & 0xFF); 519 block[2] = (byte)(y & 0xFF); 520 block[3] = (byte)((y >> 8) & 0xFF); 521 block[4] = (byte)(width & 0xFF); 522 block[5] = (byte)((width >> 8) & 0xFF); 523 block[6] = (byte)(height & 0xFF); 524 block[7] = (byte)((height >> 8) & 0xFF); 525 block[8] = (byte)(globalTable == 0 ? (depth-1) | 0x80 : 0x00); 526 outputStream.write(block); 527 } catch (IOException e) { 528 SWT.error(SWT.ERROR_IO, e); 529 } 530 531 532 if (globalTable == 0) { 533 writePalette(data[frame].palette, depth); 534 } 535 536 537 try { 538 outputStream.write(depth); } catch (IOException e) { 540 SWT.error(SWT.ERROR_IO, e); 541 } 542 new LZWCodec().encode(outputStream, data[frame]); 543 } 544 545 546 try { 547 outputStream.write(0x3B); 548 } catch (IOException e) { 549 SWT.error(SWT.ERROR_IO, e); 550 } 551 } 552 553 557 void writeGraphicsControlBlock(ImageData image) { 558 try { 559 outputStream.write(GIF_EXTENSION_BLOCK_ID); 560 outputStream.write(GIF_GRAPHICS_CONTROL_BLOCK_ID); 561 byte[] gcBlock = new byte[4]; 562 gcBlock[0] = 0; 563 gcBlock[1] = 0; 564 gcBlock[2] = 0; 565 gcBlock[3] = 0; 566 if (image.transparentPixel != -1) { 567 gcBlock[0] = (byte)0x01; 568 gcBlock[3] = (byte)image.transparentPixel; 569 } 570 if (image.disposalMethod != 0) { 571 gcBlock[0] |= (byte)((image.disposalMethod & 0x07) << 2); 572 } 573 if (image.delayTime != 0) { 574 gcBlock[1] = (byte)(image.delayTime & 0xFF); 575 gcBlock[2] = (byte)((image.delayTime >> 8) & 0xFF); 576 } 577 outputStream.write((byte)gcBlock.length); 578 outputStream.write(gcBlock); 579 outputStream.write(0); } catch (IOException e) { 581 SWT.error(SWT.ERROR_IO, e); 582 } 583 } 584 585 588 void writePalette(PaletteData palette, int depth) { 589 byte[] bytes = new byte[(1 << depth) * 3]; 590 int offset = 0; 591 for (int i = 0; i < palette.colors.length; i++) { 592 RGB color = palette.colors[i]; 593 bytes[offset] = (byte)color.red; 594 bytes[offset + 1] = (byte)color.green; 595 bytes[offset + 2] = (byte)color.blue; 596 offset += 3; 597 } 598 try { 599 outputStream.write(bytes); 600 } catch (IOException e) { 601 SWT.error(SWT.ERROR_IO, e); 602 } 603 } 604 } 605 | Popular Tags |