Skip to content

Instantly share code, notes, and snippets.

@ntzyz
Last active March 22, 2017 04:37
Show Gist options
  • Save ntzyz/4afe5b09f2c2b230e3fd1ccf5174cdc9 to your computer and use it in GitHub Desktop.
Save ntzyz/4afe5b09f2c2b230e3fd1ccf5174cdc9 to your computer and use it in GitHub Desktop.
<!DOCTYPE html>
<html>
<head>
<title>Index</title>
<meta charset="UTF-8" />
<style>
.button {
padding-left: 0.5em;
padding-right: 0.5em;
padding-top: 0.2em;
padding-bottom: 0.2em;
border: 1px solid black;
display: inline-block;
border-radius: 5px;
cursor: pointer;
}
.footer {
font-size: 0.8em;
clear: both;
}
</style>
</head>
<body style="margin: 0; padding: 0; width: 800px; margin: 0 auto; padding: 5px;">
<canvas width="800" height="520" style="width: 800px; height: 520px; border: solid 1px black; display: block; margin: 5px 0 5px 0;"></canvas>
<img style="display: none">
<input type="file" style="display: none"/>
<div class="button" onclick="document.querySelector('input').click();">Select Image File</div>
<div class="button" onclick="window.download()">Download ebm file</div>
<div class="footer">Written by @ntzyz with pure JavaScript. No image will be uploaded. Use latest version of Chrome/FireFox/Edge if not working properly.</div>
<a style="display: none"></a>
<script>
(function() {
let canvas = document.querySelector('canvas');
let input = document.querySelector('input');
let img = document.querySelector('img');
let [screenWidth, screenHeight] = [800, 530];
let ctx = canvas.getContext('2d');
let at = (x, y) => (y * screenWidth + x) * 4;
let set = (data, x, y, c) => {
let off = at(x, y);
data[off] = data[off + 1] = data[off + 2] = c > 255 ? 255 : (c < 0 ? 0 : c);
}
input.addEventListener('change', (e) => {
if (input.files && input.files[0]) {
let reader = new FileReader();
reader.onload = (e) => {
img.src = e.target.result;
setTimeout(() => {
let imgRatio = img.naturalWidth / img.naturalHeight;
let screenRatio = screenWidth / screenHeight;
let imageWidth, imageHeight;
if (imgRatio > screenRatio) {
imageHeight = img.naturalHeight;
imageWidth = imageHeight * screenRatio;
ctx.drawImage(img, (img.naturalWidth - imageWidth) / 2, 0, imageWidth, imageHeight, 0, 0, screenWidth, screenHeight);
} else {
imageWidth = img.naturalWidth;
imageHeight = imageWidth / screenRatio;
ctx.drawImage(img, 0, (img.naturalHeight - imageHeight) / 2, imageWidth, imageHeight, 0, 0, screenWidth, screenHeight);
}
let imageData = ctx.getImageData(0, 0, screenWidth, screenHeight);
let raw = imageData.data;
for (let i = 0; i < raw.length; i += 4) {
raw[i] = raw[i+1] = raw[i+2] = (raw[i] + raw[i+1] + raw[i+2]) / 3;
}
for (let x = 0; x < screenWidth - 1; ++x) {
for (let y = 0; y < screenHeight - 1; ++y) {
let oldPixel = raw[at(x, y)];
let newPixel = ((oldPixel & 0xF0) | (oldPixel >> 4)) & 0xff;
let quantError = oldPixel - newPixel;
set(raw, x, y, newPixel);
set(raw, x + 1, y, raw[at(x + 1, y)] + quantError * 7 / 16);
set(raw, x - 1, y + 1, raw[at(x - 1, y + 1)] + quantError * 3 / 16);
set(raw, x, y + 1, raw[at(x, y + 1)] + quantError * 5 / 16);
set(raw, x + 1, y + 1, raw[at(x + 1, y + 1)] + quantError * 1 / 16);
}
}
ctx.putImageData(imageData, 0, 0, 0, 0, screenWidth, screenHeight);
}, 0);
}
reader.readAsDataURL(input.files[0]);
}
});
window.download = () => {
let buffer = new Uint8Array(screenWidth * screenHeight / 2);
let imageData = ctx.getImageData(0, 0, screenWidth, screenHeight).data;
for (let x = 0; x < screenWidth; ++x) {
for (let y = 0; y < screenHeight; ++y) {
buffer[(y * screenWidth + x) / 2] = ~(((imageData[at(x, y)]) & 0xF0 ) | ((imageData[at(x + 1, y)]) >> 4));
}
}
let blob = new Blob([buffer], { type: 'application/octet-stream'});
if (navigator.userAgent.indexOf('Edge') > -1) {
window.navigator.msSaveBlob(blob, 'Image.ebm');
} else if (window.saveAs) {
window.saveAs(blob, 'Image.ebm');
} else {
let url = window.URL.createObjectURL(blob);
let a = document.querySelector('a');
a.href = url;
a.download = 'Image.ebm';
a.click();
}
}
})();
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment