diff options
author | elioat <elioat@tilde.institute> | 2024-06-29 19:10:09 -0400 |
---|---|---|
committer | elioat <elioat@tilde.institute> | 2024-06-29 19:10:09 -0400 |
commit | 18062ebd2bdc5d090676e6f606c33299b665f6cb (patch) | |
tree | b6ce83fc119e919cca724d57f039abddaeb2cab4 | |
parent | 3acf61d9f774418cafa99f9800aef9bb9741047c (diff) | |
download | tour-18062ebd2bdc5d090676e6f606c33299b665f6cb.tar.gz |
*
-rw-r--r-- | js/MAP.md | 1 | ||||
-rw-r--r-- | js/dither/dither.js | 91 | ||||
-rw-r--r-- | js/dither/index.html | 37 |
3 files changed, 129 insertions, 0 deletions
diff --git a/js/MAP.md b/js/MAP.md index 6b1488e..2737154 100644 --- a/js/MAP.md +++ b/js/MAP.md @@ -4,6 +4,7 @@ - `bird-words`, an exploration of Markov chain generation, sort of migrated to <https://git.sr.ht/~eli_oat/beak> - `blototboot`, irc bot and broken lisp interpreter - `canvas`, an exploration of the HTML canvas, lets you move a little sprite around a canvas +- `dither`, Floyd-Steinberg dithering - `game-frame`, my attempt at creating a generic starting point for HTML/JS game dev - `games`, some games from other people - `gg`, testing out how to support game controllers from the browser diff --git a/js/dither/dither.js b/js/dither/dither.js new file mode 100644 index 0000000..f7e549f --- /dev/null +++ b/js/dither/dither.js @@ -0,0 +1,91 @@ +const upload = document.getElementById('upload'); +const canvas = document.getElementById('canvas'); +const ctx = canvas.getContext('2d'); + +upload.addEventListener('change', function (e) { + const file = e.target.files[0]; + const reader = new FileReader(); + reader.onload = function (event) { + const img = new Image(); + img.onload = function () { + canvas.width = img.width; + canvas.height = img.height; + ctx.drawImage(img, 0, 0); + applyDithering(); + }; + img.src = event.target.result; + }; + reader.readAsDataURL(file); +}); + +// TIL how to do this! +document.getElementById('download').addEventListener('click', function() { + const dataUrl = canvas.toDataURL('image/png'); // Convert the canvas to a data URL + const a = document.createElement('a'); // Create an anchor element + a.href = dataUrl; // Set the href of the anchor to the data URL + a.download = 'dithered-image.png'; // Set the download attribute to the desired file name + document.body.appendChild(a); // Append the anchor to the body + a.click(); // Trigger a click on the anchor to start the download + document.body.removeChild(a); // Remove the anchor from the body +}); + +function applyDithering() { + const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); + const data = imageData.data; + const width = canvas.width; + const height = canvas.height; + + // Find the gray value of a pixel + const getGrayValue = (r, g, b) => 0.299 * r + 0.587 * g + 0.114 * b; + + // Set the pixel value + const setPixel = (x, y, value) => { + const index = (y * width + x) * 4; + const gray = value < 128 ? 0 : 255; + data[index] = gray; + data[index + 1] = gray; + data[index + 2] = gray; + data[index + 3] = 255; + }; + + // https://en.wikipedia.org/wiki/Floyd–Steinberg_dithering + + // Loop through each pixel in the image + for (let y = 0; y < height; y++) { + for (let x = 0; x < width; x++) { + const index = (y * width + x) * 4; + const oldR = data[index]; + const oldG = data[index + 1]; + const oldB = data[index + 2]; + const oldGray = getGrayValue(oldR, oldG, oldB); + const newGray = oldGray < 128 ? 0 : 255; + const error = oldGray - newGray; + + setPixel(x, y, oldGray); + + // Spread error to neighboring pixels + if (x + 1 < width) { + data[(y * width + x + 1) * 4] += error * 7 / 16; + data[(y * width + x + 1) * 4 + 1] += error * 7 / 16; + data[(y * width + x + 1) * 4 + 2] += error * 7 / 16; + } + if (y + 1 < height) { + if (x - 1 >= 0) { + data[((y + 1) * width + x - 1) * 4] += error * 3 / 16; + data[((y + 1) * width + x - 1) * 4 + 1] += error * 3 / 16; + data[((y + 1) * width + x - 1) * 4 + 2] += error * 3 / 16; + } + data[((y + 1) * width + x) * 4] += error * 5 / 16; + data[((y + 1) * width + x) * 4 + 1] += error * 5 / 16; + data[((y + 1) * width + x) * 4 + 2] += error * 5 / 16; + if (x + 1 < width) { + data[((y + 1) * width + x + 1) * 4] += error * 1 / 16; + data[((y + 1) * width + x + 1) * 4 + 1] += error * 1 / 16; + data[((y + 1) * width + x + 1) * 4 + 2] += error * 1 / 16; + } + } + } + } + + ctx.putImageData(imageData, 0, 0); +} \ No newline at end of file diff --git a/js/dither/index.html b/js/dither/index.html new file mode 100644 index 0000000..fc06402 --- /dev/null +++ b/js/dither/index.html @@ -0,0 +1,37 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>Floyd-Steinberg Dithering</title> + <style> + body { + margin: 0 auto; + display: block; + padding: 2em; + } + canvas, input, button { + max-width: 100%; + padding: 1em; + margin: 0 auto; + display: block; + border: 1px solid black; + } + button, input[type="file"] { + cursor: pointer; + padding: 1em 2em; + font-size: 1.25em; + } + </style> +</head> +<body> + <canvas id="canvas"></canvas> + <br> + <br> + <input type="file" id="upload" accept="image/*"> + <br> + <br> + <button id="download">Download Image</button> + <script src="dither.js"></script> +</body> +</html> |