web: filter the straighten draw so a rotated edge stops staircasing
The FRAME tab's fine rotation drew the photo through canvas.rotate() + canvas.scale() with a plain drawImage, which CanvasKit samples with nearest: the edge landed on the same pixel in every row, so a rotated edge came out as 1px steps every 1/tan(angle) rows. Measured on the 30deg export of a hard black/white edge: 42.3% of rows repeated the previous row's edge position, the step across the edge was 252.9 of 255, and there were no intermediate pixels at all. Only the *Options/*Cubic call shapes take a sampling option, and drawImageRectOptions exists in RN Skia too, so the shared renderer can use it unchanged. The same export now moves the edge in every row (0.2% of rows repeat, 0.35 intermediate pixels per row) and its edge step drops to 222. Cost: the filtered draw takes 0.35s against 0.24s for the 1600px preview copy and 2.0s against 1.4s for a 12MP photo, once per render. Preview and export share the function, so both change together.
This commit is contained in:
@@ -56,7 +56,20 @@ export function rotateSkImageBy(image: SkImage | null, degrees: number): SkImage
|
||||
canvas.translate(w / 2, h / 2);
|
||||
canvas.rotate(degrees, 0, 0);
|
||||
canvas.scale(k, k);
|
||||
canvas.drawImage(image, -w / 2, -h / 2);
|
||||
// A plain drawImage through a rotation is nearest-neighbour on CanvasKit: the
|
||||
// edge lands on the same pixel in every row and the result staircases
|
||||
// (measured: residual std of the edge position 0.289 vs 0.048 filtered, see
|
||||
// straighten-sampling-probe.cjs). Only the *Options/*Cubic call shapes take a
|
||||
// sampling option, and `drawImageRectOptions` exists in RN Skia too, so the
|
||||
// shared renderer stays portable.
|
||||
canvas.drawImageRectOptions(
|
||||
image,
|
||||
Skia.XYWHRect(0, 0, w, h),
|
||||
Skia.XYWHRect(-w / 2, -h / 2, w, h),
|
||||
Skia.FilterMode.Linear,
|
||||
Skia.MipmapMode.None,
|
||||
null
|
||||
);
|
||||
return surface.makeImageSnapshot() ?? image;
|
||||
} finally {
|
||||
surface.dispose(); // see rotateSkImage90
|
||||
|
||||
Reference in New Issue
Block a user