diff --git a/simulator/src/compositor.ts b/simulator/src/compositor.ts index a7fd853..e565c53 100644 --- a/simulator/src/compositor.ts +++ b/simulator/src/compositor.ts @@ -1,11 +1,10 @@ import { WIDTH, HEIGHT } from "./constants"; -import * as constants from "./constants"; import * as GL from "./webgl-constants"; import type { Framebuffer } from "./framebuffer"; -const PALETTE_SIZE = 4; - export class WebGLCompositor { + flipped = new Uint16Array(WIDTH * HEIGHT); + constructor (public gl: WebGLRenderingContext) { const canvas = gl.canvas; canvas.addEventListener("webglcontextlost", event => { @@ -110,8 +109,18 @@ export class WebGLCompositor { const gl = this.gl; const bytes = framebuffer.bytes; + // Rotate and swap byte order + // Would not be required if WebGL supported `UNSIGNED_SHORT_5_6_5_REV` + for (let x = 0; x < WIDTH; x++) { + for (let y = 0; y < HEIGHT; y++) { + const fi = x + y * WIDTH; + const bi = x * HEIGHT + y; + this.flipped[fi] = ((bytes[bi] & 0xff) << 8) | (bytes[bi] >> 8); + } + } + // Upload framebuffer - gl.texImage2D(GL.TEXTURE_2D, 0, GL.RGB565, WIDTH, HEIGHT, 0, GL.RGB, GL.UNSIGNED_SHORT_5_6_5, bytes); + gl.texImage2D(GL.TEXTURE_2D, 0, GL.RGB565, WIDTH, HEIGHT, 0, GL.RGB, GL.UNSIGNED_SHORT_5_6_5, this.flipped); // Draw the fullscreen quad gl.drawArrays(GL.TRIANGLES, 0, 6); diff --git a/simulator/src/constants.ts b/simulator/src/constants.ts index c5947e5..69e1336 100644 --- a/simulator/src/constants.ts +++ b/simulator/src/constants.ts @@ -11,7 +11,8 @@ export const ADDR_CONTROLS = 0x04; export const ADDR_LIGHT_LEVEL = 0x06; export const ADDR_NEOPIXELS = 0x08; export const ADDR_RED_LED = 0x1c; -export const ADDR_FRAMEBUFFER = 0x1e; +export const ADDR_BATTERY_LEVEL = 0x1e; +export const ADDR_FRAMEBUFFER = 0x20; export const CONTROLS_START = 1; export const CONTROLS_SELECT = 2; diff --git a/simulator/src/framebuffer.ts b/simulator/src/framebuffer.ts index 3eaa169..27c0164 100644 --- a/simulator/src/framebuffer.ts +++ b/simulator/src/framebuffer.ts @@ -18,7 +18,7 @@ export class Framebuffer { } drawPoint (color: number, x: number, y: number) { - this.bytes[WIDTH * y + x] = color; + this.bytes[x * HEIGHT + y] = color; } drawPointUnclipped (color: number, x: number, y: number) { @@ -27,27 +27,17 @@ export class Framebuffer { } } - drawHLineFast(color: number, startX: number, y: number, endX: number) { - const yOff = WIDTH * y; - this.bytes.fill(color, yOff + startX, yOff + endX); - } - - drawHLineUnclipped(color: number, startX: number, y: number, endX: number) { - if (y >= 0 && y < HEIGHT) { - if (startX < 0) { - startX = 0; - } - if (endX > WIDTH) { - endX = WIDTH; - } - if (startX < endX) { - this.drawHLineFast(color, startX, y, endX); - } + drawHLine(color: number, x: number, y: number, len: number) { + if (x + len <= 0 || y < 0 || y >= HEIGHT) { + return; } - } - drawHLine(color: number, x: number, y: number, len: number) { - this.drawHLineUnclipped(color, x, y, x + len); + const startX = Math.max(0, x); + const endX = Math.min(WIDTH, x + len); + + for (let xx = startX; xx < endX; xx++) { + this.drawPointUnclipped(color, xx, y); + } } drawVLine(color: number, x: number, y: number, len: number) { @@ -77,7 +67,7 @@ export class Framebuffer { if (fillColor !== OPTIONAL_COLOR_NONE) { for (let yy = startY; yy < endY; ++yy) { - this.drawHLineFast(fillColor, startX, yy, endX); + this.drawHLine(fillColor, startX, yy, endX - startX); } } @@ -98,12 +88,12 @@ export class Framebuffer { // Top edge if (y >= 0 && y < HEIGHT) { - this.drawHLineFast(strokeColor, startX, y, endX); + this.drawHLine(strokeColor, startX, y, endX - startX); } // Bottom edge if (endYUnclamped > 0 && endYUnclamped <= HEIGHT) { - this.drawHLineFast(strokeColor, startX, endYUnclamped - 1, endX); + this.drawHLine(strokeColor, startX, endYUnclamped - 1, endX - startX); } } } @@ -125,7 +115,7 @@ export class Framebuffer { if (strokeColor === OPTIONAL_COLOR_NONE && fillColor === OPTIONAL_COLOR_NONE) { return; } - + let a = width - 1; const b = height - 1; let b1 = b % 2; // Compensates for precision loss when dividing @@ -161,8 +151,8 @@ export class Framebuffer { const len = east - start; if (fillColor !== OPTIONAL_COLOR_NONE && len > 0) { // Only draw fill if the length from west to east is not 0 - this.drawHLineUnclipped(fillColor, start, north, east); /* I and III. Quadrant */ - this.drawHLineUnclipped(fillColor, start, south, east); /* II and IV. Quadrant */ + this.drawHLine(fillColor, start, north, east - start); /* I and III. Quadrant */ + this.drawHLine(fillColor, start, south, east - start); /* II and IV. Quadrant */ } const err2 = 2 * err; diff --git a/simulator/src/ui/utils.ts b/simulator/src/ui/utils.ts index 1b4f888..618aae3 100644 --- a/simulator/src/ui/utils.ts +++ b/simulator/src/ui/utils.ts @@ -44,11 +44,13 @@ export function requestFullscreen () { * @returns RGB565 representation */ export function pack565(red: number, green: number, blue: number): number { - return blue | (green << 5) | (red << 11); + const rev = blue | (green << 5) | (red << 11); + return ((rev & 0xff) << 8) | (rev >> 8); } export function unpack565(bgr565: number): [number, number, number] { - return [bgr565 >> 11, bgr565 >> 5 & 0b111111, bgr565 & 0b11111]; + const flipped = ((bgr565 & 0xff) << 8) | (bgr565 >> 8); + return [flipped >> 11, flipped >> 5 & 0b111111, flipped & 0b11111]; } export function unpack888(bgr888: number): [number, number, number] {