File size: 2,190 Bytes
2a0e91b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
74
75
76
77
78
// 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

/** One sprite row: a string of single-char palette keys, or an array of cell values

 *  (full hex colors / multi-char keys). Indexing works the same on both. */
export type PixelRow = string | string[]
export type PixelMap = PixelRow[]

/** Draw a pixel map: rows of cells, each cell a key in `pal` or a literal color. */
export function drawMap(

  ctx: CanvasRenderingContext2D,

  map: PixelMap,

  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)
    }
  }
}