package com.example.smtias_capture import android.graphics.Bitmap import android.graphics.BitmapFactory import android.graphics.ImageFormat import android.graphics.Matrix import android.graphics.Rect import android.graphics.YuvImage import java.io.ByteArrayOutputStream /** * YUV_420_888 のプレーンデータを NV21 経由で JPEG に変換するユーティリティ. * * ライブプレビューのスナップショット保存に使用する(撮影本体の RAW とは独立). * 回転・左右反転にも対応する. */ internal object YuvJpegConverter { /** * YUV_420_888 のプレーン群を JPEG バイト列に変換する. * * [rotation] は時計回りの度数,[mirror] が true なら左右反転を適用する. */ fun convert( width: Int, height: Int, yPlane: ByteArray, uPlane: ByteArray, vPlane: ByteArray, yRowStride: Int, uvRowStride: Int, uvPixelStride: Int, rotation: Int, mirror: Boolean, quality: Int, ): ByteArray { // YUV_420_888 → NV21 変換 val nv21 = ByteArray(width * height * 3 / 2) // Y プレーンをコピー for (row in 0 until height) { System.arraycopy(yPlane, row * yRowStride, nv21, row * width, width) } // UV プレーンを NV21 形式(VUVU...)にインターリーブ val uvOffset = width * height for (row in 0 until height / 2) { for (col in 0 until width / 2) { val uvIndex = row * uvRowStride + col * uvPixelStride nv21[uvOffset + row * width + col * 2] = vPlane[uvIndex] nv21[uvOffset + row * width + col * 2 + 1] = uPlane[uvIndex] } } // NV21 → JPEG val yuvImage = YuvImage(nv21, ImageFormat.NV21, width, height, null) val jpegStream = ByteArrayOutputStream() yuvImage.compressToJpeg(Rect(0, 0, width, height), quality, jpegStream) // 回転・反転が必要な場合 return if (rotation != 0 || mirror) { val bitmap = BitmapFactory.decodeByteArray(jpegStream.toByteArray(), 0, jpegStream.size()) val matrix = Matrix() if (rotation != 0) matrix.postRotate(rotation.toFloat()) if (mirror) matrix.postScale(-1f, 1f) val transformed = Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, matrix, true) bitmap.recycle() val outStream = ByteArrayOutputStream() transformed.compress(Bitmap.CompressFormat.JPEG, quality, outStream) transformed.recycle() outStream.toByteArray() } else { jpegStream.toByteArray() } } }