class PNGConverterState
    {
        List<Chunk> IDATs = new ArrayList<>();
        @SuppressWarnings("SpellCheckingInspection") Chunk IHDR;
        @SuppressWarnings("SpellCheckingInspection") Chunk PLTE;
        Chunk iCCP;
        Chunk tRNS;
        Chunk sRGB;
        Chunk gAMA;
        Chunk cHRM;

        // Parsed header fields
        int width;
        int height;
        int bitsPerComponent;
    }

    private static int readInt(byte[] data, int offset)
    {
        int b1 = (data[offset] & 0xFF) << 24;
        int b2 = (data[offset + 1] & 0xFF) << 16;
        int b3 = (data[offset + 2] & 0xFF) << 8;
        int b4 = (data[offset + 3] & 0xFF);
        return b1 | b2 | b3 | b4;
    }

    private static float readPNGFloat(byte[] bytes, int offset)
    {
        int v = readInt(bytes, offset);
        return v / 100000f;
    }

    /**
     * Parse the PNG structure into the PNGConverterState. If we can't handle
     * something, this method will return null.
     *
     * @param imageData the byte array with the PNG data
     * @return null or the converter state with all relevant chunks
     */
    private static PNGConverterState parsePNGChunks(byte[] imageData)
    {<FILL_FUNCTION_BODY>

        if (imageData.length < 20)
        {
            LOG.error("ByteArray way to small: {}", imageData.length);
            return null;
        }

        PNGConverterState state = new PNGConverterState();
        int ptr = 8;
        int firstChunkType = readInt(imageData, ptr + 4);

        if (firstChunkType != CHUNK_IHDR)
        {
            LOG.error(String.format("First Chunktype was %08X, not IHDR", firstChunkType));
            return null;
        }

        while (ptr + 12 <= imageData.length)
        {
            int chunkLength = readInt(imageData, ptr);
            int chunkType = readInt(imageData, ptr + 4);
            ptr += 8;

            if (ptr + chunkLength + 4 > imageData.length)
            {
                LOG.error(
                        "Not enough bytes. At offset {} are {} bytes expected. Overall length is {}",
                        ptr, chunkLength, imageData.length);
                return null;
            }

            Chunk chunk = new Chunk();
            chunk.chunkType = chunkType;
            chunk.bytes = imageData;
            chunk.start = ptr;
            chunk.length = chunkLength;

            switch (chunkType)
            {
            case CHUNK_IHDR:
                if (state.IHDR != null)
                {
                    LOG.error("Two IHDR chunks? There is something wrong.");
                    return null;
                }
                state.IHDR = chunk;
                break;
            case CHUNK_IDAT:
                // The image data itself
                state.IDATs.add(chunk);
                break;
            case CHUNK_PLTE:
                // For indexed images the palette table
                if (state.PLTE != null)
                {
                    LOG.error("Two PLTE chunks? There is something wrong.");
                    return null;
                }
                state.PLTE = chunk;
                break;
            case CHUNK_IEND:
                // We are done, return the state
                return state;
            case CHUNK_TRNS:
                // For indexed images the alpha transparency table
                if (state.tRNS != null)
                {
                    LOG.error("Two tRNS chunks? There is something wrong.");
                    return null;
                }
                state.tRNS = chunk;
                break;
            case CHUNK_GAMA:
                // Gama
                state.gAMA = chunk;
                break;
            case CHUNK_CHRM:
                // Chroma
                state.cHRM = chunk;
                break;
            case CHUNK_ICCP:
                // ICC Profile
                state.iCCP = chunk;
                break;
            case CHUNK_SBIT:
                LOG.debug("Can't convert PNGs with sBIT chunk.");
                break;
            case CHUNK_SRGB:
                // We use the rendering intent from the chunk
                state.sRGB = chunk;
                break;
            case CHUNK_TEXT:
            case CHUNK_ZTXT:
            case CHUNK_ITXT:
                // We don't care about this text infos / metadata
                break;
            case CHUNK_KBKG:
                // As we can handle transparency we don't need the background color information.
                break;
            case CHUNK_HIST:
                // We don't need the color histogram
                break;
            case CHUNK_PHYS:
                // The PDImageXObject will be placed by the user however he wants,
                // so we can not enforce the physical dpi information stored here.
                // We just ignore it.
                break;
            case CHUNK_SPLT:
                // This palette stuff seems editor related, we don't need it.
                break;
            case CHUNK_TIME:
                // We don't need the last image change time either
                break;
            default:
                LOG.debug(String.format("Unknown chunk type %08X, skipping.", chunkType));
                break;
            }
            ptr += chunkLength;

            // Read the CRC
            chunk.crc = readInt(imageData, ptr);
            ptr += 4;
        }
        LOG.error("No IEND chunk found.");
        return null;
    
<no_super_class>