78630d8a2d
The ultra-wide preview is a bare Camera2 surface: it never goes through Skia, so the software EV gain that carries exposure on the 1x preview had nothing to act on and quick EV / the LIGHT-EV slider did nothing at 0.5x. The lens itself now takes the bias, as CONTROL_AE_EXPOSURE_COMPENSATION on every preview request (session builder, metering rebuilds, and a setEv call the viewfinder pushes whenever the value changes). The still is left alone: capture() pins the compensation back to 0 and waits on an AE precapture trigger, so the file stays unbiased and the export applies its software gain exactly once, as it does for the 1x path. The hardware range clamps the bias where the lens offers less than the app's +/-3 EV.
56 lines
2.4 KiB
TypeScript
56 lines
2.4 KiB
TypeScript
import { requireNativeModule, requireNativeViewManager } from 'expo-modules-core';
|
|
import type { ComponentType } from 'react';
|
|
import type { ViewProps } from 'react-native';
|
|
|
|
export interface UltraWideModule {
|
|
/** Hidden back ultra-wide camera id ('' when this device has none). */
|
|
availableAsync(): Promise<string>;
|
|
/** Still capture from the ultra-wide lens; resolves to an absolute file path. */
|
|
captureAsync(): Promise<string>;
|
|
/**
|
|
* Tap-to-focus + spot metering on the ultra-wide lens, at a point in the
|
|
* preview view (React Native dp, same space as a touch's locationX/Y). The
|
|
* main CameraX session is stopped while 0.5x is live, so this is the only
|
|
* focus path that exists in that mode.
|
|
*/
|
|
focusAsync(x: number, y: number): Promise<void>;
|
|
/** Back to whole-frame continuous AF/AE. */
|
|
resetFocusAsync(): Promise<void>;
|
|
/**
|
|
* Hardware AE bias, in EV stops, for the live 0.5x preview. The 1x preview
|
|
* gets its EV from the Skia matrix gain, but this feed is a bare Camera2
|
|
* surface with no frame pipeline behind it, so the lens has to be biased.
|
|
* Preview only: the still is captured without the bias, exactly like the 1x
|
|
* path, so the export applies the software gain once and the file matches.
|
|
*/
|
|
setEvAsync(ev: number): Promise<void>;
|
|
/**
|
|
* Closes the native session and resolves once the HAL has really released the
|
|
* back camera slot. Leaving 0.5x must wait for this before switching the 1x
|
|
* preview back on: the two back lenses cannot open at once, so an early 1x
|
|
* open is refused and only retried ~520ms later.
|
|
*/
|
|
closeAsync(): Promise<void>;
|
|
/** Takes the ultra-wide again after a closeAsync whose handoff was cancelled. */
|
|
reopenAsync(): Promise<void>;
|
|
}
|
|
|
|
export interface UltraWidePreviewProps extends ViewProps {
|
|
/** Opens the camera while true, closes it on false/unmount. */
|
|
active: boolean;
|
|
/**
|
|
* Draws this view while true, session or not. While the session is open the
|
|
* first frame gates it (the previous session's last frame must not show), and
|
|
* after it closes the texture holds its last frame — which is what lets the
|
|
* caller keep the ultra-wide on screen until the main preview is live again.
|
|
*/
|
|
cover: boolean;
|
|
}
|
|
|
|
export const UltraWidePreview = requireNativeViewManager<UltraWidePreviewProps>(
|
|
'RecipesCamUltraWide',
|
|
'UltraWidePreviewView'
|
|
) as ComponentType<UltraWidePreviewProps>;
|
|
|
|
export default requireNativeModule('RecipesCamUltraWide') as UltraWideModule;
|