UNPKG

jeri

Version:

JavaScript Extended-Range Image viewer

208 lines 7.54 kB
"use strict"; var __assign = (this && this.__assign) || Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; Object.defineProperty(exports, "__esModule", { value: true }); var ExrParserWorker = require("worker-loader?name=exr.worker.js!./exr-parser.worker.js"); /** * A pool of exr parsing webworkers that get assigned tasks in a round-robin fashion. */ var ExrParserPool = /** @class */ (function () { function ExrParserPool(nWorkers) { this.nWorkers = nWorkers; /** To divide the work equally, keep track of the worker that got the previous job. */ this.nextWorkerId = 0; /** Each job that is sent to a worker gets a unique jobId. */ this.jobId = 0; /** After sending a job to a web worker, we register a return handler for when data comes back */ this.returnHandlers = {}; this.workers = []; for (var i = 0; i < nWorkers; ++i) { var worker = new ExrParserWorker(); this.workers.push(worker); worker.onmessage = this.handleResult.bind(this); } } /** * Parse raw EXR data using by assigning the task to a web worker in the pool */ ExrParserPool.prototype.parse = function (url, data) { var _this = this; return new Promise(function (resolve, reject) { var worker = _this.nextWorker(); var jobId = _this.jobId++; _this.returnHandlers[jobId] = function (event) { if (event.data.success) { resolve(__assign({ url: url }, event.data.image)); } else { reject(new Error(event.data.message)); } }; worker.postMessage({ jobId: jobId, data: data }, [data]); }); }; /** * Handler that gets called whenever a result comes back from the webworkers * It looks up the corresponding handler by the jobId. */ ExrParserPool.prototype.handleResult = function (event) { if (event.data.jobId != null) { var callback = this.returnHandlers[event.data.jobId]; delete this.returnHandlers[event.data.jobId]; callback(event); } else { throw new Error("Got a message from the webworker without job id."); } }; /** * Get the web worker whose turn it is */ ExrParserPool.prototype.nextWorker = function () { var worker = this.workers[this.nextWorkerId]; this.nextWorkerId = (this.nextWorkerId + 1) % this.nWorkers; return worker; }; return ExrParserPool; }()); var pool = new ExrParserPool(2); function parseExr(url, data) { return pool.parse(url, data); } function loadImage(url) { var suffix = url.split('.').pop(); if (suffix && suffix.toLocaleLowerCase() === 'exr') { return loadExr(url); } else { return loadLdr(url); } } exports.loadImage = loadImage; function loadExr(url) { console.time("Downloading '" + url + "'"); // tslint:disable-line return fetch(url) .then(function (result) { console.timeEnd("Downloading '" + url + "'"); // tslint:disable-line return result; }) .then(function (result) { return result.arrayBuffer(); }) .then(function (data) { return parseExr(url, data); }); } exports.loadExr = loadExr; function loadLdr(url) { console.time("Downloading '" + url + "'"); // tslint:disable-line return new Promise(function (resolve, reject) { var image = new Image(); image.onerror = function (error) { return reject(new Error("Failed to load '" + url + "'.")); }; image.onload = function () { console.timeEnd("Downloading '" + url + "'"); // tslint:disable-line try { var canvas = document.createElement('canvas'); canvas.width = image.width; canvas.height = image.height; var ctx = canvas.getContext('2d'); if (!ctx) { reject(new Error('Failed to get 2d canvas context.')); return; } ctx.drawImage(image, 0, 0); resolve({ url: url, width: image.width, height: image.height, nChannels: 4, data: image, type: 'LdrImage', }); } catch (error) { reject(new Error("Failed to load image '" + url + "': " + error)); } }; image.src = url; }); } exports.loadLdr = loadLdr; var pixelColorCache = new Map(); /** * Extract a pixel's color * Caches data for LDR images * @param image image * @param x pixel's x coordinate * @param y pixel's y coordinate * @param c color channel (r=0, g=1, b=2) */ function getPixelColor(image, x, y, c) { if (image.type === 'HdrImage') { return image.data[(x + y * image.width) * image.nChannels + c]; } else { var getColorFnc = pixelColorCache.get(image); if (getColorFnc == null) { var canvas = document.createElement('canvas'); canvas.width = image.width; canvas.height = image.height; var ctx_1 = canvas.getContext('2d'); if (!ctx_1) { throw new Error('Failed to create 2d context to retrieve LDR image data'); } ctx_1.drawImage(image.data, 0, 0, image.width, image.height); getColorFnc = function (X, Y, C) { return ctx_1.getImageData(X, Y, 1, 1).data[C] / 256; }; pixelColorCache.set(image, getColorFnc); } return getColorFnc(x, y, c); } } exports.getPixelColor = getPixelColor; var ImageCache = /** @class */ (function () { function ImageCache() { this.images = {}; this.downloading = {}; } ImageCache.prototype.contains = function (url) { return this.images.hasOwnProperty(url); }; ImageCache.prototype.currentlyDownloading = function (url) { return this.downloading.hasOwnProperty(url); }; ImageCache.prototype.size = function () { return Object.keys(this.images).length; }; ImageCache.prototype.get = function (url) { if (this.contains(url)) { // console.log(`Image ${url} was in cache.`); // tslint:disable-line return Promise.resolve(this.images[url]); } else if (this.currentlyDownloading(url)) { return this.downloading[url]; } else { // console.log(`Image ${url} is downloaded.`); // tslint:disable-line return this.load(url); } }; ImageCache.prototype.store = function (url, image) { if (this.currentlyDownloading(url)) { delete this.currentlyDownloading[url]; } this.images[url] = image; return image; }; ImageCache.prototype.load = function (url) { var _this = this; var imagePromise = loadImage(url); this.downloading[url] = imagePromise; return imagePromise .then(function (image) { return _this.store(url, image); }); }; return ImageCache; }()); exports.ImageCache = ImageCache; //# sourceMappingURL=image-loading.js.map