modules/graphics/src/main/java/com/sun/javafx/iio/bmp/BMPImageLoaderFactory.java
Print this page
*** 82,91 ****
--- 82,97 ----
final class BitmapInfoHeader {
static final int BIH_SIZE = 40;
static final int BIH4_SIZE = 108;
static final int BIH5_SIZE = 124;
+ static final int BI_RGB = 0;
+ static final int BI_RLE8 = 1;
+ static final int BI_RLE4 = 2;
+ static final int BI_BITFIELDS = 3;
+ static final int BI_JPEG = 4;
+ static final int BI_PNG = 5;
final int biSize;
final int biWidth;
final int biHeight;
final short biPlanes;
*** 118,132 ****
}
}
validate();
}
! void validate() {
! if (biCompression != 0 || biPlanes != 1 || biBitCount != 24) {
! throw new RuntimeException(
! "Unsupported BMP image: " +
! "only 24 bit uncompressed BMP`s is supported");
}
}
}
final class BMPImageLoader extends ImageLoaderImpl {
--- 124,155 ----
}
}
validate();
}
! void validate() throws IOException {
! if (biBitCount < 1
! || biCompression == BI_JPEG || biCompression == BI_PNG) {
! throw new IOException(
! "Unsupported BMP image: "
! + "Embedded JPEG or PNG images are not supported");
! }
!
! if (biCompression == BI_RLE4 && biBitCount != 4) {
! throw new IOException(
! "Invalid BMP image: "
! + "Only 4 bpp images can be RLE4 compressed");
! }
! if (biCompression == BI_RLE8 && biBitCount != 8) {
! throw new IOException(
! "Invalid BMP image: "
! + "Only 8 bpp images can be RLE8 compressed");
! }
! if (biCompression == BI_BITFIELDS) {
! throw new IOException(
! "Unsupported BMP image: "
! + "Bitfields BMP files are not supported");
}
}
}
final class BMPImageLoader extends ImageLoaderImpl {
*** 134,173 ****
static final short BM = 0x4D42;
static final int BFH_SIZE = 14;
final LEInputStream data;
- short bfType; // must be equal to BM
int bfSize;
int bfOffBits;
! int bgra_palette[];
BitmapInfoHeader bih;
BMPImageLoader(InputStream input) throws IOException {
super(BMPDescriptor.theInstance);
data = new LEInputStream(input);
! bfType = data.readShort();
! if (isValid()) {
! readHeader();
}
}
private void readHeader() throws IOException {
bfSize = data.readInt();
data.skipBytes(4); // 32 bits reserved
bfOffBits = data.readInt();
bih = new BitmapInfoHeader(data);
if (bih.biSize + BFH_SIZE != bfOffBits) {
! data.skipBytes(bfOffBits - bih.biSize - BFH_SIZE);
}
}
! private boolean isValid() {
! return bfType == BM;
}
! public void dispose() { }
static void GBRtoRGB(byte data[], int pos, int size) {
for (int sz = size / 3; sz != 0; --sz) {
byte x = data[pos], y = data[pos + 2];
data[pos + 2] = x; data[pos] = y;
--- 157,403 ----
static final short BM = 0x4D42;
static final int BFH_SIZE = 14;
final LEInputStream data;
int bfSize;
int bfOffBits;
! byte bgra_palette[];
! int masks[] = new int[3];
! int maskOffsets[] = new int[3];
BitmapInfoHeader bih;
BMPImageLoader(InputStream input) throws IOException {
super(BMPDescriptor.theInstance);
data = new LEInputStream(input);
! if (data.readShort() != BM) {
! throw new IOException("Invalid BMP file signature");
}
+ readHeader();
}
private void readHeader() throws IOException {
bfSize = data.readInt();
data.skipBytes(4); // 32 bits reserved
bfOffBits = data.readInt();
bih = new BitmapInfoHeader(data);
+ if (bfOffBits < bih.biSize + BFH_SIZE) {
+ throw new IOException("Invalid bitmap bits offset");
+ }
+
+ // assign default masks
+ // TODO: parse BI_BITFIELDS
+ if (bih.biBitCount == 16) {
+ masks[0] = 0x7C00;
+ masks[1] = 0x03E0;
+ masks[2] = 0x001F;
+ maskOffsets[0] = 10;
+ maskOffsets[1] = 5;
+ maskOffsets[2] = 0;
+ } else if (bih.biBitCount == 32) {
+ masks[0] = 0x00FF0000;
+ masks[1] = 0x0000FF00;
+ masks[2] = 0x000000FF;
+ maskOffsets[0] = 24;
+ maskOffsets[1] = 16;
+ maskOffsets[2] = 0;
+ }
+
if (bih.biSize + BFH_SIZE != bfOffBits) {
! int length = bfOffBits - bih.biSize - BFH_SIZE;
! int paletteSize = length / 4;
! bgra_palette = new byte[paletteSize * 4];
! int read = data.in.read(bgra_palette);
! // goto bitmap bits
! if (read < length) {
! data.in.skip(length - read);
! }
! }
! }
!
! @Override
! public void dispose() {
! }
!
!
! private void readRLE(byte[] image, int rowLength, int hght, boolean isRLE4)
! throws IOException
! {
! int imgSize = bih.biSizeImage;
! if (imgSize == 0) {
! imgSize = bfSize - bfOffBits;
! }
! byte imgData[] = new byte[imgSize];
! if (data.in.read(imgData) < imgSize) {
! return;
! }
!
! boolean isBottomUp = bih.biHeight > 0;
! int line = isBottomUp ? hght - 1 : 0;
! int i = 0;
! int x = 0;
! while (i < imgSize) {
! int b1 = getByte(imgData, i++);
! int b2 = getByte(imgData, i++);
! if (b1 == 0) { // absolute
! switch (b2) {
! case 0: // end of line
! x = 0;
! line += isBottomUp ? -1 : 1;
! break;
! case 1: // end of bitmap
! return;
! case 2: // delta
! int deltaX = getByte(imgData, i++);
! int deltaY = getByte(imgData, i++);
! x += deltaX;
! line += deltaY;
! break;
! default:
! int indexData = 0;
! int index;
! for (int p = 0; p < b2; p++) {
! if (isRLE4) {
! if ((p & 1) == 0) {
! indexData = getByte(imgData, i++);
! index = (indexData & 0xf0) >> 4;
! } else {
! index = indexData & 0x0f;
! }
! } else {
! index = getByte(imgData, i++);
! }
! setRGBFromPalette(image, rowLength, x++, line, index);
! }
! if (isRLE4) {
! if ((b2 & 3) == 1 || (b2 & 3) == 2) i++;
! } else {
! if ((b2 & 1) == 1) i++;
! }
! break;
! }
! } else { // encoded
! if (isRLE4) {
! int index1 = (b2 & 0xf0) >> 4;
! int index2 = b2 & 0x0f;
! for (int p = 0; p < b1; p++) {
! setRGBFromPalette(image, rowLength, x++, line,
! (p & 1) == 0 ? index1 : index2);
! }
! } else {
! for (int p = 0; p < b1; p++) {
! setRGBFromPalette(image, rowLength, x++, line, b2);
! }
! }
! }
! }
!
! }
!
! private void setRGBFromPalette(byte[] image, int rowLength, int x, int y, int index) {
! int i = y * rowLength + x * 3;
! image[i] = bgra_palette[index * 4 + 2];
! image[i + 1] = bgra_palette[index * 4 + 1];
! image[i + 2] = bgra_palette[index * 4];
! }
!
! private void readPackedBits(byte[] image, int rowLength, int hght)
! throws IOException
! {
! int pixPerByte = 8 / bih.biBitCount;
! int bytesPerLine = (bih.biWidth + pixPerByte - 1) / pixPerByte;
! int srcStride = (bytesPerLine + 3) & ~3;
! int bitMask = (1 << bih.biBitCount) - 1;
!
! byte lineBuf[] = new byte[srcStride];
! for (int i = 0; i != hght; ++i) {
! int line = bih.biHeight < 0 ? i : hght - i - 1;
! int nRead = data.in.read(lineBuf);
!
! for (int x = 0; x != bih.biWidth; x++) {
! int bitnum = x * bih.biBitCount;
! int element = lineBuf[bitnum / 8];
! int shift = 8 - (bitnum & 7) - bih.biBitCount;
! int index = (element >> shift) & bitMask;
! setRGBFromPalette(image, rowLength, x, line, index);
! }
! if (nRead != srcStride) {
! break;
! }
! }
! }
!
! private static int getWord(byte[] buf, int pos) {
! return buf[pos] & 0xff | buf[pos + 1] << 8 & 0xff00;
! }
!
! private static int getByte(byte[] buf, int pos) {
! return buf[pos] & 0xff;
}
+
+ private void read16Bit(byte[] image, int rowLength, int hght) throws IOException {
+ int bytesPerLine = bih.biWidth * 2;
+ int srcStride = (bytesPerLine + 3) & ~3;
+ byte lineBuf[] = new byte[srcStride];
+ for (int i = 0; i != hght; ++i) {
+ int line = bih.biHeight < 0 ? i : hght - i - 1;
+ int nRead = data.in.read(lineBuf);
+
+ for (int x = 0; x != bih.biWidth; x++) {
+ int element = getWord(lineBuf, x * 2);
+ for (int b = 0; b < 3; b++) {
+ byte c = (byte) ((element & masks[b]) >>> maskOffsets[b]);
+ c = (byte) ((double) c / ((1 << 5) - 1) * 255 + 0.5);
+ image[line * rowLength + x * 3 + b] = c;
+ }
+ }
+ if (nRead != srcStride) {
+ break;
+ }
+ }
+ }
+
+ private void read32Bit(byte[] image, int rowLength, int hght) throws IOException {
+ int bytesPerLine = bih.biWidth * 4;
+ byte lineBuf[] = new byte[bytesPerLine];
+ for (int i = 0; i != hght; ++i) {
+ int line = bih.biHeight < 0 ? i : hght - i - 1;
+ int nRead = data.in.read(lineBuf);
+
+ for (int x = 0; x != bih.biWidth; x++) {
+ int element = lineBuf[x] << 24
+ | lineBuf[x + 1] << 16
+ | lineBuf[x + 2] << 8
+ | lineBuf[x + 3];
+ for (int b = 0; b < 3; b++) {
+ image[line * rowLength + x * 3 + b]
+ = (byte) ((element & masks[b]) >>> maskOffsets[b]);
}
+ }
+ if (nRead != bytesPerLine) {
+ break;
+ }
+ }
+ }
+
+ private void read24Bit(byte[] image, int rowLength, int hght) throws IOException {
+ int bmpStride = (rowLength + 3) & ~3;
! for (int i = 0; i != hght; ++i) {
! int line = bih.biHeight < 0 ? i : hght - i - 1;
! int nRead = data.in.read(image, line * rowLength, rowLength);
! GBRtoRGB(image, line * rowLength, nRead);
!
! if (nRead != rowLength) {
! break;
}
! if (nRead < bmpStride) {
! data.skipBytes(bmpStride - nRead);
! }
! }
! }
static void GBRtoRGB(byte data[], int pos, int size) {
for (int sz = size / 3; sz != 0; --sz) {
byte x = data[pos], y = data[pos + 2];
data[pos + 2] = x; data[pos] = y;
*** 175,224 ****
}
}
public ImageFrame load(int imageIndex, int width, int height,
boolean preserveAspectRatio, boolean smooth) throws IOException
! {
if (0 != imageIndex) {
return null;
}
if ((width > 0 && width != bih.biWidth) ||
! (height > 0 && height != bih.biHeight))
{
! throw new RuntimeException("scaling for BMP is not supported");
}
// Pass image metadata to any listeners.
ImageMetadata imageMetadata = new ImageMetadata(null, Boolean.TRUE,
! null, null, null, null, bih.biWidth, bih.biHeight,
null, null, null);
updateImageMetadata(imageMetadata);
! int bmpStride = (bih.biBitCount*bih.biWidth/8 + 3) & ~3;
! int rowLength = (bih.biBitCount/8)*bih.biWidth;
!
! int hght = Math.abs(bih.biHeight);
!
! byte image[] = new byte[rowLength * hght];
! for (int i = 0; i != hght; ++i) {
! int line = bih.biHeight < 0 ? i : hght-i-1;
! int nRead = data.in.read(image, line * rowLength, rowLength);
! GBRtoRGB(image, line * rowLength, nRead);
! if (nRead != rowLength) {
break;
}
!
! if (nRead < bmpStride) {
! data.skipBytes(bmpStride-nRead);
}
}
return new ImageFrame(ImageStorage.ImageType.RGB, ByteBuffer.wrap(image),
! bih.biWidth, hght, rowLength, null, null);
}
}
public final class BMPImageLoaderFactory implements ImageLoaderFactory {
--- 405,468 ----
}
}
public ImageFrame load(int imageIndex, int width, int height,
boolean preserveAspectRatio, boolean smooth) throws IOException
! {
if (0 != imageIndex) {
return null;
}
+ int hght = Math.abs(bih.biHeight);
+
if ((width > 0 && width != bih.biWidth) ||
! (height > 0 && height != hght))
{
! throw new IOException("scaling for BMP is not supported");
}
// Pass image metadata to any listeners.
ImageMetadata imageMetadata = new ImageMetadata(null, Boolean.TRUE,
! null, null, null, null, bih.biWidth, hght,
null, null, null);
updateImageMetadata(imageMetadata);
! int stride = bih.biWidth * 3;
! byte image[] = new byte[stride * hght];
! switch (bih.biBitCount) {
! case 1:
! readPackedBits(image, stride, hght);
break;
+ case 4:
+ if (bih.biCompression == BitmapInfoHeader.BI_RLE4) {
+ readRLE(image, stride, hght, true);
+ } else {
+ readPackedBits(image, stride, hght);
}
! break;
! case 8:
! if (bih.biCompression == BitmapInfoHeader.BI_RLE8) {
! readRLE(image, stride, hght, false);
! } else {
! readPackedBits(image, stride, hght);
}
+ break;
+ case 16:
+ read16Bit(image, stride, hght);
+ break;
+ case 32:
+ read32Bit(image, stride, hght);
+ break;
+ case 24:
+ read24Bit(image, stride, hght);
+ break;
}
return new ImageFrame(ImageStorage.ImageType.RGB, ByteBuffer.wrap(image),
! bih.biWidth, hght, stride, null, null);
}
}
public final class BMPImageLoaderFactory implements ImageLoaderFactory {
*** 235,240 ****
public ImageLoader createImageLoader(InputStream input) throws IOException {
return new BMPImageLoader(input);
}
}
-
--- 479,483 ----