History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/com/sun/imageio/plugins/bmp/BMPImageWriter.java (1 of 18352 files). Show all

Unified · Split

java/pkg/com/sun/imageio/plugins/bmp/BMPImageWriter.java+1532−0
@@ -0,0 +1,1532 @@
1/*
2 * Copyright (c) 2003, 2007, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package com.sun.imageio.plugins.bmp;
27
28import java.awt.Point;
29import java.awt.Rectangle;
30import java.awt.image.ColorModel;
31import java.awt.image.ComponentSampleModel;
32import java.awt.image.DataBuffer;
33import java.awt.image.DataBufferByte;
34import java.awt.image.DataBufferInt;
35import java.awt.image.DataBufferShort;
36import java.awt.image.DataBufferUShort;
37import java.awt.image.DirectColorModel;
38import java.awt.image.IndexColorModel;
39import java.awt.image.MultiPixelPackedSampleModel;
40import java.awt.image.BandedSampleModel;
41import java.awt.image.Raster;
42import java.awt.image.RenderedImage;
43import java.awt.image.SampleModel;
44import java.awt.image.SinglePixelPackedSampleModel;
45import java.awt.image.WritableRaster;
46import java.awt.image.BufferedImage;
47
48import java.io.IOException;
49import java.io.ByteArrayOutputStream;
50import java.nio.ByteOrder;
51import java.util.Iterator;
52
53import javax.imageio.IIOImage;
54import javax.imageio.IIOException;
55import javax.imageio.ImageIO;
56import javax.imageio.ImageTypeSpecifier;
57import javax.imageio.ImageWriteParam;
58import javax.imageio.ImageWriter;
59import javax.imageio.metadata.IIOMetadata;
60import javax.imageio.metadata.IIOMetadataNode;
61import javax.imageio.metadata.IIOMetadataFormatImpl;
62import javax.imageio.metadata.IIOInvalidTreeException;
63import javax.imageio.spi.ImageWriterSpi;
64import javax.imageio.stream.ImageOutputStream;
65import javax.imageio.event.IIOWriteProgressListener;
66import javax.imageio.event.IIOWriteWarningListener;
67
68import org.w3c.dom.Node;
69import org.w3c.dom.NodeList;
70
71import javax.imageio.plugins.bmp.BMPImageWriteParam;
72import com.sun.imageio.plugins.common.ImageUtil;
73import com.sun.imageio.plugins.common.I18N;
74
75/**
76 * The Java Image IO plugin writer for encoding a binary RenderedImage into
77 * a BMP format.
78 *
79 * The encoding process may clip, subsample using the parameters
80 * specified in the <code>ImageWriteParam</code>.
81 *
82 * @see javax.imageio.plugins.bmp.BMPImageWriteParam
83 */
84public class BMPImageWriter extends ImageWriter implements BMPConstants {
85 /** The output stream to write into */
86 private ImageOutputStream stream = null;
87 private ByteArrayOutputStream embedded_stream = null;
88 private int version;
89 private int compressionType;
90 private boolean isTopDown;
91 private int w, h;
92 private int compImageSize = 0;
93 private int[] bitMasks;
94 private int[] bitPos;
95 private byte[] bpixels;
96 private short[] spixels;
97 private int[] ipixels;
98
99 /** Constructs <code>BMPImageWriter</code> based on the provided
100 * <code>ImageWriterSpi</code>.
101 */
102 public BMPImageWriter(ImageWriterSpi originator) {
103 super(originator);
104 }
105
106 public void setOutput(Object output) {
107 super.setOutput(output); // validates output
108 if (output != null) {
109 if (!(output instanceof ImageOutputStream))
110 throw new IllegalArgumentException(I18N.getString("BMPImageWriter0"));
111 this.stream = (ImageOutputStream)output;
112 stream.setByteOrder(ByteOrder.LITTLE_ENDIAN);
113 } else
114 this.stream = null;
115 }
116
117 public ImageWriteParam getDefaultWriteParam() {
118 return new BMPImageWriteParam();
119 }
120
121 public IIOMetadata getDefaultStreamMetadata(ImageWriteParam param) {
122 return null;
123 }
124
125 public IIOMetadata getDefaultImageMetadata(ImageTypeSpecifier imageType,
126 ImageWriteParam param) {
127 BMPMetadata meta = new BMPMetadata();
128 meta.bmpVersion = VERSION_3;
129 meta.compression = getPreferredCompressionType(imageType);
130 if (param != null
131 && param.getCompressionMode() == ImageWriteParam.MODE_EXPLICIT) {
132 meta.compression = getCompressionType(param.getCompressionType());
133 }
134 meta.bitsPerPixel = (short)imageType.getColorModel().getPixelSize();
135 return meta;
136 }
137
138 public IIOMetadata convertStreamMetadata(IIOMetadata inData,
139 ImageWriteParam param) {
140 return null;
141 }
142
143 public IIOMetadata convertImageMetadata(IIOMetadata metadata,
144 ImageTypeSpecifier type,
145 ImageWriteParam param) {
146 return null;
147 }
148
149 public boolean canWriteRasters() {
150 return true;
151 }
152
153 public void write(IIOMetadata streamMetadata,
154 IIOImage image,
155 ImageWriteParam param) throws IOException {
156
157 if (stream == null) {
158 throw new IllegalStateException(I18N.getString("BMPImageWriter7"));
159 }
160
161 if (image == null) {
162 throw new IllegalArgumentException(I18N.getString("BMPImageWriter8"));
163 }
164
165 clearAbortRequest();
166 processImageStarted(0);
167 if (param == null)
168 param = getDefaultWriteParam();
169
170 BMPImageWriteParam bmpParam = (BMPImageWriteParam)param;
171
172 // Default is using 24 bits per pixel.
173 int bitsPerPixel = 24;
174 boolean isPalette = false;
175 int paletteEntries = 0;
176 IndexColorModel icm = null;
177
178 RenderedImage input = null;
179 Raster inputRaster = null;
180 boolean writeRaster = image.hasRaster();
181 Rectangle sourceRegion = param.getSourceRegion();
182 SampleModel sampleModel = null;
183 ColorModel colorModel = null;
184
185 compImageSize = 0;
186
187 if (writeRaster) {
188 inputRaster = image.getRaster();
189 sampleModel = inputRaster.getSampleModel();
190 colorModel = ImageUtil.createColorModel(null, sampleModel);
191 if (sourceRegion == null)
192 sourceRegion = inputRaster.getBounds();
193 else
194 sourceRegion = sourceRegion.intersection(inputRaster.getBounds());
195 } else {
196 input = image.getRenderedImage();
197 sampleModel = input.getSampleModel();
198 colorModel = input.getColorModel();
199 Rectangle rect = new Rectangle(input.getMinX(), input.getMinY(),
200 input.getWidth(), input.getHeight());
201 if (sourceRegion == null)
202 sourceRegion = rect;
203 else
204 sourceRegion = sourceRegion.intersection(rect);
205 }
206
207 IIOMetadata imageMetadata = image.getMetadata();
208 BMPMetadata bmpImageMetadata = null;
209 if (imageMetadata != null
210 && imageMetadata instanceof BMPMetadata)
211 {
212 bmpImageMetadata = (BMPMetadata)imageMetadata;
213 } else {
214 ImageTypeSpecifier imageType =
215 new ImageTypeSpecifier(colorModel, sampleModel);
216
217 bmpImageMetadata = (BMPMetadata)getDefaultImageMetadata(imageType,
218 param);
219 }
220
221 if (sourceRegion.isEmpty())
222 throw new RuntimeException(I18N.getString("BMPImageWrite0"));
223
224 int scaleX = param.getSourceXSubsampling();
225 int scaleY = param.getSourceYSubsampling();
226 int xOffset = param.getSubsamplingXOffset();
227 int yOffset = param.getSubsamplingYOffset();
228
229 // cache the data type;
230 int dataType = sampleModel.getDataType();
231
232 sourceRegion.translate(xOffset, yOffset);
233 sourceRegion.width -= xOffset;
234 sourceRegion.height -= yOffset;
235
236 int minX = sourceRegion.x / scaleX;
237 int minY = sourceRegion.y / scaleY;
238 w = (sourceRegion.width + scaleX - 1) / scaleX;
239 h = (sourceRegion.height + scaleY - 1) / scaleY;
240 xOffset = sourceRegion.x % scaleX;
241 yOffset = sourceRegion.y % scaleY;
242
243 Rectangle destinationRegion = new Rectangle(minX, minY, w, h);
244 boolean noTransform = destinationRegion.equals(sourceRegion);
245
246 // Raw data can only handle bytes, everything greater must be ASCII.
247 int[] sourceBands = param.getSourceBands();
248 boolean noSubband = true;
249 int numBands = sampleModel.getNumBands();
250
251 if (sourceBands != null) {
252 sampleModel = sampleModel.createSubsetSampleModel(sourceBands);
253 colorModel = null;
254 noSubband = false;
255 numBands = sampleModel.getNumBands();
256 } else {
257 sourceBands = new int[numBands];
258 for (int i = 0; i < numBands; i++)
259 sourceBands[i] = i;
260 }
261
262 int[] bandOffsets = null;
263 boolean bgrOrder = true;
264
265 if (sampleModel instanceof ComponentSampleModel) {
266 bandOffsets = ((ComponentSampleModel)sampleModel).getBandOffsets();
267 if (sampleModel instanceof BandedSampleModel) {
268 // for images with BandedSampleModel we can not work
269 // with raster directly and must use writePixels()
270 bgrOrder = false;
271 } else {
272 // we can work with raster directly only in case of
273 // BGR component order.
274 // In any other case we must use writePixels()
275 for (int i = 0; i < bandOffsets.length; i++) {
276 bgrOrder &= (bandOffsets[i] == (bandOffsets.length - i - 1));
277 }
278 }
279 } else {
280 if (sampleModel instanceof SinglePixelPackedSampleModel) {
281
282 // BugId 4892214: we can not work with raster directly
283 // if image have different color order than RGB.
284 // We should use writePixels() for such images.
285 int[] bitOffsets = ((SinglePixelPackedSampleModel)sampleModel).getBitOffsets();
286 for (int i=0; i<bitOffsets.length-1; i++) {
287 bgrOrder &= bitOffsets[i] > bitOffsets[i+1];
288 }
289 }
290 }
291
292 if (bandOffsets == null) {
293 // we will use getPixels() to extract pixel data for writePixels()
294 // Please note that getPixels() provides rgb bands order.
295 bandOffsets = new int[numBands];
296 for (int i = 0; i < numBands; i++)
297 bandOffsets[i] = i;
298 }
299
300 noTransform &= bgrOrder;
301
302 int sampleSize[] = sampleModel.getSampleSize();
303
304 //XXX: check more
305
306 // Number of bytes that a scanline for the image written out will have.
307 int destScanlineBytes = w * numBands;
308
309 switch(bmpParam.getCompressionMode()) {
310 case ImageWriteParam.MODE_EXPLICIT:
311 compressionType = getCompressionType(bmpParam.getCompressionType());
312 break;
313 case ImageWriteParam.MODE_COPY_FROM_METADATA:
314 compressionType = bmpImageMetadata.compression;
315 break;
316 case ImageWriteParam.MODE_DEFAULT:
317 compressionType = getPreferredCompressionType(colorModel, sampleModel);
318 break;
319 default:
320 // ImageWriteParam.MODE_DISABLED:
321 compressionType = BI_RGB;
322 }
323
324 if (!canEncodeImage(compressionType, colorModel, sampleModel)) {
325 throw new IOException("Image can not be encoded with compression type "
326 + compressionTypeNames[compressionType]);
327 }
328
329 byte r[] = null, g[] = null, b[] = null, a[] = null;
330
331 if (compressionType == BMPConstants.BI_BITFIELDS) {
332 bitsPerPixel =
333 DataBuffer.getDataTypeSize(sampleModel.getDataType());
334
335 if (bitsPerPixel != 16 && bitsPerPixel != 32) {
336 // we should use 32bpp images in case of BI_BITFIELD
337 // compression to avoid color conversion artefacts
338 bitsPerPixel = 32;
339
340 // Setting this flag to false ensures that generic
341 // writePixels() will be used to store image data
342 noTransform = false;
343 }
344
345 destScanlineBytes = w * bitsPerPixel + 7 >> 3;
346
347 isPalette = true;
348 paletteEntries = 3;
349 r = new byte[paletteEntries];
350 g = new byte[paletteEntries];
351 b = new byte[paletteEntries];
352 a = new byte[paletteEntries];
353
354 int rmask = 0x00ff0000;
355 int gmask = 0x0000ff00;
356 int bmask = 0x000000ff;
357
358 if (bitsPerPixel == 16) {
359 /* NB: canEncodeImage() ensures we have image of
360 * either USHORT_565_RGB or USHORT_555_RGB type here.
361 * Technically, it should work for other direct color
362 * model types but it might be non compatible with win98
363 * and friends.
364 */
365 if (colorModel instanceof DirectColorModel) {
366 DirectColorModel dcm = (DirectColorModel)colorModel;
367 rmask = dcm.getRedMask();
368 gmask = dcm.getGreenMask();
369 bmask = dcm.getBlueMask();
370 } else {
371 // it is unlikely, but if it happens, we should throw
372 // an exception related to unsupported image format
373 throw new IOException("Image can not be encoded with " +
374 "compression type " +
375 compressionTypeNames[compressionType]);
376 }
377 }
378 writeMaskToPalette(rmask, 0, r, g, b, a);
379 writeMaskToPalette(gmask, 1, r, g, b, a);
380 writeMaskToPalette(bmask, 2, r, g, b, a);
381
382 if (!noTransform) {
383 // prepare info for writePixels procedure
384 bitMasks = new int[3];
385 bitMasks[0] = rmask;
386 bitMasks[1] = gmask;
387 bitMasks[2] = bmask;
388
389 bitPos = new int[3];
390 bitPos[0] = firstLowBit(rmask);
391 bitPos[1] = firstLowBit(gmask);
392 bitPos[2] = firstLowBit(bmask);
393 }
394
395 if (colorModel instanceof IndexColorModel) {
396 icm = (IndexColorModel)colorModel;
397 }
398 } else { // handle BI_RGB compression
399 if (colorModel instanceof IndexColorModel) {
400 isPalette = true;
401 icm = (IndexColorModel)colorModel;
402 paletteEntries = icm.getMapSize();
403
404 if (paletteEntries <= 2) {
405 bitsPerPixel = 1;
406 destScanlineBytes = w + 7 >> 3;
407 } else if (paletteEntries <= 16) {
408 bitsPerPixel = 4;
409 destScanlineBytes = w + 1 >> 1;
410 } else if (paletteEntries <= 256) {
411 bitsPerPixel = 8;
412 } else {
413 // Cannot be written as a Palette image. So write out as
414 // 24 bit image.
415 bitsPerPixel = 24;
416 isPalette = false;
417 paletteEntries = 0;
418 destScanlineBytes = w * 3;
419 }
420
421 if (isPalette == true) {
422 r = new byte[paletteEntries];
423 g = new byte[paletteEntries];
424 b = new byte[paletteEntries];
425 a = new byte[paletteEntries];
426
427 icm.getAlphas(a);
428 icm.getReds(r);
429 icm.getGreens(g);
430 icm.getBlues(b);
431 }
432
433 } else {
434 // Grey scale images
435 if (numBands == 1) {
436
437 isPalette = true;
438 paletteEntries = 256;
439 bitsPerPixel = sampleSize[0];
440
441 destScanlineBytes = (w * bitsPerPixel + 7 >> 3);
442
443 r = new byte[256];
444 g = new byte[256];
445 b = new byte[256];
446 a = new byte[256];
447
448 for (int i = 0; i < 256; i++) {
449 r[i] = (byte)i;
450 g[i] = (byte)i;
451 b[i] = (byte)i;
452 a[i] = (byte)255;
453 }
454
455 } else {
456 if (sampleModel instanceof SinglePixelPackedSampleModel &&
457 noSubband)
458 {
459 /* NB: the actual pixel size can be smaller than
460 * size of used DataBuffer element.
461 * For example: in case of TYPE_INT_RGB actual pixel
462 * size is 24 bits, but size of DataBuffere element
463 * is 32 bits
464 */
465 int[] sample_sizes = sampleModel.getSampleSize();
466 bitsPerPixel = 0;
467 for (int size : sample_sizes) {
468 bitsPerPixel += size;
469 }
470 bitsPerPixel = roundBpp(bitsPerPixel);
471 if (bitsPerPixel != DataBuffer.getDataTypeSize(sampleModel.getDataType())) {
472 noTransform = false;
473 }
474 destScanlineBytes = w * bitsPerPixel + 7 >> 3;
475 }
476 }
477 }
478 }
479
480 // actual writing of image data
481 int fileSize = 0;
482 int offset = 0;
483 int headerSize = 0;
484 int imageSize = 0;
485 int xPelsPerMeter = 0;
486 int yPelsPerMeter = 0;
487 int colorsUsed = 0;
488 int colorsImportant = paletteEntries;
489
490 // Calculate padding for each scanline
491 int padding = destScanlineBytes % 4;
492 if (padding != 0) {
493 padding = 4 - padding;
494 }
495
496
497 // FileHeader is 14 bytes, BitmapHeader is 40 bytes,
498 // add palette size and that is where the data will begin
499 offset = 54 + paletteEntries * 4;
500
501 imageSize = (destScanlineBytes + padding) * h;
502 fileSize = imageSize + offset;
503 headerSize = 40;
504
505 long headPos = stream.getStreamPosition();
506
507 writeFileHeader(fileSize, offset);
508
509 /* According to MSDN description, the top-down image layout
510 * is allowed only if compression type is BI_RGB or BI_BITFIELDS.
511 * Images with any other compression type must be wrote in the
512 * bottom-up layout.
513 */
514 if (compressionType == BMPConstants.BI_RGB ||
515 compressionType == BMPConstants.BI_BITFIELDS)
516 {
517 isTopDown = bmpParam.isTopDown();
518 } else {
519 isTopDown = false;
520 }
521
522 writeInfoHeader(headerSize, bitsPerPixel);
523
524 // compression
525 stream.writeInt(compressionType);
526
527 // imageSize
528 stream.writeInt(imageSize);
529
530 // xPelsPerMeter
531 stream.writeInt(xPelsPerMeter);
532
533 // yPelsPerMeter
534 stream.writeInt(yPelsPerMeter);
535
536 // Colors Used
537 stream.writeInt(colorsUsed);
538
539 // Colors Important
540 stream.writeInt(colorsImportant);
541
542 // palette
543 if (isPalette == true) {
544
545 // write palette
546 if (compressionType == BMPConstants.BI_BITFIELDS) {
547 // write masks for red, green and blue components.
548 for (int i=0; i<3; i++) {
549 int mask = (a[i]&0xFF) + ((r[i]&0xFF)*0x100) + ((g[i]&0xFF)*0x10000) + ((b[i]&0xFF)*0x1000000);
550 stream.writeInt(mask);
551 }
552 } else {
553 for (int i=0; i<paletteEntries; i++) {
554 stream.writeByte(b[i]);
555 stream.writeByte(g[i]);
556 stream.writeByte(r[i]);
557 stream.writeByte(a[i]);
558 }
559 }
560 }
561
562 // Writing of actual image data
563 int scanlineBytes = w * numBands;
564
565 // Buffer for up to 8 rows of pixels
566 int[] pixels = new int[scanlineBytes * scaleX];
567
568 // Also create a buffer to hold one line of the data
569 // to be written to the file, so we can use array writes.
570 bpixels = new byte[destScanlineBytes];
571
572 int l;
573
574 if (compressionType == BMPConstants.BI_JPEG ||
575 compressionType == BMPConstants.BI_PNG) {
576
577 // prepare embedded buffer
578 embedded_stream = new ByteArrayOutputStream();
579 writeEmbedded(image, bmpParam);
580 // update the file/image Size
581 embedded_stream.flush();
582 imageSize = embedded_stream.size();
583
584 long endPos = stream.getStreamPosition();
585 fileSize = (int)(offset + imageSize);
586 stream.seek(headPos);
587 writeSize(fileSize, 2);
588 stream.seek(headPos);
589 writeSize(imageSize, 34);
590 stream.seek(endPos);
591 stream.write(embedded_stream.toByteArray());
592 embedded_stream = null;
593
594 if (abortRequested()) {
595 processWriteAborted();
596 } else {
597 processImageComplete();
598 stream.flushBefore(stream.getStreamPosition());
599 }
600
601 return;
602 }
603
604 int maxBandOffset = bandOffsets[0];
605 for (int i = 1; i < bandOffsets.length; i++)
606 if (bandOffsets[i] > maxBandOffset)
607 maxBandOffset = bandOffsets[i];
608
609 int[] pixel = new int[maxBandOffset + 1];
610
611 int destScanlineLength = destScanlineBytes;
612
613 if (noTransform && noSubband) {
614 destScanlineLength = destScanlineBytes / (DataBuffer.getDataTypeSize(dataType)>>3);
615 }
616 for (int i = 0; i < h; i++) {
617 if (abortRequested()) {
618 break;
619 }
620
621 int row = minY + i;
622
623 if (!isTopDown)
624 row = minY + h - i -1;
625
626 // Get the pixels
627 Raster src = inputRaster;
628
629 Rectangle srcRect =
630 new Rectangle(minX * scaleX + xOffset,
631 row * scaleY + yOffset,
632 (w - 1)* scaleX + 1,
633 1);
634 if (!writeRaster)
635 src = input.getData(srcRect);
636
637 if (noTransform && noSubband) {
638 SampleModel sm = src.getSampleModel();
639 int pos = 0;
640 int startX = srcRect.x - src.getSampleModelTranslateX();
641 int startY = srcRect.y - src.getSampleModelTranslateY();
642 if (sm instanceof ComponentSampleModel) {
643 ComponentSampleModel csm = (ComponentSampleModel)sm;
644 pos = csm.getOffset(startX, startY, 0);
645 for(int nb=1; nb < csm.getNumBands(); nb++) {
646 if (pos > csm.getOffset(startX, startY, nb)) {
647 pos = csm.getOffset(startX, startY, nb);
648 }
649 }
650 } else if (sm instanceof MultiPixelPackedSampleModel) {
651 MultiPixelPackedSampleModel mppsm =
652 (MultiPixelPackedSampleModel)sm;
653 pos = mppsm.getOffset(startX, startY);
654 } else if (sm instanceof SinglePixelPackedSampleModel) {
655 SinglePixelPackedSampleModel sppsm =
656 (SinglePixelPackedSampleModel)sm;
657 pos = sppsm.getOffset(startX, startY);
658 }
659
660 if (compressionType == BMPConstants.BI_RGB || compressionType == BMPConstants.BI_BITFIELDS){
661 switch(dataType) {
662 case DataBuffer.TYPE_BYTE:
663 byte[] bdata =
664 ((DataBufferByte)src.getDataBuffer()).getData();
665 stream.write(bdata, pos, destScanlineLength);
666 break;
667
668 case DataBuffer.TYPE_SHORT:
669 short[] sdata =
670 ((DataBufferShort)src.getDataBuffer()).getData();
671 stream.writeShorts(sdata, pos, destScanlineLength);
672 break;
673
674 case DataBuffer.TYPE_USHORT:
675 short[] usdata =
676 ((DataBufferUShort)src.getDataBuffer()).getData();
677 stream.writeShorts(usdata, pos, destScanlineLength);
678 break;
679
680 case DataBuffer.TYPE_INT:
681 int[] idata =
682 ((DataBufferInt)src.getDataBuffer()).getData();
683 stream.writeInts(idata, pos, destScanlineLength);
684 break;
685 }
686
687 for(int k=0; k<padding; k++) {
688 stream.writeByte(0);
689 }
690 } else if (compressionType == BMPConstants.BI_RLE4) {
691 if (bpixels == null || bpixels.length < scanlineBytes)
692 bpixels = new byte[scanlineBytes];
693 src.getPixels(srcRect.x, srcRect.y,
694 srcRect.width, srcRect.height, pixels);
695 for (int h=0; h<scanlineBytes; h++) {
696 bpixels[h] = (byte)pixels[h];
697 }
698 encodeRLE4(bpixels, scanlineBytes);
699 } else if (compressionType == BMPConstants.BI_RLE8) {
700 //byte[] bdata =
701 // ((DataBufferByte)src.getDataBuffer()).getData();
702 //System.out.println("bdata.length="+bdata.length);
703 //System.arraycopy(bdata, pos, bpixels, 0, scanlineBytes);
704 if (bpixels == null || bpixels.length < scanlineBytes)
705 bpixels = new byte[scanlineBytes];
706 src.getPixels(srcRect.x, srcRect.y,
707 srcRect.width, srcRect.height, pixels);
708 for (int h=0; h<scanlineBytes; h++) {
709 bpixels[h] = (byte)pixels[h];
710 }
711
712 encodeRLE8(bpixels, scanlineBytes);
713 }
714 } else {
715 src.getPixels(srcRect.x, srcRect.y,
716 srcRect.width, srcRect.height, pixels);
717
718 if (scaleX != 1 || maxBandOffset != numBands - 1) {
719 for (int j = 0, k = 0, n=0; j < w;
720 j++, k += scaleX * numBands, n += numBands)
721 {
722 System.arraycopy(pixels, k, pixel, 0, pixel.length);
723
724 for (int m = 0; m < numBands; m++) {
725 // pixel data is provided here in RGB order
726 pixels[n + m] = pixel[sourceBands[m]];
727 }
728 }
729 }
730 writePixels(0, scanlineBytes, bitsPerPixel, pixels,
731 padding, numBands, icm);
732 }
733
734 processImageProgress(100.0f * (((float)i) / ((float)h)));
735 }
736
737 if (compressionType == BMPConstants.BI_RLE4 ||
738 compressionType == BMPConstants.BI_RLE8) {
739 // Write the RLE EOF marker and
740 stream.writeByte(0);
741 stream.writeByte(1);
742 incCompImageSize(2);
743 // update the file/image Size
744 imageSize = compImageSize;
745 fileSize = compImageSize + offset;
746 long endPos = stream.getStreamPosition();
747 stream.seek(headPos);
748 writeSize(fileSize, 2);
749 stream.seek(headPos);
750 writeSize(imageSize, 34);
751 stream.seek(endPos);
752 }
753
754 if (abortRequested()) {
755 processWriteAborted();
756 } else {
757 processImageComplete();
758 stream.flushBefore(stream.getStreamPosition());
759 }
760 }
761
762 private void writePixels(int l, int scanlineBytes, int bitsPerPixel,
763 int pixels[],
764 int padding, int numBands,
765 IndexColorModel icm) throws IOException {
766 int pixel = 0;
767 int k = 0;
768 switch (bitsPerPixel) {
769
770 case 1:
771
772 for (int j=0; j<scanlineBytes/8; j++) {
773 bpixels[k++] = (byte)((pixels[l++] << 7) |
774 (pixels[l++] << 6) |
775 (pixels[l++] << 5) |
776 (pixels[l++] << 4) |
777 (pixels[l++] << 3) |
778 (pixels[l++] << 2) |
779 (pixels[l++] << 1) |
780 pixels[l++]);
781 }
782
783 // Partially filled last byte, if any
784 if (scanlineBytes%8 > 0) {
785 pixel = 0;
786 for (int j=0; j<scanlineBytes%8; j++) {
787 pixel |= (pixels[l++] << (7 - j));
788 }
789 bpixels[k++] = (byte)pixel;
790 }
791 stream.write(bpixels, 0, (scanlineBytes+7)/8);
792
793 break;
794
795 case 4:
796 if (compressionType == BMPConstants.BI_RLE4){
797 byte[] bipixels = new byte[scanlineBytes];
798 for (int h=0; h<scanlineBytes; h++) {
799 bipixels[h] = (byte)pixels[l++];
800 }
801 encodeRLE4(bipixels, scanlineBytes);
802 }else {
803 for (int j=0; j<scanlineBytes/2; j++) {
804 pixel = (pixels[l++] << 4) | pixels[l++];
805 bpixels[k++] = (byte)pixel;
806 }
807 // Put the last pixel of odd-length lines in the 4 MSBs
808 if ((scanlineBytes%2) == 1) {
809 pixel = pixels[l] << 4;
810 bpixels[k++] = (byte)pixel;
811 }
812 stream.write(bpixels, 0, (scanlineBytes+1)/2);
813 }
814 break;
815
816 case 8:
817 if(compressionType == BMPConstants.BI_RLE8) {
818 for (int h=0; h<scanlineBytes; h++) {
819 bpixels[h] = (byte)pixels[l++];
820 }
821 encodeRLE8(bpixels, scanlineBytes);
822 }else {
823 for (int j=0; j<scanlineBytes; j++) {
824 bpixels[j] = (byte)pixels[l++];
825 }
826 stream.write(bpixels, 0, scanlineBytes);
827 }
828 break;
829
830 case 16:
831 if (spixels == null)
832 spixels = new short[scanlineBytes / numBands];
833 /*
834 * We expect that pixel data comes in RGB order.
835 * We will assemble short pixel taking into account
836 * the compression type:
837 *
838 * BI_RGB - the RGB order should be maintained.
839 * BI_BITFIELDS - use bitPos array that was built
840 * according to bitfields masks.
841 */
842 for (int j = 0, m = 0; j < scanlineBytes; m++) {
843 spixels[m] = 0;
844 if (compressionType == BMPConstants.BI_RGB) {
845 /*
846 * please note that despite other cases,
847 * the 16bpp BI_RGB requires the RGB data order
848 */
849 spixels[m] = (short)
850 (((0x1f & pixels[j ]) << 10) |
851 ((0x1f & pixels[j + 1]) << 5) |
852 ((0x1f & pixels[j + 2]) ));
853 j += 3;
854 } else {
855 for(int i = 0 ; i < numBands; i++, j++) {
856 spixels[m] |=
857 (((pixels[j]) << bitPos[i]) & bitMasks[i]);
858 }
859 }
860 }
861 stream.writeShorts(spixels, 0, spixels.length);
862 break;
863
864 case 24:
865 if (numBands == 3) {
866 for (int j=0; j<scanlineBytes; j+=3) {
867 // Since BMP needs BGR format
868 bpixels[k++] = (byte)(pixels[l+2]);
869 bpixels[k++] = (byte)(pixels[l+1]);
870 bpixels[k++] = (byte)(pixels[l]);
871 l+=3;
872 }
873 stream.write(bpixels, 0, scanlineBytes);
874 } else {
875 // Case where IndexColorModel had > 256 colors.
876 int entries = icm.getMapSize();
877
878 byte r[] = new byte[entries];
879 byte g[] = new byte[entries];
880 byte b[] = new byte[entries];
881
882 icm.getReds(r);
883 icm.getGreens(g);
884 icm.getBlues(b);
885 int index;
886
887 for (int j=0; j<scanlineBytes; j++) {
888 index = pixels[l];
889 bpixels[k++] = b[index];
890 bpixels[k++] = g[index];
891 bpixels[k++] = b[index];
892 l++;
893 }
894 stream.write(bpixels, 0, scanlineBytes*3);
895 }
896 break;
897
898 case 32:
899 if (ipixels == null)
900 ipixels = new int[scanlineBytes / numBands];
901 if (numBands == 3) {
902 /*
903 * We expect that pixel data comes in RGB order.
904 * We will assemble int pixel taking into account
905 * the compression type.
906 *
907 * BI_RGB - the BGR order should be used.
908 * BI_BITFIELDS - use bitPos array that was built
909 * according to bitfields masks.
910 */
911 for (int j = 0, m = 0; j < scanlineBytes; m++) {
912 ipixels[m] = 0;
913 if (compressionType == BMPConstants.BI_RGB) {
914 ipixels[m] =
915 ((0xff & pixels[j + 2]) << 16) |
916 ((0xff & pixels[j + 1]) << 8) |
917 ((0xff & pixels[j ]) );
918 j += 3;
919 } else {
920 for(int i = 0 ; i < numBands; i++, j++) {
921 ipixels[m] |=
922 (((pixels[j]) << bitPos[i]) & bitMasks[i]);
923 }
924 }
925 }
926 } else {
927 // We have two possibilities here:
928 // 1. we are writing the indexed image with bitfields
929 // compression (this covers also the case of BYTE_BINARY)
930 // => use icm to get actual RGB color values.
931 // 2. we are writing the gray-scaled image with BI_BITFIELDS
932 // compression
933 // => just replicate the level of gray to color components.
934 for (int j = 0; j < scanlineBytes; j++) {
935 if (icm != null) {
936 ipixels[j] = icm.getRGB(pixels[j]);
937 } else {
938 ipixels[j] =
939 pixels[j] << 16 | pixels[j] << 8 | pixels[j];
940 }
941 }
942 }
943 stream.writeInts(ipixels, 0, ipixels.length);
944 break;
945 }
946
947 // Write out the padding
948 if (compressionType == BMPConstants.BI_RGB ||
949 compressionType == BMPConstants.BI_BITFIELDS)
950 {
951 for(k=0; k<padding; k++) {
952 stream.writeByte(0);
953 }
954 }
955 }
956
957 private void encodeRLE8(byte[] bpixels, int scanlineBytes)
958 throws IOException{
959
960 int runCount = 1, absVal = -1, j = -1;
961 byte runVal = 0, nextVal =0 ;
962
963 runVal = bpixels[++j];
964 byte[] absBuf = new byte[256];
965
966 while (j < scanlineBytes-1) {
967 nextVal = bpixels[++j];
968 if (nextVal == runVal ){
969 if(absVal >= 3 ){
970 /// Check if there was an existing Absolute Run
971 stream.writeByte(0);
972 stream.writeByte(absVal);
973 incCompImageSize(2);
974 for(int a=0; a<absVal;a++){
975 stream.writeByte(absBuf[a]);
976 incCompImageSize(1);
977 }
978 if (!isEven(absVal)){
979 //Padding
980 stream.writeByte(0);
981 incCompImageSize(1);
982 }
983 }
984 else if(absVal > -1){
985 /// Absolute Encoding for less than 3
986 /// treated as regular encoding
987 /// Do not include the last element since it will
988 /// be inclued in the next encoding/run
989 for (int b=0;b<absVal;b++){
990 stream.writeByte(1);
991 stream.writeByte(absBuf[b]);
992 incCompImageSize(2);
993 }
994 }
995 absVal = -1;
996 runCount++;
997 if (runCount == 256){
998 /// Only 255 values permitted
999 stream.writeByte(runCount-1);
1000 stream.writeByte(runVal);
1001 incCompImageSize(2);
1002 runCount = 1;
1003 }
1004 }
1005 else {
1006 if (runCount > 1){
1007 /// If there was an existing run
1008 stream.writeByte(runCount);
1009 stream.writeByte(runVal);
1010 incCompImageSize(2);
1011 } else if (absVal < 0){
1012 // First time..
1013 absBuf[++absVal] = runVal;
1014 absBuf[++absVal] = nextVal;
1015 } else if (absVal < 254){
1016 // 0-254 only
1017 absBuf[++absVal] = nextVal;
1018 } else {
1019 stream.writeByte(0);
1020 stream.writeByte(absVal+1);
1021 incCompImageSize(2);
1022 for(int a=0; a<=absVal;a++){
1023 stream.writeByte(absBuf[a]);
1024 incCompImageSize(1);
1025 }
1026 // padding since 255 elts is not even
1027 stream.writeByte(0);
1028 incCompImageSize(1);
1029 absVal = -1;
1030 }
1031 runVal = nextVal;
1032 runCount = 1;
1033 }
1034
1035 if (j == scanlineBytes-1){ // EOF scanline
1036 // Write the run
1037 if (absVal == -1){
1038 stream.writeByte(runCount);
1039 stream.writeByte(runVal);
1040 incCompImageSize(2);
1041 runCount = 1;
1042 }
1043 else {
1044 // write the Absolute Run
1045 if(absVal >= 2){
1046 stream.writeByte(0);
1047 stream.writeByte(absVal+1);
1048 incCompImageSize(2);
1049 for(int a=0; a<=absVal;a++){
1050 stream.writeByte(absBuf[a]);
1051 incCompImageSize(1);
1052 }
1053 if (!isEven(absVal+1)){
1054 //Padding
1055 stream.writeByte(0);
1056 incCompImageSize(1);
1057 }
1058
1059 }
1060 else if(absVal > -1){
1061 for (int b=0;b<=absVal;b++){
1062 stream.writeByte(1);
1063 stream.writeByte(absBuf[b]);
1064 incCompImageSize(2);
1065 }
1066 }
1067 }
1068 /// EOF scanline
1069
1070 stream.writeByte(0);
1071 stream.writeByte(0);
1072 incCompImageSize(2);
1073 }
1074 }
1075 }
1076
1077 private void encodeRLE4(byte[] bipixels, int scanlineBytes)
1078 throws IOException {
1079
1080 int runCount=2, absVal=-1, j=-1, pixel=0, q=0;
1081 byte runVal1=0, runVal2=0, nextVal1=0, nextVal2=0;
1082 byte[] absBuf = new byte[256];
1083
1084
1085 runVal1 = bipixels[++j];
1086 runVal2 = bipixels[++j];
1087
1088 while (j < scanlineBytes-2){
1089 nextVal1 = bipixels[++j];
1090 nextVal2 = bipixels[++j];
1091
1092 if (nextVal1 == runVal1 ) {
1093
1094 //Check if there was an existing Absolute Run
1095 if(absVal >= 4){
1096 stream.writeByte(0);
1097 stream.writeByte(absVal - 1);
1098 incCompImageSize(2);
1099 // we need to exclude last 2 elts, similarity of
1100 // which caused to enter this part of the code
1101 for(int a=0; a<absVal-2;a+=2){
1102 pixel = (absBuf[a] << 4) | absBuf[a+1];
1103 stream.writeByte((byte)pixel);
1104 incCompImageSize(1);
1105 }
1106 // if # of elts is odd - read the last element
1107 if(!(isEven(absVal-1))){
1108 q = absBuf[absVal-2] << 4| 0;
1109 stream.writeByte(q);
1110 incCompImageSize(1);
1111 }
1112 // Padding to word align absolute encoding
1113 if ( !isEven((int)Math.ceil((absVal-1)/2)) ) {
1114 stream.writeByte(0);
1115 incCompImageSize(1);
1116 }
1117 } else if (absVal > -1){
1118 stream.writeByte(2);
1119 pixel = (absBuf[0] << 4) | absBuf[1];
1120 stream.writeByte(pixel);
1121 incCompImageSize(2);
1122 }
1123 absVal = -1;
1124
1125 if (nextVal2 == runVal2){
1126 // Even runlength
1127 runCount+=2;
1128 if(runCount == 256){
1129 stream.writeByte(runCount-1);
1130 pixel = ( runVal1 << 4) | runVal2;
1131 stream.writeByte(pixel);
1132 incCompImageSize(2);
1133 runCount =2;
1134 if(j< scanlineBytes - 1){
1135 runVal1 = runVal2;
1136 runVal2 = bipixels[++j];
1137 } else {
1138 stream.writeByte(01);
1139 int r = runVal2 << 4 | 0;
1140 stream.writeByte(r);
1141 incCompImageSize(2);
1142 runCount = -1;/// Only EOF required now
1143 }
1144 }
1145 } else {
1146 // odd runlength and the run ends here
1147 // runCount wont be > 254 since 256/255 case will
1148 // be taken care of in above code.
1149 runCount++;
1150 pixel = ( runVal1 << 4) | runVal2;
1151 stream.writeByte(runCount);
1152 stream.writeByte(pixel);
1153 incCompImageSize(2);
1154 runCount = 2;
1155 runVal1 = nextVal2;
1156 // If end of scanline
1157 if (j < scanlineBytes -1){
1158 runVal2 = bipixels[++j];
1159 }else {
1160 stream.writeByte(01);
1161 int r = nextVal2 << 4 | 0;
1162 stream.writeByte(r);
1163 incCompImageSize(2);
1164 runCount = -1;/// Only EOF required now
1165 }
1166
1167 }
1168 } else{
1169 // Check for existing run
1170 if (runCount > 2){
1171 pixel = ( runVal1 << 4) | runVal2;
1172 stream.writeByte(runCount);
1173 stream.writeByte(pixel);
1174 incCompImageSize(2);
1175 } else if (absVal < 0){ // first time
1176 absBuf[++absVal] = runVal1;
1177 absBuf[++absVal] = runVal2;
1178 absBuf[++absVal] = nextVal1;
1179 absBuf[++absVal] = nextVal2;
1180 } else if (absVal < 253){ // only 255 elements
1181 absBuf[++absVal] = nextVal1;
1182 absBuf[++absVal] = nextVal2;
1183 } else {
1184 stream.writeByte(0);
1185 stream.writeByte(absVal+1);
1186 incCompImageSize(2);
1187 for(int a=0; a<absVal;a+=2){
1188 pixel = (absBuf[a] << 4) | absBuf[a+1];
1189 stream.writeByte((byte)pixel);
1190 incCompImageSize(1);
1191 }
1192 // Padding for word align
1193 // since it will fit into 127 bytes
1194 stream.writeByte(0);
1195 incCompImageSize(1);
1196 absVal = -1;
1197 }
1198
1199 runVal1 = nextVal1;
1200 runVal2 = nextVal2;
1201 runCount = 2;
1202 }
1203 // Handle the End of scanline for the last 2 4bits
1204 if (j >= scanlineBytes-2 ) {
1205 if (absVal == -1 && runCount >= 2){
1206 if (j == scanlineBytes-2){
1207 if(bipixels[++j] == runVal1){
1208 runCount++;
1209 pixel = ( runVal1 << 4) | runVal2;
1210 stream.writeByte(runCount);
1211 stream.writeByte(pixel);
1212 incCompImageSize(2);
1213 } else {
1214 pixel = ( runVal1 << 4) | runVal2;
1215 stream.writeByte(runCount);
1216 stream.writeByte(pixel);
1217 stream.writeByte(01);
1218 pixel = bipixels[j]<<4 |0;
1219 stream.writeByte(pixel);
1220 int n = bipixels[j]<<4|0;
1221 incCompImageSize(4);
1222 }
1223 } else {
1224 stream.writeByte(runCount);
1225 pixel =( runVal1 << 4) | runVal2 ;
1226 stream.writeByte(pixel);
1227 incCompImageSize(2);
1228 }
1229 } else if(absVal > -1){
1230 if (j == scanlineBytes-2){
1231 absBuf[++absVal] = bipixels[++j];
1232 }
1233 if (absVal >=2){
1234 stream.writeByte(0);
1235 stream.writeByte(absVal+1);
1236 incCompImageSize(2);
1237 for(int a=0; a<absVal;a+=2){
1238 pixel = (absBuf[a] << 4) | absBuf[a+1];
1239 stream.writeByte((byte)pixel);
1240 incCompImageSize(1);
1241 }
1242 if(!(isEven(absVal+1))){
1243 q = absBuf[absVal] << 4|0;
1244 stream.writeByte(q);
1245 incCompImageSize(1);
1246 }
1247
1248 // Padding
1249 if ( !isEven((int)Math.ceil((absVal+1)/2)) ) {
1250 stream.writeByte(0);
1251 incCompImageSize(1);
1252 }
1253
1254 } else {
1255 switch (absVal){
1256 case 0:
1257 stream.writeByte(1);
1258 int n = absBuf[0]<<4 | 0;
1259 stream.writeByte(n);
1260 incCompImageSize(2);
1261 break;
1262 case 1:
1263 stream.writeByte(2);
1264 pixel = (absBuf[0] << 4) | absBuf[1];
1265 stream.writeByte(pixel);
1266 incCompImageSize(2);
1267 break;
1268 }
1269 }
1270
1271 }
1272 stream.writeByte(0);
1273 stream.writeByte(0);
1274 incCompImageSize(2);
1275 }
1276 }
1277 }
1278
1279
1280 private synchronized void incCompImageSize(int value){
1281 compImageSize = compImageSize + value;
1282 }
1283
1284 private boolean isEven(int number) {
1285 return (number%2 == 0 ? true : false);
1286 }
1287
1288 private void writeFileHeader(int fileSize, int offset) throws IOException {
1289 // magic value
1290 stream.writeByte('B');
1291 stream.writeByte('M');
1292
1293 // File size
1294 stream.writeInt(fileSize);
1295
1296 // reserved1 and reserved2
1297 stream.writeInt(0);
1298
1299 // offset to image data
1300 stream.writeInt(offset);
1301 }
1302
1303
1304 private void writeInfoHeader(int headerSize,
1305 int bitsPerPixel) throws IOException {
1306 // size of header
1307 stream.writeInt(headerSize);
1308
1309 // width
1310 stream.writeInt(w);
1311
1312 // height
1313 stream.writeInt(isTopDown ? -h : h);
1314
1315 // number of planes
1316 stream.writeShort(1);
1317
1318 // Bits Per Pixel
1319 stream.writeShort(bitsPerPixel);
1320 }
1321
1322 private void writeSize(int dword, int offset) throws IOException {
1323 stream.skipBytes(offset);
1324 stream.writeInt(dword);
1325 }
1326
1327 public void reset() {
1328 super.reset();
1329 stream = null;
1330 }
1331
1332 private int getCompressionType(String typeString) {
1333 for (int i = 0; i < BMPConstants.compressionTypeNames.length; i++)
1334 if (BMPConstants.compressionTypeNames[i].equals(typeString))
1335 return i;
1336 return 0;
1337 }
1338
1339 private void writeEmbedded(IIOImage image,
1340 ImageWriteParam bmpParam) throws IOException {
1341 String format =
1342 compressionType == BMPConstants.BI_JPEG ? "jpeg" : "png";
1343 Iterator iterator = ImageIO.getImageWritersByFormatName(format);
1344 ImageWriter writer = null;
1345 if (iterator.hasNext())
1346 writer = (ImageWriter)iterator.next();
1347 if (writer != null) {
1348 if (embedded_stream == null) {
1349 throw new RuntimeException("No stream for writing embedded image!");
1350 }
1351
1352 writer.addIIOWriteProgressListener(new IIOWriteProgressAdapter() {
1353 public void imageProgress(ImageWriter source, float percentageDone) {
1354 processImageProgress(percentageDone);
1355 }
1356 });
1357
1358 writer.addIIOWriteWarningListener(new IIOWriteWarningListener() {
1359 public void warningOccurred(ImageWriter source, int imageIndex, String warning) {
1360 processWarningOccurred(imageIndex, warning);
1361 }
1362 });
1363
1364 writer.setOutput(ImageIO.createImageOutputStream(embedded_stream));
1365 ImageWriteParam param = writer.getDefaultWriteParam();
1366 //param.setDestinationBands(bmpParam.getDestinationBands());
1367 param.setDestinationOffset(bmpParam.getDestinationOffset());
1368 param.setSourceBands(bmpParam.getSourceBands());
1369 param.setSourceRegion(bmpParam.getSourceRegion());
1370 param.setSourceSubsampling(bmpParam.getSourceXSubsampling(),
1371 bmpParam.getSourceYSubsampling(),
1372 bmpParam.getSubsamplingXOffset(),
1373 bmpParam.getSubsamplingYOffset());
1374 writer.write(null, image, param);
1375 } else
1376 throw new RuntimeException(I18N.getString("BMPImageWrite5") + " " + format);
1377
1378 }
1379
1380 private int firstLowBit(int num) {
1381 int count = 0;
1382 while ((num & 1) == 0) {
1383 count++;
1384 num >>>= 1;
1385 }
1386 return count;
1387 }
1388
1389 private class IIOWriteProgressAdapter implements IIOWriteProgressListener {
1390
1391 public void imageComplete(ImageWriter source) {
1392 }
1393
1394 public void imageProgress(ImageWriter source, float percentageDone) {
1395 }
1396
1397 public void imageStarted(ImageWriter source, int imageIndex) {
1398 }
1399
1400 public void thumbnailComplete(ImageWriter source) {
1401 }
1402
1403 public void thumbnailProgress(ImageWriter source, float percentageDone) {
1404 }
1405
1406 public void thumbnailStarted(ImageWriter source, int imageIndex, int thumbnailIndex) {
1407 }
1408
1409 public void writeAborted(ImageWriter source) {
1410 }
1411 }
1412
1413 /*
1414 * Returns preferred compression type for given image.
1415 * The default compression type is BI_RGB, but some image types can't be
1416 * encodeed with using default compression without cahnge color resolution.
1417 * For example, TYPE_USHORT_565_RGB may be encodeed only by using BI_BITFIELDS
1418 * compression type.
1419 *
1420 * NB: we probably need to extend this method if we encounter other image
1421 * types which can not be encoded with BI_RGB compression type.
1422 */
1423 protected int getPreferredCompressionType(ColorModel cm, SampleModel sm) {
1424 ImageTypeSpecifier imageType = new ImageTypeSpecifier(cm, sm);
1425 return getPreferredCompressionType(imageType);
1426 }
1427
1428 protected int getPreferredCompressionType(ImageTypeSpecifier imageType) {
1429 if (imageType.getBufferedImageType() == BufferedImage.TYPE_USHORT_565_RGB) {
1430 return BI_BITFIELDS;
1431 }
1432 return BI_RGB;
1433 }
1434
1435 /*
1436 * Check whether we can encode image of given type using compression method in question.
1437 *
1438 * For example, TYPE_USHORT_565_RGB can be encodeed with BI_BITFIELDS compression only.
1439 *
1440 * NB: method should be extended if other cases when we can not encode
1441 * with given compression will be discovered.
1442 */
1443 protected boolean canEncodeImage(int compression, ColorModel cm, SampleModel sm) {
1444 ImageTypeSpecifier imgType = new ImageTypeSpecifier(cm, sm);
1445 return canEncodeImage(compression, imgType);
1446 }
1447
1448 protected boolean canEncodeImage(int compression, ImageTypeSpecifier imgType) {
1449 ImageWriterSpi spi = this.getOriginatingProvider();
1450 if (!spi.canEncodeImage(imgType)) {
1451 return false;
1452 }
1453 int biType = imgType.getBufferedImageType();
1454 int bpp = imgType.getColorModel().getPixelSize();
1455 if (compressionType == BI_RLE4 && bpp != 4) {
1456 // only 4bpp images can be encoded as BI_RLE4
1457 return false;
1458 }
1459 if (compressionType == BI_RLE8 && bpp != 8) {
1460 // only 8bpp images can be encoded as BI_RLE8
1461 return false;
1462 }
1463 if (bpp == 16) {
1464 /*
1465 * Technically we expect that we may be able to
1466 * encode only some of SinglePixelPackedSampleModel
1467 * images here.
1468 *
1469 * In addition we should take into account following:
1470 *
1471 * 1. BI_RGB case, according to the MSDN description:
1472 *
1473 * The bitmap has a maximum of 2^16 colors. If the
1474 * biCompression member of the BITMAPINFOHEADER is BI_RGB,
1475 * the bmiColors member of BITMAPINFO is NULL. Each WORD
1476 * in the bitmap array represents a single pixel. The
1477 * relative intensities of red, green, and blue are
1478 * represented with five bits for each color component.
1479 *
1480 * 2. BI_BITFIELDS case, according ot the MSDN description:
1481 *
1482 * Windows 95/98/Me: When the biCompression member is
1483 * BI_BITFIELDS, the system supports only the following
1484 * 16bpp color masks: A 5-5-5 16-bit image, where the blue
1485 * mask is 0x001F, the green mask is 0x03E0, and the red mask
1486 * is 0x7C00; and a 5-6-5 16-bit image, where the blue mask
1487 * is 0x001F, the green mask is 0x07E0, and the red mask is
1488 * 0xF800.
1489 */
1490 boolean canUseRGB = false;
1491 boolean canUseBITFIELDS = false;
1492
1493 SampleModel sm = imgType.getSampleModel();
1494 if (sm instanceof SinglePixelPackedSampleModel) {
1495 int[] sizes =
1496 ((SinglePixelPackedSampleModel)sm).getSampleSize();
1497
1498 canUseRGB = true;
1499 canUseBITFIELDS = true;
1500 for (int i = 0; i < sizes.length; i++) {
1501 canUseRGB &= (sizes[i] == 5);
1502 canUseBITFIELDS &= ((sizes[i] == 5) ||
1503 (i == 1 && sizes[i] == 6));
1504 }
1505 }
1506
1507 return (((compressionType == BI_RGB) && canUseRGB) ||
1508 ((compressionType == BI_BITFIELDS) && canUseBITFIELDS));
1509 }
1510 return true;
1511 }
1512
1513 protected void writeMaskToPalette(int mask, int i,
1514 byte[] r, byte[]g, byte[] b, byte[]a) {
1515 b[i] = (byte)(0xff & (mask >> 24));
1516 g[i] = (byte)(0xff & (mask >> 16));
1517 r[i] = (byte)(0xff & (mask >> 8));
1518 a[i] = (byte)(0xff & mask);
1519 }
1520
1521 private int roundBpp(int x) {
1522 if (x <= 8) {
1523 return 8;
1524 } else if (x <= 16) {
1525 return 16;
1526 } if (x <= 24) {
1527 return 24;
1528 } else {
1529 return 32;
1530 }
1531 }
1532}