mirror of
https://github.com/andrewstephens75/as-dithered-image.git
synced 2026-04-14 12:29:30 -07:00
Javascript worker - working
This commit is contained in:
@@ -19,6 +19,12 @@ class ASDitheredImage extends HTMLElement {
|
|||||||
this.context_ = undefined
|
this.context_ = undefined
|
||||||
this.image_loading_ = false
|
this.image_loading_ = false
|
||||||
this.ignore_next_resize_ = false
|
this.ignore_next_resize_ = false
|
||||||
|
this.worker_ = new Worker("ditherworker.js")
|
||||||
|
|
||||||
|
this.worker_.onmessage = ((e) => {
|
||||||
|
const imageData = e.data.imageData
|
||||||
|
this.context_.putImageData(imageData, 0, 0)
|
||||||
|
}).bind(this)
|
||||||
|
|
||||||
this.resizing_timeout_ = undefined
|
this.resizing_timeout_ = undefined
|
||||||
}
|
}
|
||||||
@@ -177,7 +183,13 @@ class ASDitheredImage extends HTMLElement {
|
|||||||
|
|
||||||
this.context_.imageSmoothingEnabled = false
|
this.context_.imageSmoothingEnabled = false
|
||||||
this.context_.drawImage(this.original_image_, 0, 0, this.canvas_.width, this.canvas_.height)
|
this.context_.drawImage(this.original_image_, 0, 0, this.canvas_.width, this.canvas_.height)
|
||||||
console.log("Repainted")
|
const originalData = this.context_.getImageData(0, 0, this.canvas_.width, this.canvas_.height)
|
||||||
|
this.context_.fillRect(0, 0, this.canvas_.width, this.canvas_.height)
|
||||||
|
// TODO: look at transferring the data in a different datastructure to prevent copying
|
||||||
|
const msg = {}
|
||||||
|
msg.imageData = originalData
|
||||||
|
this.worker_.postMessage(msg)
|
||||||
|
|
||||||
this.force_refresh_ = false
|
this.force_refresh_ = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
59
ditherworker.js
Normal file
59
ditherworker.js
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
onmessage = function (e) {
|
||||||
|
console.log("Worker: start", e.data.imageData)
|
||||||
|
|
||||||
|
const result = dither(e.data.imageData, 1)
|
||||||
|
const reply = {}
|
||||||
|
reply.imageData = result
|
||||||
|
postMessage(reply)
|
||||||
|
}
|
||||||
|
|
||||||
|
function dither(imageData, scaleFactor) {
|
||||||
|
let output = new ImageData(imageData.width * scaleFactor, imageData.height * scaleFactor)
|
||||||
|
for (let i = 0; i < imageData.data.length; i += 4) {
|
||||||
|
imageData.data[i] = imageData.data[i + 1] = imageData.data[i + 2] = Math.floor(imageData.data[i] * 0.3 + imageData.data[i + 1] * 0.59 + imageData.data[i + 2] * 0.11)
|
||||||
|
}
|
||||||
|
|
||||||
|
// most implementations I see just distribute error into the existing image, wrapping around edge pixels
|
||||||
|
// this implementation uses a sliding window of floats for more accuracy (probably not needed really)
|
||||||
|
|
||||||
|
let slidingErrorWindow = [new Float32Array(imageData.width), new Float32Array(imageData.width), new Float32Array(imageData.width)]
|
||||||
|
const offsets = [[1, 0], [2, 0], [-1, 1], [0, 1], [1, 1], [0, 2]]
|
||||||
|
|
||||||
|
for (let y = 0, limY = imageData.height; y < limY; ++y) {
|
||||||
|
for (let x = 0, limX = imageData.width; x < limX; ++x) {
|
||||||
|
let i = ((y * limX) + x) * 4;
|
||||||
|
let accumulatedError = Math.floor(slidingErrorWindow[0][x])
|
||||||
|
let expectedMono = imageData.data[i] + accumulatedError
|
||||||
|
let monoValue = expectedMono
|
||||||
|
if (monoValue <= 127) {
|
||||||
|
monoValue = 0
|
||||||
|
} else {
|
||||||
|
monoValue = 255
|
||||||
|
}
|
||||||
|
let error = (expectedMono - monoValue) / 8.0
|
||||||
|
for (let q = 0; q < offsets.length; ++q) {
|
||||||
|
let offsetX = offsets[q][0] + x
|
||||||
|
let offsetY = offsets[q][1] + y
|
||||||
|
if ((offsetX >= 0) && (offsetX < slidingErrorWindow[0].length))
|
||||||
|
slidingErrorWindow[offsets[q][1]][offsetX] += error
|
||||||
|
}
|
||||||
|
|
||||||
|
// this is stupid but we have to do the pixel scaling ourselves because safari insists on interpolating putImageData
|
||||||
|
// which gives us blurry pixels (and it doesn't support the createImageBitmap call with an ImageData instance which
|
||||||
|
// would make this easy)
|
||||||
|
|
||||||
|
for (let scaleY = 0; scaleY < scaleFactor; ++scaleY) {
|
||||||
|
let pixelOffset = (((y * scaleFactor + scaleY) * output.width) + (x * scaleFactor)) * 4
|
||||||
|
for (let scaleX = 0; scaleX < scaleFactor; ++scaleX) {
|
||||||
|
output.data[pixelOffset] = output.data[pixelOffset + 1] = output.data[pixelOffset + 2] = monoValue
|
||||||
|
output.data[pixelOffset + 3] = 255
|
||||||
|
pixelOffset += 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// move the sliding window
|
||||||
|
slidingErrorWindow.push(slidingErrorWindow.shift())
|
||||||
|
slidingErrorWindow[2].fill(0, 0, slidingErrorWindow[2].length)
|
||||||
|
}
|
||||||
|
return output
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user