@@ -32,14 +32,12 @@ var ctx = canvas.getContext("2d");
3232const downloadButton = document . getElementById ( "download-button" ) ;
3333const resetButton = document . getElementById ( "reset-button" ) ;
3434const customMenu = document . getElementById ( "custom-menu" ) ;
35- const loadingScreen = document . getElementById ( "loading-screen" ) ;
3635const colours_div = document . getElementById ( "colours" ) ;
3736const palette_div = document . getElementById ( "palette" ) ;
3837const menu = document . getElementById ( "theme-select" ) ;
3938
4039// Changing visibility of download/reset buttons, image canvas, and the menu for custom theme
4140canvas . style . visibility = "hidden" ;
42- loadingScreen . style . visibility = "hidden" ;
4341customMenu . 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- */
222208function 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
0 commit comments