To make a JavaScript program demonstrates how to use a worker to implement image processing operations.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>XoaX.net's Workers Demo</title>
<!-- module type is used to allow import and export commands -->
<script src="Imaging.js" type="module"></script>
<style>
#idOutput {
background: repeating-conic-gradient(black 0 25%, white 0 50%);
background-size: 8px 8px;
border: 1px solid black;
padding: 5px;
margin: 5px;
float: left;
}
</style>
</head>
<body>
<div>
<label>Choose an image <input type="url" onfocus="e => { this.value = ''}" id="idImageUrl" list="idImageList">
<datalist id="idImageList">
<option value="AllegoryOfTheCatholicFaith_JohannesVermeer_c1670_1672.jpg"></option>
<option value="TheLastSupper_1495_LeonardoDaVinci.png"></option>
<option value="TheCrucifixion_LeonBonnat.png"></option>
<option value="PrincessOlga.png"></option>
<option value="TheLastSupper_1896_DagnanBouveret.png"></option>
</datalist>
</label>
<label>Choose a filter <select id="idFilter">
<option value="fnOriginal">Original</option>
<option value="fnGrayscale">Grayscale</option>
<option value="fnBrighten">Brighten</option>
<option value="fnDarken">Darken</option>
<option value="fnNegative">Negative</option>
<option value="fnTransparent">Transparent</option>
<option value="fnBlur">Blur</option>
</select></label>
</div>
<div style="visibility: hidden;" id="idOutput"></div>
</body>
</html>
// NOTE: THIS REQUIRES A WEB SERVER TO RUN
const kqWorker = new Worker("Worker.js", { type: "module" });
kqWorker.onmessage = fnReceiveProcessedFromWorker;
const kqImageUrlElement = document.querySelector("#idImageUrl");
const kqFilterElement = document.querySelector("#idFilter");
const kqOutputDiv = document.querySelector("#idOutput");
kqImageUrlElement.oninput = fnDisplayNewImage;
kqFilterElement.oninput = fnSendImageToWorker;
let qImageData = null;
let qContext = null;
function fnDisplayNewImage() {
const kqImage = new Image();
kqImage.src = kqImageUrlElement.value;
// Remove the image file name so that a new image can be selected.
kqImageUrlElement.value = "";
// Make the canvas container visible when an image is selected
kqOutputDiv.style.visibility = "visible";
kqImage.onload = () => {
const kqCanvas = document.createElement("canvas");
kqCanvas.width = kqImage.width;
kqCanvas.height = kqImage.height;
qContext = kqCanvas.getContext("2d");
qContext.drawImage(kqImage, 0, 0);
qImageData = qContext.getImageData(0, 0, kqCanvas.width, kqCanvas.height);
fnSendImageToWorker();
kqOutputDiv.replaceChildren(kqCanvas);
};
}
function fnSendImageToWorker() {
kqWorker.postMessage({ qImageData, filter: kqFilterElement.value });
}
function fnReceiveProcessedFromWorker(e) {
qContext.putImageData(e.data, 0, 0);
}import * as filters from "./Filters.js";
self.onmessage = e => {
const { qImageData, filter } = e.data;
filters[filter](qImageData);
self.postMessage(qImageData, [qImageData.data.buffer]);
};export function fnOriginal() {}
export function fnGrayscale({ data: d }) {
for (let i = 0; i < d.length; i += 4) {
const [r, g, b] = [d[i], d[i + 1], d[i + 2]];
d[i] = d[i + 1] = d[i + 2] = 0.2126 * r + 0.7152 * g + 0.0722 * b;
}
};
export function fnBrighten({ data: d }) {
for (let i = 0; i < d.length; ++i) {
d[i] = ((1.2*d[i] > 255) ? 255 : 1.2*d[i]);
}
};
export function fnDarken({ data: d }) {
for (let i = 0; i < d.length; ++i) {
if (i % 4 != 3) {
d[i] = .8*d[i];
}
}
};
export function fnNegative({ data: d }) {
for (let i = 0; i < d.length; ++i) {
if (i % 4 != 3) {
d[i] = 255 - d[i];
}
}
}
export function fnTransparent({ data: d }) {
for (let i = 0; i < d.length; ++i) {
if (i % 4 == 3) {
d[i] >>= 1;
}
}
}
export function fnBlur(qImageData) {
let ui8PixelsRGBA = qImageData.data;
const kiFilterWidth = 11;
const kiNeighbors = Math.floor(kiFilterWidth/2);
const kiHeight = qImageData.height;
const kiWidth = qImageData.width;
const kui8aSums = new Uint8Array(4*ui8PixelsRGBA.length);
let ui32aArray = new Uint32Array(kui8aSums.buffer);
// Horizontal sums
for (let j = 0; j < kiHeight; ++j) {
for (let i = 0; i < kiWidth; ++i) {
let iLowIndex = ((i - kiNeighbors < 0) ? 0 : i - kiNeighbors);
let iHighIndex = ((i + kiNeighbors + 1 > kiWidth) ? kiWidth : i + kiNeighbors + 1);
let iLength = iHighIndex - iLowIndex;
// Initialize each entry to zero.
for (let iChannel = 0; iChannel < 4; ++iChannel) {
ui32aArray[((i + j*kiWidth)*4 + iChannel)] = 0;
}
for (let k = iLowIndex; k < iHighIndex; ++k) {
for (let iChannel = 0; iChannel < 4; ++iChannel) {
ui32aArray[((i + j*kiWidth)*4 + iChannel)] += ui8PixelsRGBA[(k + j*kiWidth)*4 + iChannel];
}
}
}
}
const kui8aSums2 = new Uint8Array(4*ui8PixelsRGBA.length);
let ui32aArray2 = new Uint32Array(kui8aSums2.buffer);
// Vertical sums
for (let j = 0; j < kiHeight; ++j) {
let iLowIndexJ = ((j - kiNeighbors < 0) ? 0 : j - kiNeighbors);
let iHighIndexJ = ((j + kiNeighbors + 1 > kiHeight) ? kiHeight : j + kiNeighbors + 1);
let iLengthJ = iHighIndexJ - iLowIndexJ;
for (let i = 0; i < kiWidth; ++i) {
let iLowIndexI = ((i - kiNeighbors < 0) ? 0 : i - kiNeighbors);
let iHighIndexI = ((i + kiNeighbors + 1 > kiWidth) ? kiWidth : i + kiNeighbors + 1);
let iLengthI = iHighIndexI - iLowIndexI;
for (let iChannel = 0; iChannel < 4; ++iChannel) {
ui32aArray2[((i + j*kiWidth)*4 + iChannel)] = 0;
}
for (let k = iLowIndexJ; k < iHighIndexJ; ++k) {
for (let iChannel = 0; iChannel < 4; ++iChannel) {
ui32aArray2[((i + j*kiWidth)*4 + iChannel)] += ui32aArray[((i + k*kiWidth)*4 + iChannel)];
}
}
}
}
for (let j = 0; j < kiHeight; ++j) {
let iLowIndexJ = ((j - kiNeighbors < 0) ? 0 : j - kiNeighbors);
let iHighIndexJ = ((j + kiNeighbors + 1 > kiHeight) ? kiHeight : j + kiNeighbors + 1);
let iLengthJ = iHighIndexJ - iLowIndexJ;
for (let i = 0; i < kiWidth; ++i) {
let iLowIndexI = ((i - kiNeighbors < 0) ? 0 : i - kiNeighbors);
let iHighIndexI = ((i + kiNeighbors + 1 > kiWidth) ? kiWidth : i + kiNeighbors + 1);
let iLengthI = iHighIndexI - iLowIndexI;
for (let iChannel = 0; iChannel < 4; ++iChannel) {
ui8PixelsRGBA[(i + j*kiWidth)*4 + iChannel] = ui32aArray2[((i + j*kiWidth)*4 + iChannel)]/(iLengthI*iLengthJ);
}
}
}
}© 20072026 XoaX.net LLC. All rights reserved.