Kein Weichzeichner mehr -- die Bilder werden scharf, die Kacheln Lampen

Filipe: "gerade sehen die sogar leicht verschwommen aus ... die sollen
nicht durch die kacheln gehen ... ich wollte eine krasse aenderung."

Drei Ursachen, alle drei von mir eingebaut, alle drei gemessen:

1. ICH HABE 1647-PIXEL-BILDER ALS "uhd" MIT 2400 PIXELN AUSGELIEFERT.
   Am Morgen stand in derselben Datei, sein Schirm sei 2550 px breit und
   das groesste Bild 1600 -- meine "Loesung" war, die Ausgabe
   hochzurechnen. Es gab nie mehr Bildpunkte. Dazu fehlte der Schritt,
   den der Kommentar daneben selbst verlangt ("Verkleinern MITTELT
   Bildpunkte ... deshalb schaerft man danach nach"): Es wurde nie
   nachgeschaerft. Jetzt Unscharfmaske auf der ENDgroesse
   (Ausgabeschaerfung) und ein Glanz-Durchgang: die hellsten Stellen
   weich gezeichnet und additiv dazu -- Licht, das ueber seine Kante
   strahlt. Guete 0,80 -> 0,88, am Gate 0,93.

2. backdrop-filter: blur() UNTER JEDER FLAECHE. 16 px unter siebzehn
   Kacheln, 13 px unter sieben weiteren Flaechen, 26 px unter der
   Anmeldekarte. Ein Weichzeichner mittelt nicht nur, was hinter dem
   Element liegt -- er zieht seinen Radius weit darueber hinaus. Hinter
   der Anmeldetafel ist Schwarz, direkt daneben aber die hellste Stelle
   des Motivs: Die Karte wurde deshalb GRAU, gemessen rgb(46,57,64)
   statt rgb(15,22,34). Je schoener der Rahmen, desto grauer die Karte.
   Alle Weichzeichner raus, --flaeche 0,78 -> 0,89: Durchsicht bleibt,
   aber scharf.

3. DIE TAFELMESSUNG WAR FALSCH, UND ICH HABE SIE VON HAND "KORRIGIERT".
   Sie lief vom Inneren nach aussen und hielt beim ersten farbigen Punkt
   an -- links glueht der Rahmen breiter als rechts, also hielt sie dort
   frueher an (58,59 statt 55,37). Statt den Messfehler zu beheben, habe
   ich in der CSS "um 1,1 % nach links" geschaetzt. Ergebnis: 32 px
   schwarze Tafel blieben links offen. Jetzt wird die einzige
   Eigenschaft gesucht, die nur der Rahmen hat -- kraeftig UND farbig --,
   mit Gegenprobe auf der linken Bildhaelfte (dort muessen es 0 sein).

KACHELN: keine Politur mehr, ein anderes Ding. Jede steckt in einer
LEUCHTSCHIENE ihrer Kategoriefarbe, die Licht in die Platte wirft --
links scharfe Kante, rechts rund. Steiler Abfall (52 % statt 68 %):
beleuchtet, nicht eingefaerbt. Der Glanz wandert beim Ueberfahren
einmal durch. Dieselbe Sprache auf der Anmeldeseite: die vier Rollen
sind Tasten in Schienen (Gold/Bernstein/Gruen/Blau).

Zwei eigene Fehler nebenbei, beide durch das Unveraenderlichkeits-
Zeichen gefunden -- eine Zahl, die sich nach einer Aenderung KEIN Stueck
bewegt, sagt "falsche Stelle", nicht "zu wenig":
  - Dreimal exakt 4,16:1 an "Ueberfaellig". Der Pruefpunkt lag nicht auf
    dem Knopf, sondern in der Luecke daneben auf dem Bild. Ursache war
    das hellere Hochkant-Bild, nicht das Bedienelement -> mehr Schleier
    und weniger Glanz NUR fuer die Handy-Fassung. 4,16 -> 5,10.
  - Die Koernung der Anmeldekarte lag mit 90 % Deckkraft ohne Mischmodus
    ueber allem und hob jeden Bildpunkt um 30 Stufen. Jetzt 4 % mit
    overlay, auf eigener Ebene.
  - --flaeche anzuheben machte die WICHTIGE Anleitung duenner als eine
    gewoehnliche (88 gegen 89) -- feste Zahl neben beweglicher Groesse.
    Gefunden von pruef-lesbarkeit, jetzt an --flaeche gebunden.

Gruen: buehne (38), start-ansicht (136), handy (59), breiten (23),
lesbarkeit (14), css-klassen (15), barrierefrei (18), tempo (8).

Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
2026-09-07 03:10:13 +02:00
co-authored by Claude Opus 5
parent 947c9d8a26
commit 8efdb39ce9
59 changed files with 848 additions and 328 deletions
+275 -84
View File
@@ -55,6 +55,35 @@ await seite.setContent("<!doctype html><meta charset=utf-8><body>");
/* ---------------------------------------------------------------------
1. Wo sitzt die Tafel?
DRITTER ANLAUF, und der davor war es, der Filipe geaergert hat
(07.09.2026).
Anlauf 1 suchte den LEUCHTENDEN Rahmen und fand den Mond, die Blueten
und jede Spiegelung im Wasser -- also die halbe Bildhaelfte.
Anlauf 2 suchte die groesste DUNKLE Flaeche und fand 94 % Hoehe: Ueber
und unter der Tafel ist die Szene genauso schwarz.
Anlauf 3 lief vom Tafelinneren nach aussen und hielt beim ersten
farbigen Punkt an. Das klang richtig und war es fast -- nur glueht der
linke Rahmen im Motiv viel breiter als der rechte (breites Rot gegen
duennes Blau), also hielt er links FRUEHER an. Heraus kamen 58,59 %
statt 55,37 %. Ich habe den Unterschied damals nicht als Messfehler
erkannt, sondern in der CSS "um 1,1 % nach links" von Hand
nachjustiert -- eine Schaetzung auf eine Messung, der ich nicht
traute. Danach stand links ein 32 px breiter schwarzer Streifen der
gemalten Tafel offen, und genau den hat Filipe gesehen.
WAS JETZT GESUCHT WIRD, IST NICHT "hell" UND NICHT "dunkel", SONDERN
DIE EINZIGE EIGENSCHAFT, DIE NUR DER RAHMEN HAT: kraeftig UND stark
farbig zugleich (Maximum > 150 und Spanne zwischen groesstem und
kleinstem Kanal > 60). Mond und Nebel sind hell, aber grau. Die
Tafelflaeche ist dunkel und grau. Nur die vier Neonlinien sind beides.
Gezaehlt wird spaltenweise und zeilenweise, wie viele Punkte das
erfuellen. Die Linien stehen dann als klare Spitzen heraus -- kein
Hinlaufen, kein Anhalten, kein Richtungsproblem. Die Oberkante ist
dunkelrot und faellt unter die Neonschwelle; fuer sie gibt es eine
zweite Messung ueber das Helligkeitsprofil (136 gegen 20 daneben).
--------------------------------------------------------------------- */
const tafel = await seite.evaluate(async (daten) => {
const i = new Image(); i.src = daten; await i.decode();
@@ -63,90 +92,56 @@ const tafel = await seite.evaluate(async (daten) => {
const g = c.getContext("2d");
g.drawImage(i, 0, 0);
const d = g.getImageData(0, 0, c.width, c.height).data;
const W = c.width, H = c.height;
/* ZWEI ANLAEUFE WAREN FALSCH, und beide haben es selbst verraten.
(1) "Suche den LEUCHTENDEN Rahmen" fand den Mond, die roten Blueten
und jede Spiegelung im Wasser -- Ergebnis: die ganze rechte
Bildhaelfte. Eine Messung, die das Offensichtliche zurueckgibt,
hat nichts gemessen.
(2) "Suche die groesste DUNKLE Flaeche" fand die Breite richtig,
aber 94 % Hoehe: Ueber und unter der Tafel ist die Szene
genauso schwarz. Dunkelheit unterscheidet sie nicht.
Was sie WIRKLICH unterscheidet, ist der Rahmen -- aber nur, wenn
man ihn dort sucht, wo nichts anderes leuchtet: INNERHALB der
Tafel. Deshalb wird jetzt vom Mittelpunkt aus nach aussen gelaufen,
bis es hell wird. Auf diesem Weg liegt garantiert nichts anderes,
denn die Tafelflaeche ist leer.
Der Startpunkt ist dabei nicht die Bildmitte, sondern ein Punkt
sicher INNERHALB der Tafel -- gefunden ueber die dunkle Strecke aus
Anlauf (2), die waagerecht ja richtig lag. */
/* HELL ODER FARBIG. Nur nach Helligkeit zu suchen, hat die OBERE
Kante nicht gefunden: Sie ist ein duenner, dunkelroter Strich (der
rote Teil des Rahmens laeuft dort aus), waehrend die untere Kante
hell blau leuchtet. Der Rahmen ist aber an JEDER Stelle deutlich
FARBIG -- die Tafelflaeche daneben ist neutralgrau. Genau das
unterscheidet ihn, und zwar rundum. */
const hell = (x, y) => {
const p = (y * c.width + x) * 4;
const r = d[p], g2 = d[p + 1], b = d[p + 2];
const max = Math.max(r, g2, b), min = Math.min(r, g2, b);
return max > 118 || (max > 52 && max - min > 26);
const neon = (x, y) => {
const p = (y * W + x) * 4;
const r = d[p], gg = d[p + 1], b = d[p + 2];
const max = Math.max(r, gg, b), min = Math.min(r, gg, b);
return max > 150 && max - min > 60;
};
const dunkel = (x, y) => {
const p = (y * c.width + x) * 4;
return Math.max(d[p], d[p + 1], d[p + 2]) < 62;
const helligkeit = (x, y) => {
const p = (y * W + x) * 4;
return Math.max(d[p], d[p + 1], d[p + 2]);
};
/* Grobe Waagerechte: die laengste dunkle Strecke auf halber Hoehe.
Sie liegt sicher in der Tafel -- daraus kommt der Startpunkt. */
function laengsteStrecke(von, bis, punkt) {
let bA = von, bB = von - 1, a = -1, luecke = 0;
for (let n = von; n <= bis; n++) {
if (punkt(n)) { if (a < 0) a = n; luecke = 0; }
else if (a >= 0 && ++luecke > 6) {
const b = n - luecke;
if (b - a > bB - bA) { bA = a; bB = b; }
a = -1; luecke = 0;
}
}
if (a >= 0 && bis - a > bB - bA) { bA = a; bB = bis; }
return [bA, bB];
/* Alle Spalten der rechten Bildhaelfte, in denen ueber die Tafelhoehe
hinweg mindestens die Haelfte der Punkte Neon ist. */
const yA = Math.round(H * 0.16), yB = Math.round(H * 0.74);
const spalten = [];
for (let x = Math.round(W * 0.50); x < W; x++) {
let n = 0;
for (let y = yA; y < yB; y++) if (neon(x, y)) n++;
if (n / (yB - yA) > 0.55) spalten.push(x);
}
const yMitte = Math.round(c.height * 0.45);
const [gLinks, gRechts] = laengsteStrecke(
Math.round(c.width * 0.45), c.width - 1, (x) => dunkel(x, yMitte));
const xStart = Math.round((gLinks + gRechts) / 2);
/* Die beiden aeussersten sind die senkrechten Rahmenlinien. */
const links = spalten.length ? Math.min(...spalten) : -1;
const rechts = spalten.length ? Math.max(...spalten) : -1;
/** Vom Startpunkt aus in eine Richtung laufen, bis es hell wird.
* Verlangt werden ZWEI helle Punkte hintereinander -- ein einzelner
* waere Bildrauschen. */
function bisZumRahmen(x0, y0, dx, dy, grenze) {
let x = x0, y = y0, hintereinander = 0;
while (x >= 0 && y >= 0 && x < c.width && y < c.height) {
if (hell(x, y)) { if (++hintereinander >= 2) return dx ? x : y; }
else hintereinander = 0;
x += dx; y += dy;
if (dx && (dx > 0 ? x > grenze : x < grenze)) break;
if (dy && (dy > 0 ? y > grenze : y < grenze)) break;
}
return dx ? (dx > 0 ? grenze : grenze) : (dy > 0 ? grenze : grenze);
/* Dasselbe waagerecht, INNERHALB der eben gefundenen Breite. */
const xA = links > 0 ? links + Math.round((rechts - links) * 0.08) : Math.round(W * 0.58);
const xB = rechts > 0 ? rechts - Math.round((rechts - links) * 0.08) : Math.round(W * 0.90);
const zeilen = [];
for (let y = Math.round(H * 0.05); y < Math.round(H * 0.90); y++) {
let n = 0;
for (let x = xA; x < xB; x++) if (neon(x, y)) n++;
if (n / (xB - xA) > 0.55) zeilen.push(y);
}
const unten = zeilen.length ? Math.max(...zeilen) : -1;
/* DIE OBERKANTE EINZELN. Sie ist ein duenner dunkelroter Strich und
erreicht die Neonschwelle nicht. Ueber das Helligkeitsprofil ist sie
trotzdem eindeutig: die hellste Zeile im oberen Fuenftel, weit ueber
allem daneben. */
let oben = -1, bestes = 0;
for (let y = Math.round(H * 0.06); y < Math.round(H * 0.24); y++) {
let summe = 0;
for (let x = xA; x < xB; x++) summe += helligkeit(x, y);
const mittel = summe / (xB - xA);
if (mittel > bestes) { bestes = mittel; oben = y; }
}
/* Auf mehreren Linien, damit ein Staubkorn nicht das Ergebnis
bestimmt -- genommen wird jeweils der mittlere Wert. */
const mitte = (w) => w.slice().sort((a, b) => a - b)[Math.floor(w.length / 2)];
const yProben = [0.35, 0.42, 0.5, 0.58, 0.65].map((f) => Math.round(c.height * f));
const links = mitte(yProben.map((y) => bisZumRahmen(xStart, y, -1, 0, Math.round(c.width * 0.42))));
const rechts = mitte(yProben.map((y) => bisZumRahmen(xStart, y, +1, 0, c.width - 1)));
const xProben = [0.3, 0.42, 0.5, 0.58, 0.7].map((f) => Math.round(links + (rechts - links) * f));
const oben = mitte(xProben.map((x) => bisZumRahmen(x, yMitte, 0, -1, 0)));
const unten = mitte(xProben.map((x) => bisZumRahmen(x, yMitte, 0, +1, c.height - 1)));
return { breite: c.width, hoehe: c.height, links, rechts, oben, unten };
return { breite: W, hoehe: H, links, rechts, oben, unten, obenHelligkeit: Math.round(bestes) };
}, alsDatenAdresse(join(QUELLE, "gate-spicy-dogfather.jpeg")));
const p = (v, g) => Math.round((v / g) * 10000) / 100;
@@ -159,7 +154,11 @@ const p = (v, g) => Math.round((v / g) * 10000) / 100;
{
const bAnteil = (tafel.rechts - tafel.links) / tafel.breite;
const hAnteil = (tafel.unten - tafel.oben) / tafel.hoehe;
const gut = bAnteil > 0.25 && bAnteil < 0.62 && hAnteil > 0.35 && hAnteil < 0.92
/* "Nichts gefunden" ist ein eigener Ausgang, nicht bloss ein
unplausibler Wert: -1 heisst, es gab ueberhaupt keine Spitze. */
const gefunden = tafel.links > 0 && tafel.rechts > 0 && tafel.unten > 0 && tafel.oben > 0;
const gut = gefunden
&& bAnteil > 0.25 && bAnteil < 0.62 && hAnteil > 0.35 && hAnteil < 0.92
&& tafel.links / tafel.breite > 0.45;
if (!gut) {
console.error(`
@@ -168,11 +167,51 @@ ABBRUCH: Die gefundene Tafel ist nicht plausibel `
+ `links bei ${p(tafel.links, tafel.breite)} %).`);
console.error(`Erwartet wird ein Feld zwischen 25 und 62 Prozent Breite in der `
+ `rechten Bildhaelfte.
Wahrscheinlich passt die Helligkeitsschwelle nicht `
+ `zum Bild -- in gate-bauen.mjs bei "dunkel()" nachsehen.`);
${gefunden
? "Die vier Linien wurden gefunden, liegen aber falsch -- passt das Motiv noch?"
: "Es wurde ueberhaupt keine Neonlinie gefunden. Die Schwelle in neon() "
+ "(max > 150, Spanne > 60) passt dann nicht zum Bild."}`);
await browser.close();
process.exit(2);
}
/* GEGENPROBE: Kann diese Messung ueberhaupt "nein" sagen? Dieselben
Zaehlungen auf der LINKEN Bildhaelfte duerfen KEINE durchgehende
senkrechte Linie finden -- dort steht das Logo, hell und farbig,
aber ohne Rahmen. Faende sie auch dort etwas, wuerde sie auf
"irgendetwas Buntes" reagieren statt auf den Rahmen, und ihre
vier Zahlen waeren Zufall. */
const gegenprobe = await seite.evaluate(async (daten) => {
const i = new Image(); i.src = daten; await i.decode();
const c = document.createElement("canvas");
c.width = i.width; c.height = i.height;
const g = c.getContext("2d"); g.drawImage(i, 0, 0);
const d = g.getImageData(0, 0, c.width, c.height).data;
const neon = (x, y) => {
const p = (y * c.width + x) * 4;
const r = d[p], gg = d[p + 1], b = d[p + 2];
const max = Math.max(r, gg, b), min = Math.min(r, gg, b);
return max > 150 && max - min > 60;
};
const yA = Math.round(c.height * 0.16), yB = Math.round(c.height * 0.74);
let treffer = 0;
for (let x = 0; x < Math.round(c.width * 0.45); x++) {
let n = 0;
for (let y = yA; y < yB; y++) if (neon(x, y)) n++;
if (n / (yB - yA) > 0.55) treffer++;
}
return treffer;
}, alsDatenAdresse(join(QUELLE, "gate-spicy-dogfather.jpeg")));
if (gegenprobe > 4) {
console.error(`
ABBRUCH (Gegenprobe): Auch links vom Rahmen wurden ${gegenprobe} `
+ `durchgehende Neonspalten gefunden. Die Suche reagiert dann nicht auf den `
+ `Rahmen, sondern auf irgendetwas Buntes -- ihre vier Zahlen waeren Zufall.`);
await browser.close();
process.exit(2);
}
console.log(`Gegenprobe: links vom Rahmen ${gegenprobe} durchgehende Neonspalten `
+ `(erwartet 0 bis 4) -- die Suche findet wirklich den Rahmen.`);
}
console.log(`Quellbild: ${tafel.breite}x${tafel.hoehe}`);
console.log("Die Tafel im Bild:");
@@ -242,9 +281,116 @@ console.log(` --gate-seiten: ${tafel.breite} / ${tafel.hoehe};`);
*
* Alles im Browser gerechnet, der ohnehin fuer die Pruefungen da ist --
* kein Bildprogramm, keine neue Abhaengigkeit, 0 EUR.
*
* ---------------------------------------------------------------------
* ZWEITER ANLAUF (07.09.2026): DER SATZ OBEN WAR RICHTIG UND ICH HABE
* IHN NICHT BEFOLGT
*
* Filipe: "gerade sehen die sogar leicht verschwommen aus, das ist
* richtig scheisse ... die sollen wirklich sogar glaenzen."
*
* Er hat recht, und der Grund stand die ganze Zeit vier Absaetze weiter
* oben in dieser Datei: "Verkleinern MITTELT Bildpunkte ... und der
* Grund, warum man danach NACHSCHAERFT." Nachgeschaerft wurde nie.
* Kontrast und Saettigung waren da, der eine Schritt, der Kanten
* wirklich wieder Kanten macht, fehlte.
*
* Dazu ein zweiter, groesserer Fehler, und der ist meiner:
*
* DIE QUELLBILDER SIND 1647 x 955 PIXEL GROSS.
* ICH HABE DARAUS "uhd"-FASSUNGEN MIT 2400 PIXELN GEBAUT.
*
* Das ist eine VERGROESSERUNG um das 1,46-fache. Am 07.09. habe ich
* genau hier notiert, sein Bildschirm sei 2550 px breit und das groesste
* Bild nur 1600, deshalb werde jede Kante weich -- und habe als "Loesung"
* die Ausgabe auf 2400 hochgerechnet. Es gab nie mehr Bildpunkte. Ich
* habe die Datei um zwei Drittel groesser gemacht und dabei erfundene,
* weiche Bildpunkte ausgeliefert, in einer Datei, in der daneben steht,
* dass man Bildpunkte nicht zurueckholen kann.
*
* Behoben wird das nicht durch "kleiner ausliefern" -- dann rechnet der
* Browser hoch und das Ergebnis ist genauso weich, nur ohne dass man es
* steuern kann. Behoben wird es durch die Reihenfolge, die jedes
* Druckvorstufen-Handbuch vorschreibt: erst auf die ENDGROESSE rechnen,
* DANN schaerfen (Ausgabeschaerfung). Was der Browser danach noch
* skaliert, ist ein Zehntel statt der Haelfte.
*
* 4. UNSCHARFMASKE. Der Name taeuscht: Es wird eine WEICHE Kopie
* gebaut und die Differenz zum Original wieder DRAUFGERECHNET.
* Wo beide gleich sind (glatte Flaechen) passiert nichts; wo sie
* auseinandergehen (Kanten) wird der Unterschied verstaerkt. Genau
* deshalb schaerft es Kanten, ohne Rauschen in glatten Flaechen
* hochzuziehen.
*
* 5. GLANZ (Bloom). Das ist der Unterschied zwischen "hell" und
* "leuchtet". Aus dem Bild werden nur die HELLSTEN Stellen
* herausgezogen (Neon, nasser Stein, Wasserspiegelung), weich
* gezeichnet und ADDITIV wieder darueber gelegt. Licht, das ueber
* seine Kante hinausstrahlt -- so sieht jede Kameraaufnahme aus,
* und ohne das wirkt jedes Neonmotiv wie eine Zeichnung.
* Additiv (`lighter`) und nicht deckend: Der Glanz LEGT LICHT DAZU,
* er ueberdeckt nichts. Die dunklen Stellen bleiben unberuehrt,
* damit der Text darueber lesbar bleibt.
*/
/* Die beiden neuen Schritte laufen im Browser und bekommen deshalb ihren
Quelltext als Zeichenkette mit -- `evaluate` schickt nur Daten, keine
Funktionen aus dem Node-Teil. */
const BILD_WERKZEUG = `
/* Unscharfmaske: Original + staerke * (Original - Weichzeichnung). */
function schaerfen(g, c, staerke, radius) {
if (staerke <= 0) return;
const b = document.createElement("canvas");
b.width = c.width; b.height = c.height;
const gb = b.getContext("2d");
gb.filter = "blur(" + radius + "px)";
gb.drawImage(c, 0, 0);
const o = g.getImageData(0, 0, c.width, c.height);
const u = gb.getImageData(0, 0, c.width, c.height);
const A = o.data, B = u.data;
for (let i = 0; i < A.length; i += 4) {
for (let k = 0; k < 3; k++) {
const v = A[i + k] + staerke * (A[i + k] - B[i + k]);
A[i + k] = v < 0 ? 0 : v > 255 ? 255 : v;
}
}
g.putImageData(o, 0, 0);
}
/* Glanz: die hellsten Stellen weich machen und Licht dazugeben. */
function glanz(g, c, staerke, schwelle, radius) {
if (staerke <= 0) return;
const h = document.createElement("canvas");
h.width = c.width; h.height = c.height;
const gh = h.getContext("2d");
gh.drawImage(c, 0, 0);
const d = gh.getImageData(0, 0, c.width, c.height);
const D = d.data;
for (let i = 0; i < D.length; i += 4) {
const m = Math.max(D[i], D[i + 1], D[i + 2]);
if (m <= schwelle) { D[i] = 0; D[i + 1] = 0; D[i + 2] = 0; continue; }
/* Weich einsetzend, nicht als Stufe -- eine harte Schwelle malt
Raender um jede helle Flaeche. */
const f = (m - schwelle) / (255 - schwelle);
D[i] *= f; D[i + 1] *= f; D[i + 2] *= f;
}
gh.putImageData(d, 0, 0);
const w = document.createElement("canvas");
w.width = c.width; w.height = c.height;
const gw = w.getContext("2d");
gw.filter = "blur(" + radius + "px)";
gw.drawImage(h, 0, 0);
g.globalCompositeOperation = "lighter";
g.globalAlpha = staerke;
g.drawImage(w, 0, 0);
g.globalAlpha = 1;
g.globalCompositeOperation = "source-over";
}
`;
async function bauen({ quelle, ziel, breite, ausschnitt = null, dunkel = 0, guete = 0.82,
seitenverhaeltnis = null, kontrast = 1.08, saettigung = 1.1, vignette = 0.34 }) {
seitenverhaeltnis = null, kontrast = 1.08, saettigung = 1.1, vignette = 0.34,
schaerfe = 0.9, glanzStaerke = 0.34 }) {
const webp = await seite.evaluate(async (a) => {
const i = new Image(); i.src = a.daten; await i.decode();
@@ -268,6 +414,32 @@ async function bauen({ quelle, ziel, breite, ausschnitt = null, dunkel = 0, guet
g.drawImage(i, qx, qy, qb, qh, 0, 0, zb, zh);
g.filter = "none";
/* Die beiden Werkzeuge stehen als Text im Aufruf -- hier
eingesetzt. */
// eslint-disable-next-line no-eval
eval(a.werkzeug);
/* 4: NACHSCHAERFEN -- und zwar HIER, direkt nach dem Rechnen auf die
Endgroesse und VOR Vignette und Schleier.
Die Reihenfolge ist nicht Geschmack. Vignette und Schleier sind
weiche Verlaeufe; wer danach schaerft, schaerft deren Uebergaenge
mit und bekommt Ringe um die dunklen Ecken. Und wer vor dem
Verkleinern schaerft, wirft die Schaerfe beim Verkleinern wieder
weg -- genau deshalb heisst dieser Schritt "Ausgabeschaerfung".
Radius nach Bildgroesse: Eine feste Zahl waere bei 900 px eine
Kante und bei 2400 px unsichtbar. Ueber die Breite gerechnet
bleibt die Wirkung gleich -- dieselbe Ueberlegung wie bei einer
Schwelle, die man besser messen als festschreiben laesst. */
const radius = Math.max(0.6, zb / 1400);
schaerfen(g, c, a.schaerfe, radius);
/* 5: GLANZ. Nach dem Schaerfen, damit das Licht die geschaerften
Kanten umspielt und nicht umgekehrt der Glanz mitgeschaerft wird
(das gaebe harte Lichtsaeume statt weichem Strahlen). */
glanz(g, c, a.glanzStaerke, 176, Math.max(3, zb / 190));
/* 3: Die Vignette. Elliptisch ueber die ganze Flaeche, in der Mitte
voellig klar, zu den Ecken hin dunkel. Der weiche Verlauf ist
wichtig -- eine harte Kante saehe aus wie ein Fehler und nicht wie
@@ -290,7 +462,7 @@ async function bauen({ quelle, ziel, breite, ausschnitt = null, dunkel = 0, guet
}
return c.toDataURL("image/webp", a.guete);
}, { daten: alsDatenAdresse(quelle), breite, ausschnitt, dunkel, guete, seitenverhaeltnis,
kontrast, saettigung, vignette });
kontrast, saettigung, vignette, schaerfe, glanzStaerke, werkzeug: BILD_WERKZEUG });
const puffer = Buffer.from(webp.split(",")[1], "base64");
writeFileSync(ziel, puffer);
@@ -328,7 +500,7 @@ for (const b of [960, 1280, 1600, 1920, 2560]) {
/* Nur ein Hauch. Der Neonrahmen der Tafel ist das, was die Karte
traegt -- wer ihn wegdunkelt, hat ein huebsches Bild und eine
Karte, die im Nichts schwebt. */
dunkel: 0.08, guete: 0.88, vignette: 0.26,
dunkel: 0.08, guete: 0.93, vignette: 0.26, schaerfe: 1.15, glanzStaerke: 0.42,
});
console.log(` gate-${b}.webp ${Math.round(n / 1024)} KB`);
}
@@ -427,7 +599,7 @@ const gebaut = new Set();
for (const [szene, datei] of Object.entries(SZENEN)) {
const quelle = join(QUELLE, datei);
const zeile = [];
for (const [name, breite, guete] of [["uhd", 2400, 0.80], ["breit", 1600, 0.82]]) {
for (const [name, breite, guete] of [["uhd", 2400, 0.88], ["breit", 1600, 0.88]]) {
const n = await bauen({
quelle, ziel: join(ZIEL, `buehne-${szene}-${name}.webp`), breite,
dunkel: DUNKEL, guete,
@@ -440,7 +612,26 @@ for (const [szene, datei] of Object.entries(SZENEN)) {
es geht, steht in allen sieben Motiven mittig. */
const n = await bauen({
quelle, ziel: join(ZIEL, `buehne-${szene}-schmal.webp`), breite: 900,
ausschnitt: [0.22, 0, 0.56, 1], dunkel: DUNKEL + 0.04, guete: 0.80,
/* HOCHKANT DARF NICHT SO HELL SEIN WIE BREIT (07.09.2026).
Der Glanz hebt die hellen Stellen an -- auf dem Handy trifft das
ausgerechnet die Bildmitte, und dort liegen die kleinsten Texte
der ganzen Anwendung. pruef-buehne hat es an echten Bildpunkten
gemeldet: "Ueberfaellig" kam auf 4,16:1 statt 4,5:1, gemessen auf
rgb(79, 76, 79) -- und dieser Punkt lag nicht auf dem Knopf,
sondern in der Luecke DANEBEN, also direkt auf dem Bild.
Drei Aenderungen am Knopf haben die Zahl auf die Stelle genau
nicht bewegt (dreimal exakt 4,16). Genau das ist das Zeichen, dass
man an der falschen Stelle dreht -- die Ursache lag im Bild, nicht
im Bedienelement.
Deshalb hier: mehr Schleier und weniger Glanz, aber NUR fuer die
Hochkant-Fassung. Auf dem grossen Schirm bleibt alles so hell und
glaenzend, wie Filipe es wollte -- dort ist der Text groesser und
die Kacheln decken mehr ab. */
ausschnitt: [0.22, 0, 0.56, 1], dunkel: DUNKEL + 0.12, guete: 0.87,
glanzStaerke: 0.20,
});
gesamt += n;
zeile.push(`schmal ${Math.round(n / 1024)} KB`);