Skip to content
This repository has been archived by the owner on Aug 11, 2020. It is now read-only.

Commit

Permalink
#616 Adds support for windows platform
Browse files Browse the repository at this point in the history
  • Loading branch information
Vladimir Kotikov committed Apr 8, 2015
1 parent c74e37a commit aa80367
Show file tree
Hide file tree
Showing 4 changed files with 139 additions and 118 deletions.
129 changes: 87 additions & 42 deletions src/windows8/BarcodeScannerProxy.js
Expand Up @@ -18,16 +18,11 @@ module.exports = {
*/
scan: function (success, fail, args) {

var capturePreview = null,
captureCancelButton = null,
capture = null,
captureSettings = null,
reader = null,

/* Width of bitmap, generated from capture stream and used for barcode search */
bitmapWidth = 800,
/* Width of bitmap, generated from capture stream and used for barcode search */
bitmapHeight = 600;
var capturePreview,
capturePreviewAlignmentMark,
captureCancelButton,
capture,
reader;

/**
* Creates a preview frame and necessary objects
Expand All @@ -36,7 +31,10 @@ module.exports = {

// Create fullscreen preview
capturePreview = document.createElement("video");
capturePreview.style.cssText = "position: absolute; left: 0; top: 0; width: 100%; height: 100%";
capturePreview.style.cssText = "position: absolute; left: 0; top: 0; width: 100%; height: 100%; background: black";

capturePreviewAlignmentMark = document.createElement('div');
capturePreviewAlignmentMark.style.cssText = "position: absolute; left: 0; top: 50%; width: 100%; height: 3px; background: red";

// Create cancel button
captureCancelButton = document.createElement("button");
Expand All @@ -45,68 +43,114 @@ module.exports = {
captureCancelButton.addEventListener('click', cancelPreview, false);

capture = new Windows.Media.Capture.MediaCapture();

captureSettings = new Windows.Media.Capture.MediaCaptureInitializationSettings();
captureSettings.streamingCaptureMode = Windows.Media.Capture.StreamingCaptureMode.video;
}

/**
* Starts stream transmission to preview frame and then run barcode search
*/
function startPreview() {
var captureSettings = new Windows.Media.Capture.MediaCaptureInitializationSettings();
captureSettings.streamingCaptureMode = Windows.Media.Capture.StreamingCaptureMode.video;
captureSettings.photoCaptureSource = Windows.Media.Capture.PhotoCaptureSource.videoPreview;

capture.initializeAsync(captureSettings).done(function () {
capturePreview.src = URL.createObjectURL(capture);
capturePreview.play();

// Insert preview frame and controls into page
document.body.appendChild(capturePreview);
document.body.appendChild(captureCancelButton);

startBarcodeSearch();

//trying to set focus mode
var controller = capture.videoDeviceController;

if (controller.focusControl && controller.focusControl.supported) {
if (controller.focusControl.configure) {
var focusConfig = new Windows.Media.Devices.FocusSettings();
focusConfig.autoFocusRange = Windows.Media.Devices.AutoFocusRange.macro;

var supportContinuousFocus = controller.focusControl.supportedFocusModes.indexOf(Windows.Media.Devices.FocusMode.continuous).returnValue;
var supportAutoFocus = controller.focusControl.supportedFocusModes.indexOf(Windows.Media.Devices.FocusMode.auto).returnValue;

if (supportContinuousFocus) {
focusConfig.mode = Windows.Media.Devices.FocusMode.continuous;
} else if (supportAutoFocus) {
focusConfig.mode = Windows.Media.Devices.FocusMode.auto;
}

controller.focusControl.configure(focusConfig);
controller.focusControl.focusAsync();
}
}

var deviceProps = controller.getAvailableMediaStreamProperties(Windows.Media.Capture.MediaStreamType.videoRecord);

deviceProps = Array.prototype.slice.call(deviceProps);
deviceProps = deviceProps.filter(function (prop) {
// filter out streams with "unknown" subtype - causes errors on some devices
return prop.subtype !== "Unknown";
}).sort(function (propA, propB) {
// sort properties by resolution
return propB.width - propA.width;
});

var maxResProps = deviceProps[0];

controller.setMediaStreamPropertiesAsync(Windows.Media.Capture.MediaStreamType.videoRecord, maxResProps).done(function () {
// handle portrait orientation
if (Windows.Graphics.Display.DisplayProperties.nativeOrientation == Windows.Graphics.Display.DisplayOrientations.portrait) {
capture.setPreviewRotation(Windows.Media.Capture.VideoRotation.clockwise90Degrees);
capturePreview.msZoom = true;
}

capturePreview.src = URL.createObjectURL(capture);
capturePreview.play();

// Insert preview frame and controls into page
document.body.appendChild(capturePreview);
document.body.appendChild(capturePreviewAlignmentMark);
document.body.appendChild(captureCancelButton);

startBarcodeSearch(maxResProps.width, maxResProps.height);
});
});
}

/**
* Starts barcode search process, implemented in WinRTBarcodeReader.winmd library
* Calls success callback, when barcode found.
*/
function startBarcodeSearch() {
reader = new WinRTBarcodeReader.Reader(capture, bitmapWidth, bitmapHeight);
var readOp = reader.readCode();
readOp.done(function (result) {
function startBarcodeSearch(width, height) {

reader = new WinRTBarcodeReader.Reader(capture, width, height);
reader.readCode().done(function (result) {
destroyPreview();
success({ text: result.text, format: result.barcodeFormat, cancelled: false });
success({ text: result && result.text, format: result && result.barcodeFormat, cancelled: !result });
}, function (err) {
destroyPreview();
fail(err);
});
}

/**
* Removes preview frame and corresponding objects from window
*/
function destroyPreview() {

capturePreview.pause();
capturePreview.src = null;
[capturePreview, captureCancelButton].forEach(function (elem) {
if (elem /* && elem in document.body.childNodes */) {
document.body.removeChild(elem);
}

[capturePreview, capturePreviewAlignmentMark, captureCancelButton].forEach(function (elem) {
elem && document.body.removeChild(elem);
});
if (reader) {
reader.stop();
reader = null;
}
if (capture) {
capture.stopRecordAsync();
capture = null;
}

reader && reader.stop();
reader = null;

capture && capture.stopRecordAsync();
capture = null;
}

/**
* Stops preview and then call success callback with cancelled=true
* See https://github.com/phonegap-build/BarcodeScanner#using-the-plugin
*/
function cancelPreview() {
destroyPreview();
success({ text: null, format: null, cancelled: true });
reader && reader.stop();
}

try {
Expand All @@ -127,4 +171,5 @@ module.exports = {
fail("Not implemented yet");
}
};
require("cordova/windows8/commandProxy").add("BarcodeScanner", module.exports);

require("cordova/exec/proxy").add("BarcodeScanner", module.exports);
Binary file modified src/windows8/lib/WinRTBarcodeReader.winmd
Binary file not shown.
Expand Up @@ -15,11 +15,11 @@
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("WindowsRuntimeComponent1")]
[assembly: AssemblyTitle("WinRTBarcodeReader")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("WindowsRuntimeComponent1")]
[assembly: AssemblyProduct("WinRTBarcodeReader")]
[assembly: AssemblyCopyright("Copyright © 2014")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
Expand Down
124 changes: 50 additions & 74 deletions src/windows8/lib/WinRTBarcodeReader/WinRTBarcodeReader/Reader.cs
Expand Up @@ -15,6 +15,7 @@ namespace WinRTBarcodeReader
using System.Threading.Tasks;

using Windows.Foundation;
using Windows.Graphics.Imaging;
using Windows.Media.Capture;
using Windows.Media.MediaProperties;
using Windows.Storage.Streams;
Expand All @@ -29,34 +30,34 @@ public sealed class Reader
#region Private fields

/// <summary>
/// MediaCapture instance, used for barcode search.
/// Data reader, used to create bitmap array.
/// </summary>
private readonly MediaCapture capture;
private readonly BarcodeReader barcodeReader;

/// <summary>
/// Encoding properties for mediaCapture object.
/// The cancel search flag.
/// </summary>
private readonly ImageEncodingProperties encodingProps;
private readonly CancellationTokenSource cancelSearch;

/// <summary>
/// Image stream for MediaCapture content.
/// MediaCapture instance, used for barcode search.
/// </summary>
private InMemoryRandomAccessStream imageStream;
private readonly MediaCapture capture;

/// <summary>
/// Data reader, used to create bitmap array.
/// Encoding properties for mediaCapture object.
/// </summary>
private DataReader datareader;
private readonly ImageEncodingProperties encodingProps;

/// <summary>
/// Flag that indicates successful barcode search.
/// Flag that indicates successful barcode search.
/// </summary>
private bool barcodeFound;
private bool barcodeFoundOrCancelled;

/// <summary>
/// The cancel search flag.
/// Image stream for MediaCapture content.
/// </summary>
private CancellationTokenSource cancelSearch;
private InMemoryRandomAccessStream imageStream;

#endregion

Expand All @@ -71,9 +72,12 @@ public sealed class Reader
public Reader(MediaCapture capture, uint width, uint height)
{
this.capture = capture;
this.encodingProps = new ImageEncodingProperties { Subtype = "BMP", Width = width, Height = height};
this.barcodeFound = false;
this.cancelSearch = new CancellationTokenSource();
encodingProps = ImageEncodingProperties.CreateJpeg();
encodingProps.Width = width;
encodingProps.Height = height;

barcodeReader = new BarcodeReader {Options = {TryHarder = true}};
cancelSearch = new CancellationTokenSource();
}

#endregion
Expand Down Expand Up @@ -108,17 +112,14 @@ public void Stop()
private async Task<Result> Read()
{
Result result = null;
while (!this.barcodeFound)
try
{
try
{
result = await this.GetCameraImage(this.cancelSearch.Token);
}
catch (OperationCanceledException)
while (result == null)
{
result = null;
result = await GetCameraImage(cancelSearch.Token);
}
}
catch (OperationCanceledException) { }

return result;
}
Expand All @@ -134,61 +135,36 @@ private async Task<Result> Read()
/// </returns>
private async Task<Result> GetCameraImage(CancellationToken cancelToken)
{
Result result = null;
await Task.Run(
async () =>
{
this.imageStream = new InMemoryRandomAccessStream();
await this.capture.CapturePhotoToStreamAsync(this.encodingProps, this.imageStream);
await this.imageStream.FlushAsync();
this.datareader = new DataReader(this.imageStream);
await this.datareader.LoadAsync((uint)this.imageStream.Size);
var bitmap = new byte[this.encodingProps.Width * this.encodingProps.Height * 4];
uint index = 0;
while (this.datareader.UnconsumedBufferLength > 0)
{
bitmap[index] = datareader.ReadByte();
index++;
}
result = await this.DecodeBitmap(bitmap);
if (result != null)
{
this.barcodeFound = true;
}
},
cancelToken).ConfigureAwait(false);
return result;
}

/// <summary>
/// Searches the bitmap for barcode.
/// </summary>
/// <param name="bitmap">Array of bytes, represents bitmap</param>
/// <param name="format">Bitmap format, default is BGRA32</param>
/// <returns>String, encoded with barcode or empty string if barcode not found</returns>
private async Task<Result> DecodeBitmap(byte[] bitmap, BitmapFormat format = BitmapFormat.BGRA32)
{
Result result = null;
try
{
await Task.Run(
() =>
{
var c = new BarcodeReader();
result = c.Decode(
bitmap,
(int)this.encodingProps.Width,
(int)this.encodingProps.Height,
format);
}).ConfigureAwait(false);
}
catch (Exception)
if (cancelToken.IsCancellationRequested)
{
throw new OperationCanceledException(cancelToken);
}

imageStream = new InMemoryRandomAccessStream();

await capture.CapturePhotoToStreamAsync(encodingProps, imageStream);
await imageStream.FlushAsync();

var decoder = await BitmapDecoder.CreateAsync(imageStream);

byte[] pixels =
(await
decoder.GetPixelDataAsync(BitmapPixelFormat.Rgba8,
BitmapAlphaMode.Ignore,
new BitmapTransform(),
ExifOrientationMode.IgnoreExifOrientation,
ColorManagementMode.DoNotColorManage)).DetachPixelData();

const BitmapFormat format = BitmapFormat.RGB32;

imageStream.Dispose();

var result =
await
Task.Run(
() => barcodeReader.Decode(pixels, (int) decoder.PixelWidth, (int) decoder.PixelHeight, format),
cancelToken);

return result;
}

Expand Down

0 comments on commit aa80367

Please sign in to comment.