package com.sun.imageio.plugins.bmp;
import java.awt.Rectangle;
import java.awt.image.ColorModel;
import java.awt.image.ComponentSampleModel;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferByte;
import java.awt.image.DataBufferInt;
import java.awt.image.DataBufferShort;
import java.awt.image.DataBufferUShort;
import java.awt.image.DirectColorModel;
import java.awt.image.IndexColorModel;
import java.awt.image.MultiPixelPackedSampleModel;
import java.awt.image.BandedSampleModel;
import java.awt.image.Raster;
import java.awt.image.RenderedImage;
import java.awt.image.SampleModel;
import java.awt.image.SinglePixelPackedSampleModel;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.ByteArrayOutputStream;
import java.nio.ByteOrder;
import java.util.Iterator;
import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageTypeSpecifier;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.spi.ImageWriterSpi;
import javax.imageio.stream.ImageOutputStream;
import javax.imageio.event.IIOWriteProgressListener;
import javax.imageio.event.IIOWriteWarningListener;
import javax.imageio.plugins.bmp.BMPImageWriteParam;
import com.sun.imageio.plugins.common.ImageUtil;
import com.sun.imageio.plugins.common.I18N;
public class BMPImageWriter extends ImageWriter implements BMPConstants {
private ImageOutputStream stream = null;
private ByteArrayOutputStream embedded_stream = null;
private int version;
private int compressionType;
private boolean isTopDown;
private int w, h;
private int compImageSize = 0;
private int[] bitMasks;
private int[] bitPos;
private byte[] bpixels;
private short[] spixels;
private int[] ipixels;
public BMPImageWriter(ImageWriterSpi originator) {
super(originator);
}
public void setOutput(Object output) {
super.setOutput(output);
if (output != null) {
if (!(output instanceof ImageOutputStream))
throw new IllegalArgumentException(I18N.getString("BMPImageWriter0"));
this.stream = (ImageOutputStream)output;
stream.setByteOrder(ByteOrder.LITTLE_ENDIAN);
} else
this.stream = null;
}
public ImageWriteParam getDefaultWriteParam() {
return new BMPImageWriteParam();
}
public IIOMetadata getDefaultStreamMetadata(ImageWriteParam param) {
return null;
}
public IIOMetadata getDefaultImageMetadata(ImageTypeSpecifier imageType,
ImageWriteParam param) {
BMPMetadata meta = new BMPMetadata();
meta.bmpVersion = VERSION_3;
meta.compression = getPreferredCompressionType(imageType);
if (param != null
&& param.getCompressionMode() == ImageWriteParam.MODE_EXPLICIT) {
meta.compression = BMPCompressionTypes.getType(param.getCompressionType());
}
meta.bitsPerPixel = (short)imageType.getColorModel().getPixelSize();
return meta;
}
public IIOMetadata convertStreamMetadata(IIOMetadata inData,
ImageWriteParam param) {
return null;
}
public IIOMetadata convertImageMetadata(IIOMetadata metadata,
ImageTypeSpecifier type,
ImageWriteParam param) {
return null;
}
public boolean canWriteRasters() {
return true;
}
public void write(IIOMetadata streamMetadata,
IIOImage image,
ImageWriteParam param) throws IOException {
if (stream == null) {
throw new IllegalStateException(I18N.getString("BMPImageWriter7"));
}
if (image == null) {
throw new IllegalArgumentException(I18N.getString("BMPImageWriter8"));
}
clearAbortRequest();
processImageStarted(0);
if (abortRequested()) {
processWriteAborted();
return;
}
if (param == null)
param = getDefaultWriteParam();
BMPImageWriteParam bmpParam = (BMPImageWriteParam)param;
int bitsPerPixel = 24;
boolean isPalette = false;
int paletteEntries = 0;
IndexColorModel icm = null;
RenderedImage input = null;
Raster inputRaster = null;
boolean writeRaster = image.hasRaster();
Rectangle sourceRegion = param.getSourceRegion();
SampleModel sampleModel = null;
ColorModel colorModel = null;
compImageSize = 0;
if (writeRaster) {
inputRaster = image.getRaster();
sampleModel = inputRaster.getSampleModel();
colorModel = ImageUtil.createColorModel(null, sampleModel);
if (sourceRegion == null)
sourceRegion = inputRaster.getBounds();
else
sourceRegion = sourceRegion.intersection(inputRaster.getBounds());
} else {
input = image.getRenderedImage();
sampleModel = input.getSampleModel();
colorModel = input.getColorModel();
Rectangle rect = new Rectangle(input.getMinX(), input.getMinY(),
input.getWidth(), input.getHeight());
if (sourceRegion == null)
sourceRegion = rect;
else
sourceRegion = sourceRegion.intersection(rect);
}
IIOMetadata imageMetadata = image.getMetadata();
BMPMetadata bmpImageMetadata = null;
if (imageMetadata != null
&& imageMetadata instanceof BMPMetadata)
{
bmpImageMetadata = (BMPMetadata)imageMetadata;
} else {
ImageTypeSpecifier imageType =
new ImageTypeSpecifier(colorModel, sampleModel);
bmpImageMetadata = (BMPMetadata)getDefaultImageMetadata(imageType,
param);
}
if (sourceRegion.isEmpty())
throw new RuntimeException(I18N.getString("BMPImageWrite0"));
int scaleX = param.getSourceXSubsampling();
int scaleY = param.getSourceYSubsampling();
int xOffset = param.getSubsamplingXOffset();
int yOffset = param.getSubsamplingYOffset();
int dataType = sampleModel.getDataType();
sourceRegion.translate(xOffset, yOffset);
sourceRegion.width -= xOffset;
sourceRegion.height -= yOffset;
int minX = sourceRegion.x / scaleX;
int minY = sourceRegion.y / scaleY;
w = (sourceRegion.width + scaleX - 1) / scaleX;
h = (sourceRegion.height + scaleY - 1) / scaleY;
xOffset = sourceRegion.x % scaleX;
yOffset = sourceRegion.y % scaleY;
Rectangle destinationRegion = new Rectangle(minX, minY, w, h);
boolean noTransform = destinationRegion.equals(sourceRegion);
int[] sourceBands = param.getSourceBands();
boolean noSubband = true;
int numBands = sampleModel.getNumBands();
if (sourceBands != null) {
sampleModel = sampleModel.createSubsetSampleModel(sourceBands);
colorModel = null;
noSubband = false;
numBands = sampleModel.getNumBands();
} else {
sourceBands = new int[numBands];
for (int i = 0; i < numBands; i++)
sourceBands[i] = i;
}
int[] bandOffsets = null;
boolean bgrOrder = true;
if (sampleModel instanceof ComponentSampleModel) {
bandOffsets = ((ComponentSampleModel)sampleModel).getBandOffsets();
if (sampleModel instanceof BandedSampleModel) {
bgrOrder = false;
} else {
for (int i = 0; i < bandOffsets.length; i++) {
bgrOrder &= (bandOffsets[i] == (bandOffsets.length - i - 1));
}
}
} else {
if (sampleModel instanceof SinglePixelPackedSampleModel) {
int[] bitOffsets = ((SinglePixelPackedSampleModel)sampleModel).getBitOffsets();
for (int i=0; i<bitOffsets.length-1; i++) {
bgrOrder &= bitOffsets[i] > bitOffsets[i+1];
}
}
}
if (bandOffsets == null) {
bandOffsets = new int[numBands];
for (int i = 0; i < numBands; i++)
bandOffsets[i] = i;
}
noTransform &= bgrOrder;
int[] sampleSize = sampleModel.getSampleSize();
int destScanlineBytes = w * numBands;
switch(bmpParam.getCompressionMode()) {
case ImageWriteParam.MODE_EXPLICIT:
compressionType = BMPCompressionTypes.getType(bmpParam.getCompressionType());
break;
case ImageWriteParam.MODE_COPY_FROM_METADATA:
compressionType = bmpImageMetadata.compression;
break;
case ImageWriteParam.MODE_DEFAULT:
compressionType = getPreferredCompressionType(colorModel, sampleModel);
break;
default:
compressionType = BI_RGB;
}
if (!canEncodeImage(compressionType, colorModel, sampleModel)) {
throw new IOException("Image can not be encoded with compression type "
+ BMPCompressionTypes.getName(compressionType));
}
byte[] r = null, g = null, b = null, a = null;
if (compressionType == BI_BITFIELDS) {
bitsPerPixel =
DataBuffer.getDataTypeSize(sampleModel.getDataType());
if (bitsPerPixel != 16 && bitsPerPixel != 32) {
bitsPerPixel = 32;
noTransform = false;
}
destScanlineBytes = w * bitsPerPixel + 7 >> 3;
isPalette = true;
paletteEntries = 3;
r = new byte[paletteEntries];
g = new byte[paletteEntries];
b = new byte[paletteEntries];
a = new byte[paletteEntries];
int rmask = 0x00ff0000;
int gmask = 0x0000ff00;
int bmask = 0x000000ff;
if (bitsPerPixel == 16) {
if (colorModel instanceof DirectColorModel) {
DirectColorModel dcm = (DirectColorModel)colorModel;
rmask = dcm.getRedMask();
gmask = dcm.getGreenMask();
bmask = dcm.getBlueMask();
} else {
throw new IOException("Image can not be encoded with " +
"compression type " +
BMPCompressionTypes.getName(compressionType));
}
}
writeMaskToPalette(rmask, 0, r, g, b, a);
writeMaskToPalette(gmask, 1, r, g, b, a);
writeMaskToPalette(bmask, 2, r, g, b, a);
if (!noTransform) {
bitMasks = new int[3];
bitMasks[0] = rmask;
bitMasks[1] = gmask;
bitMasks[2] = bmask;
bitPos = new int[3];
bitPos[0] = firstLowBit(rmask);
bitPos[1] = firstLowBit(gmask);
bitPos[2] = firstLowBit(bmask);
}
if (colorModel instanceof IndexColorModel) {
icm = (IndexColorModel)colorModel;
}
} else {
if (colorModel instanceof IndexColorModel) {
isPalette = true;
icm = (IndexColorModel)colorModel;
paletteEntries = icm.getMapSize();
if (paletteEntries <= 2) {
bitsPerPixel = 1;
destScanlineBytes = w + 7 >> 3;
} else if (paletteEntries <= 16) {
bitsPerPixel = 4;
destScanlineBytes = w + 1 >> 1;
} else if (paletteEntries <= 256) {
bitsPerPixel = 8;
} else {
bitsPerPixel = 24;
isPalette = false;
paletteEntries = 0;
destScanlineBytes = w * 3;
}
if (isPalette == true) {
r = new byte[paletteEntries];
g = new byte[paletteEntries];
b = new byte[paletteEntries];
a = new byte[paletteEntries];
icm.getAlphas(a);
icm.getReds(r);
icm.getGreens(g);
icm.getBlues(b);
}
} else {
if (numBands == 1) {
isPalette = true;
paletteEntries = 256;
bitsPerPixel = sampleSize[0];
destScanlineBytes = (w * bitsPerPixel + 7 >> 3);
r = new byte[256];
g = new byte[256];
b = new byte[256];
a = new byte[256];
for (int i = 0; i < 256; i++) {
r[i] = (byte)i;
g[i] = (byte)i;
b[i] = (byte)i;
a[i] = (byte)255;
}
} else {
if (sampleModel instanceof SinglePixelPackedSampleModel &&
noSubband)
{
int[] sample_sizes = sampleModel.getSampleSize();
bitsPerPixel = 0;
for (int size : sample_sizes) {
bitsPerPixel += size;
}
bitsPerPixel = roundBpp(bitsPerPixel);
if (bitsPerPixel != DataBuffer.getDataTypeSize(sampleModel.getDataType())) {
noTransform = false;
}
destScanlineBytes = w * bitsPerPixel + 7 >> 3;
}
}
}
}
int fileSize = 0;
int offset = 0;
int headerSize = 0;
int imageSize = 0;
int xPelsPerMeter = 0;
int yPelsPerMeter = 0;
int colorsUsed = 0;
int colorsImportant = paletteEntries;
int padding = destScanlineBytes % 4;
if (padding != 0) {
padding = 4 - padding;
}
offset = 54 + paletteEntries * 4;
imageSize = (destScanlineBytes + padding) * h;
fileSize = imageSize + offset;
headerSize = 40;
long headPos = stream.getStreamPosition();
writeFileHeader(fileSize, offset);
if (compressionType == BI_RGB ||
compressionType == BI_BITFIELDS)
{
isTopDown = bmpParam.isTopDown();
} else {
isTopDown = false;
}
writeInfoHeader(headerSize, bitsPerPixel);
stream.writeInt(compressionType);
stream.writeInt(imageSize);
stream.writeInt(xPelsPerMeter);
stream.writeInt(yPelsPerMeter);
stream.writeInt(colorsUsed);
stream.writeInt(colorsImportant);
if (isPalette == true) {
if (compressionType == BI_BITFIELDS) {
for (int i=0; i<3; i++) {
int mask = (a[i]&0xFF) + ((r[i]&0xFF)*0x100) + ((g[i]&0xFF)*0x10000) + ((b[i]&0xFF)*0x1000000);
stream.writeInt(mask);
}
} else {
for (int i=0; i<paletteEntries; i++) {
stream.writeByte(b[i]);
stream.writeByte(g[i]);
stream.writeByte(r[i]);
stream.writeByte(a[i]);
}
}
}
int scanlineBytes = w * numBands;
int[] pixels = new int[scanlineBytes * scaleX];
bpixels = new byte[destScanlineBytes];
int l;
if (compressionType == BI_JPEG ||
compressionType == BI_PNG) {
embedded_stream = new ByteArrayOutputStream();
writeEmbedded(image, bmpParam);
embedded_stream.flush();
imageSize = embedded_stream.size();
long endPos = stream.getStreamPosition();
fileSize = offset + imageSize;
stream.seek(headPos);
writeSize(fileSize, 2);
stream.seek(headPos);
writeSize(imageSize, 34);
stream.seek(endPos);
stream.write(embedded_stream.toByteArray());
embedded_stream = null;
processImageComplete();
stream.flushBefore(stream.getStreamPosition());
return;
}
int maxBandOffset = bandOffsets[0];
for (int i = 1; i < bandOffsets.length; i++)
if (bandOffsets[i] > maxBandOffset)
maxBandOffset = bandOffsets[i];
int[] pixel = new int[maxBandOffset + 1];
int destScanlineLength = destScanlineBytes;
if (noTransform && noSubband) {
destScanlineLength = destScanlineBytes / (DataBuffer.getDataTypeSize(dataType)>>3);
}
for (int i = 0; i < h; i++) {
int row = minY + i;
if (!isTopDown)
row = minY + h - i -1;
Raster src = inputRaster;
Rectangle srcRect =
new Rectangle(minX * scaleX + xOffset,
row * scaleY + yOffset,
(w - 1)* scaleX + 1,
1);
if (!writeRaster)
src = input.getData(srcRect);
if (noTransform && noSubband) {
SampleModel sm = src.getSampleModel();
int pos = 0;
int startX = srcRect.x - src.getSampleModelTranslateX();
int startY = srcRect.y - src.getSampleModelTranslateY();
if (sm instanceof ComponentSampleModel) {
ComponentSampleModel csm = (ComponentSampleModel)sm;
pos = csm.getOffset(startX, startY, 0);
for(int nb=1; nb < csm.getNumBands(); nb++) {
if (pos > csm.getOffset(startX, startY, nb)) {
pos = csm.getOffset(startX, startY, nb);
}
}
} else if (sm instanceof MultiPixelPackedSampleModel) {
MultiPixelPackedSampleModel mppsm =
(MultiPixelPackedSampleModel)sm;
pos = mppsm.getOffset(startX, startY);
} else if (sm instanceof SinglePixelPackedSampleModel) {
SinglePixelPackedSampleModel sppsm =
(SinglePixelPackedSampleModel)sm;
pos = sppsm.getOffset(startX, startY);
}
if (compressionType == BI_RGB || compressionType == BI_BITFIELDS){
switch(dataType) {
case DataBuffer.TYPE_BYTE:
byte[] bdata =
((DataBufferByte)src.getDataBuffer()).getData();
stream.write(bdata, pos, destScanlineLength);
break;
case DataBuffer.TYPE_SHORT:
short[] sdata =
((DataBufferShort)src.getDataBuffer()).getData();
stream.writeShorts(sdata, pos, destScanlineLength);
break;
case DataBuffer.TYPE_USHORT:
short[] usdata =
((DataBufferUShort)src.getDataBuffer()).getData();
stream.writeShorts(usdata, pos, destScanlineLength);
break;
case DataBuffer.TYPE_INT:
int[] idata =
((DataBufferInt)src.getDataBuffer()).getData();
stream.writeInts(idata, pos, destScanlineLength);
break;
}
for(int k=0; k<padding; k++) {
stream.writeByte(0);
}
} else if (compressionType == BI_RLE4) {
if (bpixels == null || bpixels.length < scanlineBytes)
bpixels = new byte[scanlineBytes];
src.getPixels(srcRect.x, srcRect.y,
srcRect.width, srcRect.height, pixels);
for (int h=0; h<scanlineBytes; h++) {
bpixels[h] = (byte)pixels[h];
}
encodeRLE4(bpixels, scanlineBytes);
} else if (compressionType == BI_RLE8) {
if (bpixels == null || bpixels.length < scanlineBytes)
bpixels = new byte[scanlineBytes];
src.getPixels(srcRect.x, srcRect.y,
srcRect.width, srcRect.height, pixels);
for (int h=0; h<scanlineBytes; h++) {
bpixels[h] = (byte)pixels[h];
}
encodeRLE8(bpixels, scanlineBytes);
}
} else {
src.getPixels(srcRect.x, srcRect.y,
srcRect.width, srcRect.height, pixels);
if (scaleX != 1 || maxBandOffset != numBands - 1) {
for (int j = 0, k = 0, n=0; j < w;
j++, k += scaleX * numBands, n += numBands)
{
System.arraycopy(pixels, k, pixel, 0, pixel.length);
for (int m = 0; m < numBands; m++) {
pixels[n + m] = pixel[sourceBands[m]];
}
}
}
writePixels(0, scanlineBytes, bitsPerPixel, pixels,
padding, numBands, icm);
}
processImageProgress(100.0f * (((float)i) / ((float)h)));
if (abortRequested()) {
break;
}
}
if (compressionType == BI_RLE4 ||
compressionType == BI_RLE8) {
stream.writeByte(0);
stream.writeByte(1);
incCompImageSize(2);
imageSize = compImageSize;
fileSize = compImageSize + offset;
long endPos = stream.getStreamPosition();
stream.seek(headPos);
writeSize(fileSize, 2);
stream.seek(headPos);
writeSize(imageSize, 34);
stream.seek(endPos);
}
if (abortRequested()) {
processWriteAborted();
} else {
processImageComplete();
stream.flushBefore(stream.getStreamPosition());
}
}
private void writePixels(int l, int scanlineBytes, int bitsPerPixel,
int[] pixels,
int padding, int numBands,
IndexColorModel icm) throws IOException {
int pixel = 0;
int k = 0;
switch (bitsPerPixel) {
case 1:
for (int j=0; j<scanlineBytes/8; j++) {
bpixels[k++] = (byte)((pixels[l++] << 7) |
(pixels[l++] << 6) |
(pixels[l++] << 5) |
(pixels[l++] << 4) |
(pixels[l++] << 3) |
(pixels[l++] << 2) |
(pixels[l++] << 1) |
pixels[l++]);
}
if (scanlineBytes%8 > 0) {
pixel = 0;
for (int j=0; j<scanlineBytes%8; j++) {
pixel |= (pixels[l++] << (7 - j));
}
bpixels[k++] = (byte)pixel;
}
stream.write(bpixels, 0, (scanlineBytes+7)/8);
break;
case 4:
if (compressionType == BI_RLE4){
byte[] bipixels = new byte[scanlineBytes];
for (int h=0; h<scanlineBytes; h++) {
bipixels[h] = (byte)pixels[l++];
}
encodeRLE4(bipixels, scanlineBytes);
}else {
for (int j=0; j<scanlineBytes/2; j++) {
pixel = (pixels[l++] << 4) | pixels[l++];
bpixels[k++] = (byte)pixel;
}
if ((scanlineBytes%2) == 1) {
pixel = pixels[l] << 4;
bpixels[k++] = (byte)pixel;
}
stream.write(bpixels, 0, (scanlineBytes+1)/2);
}
break;
case 8:
if(compressionType == BI_RLE8) {
for (int h=0; h<scanlineBytes; h++) {
bpixels[h] = (byte)pixels[l++];
}
encodeRLE8(bpixels, scanlineBytes);
}else {
for (int j=0; j<scanlineBytes; j++) {
bpixels[j] = (byte)pixels[l++];
}
stream.write(bpixels, 0, scanlineBytes);
}
break;
case 16:
if (spixels == null)
spixels = new short[scanlineBytes / numBands];
for (int j = 0, m = 0; j < scanlineBytes; m++) {
spixels[m] = 0;
if (compressionType == BI_RGB) {
spixels[m] = (short)
(((0x1f & pixels[j ]) << 10) |
((0x1f & pixels[j + 1]) << 5) |
((0x1f & pixels[j + 2]) ));
j += 3;
} else {
for(int i = 0 ; i < numBands; i++, j++) {
spixels[m] |=
(((pixels[j]) << bitPos[i]) & bitMasks[i]);
}
}
}
stream.writeShorts(spixels, 0, spixels.length);
break;
case 24:
if (numBands == 3) {
for (int j=0; j<scanlineBytes; j+=3) {
bpixels[k++] = (byte)(pixels[l+2]);
bpixels[k++] = (byte)(pixels[l+1]);
bpixels[k++] = (byte)(pixels[l]);
l+=3;
}
stream.write(bpixels, 0, scanlineBytes);
} else {
int entries = icm.getMapSize();
byte[] r = new byte[entries];
byte[] g = new byte[entries];
byte[] b = new byte[entries];
icm.getReds(r);
icm.getGreens(g);
icm.getBlues(b);
int index;
for (int j=0; j<scanlineBytes; j++) {
index = pixels[l];
bpixels[k++] = b[index];
bpixels[k++] = g[index];
bpixels[k++] = b[index];
l++;
}
stream.write(bpixels, 0, scanlineBytes*3);
}
break;
case 32:
if (ipixels == null)
ipixels = new int[scanlineBytes / numBands];
if (numBands == 3) {
for (int j = 0, m = 0; j < scanlineBytes; m++) {
ipixels[m] = 0;
if (compressionType == BI_RGB) {
ipixels[m] =
((0xff & pixels[j + 2]) << 16) |
((0xff & pixels[j + 1]) << 8) |
((0xff & pixels[j ]) );
j += 3;
} else {
for(int i = 0 ; i < numBands; i++, j++) {
ipixels[m] |=
(((pixels[j]) << bitPos[i]) & bitMasks[i]);
}
}
}
} else {
for (int j = 0; j < scanlineBytes; j++) {
if (icm != null) {
ipixels[j] = icm.getRGB(pixels[j]);
} else {
ipixels[j] =
pixels[j] << 16 | pixels[j] << 8 | pixels[j];
}
}
}
stream.writeInts(ipixels, 0, ipixels.length);
break;
}
if (compressionType == BI_RGB ||
compressionType == BI_BITFIELDS)
{
for(k=0; k<padding; k++) {
stream.writeByte(0);
}
}
}
private void encodeRLE8(byte[] bpixels, int scanlineBytes)
throws IOException{
int runCount = 1, absVal = -1, j = -1;
byte runVal = 0, nextVal =0 ;
runVal = bpixels[++j];
byte[] absBuf = new byte[256];
while (j < scanlineBytes-1) {
nextVal = bpixels[++j];
if (nextVal == runVal ){
if(absVal >= 3 ){
stream.writeByte(0);
stream.writeByte(absVal);
incCompImageSize(2);
for(int a=0; a<absVal;a++){
stream.writeByte(absBuf[a]);
incCompImageSize(1);
}
if (!isEven(absVal)){
stream.writeByte(0);
incCompImageSize(1);
}
}
else if(absVal > -1){
for (int b=0;b<absVal;b++){
stream.writeByte(1);
stream.writeByte(absBuf[b]);
incCompImageSize(2);
}
}
absVal = -1;
runCount++;
if (runCount == 256){
stream.writeByte(runCount-1);
stream.writeByte(runVal);
incCompImageSize(2);
runCount = 1;
}
}
else {
if (runCount > 1){
stream.writeByte(runCount);
stream.writeByte(runVal);
incCompImageSize(2);
} else if (absVal < 0){
absBuf[++absVal] = runVal;
absBuf[++absVal] = nextVal;
} else if (absVal < 254){
absBuf[++absVal] = nextVal;
} else {
stream.writeByte(0);
stream.writeByte(absVal+1);
incCompImageSize(2);
for(int a=0; a<=absVal;a++){
stream.writeByte(absBuf[a]);
incCompImageSize(1);
}
stream.writeByte(0);
incCompImageSize(1);
absVal = -1;
}
runVal = nextVal;
runCount = 1;
}
if (j == scanlineBytes-1){
if (absVal == -1){
stream.writeByte(runCount);
stream.writeByte(runVal);
incCompImageSize(2);
runCount = 1;
}
else {
if(absVal >= 2){
stream.writeByte(0);
stream.writeByte(absVal+1);
incCompImageSize(2);
for(int a=0; a<=absVal;a++){
stream.writeByte(absBuf[a]);
incCompImageSize(1);
}
if (!isEven(absVal+1)){
stream.writeByte(0);
incCompImageSize(1);
}
}
else if(absVal > -1){
for (int b=0;b<=absVal;b++){
stream.writeByte(1);
stream.writeByte(absBuf[b]);
incCompImageSize(2);
}
}
}
stream.writeByte(0);
stream.writeByte(0);
incCompImageSize(2);
}
}
}
private void encodeRLE4(byte[] bipixels, int scanlineBytes)
throws IOException {
int runCount=2, absVal=-1, j=-1, pixel=0, q=0;
byte runVal1=0, runVal2=0, nextVal1=0, nextVal2=0;
byte[] absBuf = new byte[256];
runVal1 = bipixels[++j];
runVal2 = bipixels[++j];
while (j < scanlineBytes-2){
nextVal1 = bipixels[++j];
nextVal2 = bipixels[++j];
if (nextVal1 == runVal1 ) {
if(absVal >= 4){
stream.writeByte(0);
stream.writeByte(absVal - 1);
incCompImageSize(2);
for(int a=0; a<absVal-2;a+=2){
pixel = (absBuf[a] << 4) | absBuf[a+1];
stream.writeByte((byte)pixel);
incCompImageSize(1);
}
if(!(isEven(absVal-1))){
q = absBuf[absVal-2] << 4| 0;
stream.writeByte(q);
incCompImageSize(1);
}
if ( !isEven((int)Math.ceil((absVal-1)/2)) ) {
stream.writeByte(0);
incCompImageSize(1);
}
} else if (absVal > -1){
stream.writeByte(2);
pixel = (absBuf[0] << 4) | absBuf[1];
stream.writeByte(pixel);
incCompImageSize(2);
}
absVal = -1;
if (nextVal2 == runVal2){
runCount+=2;
if(runCount == 256){
stream.writeByte(runCount-1);
pixel = ( runVal1 << 4) | runVal2;
stream.writeByte(pixel);
incCompImageSize(2);
runCount =2;
if(j< scanlineBytes - 1){
runVal1 = runVal2;
runVal2 = bipixels[++j];
} else {
stream.writeByte(01);
int r = runVal2 << 4 | 0;
stream.writeByte(r);
incCompImageSize(2);
runCount = -1;
}
}
} else {
runCount++;
pixel = ( runVal1 << 4) | runVal2;
stream.writeByte(runCount);
stream.writeByte(pixel);
incCompImageSize(2);
runCount = 2;
runVal1 = nextVal2;
if (j < scanlineBytes -1){
runVal2 = bipixels[++j];
}else {
stream.writeByte(01);
int r = nextVal2 << 4 | 0;
stream.writeByte(r);
incCompImageSize(2);
runCount = -1;
}
}
} else{
if (runCount > 2){
pixel = ( runVal1 << 4) | runVal2;
stream.writeByte(runCount);
stream.writeByte(pixel);
incCompImageSize(2);
} else if (absVal < 0){
absBuf[++absVal] = runVal1;
absBuf[++absVal] = runVal2;
absBuf[++absVal] = nextVal1;
absBuf[++absVal] = nextVal2;
} else if (absVal < 253){
absBuf[++absVal] = nextVal1;
absBuf[++absVal] = nextVal2;
} else {
stream.writeByte(0);
stream.writeByte(absVal+1);
incCompImageSize(2);
for(int a=0; a<absVal;a+=2){
pixel = (absBuf[a] << 4) | absBuf[a+1];
stream.writeByte((byte)pixel);
incCompImageSize(1);
}
stream.writeByte(0);
incCompImageSize(1);
absVal = -1;
}
runVal1 = nextVal1;
runVal2 = nextVal2;
runCount = 2;
}
if (j >= scanlineBytes-2 ) {
if (absVal == -1 && runCount >= 2){
if (j == scanlineBytes-2){
if(bipixels[++j] == runVal1){
runCount++;
pixel = ( runVal1 << 4) | runVal2;
stream.writeByte(runCount);
stream.writeByte(pixel);
incCompImageSize(2);
} else {
pixel = ( runVal1 << 4) | runVal2;
stream.writeByte(runCount);
stream.writeByte(pixel);
stream.writeByte(01);
pixel = bipixels[j]<<4 |0;
stream.writeByte(pixel);
int n = bipixels[j]<<4|0;
incCompImageSize(4);
}
} else {
stream.writeByte(runCount);
pixel =( runVal1 << 4) | runVal2 ;
stream.writeByte(pixel);
incCompImageSize(2);
}
} else if(absVal > -1){
if (j == scanlineBytes-2){
absBuf[++absVal] = bipixels[++j];
}
if (absVal >=2){
stream.writeByte(0);
stream.writeByte(absVal+1);
incCompImageSize(2);
for(int a=0; a<absVal;a+=2){
pixel = (absBuf[a] << 4) | absBuf[a+1];
stream.writeByte((byte)pixel);
incCompImageSize(1);
}
if(!(isEven(absVal+1))){
q = absBuf[absVal] << 4|0;
stream.writeByte(q);
incCompImageSize(1);
}
if ( !isEven((int)Math.ceil((absVal+1)/2)) ) {
stream.writeByte(0);
incCompImageSize(1);
}
} else {
switch (absVal){
case 0:
stream.writeByte(1);
int n = absBuf[0]<<4 | 0;
stream.writeByte(n);
incCompImageSize(2);
break;
case 1:
stream.writeByte(2);
pixel = (absBuf[0] << 4) | absBuf[1];
stream.writeByte(pixel);
incCompImageSize(2);
break;
}
}
}
stream.writeByte(0);
stream.writeByte(0);
incCompImageSize(2);
}
}
}
private synchronized void incCompImageSize(int value){
compImageSize = compImageSize + value;
}
private boolean isEven(int number) {
return (number%2 == 0 ? true : false);
}
private void (int fileSize, int offset) throws IOException {
stream.writeByte('B');
stream.writeByte('M');
stream.writeInt(fileSize);
stream.writeInt(0);
stream.writeInt(offset);
}
private void (int headerSize,
int bitsPerPixel) throws IOException {
stream.writeInt(headerSize);
stream.writeInt(w);
stream.writeInt(isTopDown ? -h : h);
stream.writeShort(1);
stream.writeShort(bitsPerPixel);
}
private void writeSize(int dword, int offset) throws IOException {
stream.skipBytes(offset);
stream.writeInt(dword);
}
public void reset() {
super.reset();
stream = null;
}
private void writeEmbedded(IIOImage image,
ImageWriteParam bmpParam) throws IOException {
String format =
compressionType == BI_JPEG ? "jpeg" : "png";
Iterator<ImageWriter> iterator =
ImageIO.getImageWritersByFormatName(format);
ImageWriter writer = null;
if (iterator.hasNext())
writer = iterator.next();
if (writer != null) {
if (embedded_stream == null) {
throw new RuntimeException("No stream for writing embedded image!");
}
writer.addIIOWriteProgressListener(new IIOWriteProgressAdapter() {
public void imageProgress(ImageWriter source, float percentageDone) {
processImageProgress(percentageDone);
}
});
writer.addIIOWriteWarningListener(new IIOWriteWarningListener() {
public void warningOccurred(ImageWriter source, int imageIndex, String warning) {
processWarningOccurred(imageIndex, warning);
}
});
writer.setOutput(ImageIO.createImageOutputStream(embedded_stream));
ImageWriteParam param = writer.getDefaultWriteParam();
param.setDestinationOffset(bmpParam.getDestinationOffset());
param.setSourceBands(bmpParam.getSourceBands());
param.setSourceRegion(bmpParam.getSourceRegion());
param.setSourceSubsampling(bmpParam.getSourceXSubsampling(),
bmpParam.getSourceYSubsampling(),
bmpParam.getSubsamplingXOffset(),
bmpParam.getSubsamplingYOffset());
writer.write(null, image, param);
} else
throw new RuntimeException(I18N.getString("BMPImageWrite5") + " " + format);
}
private int firstLowBit(int num) {
int count = 0;
while ((num & 1) == 0) {
count++;
num >>>= 1;
}
return count;
}
private class IIOWriteProgressAdapter implements IIOWriteProgressListener {
public void imageComplete(ImageWriter source) {
}
public void imageProgress(ImageWriter source, float percentageDone) {
}
public void imageStarted(ImageWriter source, int imageIndex) {
}
public void thumbnailComplete(ImageWriter source) {
}
public void thumbnailProgress(ImageWriter source, float percentageDone) {
}
public void thumbnailStarted(ImageWriter source, int imageIndex, int thumbnailIndex) {
}
public void writeAborted(ImageWriter source) {
}
}
protected int getPreferredCompressionType(ColorModel cm, SampleModel sm) {
ImageTypeSpecifier imageType = new ImageTypeSpecifier(cm, sm);
return getPreferredCompressionType(imageType);
}
protected int getPreferredCompressionType(ImageTypeSpecifier imageType) {
if (imageType.getBufferedImageType() == BufferedImage.TYPE_USHORT_565_RGB) {
return BI_BITFIELDS;
}
return BI_RGB;
}
protected boolean canEncodeImage(int compression, ColorModel cm, SampleModel sm) {
ImageTypeSpecifier imgType = new ImageTypeSpecifier(cm, sm);
return canEncodeImage(compression, imgType);
}
protected boolean canEncodeImage(int compression, ImageTypeSpecifier imgType) {
ImageWriterSpi spi = this.getOriginatingProvider();
if (!spi.canEncodeImage(imgType)) {
return false;
}
int biType = imgType.getBufferedImageType();
int bpp = imgType.getColorModel().getPixelSize();
if (compressionType == BI_RLE4 && bpp != 4) {
return false;
}
if (compressionType == BI_RLE8 && bpp != 8) {
return false;
}
if (bpp == 16) {
boolean canUseRGB = false;
boolean canUseBITFIELDS = false;
SampleModel sm = imgType.getSampleModel();
if (sm instanceof SinglePixelPackedSampleModel) {
int[] sizes =
((SinglePixelPackedSampleModel)sm).getSampleSize();
canUseRGB = true;
canUseBITFIELDS = true;
for (int i = 0; i < sizes.length; i++) {
canUseRGB &= (sizes[i] == 5);
canUseBITFIELDS &= ((sizes[i] == 5) ||
(i == 1 && sizes[i] == 6));
}
}
return (((compressionType == BI_RGB) && canUseRGB) ||
((compressionType == BI_BITFIELDS) && canUseBITFIELDS));
}
return true;
}
protected void writeMaskToPalette(int mask, int i,
byte[] r, byte[]g, byte[] b, byte[]a) {
b[i] = (byte)(0xff & (mask >> 24));
g[i] = (byte)(0xff & (mask >> 16));
r[i] = (byte)(0xff & (mask >> 8));
a[i] = (byte)(0xff & mask);
}
private int roundBpp(int x) {
if (x <= 8) {
return 8;
} else if (x <= 16) {
return 16;
} if (x <= 24) {
return 24;
} else {
return 32;
}
}
}