Farbfilter können Ihren grauen Himmel blau färben

Avatar of Amelia Bellamy-Royds
Amelia Bellamy-Royds am

DigitalOcean bietet Cloud-Produkte für jede Phase Ihrer Reise. Starten Sie mit 200 $ kostenlosem Guthaben!

Der folgende Beitrag ist ein Gastbeitrag von Amelia Bellamy-Royds. Ich mochte schon immer den „Duoton“-Effekt in Fotos. In Photoshop kann man sie erstellen, indem man ein Bild in den Graustufenmodus und dann in den Duoton-Modus konvertiert. So werden die Lichter einer Farbe und die Schatten einer anderen „zugeordnet“. Das sieht nicht nur cool aus, sondern Bilder mit weniger Farben sind auch kleiner und somit gut für die Performance. Als ich Amelia sah, wie sie damit programmatisch über SVG auf CodePen experimentierte, fragte ich sie, ob sie bereit wäre, uns durch einen Gastbeitrag zu lehren. Glücklicherweise ist er hier!

Früher, wenn man künstlerische Bilder für sein Webdesign haben wollte, erstellte man sie in Photoshop. Oder vielleicht GIMP, wenn man kantig und Open-Source-orientiert war. Aber in jedem Fall war das Endergebnis eine einzige, statische Bilddatei, die auf den Server hochgeladen, vom Webbrowser des Benutzers heruntergeladen und genau so angezeigt wurde, wie man sie erstellt hatte. Wenn man einen grafischen Effekt als Reaktion auf Benutzerinteraktionen ein- und ausschalten wollte, exportierte man zwei verschiedene Bilddateien und tauschte sie mit JavaScript oder CSS-Pseudoklassen aus.

Grafische Effekte – erst in SVG, jetzt in CSS – ändern das. Man kann Photoshop-ähnliche Filter oder überblendete Ebenen direkt im Browser anwenden. Das bedeutet, man kann eine einzige Bilddatei verwenden und sie auf verschiedene Weise präsentieren, je nach Benutzerinteraktion. Es bedeutet auch, dass man mit einem langweiligen Schwarz-Weiß-Foto viel Spaß haben kann.

Filtereffekt-Grundlagen

Die am einfachsten zu verwendenden grafischen Effekte sind die Kurzschreibungs-Filterfunktionen. Sie werden mit der CSS-Eigenschaft filter angewendet. Der Browser manipuliert das Erscheinungsbild des entsprechenden Elements, bevor es auf die Seite gemalt wird. Filterfunktionen werden seit Version 35 von Firefox (stabile Version im Januar 2015) ohne Präfix unterstützt. Sie werden seit einigen Jahren mit dem Präfix -webkit- in Safari, Chrome und Blink-basiertem Opera unterstützt.

(Wir werden am Ende des Beitrags auf Alternativen eingehen, die in Internet Explorer funktionieren; für den Moment sollten Sie einen dieser Browser verwenden, um sich die Demos anzusehen!)

Eine vollständige Beschreibung der verfügbaren filter-Funktionen (mit Beispielen!) finden Sie auf der Almanachseite zur filter-Eigenschaft.

Einige Anmerkungen

  • Sie können eine Reihe von Filtern als durch Leerzeichen getrennte Liste anwenden.
  • Filter erzeugen einen Stapelkontext.
  • Ein Filter wird auf ein gesamtes Element (einschließlich Text, Hintergrundbilder und aller Kindelemente) angewendet, nachdem diese zusammengelegt wurden.
  • Es gibt immer noch eine Reihe von fehlerhaften Randfällen in Browsern, wie z. B. was passiert, wenn es fest positionierte Kindelemente oder versteckte Überläufe gibt.

Ein einfacher und nützlicher Trick ist, ein Farbbild zu nehmen und es mit grayscale(100%) oder saturate(0%) in Schwarz-Weiß zu konvertieren. Die folgende Demo verwendet dies, um einfarbige Profilbilder für ein Paar schlaue Köpfe zu erstellen. Der Filter wird beim Hover/Fokus entfernt, um das Vollfarbbild freizulegen. Um den Verlust von Farbkontrast und Lebendigkeit auszugleichen, werden die Gesamthelligkeit und der Kontrast des Bildes mit den (praktischerweise benannten) Filterfunktionen brightness() und contrast() erhöht. Es wird sogar eine kurze Übergangszeit hinzugefügt, um die Farben ein- und auszublenden.

Siehe den Pen CSS Filters Gray -> Color von Amelia Bellamy-Royds (@AmeliaBR) auf CodePen.

Das ist alles schön und gut. Ein Farbbild nehmen, dem Browser sagen, es grau zu machen. Selektiv umkehren für benutzerfreundliche Interaktion.

Aber was wäre, wenn man das Gegenteil tun könnte? Was, wenn man mit einem Graustufenbild beginnen und dem Browser sagen könnte, es einzufärben?

Ein Graustufen-JPEG-Bild ist normalerweise zwischen 5 % und 25 % kleiner als das entsprechende Farbfoto (abhängig vom Foto und dem Komprimierungsverhältnis – JPEG komprimiert Farbkanäle viel stärker als Helligkeitsdetails). Bei einem verlustfreien Bildformat wie PNG kann die Größenänderung viel größer sein. Wenn die Performance wichtig ist, könnten diese zusätzlichen Kilobytes zählen. Stellen Sie sich eine Seite voller Profilbilder vor. Der Benutzer wird nur einige davon in Farbe sehen, warum also seinen Datenplan belasten, indem man die Farbfotos sendet? Filter verbrauchen auch CPU zur Verarbeitung und Speicher zur Speicherung des Ergebnisses. Wäre es nicht besser, nur das gehoverte Foto zu filtern und den Rest normal zu lassen?

Schließlich muss man die Auswirkungen auf Browser berücksichtigen, die keine CSS-Filter unterstützen. Möchten Sie als Fallback entweder volle Farbe oder volle Graustufen?

Es gibt nur eine Kurzschreibungs-Filterfunktion, die einem Graustufenbild Farbe hinzufügen kann: sepia(), die Weiß und Grau einen gelblichen Stich verleiht. Auf einem Farbbild wendet sie zuerst eine implizite Konvertierung in ein auf Luminanz basierendes Graustufenbild an. Sie erhöht auch die Gesamthelligkeit und den Kontrast, wenn auch nicht so stark wie im vorherigen Beispiel mit separaten Filtern. Die folgende Demo sieht vor der Interaktion fast identisch aus, aber dieses Mal sind die Bilder tatsächlich als Graustufen-JPEGs gespeichert. Ein Sepia-Effekt wird hinzugefügt, um beim Hover eine kleine (falsche) Farbe zu erhalten.

Siehe den Pen CSS Filters Gray -> Sepia von Amelia Bellamy-Royds (@AmeliaBR) auf CodePen.

Das ist ein schöner Effekt, aber ich kann nicht sagen, dass ich von den besonderen Gelb-Beige-Tönen des Sepia-Tons begeistert bin. Kann ich nicht meine eigenen Farben wählen?

Das kann ich. Nur nicht mit einer Kurzschreibungs-Filterfunktion. Diese Kurzschreibungsfunktionen können alle auch lang geschrieben werden, als SVG-Filterelemente. Wenn Sie mehr benutzerdefinierte Kontrolle wünschen, müssen Sie nur den entsprechenden SVG-Filter erstellen und ihn selbst anpassen.

SVG-Filter werden mit einem <filter>-Element innerhalb eines <svg> definiert. Weitere Informationen zur allgemeinen Syntax finden Sie in Joni Trythalls großartigem Beitrag zu SVG-Lichtquellenfiltern vom September 2014. Um den Filter zu verwenden, geben Sie ihm eine id und verwenden dann eine URL-Referenz in der filter-Eigenschaft eines anderen Elements.

.filter-this {
  filter: url(#myFilter);
}
.more-filters-please {
  filter: url("/assets/filters.svg#filter-1");
}

Innerhalb von SVG werden SVG-Filter in den meisten modernen Browsern unterstützt, einschließlich Internet Explorer 10 und 11. Beachten Sie „innerhalb von SVG“. SVG-Filter werden in HTML-Inhalten in IE oder Edge (noch) nicht unterstützt. Benutzer von IE9 und Opera Mini sehen den Effekt nicht. (Es gibt auch einige Fehler und Einschränkungen in anderen Browsern, aber keiner davon beeinträchtigt die hier besprochenen Farbfilter.) Für die Anwendung von SVG-Filtern auf Nicht-SVG-Inhalte unterstützen Firefox und die neuesten Versionen von Chrome und Opera dies in der ungeprägten filter-Eigenschaft. Safari und ältere Blink-Versionen unterstützen dies in der geprägten -webkit-filter-Eigenschaft.

Die Kurzschreibungs-Filterfunktion sepia() wird in SVG-Filtern mit dem Operator <feColorMatrix> definiert. Um Ihren eigenen Kolorierungseffekt zu erstellen, können Sie daher einen Farbmatrixfilter erstellen und die Parameter anpassen.
So sieht der SVG-Filter für 100 % Sepia aus

<filter id="sepia">
 <feColorMatrix type="matrix"
     values="0.393 0.769 0.189 0 0
         0.349 0.686 0.168 0 0
         0.272 0.534 0.131 0 0
         0   0   0   1 0"/>
</filter>

Ganz einfach, oder? Nur ein paar Zahlen hier und da anpassen und ... feststellen, dass Sie absolut keine Ahnung haben, was Sie tun.

Zeit für einen Ausflug in die wunderbare Welt der Matrizenalgebra.

Der mathematische Teil

Das erste, was Sie über <feColorMatrix> verstehen müssen, ist, dass die Liste der Zahlen in values eine Matrix bildet. Speziell eine Fünf-Spalten-, Vier-Zeilen-Matrix, wie oben durch die Macht des beibehaltenen Leerzeichens angedeutet. Die Ausgabefarbe für jeden Pixel wird durch Multiplikation dieser Matrix mit den Eingabepixeln erzeugt.

(Seitenbemerkung: Dies gilt nur, wenn type="matrix" ist. Die anderen type-Optionen sind saturate und hue-rotate, die einen einzelnen Zahlenwert benötigen und Kurzschreibungs-CSS-Entsprechungen haben, und luminanceToAlpha, das keine Werte benötigt.)

Wenn Ihre Algebra-Kenntnisse aus der High School in den Nebel der Erinnerung geraten sind, hier eine Auffrischung über Matrizen und Matrixmultiplikation.

  • Eine Matrix ist im Grunde eine Kurzschreibweise zur Beschreibung einer Reihe von algebraischen Gleichungen. Anstatt Buchstaben wie x und y zu verwenden, um die variablen Mengen zu beschreiben, verwendet man die Position innerhalb der Zeilen und Spalten der Matrix.
  • Ein Wert mit mehreren Teilen – wie ein x,y-Punkt oder eine RGBa-Farbe – kann durch eine Matrix mit einer einzelnen Zeile oder Spalte dargestellt werden, auch Vektor genannt.
  • Wenn Sie eine Matrix und einen Vektor multiplizieren, ersetzen Sie die Werte aus dem Vektor durch die Variablen in der Matrix und addieren dann das Ergebnis für jede Gleichung.

Der Farbmatrixfilter wird erstellt, indem die RGBa-Eingabefarbe als Vektor dargestellt wird, bei dem die Werte in jedem Kanal auf den Bereich 0–1 skaliert wurden. Dieser Vektor wird mit der Matrix multipliziert, und das Ergebnis wird dann zurückkonvertiert, um die RGBa-Ausgabefarbe zu erzeugen. Jedem Vektor und jeder Matrix wird eine zusätzliche Zeile/Spalte hinzugefügt, damit Sie die Farben um einen festen Betrag verschieben können, falls erforderlich.

Die Matrixgleichung, unter Verwendung von Variablen der Form Nra oder Ca zur Darstellung der Matrixparameter, sieht ungefähr so aus

|R2|   | Nrr Ngr Nbr Nar Cr|   |R1|
|G2|   | Nrg Ngg Nbg Nag Cg|   |G1|
|B2| = | Nrb Ngb Nbb Nab Cb| * |B1|
|A2|   | Nra Nga Nba Naa Ca|   |A1|
| 1|   | 0   0   0   0   1 |   | 1|

Was auch als eine Reihe von fünf Gleichungen geschrieben werden kann

R2 = Nrr*R1 + Ngr*G1 + Nbr*B1 + Nar*A1 + Cr*1
G2 = Nrg*R1 + Ngg*G1 + Nbg*B1 + Nag*A1 + Cg*1
B2 = Nrb*R1 + Ngb*G1 + Nbb*B1 + Nab*A1 + Cb*1
A2 = Nra*R1 + Nga*G1 + Nba*B1 + Naa*A1 + Ca*1
1 =  0*R1 +  0*G1 +  0*B1 +  0*A1 + 1*1

Die letzte Zeile ergibt 1=1, sodass Sie sie getrost ignorieren können. Sie wird auch in den <feColorMatrix>-Attributen ignoriert, die nur die ersten vier Zeilen der Matrix (insgesamt 20 Zahlen) angeben.

Für die anderen Zeilen erstellen Sie jeden der rgba-Ausgabewerte als Summe der rgba-Eingabewerte, multipliziert mit dem entsprechenden Matrixwert, plus eine Konstante. Wenn Sie meine Namenskonvention noch nicht erraten haben, Nra ist der Anteil des ursprünglichen Rotkanals, der in den Alpha-Ausgabekanal gelangt, während Ca die Konstante ist, die zum Alpha-Kanal hinzugefügt wird.

Ein einfaches Beispiel für eine Farbmatrix ist die, die für den Farbmatrixtyp luminanceToAlpha verwendet wird. Sie entspricht der folgenden Matrix

| 0      0       0      0 0 |
| 0      0       0      0 0 |
| 0      0       0      0 0 |
| 0.2126 0.7152  0.0722 0 0 |
| 0      0       0      0 1 |

Da die Werte in den ersten drei Zeilen alle Null sind, ist die Ausgabe für die Rot-, Grün- und Blaukanäle ebenfalls Null (d. h. Schwarz). Die Ausgabe für den Alpha-Kanal wird als Funktion der drei Eingabefarbkanäle erzeugt: hauptsächlich vom Grün, mit etwas Rot und einem Hauch von Blau. Wenn die Eingabefarbe reines Weiß ist (d. h. die RGB-Werte sind nach der Skalierung alle 1), dann ist der Alpha-Wert

A2 = 0.2126*1 + 0.7152*1 + 0.0722*1 = 1

Mit anderen Worten, vollständig opak. Wenn die Eingabefarbe reines Schwarz ist (d. h. die RGB-Werte sind alle 0), dann ist der Alpha-Wert

A2 = 0.2126*0 + 0.7152*0 + 0.0722*0 = 0

Mit anderen Worten, vollständig transparent. Jede andere Farbe ergibt einen Alpha-Wert dazwischen. Helleren Farben werden undurchsichtiger und dunkleren Farben transparenter. Die spezifischen Zahlen, die für die RGB-Kanäle zugewiesen sind, sollen die wahrgenommenen Helligkeitsunterschiede zwischen intensivem Rot, intensivem Grün und intensivem Blau widerspiegeln. (Die Zahlen sind in verschiedenen Standards festgelegt, aber die Wissenschaft ist nicht wirklich perfekt. Die wahrgenommene Helligkeit wird von der Art des Monitors (oder Druckers) beeinflusst, den Sie verwenden, sowie von der Empfindlichkeit Ihrer Augen für jede Farbe.)

Kommen wir also zurück zur Sepia-Matrix

0.393 0.769 0.189 0 0
0.349 0.686 0.168 0 0
0.272 0.534 0.131 0 0
0     0     0     1 0

Die fünfte Zeile wird, wie bereits erwähnt, ausgeschlossen, da sie sich nie ändert. Die Matrix tut zwei Dinge: Sie glättet ein Farbbild zu Graustufen (mit Luminanzanpassungen) und färbt diese Farbe dann gelblich. Der Alpha-Kanal ändert sich nicht: Der Ausgabewert A2 ist exakt das 1-fache des Eingangs-A1-Kanals.

Wenn die Eingabefarbe Schwarz ist, ist die Ausgabe immer noch Schwarz, da alle Zahlen mit 0 multipliziert werden. Wenn die Eingabefarbe jedoch Weiß ist, ist die Ausgabe wie folgt

R2 = 0.393*1 + 0.769*1 + 0.189*1 = 1.351
G2 = 0.349*1 + 0.686*1 + 0.168*1 = 1.203
B2 = 0.272*1 + 0.534*1 + 0.131*1 = 0.937

Die Farbe ist daher rgb(135,1 %, 120,3 %, 93,7 %). Nun sagen Sie vielleicht: „Hey, man kann keine RGB-Werte größer als 100 % haben.“ Das kann man nicht, aber man kann es. Sie werden auf 100 % begrenzt, und das Ergebnis ist, dass die weißen Teile des Eingabebildes zu einem blassen Gelb werden. Wenn Sie die Eingabewerte leicht reduzieren, sehen Sie erst dann eine Reduzierung der Rot- und Grünkanäle, wenn die Ausgabe unter 100 % fällt, sodass Sie immer noch Gelb erhalten, aber nicht so blass (da der Blaukanal sichtbar abfällt).

Für eine mittlere Eingabe mit einem skalierten Wert von 0,5 in jedem Kanal wird die Ausgabefarbe wie folgt berechnet

R2 = 0.393*0.5 + 0.769*0.5 + 0.189*0.5 = 0.6755
G2 = 0.349*0.5 + 0.686*0.5 + 0.168*0.5 = 0.6015
B2 = 0.272*0.5 + 0.534*0.5 + 0.131*0.5 = 0.4685

Mit anderen Worten, es wäre ein orange-gelb-grau, ungefähr rgb(68 %, 60 %, 47 %).

Wunderschön.

Wenn Sie gut mit Algebra und/oder Arithmetik sind, werden Sie vielleicht bemerken, dass die Ausgabewerte für die mittelgraue Eingabe genau halb so groß sind wie für die weiße Eingabe. Wenn das Eingabebild in Graustufen vorliegt, haben Sie immer den gleichen Wert für jeden der Rot-, Grün- und Blau-Eingabekanäle. Daher könnten Sie die Mathematik stark vereinfachen, indem Sie sie so schreiben

R2 = 1.351 * gray
G2 = 1.203 * gray
B2 = 0.937 * gray

Um eine Farbmatrix mit demselben Ergebnis zu erstellen, könnten Sie *einen* der RGB-Spalten in jeder Zeile mit diesen Werten belegen und die anderen auf Null lassen

| 1.351 0  0  0 0 |
| 1.203 0  0  0 0 |
| 0.937 0  0  0 0 |
| 0     0  0  0 0 |

oder

| 1.351  0  0     0 0 |
| 0   1.203 0     0 0 |
| 0   0     0.937 0 0 |
| 0   0     0     0 0 |

Solange das Eingabebild Graustufen ist, haben diese denselben Effekt wie die ursprüngliche Sepia-Filtermatrix. Das ist gut zu wissen, denn in diesem Blogbeitrag geht es darum, Graustufenbilder zu kolorieren. (Sie haben es vielleicht nach all der Mathematik vergessen.)

Bevor wir zu den Demos zurückkehren, sollte ich eine Warnung aussprechen. Sie könnten denken, dass Sie, um einen RGB-Eingangsvektor mit einem Wert von 0,5 in jedem Kanal zu erstellen, rgb(50 %, 50 %, 50 %) oder #888 oder gray verwenden sollten. Das wäre richtig, wenn Sie einen <feColorMatrix>-Filter verwenden würden, aber falsch, wenn Sie die Kurzschreibungsfunktion sepia() verwenden würden. Das liegt daran, dass die Kurzschreibungsfilter alle das sRGB-Farbmodell verwenden, um die Eingabefarben in die Werte zu skalieren, die in den Berechnungen verwendet werden.

SVG-Filter verwenden im Gegensatz dazu standardmäßig eine direkte mathematische Konvertierung zwischen den RGB-Eingabewerten und den Werten, die in der Matrixmathematik verwendet werden. Dies kann mit der Eigenschaft color-interpolation-filters gesteuert werden, die auf den Filter entweder als Attribut oder als (vererbbar) CSS-Eigenschaft gesetzt werden kann. Die beiden Werte für die Eigenschaft sind linearRGB und sRGB.

Die restlichen Beispiele verwenden sRGB. Der sRGB-Modus bewahrt die wahrgenommenen Helligkeitsunterschiede zwischen den Teilen des Eingabebildes am effektivsten. Aber er hat erhebliche Auswirkungen auf das Ergebnis. Wenn Sie also dieselben Zahlen verwenden und sehr unterschiedliche Effekte erzielen, könnte das der Grund sein. Das bedeutet auch, dass die Farben nicht einfach von Hand berechnet werden können. Es ist besonders schwierig, Filter zu definieren, die für mittlere Grautöne genau eine bestimmte Ausgabefarbe erzeugen. Wenn Sie im Browser mit Filtern experimentieren, ist das normalerweise kein Problem. Wenn Sie jedoch bestimmte Farben in Ihrem restlichen Webdesign abgleichen möchten, sollten Sie vielleicht color-interpolation-filters: linearRGB; ausprobieren.

Monochromatische Kolorierung

Der Sepia-Filter erzeugt einen monochromen Effekt. Die Schwarz-zu-Weiß-Graustufenwerte der Eingabe werden stattdessen zu Schwarz-zu-Hellgelb abgebildet. Es ist kein perfekter monochromer Effekt, aufgrund des Clamping-Effekts auf die übersättigten Rot- und Grünkanäle, aber es ist so etwas Ähnliches.

Um Ihren eigenen monochromen Kolorierungsfilter zu erstellen, ändern Sie die Werte in der vereinfachten Version der Sepia-Matrix. Um einen Schwarz-zu-Pfirsichrosa-Filter zu erstellen (ungefähr rgb(100 %, 80 %, 65 %)), würden Sie Folgendes verwenden

<filter id="monochrome" 
    color-interpolation-filters="sRGB"
    x="0" y="0" height="100%" width="100%">
  <feColorMatrix type="matrix"
   values="1.00 0 0 0 0 
           0.80 0 0 0 0 
           0.65 0 0 0 0 
           0    0 0 1 0" />
</filter>

Hier sehen Sie, wie es aussieht. Ich habe die Teile auskommentiert, die den Hover-Effekt hinzufügen, damit Sie nur das Endergebnis sehen können.

Siehe den Pen SVG Filters Gray -> Monochrome Color von Amelia Bellamy-Royds (@AmeliaBR) auf CodePen.

Wenn Sie mit monochromen gedruckten Bildern vertraut sind, könnte der Effekt tatsächlich das Gegenteil von dem sein, was Sie als monochrome Bild bezeichnen. Beim Drucken kontrolliert man normalerweise die Farbe der dunklen Teile eines Bildes. Ein monochrom gedrucktes Bild hätte daher einen Übergang von dunkel zu weiß. Um diesen Effekt mit einem RGB-Farbmonitor zu erzielen, müssen wir zu unseren grundlegenden Farbmatrixgleichungen zurückkehren.

R2 = Nrr*R1 + Ngr*G1 + Nbr*B1 + Nar*A1 + Cr*1
G2 = Nrg*R1 + Ngg*G1 + Nbg*B1 + Nag*A1 + Cg*1
B2 = Nrb*R1 + Ngb*G1 + Nbb*B1 + Nab*A1 + Cb*1
A2 = Nra*R1 + Nga*G1 + Nba*B1 + Naa*A1 + Ca*1
1 =  0*R1 +  0*G1 +  0*B1 +  0*A1 + 1*1

Wenn die Eingabefarbe Schwarz ist, verschwinden alle Nij-Werte (multipliziert mit den 0-Werten in den RGB-Eingabekanälen). Das Einzige, was Sie kontrollieren können, sind die Konstanten in der letzten Spalte. Wenn Sie also möchten, dass die dunklen Teile des Bildes in tiefem Blau – z. B. rgb(5 %, 15 %, 50 %) – gezeichnet werden, benötigen Sie eine Matrix, die ungefähr so aussieht

? ? ? 0 0.05
? ? ? 0 0.15
? ? ? 0 0.50
0 0 0 1 0

Die Zahlen an den Fragezeichenpositionen haben keine Auswirkung, wenn die Eingabe Schwarz ist. Sie steuern, was mit dem Rest des Bildes passiert. Wenn sie alle Null wären, hätte jeder Eingabepixel die gleiche konstante Ausgabe, und das gesamte Bild würde tiefblau enden. Es gibt viel einfachere Wege, einen soliden tiefblauen Bereich in CSS zu erstellen, daher ist das wahrscheinlich nicht das, was Sie wollen.

Wie zuvor können wir die Dinge vereinfachen, indem wir uns daran erinnern, dass unser Eingabebild Graustufen ist und die RGB-Kanäle immer gleich sein werden. Wir müssen nur eine der Spalten ändern

? 0 0 0 0.05
? 0 0 0 0.15
? 0 0 0 0.50
0 0 0 1 0

Wir möchten, dass die Ausgabefarbe Weiß (alle Einsen) ist, wenn die Eingabe Weiß ist. Sie könnten also denken, Sie könnten jedes Fragezeichen durch eine 1 ersetzen. Sie haben jedoch immer noch diese Konstanten, die zu jedem Pixel hinzugefügt werden. Sobald Sie diese hinzufügen, haben Sie ein überbelichtetes Bild, bei dem die meisten helleren Details verwaschen sind.

Stattdessen müssen Sie berechnen, was zu jedem Kanal *hinzugefügt* werden muss, um für einen weißen Eingabewert einen weißen Endwert zu erzeugen. Anders ausgedrückt, die Fragezeichen werden durch 1 minus die konstante Farbe ersetzt, die für Schwarz verwendet wird.

0.95 0 0 0 0.05
0.85 0 0 0 0.15
0.50 0 0 0 0.50
0    0 0 1 0

Mit dieser Matrix sieht das Ergebnis wie folgt aus

Siehe den Pen SVG Filters Gray -> Invert Monochrome Color von Amelia Bellamy-Royds (@AmeliaBR) auf CodePen.

Duoton-Kolorierung

Bisher haben wir Farbmatrixfilter verwendet, um sowohl eine Schwarz-zu-Farbe-Monochromatisierung als auch eine Farbe-zu-Weiß-Monochromatisierung zu erstellen. Warum nicht Farbe-zu-Farbe? Natürlich! Das ist technisch gesehen keine monochrome Bild mehr: In der Drucktechnik ist der Effekt als Duoton bekannt.

Der Ansatz ist derselbe wie bei der Farbe-zu-Weiß-Matrix. Sie setzen die Konstanten (die letzte Spalte) auf den Wert, den Sie für die dunklen Teile des Eingabebildes wünschen, und setzen dann eine der anderen Spalten auf die Differenz zwischen diesem und der Farbe, die Sie für die weißen Teile wünschen.

Um also einen Tiefblau-zu-Pfirsichrosa-Duoton zu erstellen, könnten Sie die folgende Matrix verwenden

0.95 0 0 0 0.05
0.65 0 0 0 0.15
0.15 0 0 0 0.50
0    0 0 1 0

Die weißen Punkte werden letztendlich rgb(95 %+5 %, 65 %+15 %, 15 %+50 %) sein. Was dasselbe Pfirsichrosa wie zuvor ist. Alle dazwischenliegenden Grautöne werden jedoch irgendwo dazwischen liegen, zwischen dieser Farbe und dem tiefen Blau.

Die Demo macht genau das

Siehe den Pen SVG Filters Gray -> Duotone Color von Amelia Bellamy-Royds (@AmeliaBR) auf CodePen.

Natürlich muss die Farbe, die Sie für die weißen und schwarzen Punkte wählen, keine erreichbare Farbe sein. Sie dürfen Ihre Farben „überbelichten“, genauso wie es der Sepia-Filter tut. Sie können auch bestimmte Farbkanäle bei den schwarzen Punkten „unterbelichten“, indem Sie sie auf negative Werte setzen. Bei signifikanten Farbwechseln kann die Differenz zwischen dem schwarzen und dem weißen Punkt ebenfalls negativ sein. (Sie könnten diesen Ansatz sogar verwenden, um die Farben zu invertieren und einen Foto-Negativ-Effekt zu erzielen, obwohl es dafür eine CSS-Kurzschreibungsfunktion gibt.)

Die nächste Demo verwendet gesättigtere Farben, um die Verwendung negativer Farbverschiebungen und Farbwerte außerhalb des 0–1-Bereichs zu demonstrieren.

Siehe den Pen Psychedelic SVG Filters Gray -> Duotone Color von Amelia Bellamy-Royds (@AmeliaBR) auf CodePen.

Beim Betrachten dieser Demo werden Sie vielleicht überrascht feststellen, dass der box-shadow des Bildes ebenfalls lila wird. Filter werden nach allen anderen CSS-Effekten angewendet, mit Ausnahme von Clipping und Maskierung. Wenn Sie einen Schatten erstellen möchten, der sich mit dem Filter *nicht* ändert, müssen Sie einen Filter verwenden, um ihn zu erstellen. Dies könnten Sie innerhalb des SVG-Filters tun, oder Sie können es mit der Kurzschreibungs-Filterfunktion drop-shadow() tun, die dieselben Parameter wie die Eigenschaft text-shadow() verwendet. Es ist nicht ganz dasselbe wie ein Box-Schatten (Sie können keinen „Spread“-Parameter verwenden und er wird von transparenten Bereichen im Inhalt beeinflusst), aber in diesem Fall ist er verdammt nah dran.

Siehe den Pen v2: Psychedelic SVG Filters Gray -> Duotone Color von Amelia Bellamy-Royds (@AmeliaBR) auf CodePen.

Wenn Sie es bis hierher geschafft haben, sind Sie wahrscheinlich ein wenig gelangweilt von meinem grinsenden Gesicht (bzw. meinen Gesichtern), also versuchen wir ein anderes Beispiel. Ich habe den nächsten Pen von einem Beispiel von Chris Coyier für einen Beitrag über das Überlagern von Text über Bildern geforkt. Um den Text lesbar zu halten, müssen Sie den Kontrast und die Intensität des Bildes stark reduzieren. Die von Chris verwendete Technik (und die sehr nützlich ist) besteht darin, einen halbtransparenten einfarbigen Verlauf über das Farbbild zu legen.

Das Nettoergebnis für viele Bilder ist etwas, das sich nicht wesentlich von meinen kolorierten Graustufenbildern unterscheidet. In der Demo hat die erste Version jeder Folie ein farbiges Hintergrundbild, während die zweite Version eine duoton-kolorierte Graustufe ist.

Siehe den Pen feColorMatrix – Getönte Graustufen-Hero-Bilder von Amelia Bellamy-Royds (@AmeliaBR) auf CodePen.

Das Duoton-Bild ist etwas flacher, mit geringerem Kontrast als das schattierte Farbbild. Das ist nicht unbedingt schlecht, angesichts des Wunsches, den Text lesbar zu halten. Aber es ist vielleicht nicht das, was Sie wollen.

Die Hauptbeschränkung des bisher verwendeten Duoton-Ansatzes besteht darin, dass ich, um blaue Mitteltöne zu erzeugen, dunkle Blautöne für meinen Schwarzpunkt und helle Blautöne für meinen Mittelpunkt verwenden musste.

Um diese Einschränkung zu überwinden, müssen wir die lineare Algebra hinter uns lassen. Wir benötigen eine exponentielle Verbesserung unserer Kolorierungsfähigkeiten.

Gamma-korrigierte Kolorierung

Mit exponentieller Verbesserung meine ich mathematische Exponenten. (Was? Dachten Sie, wir wären mit der Mathematik fertig? Noch nicht!) Insbesondere meine ich einen Gamma-Korrekturfaktor.

Eine Gammakorrektur ist eine gängige Manipulation in der digitalen Bildbearbeitung, um eine nichtlineare Änderung der Helligkeit oder Farbintensität zu erzeugen. Wenn sie gleichmäßig auf alle Farbkanäle angewendet wird, erhöht oder verringert sie die Helligkeit der Mitteltöne, ohne die Lichter und Schatten zu über- oder unterbelichten. Wenn sie auf jede Farbe einzeln angewendet wird, kann sie die allgemeine Farbbalance eines Bildes verschieben, ohne Schwarz und Weiß zu verschieben.

Der <feColorMatrix>-Duotoneffekt kann als Auswahl zweier Farben im sRGB-Farbraum und Ziehen einer geraden Linie zwischen ihnen betrachtet werden. Alle Graustufen werden auf einen Wert entlang dieser Linie abgebildet.

Wenn Sie farbspezifische Gammafaktoren auf diese Linie anwenden, könnten Sie eine gekrümmte Linie durch den dreidimensionalen Farbraum erstellen. Sie kann bei Schwarz beginnen und bei Weiß enden, aber dennoch durch Rot, Blau oder Grün statt durch reine Grautöne führen.

Mit Matrixmultiplikation können Sie keine Gammafaktoren anwenden. Mit dem <feComponentTransfer>-Filter ist das jedoch möglich. Er unterstützt eine Vielzahl von Funktionen für Farbkanäle, aber immer nur einen Kanal gleichzeitig. Jeder Kanal hat ein eigenes Element, das die anzuwendende Umwandlungsfunktion beschreibt: <feFuncR>, <feFuncG>, <feFuncB> und <feFuncA>. Jedes Funktions-Element hat ein Attribut type, das die zu verwendende Mathematik beschreibt, und dann geben verschiedene andere Attribute die Parameter für jeden Typ an.

(Seitenbemerkung: Wir hätten viele der oben genannten Filter tatsächlich mit <feComponentTransfer> implementieren können, obwohl dies den Markup leicht erhöht hätte. Sie können für jede Farbfunktion einen linear-Typ verwenden, der den Eingangswert mit einem slope-Parameter multipliziert und einen festen intercept hinzufügt. Die Intercepts wären die Schwarzpunkt-Werte und die Slopes wären die benötigte Änderung, um zum Weißpunkt zu verschieben. Sie können nicht die Ausgabe einer Farbe von der Eingabe einer anderen Farbe abhängig machen, aber bei Graustufen-Eingaben ist das irrelevant.)

Ein einfacher Gamma-korrigierter Farbfilter sieht so aus

<filter id="gamma-red" 
    color-interpolation-filters="sRGB"
    x="0" y="0" height="100%" width="100%">
  <feComponentTransfer>
    <feFuncR type="gamma" exponent="0.5" />
    <feFuncG type="gamma" exponent="1.1" />
    <feFuncB type="gamma" exponent="1.4" />
  </feComponentTransfer>
</filter>

Die daraus resultierenden Gleichungen lauten:

R2 = R1^0.5
G2 = G1^1.1
B2 = B1^1.4

Wenn ein Eingabefarbkanal 0 ist, ist die Ausgabe ebenfalls 0 (0 hoch irgendeine Potenz ist immer noch 0). Wenn ein Eingangskanal 1 ist, ist die Ausgabe ebenfalls 1 (1 hoch irgendeine Potenz ist immer noch 1). Für alle dazwischenliegenden Werte erhöhen Exponenten kleiner als 1 die Farbe in der Ausgabe, und Exponenten größer als 1 verringern die Farbe in der Ausgabe. Dies ist das Gegenteil davon, wie Gammafaktoren in vielen Softwareprogrammen definiert sind, die einen Exponenten von 1/gamma verwenden.

Wie in der folgenden Demo gezeigt, erzeugt der oben genannte gamma-red-Filter ein Bild, bei dem Schwarz immer noch Schwarz ist, Weiß immer noch Weiß ist, aber alle Grautöne zu Burgunderrot-Tönen abgebildet wurden.

Siehe den Pen SVG Filters Gray -> Non-linear Color von Amelia Bellamy-Royds (@AmeliaBR) auf CodePen.

Die Gamma-Komponenten-Transferfunktion ist jedoch leistungsfähiger. Zusätzlich zur Angabe eines Exponenten können Sie zwei weitere Attribute angeben: amplitude und offset. Die amplitude ist ein Faktor, der mit dem Ergebnis der Potenzierung des Eingabewerts mit dem Exponenten multipliziert wird. Der offset ist ein konstanter Wert, der danach addiert wird. Mit anderen Worten, Ihre endgültigen Gleichungen sehen eher so aus:

R2 = Nr * R1^Er + Cr
G2 = Ng * G1^Eg + Cg
B2 = Nb * B1^Eb + Cb

Die konstanten Offsets legen immer noch die Ausgabefarbe für schwarze Eingabepixel fest. Die Summe dieser Konstanten plus dem Amplitudenfaktor legt die Ausgabefarbe für weiße Eingabepixel fest. Die Exponenten bestimmen, wie Ihre Farblinie zwischen diesen beiden Punkten gekrümmt wird. Sie könnten versuchen, die genauen Werte aus den Gleichungen zu ermitteln, aber es ist wahrscheinlich einfacher, sie in einem Live-Editor zu öffnen und mit den Zahlen zu spielen, bis es für Sie richtig aussieht.

So bin ich zu dieser Version der Wolkenkratzer-Demo gekommen.

Siehe den Stift feComponentTransfer – Getönte Graustufen-Heldenbilder von Amelia Bellamy-Royds (@AmeliaBR) auf CodePen.

Kannst du sagen, welches das echte Farbbild ist? Könntest du es sagen, wenn sie nicht nebeneinander wären?
Natürlich werden nicht alle Bilder so einfach zu reproduzieren sein. Sie können niemals ein volles mehrfarbiges Bild aus einem Graustufenbild erhalten. Die Wolkenkratzer funktionieren, weil das Originalbild nur sehr begrenzte Farbvariationen aufwies. Aber diese begrenzte Farbvariation war der Hauptgrund, warum das Bild so gut als Hintergrund geeignet war. Wenn Sie diesen Effekt erzielen möchten, können Sie ihn definitiv aus einem Graustufenfoto erstellen.

Mitleid mit den armen Internet Explorer-Benutzern

Wie ich bereits oben im Beitrag erwähnt habe, gibt es die Unterstützung für SVG-Filter auf HTML-Inhalten seit einigen Jahren in Firefox und (mit -webkit- Präfixen) Chrome, Opera und Safari. Es wird erwartet, dass sie auch in Microsoft Edge unterstützt wird. Aber in der Zwischenzeit gibt es immer noch einen großen Teil der Webnutzer da draußen, die keine schönen Filter-Goodies für ihr Webseiten-Erlebnis haben.

Die Unterstützung für SVG-Filter innerhalb von SVG-Inhalten ist jedoch viel besser (IE10+). Und SVG-Inhalte können eingebettete JPEG-Bilder enthalten. Sie können daher schnell Ihre Browserunterstützung verbessern, indem Sie die Bilder, die Sie filtern möchten, in Inline-SVG-Tags verschieben. Es erfordert ein wenig zusätzlichen Markup, aber das Endergebnis sieht exakt gleich aus.

Siehe den Stift IE10+ Getönte Graustufen-Heldenbilder von Amelia Bellamy-Royds (@AmeliaBR) auf CodePen.

Dennoch ist dies immer noch keine universelle Unterstützung. Wenn Ihr Chef oder Kunde darauf besteht, dass die Website in jedem Browser, einschließlich Internet Explorer 8, gleich aussehen muss, werden Sie einige manuelle oder serverseitige Bildverarbeitung durchführen, um farbige Versionen aller Effekte zu erstellen, und einige Skripte (client- oder serverseitig), um sie einzublenden.

Für pragmatischere Fallbacks ist Ihr Hauptanliegen, ob die Benutzeroberfläche benutzbar und der Text lesbar ist. Für den Graustufen-zu-Farbe-Hover-Effekt kann dies so einfach sein wie sicherzustellen, dass es andere Benutzeroberflächenhinweise gibt, dass etwas interaktiv ist. Für Text, der über einem Bild liegt, ist es etwas kniffliger. Ohne den Filter sind die im Wolkenkratzer-Demo verwendeten Graustufenbilder zu hell und haben einen zu hohen Kontrast, um den Text lesen zu können. Eine Lösung wäre, die Bilder beim Umwandeln in Grau abzudunkeln, sodass der Filter nur für die Farbgebung zuständig ist. Andere Ansätze erfordern Browser-Support-Tests, gefolgt von Änderungen anderer Stileigenschaften.

Leider ist der clientseitige Test auf filter-Unterstützung etwas knifflig. Sie können nicht einfach testen, ob die Eigenschaft unterstützt wird, da dies Ihnen nicht sagt, ob sie Auswirkungen auf HTML-Elemente hat. Die CSS @supports-Regel schlägt aus demselben Grund fehl. Sie können testen, ob die Kurzschreibfunktionen unterstützt werden (indem Sie den Filterstil eines Dummy-Elements auf den Funktionsstring setzen und sehen, ob er beim Auslesen des berechneten Stils noch vorhanden ist), oder Sie können testen, ob Filter in SVG unterstützt werden (indem Sie bestätigen, dass Sie ein SVG-Element erstellen können und dass es die element.style.filter-Eigenschaft hat), aber Sie können nicht direkt testen, ob filter: url(#blur) eine Auswirkung hat, wenn es auf ein <div> angewendet wird.

Abschließende Gedanken

Filter sind nur einer der neuen grafischen Effekte, die das Mögliche mit Bildern im Web verändern. Mischmodi und Maskierung werden eine ebenso große Auswirkung haben. Sowohl die monochromatischen als auch die duotonalen Farbeffekte können auch mit Mischmodi und Maskierung erzielt werden. Wie bei den meisten Dingen in CSS (und im Web im Allgemeinen) gibt es viele Wege, um einen bestimmten Effekt zu erzielen.

Bei der Entscheidung, welchen Ansatz Sie wählen, berücksichtigen Sie Ihre Unterstützungs- und Fallback-Optionen sowie die Auswirkungen auf die Wartbarkeit Ihres Codes. Gemischte Hintergründe sind praktisch, da sie ohne zusätzlichen Markup erstellt werden können. Aber ein SVG-gefiltertes Bild innerhalb eines Inline-<svg> hat die beste Unterstützung.

Eine Einschränkung bei der Verwendung von SVG-Filtern anstelle der CSS-Kurzschreibfilterfunktionen ist, dass url()-Referenzen nicht animiert oder Übergängen unterzogen werden können. Sie können die Attribute des Filters selbst mit SMIL-Animationselementen oder JavaScript animieren. Dies wirkt sich jedoch auf alle Verwendungen des Filters aus, nicht nur auf ein einzelnes gehovertes Element. Alternativ können Sie einen altmodischen Ansatz wählen und zwei Kopien des Bildes verwenden – eine gefiltert und eine nicht – und die Opazität der oberen Ebene ändern, um die untere langsam freizugeben. Im Gegensatz zur altmodischen Technik müssen Ihre Benutzer immer noch nur eine Datei herunterladen.