Files
RecipesCam/.kilo/plans/1784212500972-vision-camera-skia-frame-processor.md
T
3dtours 3312facd82 docs: write down the camera port and the live-view plan
CAMERA_PORT_ANDROID.md lists only what the web build has that Android does
not, one row per gap, with the web file to read next to the Android one.
The plan alongside it is the sketch for a live camera with a Skia filter
on the frame thread.
2026-09-28 17:36:30 +07:00

4.0 KiB

Kế hoạch: Real-time Skia filter trên camera live view dùng vision-camera v4

Vấn đề

expo-camera render camera preview lên native surface (PreviewView/SurfaceView) không thể intercept bằng Skia. Periodic capture bị từ chối vì mất live view.

Giải pháp

Dùng react-native-vision-camera@4.7.3 với useSkiaFrameProcessor — frame processor chạy native thread, mỗi frame camera được vẽ qua Skia với ColorMatrix trước khi hiển thị.

Các bước thực hiện

Bước 1: Cài react-native-vision-camera@4.7.3

npm install react-native-vision-camera@4.7.3
  • v4.7.3 là latest v4, có export useSkiaFrameProcessor và frameProcessor prop trên Camera
  • v5+ đã xoá Skia integration, không dùng được

Bước 2: Thêm Expo Config Plugin

Sửa app.json:

  • Thay expo-camera plugin bằng react-native-vision-camera plugin (v4 có plugin riêng)
  • Thêm withEnableFrameProcessorsAndroid để bật frame processor trên Android
"plugins": [
  "react-native-vision-camera",
  [
    "expo-camera",
    {
      "cameraPermission": "..."
    }
  ]
]

Cần kiểm tra: v4 có expo plugin riêng (withVisionCamera) hay dùng thẳng tên package.

Bước 3: Cập nhật App.tsx

  • Import useCameraPermission từ react-native-vision-camera thay vì useCameraPermissions từ expo-camera
  • Props: hasPermission, requestPermission

Bước 4: Viết lại Viewfinder.tsx

Component mới dùng:

  • Camera từ vision-camera với prop frameProcessor={frameProcessor}
  • useSkiaFrameProcessor hook — worklet chạy native, mỗi frame:
    1. Tạo Skia.Paint() với Skia.ColorFilter.MakeMatrix(colorMatrix)
    2. Gọi frame.render(paint) — vẽ frame qua color filter
  • colorMatrix là SharedValue từ react-native-reanimated (để worklet đọc được)
  • Grain noise, frame border, GPS watermark: giữ nguyên overlay React Native / Skia Canvas trên top
const App = () => {
  const frameProcessor = useSkiaFrameProcessor((frame) => {
    'worklet';
    const paint = Skia.Paint();
    paint.setColorFilter(Skia.ColorFilter.MakeMatrix(colorMatrixSV.value));
    frame.render(paint);
  }, [colorMatrixSV]);
  
  return (
    <Camera
      style={StyleSheet.absoluteFill}
      device={device}
      isActive={true}
      frameProcessor={frameProcessor}
    />
  );
};

Bước 5: Loại bỏ expo-camera khỏi code

  • CameraView → Camera (vision-camera)
  • Xoá imports không dùng
  • Giữ expo-camera trong package.json (không gỡ, tránh lỗi autolinking)

Bước 6: Re-prebuild & build APK

rm -rf android
npx expo prebuild --platform android
echo "sdk.dir=/home/locpham/android-sdk" > android/local.properties
cd android && ./gradlew assembleDebug

(Không chạy clean — tránh lỗi CMake codegen paths)

API đã xác nhận (v4.7.3)

Export Path
Camera react-native-vision-camera
useCameraDevice react-native-vision-camera
useCameraPermission react-native-vision-camera
useSkiaFrameProcessor react-native-vision-camera
SkiaCameraCanvas react-native-vision-camera
Expo plugin (withVisionCamera) lib/module/expo-plugin/withVisionCamera

Rủi ro & Xử lý

Rủi ro Xử lý
react-native-worklets-core conflict Đã có sẵn qua react-native-reanimated dependency
CameraX dependency (NoClassDefFoundError) v4 dùng CameraX không phải Nitro — build an toàn hơn v5
CMake codegen paths lỗi sau clean Không dùng ./gradlew clean, dùng --rerun-tasks nếu cần
Frame processor không chạy trên Android Bật flag qua Expo plugin: withEnableFrameProcessorsAndroid

Kiểm thử

  1. Build APK thành công
  2. Camera live view hiển thị với filter:
    • B&W/monochrome → true grayscale
    • Saturation, temp, tint, exposure thay đổi real-time
  3. Grain noise, frame border, GPS watermark vẫn hoạt động
  4. Library mode không bị ảnh hưởng