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 TIFFDirectory { 19 20 TIFFRandomFileAccess file; 21 boolean isLittleEndian; 22 ImageLoader loader; 23 int depth; 24 25 26 int imageWidth; 27 int imageLength; 28 int[] bitsPerSample; 29 int compression; 30 int photometricInterpretation; 31 int[] stripOffsets; 32 int samplesPerPixel; 33 int rowsPerStrip; 34 int[] stripByteCounts; 35 int t4Options; 36 int colorMapOffset; 37 38 39 ImageData image; 40 LEDataOutputStream out; 41 42 static final int NO_VALUE = -1; 43 44 static final short TAG_ImageWidth = 256; 45 static final short TAG_ImageLength = 257; 46 static final short TAG_BitsPerSample = 258; 47 static final short TAG_Compression = 259; 48 static final short TAG_PhotometricInterpretation = 262; 49 static final short TAG_StripOffsets = 273; 50 static final short TAG_SamplesPerPixel = 277; 51 static final short TAG_RowsPerStrip = 278; 52 static final short TAG_StripByteCounts = 279; 53 static final short TAG_XResolution = 282; 54 static final short TAG_YResolution = 283; 55 static final short TAG_T4Options = 292; 56 static final short TAG_ResolutionUnit = 296; 57 static final short TAG_ColorMap = 320; 58 59 static final int TYPE_BYTE = 1; 60 static final int TYPE_ASCII = 2; 61 static final int TYPE_SHORT = 3; 62 static final int TYPE_LONG = 4; 63 static final int TYPE_RATIONAL = 5; 64 65 66 static final int COMPRESSION_NONE = 1; 67 static final int COMPRESSION_CCITT_3_1 = 2; 68 static final int COMPRESSION_PACKBITS = 32773; 69 70 static final int IFD_ENTRY_SIZE = 12; 71 72 public TIFFDirectory(TIFFRandomFileAccess file, boolean isLittleEndian, ImageLoader loader) { 73 this.file = file; 74 this.isLittleEndian = isLittleEndian; 75 this.loader = loader; 76 } 77 78 public TIFFDirectory(ImageData image) { 79 this.image = image; 80 } 81 82 83 int decodePackBits(byte[] src, byte[] dest, int offsetDest) { 84 int destIndex = offsetDest; 85 int srcIndex = 0; 86 while (srcIndex < src.length) { 87 byte n = src[srcIndex]; 88 if (0 <= n && n <= 127) { 89 90 System.arraycopy(src, ++srcIndex, dest, destIndex, n + 1); 91 srcIndex += n + 1; 92 destIndex += n + 1; 93 } else if (-127 <= n && n <= -1) { 94 95 byte value = src[++srcIndex]; 96 for (int j = 0; j < -n + 1; j++) { 97 dest[destIndex++] = value; 98 } 99 srcIndex++; 100 } else { 101 102 srcIndex++; 103 } 104 } 105 106 return destIndex - offsetDest; 107 } 108 109 int getEntryValue(int type, byte[] buffer, int index) { 110 return toInt(buffer, index + 8, type); 111 } 112 113 void getEntryValue(int type, byte[] buffer, int index, int[] values) throws IOException { 114 int start = index + 8; 115 int size; 116 int offset = toInt(buffer, start, TYPE_LONG); 117 switch (type) { 118 case TYPE_SHORT: size = 2; break; 119 case TYPE_LONG: size = 4; break; 120 case TYPE_RATIONAL: size = 8; break; 121 case TYPE_ASCII: 122 case TYPE_BYTE: size = 1; break; 123 default: SWT.error(SWT.ERROR_UNSUPPORTED_FORMAT); return; 124 } 125 if (values.length * size > 4) { 126 buffer = new byte[values.length * size]; 127 file.seek(offset); 128 file.read(buffer); 129 start = 0; 130 } 131 for (int i = 0; i < values.length; i++) { 132 values[i] = toInt(buffer, start + i * size, type); 133 } 134 } 135 136 void decodePixels(ImageData image) throws IOException { 137 138 byte[] imageData = new byte[(imageWidth * depth + 7) / 8 * imageLength]; 139 image.data = imageData; 140 int destIndex = 0; 141 int length = stripOffsets.length; 142 for (int i = 0; i < length; i++) { 143 144 byte[] data = new byte[stripByteCounts[i]]; 145 file.seek(stripOffsets[i]); 146 file.read(data); 147 if (compression == COMPRESSION_NONE) { 148 System.arraycopy(data, 0, imageData, destIndex, data.length); 149 destIndex += data.length; 150 } else if (compression == COMPRESSION_PACKBITS) { 151 destIndex += decodePackBits(data, imageData, destIndex); 152 } else if (compression == COMPRESSION_CCITT_3_1 || compression == 3) { 153 TIFFModifiedHuffmanCodec codec = new TIFFModifiedHuffmanCodec(); 154 int nRows = rowsPerStrip; 155 if (i == length -1) { 156 int n = imageLength % rowsPerStrip; 157 if (n != 0) nRows = n; 158 } 159 destIndex += codec.decode(data, imageData, destIndex, imageWidth, nRows); 160 } 161 if (loader.hasListeners()) { 162 loader.notifyListeners(new ImageLoaderEvent(loader, image, i, i == length - 1)); 163 } 164 } 165 } 166 167 PaletteData getColorMap() throws IOException { 168 int numColors = 1 << bitsPerSample[0]; 169 170 int numBytes = 3 * 2 * numColors; 171 byte[] buffer = new byte[numBytes]; 172 file.seek(colorMapOffset); 173 file.read(buffer); 174 RGB[] colors = new RGB[numColors]; 175 184 int offset = isLittleEndian ? 1 : 0; 185 int startG = 2 * numColors; 186 int startB = startG + 2 * numColors; 187 for (int i = 0; i < numColors; i++) { 188 int r = buffer[offset] & 0xFF; 189 int g = buffer[startG + offset] & 0xFF; 190 int b = buffer[startB + offset] & 0xFF; 191 colors[i] = new RGB(r, g, b); 192 offset += 2; 193 } 194 return new PaletteData(colors); 195 } 196 197 PaletteData getGrayPalette() { 198 int numColors = 1 << bitsPerSample[0]; 199 RGB[] rgbs = new RGB[numColors]; 200 for (int i = 0; i < numColors; i++) { 201 int value = i * 0xFF / (numColors - 1); 202 if (photometricInterpretation == 0) value = 0xFF - value; 203 rgbs[i] = new RGB(value, value, value); 204 } 205 return new PaletteData(rgbs); 206 } 207 208 PaletteData getRGBPalette(int bitsR, int bitsG, int bitsB) { 209 int blueMask = 0; 210 for (int i = 0; i < bitsB; i++) { 211 blueMask |= 1 << i; 212 } 213 int greenMask = 0; 214 for (int i = bitsB; i < bitsB + bitsG; i++) { 215 greenMask |= 1 << i; 216 } 217 int redMask = 0; 218 for (int i = bitsB + bitsG; i < bitsB + bitsG + bitsR; i++) { 219 redMask |= 1 << i; 220 } 221 return new PaletteData(redMask, greenMask, blueMask); 222 } 223 224 int formatStrips(int rowByteSize, int nbrRows, byte[] data, int maxStripByteSize, int offsetPostIFD, int extraBytes, int[][] strips) { 225 234 int n, nbrRowsPerStrip; 235 if (rowByteSize > maxStripByteSize) { 236 237 n = data.length / rowByteSize; 238 nbrRowsPerStrip = 1; 239 } else { 240 int nbr = (data.length + maxStripByteSize - 1) / maxStripByteSize; 241 nbrRowsPerStrip = nbrRows / nbr; 242 n = (nbrRows + nbrRowsPerStrip - 1) / nbrRowsPerStrip; 243 } 244 int stripByteSize = rowByteSize * nbrRowsPerStrip; 245 246 int[] offsets = new int[n]; 247 int[] counts = new int[n]; 248 257 int postIFDData = n == 1 ? 0 : n * 2 * 4; 258 int startOffset = offsetPostIFD + extraBytes + postIFDData; 259 260 int offset = startOffset; 261 for (int i = 0; i < n; i++) { 262 266 offsets[i] = offset; 267 counts[i] = stripByteSize; 268 offset += stripByteSize; 269 } 270 271 int mod = data.length % stripByteSize; 272 if (mod != 0) counts[counts.length - 1] = mod; 273 274 strips[0] = offsets; 275 strips[1] = counts; 276 return nbrRowsPerStrip; 277 } 278 279 int[] formatColorMap(RGB[] rgbs) { 280 285 int[] colorMap = new int[rgbs.length * 3]; 286 int offsetGreen = rgbs.length; 287 int offsetBlue = rgbs.length * 2; 288 for (int i = 0; i < rgbs.length; i++) { 289 colorMap[i] = rgbs[i].red << 8 | rgbs[i].red; 290 colorMap[i + offsetGreen] = rgbs[i].green << 8 | rgbs[i].green; 291 colorMap[i + offsetBlue] = rgbs[i].blue << 8 | rgbs[i].blue; 292 } 293 return colorMap; 294 } 295 296 void parseEntries(byte[] buffer) throws IOException { 297 for (int offset = 0; offset < buffer.length; offset += IFD_ENTRY_SIZE) { 298 int tag = toInt(buffer, offset, TYPE_SHORT); 299 int type = toInt(buffer, offset + 2, TYPE_SHORT); 300 int count = toInt(buffer, offset + 4, TYPE_LONG); 301 switch (tag) { 302 case TAG_ImageWidth: { 303 imageWidth = getEntryValue(type, buffer, offset); 304 break; 305 } 306 case TAG_ImageLength: { 307 imageLength = getEntryValue(type, buffer, offset); 308 break; 309 } 310 case TAG_BitsPerSample: { 311 if (type != TYPE_SHORT) SWT.error(SWT.ERROR_INVALID_IMAGE); 312 bitsPerSample = new int[count]; 313 getEntryValue(type, buffer, offset, bitsPerSample); 314 break; 315 } 316 case TAG_Compression: { 317 compression = getEntryValue(type, buffer, offset); 318 break; 319 } 320 case TAG_PhotometricInterpretation: { 321 photometricInterpretation = getEntryValue(type, buffer, offset); 322 break; 323 } 324 case TAG_StripOffsets: { 325 if (type != TYPE_LONG && type != TYPE_SHORT) SWT.error(SWT.ERROR_INVALID_IMAGE); 326 stripOffsets = new int[count]; 327 getEntryValue(type, buffer, offset, stripOffsets); 328 break; 329 } 330 case TAG_SamplesPerPixel: { 331 if (type != TYPE_SHORT) SWT.error(SWT.ERROR_INVALID_IMAGE); 332 samplesPerPixel = getEntryValue(type, buffer, offset); 333 334 if (samplesPerPixel != 1 && samplesPerPixel != 3) SWT.error(SWT.ERROR_UNSUPPORTED_DEPTH); 335 break; 336 } 337 case TAG_RowsPerStrip: { 338 rowsPerStrip = getEntryValue(type, buffer, offset); 339 break; 340 } 341 case TAG_StripByteCounts: { 342 stripByteCounts = new int[count]; 343 getEntryValue(type, buffer, offset, stripByteCounts); 344 break; 345 } 346 case TAG_XResolution: { 347 348 break; 349 } 350 case TAG_YResolution: { 351 352 break; 353 } 354 case TAG_T4Options: { 355 if (type != TYPE_LONG) SWT.error(SWT.ERROR_INVALID_IMAGE); 356 t4Options = getEntryValue(type, buffer, offset); 357 if ((t4Options & 0x1) == 1) { 358 359 SWT.error(SWT.ERROR_UNSUPPORTED_FORMAT); 360 } 361 break; 362 } 363 case TAG_ResolutionUnit: { 364 365 break; 366 } 367 case TAG_ColorMap: { 368 if (type != TYPE_SHORT) SWT.error(SWT.ERROR_INVALID_IMAGE); 369 370 colorMapOffset = getEntryValue(TYPE_LONG, buffer, offset); 371 break; 372 } 373 } 374 } 375 } 376 377 public ImageData read() throws IOException { 378 379 bitsPerSample = new int[] {1}; 380 colorMapOffset = NO_VALUE; 381 compression = 1; 382 imageLength = NO_VALUE; 383 imageWidth = NO_VALUE; 384 photometricInterpretation = NO_VALUE; 385 rowsPerStrip = Integer.MAX_VALUE; 386 samplesPerPixel = 1; 387 stripByteCounts = null; 388 stripOffsets = null; 389 390 byte[] buffer = new byte[2]; 391 file.read(buffer); 392 int numberEntries = toInt(buffer, 0, TYPE_SHORT); 393 buffer = new byte[IFD_ENTRY_SIZE * numberEntries]; 394 file.read(buffer); 395 parseEntries(buffer); 396 397 PaletteData palette = null; 398 depth = 0; 399 switch (photometricInterpretation) { 400 case 0: 401 case 1: { 402 403 palette = getGrayPalette(); 404 depth = bitsPerSample[0]; 405 break; 406 } 407 case 2: { 408 409 if (colorMapOffset != NO_VALUE) SWT.error(SWT.ERROR_INVALID_IMAGE); 410 411 palette = getRGBPalette(bitsPerSample[0], bitsPerSample[1], bitsPerSample[2]); 412 depth = bitsPerSample[0] + bitsPerSample[1] + bitsPerSample[2]; 413 break; 414 } 415 case 3: { 416 417 if (colorMapOffset == NO_VALUE) SWT.error(SWT.ERROR_INVALID_IMAGE); 418 palette = getColorMap(); 419 depth = bitsPerSample[0]; 420 break; 421 } 422 default: { 423 SWT.error(SWT.ERROR_INVALID_IMAGE); 424 } 425 } 426 427 ImageData image = ImageData.internal_new( 428 imageWidth, 429 imageLength, 430 depth, 431 palette, 432 1, 433 null, 434 0, 435 null, 436 null, 437 -1, 438 -1, 439 SWT.IMAGE_TIFF, 440 0, 441 0, 442 0, 443 0); 444 decodePixels(image); 445 return image; 446 } 447 448 int toInt(byte[] buffer, int i, int type) { 449 if (type == TYPE_LONG) { 450 return isLittleEndian ? 451 (buffer[i] & 0xFF) | ((buffer[i + 1] & 0xFF) << 8) | ((buffer[i + 2] & 0xFF) << 16) | ((buffer[i + 3] & 0xFF) << 24) : 452 (buffer[i + 3] & 0xFF) | ((buffer[i + 2] & 0xFF) << 8) | ((buffer[i + 1] & 0xFF) << 16) | ((buffer[i] & 0xFF) << 24); 453 } 454 if (type == TYPE_SHORT) { 455 return isLittleEndian ? 456 (buffer[i] & 0xFF) | ((buffer[i + 1] & 0xFF) << 8) : 457 (buffer[i + 1] & 0xFF) | ((buffer[i] & 0xFF) << 8); 458 } 459 460 SWT.error(SWT.ERROR_INVALID_IMAGE); 461 return -1; 462 } 463 464 void write(int photometricInterpretation) throws IOException { 465 boolean isRGB = photometricInterpretation == 2; 466 boolean isColorMap = photometricInterpretation == 3; 467 boolean isBiLevel = photometricInterpretation == 0 || photometricInterpretation == 1; 468 469 int imageWidth = image.width; 470 int imageLength = image.height; 471 int rowByteSize = image.bytesPerLine; 472 473 int numberEntries = isBiLevel ? 9 : 11; 474 int lengthDirectory = 2 + 12 * numberEntries + 4; 475 476 int nextOffset = 8 + lengthDirectory; 477 478 479 int extraBytes = 16; 480 481 int[] colorMap = null; 482 if (isColorMap) { 483 PaletteData palette = image.palette; 484 RGB[] rgbs = palette.getRGBs(); 485 colorMap = formatColorMap(rgbs); 486 487 if (colorMap.length != 3 * 1 << image.depth) SWT.error(SWT.ERROR_UNSUPPORTED_FORMAT); 488 489 extraBytes += colorMap.length * 2; 490 } 491 if (isRGB) { 492 493 extraBytes += 6; 494 } 495 496 byte[] data = image.data; 497 int[][] strips = new int[2][]; 498 int nbrRowsPerStrip = formatStrips(rowByteSize, imageLength, data, 8192, nextOffset, extraBytes, strips); 499 int[] stripOffsets = strips[0]; 500 int[] stripByteCounts = strips[1]; 501 502 int bitsPerSampleOffset = NO_VALUE; 503 if (isRGB) { 504 bitsPerSampleOffset = nextOffset; 505 nextOffset += 6; 506 } 507 int stripOffsetsOffset = NO_VALUE, stripByteCountsOffset = NO_VALUE; 508 int xResolutionOffset, yResolutionOffset, colorMapOffset = NO_VALUE; 509 int cnt = stripOffsets.length; 510 if (cnt > 1) { 511 stripOffsetsOffset = nextOffset; 512 nextOffset += 4 * cnt; 513 stripByteCountsOffset = nextOffset; 514 nextOffset += 4 * cnt; 515 } 516 xResolutionOffset = nextOffset; 517 nextOffset += 8; 518 yResolutionOffset = nextOffset; 519 nextOffset += 8; 520 if (isColorMap) { 521 colorMapOffset = nextOffset; 522 nextOffset += colorMap.length * 2; 523 } 524 525 writeHeader(); 526 527 528 out.writeShort(numberEntries); 529 writeEntry(TAG_ImageWidth, TYPE_LONG, 1, imageWidth); 530 writeEntry(TAG_ImageLength, TYPE_LONG, 1, imageLength); 531 if (isColorMap) writeEntry(TAG_BitsPerSample, TYPE_SHORT, 1, image.depth); 532 if (isRGB) writeEntry(TAG_BitsPerSample, TYPE_SHORT, 3, bitsPerSampleOffset); 533 writeEntry(TAG_Compression, TYPE_SHORT, 1, COMPRESSION_NONE); 534 writeEntry(TAG_PhotometricInterpretation, TYPE_SHORT, 1, photometricInterpretation); 535 writeEntry(TAG_StripOffsets, TYPE_LONG, cnt, cnt > 1 ? stripOffsetsOffset : stripOffsets[0]); 536 if (isRGB) writeEntry(TAG_SamplesPerPixel, TYPE_SHORT, 1, 3); 537 writeEntry(TAG_RowsPerStrip, TYPE_LONG, 1, nbrRowsPerStrip); 538 writeEntry(TAG_StripByteCounts, TYPE_LONG, cnt, cnt > 1 ? stripByteCountsOffset : stripByteCounts[0]); 539 writeEntry(TAG_XResolution, TYPE_RATIONAL, 1, xResolutionOffset); 540 writeEntry(TAG_YResolution, TYPE_RATIONAL, 1, yResolutionOffset); 541 if (isColorMap) writeEntry(TAG_ColorMap, TYPE_SHORT, colorMap.length, colorMapOffset); 542 543 out.writeInt(0); 544 545 546 547 548 if (isRGB) for (int i = 0; i < 3; i++) out.writeShort(8); 549 if (cnt > 1) { 550 for (int i = 0; i < cnt; i++) out.writeInt(stripOffsets[i]); 551 for (int i = 0; i < cnt; i++) out.writeInt(stripByteCounts[i]); 552 } 553 554 for (int i = 0; i < 2; i++) { 555 out.writeInt(300); 556 out.writeInt(1); 557 } 558 559 if (isColorMap) for (int i = 0; i < colorMap.length; i++) out.writeShort(colorMap[i]); 560 561 562 out.write(data); 563 } 564 565 void writeEntry(short tag, int type, int count, int value) throws IOException { 566 out.writeShort(tag); 567 out.writeShort(type); 568 out.writeInt(count); 569 out.writeInt(value); 570 } 571 572 void writeHeader() throws IOException { 573 574 out.write(0x49); 575 out.write(0x49); 576 577 578 out.writeShort(42); 579 583 out.writeInt(8); 584 } 585 586 void writeToStream(LEDataOutputStream byteStream) throws IOException { 587 out = byteStream; 588 int photometricInterpretation = -1; 589 590 591 if (image.scanlinePad != 1) SWT.error(SWT.ERROR_UNSUPPORTED_FORMAT); 592 switch (image.depth) { 593 case 1: { 594 595 PaletteData palette = image.palette; 596 RGB[] rgbs = palette.colors; 597 if (palette.isDirect || rgbs == null || rgbs.length != 2) SWT.error(SWT.ERROR_UNSUPPORTED_FORMAT); 598 RGB rgb0 = rgbs[0]; 599 RGB rgb1 = rgbs[1]; 600 if (!(rgb0.red == rgb0.green && rgb0.green == rgb0.blue && 601 rgb1.red == rgb1.green && rgb1.green == rgb1.blue && 602 ((rgb0.red == 0x0 && rgb1.red == 0xFF) || (rgb0.red == 0xFF && rgb1.red == 0x0)))) { 603 SWT.error(SWT.ERROR_UNSUPPORTED_FORMAT); 604 } 605 606 photometricInterpretation = image.palette.colors[0].red == 0xFF ? 0 : 1; 607 break; 608 } 609 case 4: 610 case 8: { 611 photometricInterpretation = 3; 612 break; 613 } 614 case 24: { 615 photometricInterpretation = 2; 616 break; 617 } 618 default: { 619 SWT.error(SWT.ERROR_UNSUPPORTED_FORMAT); 620 } 621 } 622 write(photometricInterpretation); 623 } 624 625 } 626 | Popular Tags |