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 WinBMPFileFormat extends FileFormat { 19 static final int BMPFileHeaderSize = 14; 20 static final int BMPHeaderFixedSize = 40; 21 int importantColors; 22 Point pelsPerMeter = new Point(0, 0); 23 24 30 int compress(int comp, byte[] src, int srcOffset, int numBytes, byte[] dest, boolean last) { 31 if (comp == 1) { return compressRLE8Data(src, srcOffset, numBytes, dest, last); 33 } 34 if (comp == 2) { return compressRLE4Data(src, srcOffset, numBytes, dest, last); 36 } 37 SWT.error(SWT.ERROR_INVALID_IMAGE); 38 return 0; 39 } 40 int compressRLE4Data(byte[] src, int srcOffset, int numBytes, byte[] dest, boolean last) { 41 int sp = srcOffset, end = srcOffset + numBytes, dp = 0; 42 int size = 0, left, i, n; 43 byte theByte; 44 while (sp < end) { 45 46 left = end - sp - 1; 47 if (left > 127) 48 left = 127; 49 for (n = 0; n < left; n++) { 50 if (src[sp + n] == src[sp + n + 1]) 51 break; 52 } 53 54 if (n < 127 && n == left) 55 n++; 56 57 switch (n) { 58 case 0: 59 break; 60 case 1: 61 dest[dp] = 2; dp++; 62 dest[dp] = src[sp]; 63 dp++; sp++; 64 size += 2; 65 break; 66 default: 67 dest[dp] = 0; dp++; 68 dest[dp] = (byte)(n + n); dp++; 69 for (i = n; i > 0; i--) { 70 dest[dp] = src[sp]; 71 dp++; sp++; 72 } 73 size += 2 + n; 74 if ((n & 1) != 0) { 75 dest[dp] = 0; 76 dp++; 77 size++; 78 } 79 break; 80 } 81 82 left = end - sp; 83 if (left > 0) { 84 if (left > 127) 85 left = 127; 86 theByte = src[sp]; 87 for (n = 1; n < left; n++) { 88 if (src[sp + n] != theByte) 89 break; 90 } 91 dest[dp] = (byte)(n + n); dp++; 92 dest[dp] = theByte; dp++; 93 sp += n; 94 size += 2; 95 } 96 } 97 98 99 dest[dp] = 0; dp++; 100 if (last) { 101 dest[dp] = 1; dp++; 102 } else { 103 dest[dp] = 0; dp++; 104 } 105 size += 2; 106 107 return size; 108 } 109 int compressRLE8Data(byte[] src, int srcOffset, int numBytes, byte[] dest, boolean last) { 110 int sp = srcOffset, end = srcOffset + numBytes, dp = 0; 111 int size = 0, left, i, n; 112 byte theByte; 113 while (sp < end) { 114 115 left = end - sp - 1; 116 if (left > 254) 117 left = 254; 118 for (n = 0; n < left; n++) { 119 if (src[sp + n] == src[sp + n + 1]) 120 break; 121 } 122 123 if (n == left) 124 n++; 125 126 switch (n) { 127 case 0: 128 break; 129 case 2: 130 dest[dp] = 1; dp++; 131 dest[dp] = src[sp]; 132 dp++; sp++; 133 size += 2; 134 135 case 1: 136 dest[dp] = 1; dp++; 137 dest[dp] = src[sp]; 138 dp++; sp++; 139 size += 2; 140 break; 141 default: 142 dest[dp] = 0; dp++; 143 dest[dp] = (byte)n; dp++; 144 for (i = n; i > 0; i--) { 145 dest[dp] = src[sp]; 146 dp++; sp++; 147 } 148 size += 2 + n; 149 if ((n & 1) != 0) { 150 dest[dp] = 0; 151 dp++; 152 size++; 153 } 154 break; 155 } 156 157 left = end - sp; 158 if (left > 0) { 159 if (left > 255) 160 left = 255; 161 theByte = src[sp]; 162 for (n = 1; n < left; n++) { 163 if (src[sp + n] != theByte) 164 break; 165 } 166 dest[dp] = (byte)n; dp++; 167 dest[dp] = theByte; dp++; 168 sp += n; 169 size += 2; 170 } 171 } 172 173 174 dest[dp] = 0; dp++; 175 if (last) { 176 dest[dp] = 1; dp++; 177 } else { 178 dest[dp] = 0; dp++; 179 } 180 size += 2; 181 182 return size; 183 } 184 void decompressData(byte[] src, byte[] dest, int stride, int cmp) { 185 if (cmp == 1) { if (decompressRLE8Data(src, src.length, stride, dest, dest.length) <= 0) 187 SWT.error(SWT.ERROR_INVALID_IMAGE); 188 return; 189 } 190 if (cmp == 2) { if (decompressRLE4Data(src, src.length, stride, dest, dest.length) <= 0) 192 SWT.error(SWT.ERROR_INVALID_IMAGE); 193 return; 194 } 195 SWT.error(SWT.ERROR_INVALID_IMAGE); 196 } 197 int decompressRLE4Data(byte[] src, int numBytes, int stride, byte[] dest, int destSize) { 198 int sp = 0; 199 int se = numBytes; 200 int dp = 0; 201 int de = destSize; 202 int x = 0, y = 0; 203 while (sp < se) { 204 int len = src[sp] & 0xFF; 205 sp++; 206 if (len == 0) { 207 len = src[sp] & 0xFF; 208 sp++; 209 switch (len) { 210 case 0: 211 y++; 212 x = 0; 213 dp = y * stride; 214 if (dp > de) 215 return -1; 216 break; 217 case 1: 218 return 1; 219 case 2: 220 x += src[sp] & 0xFF; 221 sp++; 222 y += src[sp] & 0xFF; 223 sp++; 224 dp = y * stride + x / 2; 225 if (dp > de) 226 return -1; 227 break; 228 default: 229 if ((len & 1) != 0) 230 return -1; 231 x += len; 232 len = len / 2; 233 if (len > (se - sp)) 234 return -1; 235 if (len > (de - dp)) 236 return -1; 237 for (int i = 0; i < len; i++) { 238 dest[dp] = src[sp]; 239 dp++; 240 sp++; 241 } 242 if ((sp & 1) != 0) 243 sp++; 244 break; 245 } 246 } else { 247 if ((len & 1) != 0) 248 return -1; 249 x += len; 250 len = len / 2; 251 byte theByte = src[sp]; 252 sp++; 253 if (len > (de - dp)) 254 return -1; 255 for (int i = 0; i < len; i++) { 256 dest[dp] = theByte; 257 dp++; 258 } 259 } 260 } 261 return 1; 262 } 263 int decompressRLE8Data(byte[] src, int numBytes, int stride, byte[] dest, int destSize) { 264 int sp = 0; 265 int se = numBytes; 266 int dp = 0; 267 int de = destSize; 268 int x = 0, y = 0; 269 while (sp < se) { 270 int len = src[sp] & 0xFF; 271 sp++; 272 if (len == 0) { 273 len = src[sp] & 0xFF; 274 sp++; 275 switch (len) { 276 case 0: 277 y++; 278 x = 0; 279 dp = y * stride; 280 if (dp > de) 281 return -1; 282 break; 283 case 1: 284 return 1; 285 case 2: 286 x += src[sp] & 0xFF; 287 sp++; 288 y += src[sp] & 0xFF; 289 sp++; 290 dp = y * stride + x; 291 if (dp > de) 292 return -1; 293 break; 294 default: 295 if (len > (se - sp)) 296 return -1; 297 if (len > (de - dp)) 298 return -1; 299 for (int i = 0; i < len; i++) { 300 dest[dp] = src[sp]; 301 dp++; 302 sp++; 303 } 304 if ((sp & 1) != 0) 305 sp++; 306 x += len; 307 break; 308 } 309 } else { 310 byte theByte = src[sp]; 311 sp++; 312 if (len > (de - dp)) 313 return -1; 314 for (int i = 0; i < len; i++) { 315 dest[dp] = theByte; 316 dp++; 317 } 318 x += len; 319 } 320 } 321 return 1; 322 } 323 boolean isFileFormat(LEDataInputStream stream) { 324 try { 325 byte[] header = new byte[18]; 326 stream.read(header); 327 stream.unread(header); 328 int infoHeaderSize = (header[14] & 0xFF) | ((header[15] & 0xFF) << 8) | ((header[16] & 0xFF) << 16) | ((header[17] & 0xFF) << 24); 329 return header[0] == 0x42 && header[1] == 0x4D && infoHeaderSize >= BMPHeaderFixedSize; 330 } catch (Exception e) { 331 return false; 332 } 333 } 334 byte[] loadData(byte[] infoHeader) { 335 int width = (infoHeader[4] & 0xFF) | ((infoHeader[5] & 0xFF) << 8) | ((infoHeader[6] & 0xFF) << 16) | ((infoHeader[7] & 0xFF) << 24); 336 int height = (infoHeader[8] & 0xFF) | ((infoHeader[9] & 0xFF) << 8) | ((infoHeader[10] & 0xFF) << 16) | ((infoHeader[11] & 0xFF) << 24); 337 int bitCount = (infoHeader[14] & 0xFF) | ((infoHeader[15] & 0xFF) << 8); 338 int stride = (width * bitCount + 7) / 8; 339 stride = (stride + 3) / 4 * 4; byte[] data = loadData(infoHeader, stride); 341 flipScanLines(data, stride, height); 342 return data; 343 } 344 byte[] loadData(byte[] infoHeader, int stride) { 345 int height = (infoHeader[8] & 0xFF) | ((infoHeader[9] & 0xFF) << 8) | ((infoHeader[10] & 0xFF) << 16) | ((infoHeader[11] & 0xFF) << 24); 346 if (height < 0) height = -height; 347 int dataSize = height * stride; 348 byte[] data = new byte[dataSize]; 349 int cmp = (infoHeader[16] & 0xFF) | ((infoHeader[17] & 0xFF) << 8) | ((infoHeader[18] & 0xFF) << 16) | ((infoHeader[19] & 0xFF) << 24); 350 if (cmp == 0 || cmp == 3) { try { 352 if (inputStream.read(data) != dataSize) 353 SWT.error(SWT.ERROR_INVALID_IMAGE); 354 } catch (IOException e) { 355 SWT.error(SWT.ERROR_IO, e); 356 } 357 } else { 358 int compressedSize = (infoHeader[20] & 0xFF) | ((infoHeader[21] & 0xFF) << 8) | ((infoHeader[22] & 0xFF) << 16) | ((infoHeader[23] & 0xFF) << 24); 359 byte[] compressed = new byte[compressedSize]; 360 try { 361 if (inputStream.read(compressed) != compressedSize) 362 SWT.error(SWT.ERROR_INVALID_IMAGE); 363 } catch (IOException e) { 364 SWT.error(SWT.ERROR_IO, e); 365 } 366 decompressData(compressed, data, stride, cmp); 367 } 368 return data; 369 } 370 int[] loadFileHeader() { 371 int[] header = new int[5]; 372 try { 373 header[0] = inputStream.readShort(); 374 header[1] = inputStream.readInt(); 375 header[2] = inputStream.readShort(); 376 header[3] = inputStream.readShort(); 377 header[4] = inputStream.readInt(); 378 } catch (IOException e) { 379 SWT.error(SWT.ERROR_IO, e); 380 } 381 if (header[0] != 0x4D42) 382 SWT.error(SWT.ERROR_INVALID_IMAGE); 383 return header; 384 } 385 ImageData[] loadFromByteStream() { 386 int[] fileHeader = loadFileHeader(); 387 byte[] infoHeader = new byte[BMPHeaderFixedSize]; 388 try { 389 inputStream.read(infoHeader); 390 } catch (Exception e) { 391 SWT.error(SWT.ERROR_IO, e); 392 } 393 int width = (infoHeader[4] & 0xFF) | ((infoHeader[5] & 0xFF) << 8) | ((infoHeader[6] & 0xFF) << 16) | ((infoHeader[7] & 0xFF) << 24); 394 int height = (infoHeader[8] & 0xFF) | ((infoHeader[9] & 0xFF) << 8) | ((infoHeader[10] & 0xFF) << 16) | ((infoHeader[11] & 0xFF) << 24); 395 if (height < 0) height = -height; 396 int bitCount = (infoHeader[14] & 0xFF) | ((infoHeader[15] & 0xFF) << 8); 397 this.compression = (infoHeader[16] & 0xFF) | ((infoHeader[17] & 0xFF) << 8) | ((infoHeader[18] & 0xFF) << 16) | ((infoHeader[19] & 0xFF) << 24); 398 PaletteData palette = loadPalette(infoHeader); 399 if (inputStream.getPosition() < fileHeader[4]) { 400 try { 402 inputStream.skip(fileHeader[4] - inputStream.getPosition()); 403 } catch (IOException e) { 404 SWT.error(SWT.ERROR_IO, e); 405 } 406 } 407 byte[] data = loadData(infoHeader); 408 this.importantColors = (infoHeader[36] & 0xFF) | ((infoHeader[37] & 0xFF) << 8) | ((infoHeader[38] & 0xFF) << 16) | ((infoHeader[39] & 0xFF) << 24); 409 int xPelsPerMeter = (infoHeader[24] & 0xFF) | ((infoHeader[25] & 0xFF) << 8) | ((infoHeader[26] & 0xFF) << 16) | ((infoHeader[27] & 0xFF) << 24); 410 int yPelsPerMeter = (infoHeader[28] & 0xFF) | ((infoHeader[29] & 0xFF) << 8) | ((infoHeader[30] & 0xFF) << 16) | ((infoHeader[31] & 0xFF) << 24); 411 this.pelsPerMeter = new Point(xPelsPerMeter, yPelsPerMeter); 412 int type = (this.compression == 1 ) || (this.compression == 2 ) ? SWT.IMAGE_BMP_RLE : SWT.IMAGE_BMP; 413 return new ImageData[] { 414 ImageData.internal_new( 415 width, 416 height, 417 bitCount, 418 palette, 419 4, 420 data, 421 0, 422 null, 423 null, 424 -1, 425 -1, 426 type, 427 0, 428 0, 429 0, 430 0) 431 }; 432 } 433 PaletteData loadPalette(byte[] infoHeader) { 434 int depth = (infoHeader[14] & 0xFF) | ((infoHeader[15] & 0xFF) << 8); 435 if (depth <= 8) { 436 int numColors = (infoHeader[32] & 0xFF) | ((infoHeader[33] & 0xFF) << 8) | ((infoHeader[34] & 0xFF) << 16) | ((infoHeader[35] & 0xFF) << 24); 437 if (numColors == 0) { 438 numColors = 1 << depth; 439 } else { 440 if (numColors > 256) 441 numColors = 256; 442 } 443 byte[] buf = new byte[numColors * 4]; 444 try { 445 if (inputStream.read(buf) != buf.length) 446 SWT.error(SWT.ERROR_INVALID_IMAGE); 447 } catch (IOException e) { 448 SWT.error(SWT.ERROR_IO, e); 449 } 450 return paletteFromBytes(buf, numColors); 451 } 452 if (depth == 16) { 453 if (this.compression == 3) { 454 try { 455 return new PaletteData(inputStream.readInt(), inputStream.readInt(), inputStream.readInt()); 456 } catch (IOException e) { 457 SWT.error(SWT.ERROR_IO, e); 458 } 459 } 460 return new PaletteData(0x7C00, 0x3E0, 0x1F); 461 } 462 if (depth == 24) return new PaletteData(0xFF, 0xFF00, 0xFF0000); 463 if (this.compression == 3) { 464 try { 465 return new PaletteData(inputStream.readInt(), inputStream.readInt(), inputStream.readInt()); 466 } catch (IOException e) { 467 SWT.error(SWT.ERROR_IO, e); 468 } 469 } 470 return new PaletteData(0xFF00, 0xFF0000, 0xFF000000); 471 } 472 PaletteData paletteFromBytes(byte[] bytes, int numColors) { 473 int bytesOffset = 0; 474 RGB[] colors = new RGB[numColors]; 475 for (int i = 0; i < numColors; i++) { 476 colors[i] = new RGB(bytes[bytesOffset + 2] & 0xFF, 477 bytes[bytesOffset + 1] & 0xFF, 478 bytes[bytesOffset] & 0xFF); 479 bytesOffset += 4; 480 } 481 return new PaletteData(colors); 482 } 483 487 static byte[] paletteToBytes(PaletteData pal) { 488 int n = pal.colors == null ? 0 : (pal.colors.length < 256 ? pal.colors.length : 256); 489 byte[] bytes = new byte[n * 4]; 490 int offset = 0; 491 for (int i = 0; i < n; i++) { 492 RGB col = pal.colors[i]; 493 bytes[offset] = (byte)col.blue; 494 bytes[offset + 1] = (byte)col.green; 495 bytes[offset + 2] = (byte)col.red; 496 offset += 4; 497 } 498 return bytes; 499 } 500 505 int unloadData(ImageData image, OutputStream out, int comp) { 506 int totalSize = 0; 507 try { 508 if (comp == 0) 509 return unloadDataNoCompression(image, out); 510 int bpl = (image.width * image.depth + 7) / 8; 511 int bmpBpl = (bpl + 3) / 4 * 4; int imageBpl = image.bytesPerLine; 513 byte[] buf = new byte[bmpBpl * 2]; 515 int srcOffset = imageBpl * (image.height - 1); byte[] data = image.data; 517 totalSize = 0; 518 byte[] buf2 = new byte[32768]; 519 int buf2Offset = 0; 520 for (int y = image.height - 1; y >= 0; y--) { 521 int lineSize = compress(comp, data, srcOffset, bpl, buf, y == 0); 522 if (buf2Offset + lineSize > buf2.length) { 523 out.write(buf2, 0, buf2Offset); 524 buf2Offset = 0; 525 } 526 System.arraycopy(buf, 0, buf2, buf2Offset, lineSize); 527 buf2Offset += lineSize; 528 totalSize += lineSize; 529 srcOffset -= imageBpl; 530 } 531 if (buf2Offset > 0) 532 out.write(buf2, 0, buf2Offset); 533 } catch (IOException e) { 534 SWT.error(SWT.ERROR_IO, e); 535 } 536 return totalSize; 537 } 538 543 int unloadDataNoCompression(ImageData image, OutputStream out) { 544 int bmpBpl = 0; 545 try { 546 int bpl = (image.width * image.depth + 7) / 8; 547 bmpBpl = (bpl + 3) / 4 * 4; int linesPerBuf = 32678 / bmpBpl; 549 byte[] buf = new byte[linesPerBuf * bmpBpl]; 550 byte[] data = image.data; 551 int imageBpl = image.bytesPerLine; 552 int dataIndex = imageBpl * (image.height - 1); if (image.depth == 16) { 554 for (int y = 0; y < image.height; y += linesPerBuf) { 555 int count = image.height - y; 556 if (linesPerBuf < count) count = linesPerBuf; 557 int bufOffset = 0; 558 for (int i = 0; i < count; i++) { 559 for (int wIndex = 0; wIndex < bpl; wIndex += 2) { 560 buf[bufOffset + wIndex + 1] = data[dataIndex + wIndex + 1]; 561 buf[bufOffset + wIndex] = data[dataIndex + wIndex]; 562 } 563 bufOffset += bmpBpl; 564 dataIndex -= imageBpl; 565 } 566 out.write(buf, 0, bufOffset); 567 } 568 } else { 569 for (int y = 0; y < image.height; y += linesPerBuf) { 570 int tmp = image.height - y; 571 int count = tmp < linesPerBuf ? tmp : linesPerBuf; 572 int bufOffset = 0; 573 for (int i = 0; i < count; i++) { 574 System.arraycopy(data, dataIndex, buf, bufOffset, bpl); 575 bufOffset += bmpBpl; 576 dataIndex -= imageBpl; 577 } 578 out.write(buf, 0, bufOffset); 579 } 580 } 581 } catch (IOException e) { 582 SWT.error(SWT.ERROR_IO, e); 583 } 584 return bmpBpl * image.height; 585 } 586 590 void unloadIntoByteStream(ImageLoader loader) { 591 ImageData image = loader.data[0]; 592 byte[] rgbs; 593 int numCols; 594 if (!((image.depth == 1) || (image.depth == 4) || (image.depth == 8) || 595 (image.depth == 16) || (image.depth == 24) || (image.depth == 32))) 596 SWT.error(SWT.ERROR_UNSUPPORTED_DEPTH); 597 int comp = this.compression; 598 if (!((comp == 0) || ((comp == 1) && (image.depth == 8)) || 599 ((comp == 2) && (image.depth == 4)))) 600 SWT.error(SWT.ERROR_INVALID_IMAGE); 601 PaletteData pal = image.palette; 602 if ((image.depth == 16) || (image.depth == 24) || (image.depth == 32)) { 603 if (!pal.isDirect) 604 SWT.error(SWT.ERROR_INVALID_IMAGE); 605 numCols = 0; 606 rgbs = null; 607 } else { 608 if (pal.isDirect) 609 SWT.error(SWT.ERROR_INVALID_IMAGE); 610 numCols = pal.colors.length; 611 rgbs = paletteToBytes(pal); 612 } 613 int headersSize = BMPFileHeaderSize + BMPHeaderFixedSize; 615 int[] fileHeader = new int[5]; 616 fileHeader[0] = 0x4D42; fileHeader[1] = 0; fileHeader[2] = 0; fileHeader[3] = 0; fileHeader[4] = headersSize; if (rgbs != null) { 622 fileHeader[4] += rgbs.length; 623 } 624 625 ByteArrayOutputStream out = new ByteArrayOutputStream(); 628 unloadData(image, out, comp); 629 byte[] data = out.toByteArray(); 630 631 fileHeader[1] = fileHeader[4] + data.length; 633 634 try { 636 outputStream.writeShort(fileHeader[0]); 637 outputStream.writeInt(fileHeader[1]); 638 outputStream.writeShort(fileHeader[2]); 639 outputStream.writeShort(fileHeader[3]); 640 outputStream.writeInt(fileHeader[4]); 641 } catch (IOException e) { 642 SWT.error(SWT.ERROR_IO, e); 643 } 644 try { 645 outputStream.writeInt(BMPHeaderFixedSize); 646 outputStream.writeInt(image.width); 647 outputStream.writeInt(image.height); 648 outputStream.writeShort(1); 649 outputStream.writeShort((short)image.depth); 650 outputStream.writeInt(comp); 651 outputStream.writeInt(data.length); 652 outputStream.writeInt(pelsPerMeter.x); 653 outputStream.writeInt(pelsPerMeter.y); 654 outputStream.writeInt(numCols); 655 outputStream.writeInt(importantColors); 656 } catch (IOException e) { 657 SWT.error(SWT.ERROR_IO, e); 658 } 659 660 if (numCols > 0) { 662 try { 663 outputStream.write(rgbs); 664 } catch (IOException e) { 665 SWT.error(SWT.ERROR_IO, e); 666 } 667 } 668 669 try { 671 outputStream.write(data); 672 } catch (IOException e) { 673 SWT.error(SWT.ERROR_IO, e); 674 } 675 } 676 void flipScanLines(byte[] data, int stride, int height) { 677 int i1 = 0; 678 int i2 = (height - 1) * stride; 679 for (int i = 0; i < height / 2; i++) { 680 for (int index = 0; index < stride; index++) { 681 byte b = data[index + i1]; 682 data[index + i1] = data[index + i2]; 683 data[index + i2] = b; 684 } 685 i1 += stride; 686 i2 -= stride; 687 } 688 } 689 690 } 691 | Popular Tags |