Skip to content

Commit

Permalink
fix: gbc screen resolution (#40)
Browse files Browse the repository at this point in the history
* fix: make canvas fade generic regardless of aspect ratio

* fix: gbc screen resolution shift when run

- initial pass at fixing gbc screen resoution shifts

- emulator controls canvas, let it dictate the canvas resolution and height

- bump wasm files

* refactor: rename to fade.ts

* feat: contain different screen resolutions

-  tentative attempt at containing different screen resolutions in the screenwrapper

- avoids layout jerk

- todo: refactor to scale canvas on resize

* refactor: extract layout to a hook

* refactor: use css to maintain canvas aspect ratio

- use object fit contain to keep the canvas aspect ratio contained regardless of the parent aspect ratio

- remove unnecessary code

* feat: ensure wasm files are current

---------

Co-authored-by: Nick VanCise
  • Loading branch information
thenick775 authored Dec 29, 2023
1 parent 1eb92e5 commit 3e24bec
Show file tree
Hide file tree
Showing 10 changed files with 80 additions and 52 deletions.
2 changes: 0 additions & 2 deletions gbajs3/src/components/screen/consts.tsx

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import { domToCanvas } from 'modern-screenshot';

import { renderCanvasWidth, renderCanvasHeight } from './consts.tsx';

// uses a webgl canvas context,
// clears the canvas to all black immediately
const clearWebGlCanvas = (canvas: HTMLCanvasElement) => {
Expand All @@ -21,25 +19,30 @@ const lcdFade2d = (canvas: HTMLCanvasElement) => {

if (!context) return;

const pixelData = context.getImageData(
0,
0,
renderCanvasWidth,
renderCanvasHeight
);
const videoWidth = canvas.width;
const videoHeight = canvas.height;
const halfVideoWidth = videoWidth / 2;
const halfVideoHeight = videoHeight / 2;

const pixelData = context.getImageData(0, 0, videoWidth, videoHeight);

const drawInterval = setInterval(() => {
drawCount++;

for (let y = 0; y < renderCanvasHeight; ++y) {
for (let x = 0; x < renderCanvasWidth; ++x) {
const xDiff = Math.abs(x - 120);
const yDiff = Math.abs(y - 80) * 0.8;
const xFactor = (120 - drawCount - xDiff) / 120;
for (let y = 0; y < videoHeight; ++y) {
for (let x = 0; x < videoWidth; ++x) {
const xDiff = Math.abs(x - halfVideoWidth);
const yDiff = Math.abs(y - halfVideoHeight) * 0.8;
const xFactor = (halfVideoWidth - drawCount - xDiff) / halfVideoWidth;
const yFactor =
(80 - drawCount - (y & 1) * 10 - yDiff + Math.pow(xDiff, 1 / 2)) / 80;
(halfVideoHeight -
drawCount -
(y & 1) * 10 -
yDiff +
Math.pow(xDiff, 1 / 2)) /
halfVideoHeight;

pixelData.data[(x + y * renderCanvasWidth) * 4 + 3] *=
pixelData.data[(x + y * videoWidth) * 4 + 3] *=
Math.pow(xFactor, 1 / 3) * Math.pow(yFactor, 1 / 2);
}
}
Expand Down Expand Up @@ -75,6 +78,9 @@ export const fadeCanvas = (
copyCanvas.style.position = 'absolute';
copyCanvas.style.top = '0';
copyCanvas.style.left = '0';
copyCanvas.style.right = '0';
copyCanvas.style.margin = '0 auto';
copyCanvas.style.objectFit = 'contain';
canvas.parentElement?.appendChild(copyCanvas);

lcdFade2d(copyCanvas);
Expand Down
20 changes: 13 additions & 7 deletions gbajs3/src/components/screen/screen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { useCallback, useContext } from 'react';
import { Rnd } from 'react-rnd';
import { styled, useTheme } from 'styled-components';

import { renderCanvasWidth, renderCanvasHeight } from './consts.tsx';
import { EmulatorContext } from '../../context/emulator/emulator.tsx';
import { LayoutContext } from '../../context/layout/layout.tsx';
import { NavigationMenuWidth } from '../navigation-menu/consts.tsx';
Expand All @@ -13,14 +12,21 @@ type RenderCanvasProps = {
$pixelated?: boolean;
};

const defaultGBACanvasWidth = 240;
const defaultGBACanvasHeight = 160;

const RenderCanvas = styled.canvas<RenderCanvasProps>`
background-color: ${({ theme }) => theme.screenLight};
background-color: ${({ theme }) => theme.pureBlack};
image-rendering: -webkit-optimize-contrast;
image-rendering: -moz-crisp-edges;
image-rendering: -o-crisp-edges;
width: 100%;
height: 100%;
display: block;
margin: 0 auto;
max-height: 100%;
max-width: 100%;
object-fit: contain;
${({ $pixelated = false }) =>
$pixelated &&
Expand All @@ -29,7 +35,8 @@ const RenderCanvas = styled.canvas<RenderCanvasProps>`
`;

const ScreenWrapper = styled(Rnd)`
border: solid 1px black;
background-color: ${({ theme }) => theme.pureBlack};
border: solid 1px ${({ theme }) => theme.pureBlack};
overflow: visible;
width: 100dvw;
Expand Down Expand Up @@ -71,7 +78,7 @@ export const Screen = () => {
};
const defaultSize = {
width: isLargerThanPhone ? '' : '100dvw',
height: 'auto'
height: isLargerThanPhone ? 'auto' : '66.67dvw'
};

const position = layouts?.screen?.position ?? defaultPosition;
Expand Down Expand Up @@ -109,12 +116,11 @@ export const Screen = () => {
position: { ...position }
});
}}
lockAspectRatio={3 / 2}
>
<RenderCanvas
ref={refSetCanvas}
width={renderCanvasWidth}
height={renderCanvasHeight}
width={defaultGBACanvasWidth}
height={defaultGBACanvasHeight}
$pixelated
/>
</ScreenWrapper>
Expand Down
10 changes: 8 additions & 2 deletions gbajs3/src/context/emulator/emulator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ import {
emulatorKeyBindingsLocalStorageKey,
emulatorVolumeLocalStorageKey
} from './consts.tsx';
import { fadeCanvas } from '../../components/screen/fade.tsx';
import { fadeCanvas } from '../../components/screen/fade.ts';
import { useEmulator } from '../../hooks/use-emulator.tsx';
import { useLayouts } from '../../hooks/use-layouts.tsx';

import type {
GBAEmulator,
Expand Down Expand Up @@ -51,6 +52,7 @@ export const EmulatorProvider = ({ children }: EmulatorProviderProps) => {
const [isEmulatorRunning, setIsEmulatorRunning] = useState(false);
const [areItemsDraggable, setAreItemsDraggable] = useState(false);
const [areItemsResizable, setAreItemsResizable] = useState(false);
const { hasSetLayout, clearLayouts } = useLayouts();
const [currentEmulatorVolume] = useLocalStorage(
emulatorVolumeLocalStorageKey,
1
Expand All @@ -70,6 +72,8 @@ export const EmulatorProvider = ({ children }: EmulatorProviderProps) => {

if (currentKeyBindings) emulator.remapKeyBindings(currentKeyBindings);

if (isSuccessfulRun && !hasSetLayout) clearLayouts();

return isSuccessfulRun;
};

Expand Down Expand Up @@ -106,7 +110,9 @@ export const EmulatorProvider = ({ children }: EmulatorProviderProps) => {
isEmulatorRunning,
canvas,
currentEmulatorVolume,
currentKeyBindings
currentKeyBindings,
clearLayouts,
hasSetLayout
]);

return (
Expand Down
27 changes: 4 additions & 23 deletions gbajs3/src/context/layout/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
import { useLocalStorage } from '@uidotdev/usehooks';
import {
createContext,
useCallback,
useEffect,
useMemo,
type ReactNode
} from 'react';
import { createContext, useCallback, useEffect, type ReactNode } from 'react';

import { useLayouts } from '../../hooks/use-layouts.tsx';

type Layout = {
position?: { x: number; y: number };
Expand All @@ -26,8 +21,6 @@ type LayoutContextProps = {

type LayoutProviderProps = { children: ReactNode };

const layoutLocalStorageKey = 'componentLayouts';

export const LayoutContext = createContext<LayoutContextProps>({
layouts: {},
hasSetLayout: false,
Expand All @@ -36,19 +29,7 @@ export const LayoutContext = createContext<LayoutContextProps>({
});

export const LayoutProvider = ({ children }: LayoutProviderProps) => {
const [layouts, setLayouts] = useLocalStorage<Layouts>(
layoutLocalStorageKey,
{}
);
const hasSetLayout = useMemo(
() =>
!!Object.values(layouts).some(
(layout) => !!layout?.position || !!layout?.size
),
[layouts]
);

const clearLayouts = useCallback(() => setLayouts({}), [setLayouts]);
const { layouts, setLayouts, hasSetLayout, clearLayouts } = useLayouts();

const setLayout = useCallback(
(layoutKey: string, layout: Layout) =>
Expand Down
1 change: 0 additions & 1 deletion gbajs3/src/context/theme/theme.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ export const GbaDarkTheme: DefaultTheme = {
pattensBlue: '#dee2e6',
pureBlack: '#000',
pureWhite: '#fff',
screenLight: '#65696d',
darkGray: '#111',
panelControlGray: '#a9a9a9',
panelBlueGray: '#4f555a'
Expand Down
2 changes: 1 addition & 1 deletion gbajs3/src/emulator/mgba/wasm/mgba.js

Large diffs are not rendered by default.

Binary file modified gbajs3/src/emulator/mgba/wasm/mgba.wasm
Binary file not shown.
33 changes: 33 additions & 0 deletions gbajs3/src/hooks/use-layouts.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { useLocalStorage } from '@uidotdev/usehooks';
import { useCallback, useMemo } from 'react';

type Layout = {
position?: { x: number; y: number };
size?: { width: string | number; height: string | number };
initialBounds?: DOMRect;
};

type Layouts = {
[key: string]: Layout;
};

const layoutLocalStorageKey = 'componentLayouts';

export const useLayouts = () => {
const [layouts, setLayouts] = useLocalStorage<Layouts>(
layoutLocalStorageKey,
{}
);

const clearLayouts = useCallback(() => setLayouts({}), [setLayouts]);

const hasSetLayout = useMemo(
() =>
!!Object.values(layouts).some(
(layout) => !!layout?.position || !!layout?.size
),
[layouts]
);

return { layouts, setLayouts, hasSetLayout, clearLayouts };
};
1 change: 0 additions & 1 deletion gbajs3/src/styled.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ declare module 'styled-components' {
pattensBlue: string;
pureBlack: string;
pureWhite: string;
screenLight: string;
darkGray: string;
panelControlGray: string;
panelBlueGray: string;
Expand Down

0 comments on commit 3e24bec

Please sign in to comment.