sixel
Version:
Sixel image format for node and browser.
251 lines • 7.64 kB
JavaScript
;
/**
* Copyright (c) 2019 Joerg Breitbart.
* @license MIT
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.DEFAULT_FOREGROUND = exports.DEFAULT_BACKGROUND = exports.PALETTE_ANSI_256 = exports.PALETTE_VT340_GREY = exports.PALETTE_VT340_COLOR = exports.normalizeHLS = exports.normalizeRGB = exports.nearestColorIndex = exports.fromRGBA8888 = exports.toRGBA8888 = exports.alpha = exports.blue = exports.green = exports.red = exports.BIG_ENDIAN = void 0;
// FIXME: cleanup this mess, move things either to decoder/encoder, keep only shared things
// system endianess
exports.BIG_ENDIAN = new Uint8Array(new Uint32Array([0xFF000000]).buffer)[0] === 0xFF;
if (exports.BIG_ENDIAN) {
console.warn('BE platform detected. This version of node-sixel works only on LE properly.');
}
// channel values
function red(n) {
return n & 0xFF;
}
exports.red = red;
function green(n) {
return (n >>> 8) & 0xFF;
}
exports.green = green;
function blue(n) {
return (n >>> 16) & 0xFF;
}
exports.blue = blue;
function alpha(n) {
return (n >>> 24) & 0xFF;
}
exports.alpha = alpha;
/**
* Convert RGB channels to native color RGBA8888.
*/
function toRGBA8888(r, g, b, a = 255) {
return ((a & 0xFF) << 24 | (b & 0xFF) << 16 | (g & 0xFF) << 8 | (r & 0xFF)) >>> 0; // ABGR32
}
exports.toRGBA8888 = toRGBA8888;
/**
* Convert native color to [r, g, b, a].
*/
function fromRGBA8888(color) {
return [color & 0xFF, (color >> 8) & 0xFF, (color >> 16) & 0xFF, color >>> 24];
}
exports.fromRGBA8888 = fromRGBA8888;
/**
* Get index of nearest color in `palette` for `color`.
* Uses euclidean distance without any luminescence correction.
*/
function nearestColorIndex(color, palette) {
const r = red(color);
const g = green(color);
const b = blue(color);
let min = Number.MAX_SAFE_INTEGER;
let idx = -1;
// use euclidean distance (manhattan gives very poor results)
for (let i = 0; i < palette.length; ++i) {
const dr = r - palette[i][0];
const dg = g - palette[i][1];
const db = b - palette[i][2];
const d = dr * dr + dg * dg + db * db;
if (!d)
return i;
if (d < min) {
min = d;
idx = i;
}
}
return idx;
}
exports.nearestColorIndex = nearestColorIndex;
// color conversions
// HLS taken from: http://www.niwa.nu/2013/05/math-behind-colorspace-conversions-rgb-hsl
function clamp(low, high, value) {
return Math.max(low, Math.min(value, high));
}
function h2c(t1, t2, c) {
if (c < 0)
c += 1;
if (c > 1)
c -= 1;
return c * 6 < 1
? t2 + (t1 - t2) * 6 * c
: c * 2 < 1
? t1
: c * 3 < 2
? t2 + (t1 - t2) * (4 - c * 6)
: t2;
}
function HLStoRGB(h, l, s) {
if (!s) {
const v = Math.round(l * 255);
return toRGBA8888(v, v, v);
}
const t1 = l < 0.5 ? l * (1 + s) : l + s - l * s;
const t2 = 2 * l - t1;
return toRGBA8888(clamp(0, 255, Math.round(h2c(t1, t2, h + 1 / 3) * 255)), clamp(0, 255, Math.round(h2c(t1, t2, h) * 255)), clamp(0, 255, Math.round(h2c(t1, t2, h - 1 / 3) * 255)));
}
/**
* Normalize SIXEL RGB values (percent based, 0-100) to RGBA8888.
*/
function normalizeRGB(r, g, b) {
return (0xFF000000 | Math.round(b / 100 * 255) << 16 | Math.round(g / 100 * 255) << 8 | Math.round(r / 100 * 255)) >>> 0; // ABGR32
}
exports.normalizeRGB = normalizeRGB;
/**
* Normalize SIXEL HLS values to RGBA8888. Applies hue correction of +240°.
*/
function normalizeHLS(h, l, s) {
// Note: hue value is turned by 240° in VT340, all values given as fractions
return HLStoRGB((h + 240 % 360) / 360, l / 100, s / 100);
}
exports.normalizeHLS = normalizeHLS;
/**
* default palettes
*/
// FIXME: move palettes to Decoder.ts
/**
* 16 predefined color registers of VT340 (values in %):
* ```
* R G B
* 0 Black 0 0 0
* 1 Blue 20 20 80
* 2 Red 80 13 13
* 3 Green 20 80 20
* 4 Magenta 80 20 80
* 5 Cyan 20 80 80
* 6 Yellow 80 80 20
* 7 Gray 50% 53 53 53
* 8 Gray 25% 26 26 26
* 9 Blue* 33 33 60
* 10 Red* 60 26 26
* 11 Green* 33 60 33
* 12 Magenta* 60 33 60
* 13 Cyan* 33 60 60
* 14 Yellow* 60 60 33
* 15 Gray 75% 80 80 80
* ```
* (*) less saturated
*
* @see https://vt100.net/docs/vt3xx-gp/chapter2.html#S2.4
*/
exports.PALETTE_VT340_COLOR = new Uint32Array([
normalizeRGB(0, 0, 0),
normalizeRGB(20, 20, 80),
normalizeRGB(80, 13, 13),
normalizeRGB(20, 80, 20),
normalizeRGB(80, 20, 80),
normalizeRGB(20, 80, 80),
normalizeRGB(80, 80, 20),
normalizeRGB(53, 53, 53),
normalizeRGB(26, 26, 26),
normalizeRGB(33, 33, 60),
normalizeRGB(60, 26, 26),
normalizeRGB(33, 60, 33),
normalizeRGB(60, 33, 60),
normalizeRGB(33, 60, 60),
normalizeRGB(60, 60, 33),
normalizeRGB(80, 80, 80)
]);
/**
* 16 predefined monochrome registers of VT340 (values in %):
* ```
* R G B
* 0 Black 0 0 0
* 1 Gray-2 13 13 13
* 2 Gray-4 26 26 26
* 3 Gray-6 40 40 40
* 4 Gray-1 6 6 6
* 5 Gray-3 20 20 20
* 6 Gray-5 33 33 33
* 7 White 7 46 46 46
* 8 Black 0 0 0 0
* 9 Gray-2 13 13 13
* 10 Gray-4 26 26 26
* 11 Gray-6 40 40 40
* 12 Gray-1 6 6 6
* 13 Gray-3 20 20 20
* 14 Gray-5 33 33 33
* 15 White 7 46 46 46
* ```
*
* @see https://vt100.net/docs/vt3xx-gp/chapter2.html#S2.4
*/
exports.PALETTE_VT340_GREY = new Uint32Array([
normalizeRGB(0, 0, 0),
normalizeRGB(13, 13, 13),
normalizeRGB(26, 26, 26),
normalizeRGB(40, 40, 40),
normalizeRGB(6, 6, 6),
normalizeRGB(20, 20, 20),
normalizeRGB(33, 33, 33),
normalizeRGB(46, 46, 46),
normalizeRGB(0, 0, 0),
normalizeRGB(13, 13, 13),
normalizeRGB(26, 26, 26),
normalizeRGB(40, 40, 40),
normalizeRGB(6, 6, 6),
normalizeRGB(20, 20, 20),
normalizeRGB(33, 33, 33),
normalizeRGB(46, 46, 46)
]);
/**
* 256 predefined ANSI colors.
*
* @see https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit
*/
exports.PALETTE_ANSI_256 = (() => {
// 16 lower colors (taken from xterm)
const p = [
toRGBA8888(0, 0, 0),
toRGBA8888(205, 0, 0),
toRGBA8888(0, 205, 0),
toRGBA8888(205, 205, 0),
toRGBA8888(0, 0, 238),
toRGBA8888(205, 0, 205),
toRGBA8888(0, 250, 205),
toRGBA8888(229, 229, 229),
toRGBA8888(127, 127, 127),
toRGBA8888(255, 0, 0),
toRGBA8888(0, 255, 0),
toRGBA8888(255, 255, 0),
toRGBA8888(92, 92, 255),
toRGBA8888(255, 0, 255),
toRGBA8888(0, 255, 255),
toRGBA8888(255, 255, 255),
];
// colors up to 232
const d = [0, 95, 135, 175, 215, 255];
for (let r = 0; r < 6; ++r) {
for (let g = 0; g < 6; ++g) {
for (let b = 0; b < 6; ++b) {
p.push(toRGBA8888(d[r], d[g], d[b]));
}
}
}
// grey scale to up 255
for (let v = 8; v <= 238; v += 10) {
p.push(toRGBA8888(v, v, v));
}
return new Uint32Array(p);
})();
/**
* Background: Black by default.
* Foreground: White by default.
*
* Background color is used whenever a fill color is needed and not explicitly set.
* Foreground color is used as default initial sixel color.
*/
exports.DEFAULT_BACKGROUND = toRGBA8888(0, 0, 0, 255);
exports.DEFAULT_FOREGROUND = toRGBA8888(255, 255, 255, 255);
//# sourceMappingURL=Colors.js.map