mirror of
https://github.com/basketikun/infinite-canvas.git
synced 2026-08-06 01:14:36 +08:00
feat(canvas): add image upscaling and super resolution features with UI enhancements
This commit is contained in:
@@ -14,6 +14,15 @@ export type ImageAngleTransform = {
|
||||
wideAngle: boolean;
|
||||
};
|
||||
|
||||
export type ImageUpscaleAlgorithm = "nearest" | "bilinear" | "high";
|
||||
|
||||
export const MAX_UPSCALE_LONG_EDGE = 4096;
|
||||
|
||||
export type ImageUpscaleParams = {
|
||||
targetLongEdge: number;
|
||||
algorithm: ImageUpscaleAlgorithm;
|
||||
};
|
||||
|
||||
export async function cropDataUrl(dataUrl: string, crop?: ImageCropRect) {
|
||||
const image = await loadImage(dataUrl);
|
||||
if (crop) {
|
||||
@@ -65,6 +74,19 @@ export async function transformAngleDataUrl(dataUrl: string, params: ImageAngleT
|
||||
return canvas.toDataURL("image/png");
|
||||
}
|
||||
|
||||
export async function upscaleDataUrl(dataUrl: string, params: ImageUpscaleParams) {
|
||||
const image = await loadImage(dataUrl);
|
||||
const { width, height } = resolveUpscaleSize(image.width, image.height, params.targetLongEdge);
|
||||
return params.algorithm === "high" ? drawStepUpscale(image, width, height) : drawResize(image, image.width, image.height, width, height, params.algorithm);
|
||||
}
|
||||
|
||||
export function resolveUpscaleSize(width: number, height: number, targetLongEdge: number) {
|
||||
const longEdge = Math.max(1, width, height);
|
||||
const target = Math.min(MAX_UPSCALE_LONG_EDGE, Math.max(1, Math.round(targetLongEdge)));
|
||||
const scale = target / longEdge;
|
||||
return { width: Math.max(1, Math.round(width * scale)), height: Math.max(1, Math.round(height * scale)) };
|
||||
}
|
||||
|
||||
function drawCrop(image: HTMLImageElement, sx: number, sy: number, sw: number, sh: number) {
|
||||
const canvas = document.createElement("canvas");
|
||||
canvas.width = Math.max(1, sw);
|
||||
@@ -75,6 +97,39 @@ function drawCrop(image: HTMLImageElement, sx: number, sy: number, sw: number, s
|
||||
return canvas.toDataURL("image/png");
|
||||
}
|
||||
|
||||
function drawStepUpscale(image: HTMLImageElement, width: number, height: number) {
|
||||
let source: CanvasImageSource = image;
|
||||
let sourceWidth = image.width;
|
||||
let sourceHeight = image.height;
|
||||
|
||||
while (sourceWidth * 2 < width && sourceHeight * 2 < height) {
|
||||
const nextWidth = sourceWidth * 2;
|
||||
const nextHeight = sourceHeight * 2;
|
||||
const next = drawResizeCanvas(source, sourceWidth, sourceHeight, nextWidth, nextHeight, "high");
|
||||
source = next;
|
||||
sourceWidth = nextWidth;
|
||||
sourceHeight = nextHeight;
|
||||
}
|
||||
|
||||
return drawResize(source, sourceWidth, sourceHeight, width, height, "high");
|
||||
}
|
||||
|
||||
function drawResize(source: CanvasImageSource, sourceWidth: number, sourceHeight: number, width: number, height: number, algorithm: ImageUpscaleAlgorithm) {
|
||||
return drawResizeCanvas(source, sourceWidth, sourceHeight, width, height, algorithm).toDataURL("image/png");
|
||||
}
|
||||
|
||||
function drawResizeCanvas(source: CanvasImageSource, sourceWidth: number, sourceHeight: number, width: number, height: number, algorithm: ImageUpscaleAlgorithm) {
|
||||
const canvas = document.createElement("canvas");
|
||||
canvas.width = width;
|
||||
canvas.height = height;
|
||||
const context = canvas.getContext("2d");
|
||||
if (!context) return canvas;
|
||||
context.imageSmoothingEnabled = algorithm !== "nearest";
|
||||
context.imageSmoothingQuality = algorithm === "bilinear" ? "medium" : "high";
|
||||
context.drawImage(source, 0, 0, sourceWidth, sourceHeight, 0, 0, width, height);
|
||||
return canvas;
|
||||
}
|
||||
|
||||
function loadImage(dataUrl: string) {
|
||||
return new Promise<HTMLImageElement>((resolve) => {
|
||||
const image = new Image();
|
||||
|
||||
Reference in New Issue
Block a user