Beispiel:JS-canvas-pixel-manipulation-2.html

Aus SELFHTML-Wiki
Wechseln zu: Navigation, Suche

<!DOCTYPE html> <html lang="de"> <head>

 <meta charset="utf-8">
 <meta name="viewport" content="width=device-width, initial-scale=1.0" >
 <link rel="stylesheet" media="screen" href="./Beispiel:SELFHTML-Beispiel-Grundlayout.css" >
 <title>Bildmanipulation über Canvas</title>
 <style>

figure {

 background: white;
 display: inline-block;
 text-align: center;
 border: 1px solid black;
 padding: 2px 5px;
 border-radius: 0 8px 8px;
 margin: 1em 0

}

figcaption {

 font-family: "Arial", sans;
 font-size: 1.4em;
 font-weight: bold;

}

  1. notice {
 background: #fcc;
 border: 2px solid red;
 border-radius: 0.3em;
 color: red;
 padding: 1em;
 text-align: center;

}

 </style>
 <script>

document.addEventListener("DOMContentLoaded", function () {

 "use strict";
 var img = new Image(),
   imgData,
   main = document.querySelector("body > main:first-of-type"),
   notice = document.querySelector("#notice");
 img.src = "//wiki.selfhtml.org/images/9/98/Nbg-drahtziehmuehle.png";
 // Fehlermeldung
 img.addEventListener("error", function() {
   var p = document.createElement("p");
   p.id = "notice";
   p.innerHTML = "Die erforderliche Bilddatei konnte nicht geladen werden!";
   main.appendChild(p);
 });
 // Funktionalität einrichten
 img.addEventListener("load", function() {
   var canvas = document.createElement("canvas"),
     cap = document.createElement("figcaption"),
     context,
     fig = document.createElement("figure"),
     p = document.createElement("p"),
     sel = document.createElement("select");
   // Optionen ins <select> schreiben
   [
     "Original",
     "Verrauscht",
     "Schwarzweiß",
     "Negativ",
     "Differenziert",
     "Rot",
     "Grün",
     "Blau",
     "Pixelig"
   ].forEach(function (val) {
     var op = document.createElement("option");
     op.innerHTML = val;
     op.value = val;
     sel.appendChild(op);
   });
   // Auswahldialog
   p.innerHTML = "Sie können die Darstellung des Bildes durch folgende Filter verändern: ";
   p.appendChild(sel);
   // Dokumentinhalte aufbauen
   main.appendChild(p);
   main.appendChild(fig);
   fig.appendChild(cap);
   fig.appendChild(canvas);
   // Bildfläche einrichten
   canvas.height = img.height;
   canvas.width = img.width;
   context = canvas.getContext("2d");
   // Originalbild zeichnen
   context.drawImage(img, 0, 0, img.width, img.height);
   // originale Bilddaten speichern
   imgData = context.getImageData(0, 0, img.width, img.height);
   // reduziere auf Ganzzahl zwischen 0 und 255
   function byteRange (a) {
     if (a > 255) {
       a = 255;
     }
     if (a < 0) {
       a = 0;
     }
     return Math.floor(a);
   }
   // Bildmanipulation ausführen
   function applyFilter () {
     var data, mod, x, y, r, g, b, a, l, offset, delta, n;
     // Bildüberschrift anpassen
     cap.innerHTML = sel.options[sel.selectedIndex].value;
     // neue Bilddaten anlegen
     mod = context.createImageData(img.width, img.height);
     // Bilddaten pixelweise abarbeiten
     for (x = 0; x < imgData.width; x++) {
       for (y = 0; y < imgData.height; y++) {
         offset = (imgData.width * y + x) * 4;
         r = imgData.data[offset];   // rot
         g = imgData.data[offset + 1]; // grün
         b = imgData.data[offset + 2]; // blau
         a = imgData.data[offset + 3]; // Transparenz
         l = 0.299*r + 0.587*g + 0.114*b; // (NTSC-Standard für Luminanz)
         // jeweiligen Filter anwenden
         switch (sel.options[sel.selectedIndex].value) {
           default:
             mod.data[offset]     = r;
             mod.data[offset + 1] = g;
             mod.data[offset + 2] = b;
             mod.data[offset + 3] = a;
           break;
           case "Verrauscht":
             mod.data[offset]     = byteRange(r*.8 + 150*Math.random());
             mod.data[offset + 1] = byteRange(g*.8 + 150*Math.random());
             mod.data[offset + 2] = byteRange(b*.8 + 150*Math.random());
             mod.data[offset + 3] = a;
           break;
           case "Schwarzweiß":
             mod.data[offset]     = byteRange(l);
             mod.data[offset + 1] = byteRange(l);
             mod.data[offset + 2] = byteRange(l);
             mod.data[offset + 3] = byteRange(a);
           break;
           case "Negativ":
             mod.data[offset]     = byteRange(255 - r);
             mod.data[offset + 1] = byteRange(255 - g);
             mod.data[offset + 2] = byteRange(255 - b);
             mod.data[offset + 3] = byteRange(a);
           break;
           case "Differenziert":
             // 0 = r, 1 = g, 2=  b
             [0, 1, 2].forEach(function (rgb) {
               // 2*(rgb-rechts - rgb-links + rgb-oben - rgb-unten) +128
               mod.data[offset + rgb] = byteRange(
                 2 * (
                   // rgb-rechts
                   imgData.data[(imgData.width * (y-1) + x) * 4 + rgb]
                   // rgb-links
                   - imgData.data[(imgData.width * (y+1) + x) * 4 + rgb]
                   // rgb-oben
                   + imgData.data[(imgData.width * y + x-1) * 4 + rgb]
                   // rgb-unten
                   - imgData.data[(imgData.width * y + x+1) * 4 + rgb]
                 ) + 128
               );
             });
             // Transparenz
             mod.data[offset + 3] = imgData.data[offset + 3];
           break;
           case "Rot":
             mod.data[offset]     = r;
             mod.data[offset + 1] = 0;
             mod.data[offset + 2] = 0;
             mod.data[offset + 3] = a;
           break;
           case "Grün":
             mod.data[offset]     = 0;
             mod.data[offset + 1] = g;
             mod.data[offset + 2] = 0;
             mod.data[offset + 3] = a;
           break;
           case "Blau":
             mod.data[offset]     = 0;
             mod.data[offset + 1] = 0;
             mod.data[offset + 2] = b;
             mod.data[offset + 3] = a;
           break;
           case "Pixelig":
             // Pixel in Gruppen von n*n behandeln und
             // jedem Pixel den durchschnittlichen RGBa-Wert
             // dieser Gruppe geben:
             n = 5;
             delta = {
               // Zähler für die tatsächliche Anzahl der Pixel im n*n-Quadrat
               c: 0,
               // Abstand zur linken oberen Ecke des n*n-Quadrates
               dx: 0,
               dy: 0,
               // RGBa-Werte
               r: 0,
               g: 0,
               b: 0,
               a: 0,
               // Offset in imgData für originalen RGB-Wert
               o: 0,
               // X-Koordinate der linken oberen Ecke des n*n-Quadrates
               x: Math.floor(x / n) * n,
               // Y-Koordinate der linken oberen Ecke des n*n-Quadrates
               y: Math.floor(y / n) * n
             };
             while (delta.dy < n) {
               while (delta.dx < n) {
                 // RGB-Werte dieses Pixels aufaddieren
                 if (delta.x + delta.dx < imgData.width
                   && delta.y + delta.dy < imgData.height
                 ) {
                   // Offset eines Pixels im n*n-Raster
                   delta.o = (
                     imgData.width * (delta.y + delta.dy)
                     + delta.dx
                     + delta.x
                   ) * 4;
                   delta.c++;
                   delta.r += imgData.data[delta.o];
                   delta.g += imgData.data[delta.o + 1];
                   delta.b += imgData.data[delta.o + 2];
                   delta.a += imgData.data[delta.o + 3];
                 }
                 delta.dx++;
               }
               delta.dx = 0;
               delta.dy++;
             }
             mod.data[offset]     = byteRange(delta.r / delta.c);
             mod.data[offset + 1] = byteRange(delta.g / delta.c);
             mod.data[offset + 2] = byteRange(delta.b / delta.c);
             mod.data[offset + 3] = byteRange(delta.a / delta.c);
           break;
         }
       }
     }
     // veränderte Bilddaten ins Bild schreiben
     context.putImageData(mod, 0, 0);
   }
   // auf Nutzereingaben reagieren
   sel.addEventListener("change", applyFilter);
   applyFilter();
 });
 // Hinweis entfernen
 notice.parentNode.removeChild(notice);

});

 </script>

</head>

<body>

Bildmanipulation über Canvas

 <main>

Dieses Beispiel funktioniert nur mit JavaScript!

 </main>

</body> </html>