/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* The Java Image IO plugin writer for encoding a binary RenderedImage into
* a BMP format.
*
* The encoding process may clip, subsample using the parameters
* specified in the <code>ImageWriteParam</code>.
*
* @see javax.imageio.plugins.bmp.BMPImageWriteParam
*/
/** The output stream to write into */
private int version;
private int compressionType;
private boolean isTopDown;
private int w, h;
private int[] bitMasks;
private int[] bitPos;
private byte[] bpixels;
private short[] spixels;
private int[] ipixels;
/** Constructs <code>BMPImageWriter</code> based on the provided
* <code>ImageWriterSpi</code>.
*/
super(originator);
}
if (!(output instanceof ImageOutputStream))
} else
}
return new BMPImageWriteParam();
}
return null;
}
}
return meta;
}
return null;
}
return null;
}
public boolean canWriteRasters() {
return true;
}
}
}
// Default is using 24 bits per pixel.
int bitsPerPixel = 24;
boolean isPalette = false;
int paletteEntries = 0;
compImageSize = 0;
if (writeRaster) {
if (sourceRegion == null)
else
} else {
if (sourceRegion == null)
sourceRegion = rect;
else
}
if (imageMetadata != null
&& imageMetadata instanceof BMPMetadata)
{
} else {
param);
}
if (sourceRegion.isEmpty())
// cache the data type;
// Raw data can only handle bytes, everything greater must be ASCII.
boolean noSubband = true;
if (sourceBands != null) {
colorModel = null;
noSubband = false;
} 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) {
if (sampleModel instanceof BandedSampleModel) {
// for images with BandedSampleModel we can not work
// with raster directly and must use writePixels()
bgrOrder = false;
} else {
// we can work with raster directly only in case of
// BGR component order.
// In any other case we must use writePixels()
}
}
} else {
if (sampleModel instanceof SinglePixelPackedSampleModel) {
// BugId 4892214: we can not work with raster directly
// if image have different color order than RGB.
// We should use writePixels() for such images.
}
}
}
if (bandOffsets == null) {
// we will use getPixels() to extract pixel data for writePixels()
// Please note that getPixels() provides rgb bands order.
bandOffsets = new int[numBands];
for (int i = 0; i < numBands; i++)
bandOffsets[i] = i;
}
noTransform &= bgrOrder;
//XXX: check more
// Number of bytes that a scanline for the image written out will have.
int destScanlineBytes = w * numBands;
switch(bmpParam.getCompressionMode()) {
case ImageWriteParam.MODE_EXPLICIT:
break;
break;
case ImageWriteParam.MODE_DEFAULT:
break;
default:
// ImageWriteParam.MODE_DISABLED:
}
throw new IOException("Image can not be encoded with compression type "
}
// we should use 32bpp images in case of BI_BITFIELD
// compression to avoid color conversion artefacts
bitsPerPixel = 32;
// Setting this flag to false ensures that generic
// writePixels() will be used to store image data
noTransform = false;
}
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) {
/* NB: canEncodeImage() ensures we have image of
* either USHORT_565_RGB or USHORT_555_RGB type here.
* Technically, it should work for other direct color
* model types but it might be non compatible with win98
* and friends.
*/
if (colorModel instanceof DirectColorModel) {
} else {
// it is unlikely, but if it happens, we should throw
// an exception related to unsupported image format
throw new IOException("Image can not be encoded with " +
"compression type " +
}
}
if (!noTransform) {
// prepare info for writePixels procedure
bitMasks = new int[3];
bitPos = new int[3];
}
if (colorModel instanceof IndexColorModel) {
}
} else { // handle BI_RGB compression
if (colorModel instanceof IndexColorModel) {
isPalette = true;
if (paletteEntries <= 2) {
bitsPerPixel = 1;
} else if (paletteEntries <= 16) {
bitsPerPixel = 4;
} 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];
}
} else {
// Grey scale images
if (numBands == 1) {
isPalette = true;
paletteEntries = 256;
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 &&
{
/* NB: the actual pixel size can be smaller than
* size of used DataBuffer element.
* For example: in case of TYPE_INT_RGB actual pixel
* size is 24 bits, but size of DataBuffere element
* is 32 bits
*/
bitsPerPixel = 0;
for (int size : sample_sizes) {
bitsPerPixel += size;
}
noTransform = false;
}
}
}
}
}
// 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;
// Calculate padding for each scanline
if (padding != 0) {
}
// FileHeader is 14 bytes, BitmapHeader is 40 bytes,
// add palette size and that is where the data will begin
headerSize = 40;
/* According to MSDN description, the top-down image layout
* is allowed only if compression type is BI_RGB or BI_BITFIELDS.
* Images with any other compression type must be wrote in the
* bottom-up layout.
*/
{
} else {
isTopDown = false;
}
// compression
// imageSize
// xPelsPerMeter
// yPelsPerMeter
// Colors Used
// Colors Important
// palette
if (isPalette == true) {
// write palette
// write masks for red, green and blue components.
for (int i=0; i<3; i++) {
}
} else {
for (int i=0; i<paletteEntries; i++) {
}
}
}
// Writing of actual image data
int scanlineBytes = w * numBands;
// Buffer for up to 8 rows of pixels
// Also create a buffer to hold one line of the data
// to be written to the file, so we can use array writes.
bpixels = new byte[destScanlineBytes];
int l;
// prepare embedded buffer
embedded_stream = new ByteArrayOutputStream();
if (abortRequested()) {
} else {
}
return;
}
if (bandOffsets[i] > maxBandOffset)
maxBandOffset = bandOffsets[i];
if (noTransform && noSubband) {
}
for (int i = 0; i < h; i++) {
if (abortRequested()) {
break;
}
if (!isTopDown)
// Get the pixels
1);
if (!writeRaster)
if (noTransform && noSubband) {
int pos = 0;
if (sm instanceof ComponentSampleModel) {
}
}
} else if (sm instanceof MultiPixelPackedSampleModel) {
} else if (sm instanceof SinglePixelPackedSampleModel) {
}
switch(dataType) {
case DataBuffer.TYPE_BYTE:
byte[] bdata =
break;
case DataBuffer.TYPE_SHORT:
short[] sdata =
break;
case DataBuffer.TYPE_USHORT:
short[] usdata =
break;
case DataBuffer.TYPE_INT:
int[] idata =
break;
}
for(int k=0; k<padding; k++) {
}
bpixels = new byte[scanlineBytes];
for (int h=0; h<scanlineBytes; h++) {
}
//byte[] bdata =
// ((DataBufferByte)src.getDataBuffer()).getData();
//System.out.println("bdata.length="+bdata.length);
//System.arraycopy(bdata, pos, bpixels, 0, scanlineBytes);
bpixels = new byte[scanlineBytes];
for (int h=0; h<scanlineBytes; h++) {
}
}
} else {
for (int j = 0, k = 0, n=0; j < w;
{
for (int m = 0; m < numBands; m++) {
// pixel data is provided here in RGB order
}
}
}
}
processImageProgress(100.0f * (((float)i) / ((float)h)));
}
// Write the RLE EOF marker and
incCompImageSize(2);
}
if (abortRequested()) {
} else {
}
}
int pixels[],
int pixel = 0;
int k = 0;
switch (bitsPerPixel) {
case 1:
(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
pixel = 0;
}
}
break;
case 4:
byte[] bipixels = new byte[scanlineBytes];
for (int h=0; h<scanlineBytes; h++) {
}
}else {
}
// Put the last pixel of odd-length lines in the 4 MSBs
}
}
break;
case 8:
for (int h=0; h<scanlineBytes; h++) {
}
}else {
for (int j=0; j<scanlineBytes; j++) {
}
}
break;
case 16:
/*
* We expect that pixel data comes in RGB order.
* We will assemble short pixel taking into account
* the compression type:
*
* BI_RGB - the RGB order should be maintained.
* BI_BITFIELDS - use bitPos array that was built
* according to bitfields masks.
*/
spixels[m] = 0;
/*
* please note that despite other cases,
* the 16bpp BI_RGB requires the RGB data order
*/
spixels[m] = (short)
j += 3;
} else {
for(int i = 0 ; i < numBands; i++, j++) {
spixels[m] |=
}
}
}
break;
case 24:
if (numBands == 3) {
// Since BMP needs BGR format
l+=3;
}
} else {
// Case where IndexColorModel had > 256 colors.
byte r[] = new byte[entries];
byte g[] = new byte[entries];
byte b[] = new byte[entries];
int index;
for (int j=0; j<scanlineBytes; j++) {
l++;
}
}
break;
case 32:
if (numBands == 3) {
/*
* We expect that pixel data comes in RGB order.
* We will assemble int pixel taking into account
* the compression type.
*
* BI_RGB - the BGR order should be used.
* BI_BITFIELDS - use bitPos array that was built
* according to bitfields masks.
*/
ipixels[m] = 0;
ipixels[m] =
((0xff & pixels[j ]) );
j += 3;
} else {
for(int i = 0 ; i < numBands; i++, j++) {
ipixels[m] |=
}
}
}
} else {
// We have two possibilities here:
// 1. we are writing the indexed image with bitfields
// compression (this covers also the case of BYTE_BINARY)
// => use icm to get actual RGB color values.
// 2. we are writing the gray-scaled image with BI_BITFIELDS
// compression
// => just replicate the level of gray to color components.
for (int j = 0; j < scanlineBytes; j++) {
} else {
ipixels[j] =
}
}
}
break;
}
// Write out the padding
{
for(k=0; k<padding; k++) {
}
}
}
throws IOException{
byte[] absBuf = new byte[256];
while (j < scanlineBytes-1) {
if(absVal >= 3 ){
/// Check if there was an existing Absolute Run
incCompImageSize(2);
for(int a=0; a<absVal;a++){
incCompImageSize(1);
}
//Padding
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
for (int b=0;b<absVal;b++){
incCompImageSize(2);
}
}
absVal = -1;
runCount++;
if (runCount == 256){
/// Only 255 values permitted
incCompImageSize(2);
runCount = 1;
}
}
else {
if (runCount > 1){
/// If there was an existing run
incCompImageSize(2);
} else if (absVal < 0){
// First time..
} else if (absVal < 254){
// 0-254 only
} else {
incCompImageSize(2);
for(int a=0; a<=absVal;a++){
incCompImageSize(1);
}
// padding since 255 elts is not even
incCompImageSize(1);
absVal = -1;
}
runCount = 1;
}
// Write the run
if (absVal == -1){
incCompImageSize(2);
runCount = 1;
}
else {
// write the Absolute Run
if(absVal >= 2){
incCompImageSize(2);
for(int a=0; a<=absVal;a++){
incCompImageSize(1);
}
//Padding
incCompImageSize(1);
}
}
else if(absVal > -1){
for (int b=0;b<=absVal;b++){
incCompImageSize(2);
}
}
}
/// EOF scanline
incCompImageSize(2);
}
}
}
throws IOException {
byte[] absBuf = new byte[256];
while (j < scanlineBytes-2){
//Check if there was an existing Absolute Run
if(absVal >= 4){
incCompImageSize(2);
// we need to exclude last 2 elts, similarity of
// which caused to enter this part of the code
incCompImageSize(1);
}
// if # of elts is odd - read the last element
incCompImageSize(1);
}
// Padding to word align absolute encoding
incCompImageSize(1);
}
} else if (absVal > -1){
incCompImageSize(2);
}
absVal = -1;
// Even runlength
runCount+=2;
if(runCount == 256){
incCompImageSize(2);
runCount =2;
if(j< scanlineBytes - 1){
} else {
incCompImageSize(2);
}
}
} 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++;
incCompImageSize(2);
runCount = 2;
// If end of scanline
if (j < scanlineBytes -1){
}else {
incCompImageSize(2);
}
}
} else{
// Check for existing run
if (runCount > 2){
incCompImageSize(2);
} else {
incCompImageSize(2);
incCompImageSize(1);
}
// Padding for word align
// since it will fit into 127 bytes
incCompImageSize(1);
absVal = -1;
}
runCount = 2;
}
// Handle the End of scanline for the last 2 4bits
if (j >= scanlineBytes-2 ) {
if (j == scanlineBytes-2){
runCount++;
incCompImageSize(2);
} else {
incCompImageSize(4);
}
} else {
incCompImageSize(2);
}
} else if(absVal > -1){
if (j == scanlineBytes-2){
}
if (absVal >=2){
incCompImageSize(2);
incCompImageSize(1);
}
incCompImageSize(1);
}
// Padding
incCompImageSize(1);
}
} else {
switch (absVal){
case 0:
incCompImageSize(2);
break;
case 1:
incCompImageSize(2);
break;
}
}
}
incCompImageSize(2);
}
}
}
}
}
// magic value
// File size
// reserved1 and reserved2
// offset to image data
}
int bitsPerPixel) throws IOException {
// size of header
// width
// height
// number of planes
// Bits Per Pixel
}
}
public void reset() {
super.reset();
}
return i;
return 0;
}
if (embedded_stream == null) {
throw new RuntimeException("No stream for writing embedded image!");
}
}
});
}
});
//param.setDestinationBands(bmpParam.getDestinationBands());
} else
}
int count = 0;
count++;
num >>>= 1;
}
return count;
}
}
}
}
}
}
}
}
}
/*
* Returns preferred compression type for given image.
* The default compression type is BI_RGB, but some image types can't be
* encodeed with using default compression without cahnge color resolution.
* For example, TYPE_USHORT_565_RGB may be encodeed only by using BI_BITFIELDS
* compression type.
*
* NB: we probably need to extend this method if we encounter other image
* types which can not be encoded with BI_RGB compression type.
*/
return getPreferredCompressionType(imageType);
}
return BI_BITFIELDS;
}
return BI_RGB;
}
/*
* Check whether we can encode image of given type using compression method in question.
*
* For example, TYPE_USHORT_565_RGB can be encodeed with BI_BITFIELDS compression only.
*
* NB: method should be extended if other cases when we can not encode
* with given compression will be discovered.
*/
}
return false;
}
// only 4bpp images can be encoded as BI_RLE4
return false;
}
// only 8bpp images can be encoded as BI_RLE8
return false;
}
if (bpp == 16) {
/*
* Technically we expect that we may be able to
* encode only some of SinglePixelPackedSampleModel
* images here.
*
* In addition we should take into account following:
*
* 1. BI_RGB case, according to the MSDN description:
*
* The bitmap has a maximum of 2^16 colors. If the
* biCompression member of the BITMAPINFOHEADER is BI_RGB,
* the bmiColors member of BITMAPINFO is NULL. Each WORD
* in the bitmap array represents a single pixel. The
* relative intensities of red, green, and blue are
* represented with five bits for each color component.
*
* 2. BI_BITFIELDS case, according ot the MSDN description:
*
* Windows 95/98/Me: When the biCompression member is
* BI_BITFIELDS, the system supports only the following
* 16bpp color masks: A 5-5-5 16-bit image, where the blue
* mask is 0x001F, the green mask is 0x03E0, and the red mask
* is 0x7C00; and a 5-6-5 16-bit image, where the blue mask
* is 0x001F, the green mask is 0x07E0, and the red mask is
* 0xF800.
*/
boolean canUseRGB = false;
boolean canUseBITFIELDS = false;
if (sm instanceof SinglePixelPackedSampleModel) {
int[] sizes =
canUseRGB = true;
canUseBITFIELDS = true;
}
}
}
return true;
}
byte[] r, byte[]g, byte[] b, byte[]a) {
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;
}
}
}