initial commit
This commit is contained in:
94
scripts/gen-tray-icon.mjs
Normal file
94
scripts/gen-tray-icon.mjs
Normal file
@@ -0,0 +1,94 @@
|
||||
import { deflateSync } from 'zlib'
|
||||
import { writeFileSync, mkdirSync } from 'fs'
|
||||
import { dirname, resolve } from 'path'
|
||||
import { fileURLToPath } from 'url'
|
||||
|
||||
/** 生成一个简洁的托盘图标(粉色唱片盘),输出到 resources/tray.png */
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url))
|
||||
|
||||
const SIZE = 32
|
||||
const cx = SIZE / 2 - 0.5
|
||||
const cy = SIZE / 2 - 0.5
|
||||
const rOuter = 15
|
||||
const rHole = 4
|
||||
|
||||
function px(x, y) {
|
||||
const dx = x - cx
|
||||
const dy = y - cy
|
||||
const d = Math.sqrt(dx * dx + dy * dy)
|
||||
// 抗锯齿边缘
|
||||
const edge = (r) => Math.max(0, Math.min(1, r - d + 0.5))
|
||||
const inDisc = edge(rOuter)
|
||||
const inHole = edge(rHole)
|
||||
if (inDisc <= 0) return [0, 0, 0, 0]
|
||||
// 唱片主体粉色,中心孔洞白色
|
||||
const pink = [255, 55, 95]
|
||||
const white = [255, 255, 255]
|
||||
const a = Math.round(255 * inDisc)
|
||||
if (inHole > 0) {
|
||||
const t = inHole
|
||||
return [
|
||||
Math.round(pink[0] * (1 - t) + white[0] * t),
|
||||
Math.round(pink[1] * (1 - t) + white[1] * t),
|
||||
Math.round(pink[2] * (1 - t) + white[2] * t),
|
||||
a
|
||||
]
|
||||
}
|
||||
return [pink[0], pink[1], pink[2], a]
|
||||
}
|
||||
|
||||
// 构造原始 RGBA 扫描行(每行前置 filter 0)
|
||||
const raw = Buffer.alloc((SIZE * 4 + 1) * SIZE)
|
||||
let p = 0
|
||||
for (let y = 0; y < SIZE; y++) {
|
||||
raw[p++] = 0
|
||||
for (let x = 0; x < SIZE; x++) {
|
||||
const [r, g, b, a] = px(x, y)
|
||||
raw[p++] = r
|
||||
raw[p++] = g
|
||||
raw[p++] = b
|
||||
raw[p++] = a
|
||||
}
|
||||
}
|
||||
|
||||
const crcTable = (() => {
|
||||
const t = []
|
||||
for (let n = 0; n < 256; n++) {
|
||||
let c = n
|
||||
for (let k = 0; k < 8; k++) c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1
|
||||
t[n] = c >>> 0
|
||||
}
|
||||
return t
|
||||
})()
|
||||
function crc32(buf) {
|
||||
let c = 0xffffffff
|
||||
for (let i = 0; i < buf.length; i++) c = crcTable[(c ^ buf[i]) & 0xff] ^ (c >>> 8)
|
||||
return (c ^ 0xffffffff) >>> 0
|
||||
}
|
||||
function chunk(type, data) {
|
||||
const len = Buffer.alloc(4)
|
||||
len.writeUInt32BE(data.length, 0)
|
||||
const typeBuf = Buffer.from(type, 'ascii')
|
||||
const body = Buffer.concat([typeBuf, data])
|
||||
const crc = Buffer.alloc(4)
|
||||
crc.writeUInt32BE(crc32(body), 0)
|
||||
return Buffer.concat([len, body, crc])
|
||||
}
|
||||
|
||||
const sig = Buffer.from([137, 80, 78, 71, 13, 10, 26, 10])
|
||||
const ihdr = Buffer.alloc(13)
|
||||
ihdr.writeUInt32BE(SIZE, 0)
|
||||
ihdr.writeUInt32BE(SIZE, 4)
|
||||
ihdr[8] = 8 // bit depth
|
||||
ihdr[9] = 6 // color type RGBA
|
||||
const png = Buffer.concat([
|
||||
sig,
|
||||
chunk('IHDR', ihdr),
|
||||
chunk('IDAT', deflateSync(raw)),
|
||||
chunk('IEND', Buffer.alloc(0))
|
||||
])
|
||||
|
||||
const out = resolve(__dirname, '../resources/tray.png')
|
||||
mkdirSync(dirname(out), { recursive: true })
|
||||
writeFileSync(out, png)
|
||||
console.log('wrote', out, png.length, 'bytes')
|
||||
Reference in New Issue
Block a user