File size: 1,862 Bytes
414dc55
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
// Low-level pixel drawing helpers, shared by the canvas components and the procedural
// art. Ported verbatim from the prototype's pixel.jsx so rendering stays pixel-accurate.

export type Pal = Record<string, string> | null | undefined

/** Draw a string-grid map: rows of chars, each char a key in `pal`. */
export function drawMap(
  ctx: CanvasRenderingContext2D,
  map: string[],
  pal: Pal,
  px: number,
): void {
  for (let y = 0; y < map.length; y++) {
    const row = map[y]
    for (let x = 0; x < row.length; x++) {
      const c = row[x]
      if (c === '.' || c === ' ' || c == null) continue
      const col = pal ? pal[c] || c : c
      if (!col || col === '.') continue
      ctx.fillStyle = col
      ctx.fillRect(x * px, y * px, px, px)
    }
  }
}

/** Ordered 4x4 Bayer matrix for dithering. */
export const BAYER4 = [
  [0, 8, 2, 10],
  [12, 4, 14, 6],
  [3, 11, 1, 9],
  [15, 7, 13, 5],
]

/** Dither between two colors over a region; `t` in 0..1 is the `near` coverage. */
export function dither(
  ctx: CanvasRenderingContext2D,
  x0: number,
  y0: number,
  w: number,
  h: number,
  near: string,
  far: string,
  t: number,
): void {
  for (let y = 0; y < h; y++) {
    for (let x = 0; x < w; x++) {
      const thr = (BAYER4[y & 3][x & 3] + 0.5) / 16
      ctx.fillStyle = thr < t ? near : far
      ctx.fillRect(x0 + x, y0 + y, 1, 1)
    }
  }
}

/** Vertical dither gradient from colTop to colBottom across height h. */
export function ditherGrad(
  ctx: CanvasRenderingContext2D,
  x0: number,
  y0: number,
  w: number,
  h: number,
  colTop: string,
  colBottom: string,
): void {
  for (let y = 0; y < h; y++) {
    const t = 1 - y / h
    for (let x = 0; x < w; x++) {
      const thr = (BAYER4[y & 3][x & 3] + 0.5) / 16
      ctx.fillStyle = thr < t ? colTop : colBottom
      ctx.fillRect(x0 + x, y0 + y, 1, 1)
    }
  }
}