Skip to content

Commit

Permalink
Single canvas (v0.0.1-alpha.3) (#16)
Browse files Browse the repository at this point in the history
  • Loading branch information
satelllte authored Nov 14, 2023
1 parent 13d39b2 commit 712424e
Show file tree
Hide file tree
Showing 6 changed files with 113 additions and 88 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "displacementx",
"version": "0.0.1-alpha.2",
"version": "0.0.1-alpha.3",
"scripts": {
"dev": "next dev",
"build": "next build",
Expand Down
78 changes: 53 additions & 25 deletions src/components/pages/Generator/CanvasSection/CanvasSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import {saveImage} from './utils/saveImage';
import {draw} from './utils/draw';
import {Switch} from '@/components/ui/Switch';
import {clearCanvas} from './utils/clearCanvas';
import {drawNormal} from './utils/drawNormal';
import {getCanvasDimensions} from './utils/getCanvasDimensions';

export function CanvasSection() {
const [is8k, setIs8k] = useState<boolean>(false);
Expand All @@ -16,17 +18,18 @@ export function CanvasSection() {

const [isPristine, setIsPristine] = useState<boolean>(true);
const [isRendering, setIsRendering] = useState<boolean>(false);
const [isNormalPreview, setIsNormalPreview] = useState<boolean>(false);
const [renderTimeMs, setRenderTimeMs] = useState<number | undefined>();

const canvasRef = useRef<HTMLCanvasElement>(null);
const canvasNormalRef = useRef<HTMLCanvasElement>(null);
const canvasOriginalPreviewDataUrl = useRef<string | undefined>(undefined);

const render = () => {
setIsPristine(false);
setIsRendering(true);
setIsNormalPreview(false);

const ctx2d = getCtx2d(canvasRef);
const ctx2dNormal = getCtx2d(canvasNormalRef);

const {
iterations,
Expand Down Expand Up @@ -61,7 +64,6 @@ export function CanvasSection() {

draw({
ctx2d,
ctx2dNormal,
props: {
iterations,
backgroundBrightness,
Expand Down Expand Up @@ -116,41 +118,65 @@ export function CanvasSection() {
saveImage({canvas, fileName: 'displacementx-gen'});
};

const downloadNormal = () => {
const canvas = canvasNormalRef.current;
if (!canvas) return;

saveImage({canvas, fileName: 'displacementx-gen-normal'});
};

const onIs8kChange = (is8k: boolean) => {
const ctx2d = getCtx2d(canvasRef);
const ctx2dNormal = getCtx2d(canvasNormalRef);

clearCanvas(ctx2d);
clearCanvas(ctx2dNormal);

setIsPristine(true);
setIsNormalPreview(false);
setRenderTimeMs(undefined);
setIs8k(is8k);
};

const toggleNormalPreview = () => {
const isNormalPreviewNew = !isNormalPreview;
const renderTimeStartMs: number = performance.now();
setIsRendering(true);

const updateCanvas = () => {
const ctx2d = getCtx2d(canvasRef);

if (isNormalPreviewNew) {
// Draw normal preview
canvasOriginalPreviewDataUrl.current = ctx2d.canvas.toDataURL();
drawNormal({ctx2d, ctx2dNormal: ctx2d});
} else {
// Restore original preview
const dataUrl = canvasOriginalPreviewDataUrl.current;
if (dataUrl) {
const {w, h} = getCanvasDimensions(ctx2d);
const img = new Image();
img.src = dataUrl;
img.onload = () => {
ctx2d.clearRect(0, 0, w, h);
ctx2d.drawImage(img, 0, 0, w, h);
canvasOriginalPreviewDataUrl.current = undefined;
};
}
}
};

// Put a small timeout to allow the UI to update before canvas takes the main thread over
setTimeout(() => {
updateCanvas();
setIsNormalPreview(isNormalPreviewNew);
setIsRendering(false);
setRenderTimeMs(performance.now() - renderTimeStartMs);
}, 20);
};

return (
<section>
<SectionTitle>Output</SectionTitle>
<div className='flex gap-1'>
<Switch isOn={is8k} setIsOn={onIs8kChange} labels={['4K', '8K']} />
<div className='flex gap-1 pt-3'>
<Canvas
canvasRef={canvasRef}
width={width}
height={height}
isRendering={isRendering}
/>
<Canvas
canvasRef={canvasNormalRef}
width={width}
height={height}
isRendering={isRendering}
/>
</div>
<div>
<output className='text-sm text-gray-400'>
Expand All @@ -162,18 +188,20 @@ export function CanvasSection() {
</span>
</output>
</div>
<div className='pt-1'>
<Switch isOn={is8k} setIsOn={onIs8kChange} labels={['4K', '8K']} />
</div>
<div className='flex flex-wrap gap-1 pt-3'>
<div className='flex flex-wrap gap-1 pt-2'>
<Button disabled={isRendering} onClick={render}>
Render
</Button>
<Button disabled={isPristine || isRendering} onClick={download}>
Download
</Button>
<Button disabled={isPristine || isRendering} onClick={downloadNormal}>
Download (normal)
</div>
<div className='flex flex-wrap gap-1 pt-2'>
<Button
disabled={isPristine || isRendering}
onClick={toggleNormalPreview}
>
Preview {isNormalPreview ? 'original' : 'normal'}
</Button>
</div>
</section>
Expand Down
58 changes: 0 additions & 58 deletions src/components/pages/Generator/CanvasSection/utils/draw.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import {clearCanvas} from './clearCanvas';

export const draw = ({
ctx2d,
ctx2dNormal,
onEnd,
props: {
iterations,
Expand Down Expand Up @@ -41,7 +40,6 @@ export const draw = ({
},
}: {
ctx2d: CanvasRenderingContext2D;
ctx2dNormal: CanvasRenderingContext2D;
onEnd: (renderTimeMs: number) => void;
props: {
iterations: number;
Expand Down Expand Up @@ -77,7 +75,6 @@ export const draw = ({
const renderStartTimeMs = performance.now();

clearCanvas(ctx2d);
clearCanvas(ctx2dNormal);

drawBackground({ctx2d, backgroundBrightness});

Expand Down Expand Up @@ -142,7 +139,6 @@ export const draw = ({
}
},
onEnd() {
drawNormal({ctx2d, ctx2dNormal});
const renderTimeMs = performance.now() - renderStartTimeMs;
onEnd(renderTimeMs);
},
Expand Down Expand Up @@ -336,57 +332,3 @@ const drawLines = ({
ctx2d.fillRect(x, 0, thickness, h);
}
};

/**
* Draws the normal map.
* - "ctx2d" is the canvas context to read from.
* - "ctx2dNormal" is the canvas context to write to.
*/
const drawNormal = ({
ctx2d,
ctx2dNormal,
}: {
ctx2d: CanvasRenderingContext2D;
ctx2dNormal: CanvasRenderingContext2D;
}): void => {
const {w, h} = getCanvasDimensions(ctx2d);

const source = ctx2d.getImageData(0, 0, w, h);
const destination = ctx2dNormal.createImageData(w, h);

for (let i = 0, l = w * h * 4; i < l; i += 4) {
let x1;
let x2;
let y1;
let y2;

if (i % (w * 4) === 0) {
x1 = source.data[i];
x2 = source.data[i + 4];
} else if (i % (w * 4) === (w - 1) * 4) {
x1 = source.data[i - 4];
x2 = source.data[i];
} else {
x1 = source.data[i - 4];
x2 = source.data[i + 4];
}

if (i < h * 4) {
y1 = source.data[i];
y2 = source.data[i + h * 4];
} else if (i > h * (h - 1) * 4) {
y1 = source.data[i - h * 4];
y2 = source.data[i];
} else {
y1 = source.data[i - h * 4];
y2 = source.data[i + h * 4];
}

destination.data[i] = x1 - x2 + 127;
destination.data[i + 1] = y1 - y2 + 127;
destination.data[i + 2] = 255;
destination.data[i + 3] = 255;
}

ctx2dNormal.putImageData(destination, 0, 0);
};
55 changes: 55 additions & 0 deletions src/components/pages/Generator/CanvasSection/utils/drawNormal.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import {getCanvasDimensions} from './getCanvasDimensions';

/**
* Draws the normal map.
* - "ctx2d" is the canvas context to read from.
* - "ctx2dNormal" is the canvas context to write to.
*/
export const drawNormal = ({
ctx2d,
ctx2dNormal,
}: {
ctx2d: CanvasRenderingContext2D;
ctx2dNormal: CanvasRenderingContext2D;
}): void => {
const {w, h} = getCanvasDimensions(ctx2d);

const source = ctx2d.getImageData(0, 0, w, h);
const destination = ctx2dNormal.createImageData(w, h);

for (let i = 0, l = w * h * 4; i < l; i += 4) {
let x1;
let x2;
let y1;
let y2;

if (i % (w * 4) === 0) {
x1 = source.data[i];
x2 = source.data[i + 4];
} else if (i % (w * 4) === (w - 1) * 4) {
x1 = source.data[i - 4];
x2 = source.data[i];
} else {
x1 = source.data[i - 4];
x2 = source.data[i + 4];
}

if (i < h * 4) {
y1 = source.data[i];
y2 = source.data[i + h * 4];
} else if (i > h * (h - 1) * 4) {
y1 = source.data[i - h * 4];
y2 = source.data[i];
} else {
y1 = source.data[i - h * 4];
y2 = source.data[i + h * 4];
}

destination.data[i] = x1 - x2 + 127;
destination.data[i + 1] = y1 - y2 + 127;
destination.data[i + 2] = 255;
destination.data[i + 3] = 255;
}

ctx2dNormal.putImageData(destination, 0, 0);
};
6 changes: 3 additions & 3 deletions src/components/pages/Generator/Generator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,11 @@ export function Generator() {
/>
</Link>
</header>
<main className='flex flex-col gap-8 px-4 pb-4 md:flex-row md:gap-4'>
<div className='relative flex-[3]'>
<main className='flex flex-col gap-8 px-4 pb-4 sm:flex-row sm:gap-4'>
<div className='relative flex-1'>
<CanvasSection />
</div>
<div className='relative flex-[2]'>
<div className='relative flex-1 lg:flex-[2]'>
<SettingsSection />
</div>
</main>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export function Group(props: GroupProps) {
</div>
<div
className={clsx(
'grid grid-cols-2 gap-2 border-l border-white pl-2',
'grid grid-cols-1 gap-2 border-l border-white pl-2 lg:grid-cols-3',
props.withSwitch &&
!props.enabled &&
'pointer-events-none opacity-50',
Expand Down

0 comments on commit 712424e

Please sign in to comment.