0N/A/*
2362N/A * Copyright (c) 2003, 2007, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/Apackage com.sun.imageio.plugins.bmp;
0N/A
0N/Aimport java.awt.Point;
0N/Aimport java.awt.Rectangle;
0N/Aimport java.awt.image.ColorModel;
0N/Aimport java.awt.image.ComponentSampleModel;
0N/Aimport java.awt.image.DataBuffer;
0N/Aimport java.awt.image.DataBufferByte;
0N/Aimport java.awt.image.DataBufferInt;
0N/Aimport java.awt.image.DataBufferShort;
0N/Aimport java.awt.image.DataBufferUShort;
0N/Aimport java.awt.image.DirectColorModel;
0N/Aimport java.awt.image.IndexColorModel;
0N/Aimport java.awt.image.MultiPixelPackedSampleModel;
0N/Aimport java.awt.image.BandedSampleModel;
0N/Aimport java.awt.image.Raster;
0N/Aimport java.awt.image.RenderedImage;
0N/Aimport java.awt.image.SampleModel;
0N/Aimport java.awt.image.SinglePixelPackedSampleModel;
0N/Aimport java.awt.image.WritableRaster;
0N/Aimport java.awt.image.BufferedImage;
0N/A
0N/Aimport java.io.IOException;
0N/Aimport java.io.ByteArrayOutputStream;
0N/Aimport java.nio.ByteOrder;
0N/Aimport java.util.Iterator;
0N/A
0N/Aimport javax.imageio.IIOImage;
0N/Aimport javax.imageio.IIOException;
0N/Aimport javax.imageio.ImageIO;
0N/Aimport javax.imageio.ImageTypeSpecifier;
0N/Aimport javax.imageio.ImageWriteParam;
0N/Aimport javax.imageio.ImageWriter;
0N/Aimport javax.imageio.metadata.IIOMetadata;
0N/Aimport javax.imageio.metadata.IIOMetadataNode;
0N/Aimport javax.imageio.metadata.IIOMetadataFormatImpl;
0N/Aimport javax.imageio.metadata.IIOInvalidTreeException;
0N/Aimport javax.imageio.spi.ImageWriterSpi;
0N/Aimport javax.imageio.stream.ImageOutputStream;
0N/Aimport javax.imageio.event.IIOWriteProgressListener;
0N/Aimport javax.imageio.event.IIOWriteWarningListener;
0N/A
0N/Aimport org.w3c.dom.Node;
0N/Aimport org.w3c.dom.NodeList;
0N/A
0N/Aimport javax.imageio.plugins.bmp.BMPImageWriteParam;
0N/Aimport com.sun.imageio.plugins.common.ImageUtil;
0N/Aimport com.sun.imageio.plugins.common.I18N;
0N/A
0N/A/**
0N/A * The Java Image IO plugin writer for encoding a binary RenderedImage into
0N/A * a BMP format.
0N/A *
0N/A * The encoding process may clip, subsample using the parameters
0N/A * specified in the <code>ImageWriteParam</code>.
0N/A *
0N/A * @see javax.imageio.plugins.bmp.BMPImageWriteParam
0N/A */
0N/Apublic class BMPImageWriter extends ImageWriter implements BMPConstants {
0N/A /** The output stream to write into */
0N/A private ImageOutputStream stream = null;
0N/A private ByteArrayOutputStream embedded_stream = null;
0N/A private int version;
0N/A private int compressionType;
0N/A private boolean isTopDown;
0N/A private int w, h;
0N/A private int compImageSize = 0;
0N/A private int[] bitMasks;
0N/A private int[] bitPos;
0N/A private byte[] bpixels;
0N/A private short[] spixels;
0N/A private int[] ipixels;
0N/A
0N/A /** Constructs <code>BMPImageWriter</code> based on the provided
0N/A * <code>ImageWriterSpi</code>.
0N/A */
0N/A public BMPImageWriter(ImageWriterSpi originator) {
0N/A super(originator);
0N/A }
0N/A
0N/A public void setOutput(Object output) {
0N/A super.setOutput(output); // validates output
0N/A if (output != null) {
0N/A if (!(output instanceof ImageOutputStream))
0N/A throw new IllegalArgumentException(I18N.getString("BMPImageWriter0"));
0N/A this.stream = (ImageOutputStream)output;
0N/A stream.setByteOrder(ByteOrder.LITTLE_ENDIAN);
0N/A } else
0N/A this.stream = null;
0N/A }
0N/A
0N/A public ImageWriteParam getDefaultWriteParam() {
0N/A return new BMPImageWriteParam();
0N/A }
0N/A
0N/A public IIOMetadata getDefaultStreamMetadata(ImageWriteParam param) {
0N/A return null;
0N/A }
0N/A
0N/A public IIOMetadata getDefaultImageMetadata(ImageTypeSpecifier imageType,
0N/A ImageWriteParam param) {
0N/A BMPMetadata meta = new BMPMetadata();
0N/A meta.bmpVersion = VERSION_3;
0N/A meta.compression = getPreferredCompressionType(imageType);
0N/A if (param != null
0N/A && param.getCompressionMode() == ImageWriteParam.MODE_EXPLICIT) {
0N/A meta.compression = getCompressionType(param.getCompressionType());
0N/A }
0N/A meta.bitsPerPixel = (short)imageType.getColorModel().getPixelSize();
0N/A return meta;
0N/A }
0N/A
0N/A public IIOMetadata convertStreamMetadata(IIOMetadata inData,
0N/A ImageWriteParam param) {
0N/A return null;
0N/A }
0N/A
0N/A public IIOMetadata convertImageMetadata(IIOMetadata metadata,
0N/A ImageTypeSpecifier type,
0N/A ImageWriteParam param) {
0N/A return null;
0N/A }
0N/A
0N/A public boolean canWriteRasters() {
0N/A return true;
0N/A }
0N/A
0N/A public void write(IIOMetadata streamMetadata,
0N/A IIOImage image,
0N/A ImageWriteParam param) throws IOException {
0N/A
0N/A if (stream == null) {
0N/A throw new IllegalStateException(I18N.getString("BMPImageWriter7"));
0N/A }
0N/A
0N/A if (image == null) {
0N/A throw new IllegalArgumentException(I18N.getString("BMPImageWriter8"));
0N/A }
0N/A
0N/A clearAbortRequest();
0N/A processImageStarted(0);
0N/A if (param == null)
0N/A param = getDefaultWriteParam();
0N/A
0N/A BMPImageWriteParam bmpParam = (BMPImageWriteParam)param;
0N/A
0N/A // Default is using 24 bits per pixel.
0N/A int bitsPerPixel = 24;
0N/A boolean isPalette = false;
0N/A int paletteEntries = 0;
0N/A IndexColorModel icm = null;
0N/A
0N/A RenderedImage input = null;
0N/A Raster inputRaster = null;
0N/A boolean writeRaster = image.hasRaster();
0N/A Rectangle sourceRegion = param.getSourceRegion();
0N/A SampleModel sampleModel = null;
0N/A ColorModel colorModel = null;
0N/A
0N/A compImageSize = 0;
0N/A
0N/A if (writeRaster) {
0N/A inputRaster = image.getRaster();
0N/A sampleModel = inputRaster.getSampleModel();
0N/A colorModel = ImageUtil.createColorModel(null, sampleModel);
0N/A if (sourceRegion == null)
0N/A sourceRegion = inputRaster.getBounds();
0N/A else
0N/A sourceRegion = sourceRegion.intersection(inputRaster.getBounds());
0N/A } else {
0N/A input = image.getRenderedImage();
0N/A sampleModel = input.getSampleModel();
0N/A colorModel = input.getColorModel();
0N/A Rectangle rect = new Rectangle(input.getMinX(), input.getMinY(),
0N/A input.getWidth(), input.getHeight());
0N/A if (sourceRegion == null)
0N/A sourceRegion = rect;
0N/A else
0N/A sourceRegion = sourceRegion.intersection(rect);
0N/A }
0N/A
0N/A IIOMetadata imageMetadata = image.getMetadata();
0N/A BMPMetadata bmpImageMetadata = null;
0N/A if (imageMetadata != null
0N/A && imageMetadata instanceof BMPMetadata)
0N/A {
0N/A bmpImageMetadata = (BMPMetadata)imageMetadata;
0N/A } else {
0N/A ImageTypeSpecifier imageType =
0N/A new ImageTypeSpecifier(colorModel, sampleModel);
0N/A
0N/A bmpImageMetadata = (BMPMetadata)getDefaultImageMetadata(imageType,
0N/A param);
0N/A }
0N/A
0N/A if (sourceRegion.isEmpty())
0N/A throw new RuntimeException(I18N.getString("BMPImageWrite0"));
0N/A
0N/A int scaleX = param.getSourceXSubsampling();
0N/A int scaleY = param.getSourceYSubsampling();
0N/A int xOffset = param.getSubsamplingXOffset();
0N/A int yOffset = param.getSubsamplingYOffset();
0N/A
0N/A // cache the data type;
0N/A int dataType = sampleModel.getDataType();
0N/A
0N/A sourceRegion.translate(xOffset, yOffset);
0N/A sourceRegion.width -= xOffset;
0N/A sourceRegion.height -= yOffset;
0N/A
0N/A int minX = sourceRegion.x / scaleX;
0N/A int minY = sourceRegion.y / scaleY;
0N/A w = (sourceRegion.width + scaleX - 1) / scaleX;
0N/A h = (sourceRegion.height + scaleY - 1) / scaleY;
0N/A xOffset = sourceRegion.x % scaleX;
0N/A yOffset = sourceRegion.y % scaleY;
0N/A
0N/A Rectangle destinationRegion = new Rectangle(minX, minY, w, h);
0N/A boolean noTransform = destinationRegion.equals(sourceRegion);
0N/A
0N/A // Raw data can only handle bytes, everything greater must be ASCII.
0N/A int[] sourceBands = param.getSourceBands();
0N/A boolean noSubband = true;
0N/A int numBands = sampleModel.getNumBands();
0N/A
0N/A if (sourceBands != null) {
0N/A sampleModel = sampleModel.createSubsetSampleModel(sourceBands);
0N/A colorModel = null;
0N/A noSubband = false;
0N/A numBands = sampleModel.getNumBands();
0N/A } else {
0N/A sourceBands = new int[numBands];
0N/A for (int i = 0; i < numBands; i++)
0N/A sourceBands[i] = i;
0N/A }
0N/A
0N/A int[] bandOffsets = null;
0N/A boolean bgrOrder = true;
0N/A
0N/A if (sampleModel instanceof ComponentSampleModel) {
0N/A bandOffsets = ((ComponentSampleModel)sampleModel).getBandOffsets();
0N/A if (sampleModel instanceof BandedSampleModel) {
0N/A // for images with BandedSampleModel we can not work
0N/A // with raster directly and must use writePixels()
0N/A bgrOrder = false;
0N/A } else {
0N/A // we can work with raster directly only in case of
0N/A // BGR component order.
0N/A // In any other case we must use writePixels()
0N/A for (int i = 0; i < bandOffsets.length; i++) {
0N/A bgrOrder &= (bandOffsets[i] == (bandOffsets.length - i - 1));
0N/A }
0N/A }
0N/A } else {
0N/A if (sampleModel instanceof SinglePixelPackedSampleModel) {
0N/A
0N/A // BugId 4892214: we can not work with raster directly
0N/A // if image have different color order than RGB.
0N/A // We should use writePixels() for such images.
0N/A int[] bitOffsets = ((SinglePixelPackedSampleModel)sampleModel).getBitOffsets();
0N/A for (int i=0; i<bitOffsets.length-1; i++) {
0N/A bgrOrder &= bitOffsets[i] > bitOffsets[i+1];
0N/A }
0N/A }
0N/A }
0N/A
0N/A if (bandOffsets == null) {
0N/A // we will use getPixels() to extract pixel data for writePixels()
0N/A // Please note that getPixels() provides rgb bands order.
0N/A bandOffsets = new int[numBands];
0N/A for (int i = 0; i < numBands; i++)
0N/A bandOffsets[i] = i;
0N/A }
0N/A
0N/A noTransform &= bgrOrder;
0N/A
0N/A int sampleSize[] = sampleModel.getSampleSize();
0N/A
0N/A //XXX: check more
0N/A
0N/A // Number of bytes that a scanline for the image written out will have.
0N/A int destScanlineBytes = w * numBands;
0N/A
0N/A switch(bmpParam.getCompressionMode()) {
0N/A case ImageWriteParam.MODE_EXPLICIT:
0N/A compressionType = getCompressionType(bmpParam.getCompressionType());
0N/A break;
0N/A case ImageWriteParam.MODE_COPY_FROM_METADATA:
0N/A compressionType = bmpImageMetadata.compression;
0N/A break;
0N/A case ImageWriteParam.MODE_DEFAULT:
0N/A compressionType = getPreferredCompressionType(colorModel, sampleModel);
0N/A break;
0N/A default:
0N/A // ImageWriteParam.MODE_DISABLED:
0N/A compressionType = BI_RGB;
0N/A }
0N/A
0N/A if (!canEncodeImage(compressionType, colorModel, sampleModel)) {
0N/A throw new IOException("Image can not be encoded with compression type "
0N/A + compressionTypeNames[compressionType]);
0N/A }
0N/A
0N/A byte r[] = null, g[] = null, b[] = null, a[] = null;
0N/A
0N/A if (compressionType == BMPConstants.BI_BITFIELDS) {
0N/A bitsPerPixel =
0N/A DataBuffer.getDataTypeSize(sampleModel.getDataType());
0N/A
0N/A if (bitsPerPixel != 16 && bitsPerPixel != 32) {
0N/A // we should use 32bpp images in case of BI_BITFIELD
0N/A // compression to avoid color conversion artefacts
0N/A bitsPerPixel = 32;
0N/A
0N/A // Setting this flag to false ensures that generic
0N/A // writePixels() will be used to store image data
0N/A noTransform = false;
0N/A }
0N/A
0N/A destScanlineBytes = w * bitsPerPixel + 7 >> 3;
0N/A
0N/A isPalette = true;
0N/A paletteEntries = 3;
0N/A r = new byte[paletteEntries];
0N/A g = new byte[paletteEntries];
0N/A b = new byte[paletteEntries];
0N/A a = new byte[paletteEntries];
0N/A
0N/A int rmask = 0x00ff0000;
0N/A int gmask = 0x0000ff00;
0N/A int bmask = 0x000000ff;
0N/A
0N/A if (bitsPerPixel == 16) {
0N/A /* NB: canEncodeImage() ensures we have image of
0N/A * either USHORT_565_RGB or USHORT_555_RGB type here.
0N/A * Technically, it should work for other direct color
0N/A * model types but it might be non compatible with win98
0N/A * and friends.
0N/A */
0N/A if (colorModel instanceof DirectColorModel) {
0N/A DirectColorModel dcm = (DirectColorModel)colorModel;
0N/A rmask = dcm.getRedMask();
0N/A gmask = dcm.getGreenMask();
0N/A bmask = dcm.getBlueMask();
0N/A } else {
0N/A // it is unlikely, but if it happens, we should throw
0N/A // an exception related to unsupported image format
0N/A throw new IOException("Image can not be encoded with " +
0N/A "compression type " +
0N/A compressionTypeNames[compressionType]);
0N/A }
0N/A }
0N/A writeMaskToPalette(rmask, 0, r, g, b, a);
0N/A writeMaskToPalette(gmask, 1, r, g, b, a);
0N/A writeMaskToPalette(bmask, 2, r, g, b, a);
0N/A
0N/A if (!noTransform) {
0N/A // prepare info for writePixels procedure
0N/A bitMasks = new int[3];
0N/A bitMasks[0] = rmask;
0N/A bitMasks[1] = gmask;
0N/A bitMasks[2] = bmask;
0N/A
0N/A bitPos = new int[3];
0N/A bitPos[0] = firstLowBit(rmask);
0N/A bitPos[1] = firstLowBit(gmask);
0N/A bitPos[2] = firstLowBit(bmask);
0N/A }
0N/A
0N/A if (colorModel instanceof IndexColorModel) {
0N/A icm = (IndexColorModel)colorModel;
0N/A }
0N/A } else { // handle BI_RGB compression
0N/A if (colorModel instanceof IndexColorModel) {
0N/A isPalette = true;
0N/A icm = (IndexColorModel)colorModel;
0N/A paletteEntries = icm.getMapSize();
0N/A
0N/A if (paletteEntries <= 2) {
0N/A bitsPerPixel = 1;
0N/A destScanlineBytes = w + 7 >> 3;
0N/A } else if (paletteEntries <= 16) {
0N/A bitsPerPixel = 4;
0N/A destScanlineBytes = w + 1 >> 1;
0N/A } else if (paletteEntries <= 256) {
0N/A bitsPerPixel = 8;
0N/A } else {
0N/A // Cannot be written as a Palette image. So write out as
0N/A // 24 bit image.
0N/A bitsPerPixel = 24;
0N/A isPalette = false;
0N/A paletteEntries = 0;
0N/A destScanlineBytes = w * 3;
0N/A }
0N/A
0N/A if (isPalette == true) {
0N/A r = new byte[paletteEntries];
0N/A g = new byte[paletteEntries];
0N/A b = new byte[paletteEntries];
0N/A a = new byte[paletteEntries];
0N/A
0N/A icm.getAlphas(a);
0N/A icm.getReds(r);
0N/A icm.getGreens(g);
0N/A icm.getBlues(b);
0N/A }
0N/A
0N/A } else {
0N/A // Grey scale images
0N/A if (numBands == 1) {
0N/A
0N/A isPalette = true;
0N/A paletteEntries = 256;
0N/A bitsPerPixel = sampleSize[0];
0N/A
0N/A destScanlineBytes = (w * bitsPerPixel + 7 >> 3);
0N/A
0N/A r = new byte[256];
0N/A g = new byte[256];
0N/A b = new byte[256];
0N/A a = new byte[256];
0N/A
0N/A for (int i = 0; i < 256; i++) {
0N/A r[i] = (byte)i;
0N/A g[i] = (byte)i;
0N/A b[i] = (byte)i;
0N/A a[i] = (byte)255;
0N/A }
0N/A
0N/A } else {
0N/A if (sampleModel instanceof SinglePixelPackedSampleModel &&
0N/A noSubband)
0N/A {
0N/A /* NB: the actual pixel size can be smaller than
0N/A * size of used DataBuffer element.
0N/A * For example: in case of TYPE_INT_RGB actual pixel
0N/A * size is 24 bits, but size of DataBuffere element
0N/A * is 32 bits
0N/A */
0N/A int[] sample_sizes = sampleModel.getSampleSize();
0N/A bitsPerPixel = 0;
0N/A for (int size : sample_sizes) {
0N/A bitsPerPixel += size;
0N/A }
0N/A bitsPerPixel = roundBpp(bitsPerPixel);
0N/A if (bitsPerPixel != DataBuffer.getDataTypeSize(sampleModel.getDataType())) {
0N/A noTransform = false;
0N/A }
0N/A destScanlineBytes = w * bitsPerPixel + 7 >> 3;
0N/A }
0N/A }
0N/A }
0N/A }
0N/A
0N/A // actual writing of image data
0N/A int fileSize = 0;
0N/A int offset = 0;
0N/A int headerSize = 0;
0N/A int imageSize = 0;
0N/A int xPelsPerMeter = 0;
0N/A int yPelsPerMeter = 0;
0N/A int colorsUsed = 0;
0N/A int colorsImportant = paletteEntries;
0N/A
0N/A // Calculate padding for each scanline
0N/A int padding = destScanlineBytes % 4;
0N/A if (padding != 0) {
0N/A padding = 4 - padding;
0N/A }
0N/A
0N/A
0N/A // FileHeader is 14 bytes, BitmapHeader is 40 bytes,
0N/A // add palette size and that is where the data will begin
0N/A offset = 54 + paletteEntries * 4;
0N/A
0N/A imageSize = (destScanlineBytes + padding) * h;
0N/A fileSize = imageSize + offset;
0N/A headerSize = 40;
0N/A
0N/A long headPos = stream.getStreamPosition();
0N/A
0N/A writeFileHeader(fileSize, offset);
0N/A
1289N/A /* According to MSDN description, the top-down image layout
1289N/A * is allowed only if compression type is BI_RGB or BI_BITFIELDS.
1289N/A * Images with any other compression type must be wrote in the
1289N/A * bottom-up layout.
1289N/A */
1289N/A if (compressionType == BMPConstants.BI_RGB ||
1289N/A compressionType == BMPConstants.BI_BITFIELDS)
1289N/A {
1289N/A isTopDown = bmpParam.isTopDown();
1289N/A } else {
1289N/A isTopDown = false;
1289N/A }
1289N/A
0N/A writeInfoHeader(headerSize, bitsPerPixel);
0N/A
0N/A // compression
0N/A stream.writeInt(compressionType);
0N/A
0N/A // imageSize
0N/A stream.writeInt(imageSize);
0N/A
0N/A // xPelsPerMeter
0N/A stream.writeInt(xPelsPerMeter);
0N/A
0N/A // yPelsPerMeter
0N/A stream.writeInt(yPelsPerMeter);
0N/A
0N/A // Colors Used
0N/A stream.writeInt(colorsUsed);
0N/A
0N/A // Colors Important
0N/A stream.writeInt(colorsImportant);
0N/A
0N/A // palette
0N/A if (isPalette == true) {
0N/A
0N/A // write palette
0N/A if (compressionType == BMPConstants.BI_BITFIELDS) {
0N/A // write masks for red, green and blue components.
0N/A for (int i=0; i<3; i++) {
0N/A int mask = (a[i]&0xFF) + ((r[i]&0xFF)*0x100) + ((g[i]&0xFF)*0x10000) + ((b[i]&0xFF)*0x1000000);
0N/A stream.writeInt(mask);
0N/A }
0N/A } else {
0N/A for (int i=0; i<paletteEntries; i++) {
0N/A stream.writeByte(b[i]);
0N/A stream.writeByte(g[i]);
0N/A stream.writeByte(r[i]);
0N/A stream.writeByte(a[i]);
0N/A }
0N/A }
0N/A }
0N/A
0N/A // Writing of actual image data
0N/A int scanlineBytes = w * numBands;
0N/A
0N/A // Buffer for up to 8 rows of pixels
0N/A int[] pixels = new int[scanlineBytes * scaleX];
0N/A
0N/A // Also create a buffer to hold one line of the data
0N/A // to be written to the file, so we can use array writes.
0N/A bpixels = new byte[destScanlineBytes];
0N/A
0N/A int l;
0N/A
0N/A if (compressionType == BMPConstants.BI_JPEG ||
0N/A compressionType == BMPConstants.BI_PNG) {
0N/A
0N/A // prepare embedded buffer
0N/A embedded_stream = new ByteArrayOutputStream();
0N/A writeEmbedded(image, bmpParam);
0N/A // update the file/image Size
0N/A embedded_stream.flush();
0N/A imageSize = embedded_stream.size();
0N/A
0N/A long endPos = stream.getStreamPosition();
0N/A fileSize = (int)(offset + imageSize);
0N/A stream.seek(headPos);
0N/A writeSize(fileSize, 2);
0N/A stream.seek(headPos);
0N/A writeSize(imageSize, 34);
0N/A stream.seek(endPos);
0N/A stream.write(embedded_stream.toByteArray());
0N/A embedded_stream = null;
0N/A
0N/A if (abortRequested()) {
0N/A processWriteAborted();
0N/A } else {
0N/A processImageComplete();
0N/A stream.flushBefore(stream.getStreamPosition());
0N/A }
0N/A
0N/A return;
0N/A }
0N/A
0N/A int maxBandOffset = bandOffsets[0];
0N/A for (int i = 1; i < bandOffsets.length; i++)
0N/A if (bandOffsets[i] > maxBandOffset)
0N/A maxBandOffset = bandOffsets[i];
0N/A
0N/A int[] pixel = new int[maxBandOffset + 1];
0N/A
0N/A int destScanlineLength = destScanlineBytes;
0N/A
0N/A if (noTransform && noSubband) {
0N/A destScanlineLength = destScanlineBytes / (DataBuffer.getDataTypeSize(dataType)>>3);
0N/A }
0N/A for (int i = 0; i < h; i++) {
0N/A if (abortRequested()) {
0N/A break;
0N/A }
0N/A
0N/A int row = minY + i;
0N/A
0N/A if (!isTopDown)
0N/A row = minY + h - i -1;
0N/A
0N/A // Get the pixels
0N/A Raster src = inputRaster;
0N/A
0N/A Rectangle srcRect =
0N/A new Rectangle(minX * scaleX + xOffset,
0N/A row * scaleY + yOffset,
0N/A (w - 1)* scaleX + 1,
0N/A 1);
0N/A if (!writeRaster)
0N/A src = input.getData(srcRect);
0N/A
0N/A if (noTransform && noSubband) {
0N/A SampleModel sm = src.getSampleModel();
0N/A int pos = 0;
0N/A int startX = srcRect.x - src.getSampleModelTranslateX();
0N/A int startY = srcRect.y - src.getSampleModelTranslateY();
0N/A if (sm instanceof ComponentSampleModel) {
0N/A ComponentSampleModel csm = (ComponentSampleModel)sm;
0N/A pos = csm.getOffset(startX, startY, 0);
0N/A for(int nb=1; nb < csm.getNumBands(); nb++) {
0N/A if (pos > csm.getOffset(startX, startY, nb)) {
0N/A pos = csm.getOffset(startX, startY, nb);
0N/A }
0N/A }
0N/A } else if (sm instanceof MultiPixelPackedSampleModel) {
0N/A MultiPixelPackedSampleModel mppsm =
0N/A (MultiPixelPackedSampleModel)sm;
0N/A pos = mppsm.getOffset(startX, startY);
0N/A } else if (sm instanceof SinglePixelPackedSampleModel) {
0N/A SinglePixelPackedSampleModel sppsm =
0N/A (SinglePixelPackedSampleModel)sm;
0N/A pos = sppsm.getOffset(startX, startY);
0N/A }
0N/A
0N/A if (compressionType == BMPConstants.BI_RGB || compressionType == BMPConstants.BI_BITFIELDS){
0N/A switch(dataType) {
0N/A case DataBuffer.TYPE_BYTE:
0N/A byte[] bdata =
0N/A ((DataBufferByte)src.getDataBuffer()).getData();
0N/A stream.write(bdata, pos, destScanlineLength);
0N/A break;
0N/A
0N/A case DataBuffer.TYPE_SHORT:
0N/A short[] sdata =
0N/A ((DataBufferShort)src.getDataBuffer()).getData();
0N/A stream.writeShorts(sdata, pos, destScanlineLength);
0N/A break;
0N/A
0N/A case DataBuffer.TYPE_USHORT:
0N/A short[] usdata =
0N/A ((DataBufferUShort)src.getDataBuffer()).getData();
0N/A stream.writeShorts(usdata, pos, destScanlineLength);
0N/A break;
0N/A
0N/A case DataBuffer.TYPE_INT:
0N/A int[] idata =
0N/A ((DataBufferInt)src.getDataBuffer()).getData();
0N/A stream.writeInts(idata, pos, destScanlineLength);
0N/A break;
0N/A }
0N/A
0N/A for(int k=0; k<padding; k++) {
0N/A stream.writeByte(0);
0N/A }
0N/A } else if (compressionType == BMPConstants.BI_RLE4) {
0N/A if (bpixels == null || bpixels.length < scanlineBytes)
0N/A bpixels = new byte[scanlineBytes];
0N/A src.getPixels(srcRect.x, srcRect.y,
0N/A srcRect.width, srcRect.height, pixels);
0N/A for (int h=0; h<scanlineBytes; h++) {
0N/A bpixels[h] = (byte)pixels[h];
0N/A }
0N/A encodeRLE4(bpixels, scanlineBytes);
0N/A } else if (compressionType == BMPConstants.BI_RLE8) {
0N/A //byte[] bdata =
0N/A // ((DataBufferByte)src.getDataBuffer()).getData();
0N/A //System.out.println("bdata.length="+bdata.length);
0N/A //System.arraycopy(bdata, pos, bpixels, 0, scanlineBytes);
0N/A if (bpixels == null || bpixels.length < scanlineBytes)
0N/A bpixels = new byte[scanlineBytes];
0N/A src.getPixels(srcRect.x, srcRect.y,
0N/A srcRect.width, srcRect.height, pixels);
0N/A for (int h=0; h<scanlineBytes; h++) {
0N/A bpixels[h] = (byte)pixels[h];
0N/A }
0N/A
0N/A encodeRLE8(bpixels, scanlineBytes);
0N/A }
0N/A } else {
0N/A src.getPixels(srcRect.x, srcRect.y,
0N/A srcRect.width, srcRect.height, pixels);
0N/A
0N/A if (scaleX != 1 || maxBandOffset != numBands - 1) {
0N/A for (int j = 0, k = 0, n=0; j < w;
0N/A j++, k += scaleX * numBands, n += numBands)
0N/A {
0N/A System.arraycopy(pixels, k, pixel, 0, pixel.length);
0N/A
0N/A for (int m = 0; m < numBands; m++) {
0N/A // pixel data is provided here in RGB order
0N/A pixels[n + m] = pixel[sourceBands[m]];
0N/A }
0N/A }
0N/A }
0N/A writePixels(0, scanlineBytes, bitsPerPixel, pixels,
0N/A padding, numBands, icm);
0N/A }
0N/A
0N/A processImageProgress(100.0f * (((float)i) / ((float)h)));
0N/A }
0N/A
0N/A if (compressionType == BMPConstants.BI_RLE4 ||
0N/A compressionType == BMPConstants.BI_RLE8) {
0N/A // Write the RLE EOF marker and
0N/A stream.writeByte(0);
0N/A stream.writeByte(1);
0N/A incCompImageSize(2);
0N/A // update the file/image Size
0N/A imageSize = compImageSize;
0N/A fileSize = compImageSize + offset;
0N/A long endPos = stream.getStreamPosition();
0N/A stream.seek(headPos);
0N/A writeSize(fileSize, 2);
0N/A stream.seek(headPos);
0N/A writeSize(imageSize, 34);
0N/A stream.seek(endPos);
0N/A }
0N/A
0N/A if (abortRequested()) {
0N/A processWriteAborted();
0N/A } else {
0N/A processImageComplete();
0N/A stream.flushBefore(stream.getStreamPosition());
0N/A }
0N/A }
0N/A
0N/A private void writePixels(int l, int scanlineBytes, int bitsPerPixel,
0N/A int pixels[],
0N/A int padding, int numBands,
0N/A IndexColorModel icm) throws IOException {
0N/A int pixel = 0;
0N/A int k = 0;
0N/A switch (bitsPerPixel) {
0N/A
0N/A case 1:
0N/A
0N/A for (int j=0; j<scanlineBytes/8; j++) {
0N/A bpixels[k++] = (byte)((pixels[l++] << 7) |
0N/A (pixels[l++] << 6) |
0N/A (pixels[l++] << 5) |
0N/A (pixels[l++] << 4) |
0N/A (pixels[l++] << 3) |
0N/A (pixels[l++] << 2) |
0N/A (pixels[l++] << 1) |
0N/A pixels[l++]);
0N/A }
0N/A
0N/A // Partially filled last byte, if any
0N/A if (scanlineBytes%8 > 0) {
0N/A pixel = 0;
0N/A for (int j=0; j<scanlineBytes%8; j++) {
0N/A pixel |= (pixels[l++] << (7 - j));
0N/A }
0N/A bpixels[k++] = (byte)pixel;
0N/A }
0N/A stream.write(bpixels, 0, (scanlineBytes+7)/8);
0N/A
0N/A break;
0N/A
0N/A case 4:
0N/A if (compressionType == BMPConstants.BI_RLE4){
0N/A byte[] bipixels = new byte[scanlineBytes];
0N/A for (int h=0; h<scanlineBytes; h++) {
0N/A bipixels[h] = (byte)pixels[l++];
0N/A }
0N/A encodeRLE4(bipixels, scanlineBytes);
0N/A }else {
0N/A for (int j=0; j<scanlineBytes/2; j++) {
0N/A pixel = (pixels[l++] << 4) | pixels[l++];
0N/A bpixels[k++] = (byte)pixel;
0N/A }
0N/A // Put the last pixel of odd-length lines in the 4 MSBs
0N/A if ((scanlineBytes%2) == 1) {
0N/A pixel = pixels[l] << 4;
0N/A bpixels[k++] = (byte)pixel;
0N/A }
0N/A stream.write(bpixels, 0, (scanlineBytes+1)/2);
0N/A }
0N/A break;
0N/A
0N/A case 8:
0N/A if(compressionType == BMPConstants.BI_RLE8) {
0N/A for (int h=0; h<scanlineBytes; h++) {
0N/A bpixels[h] = (byte)pixels[l++];
0N/A }
0N/A encodeRLE8(bpixels, scanlineBytes);
0N/A }else {
0N/A for (int j=0; j<scanlineBytes; j++) {
0N/A bpixels[j] = (byte)pixels[l++];
0N/A }
0N/A stream.write(bpixels, 0, scanlineBytes);
0N/A }
0N/A break;
0N/A
0N/A case 16:
0N/A if (spixels == null)
0N/A spixels = new short[scanlineBytes / numBands];
0N/A /*
0N/A * We expect that pixel data comes in RGB order.
0N/A * We will assemble short pixel taking into account
0N/A * the compression type:
0N/A *
0N/A * BI_RGB - the RGB order should be maintained.
0N/A * BI_BITFIELDS - use bitPos array that was built
0N/A * according to bitfields masks.
0N/A */
0N/A for (int j = 0, m = 0; j < scanlineBytes; m++) {
0N/A spixels[m] = 0;
0N/A if (compressionType == BMPConstants.BI_RGB) {
0N/A /*
0N/A * please note that despite other cases,
0N/A * the 16bpp BI_RGB requires the RGB data order
0N/A */
0N/A spixels[m] = (short)
0N/A (((0x1f & pixels[j ]) << 10) |
0N/A ((0x1f & pixels[j + 1]) << 5) |
0N/A ((0x1f & pixels[j + 2]) ));
0N/A j += 3;
0N/A } else {
0N/A for(int i = 0 ; i < numBands; i++, j++) {
0N/A spixels[m] |=
0N/A (((pixels[j]) << bitPos[i]) & bitMasks[i]);
0N/A }
0N/A }
0N/A }
0N/A stream.writeShorts(spixels, 0, spixels.length);
0N/A break;
0N/A
0N/A case 24:
0N/A if (numBands == 3) {
0N/A for (int j=0; j<scanlineBytes; j+=3) {
0N/A // Since BMP needs BGR format
0N/A bpixels[k++] = (byte)(pixels[l+2]);
0N/A bpixels[k++] = (byte)(pixels[l+1]);
0N/A bpixels[k++] = (byte)(pixels[l]);
0N/A l+=3;
0N/A }
0N/A stream.write(bpixels, 0, scanlineBytes);
0N/A } else {
0N/A // Case where IndexColorModel had > 256 colors.
0N/A int entries = icm.getMapSize();
0N/A
0N/A byte r[] = new byte[entries];
0N/A byte g[] = new byte[entries];
0N/A byte b[] = new byte[entries];
0N/A
0N/A icm.getReds(r);
0N/A icm.getGreens(g);
0N/A icm.getBlues(b);
0N/A int index;
0N/A
0N/A for (int j=0; j<scanlineBytes; j++) {
0N/A index = pixels[l];
0N/A bpixels[k++] = b[index];
0N/A bpixels[k++] = g[index];
0N/A bpixels[k++] = b[index];
0N/A l++;
0N/A }
0N/A stream.write(bpixels, 0, scanlineBytes*3);
0N/A }
0N/A break;
0N/A
0N/A case 32:
0N/A if (ipixels == null)
0N/A ipixels = new int[scanlineBytes / numBands];
0N/A if (numBands == 3) {
0N/A /*
0N/A * We expect that pixel data comes in RGB order.
0N/A * We will assemble int pixel taking into account
0N/A * the compression type.
0N/A *
0N/A * BI_RGB - the BGR order should be used.
0N/A * BI_BITFIELDS - use bitPos array that was built
0N/A * according to bitfields masks.
0N/A */
0N/A for (int j = 0, m = 0; j < scanlineBytes; m++) {
0N/A ipixels[m] = 0;
0N/A if (compressionType == BMPConstants.BI_RGB) {
0N/A ipixels[m] =
0N/A ((0xff & pixels[j + 2]) << 16) |
0N/A ((0xff & pixels[j + 1]) << 8) |
0N/A ((0xff & pixels[j ]) );
0N/A j += 3;
0N/A } else {
0N/A for(int i = 0 ; i < numBands; i++, j++) {
0N/A ipixels[m] |=
0N/A (((pixels[j]) << bitPos[i]) & bitMasks[i]);
0N/A }
0N/A }
0N/A }
0N/A } else {
0N/A // We have two possibilities here:
0N/A // 1. we are writing the indexed image with bitfields
0N/A // compression (this covers also the case of BYTE_BINARY)
0N/A // => use icm to get actual RGB color values.
0N/A // 2. we are writing the gray-scaled image with BI_BITFIELDS
0N/A // compression
0N/A // => just replicate the level of gray to color components.
0N/A for (int j = 0; j < scanlineBytes; j++) {
0N/A if (icm != null) {
0N/A ipixels[j] = icm.getRGB(pixels[j]);
0N/A } else {
0N/A ipixels[j] =
0N/A pixels[j] << 16 | pixels[j] << 8 | pixels[j];
0N/A }
0N/A }
0N/A }
0N/A stream.writeInts(ipixels, 0, ipixels.length);
0N/A break;
0N/A }
0N/A
0N/A // Write out the padding
0N/A if (compressionType == BMPConstants.BI_RGB ||
0N/A compressionType == BMPConstants.BI_BITFIELDS)
0N/A {
0N/A for(k=0; k<padding; k++) {
0N/A stream.writeByte(0);
0N/A }
0N/A }
0N/A }
0N/A
0N/A private void encodeRLE8(byte[] bpixels, int scanlineBytes)
0N/A throws IOException{
0N/A
0N/A int runCount = 1, absVal = -1, j = -1;
0N/A byte runVal = 0, nextVal =0 ;
0N/A
0N/A runVal = bpixels[++j];
0N/A byte[] absBuf = new byte[256];
0N/A
0N/A while (j < scanlineBytes-1) {
0N/A nextVal = bpixels[++j];
0N/A if (nextVal == runVal ){
0N/A if(absVal >= 3 ){
0N/A /// Check if there was an existing Absolute Run
0N/A stream.writeByte(0);
0N/A stream.writeByte(absVal);
0N/A incCompImageSize(2);
0N/A for(int a=0; a<absVal;a++){
0N/A stream.writeByte(absBuf[a]);
0N/A incCompImageSize(1);
0N/A }
0N/A if (!isEven(absVal)){
0N/A //Padding
0N/A stream.writeByte(0);
0N/A incCompImageSize(1);
0N/A }
0N/A }
0N/A else if(absVal > -1){
0N/A /// Absolute Encoding for less than 3
0N/A /// treated as regular encoding
0N/A /// Do not include the last element since it will
0N/A /// be inclued in the next encoding/run
0N/A for (int b=0;b<absVal;b++){
0N/A stream.writeByte(1);
0N/A stream.writeByte(absBuf[b]);
0N/A incCompImageSize(2);
0N/A }
0N/A }
0N/A absVal = -1;
0N/A runCount++;
0N/A if (runCount == 256){
0N/A /// Only 255 values permitted
0N/A stream.writeByte(runCount-1);
0N/A stream.writeByte(runVal);
0N/A incCompImageSize(2);
0N/A runCount = 1;
0N/A }
0N/A }
0N/A else {
0N/A if (runCount > 1){
0N/A /// If there was an existing run
0N/A stream.writeByte(runCount);
0N/A stream.writeByte(runVal);
0N/A incCompImageSize(2);
0N/A } else if (absVal < 0){
0N/A // First time..
0N/A absBuf[++absVal] = runVal;
0N/A absBuf[++absVal] = nextVal;
0N/A } else if (absVal < 254){
0N/A // 0-254 only
0N/A absBuf[++absVal] = nextVal;
0N/A } else {
0N/A stream.writeByte(0);
0N/A stream.writeByte(absVal+1);
0N/A incCompImageSize(2);
0N/A for(int a=0; a<=absVal;a++){
0N/A stream.writeByte(absBuf[a]);
0N/A incCompImageSize(1);
0N/A }
0N/A // padding since 255 elts is not even
0N/A stream.writeByte(0);
0N/A incCompImageSize(1);
0N/A absVal = -1;
0N/A }
0N/A runVal = nextVal;
0N/A runCount = 1;
0N/A }
0N/A
0N/A if (j == scanlineBytes-1){ // EOF scanline
0N/A // Write the run
0N/A if (absVal == -1){
0N/A stream.writeByte(runCount);
0N/A stream.writeByte(runVal);
0N/A incCompImageSize(2);
0N/A runCount = 1;
0N/A }
0N/A else {
0N/A // write the Absolute Run
0N/A if(absVal >= 2){
0N/A stream.writeByte(0);
0N/A stream.writeByte(absVal+1);
0N/A incCompImageSize(2);
0N/A for(int a=0; a<=absVal;a++){
0N/A stream.writeByte(absBuf[a]);
0N/A incCompImageSize(1);
0N/A }
0N/A if (!isEven(absVal+1)){
0N/A //Padding
0N/A stream.writeByte(0);
0N/A incCompImageSize(1);
0N/A }
0N/A
0N/A }
0N/A else if(absVal > -1){
0N/A for (int b=0;b<=absVal;b++){
0N/A stream.writeByte(1);
0N/A stream.writeByte(absBuf[b]);
0N/A incCompImageSize(2);
0N/A }
0N/A }
0N/A }
0N/A /// EOF scanline
0N/A
0N/A stream.writeByte(0);
0N/A stream.writeByte(0);
0N/A incCompImageSize(2);
0N/A }
0N/A }
0N/A }
0N/A
0N/A private void encodeRLE4(byte[] bipixels, int scanlineBytes)
0N/A throws IOException {
0N/A
0N/A int runCount=2, absVal=-1, j=-1, pixel=0, q=0;
0N/A byte runVal1=0, runVal2=0, nextVal1=0, nextVal2=0;
0N/A byte[] absBuf = new byte[256];
0N/A
0N/A
0N/A runVal1 = bipixels[++j];
0N/A runVal2 = bipixels[++j];
0N/A
0N/A while (j < scanlineBytes-2){
0N/A nextVal1 = bipixels[++j];
0N/A nextVal2 = bipixels[++j];
0N/A
0N/A if (nextVal1 == runVal1 ) {
0N/A
0N/A //Check if there was an existing Absolute Run
0N/A if(absVal >= 4){
0N/A stream.writeByte(0);
0N/A stream.writeByte(absVal - 1);
0N/A incCompImageSize(2);
0N/A // we need to exclude last 2 elts, similarity of
0N/A // which caused to enter this part of the code
0N/A for(int a=0; a<absVal-2;a+=2){
0N/A pixel = (absBuf[a] << 4) | absBuf[a+1];
0N/A stream.writeByte((byte)pixel);
0N/A incCompImageSize(1);
0N/A }
0N/A // if # of elts is odd - read the last element
0N/A if(!(isEven(absVal-1))){
0N/A q = absBuf[absVal-2] << 4| 0;
0N/A stream.writeByte(q);
0N/A incCompImageSize(1);
0N/A }
0N/A // Padding to word align absolute encoding
0N/A if ( !isEven((int)Math.ceil((absVal-1)/2)) ) {
0N/A stream.writeByte(0);
0N/A incCompImageSize(1);
0N/A }
0N/A } else if (absVal > -1){
0N/A stream.writeByte(2);
0N/A pixel = (absBuf[0] << 4) | absBuf[1];
0N/A stream.writeByte(pixel);
0N/A incCompImageSize(2);
0N/A }
0N/A absVal = -1;
0N/A
0N/A if (nextVal2 == runVal2){
0N/A // Even runlength
0N/A runCount+=2;
0N/A if(runCount == 256){
0N/A stream.writeByte(runCount-1);
0N/A pixel = ( runVal1 << 4) | runVal2;
0N/A stream.writeByte(pixel);
0N/A incCompImageSize(2);
0N/A runCount =2;
0N/A if(j< scanlineBytes - 1){
0N/A runVal1 = runVal2;
0N/A runVal2 = bipixels[++j];
0N/A } else {
0N/A stream.writeByte(01);
0N/A int r = runVal2 << 4 | 0;
0N/A stream.writeByte(r);
0N/A incCompImageSize(2);
0N/A runCount = -1;/// Only EOF required now
0N/A }
0N/A }
0N/A } else {
0N/A // odd runlength and the run ends here
0N/A // runCount wont be > 254 since 256/255 case will
0N/A // be taken care of in above code.
0N/A runCount++;
0N/A pixel = ( runVal1 << 4) | runVal2;
0N/A stream.writeByte(runCount);
0N/A stream.writeByte(pixel);
0N/A incCompImageSize(2);
0N/A runCount = 2;
0N/A runVal1 = nextVal2;
0N/A // If end of scanline
0N/A if (j < scanlineBytes -1){
0N/A runVal2 = bipixels[++j];
0N/A }else {
0N/A stream.writeByte(01);
0N/A int r = nextVal2 << 4 | 0;
0N/A stream.writeByte(r);
0N/A incCompImageSize(2);
0N/A runCount = -1;/// Only EOF required now
0N/A }
0N/A
0N/A }
0N/A } else{
0N/A // Check for existing run
0N/A if (runCount > 2){
0N/A pixel = ( runVal1 << 4) | runVal2;
0N/A stream.writeByte(runCount);
0N/A stream.writeByte(pixel);
0N/A incCompImageSize(2);
0N/A } else if (absVal < 0){ // first time
0N/A absBuf[++absVal] = runVal1;
0N/A absBuf[++absVal] = runVal2;
0N/A absBuf[++absVal] = nextVal1;
0N/A absBuf[++absVal] = nextVal2;
0N/A } else if (absVal < 253){ // only 255 elements
0N/A absBuf[++absVal] = nextVal1;
0N/A absBuf[++absVal] = nextVal2;
0N/A } else {
0N/A stream.writeByte(0);
0N/A stream.writeByte(absVal+1);
0N/A incCompImageSize(2);
0N/A for(int a=0; a<absVal;a+=2){
0N/A pixel = (absBuf[a] << 4) | absBuf[a+1];
0N/A stream.writeByte((byte)pixel);
0N/A incCompImageSize(1);
0N/A }
0N/A // Padding for word align
0N/A // since it will fit into 127 bytes
0N/A stream.writeByte(0);
0N/A incCompImageSize(1);
0N/A absVal = -1;
0N/A }
0N/A
0N/A runVal1 = nextVal1;
0N/A runVal2 = nextVal2;
0N/A runCount = 2;
0N/A }
0N/A // Handle the End of scanline for the last 2 4bits
0N/A if (j >= scanlineBytes-2 ) {
0N/A if (absVal == -1 && runCount >= 2){
0N/A if (j == scanlineBytes-2){
0N/A if(bipixels[++j] == runVal1){
0N/A runCount++;
0N/A pixel = ( runVal1 << 4) | runVal2;
0N/A stream.writeByte(runCount);
0N/A stream.writeByte(pixel);
0N/A incCompImageSize(2);
0N/A } else {
0N/A pixel = ( runVal1 << 4) | runVal2;
0N/A stream.writeByte(runCount);
0N/A stream.writeByte(pixel);
0N/A stream.writeByte(01);
0N/A pixel = bipixels[j]<<4 |0;
0N/A stream.writeByte(pixel);
0N/A int n = bipixels[j]<<4|0;
0N/A incCompImageSize(4);
0N/A }
0N/A } else {
0N/A stream.writeByte(runCount);
0N/A pixel =( runVal1 << 4) | runVal2 ;
0N/A stream.writeByte(pixel);
0N/A incCompImageSize(2);
0N/A }
0N/A } else if(absVal > -1){
0N/A if (j == scanlineBytes-2){
0N/A absBuf[++absVal] = bipixels[++j];
0N/A }
0N/A if (absVal >=2){
0N/A stream.writeByte(0);
0N/A stream.writeByte(absVal+1);
0N/A incCompImageSize(2);
0N/A for(int a=0; a<absVal;a+=2){
0N/A pixel = (absBuf[a] << 4) | absBuf[a+1];
0N/A stream.writeByte((byte)pixel);
0N/A incCompImageSize(1);
0N/A }
0N/A if(!(isEven(absVal+1))){
0N/A q = absBuf[absVal] << 4|0;
0N/A stream.writeByte(q);
0N/A incCompImageSize(1);
0N/A }
0N/A
0N/A // Padding
0N/A if ( !isEven((int)Math.ceil((absVal+1)/2)) ) {
0N/A stream.writeByte(0);
0N/A incCompImageSize(1);
0N/A }
0N/A
0N/A } else {
0N/A switch (absVal){
0N/A case 0:
0N/A stream.writeByte(1);
0N/A int n = absBuf[0]<<4 | 0;
0N/A stream.writeByte(n);
0N/A incCompImageSize(2);
0N/A break;
0N/A case 1:
0N/A stream.writeByte(2);
0N/A pixel = (absBuf[0] << 4) | absBuf[1];
0N/A stream.writeByte(pixel);
0N/A incCompImageSize(2);
0N/A break;
0N/A }
0N/A }
0N/A
0N/A }
0N/A stream.writeByte(0);
0N/A stream.writeByte(0);
0N/A incCompImageSize(2);
0N/A }
0N/A }
0N/A }
0N/A
0N/A
0N/A private synchronized void incCompImageSize(int value){
0N/A compImageSize = compImageSize + value;
0N/A }
0N/A
0N/A private boolean isEven(int number) {
0N/A return (number%2 == 0 ? true : false);
0N/A }
0N/A
0N/A private void writeFileHeader(int fileSize, int offset) throws IOException {
0N/A // magic value
0N/A stream.writeByte('B');
0N/A stream.writeByte('M');
0N/A
0N/A // File size
0N/A stream.writeInt(fileSize);
0N/A
0N/A // reserved1 and reserved2
0N/A stream.writeInt(0);
0N/A
0N/A // offset to image data
0N/A stream.writeInt(offset);
0N/A }
0N/A
0N/A
0N/A private void writeInfoHeader(int headerSize,
0N/A int bitsPerPixel) throws IOException {
0N/A // size of header
0N/A stream.writeInt(headerSize);
0N/A
0N/A // width
0N/A stream.writeInt(w);
0N/A
0N/A // height
1289N/A stream.writeInt(isTopDown ? -h : h);
0N/A
0N/A // number of planes
0N/A stream.writeShort(1);
0N/A
0N/A // Bits Per Pixel
0N/A stream.writeShort(bitsPerPixel);
0N/A }
0N/A
0N/A private void writeSize(int dword, int offset) throws IOException {
0N/A stream.skipBytes(offset);
0N/A stream.writeInt(dword);
0N/A }
0N/A
0N/A public void reset() {
0N/A super.reset();
0N/A stream = null;
0N/A }
0N/A
0N/A private int getCompressionType(String typeString) {
0N/A for (int i = 0; i < BMPConstants.compressionTypeNames.length; i++)
0N/A if (BMPConstants.compressionTypeNames[i].equals(typeString))
0N/A return i;
0N/A return 0;
0N/A }
0N/A
0N/A private void writeEmbedded(IIOImage image,
0N/A ImageWriteParam bmpParam) throws IOException {
0N/A String format =
0N/A compressionType == BMPConstants.BI_JPEG ? "jpeg" : "png";
0N/A Iterator iterator = ImageIO.getImageWritersByFormatName(format);
0N/A ImageWriter writer = null;
0N/A if (iterator.hasNext())
0N/A writer = (ImageWriter)iterator.next();
0N/A if (writer != null) {
0N/A if (embedded_stream == null) {
0N/A throw new RuntimeException("No stream for writing embedded image!");
0N/A }
0N/A
0N/A writer.addIIOWriteProgressListener(new IIOWriteProgressAdapter() {
0N/A public void imageProgress(ImageWriter source, float percentageDone) {
0N/A processImageProgress(percentageDone);
0N/A }
0N/A });
0N/A
0N/A writer.addIIOWriteWarningListener(new IIOWriteWarningListener() {
0N/A public void warningOccurred(ImageWriter source, int imageIndex, String warning) {
0N/A processWarningOccurred(imageIndex, warning);
0N/A }
0N/A });
0N/A
0N/A writer.setOutput(ImageIO.createImageOutputStream(embedded_stream));
0N/A ImageWriteParam param = writer.getDefaultWriteParam();
0N/A //param.setDestinationBands(bmpParam.getDestinationBands());
0N/A param.setDestinationOffset(bmpParam.getDestinationOffset());
0N/A param.setSourceBands(bmpParam.getSourceBands());
0N/A param.setSourceRegion(bmpParam.getSourceRegion());
0N/A param.setSourceSubsampling(bmpParam.getSourceXSubsampling(),
0N/A bmpParam.getSourceYSubsampling(),
0N/A bmpParam.getSubsamplingXOffset(),
0N/A bmpParam.getSubsamplingYOffset());
0N/A writer.write(null, image, param);
0N/A } else
0N/A throw new RuntimeException(I18N.getString("BMPImageWrite5") + " " + format);
0N/A
0N/A }
0N/A
0N/A private int firstLowBit(int num) {
0N/A int count = 0;
0N/A while ((num & 1) == 0) {
0N/A count++;
0N/A num >>>= 1;
0N/A }
0N/A return count;
0N/A }
0N/A
0N/A private class IIOWriteProgressAdapter implements IIOWriteProgressListener {
0N/A
0N/A public void imageComplete(ImageWriter source) {
0N/A }
0N/A
0N/A public void imageProgress(ImageWriter source, float percentageDone) {
0N/A }
0N/A
0N/A public void imageStarted(ImageWriter source, int imageIndex) {
0N/A }
0N/A
0N/A public void thumbnailComplete(ImageWriter source) {
0N/A }
0N/A
0N/A public void thumbnailProgress(ImageWriter source, float percentageDone) {
0N/A }
0N/A
0N/A public void thumbnailStarted(ImageWriter source, int imageIndex, int thumbnailIndex) {
0N/A }
0N/A
0N/A public void writeAborted(ImageWriter source) {
0N/A }
0N/A }
0N/A
0N/A /*
0N/A * Returns preferred compression type for given image.
0N/A * The default compression type is BI_RGB, but some image types can't be
0N/A * encodeed with using default compression without cahnge color resolution.
0N/A * For example, TYPE_USHORT_565_RGB may be encodeed only by using BI_BITFIELDS
0N/A * compression type.
0N/A *
0N/A * NB: we probably need to extend this method if we encounter other image
0N/A * types which can not be encoded with BI_RGB compression type.
0N/A */
0N/A protected int getPreferredCompressionType(ColorModel cm, SampleModel sm) {
0N/A ImageTypeSpecifier imageType = new ImageTypeSpecifier(cm, sm);
0N/A return getPreferredCompressionType(imageType);
0N/A }
0N/A
0N/A protected int getPreferredCompressionType(ImageTypeSpecifier imageType) {
0N/A if (imageType.getBufferedImageType() == BufferedImage.TYPE_USHORT_565_RGB) {
0N/A return BI_BITFIELDS;
0N/A }
0N/A return BI_RGB;
0N/A }
0N/A
0N/A /*
0N/A * Check whether we can encode image of given type using compression method in question.
0N/A *
0N/A * For example, TYPE_USHORT_565_RGB can be encodeed with BI_BITFIELDS compression only.
0N/A *
0N/A * NB: method should be extended if other cases when we can not encode
0N/A * with given compression will be discovered.
0N/A */
0N/A protected boolean canEncodeImage(int compression, ColorModel cm, SampleModel sm) {
0N/A ImageTypeSpecifier imgType = new ImageTypeSpecifier(cm, sm);
0N/A return canEncodeImage(compression, imgType);
0N/A }
0N/A
0N/A protected boolean canEncodeImage(int compression, ImageTypeSpecifier imgType) {
0N/A ImageWriterSpi spi = this.getOriginatingProvider();
0N/A if (!spi.canEncodeImage(imgType)) {
0N/A return false;
0N/A }
0N/A int biType = imgType.getBufferedImageType();
0N/A int bpp = imgType.getColorModel().getPixelSize();
0N/A if (compressionType == BI_RLE4 && bpp != 4) {
0N/A // only 4bpp images can be encoded as BI_RLE4
0N/A return false;
0N/A }
0N/A if (compressionType == BI_RLE8 && bpp != 8) {
0N/A // only 8bpp images can be encoded as BI_RLE8
0N/A return false;
0N/A }
0N/A if (bpp == 16) {
0N/A /*
0N/A * Technically we expect that we may be able to
0N/A * encode only some of SinglePixelPackedSampleModel
0N/A * images here.
0N/A *
0N/A * In addition we should take into account following:
0N/A *
0N/A * 1. BI_RGB case, according to the MSDN description:
0N/A *
0N/A * The bitmap has a maximum of 2^16 colors. If the
0N/A * biCompression member of the BITMAPINFOHEADER is BI_RGB,
0N/A * the bmiColors member of BITMAPINFO is NULL. Each WORD
0N/A * in the bitmap array represents a single pixel. The
0N/A * relative intensities of red, green, and blue are
0N/A * represented with five bits for each color component.
0N/A *
0N/A * 2. BI_BITFIELDS case, according ot the MSDN description:
0N/A *
0N/A * Windows 95/98/Me: When the biCompression member is
0N/A * BI_BITFIELDS, the system supports only the following
0N/A * 16bpp color masks: A 5-5-5 16-bit image, where the blue
0N/A * mask is 0x001F, the green mask is 0x03E0, and the red mask
0N/A * is 0x7C00; and a 5-6-5 16-bit image, where the blue mask
0N/A * is 0x001F, the green mask is 0x07E0, and the red mask is
0N/A * 0xF800.
0N/A */
0N/A boolean canUseRGB = false;
0N/A boolean canUseBITFIELDS = false;
0N/A
0N/A SampleModel sm = imgType.getSampleModel();
0N/A if (sm instanceof SinglePixelPackedSampleModel) {
0N/A int[] sizes =
0N/A ((SinglePixelPackedSampleModel)sm).getSampleSize();
0N/A
0N/A canUseRGB = true;
0N/A canUseBITFIELDS = true;
0N/A for (int i = 0; i < sizes.length; i++) {
0N/A canUseRGB &= (sizes[i] == 5);
0N/A canUseBITFIELDS &= ((sizes[i] == 5) ||
0N/A (i == 1 && sizes[i] == 6));
0N/A }
0N/A }
0N/A
0N/A return (((compressionType == BI_RGB) && canUseRGB) ||
0N/A ((compressionType == BI_BITFIELDS) && canUseBITFIELDS));
0N/A }
0N/A return true;
0N/A }
0N/A
0N/A protected void writeMaskToPalette(int mask, int i,
0N/A byte[] r, byte[]g, byte[] b, byte[]a) {
0N/A b[i] = (byte)(0xff & (mask >> 24));
0N/A g[i] = (byte)(0xff & (mask >> 16));
0N/A r[i] = (byte)(0xff & (mask >> 8));
0N/A a[i] = (byte)(0xff & mask);
0N/A }
0N/A
0N/A private int roundBpp(int x) {
0N/A if (x <= 8) {
0N/A return 8;
0N/A } else if (x <= 16) {
0N/A return 16;
0N/A } if (x <= 24) {
0N/A return 24;
0N/A } else {
0N/A return 32;
0N/A }
0N/A }
0N/A}