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

commonMain.org.jetbrains.letsPlot.util.pngj.IdatSet.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).
 */

package org.jetbrains.letsPlot.util.pngj

/**
 * This object process the concatenation of IDAT chunks.
 *
 *
 * It extends [DeflatedChunksSet], adding the intelligence to unfilter
 * rows, and to understand row lenghts in terms of ImageInfo and (eventually)
 * Deinterlacer
 */
internal class IdatSet(
    id: String,
    private val callbackMode: Boolean,
    iminfo: ImageInfo,
    deinterlacer: Deinterlacer?,
    inf: Inflater?,
    buffer: ByteArray?
) : DeflatedChunksSet(
    id, callbackMode, if (deinterlacer != null) deinterlacer.bytesToRead + 1 else iminfo.bytesPerRow + 1,
    iminfo.bytesPerRow + 1, inf, buffer
) {
    /**
     * Unfiltered row.
     *
     *
     * This should be called only if [.isRowReady] returns true.
     *
     *
     * To get real length, use [.getRowLen]
     *
     *
     *
     * @return Unfiltered row, includes filter byte
     */
    var unfilteredRow: ByteArray? = null
        private set
    private var rowUnfilteredPrev: ByteArray? = null
    private val imgInfo // in the case of APNG this is the frame image
            : ImageInfo
    private val deinterlacer: Deinterlacer?
    internal val rowinfo // info for the last processed row, for debug
            : RowInfo

    /**
     * Only for debug/stats
     *
     * @return Array of 5 integers (sum equal numbers of rows) counting each
     * filter use
     */
    private var filterUseStat = IntArray(5) // for stats

    /**
     * @param id
     * Chunk id (first chunk), should be shared by all concatenated
     * chunks
     * @param iminfo
     * Image info
     * @param deinterlacer
     * Not null if interlaced
     */
    constructor(id: String, callbackMode: Boolean, iminfo: ImageInfo, deinterlacer: Deinterlacer?) : this(
        id,
        callbackMode,
        iminfo,
        deinterlacer,
        null,
        null
    )

    /**
     * Special constructor with preallocated buffer.
     *
     *
     *
     *
     * Same as [.IdatSet], but you can
     * pass a Inflater (will be reset internally), and a buffer (will be used
     * only if size is enough)
     */
    init {
        imgInfo = iminfo
        this.deinterlacer = deinterlacer
        rowinfo = RowInfo(iminfo, deinterlacer)
        println("Creating IDAT set ")
    }

    /**
     * Applies PNG un-filter to inflated raw line. Result in
     * [.getUnfilteredRow] [.getRowLen]
     */
    private fun unfilterRow() {
        unfilterRow(rowinfo.bytesRow)
    }

    // nbytes: NOT including the filter byte. leaves result in rowUnfiltered
    private fun unfilterRow(nbytes: Int) {
        if (unfilteredRow == null || unfilteredRow!!.size < row!!.size) {
            unfilteredRow = ByteArray(row!!.size)
            rowUnfilteredPrev = ByteArray(row!!.size)
        }
        if (rowinfo.rowNsubImg == 0) fill(unfilteredRow!!, 0.toByte()) // see swap that follows
        // swap
        val tmp = unfilteredRow!!
        unfilteredRow = rowUnfilteredPrev
        rowUnfilteredPrev = tmp
        val ftn: Int = row!![0].toInt()
        if (!FilterType.isValidStandard(ftn)) throw PngjInputException("Filter type $ftn invalid")
        val ft: FilterType = FilterType.getByVal(ftn)
        filterUseStat[ftn]++
        unfilteredRow!![0] = row!![0] // we copy the filter type, can be useful
        when (ft) {
            FilterType.FILTER_NONE -> unfilterRowNone(nbytes)
            FilterType.FILTER_SUB -> unfilterRowSub(nbytes)
            FilterType.FILTER_UP -> unfilterRowUp(nbytes)
            FilterType.FILTER_AVERAGE -> unfilterRowAverage(nbytes)
            FilterType.FILTER_PAETH -> unfilterRowPaeth(nbytes)
            else -> throw PngjInputException("Filter type $ftn not implemented")
        }
    }

    private fun unfilterRowAverage(nbytes: Int) {
        var x: Int
        var j: Int = 1 - imgInfo.bytesPixel
        var i = 1
        while (i <= nbytes) {
            x = if (j > 0) unfilteredRow!![j].toInt() and 0xff else 0
            unfilteredRow!![i] = (row!![i] + (x + (rowUnfilteredPrev!![i].toInt() and 0xFF)) / 2).toByte()
            i++
            j++
        }
    }

    private fun unfilterRowNone(nbytes: Int) {
        for (i in 1..nbytes) {
            unfilteredRow!![i] = row!![i]
        }
    }

    private fun unfilterRowPaeth(nbytes: Int) {
        var x: Int
        var y: Int
        var j: Int = 1 - imgInfo.bytesPixel
        var i = 1
        while (i <= nbytes) {
            x = if (j > 0) unfilteredRow!![j].toInt() and 0xFF else 0
            y = if (j > 0) rowUnfilteredPrev!![j].toInt() and 0xFF else 0
            unfilteredRow!![i] = (row!![i]
                    + PngHelperInternal.filterPaethPredictor(x, rowUnfilteredPrev!![i].toInt() and 0xFF, y)).toByte()
            i++
            j++
        }
    }

    private fun unfilterRowSub(nbytes: Int) {
        var i = 1
        while (i <= imgInfo.bytesPixel) {
            unfilteredRow!![i] = row!![i]
            i++
        }
        var j = 1
        i = imgInfo.bytesPixel + 1
        while (i <= nbytes) {
            unfilteredRow!![i] = (row!![i] + unfilteredRow!![j]).toByte()
            i++
            j++
        }
    }

    private fun unfilterRowUp(nbytes: Int) {
        for (i in 1..nbytes) {
            unfilteredRow!![i] = (row!![i] + rowUnfilteredPrev!![i]).toByte()
        }
    }

    /**
     * does the unfiltering of the inflated row, and updates row info
     */
    override fun preProcessRow() {
        super.preProcessRow()
        rowinfo.update(rown)
        unfilterRow()
        rowinfo.updateBuf(unfilteredRow!!, rowinfo.bytesRow + 1)
    }

    /**
     * Method for async/callback mode .
     *
     *
     * In callback mode will be called as soon as each row is retrieved
     * (inflated and unfiltered), after [.preProcessRow]
     *
     *
     * This is a dummy implementation (this normally should be overriden) that
     * does nothing more than compute the length of next row.
     *
     *
     * The return value is essential
     *
     *
     *
     * @return Length of next row, in bytes (including filter byte),
     * non-positive if done
     */
    override fun processRowCallback(): Int {
        return advanceToNextRow()
    }

    /**
     * Signals that we are done with the previous row, begin reading the next
     * one.
     *
     *
     * In polled mode, calls setNextRowLen()
     *
     *
     * Warning: after calling this, the unfilterRow is invalid!
     *
     * @return Returns nextRowLen
     */
    fun advanceToNextRow(): Int {
        // PngHelperInternal.LOGGER.info("advanceToNextRow");
        val bytesNextRow: Int = if (deinterlacer == null) {
            if (rown >= imgInfo.rows - 1) 0 else imgInfo.bytesPerRow + 1
        } else {
            val more: Boolean = deinterlacer.nextRow()
            if (more) deinterlacer.bytesToRead + 1 else 0
        }
        if (!callbackMode) { // in callback mode, setNextRowLen() is called internally
            prepareForNextRow(bytesNextRow)
        }
        return bytesNextRow
    }

    override val isRowReady: Boolean
        get() = !isWaitingForMoreInput

    fun updateCrcs(vararg idatCrcs: Checksum?) {
        for (idatCrca in idatCrcs)
            idatCrca?.update(unfilteredRow!!, 1, rowFilled - 1)
    }

    override fun close() {
        super.close()
        unfilteredRow = null // not really necessary...
        rowUnfilteredPrev = null
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy