@micahdb/tscon 0.0.0-stage → 0.1.1
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/LICENSE +21 -0
- package/README.md +44 -2
- package/dist/canvas.d.ts +147 -0
- package/dist/canvas.js +369 -0
- package/dist/colour.d.ts +56 -0
- package/dist/colour.js +77 -0
- package/dist/cp437.d.ts +48 -0
- package/dist/cp437.js +124 -0
- package/dist/glyphs.d.ts +222 -0
- package/dist/glyphs.js +449 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +9 -0
- package/dist/math.d.ts +214 -0
- package/dist/math.js +499 -0
- package/dist/moderndos.d.ts +1 -0
- package/dist/moderndos.js +322 -0
- package/dist/program.d.ts +72 -0
- package/dist/program.js +127 -0
- package/dist/rect.d.ts +26 -0
- package/dist/rect.js +1 -0
- package/dist/renderer.d.ts +59 -0
- package/dist/renderer.js +172 -0
- package/dist/shaders.d.ts +5 -0
- package/dist/shaders.js +470 -0
- package/dist/terminal.d.ts +92 -0
- package/dist/terminal.js +275 -0
- package/dist/texture.d.ts +66 -0
- package/dist/texture.js +119 -0
- package/package.json +44 -5
package/dist/renderer.js
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { renderer_frag, renderer_vert } from "./shaders.js";
|
|
2
|
+
import { Canvas } from "./canvas.js";
|
|
3
|
+
import { compileProgram, getAttribLocations, getUniformLocations, } from "./program.js";
|
|
4
|
+
/**
|
|
5
|
+
* Renders a source texture as glyphs onto a {@link Canvas}.
|
|
6
|
+
*
|
|
7
|
+
* `Renderer` draws a grid of {@link TexGlyphs} cells, sampling an
|
|
8
|
+
* application-provided texture per cell according to each cell's
|
|
9
|
+
* {@link TexGlyphMode}. An application renders into a texture (e.g. a
|
|
10
|
+
* framebuffer object) and then calls {@link draw} with a matching
|
|
11
|
+
* {@link TexGlyphs} grid to draw that texture directly or as dithered
|
|
12
|
+
* coloured characters.
|
|
13
|
+
*/
|
|
14
|
+
export class Renderer {
|
|
15
|
+
gl_program;
|
|
16
|
+
attributes;
|
|
17
|
+
uniforms;
|
|
18
|
+
vao;
|
|
19
|
+
vbo;
|
|
20
|
+
resized;
|
|
21
|
+
/** The {@link Canvas} this renderer draws into. */
|
|
22
|
+
canvas;
|
|
23
|
+
/**
|
|
24
|
+
* Whether to use {@link Canvas.user_font} for non-box glyphs when
|
|
25
|
+
* available. Set to `true` by the application after assigning a font
|
|
26
|
+
* texture to `canvas.user_font`; automatically falls back to the bitmap
|
|
27
|
+
* font if `user_font` is `null`.
|
|
28
|
+
*/
|
|
29
|
+
use_user_font;
|
|
30
|
+
/**
|
|
31
|
+
* Initializes the renderer for use.
|
|
32
|
+
* Listens for `"resize"` events from the canvas.
|
|
33
|
+
*
|
|
34
|
+
* @param canvas The canvas to draw into.
|
|
35
|
+
*/
|
|
36
|
+
constructor(canvas) {
|
|
37
|
+
this.canvas = canvas;
|
|
38
|
+
const gl = canvas.gl;
|
|
39
|
+
this.gl_program = compileProgram(gl, renderer_vert, renderer_frag);
|
|
40
|
+
this.attributes = getAttribLocations(gl, this.gl_program, {
|
|
41
|
+
row: "a_row",
|
|
42
|
+
col: "a_col",
|
|
43
|
+
mode: "a_mode",
|
|
44
|
+
});
|
|
45
|
+
this.uniforms = getUniformLocations(gl, this.gl_program, {
|
|
46
|
+
row: "u_row",
|
|
47
|
+
col: "u_col",
|
|
48
|
+
rows: "u_rows",
|
|
49
|
+
cols: "u_cols",
|
|
50
|
+
canvas_rows: "u_canvas_rows",
|
|
51
|
+
canvas_cols: "u_canvas_cols",
|
|
52
|
+
mouse_row: "u_mouse_row",
|
|
53
|
+
mouse_col: "u_mouse_col",
|
|
54
|
+
palette: "u_palette",
|
|
55
|
+
bitmap_font: "u_bitmap_font",
|
|
56
|
+
texture: "u_texture",
|
|
57
|
+
user_font: "u_user_font",
|
|
58
|
+
use_user_font: "u_use_user_font",
|
|
59
|
+
});
|
|
60
|
+
const vao = gl.createVertexArray();
|
|
61
|
+
this.vao = vao;
|
|
62
|
+
if (!vao) {
|
|
63
|
+
throw new Error("Could not create vertex array object");
|
|
64
|
+
}
|
|
65
|
+
gl.bindVertexArray(vao);
|
|
66
|
+
const vbo = gl.createBuffer();
|
|
67
|
+
this.vbo = vbo;
|
|
68
|
+
if (!vbo) {
|
|
69
|
+
throw new Error("Could not create vertex buffer");
|
|
70
|
+
}
|
|
71
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
|
|
72
|
+
// every vertex in the VBO is a 32-bit integer split like so:
|
|
73
|
+
// [ row | mode | col ]
|
|
74
|
+
// [ 8-bits | 8-bits | 16-bits ]
|
|
75
|
+
// a_row
|
|
76
|
+
gl.vertexAttribIPointer(this.attributes.row, 1, gl.UNSIGNED_BYTE, 4, 3);
|
|
77
|
+
gl.vertexAttribDivisor(this.attributes.row, 1);
|
|
78
|
+
gl.enableVertexAttribArray(this.attributes.row);
|
|
79
|
+
// a_mode
|
|
80
|
+
gl.vertexAttribIPointer(this.attributes.mode, 1, gl.UNSIGNED_BYTE, 4, 2);
|
|
81
|
+
gl.vertexAttribDivisor(this.attributes.mode, 1);
|
|
82
|
+
gl.enableVertexAttribArray(this.attributes.mode);
|
|
83
|
+
// a_col
|
|
84
|
+
gl.vertexAttribIPointer(this.attributes.col, 1, gl.UNSIGNED_SHORT, 4, 0);
|
|
85
|
+
gl.vertexAttribDivisor(this.attributes.col, 1);
|
|
86
|
+
gl.enableVertexAttribArray(this.attributes.col);
|
|
87
|
+
gl.bindVertexArray(null);
|
|
88
|
+
gl.useProgram(this.gl_program);
|
|
89
|
+
gl.uniform1i(this.uniforms.row, 0);
|
|
90
|
+
gl.uniform1i(this.uniforms.col, 0);
|
|
91
|
+
gl.uniform1i(this.uniforms.rows, 0);
|
|
92
|
+
gl.uniform1i(this.uniforms.cols, 0);
|
|
93
|
+
gl.uniform1i(this.uniforms.canvas_rows, this.canvas.rows);
|
|
94
|
+
gl.uniform1i(this.uniforms.canvas_cols, this.canvas.cols);
|
|
95
|
+
gl.uniform3fv(this.uniforms.palette, this.canvas.palette);
|
|
96
|
+
gl.uniform1i(this.uniforms.bitmap_font, 0);
|
|
97
|
+
gl.uniform1i(this.uniforms.texture, 1);
|
|
98
|
+
gl.uniform1i(this.uniforms.user_font, 2);
|
|
99
|
+
gl.uniform1i(this.uniforms.use_user_font, 0);
|
|
100
|
+
this.resized = false;
|
|
101
|
+
this.use_user_font = false;
|
|
102
|
+
this.canvas.addEventListener("resize", () => {
|
|
103
|
+
this.resized = true;
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Draw a {@link TexGlyphs} grid sampling `texture` into a region of the
|
|
108
|
+
* canvas.
|
|
109
|
+
*
|
|
110
|
+
* `tglyphs.rows`/`cols` must match `dst.rows`/`cols`; otherwise the
|
|
111
|
+
* draw is skipped. `dst` is in canvas cell coordinates and may extend
|
|
112
|
+
* beyond the canvas; the shader clips via NDC. Use a `tglyphs` grid
|
|
113
|
+
* sized to the texture's aspect so the texture isn't distorted.
|
|
114
|
+
*
|
|
115
|
+
* @example
|
|
116
|
+
* ```ts
|
|
117
|
+
* renderer.draw(tglyphs, texture, {
|
|
118
|
+
* row: 0, col: 0,
|
|
119
|
+
* rows: tglyphs.rows, cols: tglyphs.cols,
|
|
120
|
+
* });
|
|
121
|
+
* ```
|
|
122
|
+
*
|
|
123
|
+
* @param tglyphs Texture-glyph grid describing how to sample.
|
|
124
|
+
* @param texture Source texture to sample from.
|
|
125
|
+
* @param dst Destination region on the canvas (cells).
|
|
126
|
+
*/
|
|
127
|
+
draw(tglyphs, texture, dst) {
|
|
128
|
+
if (tglyphs.rows !== dst.rows || tglyphs.cols !== dst.cols) {
|
|
129
|
+
console.log("Renderer.draw: skipping due to bad tglyphs or dst");
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
const gl = this.canvas.gl;
|
|
133
|
+
gl.useProgram(this.gl_program);
|
|
134
|
+
if (this.resized) {
|
|
135
|
+
this.resized = false;
|
|
136
|
+
gl.uniform1i(this.uniforms.canvas_rows, this.canvas.rows);
|
|
137
|
+
gl.uniform1i(this.uniforms.canvas_cols, this.canvas.cols);
|
|
138
|
+
}
|
|
139
|
+
// set destination information uniforms
|
|
140
|
+
gl.uniform1i(this.uniforms.row, dst.row);
|
|
141
|
+
gl.uniform1i(this.uniforms.col, dst.col);
|
|
142
|
+
gl.uniform1i(this.uniforms.rows, tglyphs.rows);
|
|
143
|
+
gl.uniform1i(this.uniforms.cols, tglyphs.cols);
|
|
144
|
+
gl.uniform1i(this.uniforms.mouse_row, this.canvas.mouse_row ?? -1);
|
|
145
|
+
gl.uniform1i(this.uniforms.mouse_col, this.canvas.mouse_col ?? -1);
|
|
146
|
+
// set vertex buffer data to provided texture glyphs
|
|
147
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, this.vbo);
|
|
148
|
+
gl.bufferData(gl.ARRAY_BUFFER, tglyphs.data, gl.DYNAMIC_DRAW);
|
|
149
|
+
// bind and activate bitmap font
|
|
150
|
+
gl.activeTexture(gl.TEXTURE0);
|
|
151
|
+
gl.bindTexture(gl.TEXTURE_2D, this.canvas.bitmap_font);
|
|
152
|
+
// bind and activate provided texture
|
|
153
|
+
gl.activeTexture(gl.TEXTURE1);
|
|
154
|
+
gl.bindTexture(gl.TEXTURE_2D, texture);
|
|
155
|
+
// user application-provided font
|
|
156
|
+
if (this.use_user_font) {
|
|
157
|
+
gl.uniform1i(this.uniforms.use_user_font, 1);
|
|
158
|
+
}
|
|
159
|
+
if (this.canvas.user_font !== null) {
|
|
160
|
+
gl.activeTexture(gl.TEXTURE2);
|
|
161
|
+
gl.bindTexture(gl.TEXTURE_2D, this.canvas.user_font);
|
|
162
|
+
}
|
|
163
|
+
else if (this.use_user_font) {
|
|
164
|
+
this.use_user_font = false;
|
|
165
|
+
gl.uniform1i(this.uniforms.use_user_font, 0);
|
|
166
|
+
}
|
|
167
|
+
// draw the glyphs covered by the provided texture glyphs
|
|
168
|
+
gl.bindVertexArray(this.vao);
|
|
169
|
+
gl.drawArraysInstanced(gl.TRIANGLES, 0, 6, tglyphs.rows * tglyphs.cols);
|
|
170
|
+
gl.bindVertexArray(null);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare const glyph_frag = "// Computes the mix value for a coordinate relative to pixels, that will\n// snap to the center of the pixel if it is in the center 50% of it, and\n// otherwise interpolate linearly outside of the center 50%.\nfloat sharp_bilinear_weight(float coord) {\n\t// distance of coord to center of cooresponding pixel\n\tfloat center_dist = fract(coord) - 0.5;\n\n\t// the component of coord that is within 50% of the center of the pixel\n\tfloat center_comp = clamp(center_dist, -0.25, 0.25);\n\n\t// when center_dist is outside of the center 50% of the pixel, this\n\t// will be an interpolation between this pixel and its neighbour\n\tfloat interp_coord = floor(coord) + (center_dist - center_comp) * 2.0;\n\n\t// this will be 0 when coord is in the center 50% of the pixel\n\treturn interp_coord - floor(coord);\n}\n\nfloat bit_value(uvec4 texel, int x, int y) {\n\treturn float((texel[y] >> (7 - x)) & 1u);\n}\n\nfloat bitmap_glyph_value(\n\tmediump usampler2D bitmap,\n\tivec2 glyph_coord,\n\tvec2 cell_coord\n) {\n\tint block = clamp(int(floor(cell_coord.y * 4.0)), 0, 3);\n\tivec2 texel_coord = ivec2(glyph_coord.x, glyph_coord.y + block);\n\tuvec4 texel = texelFetch(bitmap, texel_coord, 0);\n\n\t// each block is 8px wide\n\tfloat block_x = clamp(cell_coord.x * 8.0, 0.0, 7.0);\n\n\t// each cell is 4 blocks high, each block being 4px tall\n\tfloat block_y = clamp(cell_coord.y * 16.0 - float(block) * 4.0,\n\t\t\t 0.0, 3.0);\n\n\tfloat horiz_weight = sharp_bilinear_weight(block_x);\n\tfloat vert_weight = sharp_bilinear_weight(block_y);\n\n\tint x = clamp(int(block_x), 0, 7);\n\tint y = clamp(int(block_y), 0, 3);\n\n\t// get each bit\n\tfloat tl = bit_value(texel, x, y);\n\tfloat tr = bit_value(texel, x + 1, y);\n\tfloat bl = bit_value(texel, x, y + 1);\n\tfloat br = bit_value(texel, x + 1, y + 1);\n\n\tfloat top = mix(tl, tr, horiz_weight);\n\tfloat bot = mix(bl, br, horiz_weight);\n\n\treturn mix(top, bot, vert_weight);\n}\n";
|
|
2
|
+
export declare const terminal_vert = "#version 300 es\n\nin uint a_colour;\nin uint a_char_code;\n\nuniform int u_rows;\nuniform int u_cols;\nuniform int u_mouse_row;\nuniform int u_mouse_col;\nuniform vec3 u_palette[16];\n\nout vec2 v_cell_coord;\nout vec2 v_uv_coord;\n\nflat out vec3 f_fg_colour;\nflat out vec3 f_bg_colour;\nflat out ivec2 f_glyph_coord;\nflat out uint f_char_code;\n\nvoid main() {\n\tint row = gl_InstanceID / u_cols;\n\tint col = gl_InstanceID % u_cols;\n\tint is_cursor = int(u_mouse_row == row && u_mouse_col == col);\n\tint not_is_cursor = int(is_cursor == 0);\n\n\tif (a_colour == 0u && a_char_code == 0u) {\n\t\t// clipped: don't cover any pixels below this glyph\n\t\t// e.g., a previously rendered texture\n\t\tgl_Position = vec4(2.0, 2.0, 2.0, 1.0);\n\t\treturn;\n\t}\n\n\t// 240 = 0b11110000\n\tint colour = int(a_colour + uint(a_colour == 0u) * 240u);\n\n\tint fg_idx = int(colour >> (not_is_cursor * 4) & 0xf);\n\tint bg_idx = int(colour >> (is_cursor * 4) & 0xf);\n\n\tf_fg_colour = u_palette[fg_idx];\n\tf_bg_colour = u_palette[bg_idx];\n\n\t// 32 glyph columns, 8 glyph rows\n\tint glyph_row = clamp(int(a_char_code / 32u), 0, 7);\n\tint glyph_col = clamp(int(a_char_code % 32u), 0, 31);\n\n\t// coordinate of a_char_code in u_bitmap_font\n\t// (note: there are 4 pixels per glyph, in a column; hence row * 4)\n\tf_glyph_coord = ivec2(glyph_col, glyph_row * 4);\n\n\t// used when checking whether to use u_user_font or not\n\tf_char_code = a_char_code;\n\n\t/* A glyph is made up of two triangles, six vertices:\n\t *\n\t * 0--2 ,5 0--2,5\n\t * | / + / | = | |\n\t * 1' 3--4 1,3--4\n\t */\n\n\tint row_inc = int(gl_VertexID == 1 || gl_VertexID == 3 ||\n\t\t\t gl_VertexID == 4);\n\n\tint col_inc = int(gl_VertexID == 2 || gl_VertexID == 4 ||\n\t\t\t gl_VertexID == 5);\n\n\tv_cell_coord = vec2(float(col_inc), float(row_inc));\n\n\trow += row_inc;\n\tcol += col_inc;\n\n\tv_uv_coord = vec2(float(glyph_col + col_inc) / 32.0,\n\t\t\t float(glyph_row + row_inc) / 8.0);\n\n\t// position needs to be in [-1.0, 1.0]\n\tfloat ndc_x = 2.0 * float(col) / float(u_cols) - 1.0;\n\tfloat ndc_y = -2.0 * float(row) / float(u_rows) + 1.0;\n\n\tgl_Position = vec4(ndc_x, ndc_y, 0.0, 1.0);\n}\n";
|
|
3
|
+
export declare const terminal_frag = "#version 300 es\n\nprecision mediump float;\n\n// Computes the mix value for a coordinate relative to pixels, that will\n// snap to the center of the pixel if it is in the center 50% of it, and\n// otherwise interpolate linearly outside of the center 50%.\nfloat sharp_bilinear_weight(float coord) {\n\t// distance of coord to center of cooresponding pixel\n\tfloat center_dist = fract(coord) - 0.5;\n\n\t// the component of coord that is within 50% of the center of the pixel\n\tfloat center_comp = clamp(center_dist, -0.25, 0.25);\n\n\t// when center_dist is outside of the center 50% of the pixel, this\n\t// will be an interpolation between this pixel and its neighbour\n\tfloat interp_coord = floor(coord) + (center_dist - center_comp) * 2.0;\n\n\t// this will be 0 when coord is in the center 50% of the pixel\n\treturn interp_coord - floor(coord);\n}\n\nfloat bit_value(uvec4 texel, int x, int y) {\n\treturn float((texel[y] >> (7 - x)) & 1u);\n}\n\nfloat bitmap_glyph_value(\n\tmediump usampler2D bitmap,\n\tivec2 glyph_coord,\n\tvec2 cell_coord\n) {\n\tint block = clamp(int(floor(cell_coord.y * 4.0)), 0, 3);\n\tivec2 texel_coord = ivec2(glyph_coord.x, glyph_coord.y + block);\n\tuvec4 texel = texelFetch(bitmap, texel_coord, 0);\n\n\t// each block is 8px wide\n\tfloat block_x = clamp(cell_coord.x * 8.0, 0.0, 7.0);\n\n\t// each cell is 4 blocks high, each block being 4px tall\n\tfloat block_y = clamp(cell_coord.y * 16.0 - float(block) * 4.0,\n\t\t\t 0.0, 3.0);\n\n\tfloat horiz_weight = sharp_bilinear_weight(block_x);\n\tfloat vert_weight = sharp_bilinear_weight(block_y);\n\n\tint x = clamp(int(block_x), 0, 7);\n\tint y = clamp(int(block_y), 0, 3);\n\n\t// get each bit\n\tfloat tl = bit_value(texel, x, y);\n\tfloat tr = bit_value(texel, x + 1, y);\n\tfloat bl = bit_value(texel, x, y + 1);\n\tfloat br = bit_value(texel, x + 1, y + 1);\n\n\tfloat top = mix(tl, tr, horiz_weight);\n\tfloat bot = mix(bl, br, horiz_weight);\n\n\treturn mix(top, bot, vert_weight);\n}\n\n\nin vec2 v_cell_coord;\nin vec2 v_uv_coord;\n\nflat in vec3 f_fg_colour;\nflat in vec3 f_bg_colour;\nflat in ivec2 f_glyph_coord;\nflat in uint f_char_code;\n\nuniform mediump usampler2D u_bitmap_font;\nuniform sampler2D u_user_font;\nuniform bool u_use_user_font;\n\nout vec4 frag_colour;\n\nvoid main() {\n\t// 176U and 191U are the first and last box-drawing characters\n\tif (u_use_user_font && (f_char_code < 176U || f_char_code >= 224U)) {\n\t\tvec4 samp = texture(u_user_font, v_uv_coord);\n\t\tfrag_colour = vec4(mix(f_bg_colour, f_fg_colour, samp.r), 1.0);\n\t\treturn;\n\t}\n\n\tfloat value = bitmap_glyph_value(u_bitmap_font, f_glyph_coord,\n\t\t\t\t\t v_cell_coord);\n\tfrag_colour = vec4(mix(f_bg_colour, f_fg_colour, value), 1.0);\n}\n";
|
|
4
|
+
export declare const renderer_vert = "#version 300 es\n\n#define SAMPLE_MODE\t0u\n#define GLYPHS_MODE\t1u\n#define BG_GLYPHS_MODE\t2u\n\nprecision mediump float;\n\nin uint a_row;\nin uint a_col;\nin uint a_mode;\n\nuniform int u_row;\nuniform int u_col;\nuniform int u_rows;\nuniform int u_cols;\nuniform int u_canvas_rows;\nuniform int u_canvas_cols;\nuniform int u_mouse_row;\nuniform int u_mouse_col;\nuniform vec3 u_palette[16];\nuniform sampler2D u_texture;\n\nout vec2 v_cell_coord;\nout vec2 v_tex_uv_coord; // sample mode\nout vec2 v_font_uv_coord;\n\nflat out uint f_mode;\nflat out int f_is_cursor;\nflat out vec3 f_fg_colour;\nflat out vec3 f_bg_colour;\nflat out ivec2 f_glyph_coord;\nflat out int f_char_code;\n\nconst vec3 dimensions[8] = vec3[](\n\tvec3(0.0, 0.0, 0.0),\t// black (unused)\n\tvec3(1.0, 0.8, 0.8),\t// red\n\tvec3(0.8, 1.0, 0.8),\t// green\n\tvec3(1.0, 1.0, 0.8),\t// yellow\n\tvec3(0.8, 0.8, 1.0),\t// blue\n\tvec3(1.0, 0.8, 1.0),\t// magenta\n\tvec3(0.8, 1.0, 1.0),\t// cyan\n\tvec3(1.0, 1.0, 1.0)\t// white\n);\n\nstruct Dims {\n\tint dim1;\n\tint dim2;\n\tfloat ratio;\n};\n\nDims getDims(vec3 colour) {\n\tvec3 ncolour = normalize(colour);\n\tfloat d1 = 1000.0;\n\tfloat d2 = 1000.0;\n\tint dim1 = 0;\n\tint dim2 = 0;\n\n\tfor (int i = 1; i < 8; i++) {\n\t\tfloat d = distance(ncolour, normalize(dimensions[i]));\n\n\t\tif (d < d1) {\n\t\t\td2 = d1;\n\t\t\tdim2 = dim1;\n\t\t\td1 = d;\n\t\t\tdim1 = i;\n\t\t} else if (d < d2) {\n\t\t\td2 = d;\n\t\t\tdim2 = i;\n\t\t}\n\t}\n\n\treturn Dims(dim1, dim2, d1 / (d1 + d2));\n}\n\nfloat getLum(vec3 colour, int dim) {\n\tvec3 dcolour = dimensions[dim];\n\treturn dot(colour, dcolour) / dot(dcolour, dcolour);\n}\n\nstruct Glyph {\n\tint char_code;\n\tint fg_idx;\n\tint bg_idx;\n};\n\nGlyph getGlyph(int dim, float lum) {\n\t// . , - : ; = ! * # $ @\n\tint glyphs[12] = int[](0, 46, 44, 45, 58, 59, 61, 33, 42, 35, 36, 64);\n\n\tint fgs[2] = int[](dim, dim + 8);\n\n\tif (dim == 7) {\n\t\tfgs = int[](8, 15);\n\t}\n\n\tint layer = clamp(int(lum * 2.0), 0, 2);\n\n\tGlyph glyph;\n\n\tint glyph_idx = clamp(int(12.0 * lum), 0, 11);\n\tglyph.char_code = glyphs[glyph_idx];\n\tglyph.fg_idx = fgs[layer];\n\tglyph.bg_idx = 0;\n\n\treturn glyph;\n}\n\nGlyph getBgGlyph(int dim, float lum) {\n\t// . - , : ; = ! * # $ @\n\tint glyphs[12] = int[](0, 46, 45, 44, 58, 59, 61, 33, 42, 35, 36, 64);\n\n\tint fgs[3] = int[](dim, dim + 8, 15);\n\tint bgs[3] = int[](0, dim, dim + 8);\n\n\tint layer = clamp(int(lum * 3.0), 0, 2);\n\n\t// check if dimension is white\n\tif (dim == 7) {\n\t\tif (layer > 0) {\n\t\t\t// reverse the order of the glyphs\n\t\t\tglyphs = int[](64, 36, 35, 42, 33, 61, 59, 58, 44, 45,\n\t\t\t\t 46, 0);\n\t\t}\n\n\t\tfgs = int[](7, 0, 8);\n\t\tbgs = int[](0, 7, 15);\n\t}\n\n\tGlyph glyph;\n\n\tint glyph_idx = clamp(int(12.0 * (3.0 * lum - float(layer))), 0, 11);\n\tglyph.char_code = glyphs[glyph_idx];\n\tglyph.fg_idx = fgs[layer];\n\tglyph.bg_idx = bgs[layer];\n\n\treturn glyph;\n}\n\nvoid main() {\n\tint origin_row = int(a_row);\n\tint origin_col = int(a_col);\n\n\tf_is_cursor = int(origin_row + u_row == u_mouse_row &&\n\t\t\t origin_col + u_col == u_mouse_col);\n\n\t/* A glyph is made up of two triangles, six vertices:\n\t *\n\t * 0--2 ,5 0--2,5\n\t * | / + / | = | |\n\t * 1' 3--4 1,3--4\n\t */\n\n\tint row_inc = int(gl_VertexID == 1 || gl_VertexID == 3 ||\n\t\t\t gl_VertexID == 4);\n\n\tint col_inc = int(gl_VertexID == 2 || gl_VertexID == 4 ||\n\t\t\t gl_VertexID == 5);\n\n\tv_cell_coord = vec2(float(col_inc), float(row_inc));\n\n\tint row = origin_row + row_inc;\n\tint col = origin_col + col_inc;\n\n\tv_tex_uv_coord = vec2(float(col) / float(u_cols),\n\t\t\t float(row) / float(u_rows));\n\n\t// position needs to be in [-1.0, 1.0]\n\tfloat ndc_x = 2.0 * float(u_col + col) / float(u_canvas_cols) - 1.0;\n\tfloat ndc_y = -2.0 * float(u_row + row) / float(u_canvas_rows) + 1.0;\n\n\tgl_Position = vec4(ndc_x, ndc_y, 0.0, 1.0);\n\n\tf_mode = a_mode;\n\n\t// sample mode\n\tif (f_mode == SAMPLE_MODE) {\n\t\t// no more work to do\n\t\treturn;\n\t}\n\n\t// dithered glyphs mode\n\n\tvec2 origin_uv_coord = vec2(float(origin_col) / float(u_cols),\n\t\t\t\t float(origin_row) / float(u_rows));\n\n\tvec2 full_uv_cell = vec2(1.0 / float(u_cols), 1.0 / float(u_rows));\n\tvec4 samp = texture(u_texture, origin_uv_coord + 0.5 * full_uv_cell);\n\tvec3 colour = samp.rgb;\n\n\t// dithering\n\tfloat bayer[16] = float[](\n\t\t0.0, 0.5, 0.125, 0.625,\n\t\t0.75, 0.25, 0.875, 0.375,\n\t\t0.1875, 0.6875, 0.0625, 0.5625,\n\t\t0.9375, 0.4375, 0.8125, 0.3125\n\t);\n\tfloat bayer_val = bayer[int(origin_row) % 4 * 4 + int(origin_col) % 4];\n\n\tDims dims = getDims(colour);\n\tint dim = (bayer_val < dims.ratio) ? dims.dim2 : dims.dim1;\n\tfloat lum = getLum(colour, dim);\n\n\tfloat dither = (bayer_val - 0.5) / 12.0;\n\tlum = clamp(lum + dither, 0.0, 1.0);\n\n\tGlyph glyph;\n\n\tif (f_mode == BG_GLYPHS_MODE) {\n\t\tglyph = getBgGlyph(dim, lum);\n\t} else {\n\t\tglyph = getGlyph(dim, lum);\n\t}\n\n\tif (glyph.fg_idx == glyph.bg_idx && f_is_cursor == 1) {\n\t\tglyph.fg_idx = 15;\n\t\tglyph.bg_idx = 0;\n\t}\n\n\tf_fg_colour = u_palette[glyph.fg_idx];\n\tf_bg_colour = u_palette[glyph.bg_idx];\n\n\t// 32 glyph columns, 8 glyph rows\n\tint glyph_row = clamp(int(glyph.char_code / 32), 0, 7);\n\tint glyph_col = clamp(int(glyph.char_code % 32), 0, 31);\n\n\t// coordinate of char_code in u_bitmap_font\n\tf_glyph_coord = ivec2(glyph_col, glyph_row << 2);\n\n\tf_char_code = glyph.char_code;\n\n\tv_font_uv_coord = vec2(float(glyph_col + col_inc) / 32.0,\n\t\t\t float(glyph_row + row_inc) / 8.0);\n}\n";
|
|
5
|
+
export declare const renderer_frag = "#version 300 es\n\nprecision mediump float;\n\n// Computes the mix value for a coordinate relative to pixels, that will\n// snap to the center of the pixel if it is in the center 50% of it, and\n// otherwise interpolate linearly outside of the center 50%.\nfloat sharp_bilinear_weight(float coord) {\n\t// distance of coord to center of cooresponding pixel\n\tfloat center_dist = fract(coord) - 0.5;\n\n\t// the component of coord that is within 50% of the center of the pixel\n\tfloat center_comp = clamp(center_dist, -0.25, 0.25);\n\n\t// when center_dist is outside of the center 50% of the pixel, this\n\t// will be an interpolation between this pixel and its neighbour\n\tfloat interp_coord = floor(coord) + (center_dist - center_comp) * 2.0;\n\n\t// this will be 0 when coord is in the center 50% of the pixel\n\treturn interp_coord - floor(coord);\n}\n\nfloat bit_value(uvec4 texel, int x, int y) {\n\treturn float((texel[y] >> (7 - x)) & 1u);\n}\n\nfloat bitmap_glyph_value(\n\tmediump usampler2D bitmap,\n\tivec2 glyph_coord,\n\tvec2 cell_coord\n) {\n\tint block = clamp(int(floor(cell_coord.y * 4.0)), 0, 3);\n\tivec2 texel_coord = ivec2(glyph_coord.x, glyph_coord.y + block);\n\tuvec4 texel = texelFetch(bitmap, texel_coord, 0);\n\n\t// each block is 8px wide\n\tfloat block_x = clamp(cell_coord.x * 8.0, 0.0, 7.0);\n\n\t// each cell is 4 blocks high, each block being 4px tall\n\tfloat block_y = clamp(cell_coord.y * 16.0 - float(block) * 4.0,\n\t\t\t 0.0, 3.0);\n\n\tfloat horiz_weight = sharp_bilinear_weight(block_x);\n\tfloat vert_weight = sharp_bilinear_weight(block_y);\n\n\tint x = clamp(int(block_x), 0, 7);\n\tint y = clamp(int(block_y), 0, 3);\n\n\t// get each bit\n\tfloat tl = bit_value(texel, x, y);\n\tfloat tr = bit_value(texel, x + 1, y);\n\tfloat bl = bit_value(texel, x, y + 1);\n\tfloat br = bit_value(texel, x + 1, y + 1);\n\n\tfloat top = mix(tl, tr, horiz_weight);\n\tfloat bot = mix(bl, br, horiz_weight);\n\n\treturn mix(top, bot, vert_weight);\n}\n\n\n#define SAMPLE_MODE 0u\n\nin vec2 v_cell_coord;\nin vec2 v_tex_uv_coord; // f_mode == 0\nin vec2 v_font_uv_coord;\n\nflat in uint f_mode;\nflat in int f_is_cursor;\nflat in vec3 f_fg_colour;\nflat in vec3 f_bg_colour;\nflat in ivec2 f_glyph_coord;\nflat in int f_char_code;\n\nuniform mediump usampler2D u_bitmap_font;\nuniform sampler2D u_texture; // f_mode == 0\nuniform sampler2D u_user_font;\nuniform bool u_use_user_font;\n\nout vec4 frag_colour;\n\nvoid main() {\n\tif (f_mode == SAMPLE_MODE) {\n\t\tfrag_colour = texture(u_texture, v_tex_uv_coord);\n\n\t\t// invert the sample\n\t\tif (f_is_cursor == 1) {\n\t\t\tfrag_colour = vec4(vec3(1.0, 1.0, 1.0) -\n\t\t\t\t\t frag_colour.rgb, 1.0);\n\t\t}\n\n\t\treturn;\n\t}\n\n\tvec3 fg = f_fg_colour;\n\tvec3 bg = f_bg_colour;\n\n\tif (f_is_cursor == 1) {\n\t\tvec3 tmp = fg;\n\t\tfg = bg;\n\t\tbg = tmp;\n\t}\n\n\t// 176 and 191 are the first and last box-drawing characters\n\tif (u_use_user_font && (f_char_code < 176 || f_char_code >= 224)) {\n\t\tvec4 samp = texture(u_user_font, v_font_uv_coord);\n\t\tfrag_colour = vec4(mix(bg, fg, samp.r), 1.0);\n\t\treturn;\n\t}\n\n\tfloat value = bitmap_glyph_value(u_bitmap_font, f_glyph_coord,\n\t\t\t\t\t v_cell_coord);\n\tfrag_colour = vec4(mix(bg, fg, value), 1.0);\n}\n";
|
package/dist/shaders.js
ADDED
|
@@ -0,0 +1,470 @@
|
|
|
1
|
+
// generated by `bun run shaders`
|
|
2
|
+
export const glyph_frag = `\
|
|
3
|
+
// Computes the mix value for a coordinate relative to pixels, that will
|
|
4
|
+
// snap to the center of the pixel if it is in the center 50% of it, and
|
|
5
|
+
// otherwise interpolate linearly outside of the center 50%.
|
|
6
|
+
float sharp_bilinear_weight(float coord) {
|
|
7
|
+
// distance of coord to center of cooresponding pixel
|
|
8
|
+
float center_dist = fract(coord) - 0.5;
|
|
9
|
+
|
|
10
|
+
// the component of coord that is within 50% of the center of the pixel
|
|
11
|
+
float center_comp = clamp(center_dist, -0.25, 0.25);
|
|
12
|
+
|
|
13
|
+
// when center_dist is outside of the center 50% of the pixel, this
|
|
14
|
+
// will be an interpolation between this pixel and its neighbour
|
|
15
|
+
float interp_coord = floor(coord) + (center_dist - center_comp) * 2.0;
|
|
16
|
+
|
|
17
|
+
// this will be 0 when coord is in the center 50% of the pixel
|
|
18
|
+
return interp_coord - floor(coord);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
float bit_value(uvec4 texel, int x, int y) {
|
|
22
|
+
return float((texel[y] >> (7 - x)) & 1u);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
float bitmap_glyph_value(
|
|
26
|
+
mediump usampler2D bitmap,
|
|
27
|
+
ivec2 glyph_coord,
|
|
28
|
+
vec2 cell_coord
|
|
29
|
+
) {
|
|
30
|
+
int block = clamp(int(floor(cell_coord.y * 4.0)), 0, 3);
|
|
31
|
+
ivec2 texel_coord = ivec2(glyph_coord.x, glyph_coord.y + block);
|
|
32
|
+
uvec4 texel = texelFetch(bitmap, texel_coord, 0);
|
|
33
|
+
|
|
34
|
+
// each block is 8px wide
|
|
35
|
+
float block_x = clamp(cell_coord.x * 8.0, 0.0, 7.0);
|
|
36
|
+
|
|
37
|
+
// each cell is 4 blocks high, each block being 4px tall
|
|
38
|
+
float block_y = clamp(cell_coord.y * 16.0 - float(block) * 4.0,
|
|
39
|
+
0.0, 3.0);
|
|
40
|
+
|
|
41
|
+
float horiz_weight = sharp_bilinear_weight(block_x);
|
|
42
|
+
float vert_weight = sharp_bilinear_weight(block_y);
|
|
43
|
+
|
|
44
|
+
int x = clamp(int(block_x), 0, 7);
|
|
45
|
+
int y = clamp(int(block_y), 0, 3);
|
|
46
|
+
|
|
47
|
+
// get each bit
|
|
48
|
+
float tl = bit_value(texel, x, y);
|
|
49
|
+
float tr = bit_value(texel, x + 1, y);
|
|
50
|
+
float bl = bit_value(texel, x, y + 1);
|
|
51
|
+
float br = bit_value(texel, x + 1, y + 1);
|
|
52
|
+
|
|
53
|
+
float top = mix(tl, tr, horiz_weight);
|
|
54
|
+
float bot = mix(bl, br, horiz_weight);
|
|
55
|
+
|
|
56
|
+
return mix(top, bot, vert_weight);
|
|
57
|
+
}
|
|
58
|
+
`;
|
|
59
|
+
export const terminal_vert = `\
|
|
60
|
+
#version 300 es
|
|
61
|
+
|
|
62
|
+
in uint a_colour;
|
|
63
|
+
in uint a_char_code;
|
|
64
|
+
|
|
65
|
+
uniform int u_rows;
|
|
66
|
+
uniform int u_cols;
|
|
67
|
+
uniform int u_mouse_row;
|
|
68
|
+
uniform int u_mouse_col;
|
|
69
|
+
uniform vec3 u_palette[16];
|
|
70
|
+
|
|
71
|
+
out vec2 v_cell_coord;
|
|
72
|
+
out vec2 v_uv_coord;
|
|
73
|
+
|
|
74
|
+
flat out vec3 f_fg_colour;
|
|
75
|
+
flat out vec3 f_bg_colour;
|
|
76
|
+
flat out ivec2 f_glyph_coord;
|
|
77
|
+
flat out uint f_char_code;
|
|
78
|
+
|
|
79
|
+
void main() {
|
|
80
|
+
int row = gl_InstanceID / u_cols;
|
|
81
|
+
int col = gl_InstanceID % u_cols;
|
|
82
|
+
int is_cursor = int(u_mouse_row == row && u_mouse_col == col);
|
|
83
|
+
int not_is_cursor = int(is_cursor == 0);
|
|
84
|
+
|
|
85
|
+
if (a_colour == 0u && a_char_code == 0u) {
|
|
86
|
+
// clipped: don't cover any pixels below this glyph
|
|
87
|
+
// e.g., a previously rendered texture
|
|
88
|
+
gl_Position = vec4(2.0, 2.0, 2.0, 1.0);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// 240 = 0b11110000
|
|
93
|
+
int colour = int(a_colour + uint(a_colour == 0u) * 240u);
|
|
94
|
+
|
|
95
|
+
int fg_idx = int(colour >> (not_is_cursor * 4) & 0xf);
|
|
96
|
+
int bg_idx = int(colour >> (is_cursor * 4) & 0xf);
|
|
97
|
+
|
|
98
|
+
f_fg_colour = u_palette[fg_idx];
|
|
99
|
+
f_bg_colour = u_palette[bg_idx];
|
|
100
|
+
|
|
101
|
+
// 32 glyph columns, 8 glyph rows
|
|
102
|
+
int glyph_row = clamp(int(a_char_code / 32u), 0, 7);
|
|
103
|
+
int glyph_col = clamp(int(a_char_code % 32u), 0, 31);
|
|
104
|
+
|
|
105
|
+
// coordinate of a_char_code in u_bitmap_font
|
|
106
|
+
// (note: there are 4 pixels per glyph, in a column; hence row * 4)
|
|
107
|
+
f_glyph_coord = ivec2(glyph_col, glyph_row * 4);
|
|
108
|
+
|
|
109
|
+
// used when checking whether to use u_user_font or not
|
|
110
|
+
f_char_code = a_char_code;
|
|
111
|
+
|
|
112
|
+
/* A glyph is made up of two triangles, six vertices:
|
|
113
|
+
*
|
|
114
|
+
* 0--2 ,5 0--2,5
|
|
115
|
+
* | / + / | = | |
|
|
116
|
+
* 1' 3--4 1,3--4
|
|
117
|
+
*/
|
|
118
|
+
|
|
119
|
+
int row_inc = int(gl_VertexID == 1 || gl_VertexID == 3 ||
|
|
120
|
+
gl_VertexID == 4);
|
|
121
|
+
|
|
122
|
+
int col_inc = int(gl_VertexID == 2 || gl_VertexID == 4 ||
|
|
123
|
+
gl_VertexID == 5);
|
|
124
|
+
|
|
125
|
+
v_cell_coord = vec2(float(col_inc), float(row_inc));
|
|
126
|
+
|
|
127
|
+
row += row_inc;
|
|
128
|
+
col += col_inc;
|
|
129
|
+
|
|
130
|
+
v_uv_coord = vec2(float(glyph_col + col_inc) / 32.0,
|
|
131
|
+
float(glyph_row + row_inc) / 8.0);
|
|
132
|
+
|
|
133
|
+
// position needs to be in [-1.0, 1.0]
|
|
134
|
+
float ndc_x = 2.0 * float(col) / float(u_cols) - 1.0;
|
|
135
|
+
float ndc_y = -2.0 * float(row) / float(u_rows) + 1.0;
|
|
136
|
+
|
|
137
|
+
gl_Position = vec4(ndc_x, ndc_y, 0.0, 1.0);
|
|
138
|
+
}
|
|
139
|
+
`;
|
|
140
|
+
export const terminal_frag = `\
|
|
141
|
+
#version 300 es
|
|
142
|
+
|
|
143
|
+
precision mediump float;
|
|
144
|
+
|
|
145
|
+
${glyph_frag}
|
|
146
|
+
|
|
147
|
+
in vec2 v_cell_coord;
|
|
148
|
+
in vec2 v_uv_coord;
|
|
149
|
+
|
|
150
|
+
flat in vec3 f_fg_colour;
|
|
151
|
+
flat in vec3 f_bg_colour;
|
|
152
|
+
flat in ivec2 f_glyph_coord;
|
|
153
|
+
flat in uint f_char_code;
|
|
154
|
+
|
|
155
|
+
uniform mediump usampler2D u_bitmap_font;
|
|
156
|
+
uniform sampler2D u_user_font;
|
|
157
|
+
uniform bool u_use_user_font;
|
|
158
|
+
|
|
159
|
+
out vec4 frag_colour;
|
|
160
|
+
|
|
161
|
+
void main() {
|
|
162
|
+
// 176U and 191U are the first and last box-drawing characters
|
|
163
|
+
if (u_use_user_font && (f_char_code < 176U || f_char_code >= 224U)) {
|
|
164
|
+
vec4 samp = texture(u_user_font, v_uv_coord);
|
|
165
|
+
frag_colour = vec4(mix(f_bg_colour, f_fg_colour, samp.r), 1.0);
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
float value = bitmap_glyph_value(u_bitmap_font, f_glyph_coord,
|
|
170
|
+
v_cell_coord);
|
|
171
|
+
frag_colour = vec4(mix(f_bg_colour, f_fg_colour, value), 1.0);
|
|
172
|
+
}
|
|
173
|
+
`;
|
|
174
|
+
export const renderer_vert = `\
|
|
175
|
+
#version 300 es
|
|
176
|
+
|
|
177
|
+
#define SAMPLE_MODE 0u
|
|
178
|
+
#define GLYPHS_MODE 1u
|
|
179
|
+
#define BG_GLYPHS_MODE 2u
|
|
180
|
+
|
|
181
|
+
precision mediump float;
|
|
182
|
+
|
|
183
|
+
in uint a_row;
|
|
184
|
+
in uint a_col;
|
|
185
|
+
in uint a_mode;
|
|
186
|
+
|
|
187
|
+
uniform int u_row;
|
|
188
|
+
uniform int u_col;
|
|
189
|
+
uniform int u_rows;
|
|
190
|
+
uniform int u_cols;
|
|
191
|
+
uniform int u_canvas_rows;
|
|
192
|
+
uniform int u_canvas_cols;
|
|
193
|
+
uniform int u_mouse_row;
|
|
194
|
+
uniform int u_mouse_col;
|
|
195
|
+
uniform vec3 u_palette[16];
|
|
196
|
+
uniform sampler2D u_texture;
|
|
197
|
+
|
|
198
|
+
out vec2 v_cell_coord;
|
|
199
|
+
out vec2 v_tex_uv_coord; // sample mode
|
|
200
|
+
out vec2 v_font_uv_coord;
|
|
201
|
+
|
|
202
|
+
flat out uint f_mode;
|
|
203
|
+
flat out int f_is_cursor;
|
|
204
|
+
flat out vec3 f_fg_colour;
|
|
205
|
+
flat out vec3 f_bg_colour;
|
|
206
|
+
flat out ivec2 f_glyph_coord;
|
|
207
|
+
flat out int f_char_code;
|
|
208
|
+
|
|
209
|
+
const vec3 dimensions[8] = vec3[](
|
|
210
|
+
vec3(0.0, 0.0, 0.0), // black (unused)
|
|
211
|
+
vec3(1.0, 0.8, 0.8), // red
|
|
212
|
+
vec3(0.8, 1.0, 0.8), // green
|
|
213
|
+
vec3(1.0, 1.0, 0.8), // yellow
|
|
214
|
+
vec3(0.8, 0.8, 1.0), // blue
|
|
215
|
+
vec3(1.0, 0.8, 1.0), // magenta
|
|
216
|
+
vec3(0.8, 1.0, 1.0), // cyan
|
|
217
|
+
vec3(1.0, 1.0, 1.0) // white
|
|
218
|
+
);
|
|
219
|
+
|
|
220
|
+
struct Dims {
|
|
221
|
+
int dim1;
|
|
222
|
+
int dim2;
|
|
223
|
+
float ratio;
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
Dims getDims(vec3 colour) {
|
|
227
|
+
vec3 ncolour = normalize(colour);
|
|
228
|
+
float d1 = 1000.0;
|
|
229
|
+
float d2 = 1000.0;
|
|
230
|
+
int dim1 = 0;
|
|
231
|
+
int dim2 = 0;
|
|
232
|
+
|
|
233
|
+
for (int i = 1; i < 8; i++) {
|
|
234
|
+
float d = distance(ncolour, normalize(dimensions[i]));
|
|
235
|
+
|
|
236
|
+
if (d < d1) {
|
|
237
|
+
d2 = d1;
|
|
238
|
+
dim2 = dim1;
|
|
239
|
+
d1 = d;
|
|
240
|
+
dim1 = i;
|
|
241
|
+
} else if (d < d2) {
|
|
242
|
+
d2 = d;
|
|
243
|
+
dim2 = i;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
return Dims(dim1, dim2, d1 / (d1 + d2));
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
float getLum(vec3 colour, int dim) {
|
|
251
|
+
vec3 dcolour = dimensions[dim];
|
|
252
|
+
return dot(colour, dcolour) / dot(dcolour, dcolour);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
struct Glyph {
|
|
256
|
+
int char_code;
|
|
257
|
+
int fg_idx;
|
|
258
|
+
int bg_idx;
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
Glyph getGlyph(int dim, float lum) {
|
|
262
|
+
// . , - : ; = ! * # $ @
|
|
263
|
+
int glyphs[12] = int[](0, 46, 44, 45, 58, 59, 61, 33, 42, 35, 36, 64);
|
|
264
|
+
|
|
265
|
+
int fgs[2] = int[](dim, dim + 8);
|
|
266
|
+
|
|
267
|
+
if (dim == 7) {
|
|
268
|
+
fgs = int[](8, 15);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
int layer = clamp(int(lum * 2.0), 0, 2);
|
|
272
|
+
|
|
273
|
+
Glyph glyph;
|
|
274
|
+
|
|
275
|
+
int glyph_idx = clamp(int(12.0 * lum), 0, 11);
|
|
276
|
+
glyph.char_code = glyphs[glyph_idx];
|
|
277
|
+
glyph.fg_idx = fgs[layer];
|
|
278
|
+
glyph.bg_idx = 0;
|
|
279
|
+
|
|
280
|
+
return glyph;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
Glyph getBgGlyph(int dim, float lum) {
|
|
284
|
+
// . - , : ; = ! * # $ @
|
|
285
|
+
int glyphs[12] = int[](0, 46, 45, 44, 58, 59, 61, 33, 42, 35, 36, 64);
|
|
286
|
+
|
|
287
|
+
int fgs[3] = int[](dim, dim + 8, 15);
|
|
288
|
+
int bgs[3] = int[](0, dim, dim + 8);
|
|
289
|
+
|
|
290
|
+
int layer = clamp(int(lum * 3.0), 0, 2);
|
|
291
|
+
|
|
292
|
+
// check if dimension is white
|
|
293
|
+
if (dim == 7) {
|
|
294
|
+
if (layer > 0) {
|
|
295
|
+
// reverse the order of the glyphs
|
|
296
|
+
glyphs = int[](64, 36, 35, 42, 33, 61, 59, 58, 44, 45,
|
|
297
|
+
46, 0);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
fgs = int[](7, 0, 8);
|
|
301
|
+
bgs = int[](0, 7, 15);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
Glyph glyph;
|
|
305
|
+
|
|
306
|
+
int glyph_idx = clamp(int(12.0 * (3.0 * lum - float(layer))), 0, 11);
|
|
307
|
+
glyph.char_code = glyphs[glyph_idx];
|
|
308
|
+
glyph.fg_idx = fgs[layer];
|
|
309
|
+
glyph.bg_idx = bgs[layer];
|
|
310
|
+
|
|
311
|
+
return glyph;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
void main() {
|
|
315
|
+
int origin_row = int(a_row);
|
|
316
|
+
int origin_col = int(a_col);
|
|
317
|
+
|
|
318
|
+
f_is_cursor = int(origin_row + u_row == u_mouse_row &&
|
|
319
|
+
origin_col + u_col == u_mouse_col);
|
|
320
|
+
|
|
321
|
+
/* A glyph is made up of two triangles, six vertices:
|
|
322
|
+
*
|
|
323
|
+
* 0--2 ,5 0--2,5
|
|
324
|
+
* | / + / | = | |
|
|
325
|
+
* 1' 3--4 1,3--4
|
|
326
|
+
*/
|
|
327
|
+
|
|
328
|
+
int row_inc = int(gl_VertexID == 1 || gl_VertexID == 3 ||
|
|
329
|
+
gl_VertexID == 4);
|
|
330
|
+
|
|
331
|
+
int col_inc = int(gl_VertexID == 2 || gl_VertexID == 4 ||
|
|
332
|
+
gl_VertexID == 5);
|
|
333
|
+
|
|
334
|
+
v_cell_coord = vec2(float(col_inc), float(row_inc));
|
|
335
|
+
|
|
336
|
+
int row = origin_row + row_inc;
|
|
337
|
+
int col = origin_col + col_inc;
|
|
338
|
+
|
|
339
|
+
v_tex_uv_coord = vec2(float(col) / float(u_cols),
|
|
340
|
+
float(row) / float(u_rows));
|
|
341
|
+
|
|
342
|
+
// position needs to be in [-1.0, 1.0]
|
|
343
|
+
float ndc_x = 2.0 * float(u_col + col) / float(u_canvas_cols) - 1.0;
|
|
344
|
+
float ndc_y = -2.0 * float(u_row + row) / float(u_canvas_rows) + 1.0;
|
|
345
|
+
|
|
346
|
+
gl_Position = vec4(ndc_x, ndc_y, 0.0, 1.0);
|
|
347
|
+
|
|
348
|
+
f_mode = a_mode;
|
|
349
|
+
|
|
350
|
+
// sample mode
|
|
351
|
+
if (f_mode == SAMPLE_MODE) {
|
|
352
|
+
// no more work to do
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// dithered glyphs mode
|
|
357
|
+
|
|
358
|
+
vec2 origin_uv_coord = vec2(float(origin_col) / float(u_cols),
|
|
359
|
+
float(origin_row) / float(u_rows));
|
|
360
|
+
|
|
361
|
+
vec2 full_uv_cell = vec2(1.0 / float(u_cols), 1.0 / float(u_rows));
|
|
362
|
+
vec4 samp = texture(u_texture, origin_uv_coord + 0.5 * full_uv_cell);
|
|
363
|
+
vec3 colour = samp.rgb;
|
|
364
|
+
|
|
365
|
+
// dithering
|
|
366
|
+
float bayer[16] = float[](
|
|
367
|
+
0.0, 0.5, 0.125, 0.625,
|
|
368
|
+
0.75, 0.25, 0.875, 0.375,
|
|
369
|
+
0.1875, 0.6875, 0.0625, 0.5625,
|
|
370
|
+
0.9375, 0.4375, 0.8125, 0.3125
|
|
371
|
+
);
|
|
372
|
+
float bayer_val = bayer[int(origin_row) % 4 * 4 + int(origin_col) % 4];
|
|
373
|
+
|
|
374
|
+
Dims dims = getDims(colour);
|
|
375
|
+
int dim = (bayer_val < dims.ratio) ? dims.dim2 : dims.dim1;
|
|
376
|
+
float lum = getLum(colour, dim);
|
|
377
|
+
|
|
378
|
+
float dither = (bayer_val - 0.5) / 12.0;
|
|
379
|
+
lum = clamp(lum + dither, 0.0, 1.0);
|
|
380
|
+
|
|
381
|
+
Glyph glyph;
|
|
382
|
+
|
|
383
|
+
if (f_mode == BG_GLYPHS_MODE) {
|
|
384
|
+
glyph = getBgGlyph(dim, lum);
|
|
385
|
+
} else {
|
|
386
|
+
glyph = getGlyph(dim, lum);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
if (glyph.fg_idx == glyph.bg_idx && f_is_cursor == 1) {
|
|
390
|
+
glyph.fg_idx = 15;
|
|
391
|
+
glyph.bg_idx = 0;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
f_fg_colour = u_palette[glyph.fg_idx];
|
|
395
|
+
f_bg_colour = u_palette[glyph.bg_idx];
|
|
396
|
+
|
|
397
|
+
// 32 glyph columns, 8 glyph rows
|
|
398
|
+
int glyph_row = clamp(int(glyph.char_code / 32), 0, 7);
|
|
399
|
+
int glyph_col = clamp(int(glyph.char_code % 32), 0, 31);
|
|
400
|
+
|
|
401
|
+
// coordinate of char_code in u_bitmap_font
|
|
402
|
+
f_glyph_coord = ivec2(glyph_col, glyph_row << 2);
|
|
403
|
+
|
|
404
|
+
f_char_code = glyph.char_code;
|
|
405
|
+
|
|
406
|
+
v_font_uv_coord = vec2(float(glyph_col + col_inc) / 32.0,
|
|
407
|
+
float(glyph_row + row_inc) / 8.0);
|
|
408
|
+
}
|
|
409
|
+
`;
|
|
410
|
+
export const renderer_frag = `\
|
|
411
|
+
#version 300 es
|
|
412
|
+
|
|
413
|
+
precision mediump float;
|
|
414
|
+
|
|
415
|
+
${glyph_frag}
|
|
416
|
+
|
|
417
|
+
#define SAMPLE_MODE 0u
|
|
418
|
+
|
|
419
|
+
in vec2 v_cell_coord;
|
|
420
|
+
in vec2 v_tex_uv_coord; // f_mode == 0
|
|
421
|
+
in vec2 v_font_uv_coord;
|
|
422
|
+
|
|
423
|
+
flat in uint f_mode;
|
|
424
|
+
flat in int f_is_cursor;
|
|
425
|
+
flat in vec3 f_fg_colour;
|
|
426
|
+
flat in vec3 f_bg_colour;
|
|
427
|
+
flat in ivec2 f_glyph_coord;
|
|
428
|
+
flat in int f_char_code;
|
|
429
|
+
|
|
430
|
+
uniform mediump usampler2D u_bitmap_font;
|
|
431
|
+
uniform sampler2D u_texture; // f_mode == 0
|
|
432
|
+
uniform sampler2D u_user_font;
|
|
433
|
+
uniform bool u_use_user_font;
|
|
434
|
+
|
|
435
|
+
out vec4 frag_colour;
|
|
436
|
+
|
|
437
|
+
void main() {
|
|
438
|
+
if (f_mode == SAMPLE_MODE) {
|
|
439
|
+
frag_colour = texture(u_texture, v_tex_uv_coord);
|
|
440
|
+
|
|
441
|
+
// invert the sample
|
|
442
|
+
if (f_is_cursor == 1) {
|
|
443
|
+
frag_colour = vec4(vec3(1.0, 1.0, 1.0) -
|
|
444
|
+
frag_colour.rgb, 1.0);
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
return;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
vec3 fg = f_fg_colour;
|
|
451
|
+
vec3 bg = f_bg_colour;
|
|
452
|
+
|
|
453
|
+
if (f_is_cursor == 1) {
|
|
454
|
+
vec3 tmp = fg;
|
|
455
|
+
fg = bg;
|
|
456
|
+
bg = tmp;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
// 176 and 191 are the first and last box-drawing characters
|
|
460
|
+
if (u_use_user_font && (f_char_code < 176 || f_char_code >= 224)) {
|
|
461
|
+
vec4 samp = texture(u_user_font, v_font_uv_coord);
|
|
462
|
+
frag_colour = vec4(mix(bg, fg, samp.r), 1.0);
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
float value = bitmap_glyph_value(u_bitmap_font, f_glyph_coord,
|
|
467
|
+
v_cell_coord);
|
|
468
|
+
frag_colour = vec4(mix(bg, fg, value), 1.0);
|
|
469
|
+
}
|
|
470
|
+
`;
|