diff --git a/App.tsx b/App.tsx index f8dd3cc..ec9780c 100644 --- a/App.tsx +++ b/App.tsx @@ -775,6 +775,12 @@ export default function App() { setIsProcessing(true); Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Medium); + // Plain library frames export the photo at its own aspect (the ratio band is + // a camera-composition tool); polaroid/wall keep their framed ratio crop. + const libraryPlainFrame = + selectedFrame === 'none' || selectedFrame === 'classic-white' || selectedFrame === 'cinematic'; + const libraryAspect = aspectRatio === 'full' || libraryPlainFrame ? undefined : aspectRatio; + const result = NATIVE_EXPORT ? await processAndExportPhotoNative( libraryImageUri, @@ -787,9 +793,10 @@ export default function App() { selectedFrame, useGeotag, gpsInfo, - // Library export follows the same composition ratio as the viewfinder band. + // Library export keeps the on-screen composition: plain frames export + // the photo as seen (own aspect), polaroid/wall keep their ratio crop. { - aspect: aspectRatio === 'full' ? undefined : aspectRatio, + aspect: libraryAspect, watermark: wmForExport(), } ) @@ -804,9 +811,10 @@ export default function App() { selectedFrame, useGeotag, gpsInfo, - // Library export follows the same composition ratio as the viewfinder band. + // Library export keeps the on-screen composition: plain frames export + // the photo as seen (own aspect), polaroid/wall keep their ratio crop. { - aspect: aspectRatio === 'full' ? undefined : aspectRatio, + aspect: libraryAspect, watermark: wmForExport(), // Pinch/drag inside a polaroid card window or wall-frame opening: // save the same crop the preview showed (null = centered 1x). diff --git a/src/components/Viewfinder.tsx b/src/components/Viewfinder.tsx index e0d5b24..0798b16 100644 --- a/src/components/Viewfinder.tsx +++ b/src/components/Viewfinder.tsx @@ -216,6 +216,7 @@ const Viewfinder = forwardRef(function Viewfi // every consumer below degrades to the old fullscreen behavior untouched. const ratioWH = aspectRatio === 'full' ? null : ASPECT_RATIO_W_H[aspectRatio]; const regionBand = + mode === 'camera' && ratioWH !== null && (selectedFrame === 'none' || selectedFrame === 'classic-white' || selectedFrame === 'cinematic'); const region = useMemo(() => { @@ -936,6 +937,17 @@ const Viewfinder = forwardRef(function Viewfi const dh = h * s; return { dx: (vw - dw) / 2, dy: (vh - dh) / 2, dw, dh }; }, [skiaImage, vw, vh]); + // Photo display rect + draw fit in the plain library viewer: the full photo + // is contain-fitted to the screen (fill into its own aspect rect — no + // distortion, identical to the old fullscreen contain). The ratio band only + // shapes the CAMERA composition; a library export is the photo as framed. + const dispRect = regionBand + ? { dx: region.x, dy: region.y, dw: region.w, dh: region.h } + : imageFitRect; + const dispFit: 'cover' | 'fill' = regionBand ? 'cover' : 'fill'; + // Rect the frame border / cinematic bars / grain hug: the ratio band on the + // camera, the contain rect of the loaded library photo, fullscreen otherwise. + const frameRect = mode === 'library' && dispRect ? dispRect : region; // Polaroid print geometry (JS/overlay space) for the CURRENT mode. The card // always fills/holds to the screen container; the photo WINDOW keeps the // *composition* aspect: with a ratio active that ratio (window content is @@ -1323,10 +1335,10 @@ const Viewfinder = forwardRef(function Viewfi }; })(); - // Border/bars scale with the composition region so framed exports stay - // proportional to the cropped photo (region == fullscreen when no ratio). - const borderWidth = Math.min(region.w, region.h) * 0.05; - const barHeight = region.h * 0.12; + // Border/bars scale with the visible photo rect so a framed export stays + // proportional to the picture (band on the camera, contain rect in library). + const borderWidth = Math.min(frameRect.w, frameRect.h) * 0.05; + const barHeight = frameRect.h * 0.12; const renderFrameOverlay = () => { if (selectedFrame === 'none') return null; @@ -1395,24 +1407,24 @@ const Viewfinder = forwardRef(function Viewfi } if (selectedFrame === 'classic-white') { - // Border drawn on the composition region (region == fullscreen when no - // ratio band), so it always sits at the photo's edge. + // Border drawn on the visible photo rect (frameRect), so it always sits + // at the picture's edge — including the letterboxed library viewer. return ( - - - - + + + + ); } if (selectedFrame === 'cinematic') { - // Letterbox bars hug the composition region's top/bottom edges. + // Letterbox bars hug the visible photo rect's top/bottom edges. return ( - - + + ); } @@ -1706,14 +1718,6 @@ const Viewfinder = forwardRef(function Viewfi toneParams[0] !== 0 || toneParams[1] !== 0 || toneParams[2] !== 0 || toneParams[3] !== 0; const cinemaOn = cinemaEffect != null && !!cinemaParams; const useShaderPass = !!imageFitRect && ((toneEffect != null && toneOn) || cinemaOn); - // Photo display rect + draw fit in the plain library viewer: with a ratio - // band the photo is center-cropped (cover) into the composition region; - // otherwise the full photo is contain-fitted to the screen (fill into its - // own aspect rect — no distortion, identical to the old fullscreen contain). - const dispRect = regionBand - ? { dx: region.x, dy: region.y, dw: region.w, dh: region.h } - : imageFitRect; - const dispFit: 'cover' | 'fill' = regionBand ? 'cover' : 'fill'; // RETRO POLAROID (library): the whole picture is contain-fitted into the // card's photo window — nothing is masked or cropped, and the card is @@ -1943,9 +1947,9 @@ const Viewfinder = forwardRef(function Viewfi {grainOpacity > 0 && noiseEffect && ( - {/* Grain covers the whole photo area — the composition region - under a ratio band, the full screen otherwise. */} - + {/* Grain covers the whole photo area — the contain rect of the + library photo, the ratio band on the camera. */} +