Cut 1.2.3 with a shutter that never waits on GPS
Bound the geotag fix with a 2 s deadline and prewarm it at launch, queue a shot pressed before the camera session binds and replay it, pass the options argument capturePhotoToFile requires, save through the native path so the media permission is not needed, and release the Skia surface and bitmap after every export. Store the JS bundle uncompressed and load the export pipeline on demand. Note in RELEASE_NOTES.md that none of this moved the tap threshold.
This commit is contained in:
+36
-15
@@ -18,12 +18,18 @@ export function rotateSkImage90(image: SkImage | null): SkImage | null {
|
||||
// the trip to the render thread.
|
||||
const surface = Skia.Surface.Make(h, w);
|
||||
if (!surface) return image; // keep the unrotated photo on allocation failure
|
||||
const canvas = surface.getCanvas();
|
||||
canvas.clear(Skia.Color('transparent'));
|
||||
canvas.translate(h, 0);
|
||||
canvas.rotate(90, 0, 0);
|
||||
canvas.drawImage(image, 0, 0);
|
||||
return surface.makeImageSnapshot() ?? image;
|
||||
try {
|
||||
const canvas = surface.getCanvas();
|
||||
canvas.clear(Skia.Color('transparent'));
|
||||
canvas.translate(h, 0);
|
||||
canvas.rotate(90, 0, 0);
|
||||
canvas.drawImage(image, 0, 0);
|
||||
return surface.makeImageSnapshot() ?? image;
|
||||
} finally {
|
||||
// The raster snapshot refcounts the pixels, so the surface can go now
|
||||
// instead of leaving one full-size copy behind per rotation.
|
||||
surface.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
// Fine rotation about the centre (the FRAME tab's STRAIGHTEN), keeping the
|
||||
@@ -44,13 +50,17 @@ export function rotateSkImageBy(image: SkImage | null, degrees: number): SkImage
|
||||
const k = Math.max((w * cos + h * sin) / w, (w * sin + h * cos) / h);
|
||||
const surface = Skia.Surface.Make(w, h);
|
||||
if (!surface) return image; // keep the unrotated photo on allocation failure
|
||||
const canvas = surface.getCanvas();
|
||||
canvas.clear(Skia.Color('transparent'));
|
||||
canvas.translate(w / 2, h / 2);
|
||||
canvas.rotate(degrees, 0, 0);
|
||||
canvas.scale(k, k);
|
||||
canvas.drawImage(image, -w / 2, -h / 2);
|
||||
return surface.makeImageSnapshot() ?? image;
|
||||
try {
|
||||
const canvas = surface.getCanvas();
|
||||
canvas.clear(Skia.Color('transparent'));
|
||||
canvas.translate(w / 2, h / 2);
|
||||
canvas.rotate(degrees, 0, 0);
|
||||
canvas.scale(k, k);
|
||||
canvas.drawImage(image, -w / 2, -h / 2);
|
||||
return surface.makeImageSnapshot() ?? image;
|
||||
} finally {
|
||||
surface.dispose(); // see rotateSkImage90
|
||||
}
|
||||
}
|
||||
|
||||
// The whole FRAME-tab photo orientation in one call: the quarter turn first
|
||||
@@ -62,7 +72,18 @@ export function applyPhotoRotation(
|
||||
straighten = 0
|
||||
): SkImage | null {
|
||||
let out = image;
|
||||
for (let i = 0; i < Math.round(quarter / 90) % 4; i++) out = rotateSkImage90(out);
|
||||
if (straighten) out = rotateSkImageBy(out, straighten);
|
||||
const turns = Math.round(quarter / 90) % 4;
|
||||
for (let i = 0; i < turns; i++) {
|
||||
const next = rotateSkImage90(out);
|
||||
// A 180/270 turn allocates one full-size copy per turn: the intermediate is
|
||||
// dead as soon as the next one exists (never the caller's input image).
|
||||
if (next && next !== out && i > 0) out.dispose();
|
||||
out = next ?? out;
|
||||
}
|
||||
if (straighten) {
|
||||
const s = rotateSkImageBy(out, straighten);
|
||||
if (s && s !== out && out !== image) out.dispose(); // see above
|
||||
out = s ?? out;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user