- Add ImageOpsVignette, ImageOpsChromaticAberration, ImageOpsBloom, and ImageOpsLensArtifacts for common lens/compositing effects. - Add ImageOpsDefocus: depth-driven defocus/bokeh with shaped aperture kernels (circle/hexagon/octagon/custom) and highlight bloom. - Add input_space (srgb/linear) to ImageOpsColorAjust so scene-linear/HDR sources aren't gamma-encoded twice. - Add image_b + compare_mode/wipe_position to ImageOpsPreview for a real A/B comparison, plus a separate live on-demand A/B toggle while editing. - Redesign the live-preview HUD: unified pill-style badges, frame counter now shows the real total and hides for single-frame sources, and the Output overlay only appears for Pad Out with the real pixel size. - Align README with the current node set and fix stale blend-mode docs.
54 lines
1.8 KiB
JavaScript
54 lines
1.8 KiB
JavaScript
function clampPreviewZoom(zoom) {
|
|
return Math.max(0.35, Math.min(6, zoom));
|
|
}
|
|
function screenToWorld(x, y, zoom, panX, panY, canvasSize) {
|
|
const half = canvasSize / 2;
|
|
return {
|
|
x: (x - panX - half) / zoom + half,
|
|
y: (y - panY - half) / zoom + half
|
|
};
|
|
}
|
|
function getFitPlacement(width, height, sourceWidth, sourceHeight) {
|
|
const safeWidth = Math.max(1, sourceWidth);
|
|
const safeHeight = Math.max(1, sourceHeight);
|
|
const scale = Math.min(width / safeWidth, height / safeHeight);
|
|
const drawWidth = Math.max(1, Math.floor(safeWidth * scale));
|
|
const drawHeight = Math.max(1, Math.floor(safeHeight * scale));
|
|
const dx = Math.floor((width - drawWidth) / 2);
|
|
const dy = Math.floor((height - drawHeight) / 2);
|
|
return { dx, dy, drawWidth, drawHeight };
|
|
}
|
|
function drawFitSource(ctx, width, height, source, sourceWidth, sourceHeight, fit) {
|
|
const placement = fit ?? getFitPlacement(width, height, sourceWidth, sourceHeight);
|
|
ctx.clearRect(0, 0, width, height);
|
|
ctx.imageSmoothingEnabled = true;
|
|
ctx.drawImage(source, placement.dx, placement.dy, placement.drawWidth, placement.drawHeight);
|
|
}
|
|
function drawOutputBounds(ctx, fit) {
|
|
ctx.save();
|
|
ctx.strokeStyle = "rgba(251,191,36,0.55)";
|
|
ctx.lineWidth = 1;
|
|
ctx.setLineDash([6, 4]);
|
|
ctx.strokeRect(fit.dx + 0.5, fit.dy + 0.5, fit.drawWidth, fit.drawHeight);
|
|
ctx.setLineDash([]);
|
|
ctx.restore();
|
|
}
|
|
function getCanvasPointer(canvas, event) {
|
|
const rect = canvas.getBoundingClientRect();
|
|
if (rect.width < 1 || rect.height < 1) return { x: 0, y: 0 };
|
|
const scaleX = canvas.width / rect.width;
|
|
const scaleY = canvas.height / rect.height;
|
|
return {
|
|
x: (event.clientX - rect.left) * scaleX,
|
|
y: (event.clientY - rect.top) * scaleY
|
|
};
|
|
}
|
|
export {
|
|
clampPreviewZoom,
|
|
drawFitSource,
|
|
drawOutputBounds,
|
|
getCanvasPointer,
|
|
getFitPlacement,
|
|
screenToWorld
|
|
};
|