====== std.color — RGBA-Farben ======
→ [[lyx_-_programmiersprache:units|Zurück zur Unit-Übersicht]]
Farben als vier ganzzahlige Kanäle von 0 bis 255: Rot, Grün, Blau und Deckkraft. Alle Operationen rechnen ohne Fließkomma. Enthalten sind Vorgabefarben, Aufhellen und Abdunkeln, Graustufen, Sättigung, Farbverläufe, Alpha-Überlagerung sowie Hex- und HSL-Umrechnung.
Einsatzbereiche: Grafikausgabe, Bildverarbeitung, Oberflächen, Terminalfarben.
**Autor:** Andreas Röne\\
**Copyright:** 2024–2025 Andreas Röne\\
**Quelle:** ''std/color.lyx''
**Seit 1.0.20F rechnet ''ColorFromHSL'' korrekt** (#1484) — nachgemessen:
HSL(0,255,128) → (255, 0, 0)
HSL(60,255,128) → (255, 255, 0)
HSL(120,255,128) → (0, 255, 0)
HSL(240,255,128) → (0, 0, 255)
HSL(0,0,128) → (128, 128, 128)
**Sättigung und Helligkeit laufen über 0..255, nicht über Prozent.** Die reine Farbe liegt bei ''l = 128''; ''ColorFromHSL(60, 100, 50)'' ergibt darum keine Gelbfarbe, sondern ein dunkles, entsättigtes (69, 69, 31).
''ColorFromHex'' unterscheidet RGB von ARGB **an der Größe der Zahl**: alles über ''$FFFFFF'' gilt als ARGB. Eine vollständig transparente Farbe lässt sich damit nicht ausdrücken — ''ColorFromHex($00000000)'' ergibt undurchsichtiges Schwarz. Dafür gibt es ''ColorFromHexARGB'', das die Zahl immer als ARGB liest (#1485).
===== Import =====
import std.color;
Zieht ''std.math'' mit (''Clamp64'', ''Min64'', ''Max64'', ''Lerp64'').
----
===== Der Typ Color =====
^ Feld ^ Typ ^ Bereich ^ Bedeutung ^
| ''r'' | ''int64'' | 0–255 | Rotanteil |
| ''g'' | ''int64'' | 0–255 | Grünanteil |
| ''b'' | ''int64'' | 0–255 | Blauanteil |
| ''a'' | ''int64'' | 0–255 | Deckkraft: 0 = unsichtbar, 255 = voll deckend |
Die Konstruktoren klemmen jeden Kanal selbst auf den gültigen Bereich — ''ColorNew(300, -20, 0, 99)'' ergibt ''rgba(255, 0, 0, 99)''. Wer die Felder direkt setzt, umgeht diese Prüfung; ''ColorIsValid'' meldet das nachträglich.
----
===== Funktionen =====
==== Erzeugen ====
^ Signatur ^ Beschreibung ^
| ''ColorNew(r, g, b, a: int64): Color'' | Alle vier Kanäle, geklemmt |
| ''ColorRGB(r, g, b: int64): Color'' | Deckend (Alpha 255) |
| ''ColorRGBA(r, g, b, a: int64): Color'' | Wie ''ColorNew'' |
| ''ColorGray(gray: int64): Color'' | Grauton, deckend |
| ''ColorEmpty(): Color'' | Schwarz mit Alpha 0 — vollständig unsichtbar |
| ''ColorOpaque(): Color'' | Schwarz, deckend |
==== Vorgabefarben ====
Je eine Funktion ohne Parameter, alle deckend:
^ Funktion ^ RGB ^ Funktion ^ RGB ^
| ''ColorBlack()'' | 0, 0, 0 | ''ColorMagenta()'' | 255, 0, 255 |
| ''ColorWhite()'' | 255, 255, 255 | ''ColorOrange()'' | 255, 165, 0 |
| ''ColorRed()'' | 255, 0, 0 | ''ColorPurple()'' | 128, 0, 128 |
| ''ColorGreen()'' | 0, 255, 0 | ''ColorPink()'' | 255, 192, 203 |
| ''ColorBlue()'' | 0, 0, 255 | ''ColorBrown()'' | 165, 42, 42 |
| ''ColorYellow()'' | 255, 255, 0 | ''ColorGrayLight()'' | 200, 200, 200 |
| ''ColorCyan()'' | 0, 255, 255 | ''ColorGrayDark()'' | 64, 64, 64 |
==== Abfragen und Ändern ====
^ Signatur ^ Beschreibung ^
| ''ColorIsOpaque(c: Color): bool'' | Alpha genau 255 |
| ''ColorIsTransparent(c: Color): bool'' | Alpha genau 0 |
| ''ColorIsValid(c: Color): bool'' | Alle Kanäle im Bereich 0–255 |
| ''ColorWithAlpha(c: Color, alpha: int64): Color'' | Deckkraft ersetzen |
| ''ColorWithRed'' · ''ColorWithGreen'' · ''ColorWithBlue'' | Einzelnen Kanal ersetzen |
| ''ColorEqual(c1, c2: Color): bool'' · ''ColorNotEqual'' | Vergleich über alle vier Kanäle |
==== Farbe verändern ====
^ Signatur ^ Beschreibung ^
| ''ColorInvert(c: Color): Color'' | Komplementärfarbe (255 − Kanal), Alpha bleibt |
| ''ColorGrayscale(c: Color): Color'' | Helligkeit nach Wahrnehmungsgewichten 0,299 / 0,587 / 0,114 |
| ''ColorBrighten(c: Color, amount: int64): Color'' | Alle Kanäle anheben, bei 255 gedeckelt |
| ''ColorDarken(c: Color, amount: int64): Color'' | Alle Kanäle senken, bei 0 gedeckelt |
| ''ColorSaturate(c: Color, amount: int64): Color'' | Abstand zum Grauwert verstärken; negativ entsättigt, −255 ergibt Grau |
==== Mischen ====
^ Signatur ^ Beschreibung ^
| ''ColorBlend(src, dst: Color): Color'' | ''src'' über ''dst'' legen, Alpha wird ausgewertet |
| ''ColorMultiply(c1, c2: Color): Color'' | Kanalweise Multiplikation — dunkelt ab, Alpha von ''c1'' |
| ''ColorMix(c1, c2: Color): Color'' | Arithmetisches Mittel aller vier Kanäle |
| ''ColorLerp(c1, c2: Color, t: int64): Color'' | Farbverlauf, ''t'' in **Promille**: 0 = ''c1'', 1000 = ''c2'' |
| ''ColorDistance(c1, c2: Color): int64'' | Summe der Kanalunterschiede (**Manhattan**, ohne Alpha) |
''ColorLerp'' erbt den Promille-Maßstab von ''Lerp64'' aus ''std.math''. Achtung beim Wechsel zwischen Units: ''Vec2Lerp'' in [[lyx_-_programmiersprache:units:vector|std.vector]] erwartet denselben Parameter in **Millionstel**.
''ColorDistance'' summiert die drei Kanalabstände, statt die Wurzel aus der Quadratsumme zu ziehen: Schwarz zu Weiß ergibt 765, geometrisch wären es 442. Für „welche Palettenfarbe passt am besten" kann das eine andere Rangfolge liefern (#1485).
==== Hex ====
^ Signatur ^ Beschreibung ^ Status ^
| ''ColorToHex(c: Color): int64'' | ''0xRRGGBB'' ohne Alpha | ok |
| ''ColorToHexARGB(c: Color): int64'' | ''0xAARRGGBB'' | ok |
| ''ColorFromHex(hex: int64): Color'' | Erkennt am Wert, ob ein Alphabyte gesetzt ist | **Alpha 0 unmöglich** (#1485) |
==== HSL ====
^ Signatur ^ Beschreibung ^ Status ^
| ''ColorFromHSL(h, s, l: int64): Color'' | ''h'' in **Grad** (0–360), ''s'' und ''l'' in **0–255** | **Mischfarben falsch** (#1484) |
Die Einheiten sind gemischt: der Farbwinkel in Grad, Sättigung und Helligkeit dagegen im 0–255-Raster. Eine Umkehrfunktion ''ColorToHSL'' gibt es nicht.
----
===== Beispiel =====
import std.color;
fn zeig(name: pchar, c: Color): void {
PrintLn(name, " = rgba(", IntToStr(c.r), ", ", IntToStr(c.g), ", ", IntToStr(c.b), ", ", IntToStr(c.a), ")");
}
fn main(): int64 {
// Werte werden beim Anlegen auf 0..255 geklemmt
zeig("ColorNew(300,-20,0,99)", ColorNew(300, -20, 0, 99));
zeig("ColorRGB(255,136,0) ", ColorRGB(255, 136, 0));
// Hex in beide Richtungen
var orange: Color := ColorFromHex(16746496); // 0xFF8800
zeig("FromHex(0xFF8800) ", orange);
PrintLn("ToHex = ", IntToStr(ColorToHex(orange)));
PrintLn("ToHexARGB = ", IntToStr(ColorToHexARGB(orange)));
// Aufhellen, abdunkeln, entsaettigen
zeig("Brighten(+50) ", ColorBrighten(orange, 50));
zeig("Darken(-50) ", ColorDarken(orange, 50));
zeig("Grayscale ", ColorGrayscale(orange));
zeig("Invert ", ColorInvert(orange));
// Farbverlauf: t in Promille (0..1000)
PrintLn("-- Verlauf schwarz -> weiss:");
zeig(" 0 %", ColorLerp(ColorBlack(), ColorWhite(), 0));
zeig(" 25 %", ColorLerp(ColorBlack(), ColorWhite(), 250));
zeig(" 50 %", ColorLerp(ColorBlack(), ColorWhite(), 500));
zeig("100 %", ColorLerp(ColorBlack(), ColorWhite(), 1000));
// Halbtransparente Farbe ueber Hintergrund legen
var halbRot: Color := ColorRGBA(255, 0, 0, 128);
zeig("Blend(halbRot ueber weiss)", ColorBlend(halbRot, ColorWhite()));
return 0;
}
Ausgabe:
ColorNew(300,-20,0,99) = rgba(255, 0, 0, 99)
ColorRGB(255,136,0) = rgba(255, 136, 0, 255)
FromHex(0xFF8800) = rgba(255, 136, 0, 255)
ToHex = 16746496
ToHexARGB = 4294936576
Brighten(+50) = rgba(255, 186, 50, 255)
Darken(-50) = rgba(205, 86, 0, 255)
Grayscale = rgba(156, 156, 156, 255)
Invert = rgba(0, 119, 255, 255)
-- Verlauf schwarz -> weiss:
0 % = rgba(0, 0, 0, 255)
25 % = rgba(63, 63, 63, 255)
50 % = rgba(127, 127, 127, 255)
100 % = rgba(255, 255, 255, 255)
Blend(halbRot ueber weiss) = rgba(255, 127, 127, 255)
''ColorBlend'' rechnet die Deckkraft korrekt ein: halbtransparentes Rot über Weiß ergibt ein helles Rosa mit voller Deckung.
----
===== Was nicht stimmt =====
Geprüft mit ''lyxc 1.0.21A''.
==== HSL: Mischfarben und Helligkeit (#1484) ====
Die Primärfarben stimmen, alles dazwischen liegt daneben:
^ Winkel ^ erwartet ^ geliefert ^
| 0° Rot | 255, 0, 0 | 255, 0, 0 |
| 60° Gelb | 255, 255, 0 | **129, 255, 0** (Gelbgrün) |
| 120° Grün | 0, 255, 0 | 0, 255, 0 |
| 180° Cyan | 0, 255, 255 | **0, 129, 255** (Azurblau) |
| 240° Blau | 0, 0, 255 | 0, 0, 255 |
| 300° Magenta | 255, 0, 255 | **255, 0, 129** (Rosa) |
Die Sektorgrenzen in der internen Hilfsfunktion sind auf das 0–255-Raster falsch übertragen; genau an den Sechstel-Grenzen fällt die Rechnung in den falschen Zweig.
Zusätzlich wirkt die Helligkeit nur nach unten:
l=64 -> rgba(128, 0, 0) dunkelrot, korrekt
l=128 -> rgba(255, 0, 0) rot, korrekt
l=200 -> rgba(255, 0, 0) sollte zu Weiss aufhellen
l=255 -> rgba(255, 0, 0) sollte reines Weiss sein
Die Formel bildet nur den Fall „Helligkeit unter der Hälfte" ab. Ein Verlauf über die Helligkeit endet damit bei der Vollfarbe statt bei Weiß.
Bis zum Fix Farbverläufe über ''ColorLerp'' gegen ''ColorWhite()'' bzw. ''ColorBlack()'' bilden — das arbeitet korrekt.
==== Hex: Alpha 0 geht verloren (#1485) ====
var c: Color := ColorEmpty(); // rgba(0, 0, 0, 0)
var zurueck: Color := ColorFromHex(ColorToHexARGB(c));
PrintLn(IntToStr(zurueck.a)); // 255 statt 0
''ColorFromHex'' schließt aus der Größe des Werts auf ein vorhandenes Alphabyte. Bei Alpha 0 bleibt das obere Byte leer, der Wert liegt unter der Schwelle — und die Farbe gilt als deckend. Eine unsichtbare Farbe übersteht den Rundlauf also nicht.
Für Werte mit gesetztem Alphabyte arbeitet die Funktion korrekt: ''ColorFromHex(0x80FF0000)'' ergibt ''rgba(255, 0, 0, 128)''.
Wer Transparenz braucht, setzt sie nach dem Einlesen ausdrücklich:
var c: Color := ColorWithAlpha(ColorFromHex(0xFF0000), 0);
----
===== Verwandte Units =====
* [[lyx_-_programmiersprache:units:math|std.math]] — ''Clamp64'', ''Lerp64'', ''Min64''/''Max64''
* [[lyx_-_programmiersprache:units:vector|std.vector]] — ''Vec2Lerp'', beachte den anderen Maßstab für ''t''