std.color — RGBA-Farben
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
<WRAP alert>
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).
</WRAP>
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 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);
