Implemented applyConfig for ImageSource; Refactored ImageSource to fit into a class

feature/image-source
Christoph Oberhofer 8 years ago
parent b799a154b0
commit d279971c46

@ -53,17 +53,8 @@ $(function() {
$(".controls .reader-config-group").off("change", "input, select"); $(".controls .reader-config-group").off("change", "input, select");
$(".controls button").off("click"); $(".controls button").off("click");
}, },
decode: function(file) { detect: function() {
this.detachListeners(); var scanner = this.scanner;
var size = this.state.inputStream.size;
console.log("decode...");
Quagga.fromImage(file, {
constraints: {width: size, height: size},
locator: this.state.locator,
decoder: this.state.decoder,
})
.then(function(scanner) {
scanner.detect() scanner.detect()
.then(function(result) { .then(function(result) {
console.log(result); console.log(result);
@ -78,9 +69,31 @@ $(function() {
drawResult(scanner, result); drawResult(scanner, result);
this.attachListeners(); this.attachListeners();
}.bind(this)); }.bind(this));
},
decode: function(file) {
this.detachListeners();
var size = this.state.inputStream.size;
console.log("decode...");
if (!this.scanner) {
Quagga.fromImage({
constraints: {src: file, width: size, height: size},
locator: this.state.locator,
decoder: this.state.decoder,
})
.then(function(scanner) {
this.scanner = scanner;
this.detect();
}.bind(this)); }.bind(this));
} else {
this.scanner.applyConfig({
constraints: {src: file, width: size, height: size},
locator: this.state.locator,
decoder: this.state.decoder,
})
.then(this.detect.bind(this));
}
// Quagga.fromCamera(constraints)
// Quagga.fromSource(); // Quagga.fromSource();
// Quagga.fromPixelCapture(); // Quagga.fromPixelCapture();
}, },

@ -101,7 +101,8 @@ $(function() {
console.log(JSON.stringify(this.state)); console.log(JSON.stringify(this.state));
this.scanner.applyConfig({ this.scanner
.applyConfig({
constraints: this.state.inputStream.constraints, constraints: this.state.inputStream.constraints,
locator: this.state.locator, locator: this.state.locator,
decoder: this.state.decoder, decoder: this.state.decoder,

@ -3,7 +3,7 @@ import {determineOrientation} from '../common/device';
import CameraAccess from './camera_access'; import CameraAccess from './camera_access';
import {getViewport} from '../common/utils'; import {getViewport} from '../common/utils';
import {generateSourceInterface} from './SourceInterface'; import {generateSourceInterface} from './SourceInterface';
import {Scope} from './input/SourceScope'; import {Scope} from './SourceScope';
const ConstraintPresets = [ const ConstraintPresets = [
{ {

@ -1,64 +1,78 @@
import {findTagsInObjectURL} from './exif_helper'; import {findTagsInObjectURL} from './exif_helper';
import {generateSourceInterface} from './SourceInterface'; import {Source} from './SourceInterface';
export function fromImage(input, constraints = {width: 800, height: 800, channels: 3}) { class ImageSource extends Source {
var $image = null; constructor() {
var src = null; super("IMAGE");
this._$image = null;
this._src = null;
this.tags = null;
this.colorChannels = 3;
}
applyConstraints(newConstraints) {
this.constraints = newConstraints;
this.colorChannels = this.constraints.channels || this.colorChannels;
return this._applyInput(this.constraints.src)
._loadResource()
.then(() => findTagsInObjectURL(this._src, ['orientation']))
.then((tags) => {this.tags = tags;})
.then(this._determineDimensions.bind(this))
.then(() => this);
}
_loadResource() {
return new Promise((resolve, reject) => {
if (this._src || !this._$image.complete) {
this._$image.addEventListener('load', resolve, false);
this._$image.addEventListener('error', reject, false);
if (this._src) {
console.log(`Setting src = ${this._src}`);
this._$image.src = this._src;
}
} else {
return resolve();
}
});
}
_applyInput(input) {
if (typeof input === 'string') { if (typeof input === 'string') {
// data or url, or queryString // data or url, or queryString
$image = new Image(); this._$image = new Image();
src = input; this._src = input;
} else if (input instanceof HTMLImageElement) { } else if (input instanceof HTMLImageElement) {
$image = input; this._$image = input;
} else if (input instanceof File) { } else if (input instanceof File) {
$image = new Image(); this._$image = new Image();
src = URL.createObjectURL(input); this._src = URL.createObjectURL(input);
} else { } else {
return Promise.reject("fromImage needs a src, HTMLImageElement or File"); throw new Error("fromImage needs a src, HTMLImageElement or File");
} }
return new Promise(function(resolve, reject) { return this;
if (src || !$image.complete) {
console.log('Adding eventlistener');
$image.addEventListener('load', function() {
resolve();
}, false);
$image.addEventListener('error', function(e) {
reject(e);
}, false);
if (src) {
console.log(`Setting src = ${src}`);
$image.src = src;
}
} else {
return resolve();
} }
})
.then(() => findTagsInObjectURL(src, ['orientation'])) _determineDimensions() {
.then((tags) => { let width = this._$image.naturalWidth;
let width = $image.naturalWidth; let height = this._$image.naturalHeight;
let height = $image.naturalHeight; let desiredWidth = this.constraints.width;
if (tags && tags.orientation) { if (this.tags && this.tags.orientation) {
switch (tags.orientation) { switch (this.tags.orientation) {
case 6: case 6:
case 8: case 8:
width = $image.naturalHeight; width = this._$image.naturalHeight;
height = $image.naturalWidth; height = this._$image.naturalWidth;
} }
} }
const imageAR = width / height; const imageAR = width / height;
const calculatedWidth = imageAR > 1 ? constraints.width : Math.floor((imageAR) * constraints.width); const calculatedWidth = imageAR > 1 ? desiredWidth : Math.floor((imageAR) * desiredWidth);
const calculatedHeight = imageAR > 1 ? Math.floor((1 / imageAR) * constraints.width) : constraints.width; const calculatedHeight = imageAR > 1 ? Math.floor((1 / imageAR) * desiredWidth) : desiredWidth;
const colorChannels = constraints.channels || 3;
return Object.assign(generateSourceInterface(), { this._dimensions = {
type: "IMAGE",
colorChannels,
tags,
getDimensions() {
return {
viewport: { viewport: {
width: $image.naturalWidth, // AR width, // AR
height: $image.naturalHeight, // AR height, // AR
x: 0, // AR x: 0, // AR
y: 0, // AR y: 0, // AR
}, },
@ -67,16 +81,22 @@ export function fromImage(input, constraints = {width: 800, height: 800, channel
height: calculatedHeight, // AR height: calculatedHeight, // AR
}, },
}; };
}, }
getDrawable: function() {
return $image; getDimensions() {
}, return this._dimensions;
getLabel: function() { }
return $image.src;
}, getDrawable() {
getConstraints: function() { return this._$image;
return constraints; }
},
}); getLabel() {
}); return this._$image.src;
}
}
export function fromImage(constraints = {width: 800, height: 800, channels: 3}) {
const imageSource = new ImageSource();
return imageSource.applyConstraints(constraints);
} }

@ -98,6 +98,7 @@ export function fromSource(source, {target = "#interactive.viewport"} = {}) {
return { return {
grabFrameData: function grabFrameData({clipping} = {}) { grabFrameData: function grabFrameData({clipping} = {}) {
const frame = source.getDrawable();
const {viewport, canvas: canvasSize} = source.getDimensions(); const {viewport, canvas: canvasSize} = source.getDimensions();
const sx = viewport.x; const sx = viewport.x;
const sy = viewport.y; const sy = viewport.y;
@ -121,12 +122,12 @@ export function fromSource(source, {target = "#interactive.viewport"} = {}) {
return sleep(100).then(grabFrameData); return sleep(100).then(grabFrameData);
} }
if (!(drawable instanceof HTMLCanvasElement)) { if (!(frame instanceof HTMLCanvasElement)) {
drawImage( drawImage(
canvasSize, canvasSize,
ctx, ctx,
source, source,
drawable, frame,
sx, sx,
sy, sy,
sWidth, sWidth,

@ -1,24 +1,22 @@
const viewport = { class Source {
x: 0, constructor(type) {
y: 0, this.type = type;
width: 0, }
height: 0, getDimensions() {}
}; getConstraints() {}
getDrawable() {}
applyConstraints() {}
getLabel() {}
stop() {}
getScope() {}
}
const canvas = { export {Source};
height: 0,
width: 0,
};
export function generateSourceInterface() { export function generateSourceInterface() {
return { return {
type: "INTERFACE", type: "INTERFACE",
getDimensions() { getDimensions() {},
return {
viewport,
canvas,
};
},
getConstraints() {}, getConstraints() {},
getDrawable() {}, getDrawable() {},
applyConstraints() {}, applyConstraints() {},
@ -26,4 +24,4 @@ export function generateSourceInterface() {
stop() {}, stop() {},
getScope() {}, getScope() {},
}; };
} };

@ -43,18 +43,12 @@ function fromConfig(pixelCapturer, config) {
scanner.start(); scanner.start();
return Promise.resolve(true); return Promise.resolve(true);
} }
pendingStart = new Promise((resolve, reject) => { pendingStart = scanner.init(currentConfig)
scanner.init(currentConfig, (error) => { .then(() => {
if (error) {
console.log(error);
reject(error);
}
initialized = true; initialized = true;
scanner.start(); scanner.start();
resolve();
pendingStart = null; pendingStart = null;
}); });
});
return pendingStart; return pendingStart;
}, },
stop() { stop() {
@ -88,14 +82,14 @@ function fromConfig(pixelCapturer, config) {
}) })
}; };
} else { } else {
return new Promise((resolve, reject) => { let pendingDecodeSingle = Promise.resolve();
scanner.decodeSingle(currentConfig, (result) => { if (!initialized) {
if (result && result.codeResult && result.codeResult.code) { pendingDecodeSingle = pendingDecodeSingle
return resolve(result); .then(scanner.init.bind(scanner, currentConfig))
.then(() => {initialized = true;});
} }
return reject(result); return pendingDecodeSingle
}); .then(scanner.decodeSingle.bind(scanner));
});
} }
}, },
registerResultCollector(resultCollector) { registerResultCollector(resultCollector) {
@ -106,6 +100,7 @@ function fromConfig(pixelCapturer, config) {
}, },
applyConfig(newConfig) { applyConfig(newConfig) {
const normalizedConfig = merge({}, Config, currentConfig, newConfig); const normalizedConfig = merge({}, Config, currentConfig, newConfig);
const wasRunning = scanner.isRunning();
let promise = Promise.resolve(); let promise = Promise.resolve();
if (hasConfigChanged(currentConfig, normalizedConfig, "constraints")) { if (hasConfigChanged(currentConfig, normalizedConfig, "constraints")) {
console.log("constraints changed!", currentConfig.constraints, normalizedConfig.constraints); console.log("constraints changed!", currentConfig.constraints, normalizedConfig.constraints);
@ -117,6 +112,9 @@ function fromConfig(pixelCapturer, config) {
if (hasConfigChanged(currentConfig, normalizedConfig)) { if (hasConfigChanged(currentConfig, normalizedConfig)) {
console.log("config changed!"); console.log("config changed!");
promise = promise.then(() => scanner.applyConfig(normalizedConfig)); promise = promise.then(() => scanner.applyConfig(normalizedConfig));
if (wasRunning) {
promise = promise.then(scanner.start.bind(scanner));
}
} }
currentConfig = normalizedConfig; currentConfig = normalizedConfig;
return promise; return promise;
@ -134,10 +132,13 @@ function fromSource(config, source) {
function createApi() { function createApi() {
return { return {
fromImage(image, options) { fromImage(options) {
const config = merge({}, Config, options); const config = merge({}, Config, options);
return Source return Source
.fromImage(image, config.constraints) .fromImage(config.constraints, {
target: config.target,
scope: Source.Scope.INTERNAL,
})
.then(fromSource.bind(null, config)); .then(fromSource.bind(null, config));
}, },
fromCamera(options) { fromCamera(options) {

@ -346,7 +346,7 @@ function createScanner(pixelCapturer) {
x: e.data.size.x, x: e.data.size.x,
y: e.data.size.y y: e.data.size.y
}, new Uint8Array(e.data.imageData)); }, new Uint8Array(e.data.imageData));
scanner.init(config, ready, imageWrapper); scanner.init(config, imageWrapper).then(ready);
scanner.subscribe("processed", onProcessed); scanner.subscribe("processed", onProcessed);
} else if (e.data.cmd === 'process') { } else if (e.data.cmd === 'process') {
imageWrapper.data = new Uint8Array(e.data.imageData); imageWrapper.data = new Uint8Array(e.data.imageData);
@ -417,15 +417,17 @@ function createScanner(pixelCapturer) {
} }
return { return {
init: function(config, cb, imageWrapper) { init: function(config, imageWrapper) {
_stopped = true;
_config = merge({}, Config, config); _config = merge({}, Config, config);
if (imageWrapper) { if (imageWrapper) {
_onUIThread = false; _onUIThread = false;
initBuffers(imageWrapper); initBuffers(imageWrapper);
return cb(); return Promise.resolve();
} else { } else {
return setup(_config).then(cb); adjustWorkerPool(0);
return setup(_config);
} }
}, },
start: function() { start: function() {
@ -443,10 +445,7 @@ function createScanner(pixelCapturer) {
} }
}, },
applyConfig(newConfig) { applyConfig(newConfig) {
_stopped = true; return this.init(newConfig);
adjustWorkerPool(0);
_config = merge({}, Config, _config, newConfig);
return setup(_config).then(start);
}, },
pause: function() { pause: function() {
_stopped = true; _stopped = true;
@ -462,11 +461,14 @@ function createScanner(pixelCapturer) {
_resultCollector = resultCollector; _resultCollector = resultCollector;
} }
}, },
decodeSingle: function(config, resultCallback) { decodeSingle() {
this.init(config, () => { return new Promise((resolve, reject) => {
_events.once("processed", (result) => { _events.once("processed", (result) => {
this.stop(); this.stop();
resultCallback.call(null, result); if (result && result.codeResult && result.codeResult.code) {
return resolve(result);
}
return reject(result);
}, true); }, true);
start(); start();
}); });

Loading…
Cancel
Save