simplify color events
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
/**
|
||||
* Copyright (c) 2021 The xterm.js authors. All rights reserved.
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { assert } from 'chai';
|
||||
import { parseColor, toRgbString } from 'common/input/XParseColor';
|
||||
|
||||
describe('XParseColor', () => {
|
||||
describe('parseColor', () => {
|
||||
it('rgb:<r>/<g>/<b> scheme in 4/8/12/16 bit', () => {
|
||||
// 4 bit
|
||||
assert.deepEqual(parseColor('rgb:0/0/0'), [0, 0, 0]);
|
||||
assert.deepEqual(parseColor('rgb:f/f/f'), [255, 255, 255]);
|
||||
assert.deepEqual(parseColor('rgb:1/2/3'), [17, 34, 51]);
|
||||
// 8 bit
|
||||
assert.deepEqual(parseColor('rgb:00/00/00'), [0, 0, 0]);
|
||||
assert.deepEqual(parseColor('rgb:ff/ff/ff'), [255, 255, 255]);
|
||||
assert.deepEqual(parseColor('rgb:11/22/33'), [17, 34, 51]);
|
||||
// 12 bit
|
||||
assert.deepEqual(parseColor('rgb:000/000/000'), [0, 0, 0]);
|
||||
assert.deepEqual(parseColor('rgb:fff/fff/fff'), [255, 255, 255]);
|
||||
assert.deepEqual(parseColor('rgb:111/222/333'), [17, 34, 51]);
|
||||
// 16 bit
|
||||
assert.deepEqual(parseColor('rgb:0000/0000/0000'), [0, 0, 0]);
|
||||
assert.deepEqual(parseColor('rgb:ffff/ffff/ffff'), [255, 255, 255]);
|
||||
assert.deepEqual(parseColor('rgb:1111/2222/3333'), [17, 34, 51]);
|
||||
});
|
||||
it('#RGB scheme in 4/8/12/16 bit', () => {
|
||||
// 4 bit
|
||||
assert.deepEqual(parseColor('#000'), [0, 0, 0]);
|
||||
assert.deepEqual(parseColor('#fff'), [240, 240, 240]);
|
||||
assert.deepEqual(parseColor('#123'), [16, 32, 48]);
|
||||
// 8 bit
|
||||
assert.deepEqual(parseColor('#000000'), [0, 0, 0]);
|
||||
assert.deepEqual(parseColor('#ffffff'), [255, 255, 255]);
|
||||
assert.deepEqual(parseColor('#112233'), [17, 34, 51]);
|
||||
// 12 bit
|
||||
assert.deepEqual(parseColor('#000000000'), [0, 0, 0]);
|
||||
assert.deepEqual(parseColor('#fffffffff'), [255, 255, 255]);
|
||||
assert.deepEqual(parseColor('#111222333'), [17, 34, 51]);
|
||||
// 16 bit
|
||||
assert.deepEqual(parseColor('#000000000000'), [0, 0, 0]);
|
||||
assert.deepEqual(parseColor('#ffffffffffff'), [255, 255, 255]);
|
||||
assert.deepEqual(parseColor('#111122223333'), [17, 34, 51]);
|
||||
});
|
||||
it('supports upper case', () => {
|
||||
assert.deepEqual(parseColor('RGB:0/A/F'), [0, 170, 255]);
|
||||
assert.deepEqual(parseColor('#FFF'), [240, 240, 240]);
|
||||
});
|
||||
it('does not parse illegal combinations', () => {
|
||||
// shifting bit width
|
||||
assert.equal(parseColor('rgb:0/11/222'), undefined);
|
||||
// unsupported scheme
|
||||
assert.equal(parseColor('rgbi:00/11/22'), undefined);
|
||||
// broken # specifier
|
||||
assert.equal(parseColor('#aabbbcc'), undefined);
|
||||
// out of range
|
||||
assert.equal(parseColor('#aabbgg'), undefined);
|
||||
assert.equal(parseColor('rgb:aa/bb/gg'), undefined);
|
||||
});
|
||||
});
|
||||
describe('toXColorRgb', () => {
|
||||
it('rgb:<r>/<g>/<b> scheme in 4/8/12/16 bit', () => {
|
||||
// 4 bit
|
||||
assert.equal(toRgbString(parseColor('rgb:0/0/0')!, 4), 'rgb:0/0/0');
|
||||
assert.equal(toRgbString(parseColor('rgb:f/f/f')!, 4), 'rgb:f/f/f');
|
||||
assert.equal(toRgbString(parseColor('rgb:1/2/3')!, 4), 'rgb:1/2/3');
|
||||
// 8 bit
|
||||
assert.equal(toRgbString(parseColor('rgb:00/00/00')!, 8), 'rgb:00/00/00');
|
||||
assert.equal(toRgbString(parseColor('rgb:ff/ff/ff')!, 8), 'rgb:ff/ff/ff');
|
||||
assert.equal(toRgbString(parseColor('rgb:11/22/33')!, 8), 'rgb:11/22/33');
|
||||
// 12 bit
|
||||
assert.equal(toRgbString(parseColor('rgb:000/000/000')!, 12), 'rgb:000/000/000');
|
||||
assert.equal(toRgbString(parseColor('rgb:fff/fff/fff')!, 12), 'rgb:fff/fff/fff');
|
||||
assert.equal(toRgbString(parseColor('rgb:111/222/333')!, 12), 'rgb:111/222/333');
|
||||
// 16 bit
|
||||
assert.equal(toRgbString(parseColor('rgb:0000/0000/0000')!, 16), 'rgb:0000/0000/0000');
|
||||
assert.equal(toRgbString(parseColor('rgb:ffff/ffff/ffff')!, 16), 'rgb:ffff/ffff/ffff');
|
||||
assert.equal(toRgbString(parseColor('rgb:1111/2222/3333')!, 16), 'rgb:1111/2222/3333');
|
||||
});
|
||||
it('defaults to 16 bit output', () => {
|
||||
assert.equal(toRgbString(parseColor('rgb:1/2/3')!), 'rgb:1111/2222/3333');
|
||||
assert.equal(toRgbString(parseColor('rgb:11/22/33')!), 'rgb:1111/2222/3333');
|
||||
assert.equal(toRgbString(parseColor('rgb:111/222/333')!), 'rgb:1111/2222/3333');
|
||||
assert.equal(toRgbString(parseColor('rgb:123/123/123')!), 'rgb:1212/1212/1212');
|
||||
});
|
||||
it('reduces colors to 8 bit resolution', () => {
|
||||
assert.equal(toRgbString(parseColor('rgb:123/123/123')!, 12), 'rgb:121/121/121');
|
||||
assert.equal(toRgbString(parseColor('rgb:1234/1234/1234')!, 16), 'rgb:1212/1212/1212');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,77 @@
|
||||
/**
|
||||
* Copyright (c) 2021 The xterm.js authors. All rights reserved.
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
|
||||
// 'rgb:' rule - matching: r/g/b | rr/gg/bb | rrr/ggg/bbb | rrrr/gggg/bbbb (hex digits)
|
||||
const RGB_REX = /^([\da-f]{1})\/([\da-f]{1})\/([\da-f]{1})$|^([\da-f]{2})\/([\da-f]{2})\/([\da-f]{2})$|^([\da-f]{3})\/([\da-f]{3})\/([\da-f]{3})$|^([\da-f]{4})\/([\da-f]{4})\/([\da-f]{4})$/;
|
||||
// '#...' rule - matching any hex digits
|
||||
const HASH_REX = /^[\da-f]+$/;
|
||||
|
||||
/**
|
||||
* Parse color spec to RGB values (8 bit per channel).
|
||||
* See `man xparsecolor` for details about certain format specifications.
|
||||
*
|
||||
* Supported formats:
|
||||
* - rgb:<red>/<green>/<blue> with <red>, <green>, <blue> in h | hh | hhh | hhhh
|
||||
* - #RGB, #RRGGBB, #RRRGGGBBB, #RRRRGGGGBBBB
|
||||
*
|
||||
* All other formats like rgbi: or device-independent string specifications
|
||||
* with float numbering are not supported.
|
||||
*/
|
||||
export function parseColor(data: string): [number, number, number] | undefined {
|
||||
if (!data) return;
|
||||
// also handle uppercases
|
||||
let low = data.toLowerCase();
|
||||
if (low.indexOf('rgb:') === 0) {
|
||||
// 'rgb:' specifier
|
||||
low = low.slice(4);
|
||||
const m = RGB_REX.exec(low);
|
||||
if (m) {
|
||||
const base = m[1] ? 15 : m[4] ? 255 : m[7] ? 4095 : 65535;
|
||||
return [
|
||||
Math.round(parseInt(m[1] || m[4] || m[7] || m[10], 16) / base * 255),
|
||||
Math.round(parseInt(m[2] || m[5] || m[8] || m[11], 16) / base * 255),
|
||||
Math.round(parseInt(m[3] || m[6] || m[9] || m[12], 16) / base * 255)
|
||||
];
|
||||
}
|
||||
} else if (low.indexOf('#') === 0) {
|
||||
// '#' specifier
|
||||
low = low.slice(1);
|
||||
if (HASH_REX.exec(low) && [3, 6, 9, 12].includes(low.length)) {
|
||||
const adv = low.length / 3;
|
||||
const result: [number, number, number] = [0, 0, 0];
|
||||
for (let i = 0; i < 3; ++i) {
|
||||
const c = parseInt(low.slice(adv * i, adv * i + adv), 16);
|
||||
result[i] = adv === 1 ? c << 4 : adv === 2 ? c : adv === 3 ? c >> 4 : c >> 8;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
// FIXME: Once #3530 is resolved, implement named colors.
|
||||
}
|
||||
|
||||
// pad hex output to requested bit width
|
||||
function pad(n: number, bits: number): string {
|
||||
const s = n.toString(16);
|
||||
const s2 = s.length < 2 ? '0' + s : s;
|
||||
switch (bits) {
|
||||
case 4:
|
||||
return s[0];
|
||||
case 8:
|
||||
return s2;
|
||||
case 12:
|
||||
return (s2 + s2).slice(0, 3);
|
||||
default:
|
||||
return s2 + s2;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a given color to rgb:../../.. string of `bits` depth.
|
||||
*/
|
||||
export function toRgbString(color: [number, number, number], bits: number = 16): string {
|
||||
const [r, g, b] = color;
|
||||
return `rgb:${pad(r, bits)}/${pad(g, bits)}/${pad(b, bits)}`;
|
||||
}
|
||||
Reference in New Issue
Block a user