@opentui/core 0.1.3 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/3d.js +203 -18
- package/Renderable.d.ts +1 -1
- package/index.js +1 -13
- package/package.json +7 -7
- package/utils.d.ts +0 -4
- package/supersampling-jw3fem06.wgsl +0 -201
package/3d.js
CHANGED
|
@@ -19581,16 +19581,6 @@ function parseColor(color) {
|
|
|
19581
19581
|
}
|
|
19582
19582
|
return color;
|
|
19583
19583
|
}
|
|
19584
|
-
async function loadTemplate(filePath, params) {
|
|
19585
|
-
const template = await Bun.file(filePath).text();
|
|
19586
|
-
return template.replace(/\${(\w+)}/g, (match, key) => params[key] || match);
|
|
19587
|
-
}
|
|
19588
|
-
function fixPaths(paths) {
|
|
19589
|
-
if (process.env.BUN_PACKER_BUNDLE) {
|
|
19590
|
-
return Object.fromEntries(Object.entries(paths).map(([key, value]) => [key, value.replace("../", "")]));
|
|
19591
|
-
}
|
|
19592
|
-
return paths;
|
|
19593
|
-
}
|
|
19594
19584
|
|
|
19595
19585
|
// src/types.ts
|
|
19596
19586
|
class RGBA {
|
|
@@ -31407,16 +31397,211 @@ var Jimp = createJimp({
|
|
|
31407
31397
|
});
|
|
31408
31398
|
|
|
31409
31399
|
// src/3d/shaders/supersampling.wgsl
|
|
31410
|
-
var supersampling_default =
|
|
31400
|
+
var supersampling_default = `struct CellResult {
|
|
31401
|
+
bg: vec4<f32>, // Background RGBA (16 bytes)
|
|
31402
|
+
fg: vec4<f32>, // Foreground RGBA (16 bytes)
|
|
31403
|
+
char: u32, // Unicode character code (4 bytes)
|
|
31404
|
+
_padding1: u32, // Padding (4 bytes)
|
|
31405
|
+
_padding2: u32, // Extra padding (4 bytes)
|
|
31406
|
+
_padding3: u32, // Extra padding (4 bytes) - total now 48 bytes (16-byte aligned)
|
|
31407
|
+
};
|
|
31408
|
+
|
|
31409
|
+
struct CellBuffer {
|
|
31410
|
+
cells: array<CellResult>
|
|
31411
|
+
};
|
|
31412
|
+
|
|
31413
|
+
struct SuperSamplingParams {
|
|
31414
|
+
width: u32, // Canvas width in pixels
|
|
31415
|
+
height: u32, // Canvas height in pixels
|
|
31416
|
+
sampleAlgo: u32, // 0 = standard 2x2, 1 = pre-squeezed horizontal blend
|
|
31417
|
+
_padding: u32, // Padding for 16-byte alignment
|
|
31418
|
+
};
|
|
31419
|
+
|
|
31420
|
+
@group(0) @binding(0) var inputTexture: texture_2d<f32>;
|
|
31421
|
+
@group(0) @binding(1) var<storage, read_write> output: CellBuffer;
|
|
31422
|
+
@group(0) @binding(2) var<uniform> params: SuperSamplingParams;
|
|
31423
|
+
|
|
31424
|
+
// Quadrant character lookup table (same as Zig implementation)
|
|
31425
|
+
const quadrantChars = array<u32, 16>(
|
|
31426
|
+
32u, // ' ' - 0000
|
|
31427
|
+
0x2597u, // \u2597 - 0001 BR
|
|
31428
|
+
0x2596u, // \u2596 - 0010 BL
|
|
31429
|
+
0x2584u, // \u2584 - 0011 Lower Half Block
|
|
31430
|
+
0x259Du, // \u259D - 0100 TR
|
|
31431
|
+
0x2590u, // \u2590 - 0101 Right Half Block
|
|
31432
|
+
0x259Eu, // \u259E - 0110 TR+BL
|
|
31433
|
+
0x259Fu, // \u259F - 0111 TR+BL+BR
|
|
31434
|
+
0x2598u, // \u2598 - 1000 TL
|
|
31435
|
+
0x259Au, // \u259A - 1001 TL+BR
|
|
31436
|
+
0x258Cu, // \u258C - 1010 Left Half Block
|
|
31437
|
+
0x2599u, // \u2599 - 1011 TL+BL+BR
|
|
31438
|
+
0x2580u, // \u2580 - 1100 Upper Half Block
|
|
31439
|
+
0x259Cu, // \u259C - 1101 TL+TR+BR
|
|
31440
|
+
0x259Bu, // \u259B - 1110 TL+TR+BL
|
|
31441
|
+
0x2588u // \u2588 - 1111 Full Block
|
|
31442
|
+
);
|
|
31443
|
+
|
|
31444
|
+
const inv_255: f32 = 1.0 / 255.0;
|
|
31445
|
+
|
|
31446
|
+
fn getPixelColor(pixelX: u32, pixelY: u32) -> vec4<f32> {
|
|
31447
|
+
if (pixelX >= params.width || pixelY >= params.height) {
|
|
31448
|
+
return vec4<f32>(0.0, 0.0, 0.0, 1.0); // Black for out-of-bounds
|
|
31449
|
+
}
|
|
31450
|
+
|
|
31451
|
+
// textureLoad automatically handles format conversion to RGBA
|
|
31452
|
+
return textureLoad(inputTexture, vec2<i32>(i32(pixelX), i32(pixelY)), 0);
|
|
31453
|
+
}
|
|
31454
|
+
|
|
31455
|
+
fn colorDistance(a: vec4<f32>, b: vec4<f32>) -> f32 {
|
|
31456
|
+
let diff = a.rgb - b.rgb;
|
|
31457
|
+
return dot(diff, diff);
|
|
31458
|
+
}
|
|
31459
|
+
|
|
31460
|
+
fn luminance(color: vec4<f32>) -> f32 {
|
|
31461
|
+
return 0.2126 * color.r + 0.7152 * color.g + 0.0722 * color.b;
|
|
31462
|
+
}
|
|
31463
|
+
|
|
31464
|
+
fn closestColorIndex(pixel: vec4<f32>, candA: vec4<f32>, candB: vec4<f32>) -> u32 {
|
|
31465
|
+
return select(1u, 0u, colorDistance(pixel, candA) <= colorDistance(pixel, candB));
|
|
31466
|
+
}
|
|
31467
|
+
|
|
31468
|
+
fn averageColor(pixels: array<vec4<f32>, 4>) -> vec4<f32> {
|
|
31469
|
+
return (pixels[0] + pixels[1] + pixels[2] + pixels[3]) * 0.25;
|
|
31470
|
+
}
|
|
31471
|
+
|
|
31472
|
+
fn blendColors(color1: vec4<f32>, color2: vec4<f32>) -> vec4<f32> {
|
|
31473
|
+
let a1 = color1.a;
|
|
31474
|
+
let a2 = color2.a;
|
|
31475
|
+
|
|
31476
|
+
if (a1 == 0.0 && a2 == 0.0) {
|
|
31477
|
+
return vec4<f32>(0.0, 0.0, 0.0, 0.0);
|
|
31478
|
+
}
|
|
31479
|
+
|
|
31480
|
+
let outAlpha = a1 + a2 - a1 * a2;
|
|
31481
|
+
if (outAlpha == 0.0) {
|
|
31482
|
+
return vec4<f32>(0.0, 0.0, 0.0, 0.0);
|
|
31483
|
+
}
|
|
31484
|
+
|
|
31485
|
+
let rgb = (color1.rgb * a1 + color2.rgb * a2 * (1.0 - a1)) / outAlpha;
|
|
31486
|
+
|
|
31487
|
+
return vec4<f32>(rgb, outAlpha);
|
|
31488
|
+
}
|
|
31489
|
+
|
|
31490
|
+
fn averageColorsWithAlpha(pixels: array<vec4<f32>, 4>) -> vec4<f32> {
|
|
31491
|
+
let blend1 = blendColors(pixels[0], pixels[1]);
|
|
31492
|
+
let blend2 = blendColors(pixels[2], pixels[3]);
|
|
31493
|
+
|
|
31494
|
+
return blendColors(blend1, blend2);
|
|
31495
|
+
}
|
|
31496
|
+
|
|
31497
|
+
fn renderQuadrantBlock(pixels: array<vec4<f32>, 4>) -> CellResult {
|
|
31498
|
+
var maxDist: f32 = colorDistance(pixels[0], pixels[1]);
|
|
31499
|
+
var pIdxA: u32 = 0u;
|
|
31500
|
+
var pIdxB: u32 = 1u;
|
|
31501
|
+
|
|
31502
|
+
for (var i: u32 = 0u; i < 4u; i++) {
|
|
31503
|
+
for (var j: u32 = i + 1u; j < 4u; j++) {
|
|
31504
|
+
let dist = colorDistance(pixels[i], pixels[j]);
|
|
31505
|
+
if (dist > maxDist) {
|
|
31506
|
+
pIdxA = i;
|
|
31507
|
+
pIdxB = j;
|
|
31508
|
+
maxDist = dist;
|
|
31509
|
+
}
|
|
31510
|
+
}
|
|
31511
|
+
}
|
|
31512
|
+
|
|
31513
|
+
let pCandA = pixels[pIdxA];
|
|
31514
|
+
let pCandB = pixels[pIdxB];
|
|
31515
|
+
|
|
31516
|
+
var chosenDarkColor: vec4<f32>;
|
|
31517
|
+
var chosenLightColor: vec4<f32>;
|
|
31518
|
+
|
|
31519
|
+
if (luminance(pCandA) <= luminance(pCandB)) {
|
|
31520
|
+
chosenDarkColor = pCandA;
|
|
31521
|
+
chosenLightColor = pCandB;
|
|
31522
|
+
} else {
|
|
31523
|
+
chosenDarkColor = pCandB;
|
|
31524
|
+
chosenLightColor = pCandA;
|
|
31525
|
+
}
|
|
31526
|
+
|
|
31527
|
+
var quadrantBits: u32 = 0u;
|
|
31528
|
+
let bitValues = array<u32, 4>(8u, 4u, 2u, 1u); // TL, TR, BL, BR
|
|
31529
|
+
|
|
31530
|
+
for (var i: u32 = 0u; i < 4u; i++) {
|
|
31531
|
+
if (closestColorIndex(pixels[i], chosenDarkColor, chosenLightColor) == 0u) {
|
|
31532
|
+
quadrantBits |= bitValues[i];
|
|
31533
|
+
}
|
|
31534
|
+
}
|
|
31535
|
+
|
|
31536
|
+
// Construct result
|
|
31537
|
+
var result: CellResult;
|
|
31538
|
+
|
|
31539
|
+
if (quadrantBits == 0u) { // All light
|
|
31540
|
+
result.char = 32u; // Space character
|
|
31541
|
+
result.fg = chosenDarkColor;
|
|
31542
|
+
result.bg = averageColorsWithAlpha(pixels);
|
|
31543
|
+
} else if (quadrantBits == 15u) { // All dark
|
|
31544
|
+
result.char = quadrantChars[15]; // Full block
|
|
31545
|
+
result.fg = averageColorsWithAlpha(pixels);
|
|
31546
|
+
result.bg = chosenLightColor;
|
|
31547
|
+
} else { // Mixed pattern
|
|
31548
|
+
result.char = quadrantChars[quadrantBits];
|
|
31549
|
+
result.fg = chosenDarkColor;
|
|
31550
|
+
result.bg = chosenLightColor;
|
|
31551
|
+
}
|
|
31552
|
+
result._padding1 = 0u;
|
|
31553
|
+
result._padding2 = 0u;
|
|
31554
|
+
result._padding3 = 0u;
|
|
31555
|
+
|
|
31556
|
+
return result;
|
|
31557
|
+
}
|
|
31558
|
+
|
|
31559
|
+
@compute @workgroup_size(\${WORKGROUP_SIZE}, \${WORKGROUP_SIZE}, 1)
|
|
31560
|
+
fn main(@builtin(global_invocation_id) id: vec3<u32>) {
|
|
31561
|
+
let cellX = id.x;
|
|
31562
|
+
let cellY = id.y;
|
|
31563
|
+
let bufferWidthCells = (params.width + 1u) / 2u;
|
|
31564
|
+
let bufferHeightCells = (params.height + 1u) / 2u;
|
|
31565
|
+
|
|
31566
|
+
if (cellX >= bufferWidthCells || cellY >= bufferHeightCells) {
|
|
31567
|
+
return;
|
|
31568
|
+
}
|
|
31569
|
+
|
|
31570
|
+
let renderX = cellX * 2u;
|
|
31571
|
+
let renderY = cellY * 2u;
|
|
31572
|
+
|
|
31573
|
+
var pixelsRgba: array<vec4<f32>, 4>;
|
|
31574
|
+
|
|
31575
|
+
if (params.sampleAlgo == 1u) {
|
|
31576
|
+
let topColor = getPixelColor(renderX, renderY);
|
|
31577
|
+
let topColor2 = getPixelColor(renderX + 1u, renderY);
|
|
31578
|
+
|
|
31579
|
+
let blendedTop = blendColors(topColor, topColor2);
|
|
31580
|
+
|
|
31581
|
+
let bottomColor = getPixelColor(renderX, renderY + 1u);
|
|
31582
|
+
let bottomColor2 = getPixelColor(renderX + 1u, renderY + 1u);
|
|
31583
|
+
let blendedBottom = blendColors(bottomColor, bottomColor2);
|
|
31584
|
+
|
|
31585
|
+
pixelsRgba[0] = blendedTop; // TL
|
|
31586
|
+
pixelsRgba[1] = blendedTop; // TR
|
|
31587
|
+
pixelsRgba[2] = blendedBottom; // BL
|
|
31588
|
+
pixelsRgba[3] = blendedBottom; // BR
|
|
31589
|
+
} else {
|
|
31590
|
+
pixelsRgba[0] = getPixelColor(renderX, renderY); // TL
|
|
31591
|
+
pixelsRgba[1] = getPixelColor(renderX + 1u, renderY); // TR
|
|
31592
|
+
pixelsRgba[2] = getPixelColor(renderX, renderY + 1u); // BL
|
|
31593
|
+
pixelsRgba[3] = getPixelColor(renderX + 1u, renderY + 1u); // BR
|
|
31594
|
+
}
|
|
31595
|
+
|
|
31596
|
+
let cellResult = renderQuadrantBlock(pixelsRgba);
|
|
31597
|
+
|
|
31598
|
+
let outputIndex = cellY * bufferWidthCells + cellX;
|
|
31599
|
+
output.cells[outputIndex] = cellResult;
|
|
31600
|
+
}`;
|
|
31411
31601
|
|
|
31412
31602
|
// src/3d/canvas.ts
|
|
31413
|
-
var filePaths = fixPaths({
|
|
31414
|
-
shaderPath: supersampling_default
|
|
31415
|
-
});
|
|
31416
31603
|
var WORKGROUP_SIZE = 4;
|
|
31417
|
-
var SUPERSAMPLING_COMPUTE_SHADER =
|
|
31418
|
-
WORKGROUP_SIZE: WORKGROUP_SIZE.toString()
|
|
31419
|
-
});
|
|
31604
|
+
var SUPERSAMPLING_COMPUTE_SHADER = supersampling_default.replace(/\${WORKGROUP_SIZE}/g, WORKGROUP_SIZE.toString());
|
|
31420
31605
|
var SuperSampleAlgorithm;
|
|
31421
31606
|
((SuperSampleAlgorithm2) => {
|
|
31422
31607
|
SuperSampleAlgorithm2[SuperSampleAlgorithm2["STANDARD"] = 0] = "STANDARD";
|
|
@@ -34100,7 +34285,7 @@ class Renderable extends EventEmitter3 {
|
|
|
34100
34285
|
this._height = options.height;
|
|
34101
34286
|
this.layoutNode.setHeight(options.height);
|
|
34102
34287
|
}
|
|
34103
|
-
this._positionType = options.
|
|
34288
|
+
this._positionType = options.position ?? "relative";
|
|
34104
34289
|
if (this._positionType === "absolute") {
|
|
34105
34290
|
node.setPositionType(PositionType.Absolute);
|
|
34106
34291
|
}
|
package/Renderable.d.ts
CHANGED
|
@@ -27,7 +27,7 @@ export interface LayoutOptions {
|
|
|
27
27
|
alignItems?: AlignString;
|
|
28
28
|
justifyContent?: JustifyString;
|
|
29
29
|
flexBasis?: number | "auto" | undefined;
|
|
30
|
-
|
|
30
|
+
position?: PositionTypeString;
|
|
31
31
|
top?: number | "auto" | `${number}%`;
|
|
32
32
|
right?: number | "auto" | `${number}%`;
|
|
33
33
|
bottom?: number | "auto" | `${number}%`;
|
package/index.js
CHANGED
|
@@ -2367,7 +2367,7 @@ class Renderable extends EventEmitter3 {
|
|
|
2367
2367
|
this._height = options.height;
|
|
2368
2368
|
this.layoutNode.setHeight(options.height);
|
|
2369
2369
|
}
|
|
2370
|
-
this._positionType = options.
|
|
2370
|
+
this._positionType = options.position ?? "relative";
|
|
2371
2371
|
if (this._positionType === "absolute") {
|
|
2372
2372
|
node.setPositionType(PositionType.Absolute);
|
|
2373
2373
|
}
|
|
@@ -2967,16 +2967,6 @@ function createTextAttributes({
|
|
|
2967
2967
|
attributes |= TextAttributes.STRIKETHROUGH;
|
|
2968
2968
|
return attributes;
|
|
2969
2969
|
}
|
|
2970
|
-
async function loadTemplate(filePath, params) {
|
|
2971
|
-
const template = await Bun.file(filePath).text();
|
|
2972
|
-
return template.replace(/\${(\w+)}/g, (match, key) => params[key] || match);
|
|
2973
|
-
}
|
|
2974
|
-
function fixPaths(paths) {
|
|
2975
|
-
if (process.env.BUN_PACKER_BUNDLE) {
|
|
2976
|
-
return Object.fromEntries(Object.entries(paths).map(([key, value]) => [key, value.replace("../", "")]));
|
|
2977
|
-
}
|
|
2978
|
-
return paths;
|
|
2979
|
-
}
|
|
2980
2970
|
|
|
2981
2971
|
// src/types.ts
|
|
2982
2972
|
class RGBA {
|
|
@@ -10287,7 +10277,6 @@ export {
|
|
|
10287
10277
|
nonAlphanumericKeys,
|
|
10288
10278
|
measureText,
|
|
10289
10279
|
magenta,
|
|
10290
|
-
loadTemplate,
|
|
10291
10280
|
italic,
|
|
10292
10281
|
isSizeType,
|
|
10293
10282
|
isPositionType,
|
|
@@ -10303,7 +10292,6 @@ export {
|
|
|
10303
10292
|
getBorderSides,
|
|
10304
10293
|
getBorderFromSides,
|
|
10305
10294
|
fonts,
|
|
10306
|
-
fixPaths,
|
|
10307
10295
|
fg,
|
|
10308
10296
|
engine,
|
|
10309
10297
|
dim,
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"main": "index.js",
|
|
5
5
|
"types": "index.d.ts",
|
|
6
6
|
"type": "module",
|
|
7
|
-
"version": "0.1.
|
|
7
|
+
"version": "0.1.5",
|
|
8
8
|
"description": "OpenTUI is a TypeScript library for building terminal user interfaces (TUIs)",
|
|
9
9
|
"license": "MIT",
|
|
10
10
|
"repository": {
|
|
@@ -32,11 +32,11 @@
|
|
|
32
32
|
"bun-webgpu": "0.1.3",
|
|
33
33
|
"planck": "^1.4.2",
|
|
34
34
|
"three": "0.177.0",
|
|
35
|
-
"@opentui/core-darwin-x64": "0.1.
|
|
36
|
-
"@opentui/core-darwin-arm64": "0.1.
|
|
37
|
-
"@opentui/core-linux-x64": "0.1.
|
|
38
|
-
"@opentui/core-linux-arm64": "0.1.
|
|
39
|
-
"@opentui/core-win32-x64": "0.1.
|
|
40
|
-
"@opentui/core-win32-arm64": "0.1.
|
|
35
|
+
"@opentui/core-darwin-x64": "0.1.5",
|
|
36
|
+
"@opentui/core-darwin-arm64": "0.1.5",
|
|
37
|
+
"@opentui/core-linux-x64": "0.1.5",
|
|
38
|
+
"@opentui/core-linux-arm64": "0.1.5",
|
|
39
|
+
"@opentui/core-win32-x64": "0.1.5",
|
|
40
|
+
"@opentui/core-win32-arm64": "0.1.5"
|
|
41
41
|
}
|
|
42
42
|
}
|
package/utils.d.ts
CHANGED
|
@@ -13,7 +13,3 @@ export declare function createTextAttributes({ bold, italic, underline, dim, bli
|
|
|
13
13
|
hidden?: boolean;
|
|
14
14
|
strikethrough?: boolean;
|
|
15
15
|
}): number;
|
|
16
|
-
export declare function loadTemplate(filePath: string, params: Record<string, string>): Promise<string>;
|
|
17
|
-
export declare function fixPaths<T extends {
|
|
18
|
-
[key: string]: string;
|
|
19
|
-
}>(paths: T): T;
|
|
@@ -1,201 +0,0 @@
|
|
|
1
|
-
struct CellResult {
|
|
2
|
-
bg: vec4<f32>, // Background RGBA (16 bytes)
|
|
3
|
-
fg: vec4<f32>, // Foreground RGBA (16 bytes)
|
|
4
|
-
char: u32, // Unicode character code (4 bytes)
|
|
5
|
-
_padding1: u32, // Padding (4 bytes)
|
|
6
|
-
_padding2: u32, // Extra padding (4 bytes)
|
|
7
|
-
_padding3: u32, // Extra padding (4 bytes) - total now 48 bytes (16-byte aligned)
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
struct CellBuffer {
|
|
11
|
-
cells: array<CellResult>
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
struct SuperSamplingParams {
|
|
15
|
-
width: u32, // Canvas width in pixels
|
|
16
|
-
height: u32, // Canvas height in pixels
|
|
17
|
-
sampleAlgo: u32, // 0 = standard 2x2, 1 = pre-squeezed horizontal blend
|
|
18
|
-
_padding: u32, // Padding for 16-byte alignment
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
@group(0) @binding(0) var inputTexture: texture_2d<f32>;
|
|
22
|
-
@group(0) @binding(1) var<storage, read_write> output: CellBuffer;
|
|
23
|
-
@group(0) @binding(2) var<uniform> params: SuperSamplingParams;
|
|
24
|
-
|
|
25
|
-
// Quadrant character lookup table (same as Zig implementation)
|
|
26
|
-
const quadrantChars = array<u32, 16>(
|
|
27
|
-
32u, // ' ' - 0000
|
|
28
|
-
0x2597u, // ▗ - 0001 BR
|
|
29
|
-
0x2596u, // ▖ - 0010 BL
|
|
30
|
-
0x2584u, // ▄ - 0011 Lower Half Block
|
|
31
|
-
0x259Du, // ▝ - 0100 TR
|
|
32
|
-
0x2590u, // ▐ - 0101 Right Half Block
|
|
33
|
-
0x259Eu, // ▞ - 0110 TR+BL
|
|
34
|
-
0x259Fu, // ▟ - 0111 TR+BL+BR
|
|
35
|
-
0x2598u, // ▘ - 1000 TL
|
|
36
|
-
0x259Au, // ▚ - 1001 TL+BR
|
|
37
|
-
0x258Cu, // ▌ - 1010 Left Half Block
|
|
38
|
-
0x2599u, // ▙ - 1011 TL+BL+BR
|
|
39
|
-
0x2580u, // ▀ - 1100 Upper Half Block
|
|
40
|
-
0x259Cu, // ▜ - 1101 TL+TR+BR
|
|
41
|
-
0x259Bu, // ▛ - 1110 TL+TR+BL
|
|
42
|
-
0x2588u // █ - 1111 Full Block
|
|
43
|
-
);
|
|
44
|
-
|
|
45
|
-
const inv_255: f32 = 1.0 / 255.0;
|
|
46
|
-
|
|
47
|
-
fn getPixelColor(pixelX: u32, pixelY: u32) -> vec4<f32> {
|
|
48
|
-
if (pixelX >= params.width || pixelY >= params.height) {
|
|
49
|
-
return vec4<f32>(0.0, 0.0, 0.0, 1.0); // Black for out-of-bounds
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// textureLoad automatically handles format conversion to RGBA
|
|
53
|
-
return textureLoad(inputTexture, vec2<i32>(i32(pixelX), i32(pixelY)), 0);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
fn colorDistance(a: vec4<f32>, b: vec4<f32>) -> f32 {
|
|
57
|
-
let diff = a.rgb - b.rgb;
|
|
58
|
-
return dot(diff, diff);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
fn luminance(color: vec4<f32>) -> f32 {
|
|
62
|
-
return 0.2126 * color.r + 0.7152 * color.g + 0.0722 * color.b;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
fn closestColorIndex(pixel: vec4<f32>, candA: vec4<f32>, candB: vec4<f32>) -> u32 {
|
|
66
|
-
return select(1u, 0u, colorDistance(pixel, candA) <= colorDistance(pixel, candB));
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
fn averageColor(pixels: array<vec4<f32>, 4>) -> vec4<f32> {
|
|
70
|
-
return (pixels[0] + pixels[1] + pixels[2] + pixels[3]) * 0.25;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
fn blendColors(color1: vec4<f32>, color2: vec4<f32>) -> vec4<f32> {
|
|
74
|
-
let a1 = color1.a;
|
|
75
|
-
let a2 = color2.a;
|
|
76
|
-
|
|
77
|
-
if (a1 == 0.0 && a2 == 0.0) {
|
|
78
|
-
return vec4<f32>(0.0, 0.0, 0.0, 0.0);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
let outAlpha = a1 + a2 - a1 * a2;
|
|
82
|
-
if (outAlpha == 0.0) {
|
|
83
|
-
return vec4<f32>(0.0, 0.0, 0.0, 0.0);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
let rgb = (color1.rgb * a1 + color2.rgb * a2 * (1.0 - a1)) / outAlpha;
|
|
87
|
-
|
|
88
|
-
return vec4<f32>(rgb, outAlpha);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
fn averageColorsWithAlpha(pixels: array<vec4<f32>, 4>) -> vec4<f32> {
|
|
92
|
-
let blend1 = blendColors(pixels[0], pixels[1]);
|
|
93
|
-
let blend2 = blendColors(pixels[2], pixels[3]);
|
|
94
|
-
|
|
95
|
-
return blendColors(blend1, blend2);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
fn renderQuadrantBlock(pixels: array<vec4<f32>, 4>) -> CellResult {
|
|
99
|
-
var maxDist: f32 = colorDistance(pixels[0], pixels[1]);
|
|
100
|
-
var pIdxA: u32 = 0u;
|
|
101
|
-
var pIdxB: u32 = 1u;
|
|
102
|
-
|
|
103
|
-
for (var i: u32 = 0u; i < 4u; i++) {
|
|
104
|
-
for (var j: u32 = i + 1u; j < 4u; j++) {
|
|
105
|
-
let dist = colorDistance(pixels[i], pixels[j]);
|
|
106
|
-
if (dist > maxDist) {
|
|
107
|
-
pIdxA = i;
|
|
108
|
-
pIdxB = j;
|
|
109
|
-
maxDist = dist;
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
let pCandA = pixels[pIdxA];
|
|
115
|
-
let pCandB = pixels[pIdxB];
|
|
116
|
-
|
|
117
|
-
var chosenDarkColor: vec4<f32>;
|
|
118
|
-
var chosenLightColor: vec4<f32>;
|
|
119
|
-
|
|
120
|
-
if (luminance(pCandA) <= luminance(pCandB)) {
|
|
121
|
-
chosenDarkColor = pCandA;
|
|
122
|
-
chosenLightColor = pCandB;
|
|
123
|
-
} else {
|
|
124
|
-
chosenDarkColor = pCandB;
|
|
125
|
-
chosenLightColor = pCandA;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
var quadrantBits: u32 = 0u;
|
|
129
|
-
let bitValues = array<u32, 4>(8u, 4u, 2u, 1u); // TL, TR, BL, BR
|
|
130
|
-
|
|
131
|
-
for (var i: u32 = 0u; i < 4u; i++) {
|
|
132
|
-
if (closestColorIndex(pixels[i], chosenDarkColor, chosenLightColor) == 0u) {
|
|
133
|
-
quadrantBits |= bitValues[i];
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
// Construct result
|
|
138
|
-
var result: CellResult;
|
|
139
|
-
|
|
140
|
-
if (quadrantBits == 0u) { // All light
|
|
141
|
-
result.char = 32u; // Space character
|
|
142
|
-
result.fg = chosenDarkColor;
|
|
143
|
-
result.bg = averageColorsWithAlpha(pixels);
|
|
144
|
-
} else if (quadrantBits == 15u) { // All dark
|
|
145
|
-
result.char = quadrantChars[15]; // Full block
|
|
146
|
-
result.fg = averageColorsWithAlpha(pixels);
|
|
147
|
-
result.bg = chosenLightColor;
|
|
148
|
-
} else { // Mixed pattern
|
|
149
|
-
result.char = quadrantChars[quadrantBits];
|
|
150
|
-
result.fg = chosenDarkColor;
|
|
151
|
-
result.bg = chosenLightColor;
|
|
152
|
-
}
|
|
153
|
-
result._padding1 = 0u;
|
|
154
|
-
result._padding2 = 0u;
|
|
155
|
-
result._padding3 = 0u;
|
|
156
|
-
|
|
157
|
-
return result;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
@compute @workgroup_size(${WORKGROUP_SIZE}, ${WORKGROUP_SIZE}, 1)
|
|
161
|
-
fn main(@builtin(global_invocation_id) id: vec3<u32>) {
|
|
162
|
-
let cellX = id.x;
|
|
163
|
-
let cellY = id.y;
|
|
164
|
-
let bufferWidthCells = (params.width + 1u) / 2u;
|
|
165
|
-
let bufferHeightCells = (params.height + 1u) / 2u;
|
|
166
|
-
|
|
167
|
-
if (cellX >= bufferWidthCells || cellY >= bufferHeightCells) {
|
|
168
|
-
return;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
let renderX = cellX * 2u;
|
|
172
|
-
let renderY = cellY * 2u;
|
|
173
|
-
|
|
174
|
-
var pixelsRgba: array<vec4<f32>, 4>;
|
|
175
|
-
|
|
176
|
-
if (params.sampleAlgo == 1u) {
|
|
177
|
-
let topColor = getPixelColor(renderX, renderY);
|
|
178
|
-
let topColor2 = getPixelColor(renderX + 1u, renderY);
|
|
179
|
-
|
|
180
|
-
let blendedTop = blendColors(topColor, topColor2);
|
|
181
|
-
|
|
182
|
-
let bottomColor = getPixelColor(renderX, renderY + 1u);
|
|
183
|
-
let bottomColor2 = getPixelColor(renderX + 1u, renderY + 1u);
|
|
184
|
-
let blendedBottom = blendColors(bottomColor, bottomColor2);
|
|
185
|
-
|
|
186
|
-
pixelsRgba[0] = blendedTop; // TL
|
|
187
|
-
pixelsRgba[1] = blendedTop; // TR
|
|
188
|
-
pixelsRgba[2] = blendedBottom; // BL
|
|
189
|
-
pixelsRgba[3] = blendedBottom; // BR
|
|
190
|
-
} else {
|
|
191
|
-
pixelsRgba[0] = getPixelColor(renderX, renderY); // TL
|
|
192
|
-
pixelsRgba[1] = getPixelColor(renderX + 1u, renderY); // TR
|
|
193
|
-
pixelsRgba[2] = getPixelColor(renderX, renderY + 1u); // BL
|
|
194
|
-
pixelsRgba[3] = getPixelColor(renderX + 1u, renderY + 1u); // BR
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
let cellResult = renderQuadrantBlock(pixelsRgba);
|
|
198
|
-
|
|
199
|
-
let outputIndex = cellY * bufferWidthCells + cellX;
|
|
200
|
-
output.cells[outputIndex] = cellResult;
|
|
201
|
-
}
|