| <!-- |
| Copyright 2019 The Chromium Authors. All rights reserved. |
| Use of this source code is governed by a BSD-style license that can be |
| found in the LICENSE file. |
| --> |
| <!doctype html> |
| <head> |
| <title>piex wasm raw image preview / thumbnail test page</title> |
| <link type="text/css" rel="stylesheet" href="/tests.css"> |
| </head> |
| |
| <body> |
| <button onclick=runTest()>Run Test</button> |
| </body> |
| |
| <script> |
| class ImageBuffer { |
| constructor(buffer) { |
| this.source = new Uint8Array(buffer); |
| this.length = buffer.byteLength; |
| } |
| |
| process() { |
| this.memory = Module._malloc(this.length); |
| if (!this.memory) |
| throw new Error('image malloc failure'); |
| |
| Module.HEAP8.set(this.source, this.memory); |
| this.result = Module.image(this.memory, this.length); |
| |
| return this.result; |
| } |
| |
| preview() { |
| let preview = this.result ? this.result.preview : null; |
| if (!preview || this.result.error) |
| return null; |
| |
| if (preview.format != 0) |
| throw new Error('preview images should be JPEG format'); |
| |
| const offset = preview.offset; |
| const length = preview.length; |
| if (offset > this.length || (this.length - offset) < length) |
| throw new Error('failed to extract preview'); |
| |
| const view = new Uint8Array(this.source.buffer, offset, length); |
| preview.data = new Uint8Array(view); |
| preview.type = 'preview'; |
| |
| return preview; |
| } |
| |
| thumbnail() { |
| let thumbnail = this.result ? this.result.thumbnail : null; |
| if (!thumbnail || this.result.error) |
| return null; |
| |
| const offset = thumbnail.offset; |
| const length = thumbnail.length; |
| if (offset > this.length || (this.length - offset) < length) |
| throw new Error('failed to extract thumbnail'); |
| |
| const view = new Uint8Array(this.source.buffer, offset, length); |
| thumbnail.data = new Uint8Array(view); |
| if (thumbnail.format == 1) // RGB |
| thumbnail.size = thumbnail.width * thumbnail.height * 3; |
| thumbnail.type = 'thumbnail'; |
| |
| return thumbnail; |
| } |
| |
| details() { |
| const details = this.result ? this.result.details : null; |
| if (!details || this.result.error) |
| return null; |
| |
| let format = {}; |
| for (const [key, value] of Object.entries(details)) { |
| if (typeof value === 'string') { |
| format[key] = value.replace(/\0+$/, '').trim(); |
| } else if (typeof value === 'number') { |
| if (!Number.isInteger(value)) { |
| format[key] = Number(value.toFixed(3).replace(/0+$/, '')); |
| } else { |
| format[key] = value; |
| } |
| } |
| } |
| |
| return JSON.stringify(format); |
| } |
| |
| close() { |
| Module._free(this.memory); |
| } |
| } |
| |
| function createFileSystem(images) { |
| return new Promise((resolve, reject) => { |
| document.title = 'createFileSystem'; |
| |
| function failed(error) { |
| reject(new Error('Creating file system: ' + error)); |
| } |
| |
| function createdFileSystem(fileSystem) { |
| console.log('test: created file system', fileSystem.name); |
| window.fileSystem = fileSystem; |
| resolve(); |
| } |
| |
| const bytes = images * 30 * 1024 * 1024; // 30M per image. |
| window.webkitRequestFileSystem( |
| window.TEMPORARY, bytes, createdFileSystem, failed); |
| }); |
| } |
| |
| function writeToFileSystem(image) { |
| return new Promise(async (resolve, reject) => { |
| document.title = image; |
| |
| const buffer = await fetch(image).then((response) => { |
| if (!response.ok) |
| throw new Error('Failed to fetch image: ' + image); |
| return response.arrayBuffer(); |
| }).catch(reject); |
| |
| function failure(error) { |
| reject(new Error('Writing file system: ' + error)); |
| } |
| |
| function writeEntry(fileEntry) { |
| fileEntry.createWriter((writer) => { |
| writer.onerror = failure; |
| writer.onwrite = resolve; |
| writer.write(new Blob([buffer])); |
| }, failure); |
| } |
| |
| window.fileSystem.root.getFile( |
| image.replace('images/', ''), {create: true}, writeEntry, failure); |
| }); |
| } |
| |
| function readFromFileSystem(image) { |
| return new Promise((resolve, reject) => { |
| document.title = image; |
| |
| function failure(error) { |
| reject(new Error('Reading file system: ' + error)); |
| } |
| |
| function invalid(size) { |
| return size <= 0 || size >= Math.pow(2, 30); |
| } |
| |
| function readEntry(fileEntry) { |
| fileEntry.file((file) => { |
| if (invalid(file.size)) |
| return failure('invalid file size'); |
| const reader = new FileReader(); |
| reader.onerror = failure; |
| reader.onload = () => resolve(reader.result); |
| reader.readAsArrayBuffer(file); |
| }, failure); |
| } |
| |
| window.fileSystem.root.getFile( |
| image.replace('images/', ''), {}, readEntry, failure); |
| }); |
| } |
| |
| function hashUint8Array(data, hash = ~0) { |
| for (let i = 0; i < data.byteLength; ++i) |
| hash = (hash << 5) - hash + data[i]; |
| return Math.abs(hash).toString(16); |
| } |
| |
| function renderJPG(name, image) { |
| const data = image.data; |
| |
| let renderer = new Image(); |
| renderer.onerror = renderer.onload = (event) => { |
| if (renderer.width > (window.screen.availWidth / 2)) |
| renderer.classList.add('zoom'); |
| document.body.appendChild(renderer); |
| URL.revokeObjectURL(renderer.src); |
| if (--window.images_ <= 0) |
| document.title = 'DONE'; |
| }; |
| |
| renderer.src = URL.createObjectURL(new Blob([data])); |
| ++window.images_; |
| } |
| |
| function renderRGB(name, image) { |
| const data = image.data; |
| |
| let canvas = document.createElement('canvas'); |
| canvas.width = image.width; |
| canvas.height = image.height; |
| if (image.width > (window.screen.availWidth / 2)) |
| canvas.classList.add('zoom'); |
| |
| // Create imageData from the image RGB data. |
| let context = canvas.getContext('2d'); |
| let imageData = context.createImageData(image.width, image.height); |
| for (let i = 0, j = 0; i < image.size; i += 3, j += 4) { |
| imageData.data[j + 0] = data[i + 0]; // R |
| imageData.data[j + 1] = data[i + 1]; // G |
| imageData.data[j + 2] = data[i + 2]; // B |
| imageData.data[j + 3] = 255; // A |
| } |
| |
| // Render the imageData. |
| context.putImageData(imageData, 0, 0); |
| document.body.appendChild(canvas); |
| } |
| |
| function renderResult(name, image) { |
| if (!image) |
| return; |
| |
| const hash = hashUint8Array(image.data); |
| const data = image.data; |
| image.data = undefined; |
| const result = JSON.stringify(image); |
| console.log('test:', name, image.type, 'hash', hash, result); |
| image.data = data; |
| |
| if (image.format == 0) |
| return renderJPG(name, image); |
| if (image.format == 1) |
| return renderRGB(name, image); |
| } |
| |
| function renderDetails(name, details) { |
| if (!details) |
| return; |
| |
| const text = new TextEncoder('UTF-8').encode(details); |
| const hash = hashUint8Array(text); |
| |
| console.log('test:', name, 'details hash', hash, details); |
| } |
| |
| window.Module = { |
| onRuntimeInitialized: () => document.title = 'READY', |
| }; |
| |
| window.onload = function loadPiexModule() { |
| let script = document.createElement('script'); |
| document.head.appendChild(script); |
| script.src = '/piex.js.wasm'; |
| }; |
| |
| window.onerror = (error) => { |
| console.log('test: FAIL', error, '\n'); |
| document.title = 'DONE'; |
| }; |
| |
| async function runTest(image = 'images/SONY_A500_01.ARW') { |
| // Start the test of image. |
| document.title = image; |
| console.log('test:', image); |
| document.body.innerHTML = `<pre>${image}</pre>`; |
| |
| // Fetch the image in an array buffer. |
| let time = window.performance.now(); |
| const buffer = await readFromFileSystem(image).catch(onerror); |
| if (!buffer) |
| return; |
| |
| let imageBuffer = new ImageBuffer(buffer); |
| const fetched = window.performance.now() - time; |
| |
| // Extract the preview|thumbnail images, render them. |
| return new Promise((resolve) => { |
| resolve(imageBuffer.process()); |
| }).then((result) => { |
| let preview = imageBuffer.preview(); |
| let thumb = imageBuffer.thumbnail(); |
| let details = imageBuffer.details(); |
| imageBuffer.close(); |
| time = window.performance.now() - time; |
| window.images_ = 0; |
| renderDetails(image, details); |
| renderResult(image, preview); |
| renderResult(image, thumb); |
| console.log('test: done', |
| time.toFixed(3), fetched.toFixed(3), (time - fetched).toFixed(3)); |
| window.testTime += time; |
| console.log('\n'); |
| if (!window.images_) |
| document.title = 'DONE'; |
| }).catch((error) => { |
| imageBuffer.close(); |
| console.log(error); |
| document.title = 'DONE'; |
| }); |
| } |
| </script> |