All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.org.jetbrains.letsPlot.util.pngj.DeflatedChunkReader.kt Maven / Gradle / Ivy

There is a newer version: 4.8.0
Show newest version
/*
 * Copyright (c) 2023 JetBrains s.r.o.
 * Use of this source code is governed by the MIT license that can be found in the LICENSE file.
 *
 * This file has been modified by JetBrains : Java code has been converted to Kotlin code.
 *
 * THE FOLLOWING IS THE COPYRIGHT OF THE ORIGINAL DOCUMENT:
 *
 * Copyright (c) 2009-2012, Hernán J. González.
 * Licensed under the Apache License, Version 2.0.
 *
 * The original PNGJ library is written in Java and can be found here: [PNGJ](https://github.com/leonbloy/pngj).
 */

@file:Suppress("unused")
package org.jetbrains.letsPlot.util.pngj

import org.jetbrains.letsPlot.util.pngj.PngHelperInternal.readInt4fromBytes
import org.jetbrains.letsPlot.util.pngj.chunks.PngChunk.Companion.ID_FDAT

/**
 *
 * Specialization of ChunkReader, for IDAT-like chunks. These chunks are part of
 * a set of similar chunks (contiguos normally, not necessariyl) which conforms
 * a zlib stream
 */
internal abstract class DeflatedChunkReader(
    clen: Int,
    chunkid: String,
    @Suppress("UNUSED_PARAMETER")
    checkCrc: Boolean,
    offsetInPng: Long,
    iDatSet: DeflatedChunksSet
) : ChunkReader(clen, chunkid, offsetInPng, ChunkReaderMode.PROCESS) {
    private val deflatedChunksSet: DeflatedChunksSet
    private var alsoBuffer = false
    private var skipBytes = false // fDAT (APNG) skips 4 bytes)
    private lateinit var skippedBytes // only for fDAT
            : ByteArray
    private var seqNumExpected = -1 // only for fDAT

    init {
        deflatedChunksSet = iDatSet
        if (chunkid == ID_FDAT) {
            skipBytes = true
            skippedBytes = ByteArray(4)
        }
        iDatSet.appendNewChunk(this)
    }

    /**
     * Delegates to ChunkReaderDeflatedSet.processData()
     */
    override fun processData(offsetInchunk: Int, buf: ByteArray, off: Int, len: Int) {
        @Suppress("NAME_SHADOWING")
        var off = off
        @Suppress("NAME_SHADOWING")
        var len = len

        if (skipBytes && offsetInchunk < 4) { // only for APNG (sigh)
            var oc = offsetInchunk
            while (oc < 4 && len > 0) {
                skippedBytes[oc] = buf[off]
                oc++
                off++
                len--
            }
        }
        if (len > 0) { // delegate to idatSet
            deflatedChunksSet.processBytes(buf, off, len)
            if (alsoBuffer) { // very rare!
                arraycopy(buf, off, chunkRaw.data!!, read, len)
            }
        }
    }

    /**
     * only a stupid check for fDAT (I wonder how many APGN readers do this)
     */
    override fun chunkDone() {
        if (skipBytes && chunkRaw.id == ID_FDAT) {
            if (seqNumExpected >= 0) {
                val seqNum = readInt4fromBytes(skippedBytes, 0)
                if (seqNum != seqNumExpected) throw PngjInputException(
                    "bad chunk sequence for fDAT chunk $seqNum expected $seqNumExpected"
                )
            }
        }
    }

    override val isFromDeflatedSet: Boolean
        get() = true

    /**
     * In some rare cases you might want to also buffer the data?
     */
    fun setAlsoBuffer() {
        require(read > 0) { "too late" }
        alsoBuffer = true
        chunkRaw.allocData()
    }

    /** only relevant for fDAT  */
    fun setSeqNumExpected(seqNumExpected: Int) {
        this.seqNumExpected = seqNumExpected
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy