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 ----