Skip to content

Commit

Permalink
fix(windows): prevent memory leaks when destroying, add rd file
Browse files Browse the repository at this point in the history
  • Loading branch information
nacardin authored and bitjson committed Apr 20, 2017
1 parent 27fdd92 commit 1a4843a
Show file tree
Hide file tree
Showing 11 changed files with 492 additions and 242 deletions.
3 changes: 0 additions & 3 deletions plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,6 @@
<js-module src="src/windows/lib/qrScanner.js" name="qrScanner">
<runs/>
</js-module>
<js-module src="src/windows/lib/videoCapture.js" name="videoCapture">
<runs/>
</js-module>
<js-module src="src/windows/lib/preview.js" name="preview">
<runs/>
</js-module>
Expand Down
263 changes: 263 additions & 0 deletions src/windows/QRReader/CameraRotationHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,263 @@
using System;
using Windows.Devices.Enumeration;
using Windows.Devices.Sensors;
using Windows.Graphics.Display;
using Windows.Media.Capture;
using Windows.Storage.FileProperties;

namespace QRReader
{
class CameraRotationHelper
{
private EnclosureLocation _cameraEnclosureLocation;
private DisplayInformation _displayInformation = DisplayInformation.GetForCurrentView();
private SimpleOrientationSensor _orientationSensor = SimpleOrientationSensor.GetDefault();

/// <summary>
/// Occurs each time the simple orientation sensor reports a new sensor reading or when the display's current or native orientation changes
/// </summary>
public event EventHandler<bool> OrientationChanged;

public CameraRotationHelper(EnclosureLocation cameraEnclosureLocation)
{
_cameraEnclosureLocation = cameraEnclosureLocation;
if (!IsEnclosureLocationExternal(_cameraEnclosureLocation) && _orientationSensor != null)
{
_orientationSensor.OrientationChanged += SimpleOrientationSensor_OrientationChanged;
}
_displayInformation.OrientationChanged += DisplayInformation_OrientationChanged;
}

/// <summary>
/// Detects whether or not the camera is external to the device
/// </summary>
public static bool IsEnclosureLocationExternal(EnclosureLocation enclosureLocation)
{
return (enclosureLocation == null || enclosureLocation.Panel == Windows.Devices.Enumeration.Panel.Unknown);
}

/// <summary>
/// Gets the rotation to rotate ui elements
/// </summary>
public SimpleOrientation GetUIOrientation()
{
if (IsEnclosureLocationExternal(_cameraEnclosureLocation))
{
// Cameras that are not attached to the device do not rotate along with it, so apply no rotation
return SimpleOrientation.NotRotated;
}

// Return the difference between the orientation of the device and the orientation of the app display
var deviceOrientation = _orientationSensor?.GetCurrentOrientation() ?? SimpleOrientation.NotRotated;
var displayOrientation = ConvertDisplayOrientationToSimpleOrientation(_displayInformation.CurrentOrientation);
return SubtractOrientations(displayOrientation, deviceOrientation);
}

/// <summary>
/// Gets the rotation of the camera to rotate pictures/videos when saving to file
/// </summary>
public SimpleOrientation GetCameraCaptureOrientation()
{
if (IsEnclosureLocationExternal(_cameraEnclosureLocation))
{
// Cameras that are not attached to the device do not rotate along with it, so apply no rotation
return SimpleOrientation.NotRotated;
}

// Get the device orientation offset by the camera hardware offset
var deviceOrientation = _orientationSensor?.GetCurrentOrientation() ?? SimpleOrientation.NotRotated;
var result = SubtractOrientations(deviceOrientation, GetCameraOrientationRelativeToNativeOrientation());

// If the preview is being mirrored for a front-facing camera, then the rotation should be inverted
if (ShouldMirrorPreview())
{
result = MirrorOrientation(result);
}
return result;
}

/// <summary>
/// Gets the rotation of the camera to display the camera preview
/// </summary>
public SimpleOrientation GetCameraPreviewOrientation()
{
if (IsEnclosureLocationExternal(_cameraEnclosureLocation))
{
// Cameras that are not attached to the device do not rotate along with it, so apply no rotation
return SimpleOrientation.NotRotated;
}

// Get the app display rotation offset by the camera hardware offset
var result = ConvertDisplayOrientationToSimpleOrientation(_displayInformation.CurrentOrientation);
result = SubtractOrientations(result, GetCameraOrientationRelativeToNativeOrientation());

// If the preview is being mirrored for a front-facing camera, then the rotation should be inverted
if (ShouldMirrorPreview())
{
result = MirrorOrientation(result);
}
return result;
}

public static PhotoOrientation ConvertSimpleOrientationToPhotoOrientation(SimpleOrientation orientation)
{
switch (orientation)
{
case SimpleOrientation.Rotated90DegreesCounterclockwise:
return PhotoOrientation.Rotate90;
case SimpleOrientation.Rotated180DegreesCounterclockwise:
return PhotoOrientation.Rotate180;
case SimpleOrientation.Rotated270DegreesCounterclockwise:
return PhotoOrientation.Rotate270;
case SimpleOrientation.NotRotated:
default:
return PhotoOrientation.Normal;
}
}

public static int ConvertSimpleOrientationToClockwiseDegrees(SimpleOrientation orientation)
{
switch (orientation)
{
case SimpleOrientation.Rotated90DegreesCounterclockwise:
return 270;
case SimpleOrientation.Rotated180DegreesCounterclockwise:
return 180;
case SimpleOrientation.Rotated270DegreesCounterclockwise:
return 90;
case SimpleOrientation.NotRotated:
default:
return 0;
}
}

private SimpleOrientation ConvertDisplayOrientationToSimpleOrientation(DisplayOrientations orientation)
{
SimpleOrientation result;
switch (orientation)
{
case DisplayOrientations.Landscape:
result = SimpleOrientation.NotRotated;
break;
case DisplayOrientations.PortraitFlipped:
result = SimpleOrientation.Rotated90DegreesCounterclockwise;
break;
case DisplayOrientations.LandscapeFlipped:
result = SimpleOrientation.Rotated180DegreesCounterclockwise;
break;
case DisplayOrientations.Portrait:
default:
result = SimpleOrientation.Rotated270DegreesCounterclockwise;
break;
}

// Above assumes landscape; offset is needed if native orientation is portrait
if (_displayInformation.NativeOrientation == DisplayOrientations.Portrait)
{
result = AddOrientations(result, SimpleOrientation.Rotated90DegreesCounterclockwise);
}

return result;
}

private static SimpleOrientation MirrorOrientation(SimpleOrientation orientation)
{
// This only affects the 90 and 270 degree cases, because rotating 0 and 180 degrees is the same clockwise and counter-clockwise
switch (orientation)
{
case SimpleOrientation.Rotated90DegreesCounterclockwise:
return SimpleOrientation.Rotated270DegreesCounterclockwise;
case SimpleOrientation.Rotated270DegreesCounterclockwise:
return SimpleOrientation.Rotated90DegreesCounterclockwise;
}
return orientation;
}

private static SimpleOrientation AddOrientations(SimpleOrientation a, SimpleOrientation b)
{
var aRot = ConvertSimpleOrientationToClockwiseDegrees(a);
var bRot = ConvertSimpleOrientationToClockwiseDegrees(b);
var result = (aRot + bRot) % 360;
return ConvertClockwiseDegreesToSimpleOrientation(result);
}

private static SimpleOrientation SubtractOrientations(SimpleOrientation a, SimpleOrientation b)
{
var aRot = ConvertSimpleOrientationToClockwiseDegrees(a);
var bRot = ConvertSimpleOrientationToClockwiseDegrees(b);
// Add 360 to ensure the modulus operator does not operate on a negative
var result = (360 + (aRot - bRot)) % 360;
return ConvertClockwiseDegreesToSimpleOrientation(result);
}

public static VideoRotation ConvertSimpleOrientationToVideoRotation(SimpleOrientation orientation)
{
switch (orientation)
{
case SimpleOrientation.Rotated90DegreesCounterclockwise:
return VideoRotation.Clockwise270Degrees;
case SimpleOrientation.Rotated180DegreesCounterclockwise:
return VideoRotation.Clockwise180Degrees;
case SimpleOrientation.Rotated270DegreesCounterclockwise:
return VideoRotation.Clockwise90Degrees;
case SimpleOrientation.NotRotated:
default:
return VideoRotation.None;
}
}

private static SimpleOrientation ConvertClockwiseDegreesToSimpleOrientation(int orientation)
{
switch (orientation)
{
case 270:
return SimpleOrientation.Rotated90DegreesCounterclockwise;
case 180:
return SimpleOrientation.Rotated180DegreesCounterclockwise;
case 90:
return SimpleOrientation.Rotated270DegreesCounterclockwise;
case 0:
default:
return SimpleOrientation.NotRotated;
}
}

private void SimpleOrientationSensor_OrientationChanged(SimpleOrientationSensor sender, SimpleOrientationSensorOrientationChangedEventArgs args)
{
if (args.Orientation != SimpleOrientation.Faceup && args.Orientation != SimpleOrientation.Facedown)
{
// Only raise the OrientationChanged event if the device is not parallel to the ground. This allows users to take pictures of documents (FaceUp)
// or the ceiling (FaceDown) in portrait or landscape, by first holding the device in the desired orientation, and then pointing the camera
// either up or down, at the desired subject.
//Note: This assumes that the camera is either facing the same way as the screen, or the opposite way. For devices with cameras mounted
// on other panels, this logic should be adjusted.
OrientationChanged?.Invoke(this, false);
}
}

private void DisplayInformation_OrientationChanged(DisplayInformation sender, object args)
{
OrientationChanged?.Invoke(this, true);
}

private bool ShouldMirrorPreview()
{
// It is recommended that applications mirror the preview for front-facing cameras, as it gives users a more natural experience, since it behaves more like a mirror
return (_cameraEnclosureLocation.Panel == Windows.Devices.Enumeration.Panel.Front);
}

private SimpleOrientation GetCameraOrientationRelativeToNativeOrientation()
{
// Get the rotation angle of the camera enclosure as it is mounted in the device hardware
var enclosureAngle = ConvertClockwiseDegreesToSimpleOrientation((int)_cameraEnclosureLocation.RotationAngleInDegreesClockwise);

// Account for the fact that, on portrait-first devices, the built in camera sensor is read at a 90 degree offset to the native orientation
if (_displayInformation.NativeOrientation == DisplayOrientations.Portrait && !IsEnclosureLocationExternal(_cameraEnclosureLocation))
{
enclosureAngle = AddOrientations(SimpleOrientation.Rotated90DegreesCounterclockwise, enclosureAngle);
}

return enclosureAngle;
}
}
}
12 changes: 5 additions & 7 deletions src/windows/QRReader/QRReader.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{2928CF9F-4255-4D8A-A7C3-70324CC137A4}</ProjectGuid>
<ProjectGuid>{DF132792-0542-4936-914D-4C25D779CAC8}</ProjectGuid>
<OutputType>winmdobj</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>QRReader</RootNamespace>
<AssemblyName>QRReader</AssemblyName>
<DefaultLanguage>en-US</DefaultLanguage>
<TargetPlatformIdentifier>UAP</TargetPlatformIdentifier>
<TargetPlatformVersion>10.0.14393.0</TargetPlatformVersion>
<TargetPlatformMinVersion>10.0.10586.0</TargetPlatformMinVersion>
<TargetPlatformMinVersion>10.0.14393.0</TargetPlatformMinVersion>
<MinimumVisualStudioVersion>14</MinimumVisualStudioVersion>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
Expand All @@ -27,7 +27,6 @@
<DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
Expand Down Expand Up @@ -109,8 +108,10 @@
<None Include="project.json" />
</ItemGroup>
<ItemGroup>
<Compile Include="Reader.cs" />
<Compile Include="CameraRotationHelper.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Reader.cs" />
<Compile Include="VideoCapture.cs" />
</ItemGroup>
<ItemGroup>
<Reference Include="ZXing">
Expand All @@ -120,9 +121,6 @@
<PropertyGroup Condition=" '$(VisualStudioVersion)' == '' or '$(VisualStudioVersion)' &lt; '14.0' ">
<VisualStudioVersion>14.0</VisualStudioVersion>
</PropertyGroup>
<PropertyGroup>
<StartupObject />
</PropertyGroup>
<Import Project="$(MSBuildExtensionsPath)\Microsoft\WindowsXaml\v$(VisualStudioVersion)\Microsoft.Windows.UI.Xaml.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
Expand Down
6 changes: 6 additions & 0 deletions src/windows/QRReader/QRReader.csproj.user
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ProjectView>ProjectFiles</ProjectView>
</PropertyGroup>
</Project>
Loading

0 comments on commit 1a4843a

Please sign in to comment.