Skip to content

Commit

Permalink
Fix piece canvas scaling on HiDPI displays (#119)
Browse files Browse the repository at this point in the history
* Fix piece canvas scaling on HiDPI displays

* Adjust the piece canvas so that the squares span the entire available width
  • Loading branch information
jpovixwm authored Dec 5, 2023
1 parent a12cca4 commit f3a5e26
Showing 1 changed file with 38 additions and 30 deletions.
68 changes: 38 additions & 30 deletions src/components/piecescanvas.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@ import React, { useEffect, useMemo, useRef } from "react";
import { useResizeDetector } from "react-resize-detector";
import type { Torrent } from "../rpc/torrent";

const toDevicePixels = (cssPixels: number) => cssPixels * window.devicePixelRatio;

export function PiecesCanvas(props: { torrent: Torrent }) {
const { width, height, ref } = useResizeDetector({
const { width: cssWidth, height: cssHeight, ref } = useResizeDetector({
refreshMode: "throttle",
refreshRate: 1000,
});
Expand Down Expand Up @@ -56,28 +58,34 @@ export function PiecesCanvas(props: { torrent: Torrent }) {
}, [props.torrent]);

const [pieceSize, rows, cols] = useMemo(() => {
if (width === undefined || height === undefined) return [5, 1, 1];

const check = (size: number) => {
const cols = Math.floor(width / size);
const rows = Math.ceil(props.torrent.pieceCount / cols);
if (rows * size < height) return [rows, cols];
else return [-1, -1];
};

let right = 20;
let left = 0.0;
let mid = 10;
let rows = 1;

while (right - left > 0.05) {
[rows] = check(mid);
if (rows < 0) right = mid;
else left = mid;
mid = (right + left) * 0.5;
if (cssWidth === undefined || cssHeight === undefined) return [5, 1, 1];

const canvasWidth = Math.floor(toDevicePixels(cssWidth));
const canvasHeight = Math.floor(toDevicePixels(cssHeight));
const pieceCount = props.torrent.pieceCount;
const maxPieceSize = toDevicePixels(20);
const minColumns = Math.ceil(canvasWidth / maxPieceSize);

if (pieceCount < minColumns && canvasHeight >= maxPieceSize) return [maxPieceSize, 1, pieceCount];

/**
* The following code is based on https://math.stackexchange.com/a/2570649
*/

const ratio = canvasWidth / canvasHeight;
let cols = Math.max(
Math.ceil(Math.sqrt(pieceCount * ratio)),
minColumns,
);
let rows = Math.ceil(pieceCount / cols);

while (cols < rows * ratio) {
cols++;
rows = Math.ceil(pieceCount / cols);
}
return [left, ...check(left)];
}, [props.torrent.pieceCount, width, height]);

return [canvasWidth / cols, rows, cols];
}, [props.torrent.pieceCount, cssWidth, cssHeight]);

const pieces = useMemo(() => {
const bstr = window.atob(props.torrent.pieces);
Expand All @@ -95,7 +103,7 @@ export function PiecesCanvas(props: { torrent: Torrent }) {
const remainder = rows * cols - props.torrent.pieceCount;

ctx.beginPath();
ctx.lineWidth = pieceSize > 5 ? 1 : 0.5;
ctx.lineWidth = toDevicePixels(pieceSize > toDevicePixels(5) ? 1 : 0.5);
ctx.strokeStyle = "rgba(0, 0, 0, 0.5)";
for (let i = 0; i < rows; i++) {
ctx.moveTo(0, i * pieceSize);
Expand All @@ -112,7 +120,7 @@ export function PiecesCanvas(props: { torrent: Torrent }) {
ctx.lineTo(i * pieceSize, (rows - 1) * pieceSize);
}
ctx.stroke();
}, [gridRef, rows, cols, width, height, props.torrent.pieceCount, pieceSize]);
}, [gridRef, rows, cols, cssWidth, cssHeight, props.torrent.pieceCount, pieceSize]);

useEffect(() => {
const canvas = piecesRef.current as HTMLCanvasElement;
Expand All @@ -134,17 +142,17 @@ export function PiecesCanvas(props: { torrent: Torrent }) {
}
if (index >= props.torrent.pieceCount) break;
}
}, [piecesRef, rows, cols, pieceSize, pieces, wantedPieces, props.torrent.pieceCount]);
}, [piecesRef, rows, cols, cssWidth, cssHeight, pieceSize, pieces, wantedPieces, props.torrent.pieceCount]);

const dw = Math.floor(window.devicePixelRatio * (width ?? 1));
const dh = Math.floor(window.devicePixelRatio * (height ?? 1));
const canvasWidth = Math.floor(toDevicePixels(cssWidth ?? 1));
const canvasHeight = Math.floor(toDevicePixels(cssHeight ?? 1));
const style: CSSProperties = {
width: width ?? 1, height: height ?? 1, position: "absolute", top: 0, left: 0,
width: cssWidth ?? 1, height: cssHeight ?? 1, position: "absolute", top: 0, left: 0,
};
return (
<div ref={ref} style={{ width: "100%", height: "100%", position: "relative", overflow: "hidden" }}>
<canvas ref={piecesRef} width={dw} height={dh} style={style} />
<canvas ref={gridRef} width={dw} height={dh} style={style} />
<canvas ref={piecesRef} width={canvasWidth} height={canvasHeight} style={style} />
<canvas ref={gridRef} width={canvasWidth} height={canvasHeight} style={style} />
</div>
);
}

0 comments on commit f3a5e26

Please sign in to comment.