blob: 80742ae5f1c3dc13bf6ec7e34e7b201ec6704449 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.imaging.formats.ico;
import java.awt.Dimension;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.imaging.ImageFormat;
import org.apache.commons.imaging.ImageFormats;
import org.apache.commons.imaging.ImageInfo;
import org.apache.commons.imaging.ImageParser;
import org.apache.commons.imaging.ImageReadException;
import org.apache.commons.imaging.ImageWriteException;
import org.apache.commons.imaging.Imaging;
import org.apache.commons.imaging.PixelDensity;
import org.apache.commons.imaging.common.BinaryOutputStream;
import org.apache.commons.imaging.common.ByteOrder;
import org.apache.commons.imaging.common.IImageMetadata;
import org.apache.commons.imaging.common.bytesource.ByteSource;
import org.apache.commons.imaging.formats.bmp.BmpImageParser;
import org.apache.commons.imaging.palette.PaletteFactory;
import org.apache.commons.imaging.palette.SimplePalette;
import org.apache.commons.imaging.util.IoUtils;
public class IcoImageParser extends ImageParser {
public IcoImageParser() {
super.setByteOrder(ByteOrder.LITTLE_ENDIAN);
}
@Override
public String getName() {
return "ico-Custom";
}
@Override
public String getDefaultExtension() {
return DEFAULT_EXTENSION;
}
private static final String DEFAULT_EXTENSION = ".ico";
private static final String ACCEPTED_EXTENSIONS[] = { ".ico", ".cur", };
@Override
protected String[] getAcceptedExtensions() {
return ACCEPTED_EXTENSIONS;
}
@Override
protected ImageFormat[] getAcceptedTypes() {
return new ImageFormat[] { ImageFormats.ICO, //
};
}
@Override
public boolean embedICCProfile(final File src, final File dst, final byte profile[]) {
return false;
}
@Override
public IImageMetadata getMetadata(final ByteSource byteSource, final Map<String,Object> params)
throws ImageReadException, IOException {
return null;
}
@Override
public ImageInfo getImageInfo(final ByteSource byteSource, final Map<String,Object> params)
throws ImageReadException, IOException {
return null;
}
@Override
public Dimension getImageSize(final ByteSource byteSource, final Map<String,Object> params)
throws ImageReadException, IOException {
return null;
}
@Override
public byte[] getICCProfileBytes(final ByteSource byteSource, final Map<String,Object> params)
throws ImageReadException, IOException {
return null;
}
private static class FileHeader {
public final int reserved; // Reserved (2 bytes), always 0
public final int iconType; // IconType (2 bytes), if the image is an
// icon it?s 1, for cursors the value is 2.
public final int iconCount; // IconCount (2 bytes), number of icons in
// this file.
public FileHeader(final int reserved, final int iconType,
final int iconCount) {
this.reserved = reserved;
this.iconType = iconType;
this.iconCount = iconCount;
}
public void dump(final PrintWriter pw) {
pw.println("FileHeader");
pw.println("Reserved: " + reserved);
pw.println("IconType: " + iconType);
pw.println("IconCount: " + iconCount);
pw.println();
}
}
private FileHeader readFileHeader(final InputStream is)
throws ImageReadException, IOException {
final int Reserved = read2Bytes("Reserved", is, "Not a Valid ICO File");
final int IconType = read2Bytes("IconType", is, "Not a Valid ICO File");
final int IconCount = read2Bytes("IconCount", is, "Not a Valid ICO File");
if (Reserved != 0) {
throw new ImageReadException("Not a Valid ICO File: reserved is "
+ Reserved);
}
if (IconType != 1 && IconType != 2) {
throw new ImageReadException("Not a Valid ICO File: icon type is "
+ IconType);
}
return new FileHeader(Reserved, IconType, IconCount);
}
private static class IconInfo {
public final byte Width;
public final byte Height;
public final byte ColorCount;
public final byte Reserved;
public final int Planes;
public final int BitCount;
public final int ImageSize;
public final int ImageOffset;
public IconInfo(final byte width, final byte height,
final byte colorCount, final byte reserved, final int planes,
final int bitCount, final int imageSize, final int imageOffset) {
Width = width;
Height = height;
ColorCount = colorCount;
Reserved = reserved;
Planes = planes;
BitCount = bitCount;
ImageSize = imageSize;
ImageOffset = imageOffset;
}
public void dump(final PrintWriter pw) {
pw.println("IconInfo");
pw.println("Width: " + Width);
pw.println("Height: " + Height);
pw.println("ColorCount: " + ColorCount);
pw.println("Reserved: " + Reserved);
pw.println("Planes: " + Planes);
pw.println("BitCount: " + BitCount);
pw.println("ImageSize: " + ImageSize);
pw.println("ImageOffset: " + ImageOffset);
}
}
private IconInfo readIconInfo(final InputStream is) throws IOException {
// Width (1 byte), Width of Icon (1 to 255)
final byte Width = readByte("Width", is, "Not a Valid ICO File");
// Height (1 byte), Height of Icon (1 to 255)
final byte Height = readByte("Height", is, "Not a Valid ICO File");
// ColorCount (1 byte), Number of colors, either
// 0 for 24 bit or higher,
// 2 for monochrome or 16 for 16 color images.
final byte ColorCount = readByte("ColorCount", is, "Not a Valid ICO File");
// Reserved (1 byte), Not used (always 0)
final byte Reserved = readByte("Reserved", is, "Not a Valid ICO File");
// Planes (2 bytes), always 1
final int Planes = read2Bytes("Planes", is, "Not a Valid ICO File");
// BitCount (2 bytes), number of bits per pixel (1 for monchrome,
// 4 for 16 colors, 8 for 256 colors, 24 for true colors,
// 32 for true colors + alpha channel)
final int BitCount = read2Bytes("BitCount", is, "Not a Valid ICO File");
// ImageSize (4 bytes), Length of resource in bytes
final int ImageSize = read4Bytes("ImageSize", is, "Not a Valid ICO File");
// ImageOffset (4 bytes), start of the image in the file
final int ImageOffset = read4Bytes("ImageOffset", is, "Not a Valid ICO File");
return new IconInfo(Width, Height, ColorCount, Reserved, Planes,
BitCount, ImageSize, ImageOffset);
}
private static class BitmapHeader {
public final int Size;
public final int Width;
public final int Height;
public final int Planes;
public final int BitCount;
public final int Compression;
public final int SizeImage;
public final int XPelsPerMeter;
public final int YPelsPerMeter;
public final int ColorsUsed;
public final int ColorsImportant;
public BitmapHeader(final int size, final int width, final int height,
final int planes, final int bitCount, final int compression,
final int sizeImage, final int pelsPerMeter,
final int pelsPerMeter2, final int colorsUsed,
final int colorsImportant) {
Size = size;
Width = width;
Height = height;
Planes = planes;
BitCount = bitCount;
Compression = compression;
SizeImage = sizeImage;
XPelsPerMeter = pelsPerMeter;
YPelsPerMeter = pelsPerMeter2;
ColorsUsed = colorsUsed;
ColorsImportant = colorsImportant;
}
public void dump(final PrintWriter pw) {
pw.println("BitmapHeader");
pw.println("Size: " + Size);
pw.println("Width: " + Width);
pw.println("Height: " + Height);
pw.println("Planes: " + Planes);
pw.println("BitCount: " + BitCount);
pw.println("Compression: " + Compression);
pw.println("SizeImage: " + SizeImage);
pw.println("XPelsPerMeter: " + XPelsPerMeter);
pw.println("YPelsPerMeter: " + YPelsPerMeter);
pw.println("ColorsUsed: " + ColorsUsed);
pw.println("ColorsImportant: " + ColorsImportant);
}
}
private static abstract class IconData {
public final IconInfo iconInfo;
public IconData(final IconInfo iconInfo) {
this.iconInfo = iconInfo;
}
public void dump(final PrintWriter pw) {
iconInfo.dump(pw);
pw.println();
dumpSubclass(pw);
}
protected abstract void dumpSubclass(PrintWriter pw);
public abstract BufferedImage readBufferedImage()
throws ImageReadException;
}
private static class BitmapIconData extends IconData {
public final BitmapHeader header;
public final BufferedImage bufferedImage;
public BitmapIconData(final IconInfo iconInfo,
final BitmapHeader header, final BufferedImage bufferedImage) {
super(iconInfo);
this.header = header;
this.bufferedImage = bufferedImage;
}
@Override
public BufferedImage readBufferedImage() throws ImageReadException {
return bufferedImage;
}
@Override
protected void dumpSubclass(final PrintWriter pw) {
pw.println("BitmapIconData");
header.dump(pw);
pw.println();
}
}
private static class PNGIconData extends IconData {
public final BufferedImage bufferedImage;
public PNGIconData(final IconInfo iconInfo,
final BufferedImage bufferedImage) {
super(iconInfo);
this.bufferedImage = bufferedImage;
}
@Override
public BufferedImage readBufferedImage() {
return bufferedImage;
}
@Override
protected void dumpSubclass(final PrintWriter pw) {
pw.println("PNGIconData");
pw.println();
}
}
private IconData readBitmapIconData(final byte[] iconData, final IconInfo fIconInfo)
throws ImageReadException, IOException {
final ByteArrayInputStream is = new ByteArrayInputStream(iconData);
final int Size = read4Bytes("Size", is, "Not a Valid ICO File"); // Size (4
// bytes),
// size of
// this
// structure
// (always
// 40)
final int Width = read4Bytes("Width", is, "Not a Valid ICO File"); // Width (4
// bytes),
// width of
// the
// image
// (same as
// iconinfo.width)
final int Height = read4Bytes("Height", is, "Not a Valid ICO File"); // Height
// (4
// bytes),
// scanlines
// in the
// color
// map +
// transparent
// map
// (iconinfo.height
// * 2)
final int Planes = read2Bytes("Planes", is, "Not a Valid ICO File"); // Planes
// (2
// bytes),
// always
// 1
final int BitCount = read2Bytes("BitCount", is, "Not a Valid ICO File"); // BitCount
// (2
// bytes),
// 1,4,8,16,24,32
// (see
// iconinfo
// for
// details)
int Compression = read4Bytes("Compression", is, "Not a Valid ICO File"); // Compression
// (4
// bytes),
// we
// don?t
// use
// this
// (0)
final int SizeImage = read4Bytes("SizeImage", is, "Not a Valid ICO File"); // SizeImage
// (4
// bytes),
// we
// don?t
// use
// this
// (0)
final int XPelsPerMeter = read4Bytes("XPelsPerMeter", is,
"Not a Valid ICO File"); // XPelsPerMeter (4 bytes), we don?t
// use this (0)
final int YPelsPerMeter = read4Bytes("YPelsPerMeter", is,
"Not a Valid ICO File"); // YPelsPerMeter (4 bytes), we don?t
// use this (0)
final int ColorsUsed = read4Bytes("ColorsUsed", is, "Not a Valid ICO File"); // ColorsUsed
// (4
// bytes),
// we
// don?t
// use
// this
// (0)
final int ColorsImportant = read4Bytes("ColorsImportant", is,
"Not a Valid ICO File"); // 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");
GreenMask = read4Bytes("GreenMask", is, "Not a Valid ICO File");
BlueMask = read4Bytes("BlueMask", is, "Not a Valid ICO File");
}
final byte[] RestOfFile = readBytes("RestOfFile", is, is.available());
if (Size != 40) {
throw new ImageReadException(
"Not a Valid ICO File: Wrong bitmap header size " + Size);
}
if (Planes != 1) {
throw new ImageReadException(
"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(bitmapSize);
BinaryOutputStream bos = null;
boolean canThrow = false;
try {
bos = new BinaryOutputStream(baos,
ByteOrder.LITTLE_ENDIAN);
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();
canThrow = true;
} finally {
IoUtils.closeQuietly(canThrow, bos);
}
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 t_scanline_size = (Width + 7) / 8;
if ((t_scanline_size % 4) != 0) {
t_scanline_size += 4 - (t_scanline_size % 4); // pad scanline to 4
// byte size.
}
final int tcolor_map_size_bytes = t_scanline_size * (Height / 2);
byte[] transparency_map = null;
try {
transparency_map = this.readBytes("transparency_map",
bmpInputStream, tcolor_map_size_bytes,
"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 (transparency_map != null) {
final int alpha_byte = 0xff & transparency_map[t_scanline_size
* (bmpImage.getHeight() - y - 1) + (x / 8)];
alpha = 0x01 & (alpha_byte >> (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);
}
private IconData readIconData(final byte[] iconData, final IconInfo fIconInfo)
throws ImageReadException, IOException {
final ImageFormat imageFormat = Imaging.guessFormat(iconData);
if (imageFormat.equals(ImageFormats.PNG)) {
final BufferedImage bufferedImage = Imaging.getBufferedImage(iconData);
final PNGIconData pngIconData = new PNGIconData(fIconInfo, bufferedImage);
return pngIconData;
} else {
return readBitmapIconData(iconData, fIconInfo);
}
}
private static class ImageContents {
public final FileHeader fileHeader;
public final IconData iconDatas[];
public ImageContents(final FileHeader fileHeader,
final IconData[] iconDatas) {
super();
this.fileHeader = fileHeader;
this.iconDatas = iconDatas;
}
}
private ImageContents readImage(final ByteSource byteSource)
throws ImageReadException, IOException {
InputStream is = null;
boolean canThrow = false;
try {
is = byteSource.getInputStream();
final FileHeader fileHeader = readFileHeader(is);
final IconInfo fIconInfos[] = new IconInfo[fileHeader.iconCount];
for (int i = 0; i < fileHeader.iconCount; i++) {
fIconInfos[i] = readIconInfo(is);
}
final IconData fIconDatas[] = new IconData[fileHeader.iconCount];
for (int i = 0; i < fileHeader.iconCount; i++) {
final byte[] iconData = byteSource.getBlock(
fIconInfos[i].ImageOffset, fIconInfos[i].ImageSize);
fIconDatas[i] = readIconData(iconData, fIconInfos[i]);
}
final ImageContents ret = new ImageContents(fileHeader, fIconDatas);
canThrow = true;
return ret;
} finally {
IoUtils.closeQuietly(canThrow, is);
}
}
@Override
public boolean dumpImageFile(final PrintWriter pw, final ByteSource byteSource)
throws ImageReadException, IOException {
final ImageContents contents = readImage(byteSource);
contents.fileHeader.dump(pw);
for (final IconData iconData : contents.iconDatas) {
iconData.dump(pw);
}
return true;
}
@Override
public final BufferedImage getBufferedImage(final ByteSource byteSource,
final Map<String,Object> params) throws ImageReadException, IOException {
final ImageContents contents = readImage(byteSource);
final FileHeader fileHeader = contents.fileHeader;
if (fileHeader.iconCount > 0) {
return contents.iconDatas[0].readBufferedImage();
} else {
throw new ImageReadException("No icons in ICO file");
}
}
@Override
public List<BufferedImage> getAllBufferedImages(final ByteSource byteSource)
throws ImageReadException, IOException {
final List<BufferedImage> result = new ArrayList<BufferedImage>();
final ImageContents contents = readImage(byteSource);
final FileHeader fileHeader = contents.fileHeader;
for (int i = 0; i < fileHeader.iconCount; i++) {
final IconData iconData = contents.iconDatas[i];
final BufferedImage image = iconData.readBufferedImage();
result.add(image);
}
return result;
}
// public boolean extractImages(ByteSource byteSource, File dst_dir,
// String dst_root, ImageParser encoder) throws ImageReadException,
// IOException, ImageWriteException
// {
// ImageContents contents = readImage(byteSource);
//
// FileHeader fileHeader = contents.fileHeader;
// for (int i = 0; i < fileHeader.iconCount; i++)
// {
// IconData iconData = contents.iconDatas[i];
//
// BufferedImage image = readBufferedImage(iconData);
//
// int size = Math.max(iconData.iconInfo.Width,
// iconData.iconInfo.Height);
// File file = new File(dst_dir, dst_root + "_" + size + "_"
// + iconData.iconInfo.BitCount
// + encoder.getDefaultExtension());
// encoder.writeImage(image, new FileOutputStream(file), null);
// }
//
// return true;
// }
@Override
public void writeImage(final BufferedImage src, final OutputStream os, Map<String,Object> params)
throws ImageWriteException, IOException {
// make copy of params; we'll clear keys as we consume them.
params = (params == null) ? new HashMap<String,Object>() : new HashMap<String,Object>(params);
// clear format key.
if (params.containsKey(PARAM_KEY_FORMAT)) {
params.remove(PARAM_KEY_FORMAT);
}
final PixelDensity pixelDensity = (PixelDensity) params.remove(PARAM_KEY_PIXEL_DENSITY);
if (params.size() > 0) {
final Object firstKey = params.keySet().iterator().next();
throw new ImageWriteException("Unknown parameter: " + firstKey);
}
final PaletteFactory paletteFactory = new PaletteFactory();
final SimplePalette palette = paletteFactory
.makeExactRgbPaletteSimple(src, 256);
final int bitCount;
final boolean hasTransparency = paletteFactory.hasTransparency(src);
if (palette == null) {
if (hasTransparency) {
bitCount = 32;
} else {
bitCount = 24;
}
} else if (palette.length() <= 2) {
bitCount = 1;
} else if (palette.length() <= 16) {
bitCount = 4;
} else {
bitCount = 8;
}
final BinaryOutputStream bos = new BinaryOutputStream(os, ByteOrder.INTEL);
int scanline_size = (bitCount * src.getWidth() + 7) / 8;
if ((scanline_size % 4) != 0) {
scanline_size += 4 - (scanline_size % 4); // pad scanline to 4 byte
// size.
}
int t_scanline_size = (src.getWidth() + 7) / 8;
if ((t_scanline_size % 4) != 0) {
t_scanline_size += 4 - (t_scanline_size % 4); // pad scanline to 4
// byte size.
}
final int imageSize = 40 + 4 * (bitCount <= 8 ? (1 << bitCount) : 0)
+ src.getHeight() * scanline_size + src.getHeight()
* t_scanline_size;
// ICONDIR
bos.write2Bytes(0); // reserved
bos.write2Bytes(1); // 1=ICO, 2=CUR
bos.write2Bytes(1); // count
// ICONDIRENTRY
int iconDirEntryWidth = src.getWidth();
int iconDirEntryHeight = src.getHeight();
if (iconDirEntryWidth > 255 || iconDirEntryHeight > 255) {
iconDirEntryWidth = 0;
iconDirEntryHeight = 0;
}
bos.write(iconDirEntryWidth);
bos.write(iconDirEntryHeight);
bos.write((bitCount >= 8) ? 0 : (1 << bitCount));
bos.write(0); // reserved
bos.write2Bytes(1); // color planes
bos.write2Bytes(bitCount);
bos.write4Bytes(imageSize);
bos.write4Bytes(22); // image offset
// BITMAPINFOHEADER
bos.write4Bytes(40); // size
bos.write4Bytes(src.getWidth());
bos.write4Bytes(2 * src.getHeight());
bos.write2Bytes(1); // planes
bos.write2Bytes(bitCount);
bos.write4Bytes(0); // compression
bos.write4Bytes(0); // image size
bos.write4Bytes(pixelDensity == null ? 0 : (int)Math.round(pixelDensity.horizontalDensityMetres())); // x pixels per meter
bos.write4Bytes(pixelDensity == null ? 0 : (int)Math.round(pixelDensity.horizontalDensityMetres())); // y pixels per meter
bos.write4Bytes(0); // colors used, 0 = (1 << bitCount) (ignored)
bos.write4Bytes(0); // colors important
if (palette != null) {
for (int i = 0; i < (1 << bitCount); i++) {
if (i < palette.length()) {
final int argb = palette.getEntry(i);
bos.write(0xff & argb);
bos.write(0xff & (argb >> 8));
bos.write(0xff & (argb >> 16));
bos.write(0);
} else {
bos.write(0);
bos.write(0);
bos.write(0);
bos.write(0);
}
}
}
int bit_cache = 0;
int bits_in_cache = 0;
final int row_padding = scanline_size - (bitCount * src.getWidth() + 7) / 8;
for (int y = src.getHeight() - 1; y >= 0; y--) {
for (int x = 0; x < src.getWidth(); x++) {
final int argb = src.getRGB(x, y);
if (bitCount < 8) {
final int rgb = 0xffffff & argb;
final int index = palette.getPaletteIndex(rgb);
bit_cache <<= bitCount;
bit_cache |= index;
bits_in_cache += bitCount;
if (bits_in_cache >= 8) {
bos.write(0xff & bit_cache);
bit_cache = 0;
bits_in_cache = 0;
}
} else if (bitCount == 8) {
final int rgb = 0xffffff & argb;
final int index = palette.getPaletteIndex(rgb);
bos.write(0xff & index);
} else if (bitCount == 24) {
bos.write(0xff & argb);
bos.write(0xff & (argb >> 8));
bos.write(0xff & (argb >> 16));
} else if (bitCount == 32) {
bos.write(0xff & argb);
bos.write(0xff & (argb >> 8));
bos.write(0xff & (argb >> 16));
bos.write(0xff & (argb >> 24));
}
}
if (bits_in_cache > 0) {
bit_cache <<= (8 - bits_in_cache);
bos.write(0xff & bit_cache);
bit_cache = 0;
bits_in_cache = 0;
}
for (int x = 0; x < row_padding; x++) {
bos.write(0);
}
}
final int t_row_padding = t_scanline_size - (src.getWidth() + 7) / 8;
for (int y = src.getHeight() - 1; y >= 0; y--) {
for (int x = 0; x < src.getWidth(); x++) {
final int argb = src.getRGB(x, y);
final int alpha = 0xff & (argb >> 24);
bit_cache <<= 1;
if (alpha == 0) {
bit_cache |= 1;
}
bits_in_cache++;
if (bits_in_cache >= 8) {
bos.write(0xff & bit_cache);
bit_cache = 0;
bits_in_cache = 0;
}
}
if (bits_in_cache > 0) {
bit_cache <<= (8 - bits_in_cache);
bos.write(0xff & bit_cache);
bit_cache = 0;
bits_in_cache = 0;
}
for (int x = 0; x < t_row_padding; x++) {
bos.write(0);
}
}
}
/**
* Extracts embedded XML metadata as XML string.
* <p>
*
* @param byteSource
* File containing image data.
* @param params
* Map of optional parameters, defined in ImagingConstants.
* @return Xmp Xml as String, if present. Otherwise, returns null.
*/
@Override
public String getXmpXml(final ByteSource byteSource, final Map<String,Object> params)
throws ImageReadException, IOException {
return null;
}
}