public static byte[] decompressT4_2D(final byte[] compressed, final int width, final int height, final boolean hasFill) throws ImagingException {
        final BitInputStreamFlexible inputStream = new BitInputStreamFlexible(new ByteArrayInputStream(compressed));
        try (BitArrayOutputStream outputStream = new BitArrayOutputStream()) {
            final int[] referenceLine = Allocator.intArray(width);
            for (int y = 0; y < height; y++) {
                int rowLength = 0;
                try {
                    T4_T6_Tables.Entry entry = CONTROL_CODES.decode(inputStream);
                    if (!isEol(entry, hasFill)) {
                        throw new ImagingException("Expected EOL not found");
                    }
                    final int tagBit = inputStream.readBits(1);
                    if (tagBit == 0) {
                        // 2D
                        int codingA0Color = WHITE;
                        int referenceA0Color = WHITE;
                        int b1 = nextChangingElement(referenceLine, referenceA0Color, 0);
                        int b2 = nextChangingElement(referenceLine, 1 - referenceA0Color, b1 + 1);
                        for (int a0 = 0; a0 < width;) {
                            final int a1;
                            final int a2;
                            entry = CONTROL_CODES.decode(inputStream);
                            if (entry == T4_T6_Tables.P) {
                                fillRange(outputStream, referenceLine, a0, b2, codingA0Color);
                                a0 = b2;
                            } else if (entry == T4_T6_Tables.H) {
                                final int a0a1 = readTotalRunLength(inputStream, codingA0Color);
                                a1 = a0 + a0a1;
                                fillRange(outputStream, referenceLine, a0, a1, codingA0Color);
                                final int a1a2 = readTotalRunLength(inputStream, 1 - codingA0Color);
                                a2 = a1 + a1a2;
                                fillRange(outputStream, referenceLine, a1, a2, 1 - codingA0Color);
                                a0 = a2;
                            } else {
                                final int a1b1;
                                if (entry == T4_T6_Tables.V0) {
                                    a1b1 = 0;
                                } else if (entry == T4_T6_Tables.VL1) {
                                    a1b1 = -1;
                                } else if (entry == T4_T6_Tables.VL2) {
                                    a1b1 = -2;
                                } else if (entry == T4_T6_Tables.VL3) {
                                    a1b1 = -3;
                                } else if (entry == T4_T6_Tables.VR1) {
                                    a1b1 = 1;
                                } else if (entry == T4_T6_Tables.VR2) {
                                    a1b1 = 2;
                                } else if (entry == T4_T6_Tables.VR3) {
                                    a1b1 = 3;
                                } else {
                                    throw new ImagingException("Invalid/unknown T.4 control code " + entry.bitString);
                                }
                                a1 = b1 + a1b1;
                                fillRange(outputStream, referenceLine, a0, a1, codingA0Color);
                                a0 = a1;
                                codingA0Color = 1 - codingA0Color;
                            }
                            referenceA0Color = changingElementAt(referenceLine, a0);
                            if (codingA0Color == referenceA0Color) {
                                b1 = nextChangingElement(referenceLine, referenceA0Color, a0 + 1);
                            } else {
                                b1 = nextChangingElement(referenceLine, referenceA0Color, a0 + 1);
                                b1 = nextChangingElement(referenceLine, 1 - referenceA0Color, b1 + 1);
                            }
                            b2 = nextChangingElement(referenceLine, 1 - codingA0Color, b1 + 1);
                            rowLength = a0;
                        }
                    } else {
                        // 1D
                        int color = WHITE;
                        for (rowLength = 0; rowLength < width;) {
                            final int runLength = readTotalRunLength(inputStream, color);
                            for (int i = 0; i < runLength; i++) {
                                outputStream.writeBit(color);
                                referenceLine[rowLength + i] = color;
                            }
                            color = 1 - color;
                            rowLength += runLength;
                        }
                    }
                } catch (final IOException e) {
                    throw new ImagingException("Decompression error", e);
                }

                if (rowLength == width) {
                    outputStream.flush();
                } else if (rowLength > width) {
                    throw new ImagingException("Unrecoverable row length error in image row " + y);
                }
            }

            return outputStream.toByteArray();
        }
    }
Decompressed T.4 2D encoded data. EOL at the beginning and after each row, can be preceded by fill bits to fit on a byte boundary, and is succeeded by a
tag bit determining whether the next line is encoded using 1D or 2D. No RTC.

@param compressed compressed byte data
@param width      image width
@param height     image height
@param hasFill    used to check the end of line
@return the decompressed data
@throws ImagingException if it fails to read the compressed data
src/main/java/org/apache/commons/imaging/formats/tiff/itu_t4/T4AndT6Compression.java