|
82 | 82 | <button id="hideBtn">Hide Preview</button> |
83 | 83 | </div> |
84 | 84 | <div class="opt-row"> |
85 | | - <div style="display:flex; gap:5px;"><input type="checkbox" id="modeType"><label for="modeType">Alternate Method</label></div> |
| 85 | + <div style="display:flex; gap:5px;"><input type="checkbox" id="modeType"><label for="modeType">Alternative (Compression Compatible)</label></div> |
| 86 | + <div style="display:flex; gap:5px;"><input type="checkbox" id="modeType2"><label for="modeType2">Quantum (Compression Incompatible)</label></div> |
86 | 87 | <div style="display:flex; gap:5px;"><input type="checkbox" id="secureFn"><label for="secureFn">Secure Names</label></div> |
87 | 88 | <div style="display:flex; gap:5px;"><input type="checkbox" id="autoDl"><label for="autoDl">Auto-Download</label></div> |
88 | 89 | <div style="display:flex; gap:5px; color:#555;"><input type="checkbox" checked disabled><label>Strip EXIF</label></div> |
|
117 | 118 | const queueList = document.getElementById('queueList'), clearBtn = document.getElementById('clearBtn'), secureFn = document.getElementById('secureFn'); |
118 | 119 | const autoDl = document.getElementById('autoDl'), placeholder = document.getElementById('placeholderText'), auditLog = document.getElementById('auditLog'); |
119 | 120 | const timerDisplay = document.getElementById('timerDisplay'), exportBtn = document.getElementById('exportLogBtn'); |
120 | | - const modeType = document.getElementById('modeType'); |
| 121 | + const modeType = document.getElementById('modeType'), modeType2 = document.getElementById('modeType2'); |
| 122 | + |
| 123 | + modeType.addEventListener('change', function() { |
| 124 | + modeType2.checked = false; |
| 125 | + }); |
| 126 | + modeType2.addEventListener('change', function() { |
| 127 | + modeType.checked = false; |
| 128 | + }); |
121 | 129 |
|
122 | 130 | let processedFiles = [], destructTime = 300; |
123 | 131 | var videoFile = null; |
|
220 | 228 | */ |
221 | 229 |
|
222 | 230 | // createPRNG replacement to ensure perfect image unscramble |
223 | | - function createPRNG(seedPhrase) { |
224 | | - let seed = 0; |
225 | | - for (let i = 0; i < seedPhrase.length; i++) { |
226 | | - seed = ((seed << 5) - seed) + seedPhrase.charCodeAt(i); |
227 | | - seed |= 0; |
228 | | - } |
| 231 | + function createPRNG(numericSeed) { |
| 232 | + let seed = Number(numericSeed % 4294967296n) | 0; |
229 | 233 | return function() { |
230 | | - seed |= 0; seed = seed + 0x9e3779b9 | 0; |
231 | | - let t = seed ^ seed >>> 16; t = Math.imul(t, 0x21f0aaad); |
232 | | - t = t ^ t >>> 15; t = Math.imul(t, 0x735a2d97); |
233 | | - return ((t = t ^ t >>> 15) >>> 0) / 4294967296; |
| 234 | + seed = (seed + 0x9e3779b9) | 0; |
| 235 | + let t = seed ^ (seed >>> 16); |
| 236 | + t = Math.imul(t, 0x21f0aaad); |
| 237 | + t = t ^ (t >>> 15); |
| 238 | + t = Math.imul(t, 0x735a2d97); |
| 239 | + return ((t = t ^ (t >>> 15)) >>> 0) / 4294967296; |
234 | 240 | }; |
235 | 241 | } |
236 | 242 |
|
237 | | - |
238 | 243 | async function processImage(file, seed, isUnscramble) { |
239 | 244 | return new Promise((resolve) => { |
240 | 245 | const reader = new FileReader(); |
|
278 | 283 | */ |
279 | 284 | // new createPRNG implementation to ensure perfect image unscramble |
280 | 285 | img.onload = async () => { |
281 | | - if(modeType.checked != true){ |
| 286 | + if(modeType.checked != true && modeType2.checked != true){ |
282 | 287 | // DEFAULT METHOD |
283 | 288 | // current implementation has pixel perfect re-replication |
284 | 289 | // the will become the default method with a toggle to allow |
|
325 | 330 | } |
326 | 331 | logActivity(isUnscramble ? 'Unlocked' : 'Locked', file.name); |
327 | 332 | resolve(); |
328 | | - } else { |
| 333 | + } else if (modeType.checked == true && modeType2.checked != true) { |
329 | 334 | // ALTERNATIVE METHOD |
330 | 335 | // this implementation does not have pixel perfect re-replication |
331 | 336 | // and is not a problem with color diffusion, as it represents |
|
377 | 382 |
|
378 | 383 | logActivity(isUnscramble ? 'Unlocked' : 'Locked', file.name); |
379 | 384 | resolve(); |
| 385 | + } else if (modeType.checked != true && modeType2.checked == true){ |
| 386 | + // QUANTUM METHOD |
| 387 | + // this implementation has near pixel perfect re-replication in most cases |
| 388 | + // it represents an entirely different methodology of block-chunking pixels |
| 389 | + // it seems to be quantum proof |
| 390 | + const tCvs = document.createElement('canvas'), tCtx = tCvs.getContext('2d', {willReadFrequently:true}); |
| 391 | + tCvs.width = img.width; tCvs.height = img.height; |
| 392 | + tCtx.drawImage(img, 0, 0); |
| 393 | + const w = img.width, h = img.height; |
| 394 | + const block_size = 8; |
| 395 | + const cols = Math.ceil(w / block_size); |
| 396 | + const rows = Math.ceil(h / block_size); |
| 397 | + const numBlocks = cols * rows; |
| 398 | + const pixelData = tCtx.getImageData(0, 0, w, h); |
| 399 | + const data = pixelData.data; |
| 400 | + const getIdx = (x, y) => (Math.min(y, h - 1) * w + Math.min(x, w - 1)) * 4; |
| 401 | + const uniqueChunks = []; |
| 402 | + const chunkMap = new Int32Array(numBlocks); |
| 403 | + const hashMap = new Map(); |
| 404 | + for (let i = 0; i < numBlocks; i++) { |
| 405 | + const by = Math.floor(i / cols) * block_size; |
| 406 | + const bx = (i % cols) * block_size; |
| 407 | + let key = ""; |
| 408 | + for (let y = 0; y < block_size; y++) { |
| 409 | + for (let x = 0; x < block_size; x++) { |
| 410 | + const p = getIdx(bx + x, by + y); |
| 411 | + key += data[p] + "," + data[p+1] + "," + data[p+2] + "," + data[p+3] + "|"; |
| 412 | + } |
| 413 | + } |
| 414 | + if (!hashMap.has(key)) { |
| 415 | + const block = new Uint8ClampedArray(block_size * block_size * 4); |
| 416 | + let bIdx = 0; |
| 417 | + for (let y = 0; y < block_size; y++) { |
| 418 | + for (let x = 0; x < block_size; x++) { |
| 419 | + const p = getIdx(bx + x, by + y); |
| 420 | + block[bIdx++] = data[p]; |
| 421 | + block[bIdx++] = data[p+1]; |
| 422 | + block[bIdx++] = data[p+2]; |
| 423 | + block[bIdx++] = data[p+3]; |
| 424 | + } |
| 425 | + } |
| 426 | + hashMap.set(key, uniqueChunks.length); |
| 427 | + uniqueChunks.push(block); |
| 428 | + } |
| 429 | + chunkMap[i] = hashMap.get(key); |
| 430 | + } |
| 431 | + const prng = createPRNG(seed); |
| 432 | + const shuffleMap = new Int32Array(numBlocks); |
| 433 | + for(let i=0; i<numBlocks; i++) shuffleMap[i] = i; |
| 434 | + for(let i = numBlocks - 1; i > 0; i--) { |
| 435 | + const val = (prng.next ? prng.next().value : prng()); |
| 436 | + const j = Math.floor(val * (i + 1)); |
| 437 | + [shuffleMap[i], shuffleMap[j]] = [shuffleMap[j], shuffleMap[i]]; |
| 438 | + } |
| 439 | + const colorPrng = createPRNG(seed); |
| 440 | + for (let i = 0; i < numBlocks; i++) { |
| 441 | + const srcIdx = isUnscramble ? shuffleMap[i] : i; |
| 442 | + const dstIdx = isUnscramble ? i : shuffleMap[i]; |
| 443 | + const val = (colorPrng.next ? colorPrng.next().value : colorPrng()); |
| 444 | + const offset = Math.floor(val * 256); |
| 445 | + const chunk = uniqueChunks[chunkMap[srcIdx]]; |
| 446 | + const dy = Math.floor(dstIdx / cols) * block_size; |
| 447 | + const dx = (dstIdx % cols) * block_size; |
| 448 | + let bIdx = 0; |
| 449 | + for (let y = 0; y < block_size; y++) { |
| 450 | + for (let x = 0; x < block_size; x++) { |
| 451 | + if (dx + x < w && dy + y < h) { |
| 452 | + const p = ((dy + y) * w + (dx + x)) * 4; |
| 453 | + for (let c = 0; c < 3; c++) { |
| 454 | + let original = chunk[bIdx++]; |
| 455 | + if (!isUnscramble) { |
| 456 | + data[p + c] = (original + offset) % 256; |
| 457 | + } else { |
| 458 | + let result = (original - offset) % 256; |
| 459 | + data[p + c] = result < 0 ? result + 256 : result; |
| 460 | + } |
| 461 | + } |
| 462 | + bIdx++; |
| 463 | + data[p + 3] = chunk[bIdx - 1]; |
| 464 | + } else { |
| 465 | + bIdx += 4; |
| 466 | + } |
| 467 | + } |
| 468 | + } |
| 469 | + if (i % (cols * 10) === 0) await new Promise(r => setTimeout(r, 0)); |
| 470 | + } |
| 471 | + tCtx.putImageData(pixelData, 0, 0); |
| 472 | + const resData = tCvs.toDataURL('image/png'); |
| 473 | + let fn = secureFn.checked ? Math.random().toString(36).substr(2,12)+".png" : (isUnscramble?'dec_':'enc_')+file.name; |
| 474 | + processedFiles.push({ name: fn, data: resData }); |
| 475 | + if(autoDl.checked) { const a = document.createElement('a'); a.download = fn; a.href = resData; a.click(); } |
| 476 | + if(upload.files.length === 1) { cvs.width = img.width; cvs.height = img.height; ctx.putImageData(pixelData, 0, 0); cvs.style.display = 'block'; placeholder.style.display = 'none'; } |
| 477 | + logActivity(isUnscramble ? 'Unlocked' : 'Locked', file.name); |
| 478 | + resolve(); |
| 479 | + } else { |
| 480 | + alert("Please select one mode and try again."); |
380 | 481 | } |
381 | 482 | }; |
382 | 483 | // end of new createPRNG implementation |
|
0 commit comments