final boolean hasAlpha = new PaletteFactory().hasTransparency(src);
final int maxColors = hasAlpha ? 255 : 256;
Palette palette2 = new PaletteFactory().makeExactRgbPaletteSimple(src, maxColors);
// int palette[] = new PaletteFactory().makePaletteSimple(src, 256);
// Map palette_map = paletteToMap(palette);
if (palette2 == null) {
palette2 = new PaletteFactory().makeQuantizedRgbPalette(src, maxColors);
if (verbose) {
System.out.println("quantizing");
}
} else if (verbose) {
System.out.println("exact palette");
}
if (palette2 == null) {
throw new ImageWriteException("Gif: can't write images with more than 256 colors");
}
final int paletteSize = palette2.length() + (hasAlpha ? 1 : 0);
final BinaryOutputStream bos = new BinaryOutputStream(os, ByteOrder.LITTLE_ENDIAN);
// write Header
os.write(0x47); // G magic numbers
os.write(0x49); // I
os.write(0x46); // F
os.write(0x38); // 8 version magic numbers
os.write(0x39); // 9
os.write(0x61); // a
// Logical Screen Descriptor.
bos.write2Bytes(width);
bos.write2Bytes(height);
final int colorTableScaleLessOne = (paletteSize > 128) ? 7
: (paletteSize > 64) ? 6 : (paletteSize > 32) ? 5
: (paletteSize > 16) ? 4 : (paletteSize > 8) ? 3
: (paletteSize > 4) ? 2
: (paletteSize > 2) ? 1 : 0;
final int colorTableSizeInFormat = 1 << (colorTableScaleLessOne + 1);
{
final byte colorResolution = (byte) colorTableScaleLessOne; // TODO:
final boolean globalColorTableFlag = false;
final boolean sortFlag = false;
final int globalColorTableFlagMask = 1 << 7;
final int sortFlagMask = 8;
final int sizeOfGlobalColorTable = 0;
final int packedFields = ((globalColorTableFlag ? globalColorTableFlagMask
: 0)
| (sortFlag ? sortFlagMask : 0)
| ((7 & colorResolution) << 4) | (7 & sizeOfGlobalColorTable));
bos.write(packedFields); // one byte
}
{
final byte backgroundColorIndex = 0;
bos.write(backgroundColorIndex);
}
{
final byte pixelAspectRatio = 0;
bos.write(pixelAspectRatio);
}
//{
// write Global Color Table.
//}
{ // ALWAYS write GraphicControlExtension
bos.write(EXTENSION_CODE);
bos.write((byte) 0xf9);
// bos.write(0xff & (kGraphicControlExtension >> 8));
// bos.write(0xff & (kGraphicControlExtension >> 0));
bos.write((byte) 4); // block size;
final int packedFields = hasAlpha ? 1 : 0; // transparency flag
bos.write((byte) packedFields);
bos.write((byte) 0); // Delay Time
bos.write((byte) 0); // Delay Time
bos.write((byte) (hasAlpha ? palette2.length() : 0)); // Transparent
// Color
// Index
bos.write((byte) 0); // terminator
}
if (null != xmpXml) {
bos.write(EXTENSION_CODE);
bos.write(APPLICATION_EXTENSION_LABEL);
bos.write(XMP_APPLICATION_ID_AND_AUTH_CODE.length); // 0x0B
bos.write(XMP_APPLICATION_ID_AND_AUTH_CODE);
final byte[] xmpXmlBytes = xmpXml.getBytes("utf-8");
bos.write(xmpXmlBytes);
// write "magic trailer"
for (int magic = 0; magic <= 0xff; magic++) {
bos.write(0xff - magic);
}
bos.write((byte) 0); // terminator
}
{ // Image Descriptor.
bos.write(IMAGE_SEPARATOR);
bos.write2Bytes(0); // Image Left Position
bos.write2Bytes(0); // Image Top Position
bos.write2Bytes(width); // Image Width
bos.write2Bytes(height); // Image Height
{
final boolean localColorTableFlag = true;
// boolean LocalColorTableFlag = false;
final boolean interlaceFlag = false;
final boolean sortFlag = false;
final int sizeOfLocalColorTable = colorTableScaleLessOne;
// int SizeOfLocalColorTable = 0;
final int packedFields;
if (localColorTableFlag) {
packedFields = (LOCAL_COLOR_TABLE_FLAG_MASK
| (interlaceFlag ? INTERLACE_FLAG_MASK : 0)
| (sortFlag ? SORT_FLAG_MASK : 0)
| (7 & sizeOfLocalColorTable));
} else {
packedFields = (0
| (interlaceFlag ? INTERLACE_FLAG_MASK : 0)
| (sortFlag ? SORT_FLAG_MASK : 0)
| (7 & sizeOfLocalColorTable));
}
bos.write(packedFields); // one byte
}
}
{ // write Local Color Table.
for (int i = 0; i < colorTableSizeInFormat; i++) {
if (i < palette2.length()) {
final int rgb = palette2.getEntry(i);
final int red = 0xff & (rgb >> 16);
final int green = 0xff & (rgb >> 8);
final int blue = 0xff & (rgb >> 0);
bos.write(red);
bos.write(green);
bos.write(blue);
} else {
bos.write(0);
bos.write(0);
bos.write(0);
}
}
}
{ // get Image Data.
// int image_data_total = 0;
int lzwMinimumCodeSize = colorTableScaleLessOne + 1;
// LZWMinimumCodeSize = Math.max(8, LZWMinimumCodeSize);
if (lzwMinimumCodeSize < 2) {
lzwMinimumCodeSize = 2;
}
// TODO:
// make
// better
// choice
// here.
bos.write(lzwMinimumCodeSize);
final MyLzwCompressor compressor = new MyLzwCompressor(
lzwMinimumCodeSize, ByteOrder.LITTLE_ENDIAN, false); // GIF
// Mode);
final byte[] imagedata = new byte[width * height];
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
final int argb = src.getRGB(x, y);
final int rgb = 0xffffff & argb;
int index;
if (hasAlpha) {
final int alpha = 0xff & (argb >> 24);
final int alphaThreshold = 255;
if (alpha < alphaThreshold) {
index = palette2.length(); // is transparent
} else {
index = palette2.getPaletteIndex(rgb);
}
} else {
index = palette2.getPaletteIndex(rgb);
}
imagedata[y * width + x] = (byte) index;
}
}