std.color — RGBA-Farben

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

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


Verwandte Units

  • std.mathClamp64, Lerp64, Min64/Max64
  • std.vectorVec2Lerp, beachte den anderen Maßstab für t