Skip to content

Commit

Permalink
RGB support
Browse files Browse the repository at this point in the history
  • Loading branch information
DexrnZacAttack committed Oct 10, 2024
1 parent b454e91 commit 73d5689
Show file tree
Hide file tree
Showing 6 changed files with 305 additions and 3 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ Binary reading and writing for Node and Web environments,.
- `Int256` (Signed + Unsigned)
- `UTF8`
- `UTF16`
- `UTF32`
- `UTF32`
- `RGB` (Includes pixel formats like RGBA, ARGB, BGR, BGRA, etc.)

### To be supported
- `MUTF`
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "binaryio.js",
"version": "1.2.1",
"version": "1.2.2",
"description": "BinaryIO for Node and Web environments, allows for reading/writing data like a DataView but with built in position tracking.",
"type": "module",
"main": "./dist/index.js",
Expand Down
80 changes: 80 additions & 0 deletions src/bReader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
*/

import { bCommons } from "./bCommons.js";
import { RGBColor, RGBFormat } from "./types/RGBA.js";

export class bReader extends bCommons {
/** Creates an endianness-aware binary reader with built in position tracking
Expand Down Expand Up @@ -730,4 +731,83 @@ export class bReader extends bCommons {

return str;
}

public readRGB(format: RGBFormat): RGBColor {
const rgb: RGBColor = new RGBColor(0, 0, 0, 0xFF);

switch (format) {
case RGBFormat.RGB:
rgb.red = this.readByte();
rgb.green = this.readByte();
rgb.blue = this.readByte();
break;
case RGBFormat.RGBA:
rgb.red = this.readByte();
rgb.green = this.readByte();
rgb.blue = this.readByte();
rgb.alpha = this.readByte();
break;
case RGBFormat.ARGB:
rgb.alpha = this.readByte();
rgb.red = this.readByte();
rgb.green = this.readByte();
rgb.blue = this.readByte();
break;
case RGBFormat.BGR:
rgb.blue = this.readByte();
rgb.green = this.readByte();
rgb.red = this.readByte();
break;
case RGBFormat.BGRA:
rgb.blue = this.readByte();
rgb.green = this.readByte();
rgb.red = this.readByte();
rgb.alpha = this.readByte();
break;
case RGBFormat.ABGR:
rgb.alpha = this.readByte();
rgb.blue = this.readByte();
rgb.green = this.readByte();
rgb.red = this.readByte();
break;
case RGBFormat.GBR:
rgb.green = this.readByte();
rgb.blue = this.readByte();
rgb.red = this.readByte();
break;
case RGBFormat.GBRA:
rgb.green = this.readByte();
rgb.blue = this.readByte();
rgb.red = this.readByte();
rgb.alpha = this.readByte();
break;
case RGBFormat.AGBR:
rgb.alpha = this.readByte();
rgb.green = this.readByte();
rgb.blue = this.readByte();
rgb.red = this.readByte();
break;
case RGBFormat.GRB:
rgb.green = this.readByte();
rgb.red = this.readByte();
rgb.blue = this.readByte();
break;
case RGBFormat.GRBA:
rgb.green = this.readByte();
rgb.red = this.readByte();
rgb.blue = this.readByte();
rgb.alpha = this.readByte();
break;
case RGBFormat.AGRB:
rgb.alpha = this.readByte();
rgb.green = this.readByte();
rgb.red = this.readByte();
rgb.blue = this.readByte();
break;
default:
throw new Error(`Unknown format: ${format}`);
}

return rgb;
}
}
78 changes: 78 additions & 0 deletions src/bWriter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

import { bCommons } from "./bCommons.js";
import { bTypes } from './bExtra.js';
import { RGBColor, RGBFormat } from "./types/RGBA.js";

export class bWriter extends bCommons {
/** Creates an endianness-aware binary writer with built-in position tracking
Expand Down Expand Up @@ -638,4 +639,81 @@ export class bWriter extends bCommons {
this.writeByte(0);
}

public writeRGB(rgb: RGBColor, format: RGBFormat) {
if (rgb.alpha == undefined)
rgb.alpha = 0xFF;

switch (format) {
case RGBFormat.RGB:
this.writeByte(rgb.red);
this.writeByte(rgb.green);
this.writeByte(rgb.blue);
break;
case RGBFormat.RGBA:
this.writeByte(rgb.red);
this.writeByte(rgb.green);
this.writeByte(rgb.blue);
this.writeByte(rgb.alpha);
break;
case RGBFormat.ARGB:
this.writeByte(rgb.alpha);
this.writeByte(rgb.red);
this.writeByte(rgb.green);
this.writeByte(rgb.blue);
break;
case RGBFormat.BGR:
this.writeByte(rgb.blue);
this.writeByte(rgb.green);
this.writeByte(rgb.red);
break;
case RGBFormat.BGRA:
this.writeByte(rgb.blue);
this.writeByte(rgb.green);
this.writeByte(rgb.red);
this.writeByte(rgb.alpha);
break;
case RGBFormat.ABGR:
this.writeByte(rgb.alpha);
this.writeByte(rgb.blue);
this.writeByte(rgb.green);
this.writeByte(rgb.red);
break;
case RGBFormat.GBR:
this.writeByte(rgb.green);
this.writeByte(rgb.blue);
this.writeByte(rgb.red);
break;
case RGBFormat.GBRA:
this.writeByte(rgb.green);
this.writeByte(rgb.blue);
this.writeByte(rgb.red);
this.writeByte(rgb.alpha);
break;
case RGBFormat.AGBR:
this.writeByte(rgb.alpha);
this.writeByte(rgb.green);
this.writeByte(rgb.blue);
this.writeByte(rgb.red);
break;
case RGBFormat.GRB:
this.writeByte(rgb.green);
this.writeByte(rgb.red);
this.writeByte(rgb.blue);
break;
case RGBFormat.GRBA:
this.writeByte(rgb.green);
this.writeByte(rgb.red);
this.writeByte(rgb.blue);
this.writeByte(rgb.alpha);
break;
case RGBFormat.AGRB:
this.writeByte(rgb.alpha);
this.writeByte(rgb.green);
this.writeByte(rgb.red);
this.writeByte(rgb.blue);
break;
default:
throw new Error(`Unknown format: ${format}`);
}
}
}
43 changes: 43 additions & 0 deletions src/types/RGBA.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/** can be RGB or RGBA. */
export interface RGBA {
red: number;
green: number;
blue: number;
alpha?: number;
}

export class RGBColor implements RGBA {
/**
* RGB(A) color class, allows for RGB(A) colors to be converted to a Number, among other things.
* Internally always RGBA.
* @param red
* @param green
* @param blue
* @param alpha
*/
constructor(public red: number, public green: number, public blue: number, public alpha?: number) {}

/**
* Converts an RGB(A) value to a Number.
* @returns Number from RGB(A) value
*/
toNumber(): number {
return (this.red << 24) | (this.green << 16) | (this.blue << 8) | (this.alpha !== undefined ? this.alpha : 255);
}
}

/** RGB pixel formats */
export enum RGBFormat {
RGB = "RGB",
RGBA = "RGBA",
ARGB = "ARGB",
BGR = "BGR",
BGRA = "BGRA",
ABGR = "ABGR",
GBR = "GBR",
GBRA = "GBRA",
AGBR = "AGBR",
GRB = "GRB",
GRBA = "GRBA",
AGRB = "AGRB"
}
102 changes: 101 additions & 1 deletion testing/writeTest.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {bWriter, bReader, bTypes} from "../src/index.js";
import * as fs from 'fs';
import { RGBColor, RGBFormat } from "../src/types/RGBA.js";

function randNumber(min: number | bigint, max: number | bigint): bigint | number {
if (typeof min === 'bigint' || typeof max === 'bigint') {
Expand Down Expand Up @@ -34,8 +35,10 @@ const randint128 = randNumber(bTypes.INT128_MIN_VALUE, bTypes.INT128_MAX_VALUE);
const randuint128 = randNumber(bTypes.UINT128_MIN_VALUE, bTypes.UINT128_MAX_VALUE);
const randint256 = randNumber(bTypes.INT256_MIN_VALUE, bTypes.INT256_MAX_VALUE);
const randuint256 = randNumber(bTypes.UINT256_MIN_VALUE, bTypes.UINT256_MAX_VALUE);
const randrgba: RGBColor = new RGBColor(Number(randNumber(bTypes.BYTE_MIN_VALUE, bTypes.BYTE_MAX_VALUE)), Number(randNumber(bTypes.BYTE_MIN_VALUE, bTypes.BYTE_MAX_VALUE)), Number(randNumber(bTypes.BYTE_MIN_VALUE, bTypes.BYTE_MAX_VALUE)), 0xFF);
const rgbString = `0x${randrgba.red.toString(16).toUpperCase().padStart(2, '0')}${randrgba.green.toString(16).toUpperCase().padStart(2, '0')}${randrgba.blue.toString(16).toUpperCase().padStart(2, '0')}${randrgba.alpha?.toString(16).toUpperCase().padStart(2, '0')}`;

const writer = new bWriter(new ArrayBuffer((337) * 2));
const writer = new bWriter(new ArrayBuffer((381) * 2));
function write(endian: boolean) {
console.log(`Writing ${endian ? "Little" : "Big"} Endian`);
writer.setEndianness(endian);
Expand Down Expand Up @@ -123,6 +126,43 @@ function write(endian: boolean) {

console.log(`Writing UTF32 string: "你好,世界!" ("Hello, world!" in Chinese (Simplified))`);
writer.writeString32("你好,世界!", endian, true);

console.log(`Writing RGB ${rgbString}`);
writer.writeRGB(randrgba, RGBFormat.RGB);

console.log(`Writing RGBA ${rgbString}`);
writer.writeRGB(randrgba, RGBFormat.RGBA);

console.log(`Writing ARGB ${rgbString}`);
writer.writeRGB(randrgba, RGBFormat.ARGB);

console.log(`Writing BGR ${rgbString}`);
writer.writeRGB(randrgba, RGBFormat.BGR);

console.log(`Writing BGRA ${rgbString}`);
writer.writeRGB(randrgba, RGBFormat.BGRA);

console.log(`Writing ABGR ${rgbString}`);
writer.writeRGB(randrgba, RGBFormat.ABGR);

console.log(`Writing GBR ${rgbString}`);
writer.writeRGB(randrgba, RGBFormat.GBR);

console.log(`Writing GBRA ${rgbString}`);
writer.writeRGB(randrgba, RGBFormat.GBRA);

console.log(`Writing AGBR ${rgbString}`);
writer.writeRGB(randrgba, RGBFormat.AGBR);

console.log(`Writing GRB ${rgbString}`);
writer.writeRGB(randrgba, RGBFormat.GRB);

console.log(`Writing GRBA ${rgbString}`);
writer.writeRGB(randrgba, RGBFormat.GRBA);

console.log(`Writing AGRB ${rgbString}`);
writer.writeRGB(randrgba, RGBFormat.AGRB);

}

write(true);
Expand Down Expand Up @@ -249,4 +289,64 @@ function compare(endian: boolean) {
const string32Chinese = reader.readString32();
console.log(`Read UTF32 string (should be "你好,世界!" ("Hello, world!" in Chinese (Simplified))):`, string32Chinese);
if (string32Chinese !== "你好,世界!") throw new Error(`"你好,世界!" (UTF32 string) does not match "${string32Chinese}"!`);

const rgb = reader.readRGB(RGBFormat.RGB);
const rgb2String = `0x${rgb.red.toString(16).toUpperCase().padStart(2, '0')}${rgb.green.toString(16).toUpperCase().padStart(2, '0')}${rgb.blue.toString(16).toUpperCase().padStart(2, '0')}${rgb.alpha?.toString(16).toUpperCase().padStart(2, '0')}`;
console.log(`Read RGB should be ${rgbString}:`, rgb2String);
if (JSON.stringify(rgb) != JSON.stringify(randrgba)) throw new Error(`${rgb2String} does not match ${rgbString}`);

const rgba = reader.readRGB(RGBFormat.RGBA);
const rgba2String = `0x${rgba.red.toString(16).toUpperCase().padStart(2, '0')}${rgba.green.toString(16).toUpperCase().padStart(2, '0')}${rgba.blue.toString(16).toUpperCase().padStart(2, '0')}${rgba.alpha?.toString(16).toUpperCase().padStart(2, '0')}`;
console.log(`Read RGBA should be ${rgbString}:`, rgba2String);
if (JSON.stringify(rgba) !== JSON.stringify(randrgba)) throw new Error(`${rgba2String} does not match ${rgbString}`);

const argb = reader.readRGB(RGBFormat.ARGB);
const argb2String = `0x${argb.red.toString(16).toUpperCase().padStart(2, '0')}${argb.green.toString(16).toUpperCase().padStart(2, '0')}${argb.blue.toString(16).toUpperCase().padStart(2, '0')}${argb.alpha?.toString(16).toUpperCase().padStart(2, '0')}`;
console.log(`Read ARGB should be ${rgbString}:`, argb2String);
if (JSON.stringify(argb) !== JSON.stringify(randrgba)) throw new Error(`${argb2String} does not match ${rgbString}`);

const bgr = reader.readRGB(RGBFormat.BGR);
const bgr2String = `0x${bgr.red.toString(16).toUpperCase().padStart(2, '0')}${bgr.green.toString(16).toUpperCase().padStart(2, '0')}${bgr.blue.toString(16).toUpperCase().padStart(2, '0')}${bgr.alpha?.toString(16).toUpperCase().padStart(2, '0')}`;
console.log(`Read BGR should be ${rgbString}:`, bgr2String);
if (JSON.stringify(bgr) !== JSON.stringify(randrgba)) throw new Error(`${bgr2String} does not match ${rgbString}`);

const bgra = reader.readRGB(RGBFormat.BGRA);
const bgra2String = `0x${bgra.red.toString(16).toUpperCase().padStart(2, '0')}${bgra.green.toString(16).toUpperCase().padStart(2, '0')}${bgra.blue.toString(16).toUpperCase().padStart(2, '0')}${bgra.alpha?.toString(16).toUpperCase().padStart(2, '0')}`;
console.log(`Read BGRA should be ${rgbString}:`, bgra2String);
if (JSON.stringify(bgra) !== JSON.stringify(randrgba)) throw new Error(`${bgra2String} does not match ${rgbString}`);

const abgr = reader.readRGB(RGBFormat.ABGR);
const abgr2String = `0x${abgr.red.toString(16).toUpperCase().padStart(2, '0')}${abgr.green.toString(16).toUpperCase().padStart(2, '0')}${abgr.blue.toString(16).toUpperCase().padStart(2, '0')}${abgr.alpha?.toString(16).toUpperCase().padStart(2, '0')}`;
console.log(`Read ABGR should be ${rgbString}:`, abgr2String);
if (JSON.stringify(abgr) !== JSON.stringify(randrgba)) throw new Error(`${abgr2String} does not match ${rgbString}`);

const gbr = reader.readRGB(RGBFormat.GBR);
const gbr2String = `0x${gbr.red.toString(16).toUpperCase().padStart(2, '0')}${gbr.green.toString(16).toUpperCase().padStart(2, '0')}${gbr.blue.toString(16).toUpperCase().padStart(2, '0')}${gbr.alpha?.toString(16).toUpperCase().padStart(2, '0')}`;
console.log(`Read GBR should be ${rgbString}:`, gbr2String);
if (JSON.stringify(gbr) !== JSON.stringify(randrgba)) throw new Error(`${gbr2String} does not match ${rgbString}`);

const gbra = reader.readRGB(RGBFormat.GBRA);
const gbra2String = `0x${gbra.red.toString(16).toUpperCase().padStart(2, '0')}${gbra.green.toString(16).toUpperCase().padStart(2, '0')}${gbra.blue.toString(16).toUpperCase().padStart(2, '0')}${gbra.alpha?.toString(16).toUpperCase().padStart(2, '0')}`;
console.log(`Read GBRA should be ${rgbString}:`, gbra2String);
if (JSON.stringify(gbra) !== JSON.stringify(randrgba)) throw new Error(`${gbra2String} does not match ${rgbString}`);

const agbr = reader.readRGB(RGBFormat.AGBR);
const agbr2String = `0x${agbr.red.toString(16).toUpperCase().padStart(2, '0')}${agbr.green.toString(16).toUpperCase().padStart(2, '0')}${agbr.blue.toString(16).toUpperCase().padStart(2, '0')}${agbr.alpha?.toString(16).toUpperCase().padStart(2, '0')}`;
console.log(`Read AGBR should be ${rgbString}:`, agbr2String);
if (JSON.stringify(agbr) !== JSON.stringify(randrgba)) throw new Error(`${agbr2String} does not match ${rgbString}`);

const grb = reader.readRGB(RGBFormat.GRB);
const grb2String = `0x${grb.red.toString(16).toUpperCase().padStart(2, '0')}${grb.green.toString(16).toUpperCase().padStart(2, '0')}${grb.blue.toString(16).toUpperCase().padStart(2, '0')}${grb.alpha?.toString(16).toUpperCase().padStart(2, '0')}`;
console.log(`Read GRB should be ${rgbString}:`, grb2String);
if (JSON.stringify(grb) !== JSON.stringify(randrgba)) throw new Error(`${grb2String} does not match ${rgbString}`);

const grba = reader.readRGB(RGBFormat.GRBA);
const grba2String = `0x${grba.red.toString(16).toUpperCase().padStart(2, '0')}${grba.green.toString(16).toUpperCase().padStart(2, '0')}${grba.blue.toString(16).toUpperCase().padStart(2, '0')}${grba.alpha?.toString(16).toUpperCase().padStart(2, '0')}`;
console.log(`Read GRBA should be ${rgbString}:`, grba2String);
if (JSON.stringify(grba) !== JSON.stringify(randrgba)) throw new Error(`${grba2String} does not match ${rgbString}`);

const agrb = reader.readRGB(RGBFormat.AGRB);
const agrb2String = `0x${agrb.red.toString(16).toUpperCase().padStart(2, '0')}${agrb.green.toString(16).toUpperCase().padStart(2, '0')}${agrb.blue.toString(16).toUpperCase().padStart(2, '0')}${agrb.alpha?.toString(16).toUpperCase().padStart(2, '0')}`;
console.log(`Read AGRB should be ${rgbString}:`, agrb2String);
if (JSON.stringify(agrb) !== JSON.stringify(randrgba)) throw new Error(`${agrb2String} does not match ${rgbString}`);
}

0 comments on commit 73d5689

Please sign in to comment.