in src/main/java/org/apache/commons/imaging/formats/ico/IcoImageParser.java [331:527]
private IconData readBitmapIconData(final byte[] iconData, final IconInfo fIconInfo)
throws ImagingException, IOException {
final ByteArrayInputStream is = new ByteArrayInputStream(iconData);
final int size = read4Bytes("size", is, "Not a Valid ICO File", getByteOrder()); // Size (4
// bytes),
// size of
// this
// structure
// (always
// 40)
final int width = read4Bytes("width", is, "Not a Valid ICO File", getByteOrder()); // Width (4
// bytes),
// width of
// the
// image
// (same as
// iconinfo.width)
final int height = read4Bytes("height", is, "Not a Valid ICO File", getByteOrder()); // Height
// (4
// bytes),
// scanlines
// in the
// color
// map +
// transparent
// map
// (iconinfo.height
// * 2)
final int planes = read2Bytes("planes", is, "Not a Valid ICO File", getByteOrder()); // Planes
// (2
// bytes),
// always
// 1
final int bitCount = read2Bytes("bitCount", is, "Not a Valid ICO File", getByteOrder()); // BitCount
// (2
// bytes),
// 1,4,8,16,24,32
// (see
// iconinfo
// for
// details)
int compression = read4Bytes("compression", is, "Not a Valid ICO File", getByteOrder()); // Compression
// (4
// bytes),
// we
// don?t
// use
// this
// (0)
final int sizeImage = read4Bytes("sizeImage", is, "Not a Valid ICO File", getByteOrder()); // SizeImage
// (4
// bytes),
// we
// don?t
// use
// this
// (0)
final int xPelsPerMeter = read4Bytes("xPelsPerMeter", is,
"Not a Valid ICO File", getByteOrder()); // XPelsPerMeter (4 bytes), we don?t
// use this (0)
final int yPelsPerMeter = read4Bytes("yPelsPerMeter", is,
"Not a Valid ICO File", getByteOrder()); // YPelsPerMeter (4 bytes), we don?t
// use this (0)
final int colorsUsed = read4Bytes("colorsUsed", is, "Not a Valid ICO File", getByteOrder()); // ColorsUsed
// (4
// bytes),
// we
// don?t
// use
// this
// (0)
final int colorsImportant = read4Bytes("ColorsImportant", is,
"Not a Valid ICO File", getByteOrder()); // ColorsImportant (4 bytes), we don?t
// use this (0)
int redMask = 0;
int greenMask = 0;
int blueMask = 0;
int alphaMask = 0;
if (compression == 3) {
redMask = read4Bytes("redMask", is, "Not a Valid ICO File", getByteOrder());
greenMask = read4Bytes("greenMask", is, "Not a Valid ICO File", getByteOrder());
blueMask = read4Bytes("blueMask", is, "Not a Valid ICO File", getByteOrder());
}
final byte[] restOfFile = readBytes("RestOfFile", is, is.available());
if (size != 40) {
throw new ImagingException("Not a Valid ICO File: Wrong bitmap header size " + size);
}
if (planes != 1) {
throw new ImagingException("Not a Valid ICO File: Planes can't be " + planes);
}
if (compression == 0 && bitCount == 32) {
// 32 BPP RGB icons need an alpha channel, but BMP files don't have
// one unless BI_BITFIELDS is used...
compression = 3;
redMask = 0x00ff0000;
greenMask = 0x0000ff00;
blueMask = 0x000000ff;
alphaMask = 0xff000000;
}
final BitmapHeader header = new BitmapHeader(size, width, height, planes,
bitCount, compression, sizeImage, xPelsPerMeter, yPelsPerMeter,
colorsUsed, colorsImportant);
final int bitmapPixelsOffset = 14 + 56 + 4 * ((colorsUsed == 0 && bitCount <= 8) ? (1 << bitCount)
: colorsUsed);
final int bitmapSize = 14 + 56 + restOfFile.length;
final ByteArrayOutputStream baos = new ByteArrayOutputStream(Allocator.checkByteArray(bitmapSize));
try (BinaryOutputStream bos = BinaryOutputStream.littleEndian(baos)) {
bos.write('B');
bos.write('M');
bos.write4Bytes(bitmapSize);
bos.write4Bytes(0);
bos.write4Bytes(bitmapPixelsOffset);
bos.write4Bytes(56);
bos.write4Bytes(width);
bos.write4Bytes(height / 2);
bos.write2Bytes(planes);
bos.write2Bytes(bitCount);
bos.write4Bytes(compression);
bos.write4Bytes(sizeImage);
bos.write4Bytes(xPelsPerMeter);
bos.write4Bytes(yPelsPerMeter);
bos.write4Bytes(colorsUsed);
bos.write4Bytes(colorsImportant);
bos.write4Bytes(redMask);
bos.write4Bytes(greenMask);
bos.write4Bytes(blueMask);
bos.write4Bytes(alphaMask);
bos.write(restOfFile);
bos.flush();
}
final ByteArrayInputStream bmpInputStream = new ByteArrayInputStream(baos.toByteArray());
final BufferedImage bmpImage = new BmpImageParser().getBufferedImage(bmpInputStream, null);
// Transparency map is optional with 32 BPP icons, because they already
// have
// an alpha channel, and Windows only uses the transparency map when it
// has to
// display the icon on a < 32 BPP screen. But it's still used instead of
// alpha
// if the image would be completely transparent with alpha...
int tScanlineSize = (width + 7) / 8;
if ((tScanlineSize % 4) != 0) {
tScanlineSize += 4 - (tScanlineSize % 4); // pad scanline to 4
// byte size.
}
final int colorMapSizeBytes = tScanlineSize * (height / 2);
byte[] transparencyMap = null;
try {
transparencyMap = readBytes("transparencyMap",
bmpInputStream, colorMapSizeBytes,
"Not a Valid ICO File");
} catch (final IOException ioEx) {
if (bitCount != 32) {
throw ioEx;
}
}
boolean allAlphasZero = true;
if (bitCount == 32) {
for (int y = 0; allAlphasZero && y < bmpImage.getHeight(); y++) {
for (int x = 0; x < bmpImage.getWidth(); x++) {
if ((bmpImage.getRGB(x, y) & 0xff000000) != 0) {
allAlphasZero = false;
break;
}
}
}
}
BufferedImage resultImage;
if (allAlphasZero) {
resultImage = new BufferedImage(bmpImage.getWidth(),
bmpImage.getHeight(), BufferedImage.TYPE_INT_ARGB);
for (int y = 0; y < resultImage.getHeight(); y++) {
for (int x = 0; x < resultImage.getWidth(); x++) {
int alpha = 0xff;
if (transparencyMap != null) {
final int alphaByte = 0xff & transparencyMap[tScanlineSize
* (bmpImage.getHeight() - y - 1) + (x / 8)];
alpha = 0x01 & (alphaByte >> (7 - (x % 8)));
alpha = (alpha == 0) ? 0xff : 0x00;
}
resultImage.setRGB(x, y, (alpha << 24)
| (0xffffff & bmpImage.getRGB(x, y)));
}
}
} else {
resultImage = bmpImage;
}
return new BitmapIconData(fIconInfo, header, resultImage);
}