Manipulation de pixels avec canvas
==================================

* « Précédent
* Suivant »

Jusqu'à présent, nous n'avons pas examiné dans le détail les pixels réels de notre canevas. Avec l'objet ImageData, vous pouvez directement lire et écrire dans le tableau de données de l'image, pour manipuler les pixels un par un. Nous verrons également comment le lissage (anticrénelage) de l'image peut être contrôlé et comment sauvegarder des images depuis votre canevas.

L'objet `ImageData`
-------------------

L'objet `ImageData` représente les données de pixels sous-jacentes à une zone d'un objet canevas. Il contient les attributs (en lecture seule) suivants :

`width`

La largeur de l'image en pixels.

`height`

La hauteur de l'image en pixels.

`data`

Un `Uint8ClampedArray` représentant un tableau monodimensionnel contenant les données dans l'ordre RVBA, ayant des valeurs entières entre 0 et 255 (inclus).

La propriété `data` retourne un tableau `Uint8ClampedArray` auquel on peut accéder pour voir plus en détail les données brutes des pixels ; chaque pixel est représenté par quatre valeurs sur un octet (rouge, vert, bleu et alpha, dans cet ordre ; c'est-à-dire, le format "RVBA"). Chaque composante de couleur est représentée par un entier entre 0 et 255. Chaque composante reçoit un indice à l'intérieur du tableau, la composante rouge du pixel supérieur gauche étant à l'indice 0 à l'intérieur du tableau. Les pixels continuent ensuite de gauche à droite, puis vers le bas, jusqu'au bout du tableau.

Le `Uint8ClampedArray` contient `height`*(hauteur)* × `width`*(largeur)* × 4 octets, dont les valeurs d'indices vont de 0 à (`height` × `width` × 4)-1.

Par exemple, pour lire la valeur de la composante bleue d'un pixel situé en colonne 200, ligne 50 de l'image, vous pouvez faire ce qui suit :


```js
composanteBleue = imageData.data[50 \* (imageData.width \* 4) + 200 \* 4 + 2];
```


Vous pouvez accéder à la taille en octets du tableau de pixels en lisant l'attribut `Uint8ClampedArray.length` :


```js
var nbOctets = imageData.data.length;
```

Création d'un objet `ImageData`
-------------------------------

Pour créer un nouvel objet `ImageData` vierge, vous pouvez utiliser la méthode `createImageData()` (en-US). Il existe deux versions de la méthode `createImageData()` :


```js
var monImageData = ctx.createImageData(largeur, hauteur);
```


Cela crée un nouvel objet `ImageData` avec les dimensions spécifiées. Tous les pixels sont prédéfinis comme étant noirs transparents.

Vous pouvez aussi créer un nouvel objet `ImageData` ayant les mêmes dimensions que celles de l'objet indiqué par `autreImageData`. Les pixels du nouvel objet sont tous prédéfinis comme étant noirs transparents. **Cela ne copie pas les données d'image !**


```js
var monImageData = ctx.createImageData(autreImageData);
```

Obtention des données pixel pour un contexte
--------------------------------------------

Pour obtenir un objet `ImageData` contenant une copie des données pixel pour un contexte de canevas, vous pouvez utiliser la méthode `getImageData()` :


```js
var monImageData = ctx.getImageData(gauche, haut, largeur, hauteur);
```


Cette méthode retourne un objet `ImageData` représentant les données pixel pour la zone du canevas dont les coins sont représentés par les points (`left`, `top`) *`(gauche,haut)`*, (`left+width`, `top`) *(gauche+largeur, haut)*, (`left`, `top+height`) *(gauche, haut+hauteur)* et (`left+width`, `top+height`) *(gauche+largeur, haut+hauteur)*. Les coordonnées sont spécifiées en unités d'espace de coordonnées du canevas.

**Note :** Tous les pixels en dehors du canevas seront retournés comme noirs transparents dans l'objet `ImageData` résultant.

Cette méthode est aussi présentée dans l'article Manipulation vidéo utilisant canvas.

### Une pipette à couleur

Dans cet exemple, nous utilisons la méthode `getImageData()` pour afficher la couleur en dessous du curseur de la souris. Pour cela, nous avons besoin de la position en cours de la souris donnée par `layerX` et `layerY`, nous recherchons ensuite les données pixel à cette position dans le tableau de pixels que `getImageData()` nous fournit. Finalement, nous utilisons les données du tableau pour définir une couleur d'arrière-plan et un texte dans le `<div>` pour afficher la couleur.
```

<canvas id="canvas" width="300" height="227" style="float:left"></canvas>
<div id="color" style="width:200px;height:50px;float:left"></div>
```



```js
var img = new Image();
img.src = "./assets/rhino.jpg";
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
img.onload = function () {
  ctx.drawImage(img, 0, 0);
  img.style.display = "none";
};
var color = document.getElementById("color");
function pick(event) {
  var x = event.layerX;
  var y = event.layerY;
  var pixel = ctx.getImageData(x, y, 1, 1);
  var data = pixel.data;
  var rgba =
    "rgba(" +
    data[0] +
    ", " +
    data[1] +
    ", " +
    data[2] +
    ", " +
    data[3] / 255 +
    ")";
  color.style.background = rgba;
  color.textContent = rgba;
}
canvas.addEventListener("mousemove", pick);
```

Peinture des données pixel dans un contexte
-------------------------------------------

Vous pouvez utiliser la méthode `putImageData()` (en-US) pour peindre les données pixel dans un contexte :


```js
ctx.putImageData(monImageData, dx, dy);
```


Les paramètres `dx` et `dy` indiquent les coordonnées système dans le contexte du coin supérieur gauche des données pixel qui doivent être peintes.

Par exemple, pour peindre l'image entière représentée par `monImageData` dans le coin supérieur gauche du contexte, vous pouvez simplement faire ce qui suit :


```js
ctx.putImageData(monImageData, 0, 0);
```

### Niveaux de gris et inversion de couleurs

Dans cet exemple, nous itérons sur tous les pixels pour changer leurs valeurs, puis nous remettons le tableau de pixels modifié sur le canevas à l'aide de putImageData() (en-US). La fonction inversion soustrait simplement chaque couleur de la valeur maximale 255. La fonction grayscale *(niveaux de gris)* fait simplement la moyenne du rouge, du vert et du bleu. Vous pouvez également utiliser une moyenne pondérée, donnée par la formule x = 0.299r + 0.587v + 0.114b, par exemple. Voir Niveaux de gris sur Wikipédia pour plus d'informations.
```

<canvas id="canevas" width="300" height="227"></canvas>
<div>
  <input id="btnniveaudegris" value="Niveau de gris" type="button" />
  <input id="btninversion" value="Inversion" type="button" />
</div>
```



```js
var img = new Image();
img.src = "./assets/rhino.jpg";
img.onload = function () {
  dessiner(this);
};

function dessiner(img) {
  var canevas = document.getElementById("canevas");
  var ctx = canevas.getContext("2d");
  ctx.drawImage(img, 0, 0);
  img.style.display = "none";
  var imageData = ctx.getImageData(0, 0, canevas.width, canevas.height);
  var data = imageData.data;

  var inversion = function () {
    for (var i = 0; i < data.length; i += 4) {
      data[i] = 255 - data[i]; // rouge
      data[i + 1] = 255 - data[i + 1]; // vert
      data[i + 2] = 255 - data[i + 2]; // bleu
    }
    ctx.putImageData(imageData, 0, 0);
  };

  var niveaudegris = function () {
    for (var i = 0; i < data.length; i += 4) {
      var moy = (data[i] + data[i + 1] + data[i + 2]) / 3;
      data[i] = moy; // rouge
      data[i + 1] = moy; // vert
      data[i + 2] = moy; // bleu
    }
    ctx.putImageData(imageData, 0, 0);
  };

  var btninversion = document.getElementById("btninversion");
  btninversion.addEventListener("click", inversion);
  var btnniveaudegris = document.getElementById("btnniveaudegris");
  btnniveaudegris.addEventListener("click", niveaudegris);
}
```

Zoom et anticrénelage
---------------------

A l'aide de la méthode `drawImage ()`, un deuxième canevas, et la propriété `imageSmoothingEnabled` , nous pouvons zoomer sur notre image et voir les détails.

Nous obtenons la position de la souris et recadrons une image de 5 pixels à gauche et au-dessus à 5 pixels à droite et en-dessous. Ensuite, nous copions celle-ci sur un autre canevas et redimensionnons l'image à la taille que nous voulons. Dans la zone de zoom, nous redimensionnons une zone de 10 × 10 pixels du canevas d'origine à 200 × 200.


```js
zoomctx.drawImage(
  canvas,
  Math.abs(x - 5),
  Math.abs(y - 5),
  10,
  10,
  0,
  0,
  200,
  200,
);
```


Étant donné que l'anticrénelage est activé par défaut, nous pouvons désactiver le lissage pour voir les pixels clairs. Vous pouvez basculer la case à cocher pour voir l'effet de la propriété `imageSmoothingEnabled` (qui a besoin de préfixes pour différents navigateurs).

### Exemple de zoom
```

<canvas id="canvas" width="300" height="227"></canvas>
<canvas id="zoom" width="300" height="227"></canvas>
<div>
  <label for="smoothbtn">
    <input type="checkbox" name="smoothbtn" checked="checked" id="smoothbtn" />
    Enable image smoothing
  </label>
</div>
```


```
var img = new Image();
img.src = "./assets/rhino.jpg";
img.onload = function () {
  draw(this);
};

function draw(img) {
  var canvas = document.getElementById("canvas");
  var ctx = canvas.getContext("2d");
  ctx.drawImage(img, 0, 0);
  img.style.display = "none";
  var zoomctx = document.getElementById("zoom").getContext("2d");

  var smoothbtn = document.getElementById("smoothbtn");
  var toggleSmoothing = function (event) {
    zoomctx.imageSmoothingEnabled = this.checked;
    zoomctx.mozImageSmoothingEnabled = this.checked;
    zoomctx.webkitImageSmoothingEnabled = this.checked;
    zoomctx.msImageSmoothingEnabled = this.checked;
  };
  smoothbtn.addEventListener("change", toggleSmoothing);

  var zoom = function (event) {
    var x = event.layerX;
    var y = event.layerY;
    zoomctx.drawImage(
      canvas,
      Math.abs(x - 5),
      Math.abs(y - 5),
      10,
      10,
      0,
      0,
      200,
      200,
    );
  };

  canvas.addEventListener("mousemove", zoom);
}
```

Sauvegarde des images
---------------------

L' `HTMLCanvasElement` fournit une méthode `toDataURL ()`, utile lors de l'enregistrement d'images. Il retourne un URI de données contenant une représentation de l'image dans le format spécifié par le paramètre de `type` (par défaut en PNG ). L'image renvoyée est dans une résolution de 96 dpi.

`canvas.toDataURL('image/png')` (en-US)

Par défaut. Crée un image PNG.

`canvas.toDataURL('image/jpeg', quality)` (en-US)

Crée une image JPG. En option, vous pouvez fournir une qualité comprise entre 0 et 1, 1 étant de la meilleure qualité et 0 presque non reconnaissable mais de petite taille.

Une fois que vous avez généré un URI de données à partir de votre canevas, vous pouvez l'utiliser comme source de `<image>` ou le mettre dans un lien hypertexte avec un attribut de téléchargement pour l'enregistrer sur le disque par exemple.

Vous pouvez également créer un `Blob` à partir du canevas.

`canvas.toBlob(callback, type, encoderOptions)` (en-US)

Crée un objet `Blob` représentant l'image contenue dans le canevas.

Voir aussi
----------

* `ImageData`
* Manipulating video using canvas
* Canevas, images et pixels – par Christian Heilmann (en)
* « Précédent
* Suivant »