package com.example.mini_tias import android.graphics.Bitmap import java.io.ByteArrayOutputStream import java.nio.ByteBuffer import java.util.zip.CRC32 import java.util.zip.Deflater import java.util.zip.DeflaterOutputStream /** * Bitmap を非圧縮 PNG(Deflater.NO_COMPRESSION)としてエンコードするユーティリティ. * * 圧縮率より速度を優先する研究用途向け.撮影フローのボトルネック解消のため * 標準の `Bitmap.compress` を使わず自前で IDAT を非圧縮 zlib でラップする. */ internal object PngEncoder { /** [bitmap] を非圧縮 PNG のバイト列に変換して返す. */ fun encodeUncompressed(bitmap: Bitmap): ByteArray { val w = bitmap.width val h = bitmap.height val out = ByteArrayOutputStream(w * h * 3 + 1024) // PNG シグネチャ out.write(byteArrayOf(0x89.toByte(), 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A)) // IHDR チャンク val ihdrData = ByteBuffer.allocate(13) .putInt(w) .putInt(h) .put(8) // bit depth .put(2) // color type: RGB .put(0) // compression method .put(0) // filter method .put(0) // interlace method .array() writeChunk(out, "IHDR", ihdrData) // IDAT チャンク: 非圧縮 zlib でラップした RGB データ val idatStream = ByteArrayOutputStream(w * h * 3 + h + 64) val deflater = Deflater(Deflater.NO_COMPRESSION) val deflaterOut = DeflaterOutputStream(idatStream, deflater, 65536) val pixels = IntArray(w) val rowBytes = ByteArray(1 + w * 3) // フィルタバイト(0) + RGB rowBytes[0] = 0 // フィルタ: None for (y in 0 until h) { bitmap.getPixels(pixels, 0, w, 0, y, w, 1) for (x in 0 until w) { val pixel = pixels[x] rowBytes[1 + x * 3] = ((pixel shr 16) and 0xFF).toByte() // R rowBytes[1 + x * 3 + 1] = ((pixel shr 8) and 0xFF).toByte() // G rowBytes[1 + x * 3 + 2] = (pixel and 0xFF).toByte() // B } deflaterOut.write(rowBytes) } deflaterOut.finish() deflaterOut.close() deflater.end() writeChunk(out, "IDAT", idatStream.toByteArray()) // IEND チャンク writeChunk(out, "IEND", byteArrayOf()) return out.toByteArray() } /** PNG チャンクを書き込む(length + type + data + CRC). */ private fun writeChunk(out: ByteArrayOutputStream, type: String, data: ByteArray) { val typeBytes = type.toByteArray(Charsets.US_ASCII) // length (4 bytes, big-endian) out.write(ByteBuffer.allocate(4).putInt(data.size).array()) // type (4 bytes) out.write(typeBytes) // data out.write(data) // CRC (type + data) val crc = CRC32() crc.update(typeBytes) crc.update(data) out.write(ByteBuffer.allocate(4).putInt(crc.value.toInt()).array()) } }