1 7 8 package com.sun.imageio.plugins.bmp; 9 10 import java.awt.Point ; 11 import java.awt.Rectangle ; 12 import java.awt.image.ColorModel ; 13 import java.awt.image.ComponentSampleModel ; 14 import java.awt.image.DataBuffer ; 15 import java.awt.image.DataBufferByte ; 16 import java.awt.image.DataBufferInt ; 17 import java.awt.image.DataBufferShort ; 18 import java.awt.image.DataBufferUShort ; 19 import java.awt.image.IndexColorModel ; 20 import java.awt.image.MultiPixelPackedSampleModel ; 21 import java.awt.image.BandedSampleModel ; 22 import java.awt.image.Raster ; 23 import java.awt.image.RenderedImage ; 24 import java.awt.image.SampleModel ; 25 import java.awt.image.SinglePixelPackedSampleModel ; 26 import java.awt.image.WritableRaster ; 27 import java.awt.image.BufferedImage ; 28 29 import java.io.IOException ; 30 import java.io.ByteArrayOutputStream ; 31 import java.nio.ByteOrder ; 32 import java.util.Iterator ; 33 34 import javax.imageio.IIOImage ; 35 import javax.imageio.IIOException ; 36 import javax.imageio.ImageIO ; 37 import javax.imageio.ImageTypeSpecifier ; 38 import javax.imageio.ImageWriteParam ; 39 import javax.imageio.ImageWriter ; 40 import javax.imageio.metadata.IIOMetadata ; 41 import javax.imageio.metadata.IIOMetadataNode ; 42 import javax.imageio.metadata.IIOMetadataFormatImpl ; 43 import javax.imageio.metadata.IIOInvalidTreeException ; 44 import javax.imageio.spi.ImageWriterSpi ; 45 import javax.imageio.stream.ImageOutputStream ; 46 import javax.imageio.event.IIOWriteProgressListener ; 47 import javax.imageio.event.IIOWriteWarningListener ; 48 49 import org.w3c.dom.Node ; 50 import org.w3c.dom.NodeList ; 51 52 import javax.imageio.plugins.bmp.BMPImageWriteParam ; 53 import com.sun.imageio.plugins.common.ImageUtil; 54 import com.sun.imageio.plugins.common.I18N; 55 56 65 public class BMPImageWriter extends ImageWriter implements BMPConstants { 66 67 private ImageOutputStream stream = null; 68 private ByteArrayOutputStream embedded_stream = null; 69 private int version; 70 private int compressionType; 71 private boolean isTopDown; 72 private int w, h; 73 private int compImageSize = 0; 74 private int[] bitPos; 75 private byte[] bpixels; 76 private short[] spixels; 77 private int[] ipixels; 78 79 82 public BMPImageWriter(ImageWriterSpi originator) { 83 super(originator); 84 } 85 86 public void setOutput(Object output) { 87 super.setOutput(output); if (output != null) { 89 if (!(output instanceof ImageOutputStream )) 90 throw new IllegalArgumentException (I18N.getString("BMPImageWriter0")); 91 this.stream = (ImageOutputStream )output; 92 stream.setByteOrder(ByteOrder.LITTLE_ENDIAN); 93 } else 94 this.stream = null; 95 } 96 97 public ImageWriteParam getDefaultWriteParam() { 98 return new BMPImageWriteParam (); 99 } 100 101 public IIOMetadata getDefaultStreamMetadata(ImageWriteParam param) { 102 return null; 103 } 104 105 public IIOMetadata getDefaultImageMetadata(ImageTypeSpecifier imageType, 106 ImageWriteParam param) { 107 BMPMetadata meta = new BMPMetadata(); 108 meta.bmpVersion = VERSION_3; 109 meta.compression = getPreferredCompressionType(imageType); 110 if (param != null 111 && param.getCompressionMode() == ImageWriteParam.MODE_EXPLICIT) { 112 meta.compression = getCompressionType(param.getCompressionType()); 113 } 114 meta.bitsPerPixel = (short)imageType.getColorModel().getPixelSize(); 115 return meta; 116 } 117 118 public IIOMetadata convertStreamMetadata(IIOMetadata inData, 119 ImageWriteParam param) { 120 return null; 121 } 122 123 public IIOMetadata convertImageMetadata(IIOMetadata metadata, 124 ImageTypeSpecifier type, 125 ImageWriteParam param) { 126 return null; 127 } 128 129 public boolean canWriteRasters() { 130 return true; 131 } 132 133 public void write(IIOMetadata streamMetadata, 134 IIOImage image, 135 ImageWriteParam param) throws IOException { 136 137 if (stream == null) { 138 throw new IllegalStateException (I18N.getString("BMPImageWriter7")); 139 } 140 141 if (image == null) { 142 throw new IllegalArgumentException (I18N.getString("BMPImageWriter8")); 143 } 144 145 clearAbortRequest(); 146 processImageStarted(0); 147 if (param == null) 148 param = getDefaultWriteParam(); 149 150 BMPImageWriteParam bmpParam = (BMPImageWriteParam )param; 151 152 int bitsPerPixel = 24; 154 boolean isPalette = false; 155 int paletteEntries = 0; 156 IndexColorModel icm = null; 157 158 RenderedImage input = null; 159 Raster inputRaster = null; 160 boolean writeRaster = image.hasRaster(); 161 Rectangle sourceRegion = param.getSourceRegion(); 162 SampleModel sampleModel = null; 163 ColorModel colorModel = null; 164 165 compImageSize = 0; 166 167 if (writeRaster) { 168 inputRaster = image.getRaster(); 169 sampleModel = inputRaster.getSampleModel(); 170 colorModel = ImageUtil.createColorModel(null, sampleModel); 171 if (sourceRegion == null) 172 sourceRegion = inputRaster.getBounds(); 173 else 174 sourceRegion = sourceRegion.intersection(inputRaster.getBounds()); 175 } else { 176 input = image.getRenderedImage(); 177 sampleModel = input.getSampleModel(); 178 colorModel = input.getColorModel(); 179 Rectangle rect = new Rectangle (input.getMinX(), input.getMinY(), 180 input.getWidth(), input.getHeight()); 181 if (sourceRegion == null) 182 sourceRegion = rect; 183 else 184 sourceRegion = sourceRegion.intersection(rect); 185 } 186 187 IIOMetadata imageMetadata = image.getMetadata(); 188 BMPMetadata bmpImageMetadata = null; 189 if (imageMetadata != null 190 && imageMetadata instanceof BMPMetadata) 191 { 192 bmpImageMetadata = (BMPMetadata)imageMetadata; 193 } else { 194 ImageTypeSpecifier imageType = 195 new ImageTypeSpecifier (colorModel, sampleModel); 196 197 bmpImageMetadata = (BMPMetadata)getDefaultImageMetadata(imageType, 198 param); 199 } 200 201 if (sourceRegion.isEmpty()) 202 throw new RuntimeException (I18N.getString("BMPImageWrite0")); 203 204 int scaleX = param.getSourceXSubsampling(); 205 int scaleY = param.getSourceYSubsampling(); 206 int xOffset = param.getSubsamplingXOffset(); 207 int yOffset = param.getSubsamplingYOffset(); 208 209 int dataType = sampleModel.getDataType(); 211 212 sourceRegion.translate(xOffset, yOffset); 213 sourceRegion.width -= xOffset; 214 sourceRegion.height -= yOffset; 215 216 int minX = sourceRegion.x / scaleX; 217 int minY = sourceRegion.y / scaleY; 218 w = (sourceRegion.width + scaleX - 1) / scaleX; 219 h = (sourceRegion.height + scaleY - 1) / scaleY; 220 xOffset = sourceRegion.x % scaleX; 221 yOffset = sourceRegion.y % scaleY; 222 223 Rectangle destinationRegion = new Rectangle (minX, minY, w, h); 224 boolean noTransform = destinationRegion.equals(sourceRegion); 225 226 int[] sourceBands = param.getSourceBands(); 228 boolean noSubband = true; 229 int numBands = sampleModel.getNumBands(); 230 231 if (sourceBands != null) { 232 sampleModel = sampleModel.createSubsetSampleModel(sourceBands); 233 colorModel = null; 234 noSubband = false; 235 numBands = sampleModel.getNumBands(); 236 } else { 237 sourceBands = new int[numBands]; 238 for (int i = 0; i < numBands; i++) 239 sourceBands[i] = i; 240 } 241 242 int[] bandOffsets = null; 243 boolean bgrOrder = true; 244 245 if (sampleModel instanceof ComponentSampleModel ) { 246 bandOffsets = ((ComponentSampleModel )sampleModel).getBandOffsets(); 247 if (sampleModel instanceof BandedSampleModel ) { 248 bgrOrder = false; 251 } else { 252 for (int i = 0; i < bandOffsets.length; i++) 256 bgrOrder &= bandOffsets[i] == bandOffsets.length - i -1; 257 } 258 } else { 259 bandOffsets = new int[numBands]; 260 for (int i = 0; i < numBands; i++) 261 bandOffsets[i] = i; 262 } 263 264 if (bgrOrder 268 && sampleModel instanceof SinglePixelPackedSampleModel ) { 269 int[] bitOffsets = ((SinglePixelPackedSampleModel )sampleModel).getBitOffsets(); 270 for (int i=0; i<bitOffsets.length-1; i++) { 271 bgrOrder &= bitOffsets[i] > bitOffsets[i+1]; 272 } 273 } 274 275 noTransform &= bgrOrder; 276 277 int sampleSize[] = sampleModel.getSampleSize(); 278 279 281 int destScanlineBytes = w * numBands; 283 284 switch(bmpParam.getCompressionMode()) { 285 case ImageWriteParam.MODE_EXPLICIT: 286 compressionType = getCompressionType(bmpParam.getCompressionType()); 287 break; 288 case ImageWriteParam.MODE_COPY_FROM_METADATA: 289 compressionType = bmpImageMetadata.compression; 290 break; 291 case ImageWriteParam.MODE_DEFAULT: 292 compressionType = getPreferredCompressionType(colorModel, sampleModel); 293 break; 294 default: 295 compressionType = BI_RGB; 297 } 298 299 if (!canEncodeImage(compressionType, colorModel, sampleModel)) { 300 throw new IOException ("Image can not be encoded with compression type " 301 + compressionTypeNames[compressionType]); 302 } 303 304 byte r[] = null, g[] = null, b[] = null, a[] = null; 305 306 if (colorModel instanceof IndexColorModel ) { 307 isPalette = true; 308 icm = (IndexColorModel )colorModel; 309 paletteEntries = icm.getMapSize(); 310 311 if (paletteEntries <= 2) { 312 bitsPerPixel = 1; 313 destScanlineBytes = w + 7 >> 3; 314 } else if (paletteEntries <= 16) { 315 bitsPerPixel = 4; 316 destScanlineBytes = w + 1 >> 1; 317 } else if (paletteEntries <= 256) { 318 bitsPerPixel = 8; 319 } else { 320 bitsPerPixel = 24; 323 isPalette = false; 324 paletteEntries = 0; 325 destScanlineBytes = w * 3; 326 } 327 328 if (isPalette == true) { 329 r = new byte[paletteEntries]; 330 g = new byte[paletteEntries]; 331 b = new byte[paletteEntries]; 332 a = new byte[paletteEntries]; 333 334 icm.getAlphas(a); 335 icm.getReds(r); 336 icm.getGreens(g); 337 icm.getBlues(b); 338 } 339 340 } else { 341 if (numBands == 1) { 343 344 isPalette = true; 345 paletteEntries = 256; 346 bitsPerPixel = sampleSize[0]; 347 348 destScanlineBytes = (w * bitsPerPixel + 7 >> 3); 349 350 r = new byte[256]; 351 g = new byte[256]; 352 b = new byte[256]; 353 a = new byte[256]; 354 355 for (int i = 0; i < 256; i++) { 356 r[i] = (byte)i; 357 g[i] = (byte)i; 358 b[i] = (byte)i; 359 a[i] = (byte)255; 360 } 361 362 } else { 363 if (sampleModel instanceof SinglePixelPackedSampleModel && 364 noSubband) { 365 bitsPerPixel = 366 DataBuffer.getDataTypeSize(sampleModel.getDataType()); 367 destScanlineBytes = w * bitsPerPixel + 7 >> 3; 368 369 if (compressionType == BMPConstants.BI_BITFIELDS) { 370 isPalette = true; 371 paletteEntries = 3; 372 r = new byte[paletteEntries]; 373 g = new byte[paletteEntries]; 374 b = new byte[paletteEntries]; 375 a = new byte[paletteEntries]; 376 if (bitsPerPixel == 16) { 377 b[0]=(byte)0x00; g[0]=(byte)0x00; r[0]=(byte)0xF8; a[0]=(byte)0x00; b[1]=(byte)0x00; g[1]=(byte)0x00; r[1]=(byte)0x07; a[1]=(byte)0xE0; b[2]=(byte)0x00; g[2]=(byte)0x00; r[2]=(byte)0x00; a[2]=(byte)0x1F; } else if (bitsPerPixel == 32) { 381 b[0]=(byte)0x00; g[0]=(byte)0xFF; r[0]=(byte)0x00; a[0]=(byte)0x00; b[1]=(byte)0x00; g[1]=(byte)0x00; r[1]=(byte)0xFF; a[1]=(byte)0x00; b[2]=(byte)0x00; g[2]=(byte)0x00; r[2]=(byte)0x00; a[2]=(byte)0xFF; } else { 385 throw new RuntimeException (I18N.getString("BMPImageWrite6")); 386 } 387 } 388 } 389 } 390 } 391 392 int fileSize = 0; 394 int offset = 0; 395 int headerSize = 0; 396 int imageSize = 0; 397 int xPelsPerMeter = 0; 398 int yPelsPerMeter = 0; 399 int colorsUsed = 0; 400 int colorsImportant = paletteEntries; 401 402 int padding = destScanlineBytes % 4; 404 if (padding != 0) { 405 padding = 4 - padding; 406 } 407 408 if (sampleModel instanceof SinglePixelPackedSampleModel && noSubband) { 409 destScanlineBytes = w; 410 bitPos = 411 ((SinglePixelPackedSampleModel )sampleModel).getBitMasks(); 412 for (int i = 0; i < bitPos.length; i++) 413 bitPos[i] = firstLowBit(bitPos[i]); 414 } 415 416 offset = 54 + paletteEntries * 4; 419 420 imageSize = (destScanlineBytes + padding) * h; 421 fileSize = imageSize + offset; 422 headerSize = 40; 423 424 long headPos = stream.getStreamPosition(); 425 426 writeFileHeader(fileSize, offset); 427 428 writeInfoHeader(headerSize, bitsPerPixel); 429 430 stream.writeInt(compressionType); 432 433 stream.writeInt(imageSize); 435 436 stream.writeInt(xPelsPerMeter); 438 439 stream.writeInt(yPelsPerMeter); 441 442 stream.writeInt(colorsUsed); 444 445 stream.writeInt(colorsImportant); 447 448 if (isPalette == true) { 450 451 if (compressionType == BMPConstants.BI_BITFIELDS) { 453 for (int i=0; i<3; i++) { 455 int mask = (a[i]&0xFF) + ((r[i]&0xFF)*0x100) + ((g[i]&0xFF)*0x10000) + ((b[i]&0xFF)*0x1000000); 456 stream.writeInt(mask); 457 } 458 } else { 459 for (int i=0; i<paletteEntries; i++) { 460 stream.writeByte(b[i]); 461 stream.writeByte(g[i]); 462 stream.writeByte(r[i]); 463 stream.writeByte(a[i]); 464 } 465 } 466 } 467 468 int scanlineBytes = w * numBands; 470 471 int[] pixels = new int[scanlineBytes * scaleX]; 473 474 bpixels = new byte[destScanlineBytes]; 477 478 int l; 479 480 if (compressionType == BMPConstants.BI_JPEG || 481 compressionType == BMPConstants.BI_PNG) { 482 483 embedded_stream = new ByteArrayOutputStream (); 485 writeEmbedded(image, bmpParam); 486 embedded_stream.flush(); 488 imageSize = embedded_stream.size(); 489 490 long endPos = stream.getStreamPosition(); 491 fileSize = (int)(offset + imageSize); 492 stream.seek(headPos); 493 writeSize(fileSize, 2); 494 stream.seek(headPos); 495 writeSize(imageSize, 34); 496 stream.seek(endPos); 497 stream.write(embedded_stream.toByteArray()); 498 embedded_stream = null; 499 500 if (abortRequested()) { 501 processWriteAborted(); 502 } else { 503 processImageComplete(); 504 stream.flushBefore(stream.getStreamPosition()); 505 } 506 507 return; 508 } 509 510 isTopDown = bmpParam.isTopDown(); 511 512 int maxBandOffset = bandOffsets[0]; 513 for (int i = 1; i < bandOffsets.length; i++) 514 if (bandOffsets[i] > maxBandOffset) 515 maxBandOffset = bandOffsets[i]; 516 517 int[] pixel = new int[maxBandOffset + 1]; 518 519 for (int i = 0; i < h; i++) { 520 if (abortRequested()) { 521 break; 522 } 523 524 int row = minY + i; 525 526 if (!isTopDown) 527 row = minY + h - i -1; 528 529 Raster src = inputRaster; 531 532 Rectangle srcRect = 533 new Rectangle (minX * scaleX + xOffset, 534 row * scaleY + yOffset, 535 (w - 1)* scaleX + 1, 536 1); 537 if (!writeRaster) 538 src = input.getData(srcRect); 539 540 if (noTransform && noSubband) { 541 SampleModel sm = src.getSampleModel(); 542 int pos = 0; 543 int startX = srcRect.x - src.getSampleModelTranslateX(); 544 int startY = srcRect.y - src.getSampleModelTranslateY(); 545 if (sm instanceof ComponentSampleModel ) { 546 ComponentSampleModel csm = (ComponentSampleModel )sm; 547 pos = csm.getOffset(startX, startY, 0); 548 for(int nb=1; nb < csm.getNumBands(); nb++) { 549 if (pos > csm.getOffset(startX, startY, nb)) { 550 pos = csm.getOffset(startX, startY, nb); 551 } 552 } 553 } else if (sm instanceof MultiPixelPackedSampleModel ) { 554 MultiPixelPackedSampleModel mppsm = 555 (MultiPixelPackedSampleModel )sm; 556 pos = mppsm.getOffset(startX, startY); 557 } else if (sm instanceof SinglePixelPackedSampleModel ) { 558 SinglePixelPackedSampleModel sppsm = 559 (SinglePixelPackedSampleModel )sm; 560 pos = sppsm.getOffset(startX, startY); 561 } 562 563 if (compressionType == BMPConstants.BI_RGB || compressionType == BMPConstants.BI_BITFIELDS){ 564 switch(dataType) { 565 case DataBuffer.TYPE_BYTE: 566 byte[] bdata = 567 ((DataBufferByte )src.getDataBuffer()).getData(); 568 stream.write(bdata, pos, destScanlineBytes); 569 break; 570 571 case DataBuffer.TYPE_SHORT: 572 short[] sdata = 573 ((DataBufferShort )src.getDataBuffer()).getData(); 574 stream.writeShorts(sdata, pos, destScanlineBytes); 575 break; 576 577 case DataBuffer.TYPE_USHORT: 578 short[] usdata = 579 ((DataBufferUShort )src.getDataBuffer()).getData(); 580 stream.writeShorts(usdata, pos, destScanlineBytes); 581 break; 582 583 case DataBuffer.TYPE_INT: 584 int[] idata = 585 ((DataBufferInt )src.getDataBuffer()).getData(); 586 stream.writeInts(idata, pos, destScanlineBytes); 587 break; 588 } 589 590 for(int k=0; k<padding; k++) { 591 stream.writeByte(0); 592 } 593 } else if (compressionType == BMPConstants.BI_RLE4) { 594 if (bpixels == null || bpixels.length < scanlineBytes) 595 bpixels = new byte[scanlineBytes]; 596 src.getPixels(srcRect.x, srcRect.y, 597 srcRect.width, srcRect.height, pixels); 598 for (int h=0; h<scanlineBytes; h++) { 599 bpixels[h] = (byte)pixels[h]; 600 } 601 encodeRLE4(bpixels, scanlineBytes); 602 } else if (compressionType == BMPConstants.BI_RLE8) { 603 if (bpixels == null || bpixels.length < scanlineBytes) 608 bpixels = new byte[scanlineBytes]; 609 src.getPixels(srcRect.x, srcRect.y, 610 srcRect.width, srcRect.height, pixels); 611 for (int h=0; h<scanlineBytes; h++) { 612 bpixels[h] = (byte)pixels[h]; 613 } 614 615 encodeRLE8(bpixels, scanlineBytes); 616 } 617 } else { 618 src.getPixels(srcRect.x, srcRect.y, 619 srcRect.width, srcRect.height, pixels); 620 621 622 if (scaleX != 1 || maxBandOffset != numBands -1 || 623 bgrOrder) 624 for (int j = 0, k = 0, n=0; j < w; 625 j++, k += scaleX * numBands, n += numBands) { 626 System.arraycopy(pixels, k, pixel, 0, pixel.length); 627 for (int m = 0; m < numBands; m++) 628 pixels[n + numBands - m - 1] = 629 pixel[bandOffsets[sourceBands[m]]]; 630 } 631 632 writePixels(0, scanlineBytes, bitsPerPixel, pixels, 633 padding, numBands, icm); 634 } 635 636 processImageProgress(100.0f * (((float)i) / ((float)h))); 637 } 638 639 if (compressionType == BMPConstants.BI_RLE4 || 640 compressionType == BMPConstants.BI_RLE8) { 641 stream.writeByte(0); 643 stream.writeByte(1); 644 incCompImageSize(2); 645 imageSize = compImageSize; 647 fileSize = compImageSize + offset; 648 long endPos = stream.getStreamPosition(); 649 stream.seek(headPos); 650 writeSize(fileSize, 2); 651 stream.seek(headPos); 652 writeSize(imageSize, 34); 653 stream.seek(endPos); 654 } 655 656 if (abortRequested()) { 657 processWriteAborted(); 658 } else { 659 processImageComplete(); 660 stream.flushBefore(stream.getStreamPosition()); 661 } 662 } 663 664 private void writePixels(int l, int scanlineBytes, int bitsPerPixel, 665 int pixels[], 666 int padding, int numBands, 667 IndexColorModel icm) throws IOException { 668 int pixel = 0; 669 int k = 0; 670 switch (bitsPerPixel) { 671 672 case 1: 673 674 for (int j=0; j<scanlineBytes/8; j++) { 675 bpixels[k++] = (byte)((pixels[l++] << 7) | 676 (pixels[l++] << 6) | 677 (pixels[l++] << 5) | 678 (pixels[l++] << 4) | 679 (pixels[l++] << 3) | 680 (pixels[l++] << 2) | 681 (pixels[l++] << 1) | 682 pixels[l++]); 683 } 684 685 if (scanlineBytes%8 > 0) { 687 pixel = 0; 688 for (int j=0; j<scanlineBytes%8; j++) { 689 pixel |= (pixels[l++] << (7 - j)); 690 } 691 bpixels[k++] = (byte)pixel; 692 } 693 stream.write(bpixels, 0, (scanlineBytes+7)/8); 694 695 break; 696 697 case 4: 698 if (compressionType == BMPConstants.BI_RLE4){ 699 byte[] bipixels = new byte[scanlineBytes]; 700 for (int h=0; h<scanlineBytes; h++) { 701 bipixels[h] = (byte)pixels[l++]; 702 } 703 encodeRLE4(bipixels, scanlineBytes); 704 }else { 705 for (int j=0; j<scanlineBytes/2; j++) { 706 pixel = (pixels[l++] << 4) | pixels[l++]; 707 bpixels[k++] = (byte)pixel; 708 } 709 if ((scanlineBytes%2) == 1) { 711 pixel = pixels[l] << 4; 712 bpixels[k++] = (byte)pixel; 713 } 714 stream.write(bpixels, 0, (scanlineBytes+1)/2); 715 } 716 break; 717 718 case 8: 719 if(compressionType == BMPConstants.BI_RLE8) { 720 for (int h=0; h<scanlineBytes; h++) { 721 bpixels[h] = (byte)pixels[l++]; 722 } 723 encodeRLE8(bpixels, scanlineBytes); 724 }else { 725 for (int j=0; j<scanlineBytes; j++) { 726 bpixels[j] = (byte)pixels[l++]; 727 } 728 stream.write(bpixels, 0, scanlineBytes); 729 } 730 break; 731 732 case 16: 733 if (spixels == null) 734 spixels = new short[scanlineBytes / numBands]; 735 for (int j = 0, m = 0; j < scanlineBytes; m++) { 736 spixels[m] = 0; 737 for(int i = numBands -1 ; i >= 0; i--, j++) 738 spixels[m] |= pixels[j] << bitPos[i]; 739 } 740 stream.writeShorts(spixels, 0, spixels.length); 741 break; 742 743 case 24: 744 if (numBands == 3) { 745 for (int j=0; j<scanlineBytes; j+=3) { 746 bpixels[k++] = (byte)(pixels[l+2]); 748 bpixels[k++] = (byte)(pixels[l+1]); 749 bpixels[k++] = (byte)(pixels[l]); 750 l+=3; 751 } 752 stream.write(bpixels, 0, scanlineBytes); 753 } else { 754 int entries = icm.getMapSize(); 756 757 byte r[] = new byte[entries]; 758 byte g[] = new byte[entries]; 759 byte b[] = new byte[entries]; 760 761 icm.getReds(r); 762 icm.getGreens(g); 763 icm.getBlues(b); 764 int index; 765 766 for (int j=0; j<scanlineBytes; j++) { 767 index = pixels[l]; 768 bpixels[k++] = b[index]; 769 bpixels[k++] = g[index]; 770 bpixels[k++] = b[index]; 771 l++; 772 } 773 stream.write(bpixels, 0, scanlineBytes*3); 774 } 775 break; 776 777 case 32: 778 if (ipixels == null) 779 ipixels = new int[scanlineBytes / numBands]; 780 for (int j = 0, m = 0; j < scanlineBytes; m++) { 781 ipixels[m] = 0; 782 for(int i = numBands -1 ; i >= 0; i--, j++) 783 ipixels[m] |= pixels[j] << bitPos[i]; 784 } 785 stream.writeInts(ipixels, 0, ipixels.length); 786 break; 787 } 788 789 if (compressionType == BMPConstants.BI_RGB){ 791 for(k=0; k<padding; k++) { 792 stream.writeByte(0); 793 } 794 } 795 } 796 797 private void encodeRLE8(byte[] bpixels, int scanlineBytes) 798 throws IOException { 799 800 int runCount = 1, absVal = -1, j = -1; 801 byte runVal = 0, nextVal =0 ; 802 803 runVal = bpixels[++j]; 804 byte[] absBuf = new byte[256]; 805 806 while (j < scanlineBytes-1) { 807 nextVal = bpixels[++j]; 808 if (nextVal == runVal ){ 809 if(absVal >= 3 ){ 810 stream.writeByte(0); 812 stream.writeByte(absVal); 813 incCompImageSize(2); 814 for(int a=0; a<absVal;a++){ 815 stream.writeByte(absBuf[a]); 816 incCompImageSize(1); 817 } 818 if (!isEven(absVal)){ 819 stream.writeByte(0); 821 incCompImageSize(1); 822 } 823 } 824 else if(absVal > -1){ 825 for (int b=0;b<absVal;b++){ 830 stream.writeByte(1); 831 stream.writeByte(absBuf[b]); 832 incCompImageSize(2); 833 } 834 } 835 absVal = -1; 836 runCount++; 837 if (runCount == 256){ 838 stream.writeByte(runCount-1); 840 stream.writeByte(runVal); 841 incCompImageSize(2); 842 runCount = 1; 843 } 844 } 845 else { 846 if (runCount > 1){ 847 stream.writeByte(runCount); 849 stream.writeByte(runVal); 850 incCompImageSize(2); 851 } else if (absVal < 0){ 852 absBuf[++absVal] = runVal; 854 absBuf[++absVal] = nextVal; 855 } else if (absVal < 254){ 856 absBuf[++absVal] = nextVal; 858 } else { 859 stream.writeByte(0); 860 stream.writeByte(absVal+1); 861 incCompImageSize(2); 862 for(int a=0; a<=absVal;a++){ 863 stream.writeByte(absBuf[a]); 864 incCompImageSize(1); 865 } 866 stream.writeByte(0); 868 incCompImageSize(1); 869 absVal = -1; 870 } 871 runVal = nextVal; 872 runCount = 1; 873 } 874 875 if (j == scanlineBytes-1){ if (absVal == -1){ 878 stream.writeByte(runCount); 879 stream.writeByte(runVal); 880 incCompImageSize(2); 881 runCount = 1; 882 } 883 else { 884 if(absVal >= 2){ 886 stream.writeByte(0); 887 stream.writeByte(absVal+1); 888 incCompImageSize(2); 889 for(int a=0; a<=absVal;a++){ 890 stream.writeByte(absBuf[a]); 891 incCompImageSize(1); 892 } 893 if (!isEven(absVal+1)){ 894 stream.writeByte(0); 896 incCompImageSize(1); 897 } 898 899 } 900 else if(absVal > -1){ 901 for (int b=0;b<=absVal;b++){ 902 stream.writeByte(1); 903 stream.writeByte(absBuf[b]); 904 incCompImageSize(2); 905 } 906 } 907 } 908 910 stream.writeByte(0); 911 stream.writeByte(0); 912 incCompImageSize(2); 913 } 914 } 915 } 916 917 private void encodeRLE4(byte[] bipixels, int scanlineBytes) 918 throws IOException { 919 920 int runCount=2, absVal=-1, j=-1, pixel=0, q=0; 921 byte runVal1=0, runVal2=0, nextVal1=0, nextVal2=0; 922 byte[] absBuf = new byte[256]; 923 924 925 runVal1 = bipixels[++j]; 926 runVal2 = bipixels[++j]; 927 928 while (j < scanlineBytes-2){ 929 nextVal1 = bipixels[++j]; 930 nextVal2 = bipixels[++j]; 931 932 if (nextVal1 == runVal1 ) { 933 934 if(absVal >= 4){ 936 stream.writeByte(0); 937 stream.writeByte(absVal - 1); 938 incCompImageSize(2); 939 for(int a=0; a<absVal-2;a+=2){ 942 pixel = (absBuf[a] << 4) | absBuf[a+1]; 943 stream.writeByte((byte)pixel); 944 incCompImageSize(1); 945 } 946 if(!(isEven(absVal-1))){ 948 q = absBuf[absVal-2] << 4| 0; 949 stream.writeByte(q); 950 incCompImageSize(1); 951 } 952 if ( !isEven((int)Math.ceil((absVal-1)/2)) ) { 954 stream.writeByte(0); 955 incCompImageSize(1); 956 } 957 } else if (absVal > -1){ 958 stream.writeByte(2); 959 pixel = (absBuf[0] << 4) | absBuf[1]; 960 stream.writeByte(pixel); 961 incCompImageSize(2); 962 } 963 absVal = -1; 964 965 if (nextVal2 == runVal2){ 966 runCount+=2; 968 if(runCount == 256){ 969 stream.writeByte(runCount-1); 970 pixel = ( runVal1 << 4) | runVal2; 971 stream.writeByte(pixel); 972 incCompImageSize(2); 973 runCount =2; 974 if(j< scanlineBytes - 1){ 975 runVal1 = runVal2; 976 runVal2 = bipixels[++j]; 977 } else { 978 stream.writeByte(01); 979 int r = runVal2 << 4 | 0; 980 stream.writeByte(r); 981 incCompImageSize(2); 982 runCount = -1; } 984 } 985 } else { 986 runCount++; 990 pixel = ( runVal1 << 4) | runVal2; 991 stream.writeByte(runCount); 992 stream.writeByte(pixel); 993 incCompImageSize(2); 994 runCount = 2; 995 runVal1 = nextVal2; 996 if (j < scanlineBytes -1){ 998 runVal2 = bipixels[++j]; 999 }else { 1000 stream.writeByte(01); 1001 int r = nextVal2 << 4 | 0; 1002 stream.writeByte(r); 1003 incCompImageSize(2); 1004 runCount = -1; } 1006 1007 } 1008 } else{ 1009 if (runCount > 2){ 1011 pixel = ( runVal1 << 4) | runVal2; 1012 stream.writeByte(runCount); 1013 stream.writeByte(pixel); 1014 incCompImageSize(2); 1015 } else if (absVal < 0){ absBuf[++absVal] = runVal1; 1017 absBuf[++absVal] = runVal2; 1018 absBuf[++absVal] = nextVal1; 1019 absBuf[++absVal] = nextVal2; 1020 } else if (absVal < 253){ absBuf[++absVal] = nextVal1; 1022 absBuf[++absVal] = nextVal2; 1023 } else { 1024 stream.writeByte(0); 1025 stream.writeByte(absVal+1); 1026 incCompImageSize(2); 1027 for(int a=0; a<absVal;a+=2){ 1028 pixel = (absBuf[a] << 4) | absBuf[a+1]; 1029 stream.writeByte((byte)pixel); 1030 incCompImageSize(1); 1031 } 1032 stream.writeByte(0); 1035 incCompImageSize(1); 1036 absVal = -1; 1037 } 1038 1039 runVal1 = nextVal1; 1040 runVal2 = nextVal2; 1041 runCount = 2; 1042 } 1043 if (j >= scanlineBytes-2 ) { 1045 if (absVal == -1 && runCount >= 2){ 1046 if (j == scanlineBytes-2){ 1047 if(bipixels[++j] == runVal1){ 1048 runCount++; 1049 pixel = ( runVal1 << 4) | runVal2; 1050 stream.writeByte(runCount); 1051 stream.writeByte(pixel); 1052 incCompImageSize(2); 1053 } else { 1054 pixel = ( runVal1 << 4) | runVal2; 1055 stream.writeByte(runCount); 1056 stream.writeByte(pixel); 1057 stream.writeByte(01); 1058 pixel = bipixels[j]<<4 |0; 1059 stream.writeByte(pixel); 1060 int n = bipixels[j]<<4|0; 1061 incCompImageSize(4); 1062 } 1063 } else { 1064 stream.writeByte(runCount); 1065 pixel =( runVal1 << 4) | runVal2 ; 1066 stream.writeByte(pixel); 1067 incCompImageSize(2); 1068 } 1069 } else if(absVal > -1){ 1070 if (j == scanlineBytes-2){ 1071 absBuf[++absVal] = bipixels[++j]; 1072 } 1073 if (absVal >=2){ 1074 stream.writeByte(0); 1075 stream.writeByte(absVal+1); 1076 incCompImageSize(2); 1077 for(int a=0; a<absVal;a+=2){ 1078 pixel = (absBuf[a] << 4) | absBuf[a+1]; 1079 stream.writeByte((byte)pixel); 1080 incCompImageSize(1); 1081 } 1082 if(!(isEven(absVal+1))){ 1083 q = absBuf[absVal] << 4|0; 1084 stream.writeByte(q); 1085 incCompImageSize(1); 1086 } 1087 1088 if ( !isEven((int)Math.ceil((absVal+1)/2)) ) { 1090 stream.writeByte(0); 1091 incCompImageSize(1); 1092 } 1093 1094 } else { 1095 switch (absVal){ 1096 case 0: 1097 stream.writeByte(1); 1098 int n = absBuf[0]<<4 | 0; 1099 stream.writeByte(n); 1100 incCompImageSize(2); 1101 break; 1102 case 1: 1103 stream.writeByte(2); 1104 pixel = (absBuf[0] << 4) | absBuf[1]; 1105 stream.writeByte(pixel); 1106 incCompImageSize(2); 1107 break; 1108 } 1109 } 1110 1111 } 1112 stream.writeByte(0); 1113 stream.writeByte(0); 1114 incCompImageSize(2); 1115 } 1116 } 1117 } 1118 1119 1120 private synchronized void incCompImageSize(int value){ 1121 compImageSize = compImageSize + value; 1122 } 1123 1124 private boolean isEven(int number) { 1125 return (number%2 == 0 ? true : false); 1126 } 1127 1128 private void writeFileHeader(int fileSize, int offset) throws IOException { 1129 stream.writeByte('B'); 1131 stream.writeByte('M'); 1132 1133 stream.writeInt(fileSize); 1135 1136 stream.writeInt(0); 1138 1139 stream.writeInt(offset); 1141 } 1142 1143 1144 private void writeInfoHeader(int headerSize, 1145 int bitsPerPixel) throws IOException { 1146 stream.writeInt(headerSize); 1148 1149 stream.writeInt(w); 1151 1152 stream.writeInt(h); 1154 1155 stream.writeShort(1); 1157 1158 stream.writeShort(bitsPerPixel); 1160 } 1161 1162 private void writeSize(int dword, int offset) throws IOException { 1163 stream.skipBytes(offset); 1164 stream.writeInt(dword); 1165 } 1166 1167 public void reset() { 1168 super.reset(); 1169 stream = null; 1170 } 1171 1172 private int getCompressionType(String typeString) { 1173 for (int i = 0; i < BMPConstants.compressionTypeNames.length; i++) 1174 if (BMPConstants.compressionTypeNames[i].equals(typeString)) 1175 return i; 1176 return 0; 1177 } 1178 1179 private void writeEmbedded(IIOImage image, 1180 ImageWriteParam bmpParam) throws IOException { 1181 String format = 1182 compressionType == BMPConstants.BI_JPEG ? "jpeg" : "png"; 1183 Iterator iterator = ImageIO.getImageWritersByFormatName(format); 1184 ImageWriter writer = null; 1185 if (iterator.hasNext()) 1186 writer = (ImageWriter )iterator.next(); 1187 if (writer != null) { 1188 if (embedded_stream == null) { 1189 throw new RuntimeException ("No stream for writing embedded image!"); 1190 } 1191 1192 writer.addIIOWriteProgressListener(new IIOWriteProgressAdapter() { 1193 public void imageProgress(ImageWriter source, float percentageDone) { 1194 processImageProgress(percentageDone); 1195 } 1196 }); 1197 1198 writer.addIIOWriteWarningListener(new IIOWriteWarningListener () { 1199 public void warningOccurred(ImageWriter source, int imageIndex, String warning) { 1200 processWarningOccurred(imageIndex, warning); 1201 } 1202 }); 1203 1204 writer.setOutput(ImageIO.createImageOutputStream(embedded_stream)); 1205 ImageWriteParam param = writer.getDefaultWriteParam(); 1206 param.setDestinationOffset(bmpParam.getDestinationOffset()); 1208 param.setSourceBands(bmpParam.getSourceBands()); 1209 param.setSourceRegion(bmpParam.getSourceRegion()); 1210 param.setSourceSubsampling(bmpParam.getSourceXSubsampling(), 1211 bmpParam.getSourceYSubsampling(), 1212 bmpParam.getSubsamplingXOffset(), 1213 bmpParam.getSubsamplingYOffset()); 1214 writer.write(null, image, param); 1215 } else 1216 throw new RuntimeException (I18N.getString("BMPImageWrite5") + " " + format); 1217 1218 } 1219 1220 private int firstLowBit(int num) { 1221 int count = 0; 1222 while ((num & 1) == 0) { 1223 count++; 1224 num >>>= 1; 1225 } 1226 return count; 1227 } 1228 1229 private class IIOWriteProgressAdapter implements IIOWriteProgressListener { 1230 1231 public void imageComplete(ImageWriter source) { 1232 } 1233 1234 public void imageProgress(ImageWriter source, float percentageDone) { 1235 } 1236 1237 public void imageStarted(ImageWriter source, int imageIndex) { 1238 } 1239 1240 public void thumbnailComplete(ImageWriter source) { 1241 } 1242 1243 public void thumbnailProgress(ImageWriter source, float percentageDone) { 1244 } 1245 1246 public void thumbnailStarted(ImageWriter source, int imageIndex, int thumbnailIndex) { 1247 } 1248 1249 public void writeAborted(ImageWriter source) { 1250 } 1251 } 1252 1253 1263 protected int getPreferredCompressionType(ColorModel cm, SampleModel sm) { 1264 ImageTypeSpecifier imageType = new ImageTypeSpecifier (cm, sm); 1265 return getPreferredCompressionType(imageType); 1266 } 1267 1268 protected int getPreferredCompressionType(ImageTypeSpecifier imageType) { 1269 if (imageType.getBufferedImageType() == BufferedImage.TYPE_USHORT_565_RGB) { 1270 return BI_BITFIELDS; 1271 } 1272 return BI_RGB; 1273 } 1274 1275 1283 protected boolean canEncodeImage(int compression, ColorModel cm, SampleModel sm) { 1284 ImageTypeSpecifier imgType = new ImageTypeSpecifier (cm, sm); 1285 return canEncodeImage(compression, imgType); 1286 } 1287 1288 protected boolean canEncodeImage(int compression, ImageTypeSpecifier imgType) { 1289 ImageWriterSpi spi = this.getOriginatingProvider(); 1290 if (!spi.canEncodeImage(imgType)) { 1291 return false; 1292 } 1293 int biType = imgType.getBufferedImageType(); 1294 if (biType == BufferedImage.TYPE_USHORT_565_RGB 1295 && compression != BI_BITFIELDS) { 1296 return false; 1297 } 1298 1299 int bpp = imgType.getColorModel().getPixelSize(); 1300 if (compressionType == BI_RLE4 && bpp != 4) { 1301 return false; 1303 } 1304 if (compressionType == BI_RLE8 && bpp != 8) { 1305 return false; 1307 } 1308 1309 return true; 1310 } 1311} 1312 | Popular Tags |