* Default is a 100x100 'css' canvas-equivalent div, filled with 50% srgb red.
* We default to showing the settings pane when loaded without a query string. This way, someone naively opens this in a browser, they can immediately see all available options.
* The 'Publish' button updates the url, and so causes the settings pane to hide.
function walk_nodes_depth_first(e, fn) {
if (fn(e) === false) return; // Don't stop on `true`, or `undefined`!
for (const c of e.childNodes) {
walk_nodes_depth_first(c, fn);
}
}
// -
// Click the canvas to toggle the settings pane.
e_canvas_list.addEventListener('click', () => {
// Toggle display:none to hide/unhide.
e_settings.hidden = !e_settings.hidden;
});
// Hide settings initially if there's a query string in the url.
if (window.location.search.startsWith('?')) {
e_settings.hidden = true;
}
// -
// Imply .name from .id, because `new FormData` collects based on names.
walk_nodes_depth_first(e_settings, e => {
if (e.id) {
e.name = e.id;
e._default_value = e.value;
}
});
// -
const URL_PARAMS = new URLSearchParams(window.location.search);
URL_PARAMS.forEach((v,k) => {
const e = window[k];
if (!e) {
if (k) {
console.warn(`Unrecognized setting: ${k} = ${v}`);
}
return;
}
v = decode_url_v(k,v);
e.value = v;
});
// -
globalThis.ASSERT = (() => {
function toPrettyString(arg) {
if (!arg) return ''+arg;
if (arg.call) {
let s = arg.toString();
const RE_TRIVIAL_LAMBDA = /\( *\) *=> *(.*)/;
const m = RE_TRIVIAL_LAMBDA.exec(s);
if (m) {
s = '`' + m[1] + '`';
}
return s;
}
if (arg.constructor == Array) {
return `[${[].join.call(arg, ', ')}]`;
}
return JSON.stringify(arg);
}
/// new AssertArg(): Construct a wrapper for args to assert functions.
function AssertArg(dict) {
this.label = Object.keys(dict)[0];
const ret = {
verbose: false,
IS,
};
ret.EQ = (was,expected) => ret.IS('==', was, expected);
ret.NEQ = (was,expected) => ret.IS('!=', was, expected);
ret.EEQ = (was,expected) => ret.IS('===', was, expected);
ret.NEEQ = (was,expected) => ret.IS('!==', was, expected);
ret.TRUE = was => ret.EQ(was, true);
ret.FALSE = was => ret.EQ(was, false);
ret.NULL = was => ret.EEQ(was, null);
return ret;
})();
// -
function parse_css_rgb(str) {
// rgb (R ,G ,B /A )
const m = /rgba?\(([^,]+),([^,]+),([^/)]+)(?: *\/([^)]+))?\)/.exec(str);
if (!m) throw str;
const rgba = m.slice(1,1+4).map((s,i) => {
if (s === undefined && i == 3) {
s = '1'; // Alpha defaults to 1.
}
s = s.trim();
let v = parseFloat(s);
if (s.endsWith('%')) {
v /= 100;
} else {
if (i < 3) { // r,g,b but not a!
v /= 255;
}
}
return v;
});
return rgba;
}
ASSERT.EQ(() => parse_css_rgb('rgb(255,255,255)'), [1,1,1,1]);
ASSERT.EQ(() => parse_css_rgb('rgba(255,255,255)'), [1,1,1,1]);
ASSERT.EQ(() => parse_css_rgb('rgb(255,255,255)'), [1,1,1,1]);
ASSERT.EQ(() => parse_css_rgb('rgba(255,255,255)'), [1,1,1,1]);
ASSERT.EQ(() => parse_css_rgb('rgb(20,40,60)'), () => [20/255, 40/255, 60/255, 1]);
ASSERT.EQ(() => parse_css_rgb('rgb(20,40,60 / 0.5)'), () => [20/255, 40/255, 60/255, 0.5]);
ASSERT.EQ(() => parse_css_rgb('rgb(20,40,60 / 0)'), () => [20/255, 40/255, 60/255, 0]);
// -
function parse_css_color(str) {
// color ( srgb R G B /A )
const m = /color\( *([^ ]+) +([^ ]+) +([^ ]+) +([^/)]+)(?:\/([^)]+))?\)/.exec(str);
if (!m) {
return ['srgb', ...parse_css_rgb(str)];
}
const cspace = m[1].trim();
let has_extreme_colors = false;
const rgba = m.slice(2, 2+4).map((s,i) => {
if (s === undefined && i == 3) {
s = '1'; // Alpha defaults to 1.
}
s = s.trim();
let v = parseFloat(s);
if (s.endsWith('%')) {
v /= 100;
}
if (v < 0 || v > 1) {
has_extreme_colors = true;
}
return v;
});
if (has_extreme_colors) {
console.warn(`parse_css_color('${str}') has colors outside [0.0,1.0]: ${JSON.stringify(rgba)}`);
}
return [cspace, ...rgba];
}
ASSERT.EQ(() => parse_css_color('rgb(255,255,255)'), ['srgb',1,1,1,1]);
ASSERT.EQ(() => parse_css_color('rgb(20,40,60 / 0.5)'), () => ['srgb', 20/255, 40/255, 60/255, 0.5]);
ASSERT.EQ(() => parse_css_color('color(srgb 1 0 1 /0.3)'), ['srgb',1,0,1,0.3]);
ASSERT.EQ(() => parse_css_color('color(display-p3 1 0% 100%/ 30%)'), ['display-p3',1,0,1,0.3]);
function make_canvas(css_color) {
css_color = CssColor.parse(css_color);
// `e_width` and e_friends are elements (by id) that we added to the raw HTML above.
// `e_width` is an old shorthand for `window.e_width || document.getElementById('e_width')`.
const W = parseInt(e_width.value);
const H = parseInt(e_height.value);
if (e_context.value == 'css') {
e_canvas = document.createElement('div');
e_canvas.style.width = `${W}px`;
e_canvas.style.height = `${H}px`;
e_canvas.style.backgroundColor = css_color;
return e_canvas;
}
e_canvas = document.createElement('canvas');
e_canvas.width = W;
e_canvas.height = H;
let requested_options = JSON.parse(e_options.value);
requested_options.colorSpace = e_cspace.value || undefined;
const context = e_canvas.getContext(e_context.value, requested_options);
if (requested_options.colorSpace) {
if (!context.drawingBufferColorSpace) {
console.warn(`${context.constructor.name}.drawingBufferColorSpace not supported by browser.`);
} else {
context.drawingBufferColorSpace = requested_options.colorSpace;
}
}
if (e_webgl_format.value) {
if (!context.drawingBufferStorage) {
console.warn(`${context.constructor.name}.drawingBufferStorage not supported by browser.`);
} else {
context.drawingBufferStorage(W, H, context[e_webgl_format.value]);
}
}
let actual_options;
if (!context.getContextAttributes) {
console.warn(`${canvas.constructor.name}.getContextAttributes not supported by browser.`);
actual_options = requested_options;
} else {
actual_options = context.getContextAttributes();
}
function encode_url_v(k,v) {
if (k == 'e_color') {
v = v.replaceAll(' ', ',');
}
console.assert(!v.includes(' '), v);
return v
}
function decode_url_v(k,v) {
console.assert(!v.includes(' '), v);
if (k == 'e_color') {
v = v.replaceAll(',', ' ');
}
return v
}
ASSERT.EQ(() => decode_url_v('e_color', encode_url_v('e_color', 'color(srgb 1 0 0)')), 'color(srgb 1 0 0)')
e_publish.addEventListener('click', () => {
const fd = new FormData(e_settings);
let settings = [];
for (let [k,v] of fd) {
const e = window[k];
if (e_publish_omit_defaults.checked && v == e._default_value) continue;
v = encode_url_v(k,v);
settings.push(`${k}=${v}`);
}
settings = settings.join('&');
if (!settings) {
settings = '='; // Empty key-value pair is 'publish with default settings'
}
window.location.search = '?' + settings;
});
</script>
</body>
</html>
Messung V0.5
¤ Dauer der Verarbeitung: 0.1 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.