diff --git a/android/app/src/main/kotlin/com/example/mini_tias/MainActivity.kt b/android/app/src/main/kotlin/com/example/mini_tias/MainActivity.kt index a8fc1e4..3b19bf4 100644 --- a/android/app/src/main/kotlin/com/example/mini_tias/MainActivity.kt +++ b/android/app/src/main/kotlin/com/example/mini_tias/MainActivity.kt @@ -1,13 +1,10 @@ package com.example.mini_tias import io.flutter.embedding.android.FlutterActivity -import io.flutter.embedding.android.RenderMode import io.flutter.embedding.engine.FlutterEngine import io.flutter.plugin.common.MethodChannel class MainActivity : FlutterActivity() { - override fun getRenderMode(): RenderMode = RenderMode.texture - override fun configureFlutterEngine(flutterEngine: FlutterEngine) { super.configureFlutterEngine(flutterEngine) diff --git a/android/app/src/main/kotlin/com/example/mini_tias/RawCapturePlugin.kt b/android/app/src/main/kotlin/com/example/mini_tias/RawCapturePlugin.kt index a15b6f6..ba799cc 100644 --- a/android/app/src/main/kotlin/com/example/mini_tias/RawCapturePlugin.kt +++ b/android/app/src/main/kotlin/com/example/mini_tias/RawCapturePlugin.kt @@ -1,6 +1,5 @@ package com.example.mini_tias -import android.app.Activity import android.content.Context import android.graphics.Bitmap import android.graphics.BitmapFactory @@ -13,7 +12,6 @@ import android.media.MediaScannerConnection import android.os.Handler import android.os.HandlerThread -import android.view.PixelCopy import io.flutter.plugin.common.MethodCall import io.flutter.plugin.common.MethodChannel import java.io.ByteArrayOutputStream @@ -22,9 +20,7 @@ * Camera2 API を使用してフロントカメラから YUV_420_888 フォーマットで * フル解像度の画像を 1 フレームキャプチャする. */ -class RawCapturePlugin(private val activity: Activity) : MethodChannel.MethodCallHandler { - - private val context: Context = activity.applicationContext +class RawCapturePlugin(private val context: Context) : MethodChannel.MethodCallHandler { private var backgroundThread: HandlerThread? = null private var backgroundHandler: Handler? = null @@ -33,7 +29,6 @@ override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) { when (call.method) { "captureFullResolutionYuv" -> captureFullResolutionYuv(result) - "capturePreviewFrame" -> capturePreviewFrame(call, result) "convertYuvToJpeg" -> convertYuvToJpeg(call, result) "scanFile" -> { val path = call.argument("path") @@ -49,57 +44,6 @@ } } - /// PixelCopy で全画面をキャプチャし,指定領域を切り取って 180° 回転して返す. - private fun capturePreviewFrame(call: MethodCall, result: MethodChannel.Result) { - val x = call.argument("x") ?: 0 - val y = call.argument("y") ?: 0 - val cropWidth = call.argument("width") ?: 0 - val cropHeight = call.argument("height") ?: 0 - - val window = activity.window - val view = window.decorView - val bitmap = Bitmap.createBitmap(view.width, view.height, Bitmap.Config.ARGB_8888) - - val handler = Handler(activity.mainLooper) - PixelCopy.request(window, bitmap, { copyResult -> - if (copyResult == PixelCopy.SUCCESS) { - try { - val output = if (cropWidth > 0 && cropHeight > 0) { - // Dart 側で回転済み左上座標を計算済み - val cx = x.coerceIn(0, bitmap.width - 1) - val cy = y.coerceIn(0, bitmap.height - 1) - val cw = cropWidth.coerceAtMost(bitmap.width - cx) - val ch = cropHeight.coerceAtMost(bitmap.height - cy) - - // 切り取り - val cropped = Bitmap.createBitmap(bitmap, cx, cy, cw, ch) - bitmap.recycle() - - // 180° 回転(app.dart の回転を相殺) - val matrix = Matrix() - matrix.postRotate(180f) - val rotated = Bitmap.createBitmap(cropped, 0, 0, cropped.width, cropped.height, matrix, true) - cropped.recycle() - rotated - } else { - bitmap - } - - val stream = ByteArrayOutputStream() - output.compress(Bitmap.CompressFormat.JPEG, 85, stream) - output.recycle() - result.success(stream.toByteArray()) - } catch (e: Exception) { - bitmap.recycle() - result.error("PROCESS_ERROR", "スナップショット処理に失敗: ${e.message}", null) - } - } else { - bitmap.recycle() - result.error("PIXEL_COPY_FAILED", "PixelCopy に失敗: $copyResult", null) - } - }, handler) - } - /// YUV_420_888 を NV21 に変換し,YuvImage で JPEG 化して返す. private fun convertYuvToJpeg(call: MethodCall, result: MethodChannel.Result) { try { diff --git "a/docs/04_SPEC/SPEC_01_\347\224\273\351\235\242\346\251\237\350\203\275\344\273\225\346\247\230\346\233\270.md" "b/docs/04_SPEC/SPEC_01_\347\224\273\351\235\242\346\251\237\350\203\275\344\273\225\346\247\230\346\233\270.md" index e0c1820..d63c009 100644 --- "a/docs/04_SPEC/SPEC_01_\347\224\273\351\235\242\346\251\237\350\203\275\344\273\225\346\247\230\346\233\270.md" +++ "b/docs/04_SPEC/SPEC_01_\347\224\273\351\235\242\346\251\237\350\203\275\344\273\225\346\247\230\346\233\270.md" @@ -73,7 +73,7 @@ - **即時撮影**: タイマー OFF 時,シャッターボタンタップで即座に撮影する - **タイマー撮影**: タイマー ON 時,シャッターボタンタップで 3, 2, 1 のカウントダウン後に撮影する(手ブレ・振動防止用) - **連続撮影**: 前回の保存完了を待ってから次の撮影が可能 -- **保存中表示**: 撮影開始時に PixelCopy API(Android)で画面をキャプチャし,保存中はそのスナップショットを表示する(黒い画面を防ぐため).TextureView モードで動作する +- **保存中表示**: カメラの画像ストリーム(`startImageStream`)から最新フレームを保持し,撮影開始時に Android ネイティブの `YuvImage.compressToJpeg()` で JPEG 変換して表示する(オーバーレイなし・瞬時) - **フィードバック**: 撮影成功時にスナックバーで「保存しました: [ファイル名]」を表示する - **エラー時**: カメラ初期化失敗やストレージ書き込み失敗時は,エラーメッセージをスナックバーで表示する @@ -170,6 +170,9 @@ YUV → RGB 変換(BT.601 係数,isolate で実行) │ ▼ +90° 時計回り回転(センサー向き補正) + │ + ▼ PNG エンコード(image パッケージ,ロスレス) │ ▼ diff --git a/lib/services/raw_capture_service.dart b/lib/services/raw_capture_service.dart index 35eebb5..5bf13e6 100644 --- a/lib/services/raw_capture_service.dart +++ b/lib/services/raw_capture_service.dart @@ -15,23 +15,6 @@ return Map.from(result as Map); } - /// PixelCopy でカメラプレビュー領域のスナップショットを JPEG として取得する. - Future capturePreviewFrame({ - required int x, - required int y, - required int width, - required int height, - }) async { - final result = await _channel.invokeMethod( - 'capturePreviewFrame', - {'x': x, 'y': y, 'width': width, 'height': height}, - ); - if (result == null) { - throw Exception('プレビューフレームの取得に失敗しました'); - } - return result; - } - /// YUV_420_888 を JPEG に変換する(Android ネイティブの高速変換). Future convertYuvToJpeg({ required int width,