@Override
    public void writeImage(final BufferedImage src, final OutputStream os, IcoImagingParameters params) throws ImagingException, IOException {
        if (params == null) {
            params = new IcoImagingParameters();
        }
        final PixelDensity pixelDensity = params.getPixelDensity();

        final PaletteFactory paletteFactory = new PaletteFactory();
        final SimplePalette palette = paletteFactory.makeExactRgbPaletteSimple(src, 256);
        final int bitCount;
        // If we can't obtain an exact rgb palette, we set the bit count to either 24 or 32
        // so there is a relation between having a palette and the bit count.
        if (palette == null) {
            final boolean hasTransparency = paletteFactory.hasTransparency(src);
            if (hasTransparency) {
                bitCount = 32;
            } else {
                bitCount = 24;
            }
        } else if (palette.length() <= 2) {
            bitCount = 1;
        } else if (palette.length() <= 16) {
            bitCount = 4;
        } else {
            bitCount = 8;
        }

        try (AbstractBinaryOutputStream bos = AbstractBinaryOutputStream.littleEndian(os)) {

            int scanlineSize = (bitCount * src.getWidth() + 7) / 8;
            if (scanlineSize % 4 != 0) {
                scanlineSize += 4 - scanlineSize % 4; // pad scanline to 4 byte
                                                      // size.
            }
            int tScanlineSize = (src.getWidth() + 7) / 8;
            if (tScanlineSize % 4 != 0) {
                tScanlineSize += 4 - tScanlineSize % 4; // pad scanline to 4
                                                        // byte size.
            }
            final int imageSize = 40 + 4 * (bitCount <= 8 ? 1 << bitCount : 0) + src.getHeight() * scanlineSize + src.getHeight() * tScanlineSize;

            // 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.write3Bytes(argb);
                        bos.write(0);
                    } else {
                        bos.write4Bytes(0);
                    }
                }
            }

            int bitCache = 0;
            int bitsInCache = 0;
            final int rowPadding = scanlineSize - (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);
                    // Remember there is a relation between having a rgb palette and the bit count, see above comment
                    if (palette == null) {
                        if (bitCount == 24) {
                            bos.write3Bytes(argb);
                        } else if (bitCount == 32) {
                            bos.write4Bytes(argb);
                        }
                    } else if (bitCount < 8) {
                        final int rgb = 0xffffff & argb;
                        final int index = palette.getPaletteIndex(rgb);
                        bitCache <<= bitCount;
                        bitCache |= index;
                        bitsInCache += bitCount;
                        if (bitsInCache >= 8) {
                            bos.write(0xff & bitCache);
                            bitCache = 0;
                            bitsInCache = 0;
                        }
                    } else if (bitCount == 8) {
                        final int rgb = 0xffffff & argb;
                        final int index = palette.getPaletteIndex(rgb);
                        bos.write(0xff & index);
                    }
                }

                if (bitsInCache > 0) {
                    bitCache <<= 8 - bitsInCache;
                    bos.write(0xff & bitCache);
                    bitCache = 0;
                    bitsInCache = 0;
                }

                for (int x = 0; x < rowPadding; x++) {
                    bos.write(0);
                }
            }

            final int tRowPadding = tScanlineSize - (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;
                    bitCache <<= 1;
                    if (alpha == 0) {
                        bitCache |= 1;
                    }
                    bitsInCache++;
                    if (bitsInCache >= 8) {
                        bos.write(0xff & bitCache);
                        bitCache = 0;
                        bitsInCache = 0;
                    }
                }

                if (bitsInCache > 0) {
                    bitCache <<= 8 - bitsInCache;
                    bos.write(0xff & bitCache);
                    bitCache = 0;
                    bitsInCache = 0;
                }

                for (int x = 0; x < tRowPadding; x++) {
                    bos.write(0);
                }
            }
        }
    }
Constructs a new instance with the little-endian byte order.
src/main/java/org/apache/commons/imaging/formats/ico/IcoImageParser.java