Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/dom/canvas/test/reftest/colors/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 12 kB image not shown  

Quelle  color_canvas.html   Sprache: HTML

 
 products/Sources/formale Sprachen/C/Firefox/dom/canvas/test/reftest/colors/color_canvas.html


<!DOCTYPE html>
<html>
  <head>
    <meta charset=utf-8>
    <title>color_canvas.html</title>
  </head>
  <!--
# color_canvas.html

* 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.

* Clicking on the canvas toggles the settings pane for further editing.
  -->

  <body>
    <form id=e_settings><fieldset><legend>Settings</legend>
      Width: <input id=e_width type=text value=100>
      <br>
      Height: <input id=e_height type=text value=100>
      <br>
      <fieldset><legend>Canvas Context</legend>
        Type: <select id=e_context>
          <option value=css selected>css</option>
          <option value=2d>2d</option>
          <option value=webgl>webgl</option>
          <option value=webgl2>webgl2</option>
        </select>
        <br>
        Options: <input id=e_options type=text value={}>
        <br>
        Colorspace: <input id=e_cspace type=text placeholder=srgb>
        <br>
        WebGL Format: <input id=e_webgl_format type=text placeholder=RGBA8>
      </fieldset>
      <br>
      Color: <input id=e_color type=text value='color(srgb 0 0.5 0)'>
      <br>
      <input id=e_publish type=button value=Publish>
      <input type=checkbox id=e_publish_omit_defaults checked><label for=e_publish_omit_defaults>Omit defaults</label>
      <hr>
    </fieldset></form>
    <div id=e_canvas_list><canvas></canvas></div>
    <script>
'use strict';

// -

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];

    this.set = function(arg) {
      this.arg = arg;
      this.value = (arg && arg.call) ? arg.call() : arg;
      this.value = toPrettyString(this.value);
    };
    this.set(dict[this.label]);

    this.toString = function() {
      let ret = `${this.label} ${toPrettyString(this.arg)}`;
      if (this.arg.call) {
        ret += ` (${this.value})`;
      }
      return ret;
    }
  }

  const eq = (a,b) => a == b;
  const neq = (a,b) => a != b;

  const CMP_BY_NAME = {
    '==': eq,
    '!=': neq,
  };

  function IS(cmp, was, expected, _console) {
    _console = _console || console;

    _console.assert(was.call, '`was.call` not defined.');
    was = new AssertArg({was});
    expected = new AssertArg({expected});

    const fn_cmp = CMP_BY_NAME[cmp] || cmp;

    _console.assert(fn_cmp(was.value, expected.value), `${toPrettyString(was.arg)} => ${was.value} not ${cmp} ${expected}`);
    if (was.value != expected.value) {
    } else if (globalThis.ASSERT && globalThis.ASSERT.verbose) {
      const maybe_cmp_str = (cmp == '==') ? '' : ` ${was.value} ${cmp}`;
      _console.log(`${toPrettyString(was.arg)} => ${maybe_cmp_str}${expected}`);
    }
  }

  // -

  const MOCK_CONSOLE = {
    _asserts: [],
    assert: function(expr, ...args) {
      if (!expr) {
        this._asserts.push(args);
      }
    },
    log: function(...args) {
      // Don't record.
    },
  };

  // -
  // Test `==`

  IS('==', () => 1, 1, MOCK_CONSOLE);
  console.assert(MOCK_CONSOLE._asserts.length == 0, MOCK_CONSOLE._asserts);
  MOCK_CONSOLE._asserts = [];

  IS('==', () => 2, 2, MOCK_CONSOLE);
  console.assert(MOCK_CONSOLE._asserts.length == 0, MOCK_CONSOLE._asserts);
  MOCK_CONSOLE._asserts = [];

  IS('==', () => 5, () => 3, MOCK_CONSOLE);
  console.assert(MOCK_CONSOLE._asserts.length == 1, MOCK_CONSOLE._asserts);
  MOCK_CONSOLE._asserts = [];

  IS('==', () => [1,2], () => [1,2], MOCK_CONSOLE);
  console.assert(MOCK_CONSOLE._asserts.length == 0, MOCK_CONSOLE._asserts);
  MOCK_CONSOLE._asserts = [];

  // -
  // Test `!=`

  IS('!=', () => [1,2,5], () => [1,2,3], MOCK_CONSOLE);
  console.assert(MOCK_CONSOLE._asserts.length == 0, MOCK_CONSOLE._asserts);
  MOCK_CONSOLE._asserts = [];

  // -

  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]);

// -

class CssColor {
  constructor(cspace, r,g,b,a=1) {
    this.cspace = cspace;
    this.rgba = [this.r, this.g, this.b, this.a] = [r,g,b,a];
    this.rgb = this.rgba.slice(0,3);
    this.tuple = [this.cspace, ...this.rgba];
  }

  toString() {
    return `color(${this.cspace} ${this.rgb.join(' ')} / ${this.a})`;
  }
};
CssColor.parse = function(str) {
  return new CssColor(...parse_css_color(str));
}

{
  let STR;
  // Test round-trip.
  STR = 'color(display-p3 1 0 1 / 0.3)';
  ASSERT.EQ(() => CssColor.parse(STR).toString(), STR);

  // Test round-trip normalization
  ASSERT.EQ(() => CssColor.parse('color( display-p3 1 0 1/30% )').toString(), 'color(display-p3 1 0 1 / 0.3)');
}

// -

function redraw() {
  while (e_canvas_list.firstChild) {
    e_canvas_list.removeChild(e_canvas_list.firstChild);
  }

  const c = make_canvas(e_color.value.trim());
  c.style.border = '4px solid black';
  e_canvas_list.appendChild(c);
}

function fill_canvas_rect(context /*: CanvasRenderingContext | WebGLRenderingContext*/, css_color, rect=null) {
  rect = rect || {left: 0, top: 0, w: context.canvas.width, h: context.canvas.height};

  const is_c2d = ('fillRect' in context);
  if (is_c2d) {
    const c2d = context;
    c2d.fillStyle = css_color;
    c2d.fillRect(rect.left, rect.top, rect.w, rect.h);
    return;
  }

  const is_webgl = ('drawArrays' in context);
  if (is_webgl) {
    const gl = context;
    console.assert(context.canvas.width == gl.drawingBufferWidth, context.canvas.width, '!=', gl.drawingBufferWidth);
    console.assert(context.canvas.height == gl.drawingBufferHeight, context.canvas.height, '!=', gl.drawingBufferHeight);

    gl.enable(gl.SCISSOR_TEST);
    gl.disable(gl.DEPTH_TEST);
    const bottom = rect.top + rect.h; // in y-down c2d coords
    gl.scissor(rect.left, context.canvas.height - bottom, rect.w, rect.h);

    const canvas_cspace = context.drawingBufferColorSpace || 'srgb';
    if (css_color.cspace != canvas_cspace) {
      console.warn(`Ignoring mismatched color vs webgl canvas cspace: ${css_color.cspace} vs ${canvas_cspace}`);
    }
    gl.clearColor(...css_color.rgba);
    gl.clear(gl.COLOR_BUFFER_BIT);
    return;
  }

  console.error('Unhandled context kind:', context);
}

window.e_canvas = null;

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

  // -

  fill_canvas_rect(context, css_color);

  return e_canvas;
}

e_settings.addEventListener('change', async () => {
  redraw();
  const e_updated = document.createElement('i');
  e_updated.textContent = '(Updated!)';
  document.body.appendChild(e_updated);
  await new Promise(go => setTimeout(go, 1000));
  document.body.removeChild(e_updated);
});
redraw();

// -

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
C=93 H=97 G=94

¤ Dauer der Verarbeitung: 0.37 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

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.