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+
26+package com.sun.imageio.plugins.bmp;
27+
28+import java.awt.Point;
29+import java.awt.Rectangle;
30+import java.awt.image.ColorModel;
31+import java.awt.image.ComponentSampleModel;
32+import java.awt.image.DataBuffer;
33+import java.awt.image.DataBufferByte;
34+import java.awt.image.DataBufferInt;
35+import java.awt.image.DataBufferShort;
36+import java.awt.image.DataBufferUShort;
37+import java.awt.image.DirectColorModel;
38+import java.awt.image.IndexColorModel;
39+import java.awt.image.MultiPixelPackedSampleModel;
40+import java.awt.image.BandedSampleModel;
41+import java.awt.image.Raster;
42+import java.awt.image.RenderedImage;
43+import java.awt.image.SampleModel;
44+import java.awt.image.SinglePixelPackedSampleModel;
45+import java.awt.image.WritableRaster;
46+import java.awt.image.BufferedImage;
47+
48+import java.io.IOException;
49+import java.io.ByteArrayOutputStream;
50+import java.nio.ByteOrder;
51+import java.util.Iterator;
52+
53+import javax.imageio.IIOImage;
54+import javax.imageio.IIOException;
55+import javax.imageio.ImageIO;
56+import javax.imageio.ImageTypeSpecifier;
57+import javax.imageio.ImageWriteParam;
58+import javax.imageio.ImageWriter;
59+import javax.imageio.metadata.IIOMetadata;
60+import javax.imageio.metadata.IIOMetadataNode;
61+import javax.imageio.metadata.IIOMetadataFormatImpl;
62+import javax.imageio.metadata.IIOInvalidTreeException;
63+import javax.imageio.spi.ImageWriterSpi;
64+import javax.imageio.stream.ImageOutputStream;
65+import javax.imageio.event.IIOWriteProgressListener;
66+import javax.imageio.event.IIOWriteWarningListener;
67+
68+import org.w3c.dom.Node;
69+import org.w3c.dom.NodeList;
70+
71+import javax.imageio.plugins.bmp.BMPImageWriteParam;
72+import com.sun.imageio.plugins.common.ImageUtil;
73+import 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+ */
84+public 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+}