feat(native-export): scaffold local Expo module + native decode/encode bench probe (P0)
Local Kotlin module modules/recipescam-export (autolinked via expo ./modules) exposes decodeEncodeAsync — BitmapFactory decode -> JPEG q95 encode -> file, all on Dispatchers.IO off the JS/main threads. EXPO_PUBLIC_BENCH=1 builds run src/dev/nativeBenchProbe.ts: 3 timed decode+encode passes on the bundled wallframe artwork plus a JS-timer gap check proving the JS thread never blocks during native rendering. Legacy Skia engine untouched (reference).
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
plugins {
|
||||
id 'com.android.library'
|
||||
id 'expo-module-gradle-plugin'
|
||||
}
|
||||
|
||||
group = 'com.locphamtran.recipescamera'
|
||||
version = '0.1.0'
|
||||
|
||||
android {
|
||||
namespace "com.locphamtran.recipescamera.export"
|
||||
defaultConfig {
|
||||
versionCode 1
|
||||
versionName "0.1.0"
|
||||
}
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
package com.locphamtran.recipescamera.export
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.BitmapFactory
|
||||
import expo.modules.kotlin.functions.Coroutine
|
||||
import expo.modules.kotlin.modules.Module
|
||||
import expo.modules.kotlin.modules.ModuleDefinition
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
|
||||
class RecipescamExportModule : Module() {
|
||||
override fun definition() = ModuleDefinition {
|
||||
Name("RecipesCamExport")
|
||||
|
||||
// P0 spike: decode (BitmapFactory) + JPEG encode + file write, all off the
|
||||
// JS/main threads. Returns per-stage ms so JS can prove the JS thread is
|
||||
// never blocked while native rendering runs.
|
||||
AsyncFunction("decodeEncodeAsync") Coroutine { srcPath: String, dstPath: String, quality: Int ->
|
||||
decodeEncodeAsync(srcPath, dstPath, quality)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun decodeEncodeAsync(srcPath: String, dstPath: String, quality: Int): Map<String, Int> {
|
||||
val timings = mutableMapOf<String, Int>()
|
||||
val t0 = System.nanoTime()
|
||||
withContext(Dispatchers.IO) {
|
||||
val src = File(srcPath)
|
||||
check(src.exists()) { "source file missing: $srcPath" }
|
||||
|
||||
val t1 = System.nanoTime()
|
||||
val bmp = BitmapFactory.decodeFile(srcPath)
|
||||
?: error("decode failed: $srcPath")
|
||||
timings["decodeMs"] = ((System.nanoTime() - t1) / 1_000_000).toInt()
|
||||
|
||||
val t2 = System.nanoTime()
|
||||
FileOutputStream(dstPath).use { fos ->
|
||||
check(bmp.compress(Bitmap.CompressFormat.JPEG, quality, fos)) { "jpeg encode failed" }
|
||||
}
|
||||
timings["encodeWriteMs"] = ((System.nanoTime() - t2) / 1_000_000).toInt()
|
||||
bmp.recycle()
|
||||
}
|
||||
timings["totalMs"] = ((System.nanoTime() - t0) / 1_000_000).toInt()
|
||||
return timings
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"platforms": ["android"],
|
||||
"android": {
|
||||
"modules": [
|
||||
"com.locphamtran.recipescamera.export.RecipescamExportModule"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import { requireNativeModule } from 'expo-modules-core';
|
||||
|
||||
export interface DecodeEncodeTimings {
|
||||
decodeMs: number;
|
||||
encodeWriteMs: number;
|
||||
totalMs: number;
|
||||
}
|
||||
|
||||
export interface RecipescamExportModule {
|
||||
/** Decode JPEG/PNG at srcPath (plain absolute path), re-encode JPEG q and write dstPath. All native, background. Returns stage timings in ms. */
|
||||
decodeEncodeAsync(srcPath: string, dstPath: string, quality: number): Promise<DecodeEncodeTimings>;
|
||||
}
|
||||
|
||||
export default requireNativeModule('RecipesCamExport') as RecipescamExportModule;
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": "recipescam-export",
|
||||
"version": "0.1.0",
|
||||
"description": "Native Kotlin photo export pipeline (decode/process/encode off the JS thread) for Recipescam.",
|
||||
"main": "index.ts",
|
||||
"license": "MIT",
|
||||
"private": true
|
||||
}
|
||||
Reference in New Issue
Block a user