You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
906 lines
23 KiB
906 lines
23 KiB
/* |
|
* Copyright (c) 2001 Sun Microsystems, Inc. All Rights Reserved. |
|
* |
|
* Redistribution and use in source and binary forms, with or without |
|
* modification, are permitted provided that the following conditions are met: |
|
* |
|
* -Redistributions of source code must retain the above copyright notice, this |
|
* list of conditions and the following disclaimer. |
|
* |
|
* -Redistribution in binary form must reproduct the above copyright notice, |
|
* this list of conditions and the following disclaimer in the documentation |
|
* and/or other materials provided with the distribution. |
|
* |
|
* Neither the name of Sun Microsystems, Inc. or the names of contributors may |
|
* be used to endorse or promote products derived from this software without |
|
* specific prior written permission. |
|
* |
|
* This software is provided "AS IS," without a warranty of any kind. ALL |
|
* EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY |
|
* IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR |
|
* NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE |
|
* LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING |
|
* OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS |
|
* LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, |
|
* INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER |
|
* CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF |
|
* OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE |
|
* POSSIBILITY OF SUCH DAMAGES. |
|
* |
|
* You acknowledge that Software is not designed,licensed or intended for use in |
|
* the design, construction, operation or maintenance of any nuclear facility. |
|
*/ |
|
package com.fr.third.JAI; |
|
import java.io.OutputStream; |
|
import java.io.IOException; |
|
import java.awt.image.Raster; |
|
import java.awt.image.DataBuffer; |
|
import java.awt.image.RenderedImage; |
|
import java.awt.image.SampleModel; |
|
import java.awt.Rectangle; |
|
import java.awt.image.ColorModel; |
|
import java.awt.image.IndexColorModel; |
|
/** |
|
* An ImageEncoder for the various versions of the BMP image file format. |
|
* |
|
* Unless specified otherwise by the BMPDecodeParam object passed to the |
|
* constructor, Version 3 will be the default version used. |
|
* |
|
* <p>If the image to be encoded has an IndexColorModel and can be encoded |
|
* using upto 8 bits per pixel, the image will be written out as a Palette |
|
* color image with an appropriate number of bits per pixel. For example an |
|
* image having a 256 color IndexColorModel will be written out as a Palette |
|
* image with 8 bits per pixel while one with a 16 color palette will be |
|
* written out as a Palette image with 4 bits per pixel. For all other images, |
|
* the 24 bit image format will be used. |
|
* |
|
* |
|
*/ |
|
public class BMPImageEncoder extends ImageEncoderImpl { |
|
|
|
private OutputStream output; |
|
private int version; |
|
private boolean isCompressed, isTopDown; |
|
private int w, h; |
|
private int compImageSize = 0; |
|
|
|
/** |
|
* An ImageEncoder for the BMP file format. |
|
* |
|
* @param output The OutputStream to write to. |
|
* @param param The BMPEncodeParam object. |
|
*/ |
|
public BMPImageEncoder(OutputStream output, ImageEncodeParam param) { |
|
|
|
super(output, param); |
|
|
|
this.output = output; |
|
|
|
BMPEncodeParam bmpParam; |
|
if (param == null) { |
|
// Use default valued BMPEncodeParam |
|
bmpParam = new BMPEncodeParam(); |
|
} else { |
|
bmpParam = (BMPEncodeParam)param; |
|
} |
|
|
|
this.version = bmpParam.getVersion(); |
|
this.isCompressed = bmpParam.isCompressed(); |
|
if(isCompressed && !(output instanceof SeekableOutputStream)){ |
|
throw new |
|
IllegalArgumentException(JaiI18N.getString("BMPImageEncoder6")); |
|
} |
|
|
|
this.isTopDown = bmpParam.isTopDown(); |
|
} |
|
|
|
/** |
|
* Encodes a RenderedImage and writes the output to the |
|
* OutputStream associated with this ImageEncoder. |
|
*/ |
|
public void encode(RenderedImage im) throws IOException { |
|
|
|
// Get image dimensions |
|
int minX = im.getMinX(); |
|
int minY = im.getMinY(); |
|
w = im.getWidth(); |
|
h = im.getHeight(); |
|
|
|
// Default is using 24 bits per pixel. |
|
int bitsPerPixel = 24; |
|
boolean isPalette = false; |
|
int paletteEntries = 0; |
|
IndexColorModel icm = null; |
|
|
|
SampleModel sm = im.getSampleModel(); |
|
int numBands = sm.getNumBands(); |
|
|
|
ColorModel cm = im.getColorModel(); |
|
|
|
if (numBands != 1 && numBands != 3) { |
|
throw new |
|
IllegalArgumentException(JaiI18N.getString("BMPImageEncoder1")); |
|
} |
|
|
|
int sampleSize[] = sm.getSampleSize(); |
|
if (sampleSize[0] > 8) { |
|
throw new RuntimeException(JaiI18N.getString("BMPImageEncoder2")); |
|
} |
|
|
|
for (int i=1; i<sampleSize.length; i++) { |
|
if (sampleSize[i] != sampleSize[0]) { |
|
throw |
|
new RuntimeException(JaiI18N.getString("BMPImageEncoder3")); |
|
} |
|
} |
|
|
|
// Float and Double data cannot be written in a BMP format. |
|
int dataType = sm.getTransferType(); |
|
if ((dataType == DataBuffer.TYPE_USHORT) || |
|
(dataType == DataBuffer.TYPE_SHORT) || |
|
(dataType == DataBuffer.TYPE_INT) || |
|
(dataType == DataBuffer.TYPE_FLOAT) || |
|
(dataType == DataBuffer.TYPE_DOUBLE)) { |
|
throw new RuntimeException(JaiI18N.getString("BMPImageEncoder0")); |
|
} |
|
|
|
// Number of bytes that a scanline for the image written out will have. |
|
int destScanlineBytes = w * numBands; |
|
int compression = 0; |
|
|
|
|
|
byte r[] = null, g[] = null, b[] = null, a[] = null; |
|
|
|
if (cm instanceof IndexColorModel) { |
|
|
|
isPalette = true; |
|
icm = (IndexColorModel)cm; |
|
paletteEntries = icm.getMapSize(); |
|
|
|
if (paletteEntries <= 2) { |
|
|
|
bitsPerPixel = 1; |
|
destScanlineBytes = (int)Math.ceil((double)w/8.0); |
|
|
|
} else if (paletteEntries <= 16) { |
|
|
|
bitsPerPixel = 4; |
|
destScanlineBytes = (int)Math.ceil((double)w/2.0); |
|
|
|
} else if (paletteEntries <= 256) { |
|
|
|
bitsPerPixel = 8; |
|
|
|
} else { |
|
|
|
// Cannot be written as a Palette image. So write out as |
|
// 24 bit image. |
|
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 { |
|
|
|
// Grey scale images |
|
if (numBands == 1) { |
|
|
|
isPalette = true; |
|
paletteEntries = 256; |
|
// int sampleSize[] = sm.getSampleSize(); |
|
bitsPerPixel = sampleSize[0]; |
|
|
|
destScanlineBytes = (int)Math.ceil((double)(w * bitsPerPixel) / |
|
8.0); |
|
|
|
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)i; |
|
} |
|
} |
|
} |
|
|
|
// actual writing of image data |
|
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 = 0; |
|
|
|
// Calculate padding for each scanline |
|
int remainder = destScanlineBytes % 4; |
|
if (remainder != 0) { |
|
padding = 4 - remainder; |
|
} |
|
|
|
switch (version) { |
|
case BMPEncodeParam.VERSION_2: |
|
offset = 26 + paletteEntries * 3; |
|
headerSize = 12; |
|
imageSize = (destScanlineBytes + padding) * h; |
|
fileSize = imageSize + offset; |
|
throw new |
|
RuntimeException(JaiI18N.getString("BMPImageEncoder5")); |
|
//break; |
|
|
|
case BMPEncodeParam.VERSION_3: |
|
// FileHeader is 14 bytes, BitmapHeader is 40 bytes, |
|
// add palette size and that is where the data will begin |
|
if (isCompressed && bitsPerPixel == 8) { |
|
compression = 1; |
|
} else if (isCompressed && bitsPerPixel == 4) { |
|
compression = 2; |
|
} |
|
offset = 54 + paletteEntries * 4; |
|
|
|
imageSize = (destScanlineBytes + padding) * h; |
|
fileSize = imageSize + offset; |
|
headerSize = 40; |
|
break; |
|
|
|
case BMPEncodeParam.VERSION_4: |
|
headerSize = 108; |
|
throw new |
|
RuntimeException(JaiI18N.getString("BMPImageEncoder5")); |
|
// break; |
|
} |
|
|
|
writeFileHeader(fileSize, offset); |
|
|
|
writeInfoHeader(headerSize, bitsPerPixel); |
|
|
|
// compression |
|
writeDWord(compression); |
|
|
|
// imageSize |
|
writeDWord(imageSize); |
|
|
|
// xPelsPerMeter |
|
writeDWord(xPelsPerMeter); |
|
|
|
// yPelsPerMeter |
|
writeDWord(yPelsPerMeter); |
|
|
|
// Colors Used |
|
writeDWord(colorsUsed); |
|
|
|
// Colors Important |
|
writeDWord(colorsImportant); |
|
|
|
// palette |
|
if (isPalette == true) { |
|
|
|
// write palette |
|
switch(version) { |
|
|
|
// has 3 field entries |
|
case BMPEncodeParam.VERSION_2: |
|
|
|
for (int i=0; i<paletteEntries; i++) { |
|
output.write(b[i]); |
|
output.write(g[i]); |
|
output.write(r[i]); |
|
} |
|
break; |
|
|
|
// has 4 field entries |
|
default: |
|
|
|
for (int i=0; i<paletteEntries; i++) { |
|
output.write(b[i]); |
|
output.write(g[i]); |
|
output.write(r[i]); |
|
output.write(a[i]); |
|
} |
|
break; |
|
} |
|
|
|
} // else no palette |
|
|
|
// Writing of actual image data |
|
|
|
int scanlineBytes = w * numBands; |
|
|
|
// Buffer for up to 8 rows of pixels |
|
int[] pixels = new int[8 * scanlineBytes]; |
|
|
|
// Also create a buffer to hold one line of the data |
|
// to be written to the file, so we can use array writes. |
|
byte[] bpixels = new byte[destScanlineBytes]; |
|
|
|
int l; |
|
|
|
if (!isTopDown) { |
|
// Process 8 rows at a time so all but the first will have a |
|
// multiple of 8 rows. |
|
int lastRow = minY + h; |
|
|
|
for (int row = (lastRow-1); row >= minY; row -= 8) { |
|
// Number of rows being read |
|
int rows = Math.min(8, row - minY + 1); |
|
|
|
// Get the pixels |
|
Raster src = im.getData(new Rectangle(minX, row - rows + 1, |
|
w, rows)); |
|
|
|
src.getPixels(minX, row - rows + 1, w, rows, pixels); |
|
|
|
l = 0; |
|
|
|
// Last possible position in the pixels array |
|
int max = scanlineBytes * rows - 1; |
|
|
|
for (int i=0; i<rows; i++) { |
|
|
|
// Beginning of each scanline in the pixels array |
|
l = max - (i+1) * scanlineBytes + 1; |
|
|
|
writePixels(l, scanlineBytes, bitsPerPixel, pixels, |
|
bpixels, padding, numBands, icm); |
|
} |
|
|
|
|
|
} |
|
|
|
} else { |
|
// Process 8 rows at a time so all but the last will have a |
|
// multiple of 8 rows. |
|
int lastRow = minY + h; |
|
|
|
for (int row = minY; row < lastRow; row += 8) { |
|
int rows = Math.min(8, lastRow - row); |
|
|
|
// Get the pixels |
|
Raster src = im.getData(new Rectangle(minX, row, |
|
w, rows)); |
|
src.getPixels(minX, row, w, rows, pixels); |
|
|
|
l=0; |
|
for (int i=0; i<rows; i++) { |
|
|
|
writePixels(l, scanlineBytes, bitsPerPixel, pixels, |
|
bpixels, padding, numBands, icm); |
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
if(isCompressed &&( bitsPerPixel == 4 || bitsPerPixel == 8)){ |
|
// Write the RLE EOF marker and |
|
output.write(0); |
|
output.write(1); |
|
incCompImageSize(2); |
|
// update the file/image Size |
|
imageSize = compImageSize; |
|
fileSize = compImageSize + offset; |
|
writeSize(fileSize, 2); |
|
writeSize(imageSize, 34); |
|
} |
|
|
|
|
|
} |
|
|
|
private void writePixels(int l, int scanlineBytes, int bitsPerPixel, |
|
int pixels[], byte bpixels[], |
|
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++]); |
|
} |
|
|
|
// Partially filled last byte, if any |
|
if (scanlineBytes%8 > 0) { |
|
pixel = 0; |
|
for (int j=0; j<scanlineBytes%8; j++) { |
|
pixel |= (pixels[l++] << (7 - j)); |
|
} |
|
bpixels[k++] = (byte)pixel; |
|
} |
|
output.write(bpixels, 0, (scanlineBytes+7)/8); |
|
|
|
break; |
|
|
|
case 4: |
|
if (isCompressed){ |
|
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; |
|
} |
|
// Put the last pixel of odd-length lines in the 4 MSBs |
|
if ((scanlineBytes%2) == 1) { |
|
pixel = pixels[l] << 4; |
|
bpixels[k++] = (byte)pixel; |
|
} |
|
output.write(bpixels, 0, (scanlineBytes+1)/2); |
|
} |
|
break; |
|
|
|
case 8: |
|
if(isCompressed) { |
|
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++]; |
|
} |
|
output.write(bpixels, 0, scanlineBytes); |
|
} |
|
break; |
|
|
|
case 24: |
|
if (numBands == 3) { |
|
for (int j=0; j<scanlineBytes; j+=3) { |
|
// Since BMP needs BGR format |
|
bpixels[k++] = (byte)(pixels[l+2]); |
|
bpixels[k++] = (byte)(pixels[l+1]); |
|
bpixels[k++] = (byte)(pixels[l]); |
|
l+=3; |
|
} |
|
output.write(bpixels, 0, scanlineBytes); |
|
} else { |
|
// Case where IndexColorModel had > 256 colors. |
|
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++; |
|
} |
|
output.write(bpixels, 0, scanlineBytes*3); |
|
} |
|
break; |
|
|
|
} |
|
|
|
// Write out the padding |
|
if (!(isCompressed && (bitsPerPixel == 8 || bitsPerPixel == 4))){ |
|
for(k=0; k<padding; k++) { |
|
output.write(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 ){ |
|
/// Check if there was an existing Absolute Run |
|
output.write(0); |
|
output.write(absVal); |
|
incCompImageSize(2); |
|
for(int a=0; a<absVal;a++){ |
|
output.write(absBuf[a]); |
|
incCompImageSize(1); |
|
} |
|
if (!isEven(absVal)){ |
|
//Padding |
|
output.write(0); |
|
incCompImageSize(1); |
|
} |
|
} |
|
else if(absVal > -1){ |
|
/// Absolute Encoding for less than 3 |
|
/// treated as regular encoding |
|
/// Do not include the last element since it will |
|
/// be inclued in the next encoding/run |
|
for (int b=0;b<absVal;b++){ |
|
output.write(1); |
|
output.write(absBuf[b]); |
|
incCompImageSize(2); |
|
} |
|
} |
|
absVal = -1; |
|
runCount++; |
|
if (runCount == 256){ |
|
/// Only 255 values permitted |
|
output.write(runCount-1); |
|
output.write(runVal); |
|
incCompImageSize(2); |
|
runCount = 1; |
|
} |
|
} |
|
else { |
|
if (runCount > 1){ |
|
/// If there was an existing run |
|
output.write(runCount); |
|
output.write(runVal); |
|
incCompImageSize(2); |
|
} else if (absVal < 0){ |
|
// First time.. |
|
absBuf[++absVal] = runVal; |
|
absBuf[++absVal] = nextVal; |
|
} else if (absVal < 254){ |
|
// 0-254 only |
|
absBuf[++absVal] = nextVal; |
|
} else { |
|
output.write(0); |
|
output.write(absVal+1); |
|
incCompImageSize(2); |
|
for(int a=0; a<=absVal;a++){ |
|
output.write(absBuf[a]); |
|
incCompImageSize(1); |
|
} |
|
// padding since 255 elts is not even |
|
output.write(0); |
|
incCompImageSize(1); |
|
absVal = -1; |
|
} |
|
runVal = nextVal; |
|
runCount = 1; |
|
} |
|
|
|
if (j == scanlineBytes-1){ // EOF scanline |
|
// Write the run |
|
if (absVal == -1){ |
|
output.write(runCount); |
|
output.write(runVal); |
|
incCompImageSize(2); |
|
runCount = 1; |
|
} |
|
else { |
|
// write the Absolute Run |
|
if(absVal >= 2){ |
|
output.write(0); |
|
output.write(absVal+1); |
|
incCompImageSize(2); |
|
for(int a=0; a<=absVal;a++){ |
|
output.write(absBuf[a]); |
|
incCompImageSize(1); |
|
} |
|
if (!isEven(absVal+1)){ |
|
//Padding |
|
output.write(0); |
|
incCompImageSize(1); |
|
} |
|
|
|
} |
|
else if(absVal > -1){ |
|
for (int b=0;b<=absVal;b++){ |
|
output.write(1); |
|
output.write(absBuf[b]); |
|
incCompImageSize(2); |
|
} |
|
} |
|
} |
|
/// EOF scanline |
|
|
|
output.write(0); |
|
output.write(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 ) { |
|
|
|
//Check if there was an existing Absolute Run |
|
if(absVal >= 4){ |
|
output.write(0); |
|
output.write(absVal - 1); |
|
incCompImageSize(2); |
|
// we need to exclude last 2 elts, similarity of |
|
// which caused to enter this part of the code |
|
for(int a=0; a<absVal-2;a+=2){ |
|
pixel = (absBuf[a] << 4) | absBuf[a+1]; |
|
output.write((byte)pixel); |
|
incCompImageSize(1); |
|
} |
|
// if # of elts is odd - read the last element |
|
if(!(isEven(absVal-1))){ |
|
q = absBuf[absVal-2] << 4| 0; |
|
output.write(q); |
|
incCompImageSize(1); |
|
} |
|
// Padding to word align absolute encoding |
|
if ( !isEven((int)Math.ceil((absVal-1)/2)) ) { |
|
output.write(0); |
|
incCompImageSize(1); |
|
} |
|
} else if (absVal > -1){ |
|
output.write(2); |
|
pixel = (absBuf[0] << 4) | absBuf[1]; |
|
output.write(pixel); |
|
incCompImageSize(2); |
|
} |
|
absVal = -1; |
|
|
|
if (nextVal2 == runVal2){ |
|
// Even runlength |
|
runCount+=2; |
|
if(runCount == 256){ |
|
output.write(runCount-1); |
|
pixel = ( runVal1 << 4) | runVal2; |
|
output.write(pixel); |
|
incCompImageSize(2); |
|
runCount =2; |
|
if(j< scanlineBytes - 1){ |
|
runVal1 = runVal2; |
|
runVal2 = bipixels[++j]; |
|
} else { |
|
output.write(01); |
|
int r = runVal2 << 4 | 0; |
|
output.write(r); |
|
incCompImageSize(2); |
|
runCount = -1;/// Only EOF required now |
|
} |
|
} |
|
} else { |
|
// odd runlength and the run ends here |
|
// runCount wont be > 254 since 256/255 case will |
|
// be taken care of in above code. |
|
runCount++; |
|
pixel = ( runVal1 << 4) | runVal2; |
|
output.write(runCount); |
|
output.write(pixel); |
|
incCompImageSize(2); |
|
runCount = 2; |
|
runVal1 = nextVal2; |
|
// If end of scanline |
|
if (j < scanlineBytes -1){ |
|
runVal2 = bipixels[++j]; |
|
}else { |
|
output.write(01); |
|
int r = nextVal2 << 4 | 0; |
|
output.write(r); |
|
incCompImageSize(2); |
|
runCount = -1;/// Only EOF required now |
|
} |
|
|
|
} |
|
} else{ |
|
// Check for existing run |
|
if (runCount > 2){ |
|
pixel = ( runVal1 << 4) | runVal2; |
|
output.write(runCount); |
|
output.write(pixel); |
|
incCompImageSize(2); |
|
} else if (absVal < 0){ // first time |
|
absBuf[++absVal] = runVal1; |
|
absBuf[++absVal] = runVal2; |
|
absBuf[++absVal] = nextVal1; |
|
absBuf[++absVal] = nextVal2; |
|
} else if (absVal < 253){ // only 255 elements |
|
absBuf[++absVal] = nextVal1; |
|
absBuf[++absVal] = nextVal2; |
|
} else { |
|
output.write(0); |
|
output.write(absVal+1); |
|
incCompImageSize(2); |
|
for(int a=0; a<absVal;a+=2){ |
|
pixel = (absBuf[a] << 4) | absBuf[a+1]; |
|
output.write((byte)pixel); |
|
incCompImageSize(1); |
|
} |
|
// Padding for word align |
|
// since it will fit into 127 bytes |
|
output.write(0); |
|
incCompImageSize(1); |
|
absVal = -1; |
|
} |
|
|
|
runVal1 = nextVal1; |
|
runVal2 = nextVal2; |
|
runCount = 2; |
|
} |
|
// Handle the End of scanline for the last 2 4bits |
|
if (j >= scanlineBytes-2 ) { |
|
if (absVal == -1 && runCount >= 2){ |
|
if (j == scanlineBytes-2){ |
|
if(bipixels[++j] == runVal1){ |
|
runCount++; |
|
pixel = ( runVal1 << 4) | runVal2; |
|
output.write(runCount); |
|
output.write(pixel); |
|
incCompImageSize(2); |
|
} else { |
|
pixel = ( runVal1 << 4) | runVal2; |
|
output.write(runCount); |
|
output.write(pixel); |
|
output.write(01); |
|
pixel = bipixels[j]<<4 |0; |
|
output.write(pixel); |
|
int n = bipixels[j]<<4|0; |
|
incCompImageSize(4); |
|
} |
|
} else { |
|
output.write(runCount); |
|
pixel =( runVal1 << 4) | runVal2 ; |
|
output.write(pixel); |
|
incCompImageSize(2); |
|
} |
|
} else if(absVal > -1){ |
|
if (j == scanlineBytes-2){ |
|
absBuf[++absVal] = bipixels[++j]; |
|
} |
|
if (absVal >=2){ |
|
output.write(0); |
|
output.write(absVal+1); |
|
incCompImageSize(2); |
|
for(int a=0; a<absVal;a+=2){ |
|
pixel = (absBuf[a] << 4) | absBuf[a+1]; |
|
output.write((byte)pixel); |
|
incCompImageSize(1); |
|
} |
|
if(!(isEven(absVal+1))){ |
|
q = absBuf[absVal] << 4|0; |
|
output.write(q); |
|
incCompImageSize(1); |
|
} |
|
|
|
// Padding |
|
if ( !isEven((int)Math.ceil((absVal+1)/2)) ) { |
|
output.write(0); |
|
incCompImageSize(1); |
|
} |
|
|
|
} else { |
|
switch (absVal){ |
|
case 0: |
|
output.write(1); |
|
int n = absBuf[0]<<4 | 0; |
|
output.write(n); |
|
incCompImageSize(2); |
|
break; |
|
case 1: |
|
output.write(2); |
|
pixel = (absBuf[0] << 4) | absBuf[1]; |
|
output.write(pixel); |
|
incCompImageSize(2); |
|
break; |
|
} |
|
} |
|
|
|
} |
|
output.write(0); |
|
output.write(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 writeFileHeader(int fileSize, int offset) throws IOException { |
|
// magic value |
|
output.write('B'); |
|
output.write('M'); |
|
|
|
// File size |
|
writeDWord(fileSize); |
|
|
|
// reserved1 and reserved2 |
|
output.write(0); |
|
output.write(0); |
|
output.write(0); |
|
output.write(0); |
|
|
|
// offset to image data |
|
writeDWord(offset); |
|
} |
|
|
|
|
|
private void writeInfoHeader(int headerSize, int bitsPerPixel) |
|
throws IOException { |
|
|
|
// size of header |
|
writeDWord(headerSize); |
|
|
|
// width |
|
writeDWord(w); |
|
|
|
// height |
|
writeDWord(h); |
|
|
|
// number of planes |
|
writeWord(1); |
|
|
|
// Bits Per Pixel |
|
writeWord(bitsPerPixel); |
|
} |
|
|
|
// Methods for little-endian writing |
|
public void writeWord(int word) throws IOException { |
|
output.write(word & 0xff); |
|
output.write((word & 0xff00) >> 8); |
|
} |
|
|
|
public void writeDWord(int dword) throws IOException { |
|
output.write(dword & 0xff); |
|
output.write((dword & 0xff00) >> 8); |
|
output.write((dword & 0xff0000) >> 16); |
|
output.write((dword & 0xff000000) >> 24); |
|
} |
|
private void writeSize(int dword, int offset) throws IOException { |
|
((SeekableOutputStream)output).seek(offset); |
|
writeDWord(dword); |
|
} |
|
} |
|
|
|
|