Skip to content

Commit c05228b

Browse files
committed
Load chunks of the converted image at a time as a more interesting loading screen
1 parent 57e227c commit c05228b

File tree

3 files changed

+70
-77
lines changed

3 files changed

+70
-77
lines changed

assets/convert.js

Lines changed: 70 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,12 @@ var ctx = canvas.getContext("2d");
3232
const downloadButton = document.getElementById("download-button");
3333
const resetButton = document.getElementById("reset-button");
3434
const customMenu = document.getElementById("custom-menu");
35-
const loadingScreen = document.getElementById("loading-screen");
3635
const colours_div = document.getElementById("colours");
3736
const palette_div = document.getElementById("palette");
3837
const menu = document.getElementById("theme-select");
3938

4039
// Changing visibility of download/reset buttons, image canvas, and the menu for custom theme
4140
canvas.style.visibility = "hidden";
42-
loadingScreen.style.visibility = "hidden";
4341
customMenu.style.display = "none";
4442

4543
// Global variables
@@ -181,23 +179,17 @@ function initialize() {
181179
if (customMenu.style.display === "block") {
182180
createCustomPalette();
183181
}
184-
loadingScreen.style.opacity = "100";
185-
loadingScreen.style.visibility = "visible";
186-
loadingScreen.style.transition = "0s";
187182
setTimeout(function () {
188183
convertImage();
189-
loadingScreen.style.transition = "0.5s";
190-
loadingScreen.style.opacity = "0";
191-
loadingScreen.style.visibility = "hidden";
192184
}, 0);
193185
}
194186

195-
function nearestColour(targetColour) {
187+
function nearestColour(targetColour, colourScheme) {
196188
let minDistance = Infinity;
197-
let closestColor = theme[0];
189+
let closestColor = colourScheme[0];
198190

199-
for (let i = 0; i < theme.length; i += 3) {
200-
let color = [theme[i], theme[i + 1], theme[i + 2]];
191+
for (let i = 0; i < colourScheme.length; i += 3) {
192+
let color = [colourScheme[i], colourScheme[i + 1], colourScheme[i + 2]];
201193
// Euclidean distance in RGB space
202194
let distance = Math.sqrt(
203195
Math.pow(targetColour[0] - color[0], 2) +
@@ -213,69 +205,83 @@ function nearestColour(targetColour) {
213205
return closestColor;
214206
}
215207

216-
/*
217-
This is the function that processes the image.
218-
It works by scanning every pixel and finding the nearest colour.
219-
After finding the nearest colour, it uses that data to reconstruct the image.
220-
If we are dithering, we calculate the quantization error and distribute it using the Sierra Lite kernel.
221-
*/
222208
function convertImage() {
223209
downloadButton.style.visibility = "hidden";
224210
resetButton.style.visibility = "hidden";
225-
// Assigning variables
211+
226212
var imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
227213
var pixels = imageData.data;
214+
// Avoid changing of theme mid-conversion
215+
let colourScheme = theme;
216+
let ditheringValue = dithering;
217+
let y = 0; // Start processing from the first row
218+
var batchSize = 0;
219+
// Make loading slightly faster on mobile
220+
if (window.innerWidth > 800){
221+
batchSize = Math.floor(canvas.height / 16)
222+
} else {
223+
batchSize = Math.floor(canvas.height / 10)
224+
}
228225

229-
for (let y = 0; y < canvas.height; y++) {
230-
for (let x = 0; x < canvas.width; x++) {
231-
const index = (y * canvas.width + x) * 4;
232-
233-
// Get the original pixel colour
234-
let oldPixel = [
235-
pixels[index],
236-
pixels[index + 1],
237-
pixels[index + 2],
238-
];
239-
240-
// Find the closest color from the palette
241-
let newPixel = nearestColour(oldPixel);
242-
243-
// Replace the pixel with the new colour
244-
pixels[index] = newPixel[0];
245-
pixels[index + 1] = newPixel[1];
246-
pixels[index + 2] = newPixel[2];
247-
248-
if (dithering) {
249-
// Calculate quantization error
250-
let quantError = [
251-
oldPixel[0] - newPixel[0],
252-
oldPixel[1] - newPixel[1],
253-
oldPixel[2] - newPixel[2],
254-
];
255-
256-
// Distribute the error using Sierra Lite kernel
257-
if (x + 1 < canvas.width) {
258-
const rightIndex = index + 4;
259-
pixels[rightIndex] += (quantError[0] * 1) / 2;
260-
pixels[rightIndex + 1] += (quantError[1] * 1) / 2;
261-
pixels[rightIndex + 2] += (quantError[2] * 1) / 2;
262-
}
263-
264-
if (y + 1 < canvas.height) {
265-
const belowIndex = index + canvas.width * 4;
266-
pixels[belowIndex] += (quantError[0] * 1) / 2;
267-
pixels[belowIndex + 1] += (quantError[1] * 1) / 2;
268-
pixels[belowIndex + 2] += (quantError[2] * 1) / 2;
226+
// Load by chunk
227+
function processBatch() {
228+
let maxY = Math.min(y + batchSize, canvas.height); // Limit processing to batch size
229+
230+
for (; y < maxY; y++) {
231+
for (let x = 0; x < canvas.width; x++) {
232+
const index = (y * canvas.width + x) * 4;
233+
234+
// Get the original pixel colour
235+
let oldPixel = [pixels[index], pixels[index + 1], pixels[index + 2]];
236+
237+
// Find the closest color from the palette
238+
let newPixel = nearestColour(oldPixel, colourScheme);
239+
240+
// Replace the pixel with the new colour
241+
pixels[index] = newPixel[0];
242+
pixels[index + 1] = newPixel[1];
243+
pixels[index + 2] = newPixel[2];
244+
245+
if (ditheringValue) {
246+
// Calculate quantization error
247+
let quantError = [
248+
oldPixel[0] - newPixel[0],
249+
oldPixel[1] - newPixel[1],
250+
oldPixel[2] - newPixel[2],
251+
];
252+
253+
// Distribute the error using Sierra Lite kernel
254+
if (x + 1 < canvas.width) {
255+
const rightIndex = index + 4;
256+
pixels[rightIndex] += (quantError[0] * 1) / 2;
257+
pixels[rightIndex + 1] += (quantError[1] * 1) / 2;
258+
pixels[rightIndex + 2] += (quantError[2] * 1) / 2;
259+
}
260+
261+
if (y + 1 < canvas.height) {
262+
const belowIndex = index + canvas.width * 4;
263+
pixels[belowIndex] += (quantError[0] * 1) / 2;
264+
pixels[belowIndex + 1] += (quantError[1] * 1) / 2;
265+
pixels[belowIndex + 2] += (quantError[2] * 1) / 2;
266+
}
269267
}
270268
}
271269
}
270+
271+
// Update the canvas after processing the batch
272+
ctx.putImageData(imageData, 0, 0);
273+
274+
if (y < canvas.height) {
275+
// Continue processing the next batch
276+
setTimeout(processBatch, 0); // wait until ready for next batch
277+
} else {
278+
// Processing complete
279+
downloadButton.style.visibility = "visible";
280+
resetButton.style.visibility = "visible";
281+
}
272282
}
273283

274-
// Reconstruct the image and make the download/reset buttons visible
275-
ctx.putImageData(imageData, 0, 0);
276-
downloadButton.style.visibility = "visible";
277-
resetButton.style.visibility = "visible";
278-
loadingScreen.style.visibility = "hidden";
284+
processBatch(); // Start the processing loop
279285
}
280286

281287
// Download function

assets/style.css

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -245,16 +245,6 @@ input[type="color"] {
245245
border: none;
246246
}
247247

248-
#loading-screen {
249-
width: 100%;
250-
height: 100%;
251-
top: 0;
252-
left: 0;
253-
position: fixed;
254-
text-align: center;
255-
font-size: xx-large;
256-
background-color: rgba(30, 30, 30, 0.8);
257-
}
258248

259249
#loading-screen p {
260250
margin-top: 40vh;

index.html

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,6 @@ <h1>Wallpaper Theme Converter</h1>
1414
</header>
1515

1616
<body>
17-
<div id="loading-screen">
18-
<p>Loading...</p>
19-
</div>
2017
<hr />
2118
<br/>
2219
<div>

0 commit comments

Comments
 (0)