Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,467 changes: 5 additions & 1,462 deletions TwinTechsForms/Droid/Resources/Resource.designer.cs

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions TwinTechsForms/Droid/Resources/values/strings.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!--HACK: Keep VS Xamarin.Android from failing to build because these two strings don't exist.-->
<string name="ApplicationName">TwinTechsFormsExample</string>
<string name="Hello">Hello</string>
</resources>
1 change: 1 addition & 0 deletions TwinTechsForms/Droid/TwinTechsFormsExample.Droid.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@
<AndroidResource Include="Resources\drawable-hdpi\icon.png" />
<AndroidResource Include="Resources\drawable-xhdpi\icon.png" />
<AndroidResource Include="Resources\drawable-xxhdpi\icon.png" />
<AndroidResource Include="Resources\values\strings.xml" />
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
<Import Project="..\packages\Xamarin.Forms.2.0.1.6505\build\portable-win+net45+wp80+win81+wpa81+MonoAndroid10+MonoTouch10+Xamarin.iOS10\Xamarin.Forms.targets" Condition="Exists('..\packages\Xamarin.Forms.2.0.1.6505\build\portable-win+net45+wp80+win81+wpa81+MonoAndroid10+MonoTouch10+Xamarin.iOS10\Xamarin.Forms.targets')" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,10 +136,21 @@ public IImageCanvas RenderSvgToCanvas (Size outputSize, double finalScale, Func<
finalCanvas.DrawImage (sliceImage, sliceFramePair.Item2);
}
} else {
// Typical approach to rendering an SVG; just draw it to the canvas.
double proportionalOutputScale = originalSvgSize.ScaleThatFits (outputSize);
// Make sure ViewBox is reset to a proportionally-scaled default in case it was previous set by slicing.
LoadedGraphic.ViewBox = new Rect (Point.Zero, originalSvgSize / proportionalOutputScale);
// Typical approach to rendering an SVG; just draw it to the canvas with a single pass.
if (Aspect == Aspect.Fill) {
var finalSize = outputSize;
LoadedGraphic.Size = finalSize;
// Reset ViewBox to make sure we aren't scaling from any previous use as 9-slice or different Aspect.
LoadedGraphic.ViewBox = new Rect (Point.Zero, originalSvgSize);
}
// TODO: else if (Aspect == AspectFill) {} (scale beyond + crop)
else { //if (Aspect == Aspect.AspectFit) {
// [default] scale within + letterbox
double proportionalOutputScale = originalSvgSize.ScaleThatFits (outputSize);
var finalSize = originalSvgSize / proportionalOutputScale;
// Ensure ViewBox set to proportionally-scaling from any previous as 9-slice or different Aspect.
LoadedGraphic.ViewBox = new Rect (Point.Zero, finalSize);
}
LoadedGraphic.Draw (finalCanvas);
}
return finalCanvas;
Expand Down
77 changes: 65 additions & 12 deletions TwinTechsForms/TwinTechsForms.SvgImage/Extensions/SizeExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,67 @@

namespace TwinTechs.Extensions
{
public static class SizeExtensions
{
public static class SizeExtensions {
/// <summary>
/// Absolute delta of value to zero is less than double.Epsilon.
/// </summary>
static bool BasicallyZero (this double value) {
return value.BasicallyEqual (0);
}
/// <summary>
/// Absolute delta of values is less than double.Epsilon.
/// </summary>
static bool BasicallyEqual (this double value1, double value2) {
return (Math.Abs (value1 - value2) * 1E-15) < double.Epsilon;
}

/// <summary>
/// Corresponds to finding a scale to use for Aspect.AspectFill (cropped).
/// </summary>
public static double ScaleThatFills (this Size size, Size max)
{
if (size.Width < 0 || size.Height < 0 || max.Width < 0 || max.Height < 0
|| max.Width.BasicallyZero () || max.Height.BasicallyZero () || size.Width.BasicallyZero () || size.Height.BasicallyZero ()) {
// Something invalid.
return 1;
}

if (size.Width.BasicallyEqual (max.Width) || size.Height.BasicallyEqual (max.Height)) {
// One dimension is "equal"; no scaling needed.
return 1;
}

var aspectRatio = size.Width / size.Height;
if (aspectRatio > 1) {
}
else {
}

var widthScaleRequiredToFit = size.Width > max.Width ? size.Width / max.Width : max.Width / size.Width;
var heightScaleRequiredToFit = size.Height > max.Height ? size.Height / max.Height : max.Height / size.Height;
double fillScale = 1;

if (size.Width > max.Width || size.Height > max.Height) {
// Scale smallest delta to fit its dimension
fillScale = Math.Min (widthScaleRequiredToFit, heightScaleRequiredToFit);
}
else {
// Scale largest delta to fit its dimension
fillScale = Math.Max (widthScaleRequiredToFit, heightScaleRequiredToFit);
}

return fillScale;
}

/// <summary>
/// Corresponds to finding a scale to use for Aspect.AspectFit (letterboxed).
/// </summary>
public static double ScaleThatFits (this Size size, Size max)
{
if (size == max || max.Width == 0 || max.Height == 0 || size.Width == 0 || size.Height == 0) {
if (size.Width < 0 || size.Height < 0 || max.Width < 0 || max.Height < 0
|| size.Width.BasicallyZero () || size.Height.BasicallyZero ()
|| max.Width.BasicallyZero () || max.Height.BasicallyZero ()) {
// Something invalid.
return 1;
}

Expand All @@ -17,23 +73,20 @@ public static double ScaleThatFits (this Size size, Size max)
return fitScale;
}

public static Size ScaleProportional (this Size size, Size max)
{
double fitScale = size.ScaleThatFits (max);
if (fitScale == 1) {
public static Size ScaleProportional(this Size size, Size max) {
double fitScale = size.ScaleThatFits(max);
if (fitScale.BasicallyEqual(1)) {
return size;
}

Size scaledSize = new Size (size.Width * fitScale, size.Height * fitScale);
Size scaledSize = new Size(size.Width * fitScale, size.Height * fitScale);
return scaledSize;
}

public static Size ScaleDownProportional (this Size size, Size max)
{
public static Size ScaleDownProportional(this Size size, Size max) {
if (size.Width <= max.Width && size.Height <= max.Height) {
return size;
}
return size.ScaleProportional (max);
return size.ScaleProportional(max);
}
}
}
31 changes: 27 additions & 4 deletions TwinTechsForms/TwinTechsForms.SvgImage/SvgImage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -246,10 +246,33 @@ public IImageCanvas RenderSvgToCanvas (Size outputSize, double finalScale, Func<
finalCanvas.DrawImage (sliceImage, sliceFramePair.Item2);
}
} else {
// Typical approach to rendering an SVG; just draw it to the canvas.
double proportionalOutputScale = originalSvgSize.ScaleThatFits (outputSize);
// Make sure ViewBox is reset to a proportionally-scaled default in case it was previous set by slicing.
LoadedGraphic.ViewBox = new Rect (Point.Zero, originalSvgSize / proportionalOutputScale);
// Typical approach to rendering an SVG; just draw it to the canvas with a single pass.
if (Aspect == Aspect.Fill) {
var finalSize = outputSize;
LoadedGraphic.Size = finalSize;
// Reset ViewBox to make sure we aren't scaling from any previous use as 9-slice or different Aspect.
LoadedGraphic.ViewBox = new Rect (Point.Zero, originalSvgSize);
}
else if (Aspect == Aspect.AspectFill) {
//(scale beyond + crop)
// [default] scale within + letterbox
double proportionalOutputScale = originalSvgSize.ScaleThatFills (outputSize);
var finalSize = originalSvgSize / proportionalOutputScale;
// Ensure ViewBox set to proportionally-scaling from any previous as 9-slice or different Aspect.
LoadedGraphic.ViewBox = new Rect (Point.Zero, finalSize);
// TODO: center on final size
var finalCanvasCenter = finalCanvas.Size / 2;
var fillCenter = finalSize / 2;
var centerOffset = fillCenter - finalCanvasCenter;
finalCanvas.Transform (Transform.Translate (centerOffset));
}
else { //if (Aspect == Aspect.AspectFit) {
// [default] scale within + letterbox
double proportionalOutputScale = originalSvgSize.ScaleThatFits (outputSize);
var finalSize = originalSvgSize / proportionalOutputScale;
// Ensure ViewBox set to proportionally-scaling from any previous as 9-slice or different Aspect.
LoadedGraphic.ViewBox = new Rect (Point.Zero, finalSize);
}
LoadedGraphic.Draw (finalCanvas);
}
return finalCanvas;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
using NUnit.Framework;
using NGraphics;
using TwinTechs.Extensions;

namespace TwinTechsForms.SvgImage.Tests
{
[TestFixture]
public class ScaleThatFits
{
[Test]
[TestCase(0, 50, 1d)]
[TestCase(50, 0, 1d)]
[TestCase(-5, 50, 1d)]
[TestCase(50, -5, 1d)]
[TestCase(double.Epsilon, 50, 1d)]
[TestCase(50, double.Epsilon, 1d)]
public void VariousInvalids_ReturnsInvalid (double width, double height, double expectedScale)
{
var original = new Size (width, height);
var max = new Size (500, 500);

var result = original.ScaleThatFits (max);

Assert.AreEqual (expectedScale, result);
}

[Test]
[TestCase(100, 50, 50, 50, 0.5d)]
[TestCase(50, 100, 50, 50, 0.5d)]
[TestCase(50, 100, 25, 25, 0.25d)]
[TestCase(100, 50, 25, 25, 0.25d)]
public void SmallerOutput_ScalesDownToLargestOriginal (double originalWidth, double originalHeight, double maxWidth, double maxHeight, double expectedScale)
{
var original = new Size (originalWidth, originalHeight);
var max = new Size (maxWidth, maxHeight);

var result = original.ScaleThatFits (max);

Assert.AreEqual (expectedScale, result);
}

[Test]
[TestCase(100, 50, 200, 200, 2d)]
[TestCase(50, 100, 200, 200, 2d)]
[TestCase(50, 100, 400, 400, 4d)]
[TestCase(100, 50, 400, 400, 4d)]
public void LargerOutput_ScalesUpToLargestOriginal (double originalWidth, double originalHeight, double maxWidth, double maxHeight, double expectedScale)
{
var original = new Size (originalWidth, originalHeight);
var max = new Size (maxWidth, maxHeight);

var result = original.ScaleThatFits (max);

Assert.AreEqual (expectedScale, result);
}
}

[TestFixture]
public class ScaleThatFills
{
[Test]
[TestCase(0, 50, 1d)]
[TestCase(50, 0, 1d)]
[TestCase(-5, 50, 1d)]
[TestCase(50, -5, 1d)]
[TestCase(double.Epsilon, 50, 1d)]
[TestCase(50, double.Epsilon, 1d)]
public void VariousInvalids_ReturnsInvalid (double width, double height, double expectedScale)
{
var original = new Size (width, height);
var max = new Size (500, 500);

var result = original.ScaleThatFills (max);

Assert.AreEqual (expectedScale, result);
}

[Test]
[TestCase(80, 20, 10, 10, 0.5d)]
[TestCase(20, 80, 10, 10, 0.5d)]
[TestCase(40, 80, 5, 5, 0.25d)]
[TestCase(80, 40, 5, 5, 0.25d)]
[TestCase(100, 150, 50, 100, 0.5d)]
public void SmallerOutput_ScalesDownToSmallestOriginal (double originalWidth, double originalHeight, double maxWidth, double maxHeight, double expectedScale)
{
var original = new Size (originalWidth, originalHeight);
var max = new Size (maxWidth, maxHeight);

var result = original.ScaleThatFills (max);

Assert.AreEqual (expectedScale, result);
}

[Test]
[TestCase(100, 150, 200, 200, 2d)]
[TestCase(150, 100, 200, 200, 2d)]
[TestCase(100, 150, 400, 400, 4d)]
[TestCase(150, 100, 400, 400, 4d)]
[TestCase(150, 100, 50, 100, 1d)]
public void LargerOutput_ScalesUpToSmallestOriginal (double originalWidth, double originalHeight, double maxWidth, double maxHeight, double expectedScale)
{
var original = new Size (originalWidth, originalHeight);
var max = new Size (maxWidth, maxHeight);

var result = original.ScaleThatFills (max);

Assert.AreEqual (expectedScale, result);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{A6B1C7CB-D48E-4EF3-A67F-393CC4F323EB}</ProjectGuid>
<OutputType>Library</OutputType>
<RootNamespace>TwinTechsForms.SvgImage.Tests</RootNamespace>
<AssemblyName>TwinTechsForms.SvgImage.Tests</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug</OutputPath>
<DefineConstants>DEBUG;</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<ConsolePause>false</ConsolePause>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>full</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release</OutputPath>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<ConsolePause>false</ConsolePause>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="nunit.framework">
<HintPath>..\..\packages\NUnit.2.6.4\lib\nunit.framework.dll</HintPath>
</Reference>
<Reference Include="NGraphics">
<HintPath>..\..\packages\NGraphics.0.4.0\lib\net45\NGraphics.dll</HintPath>
</Reference>
<Reference Include="NGraphics.Net">
<HintPath>..\..\packages\NGraphics.0.4.0\lib\net45\NGraphics.Net.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="Extensions\SizeExtensions\ScaleThatFits.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<Folder Include="Extensions\" />
<Folder Include="Extensions\SizeExtensions\" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\TwinTechsForms.SvgImage.csproj">
<Project>{3780F071-D298-4861-84DB-3B990987C58D}</Project>
<Name>TwinTechsForms.SvgImage</Name>
</ProjectReference>
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="NGraphics" version="0.4.0" targetFramework="net45" />
<package id="NUnit" version="2.6.4" targetFramework="net45" />
</packages>
15 changes: 15 additions & 0 deletions TwinTechsForms/TwinTechsForms.sln
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TwinTechsForms.SvgImage.iOS
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TwinTechsForms.SvgImage.Droid", "TwinTechsForms.SvgImage\TwinTechsForms.SvgImage.Droid\TwinTechsForms.SvgImage.Droid.csproj", "{AD5C1A1E-2E26-47F6-8A5C-60569ACD808A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TwinTechsForms.SvgImage.Tests", "TwinTechsForms.SvgImage\TwinTechsForms.SvgImage.Tests\TwinTechsForms.SvgImage.Tests.csproj", "{A6B1C7CB-D48E-4EF3-A67F-393CC4F323EB}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "NControl.SvgImageView", "NControl.SvgImageView", "{6698F986-19E8-4C6B-A98E-7B7A2882BDA3}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TwinTechsForms.NControl.SvgImageView", "TwinTechsForms.NControl.SvgImageView\TwinTechsForms.NControl.SvgImageView.csproj", "{05950D26-8F4C-4517-9295-C30655190F99}"
Expand Down Expand Up @@ -157,6 +159,18 @@ Global
{98C72D6A-9016-4881-978D-55C98C743918}.Release|iPhone.Build.0 = Release|Any CPU
{98C72D6A-9016-4881-978D-55C98C743918}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{98C72D6A-9016-4881-978D-55C98C743918}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{A6B1C7CB-D48E-4EF3-A67F-393CC4F323EB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A6B1C7CB-D48E-4EF3-A67F-393CC4F323EB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A6B1C7CB-D48E-4EF3-A67F-393CC4F323EB}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{A6B1C7CB-D48E-4EF3-A67F-393CC4F323EB}.Debug|iPhone.Build.0 = Debug|Any CPU
{A6B1C7CB-D48E-4EF3-A67F-393CC4F323EB}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{A6B1C7CB-D48E-4EF3-A67F-393CC4F323EB}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{A6B1C7CB-D48E-4EF3-A67F-393CC4F323EB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A6B1C7CB-D48E-4EF3-A67F-393CC4F323EB}.Release|Any CPU.Build.0 = Release|Any CPU
{A6B1C7CB-D48E-4EF3-A67F-393CC4F323EB}.Release|iPhone.ActiveCfg = Release|Any CPU
{A6B1C7CB-D48E-4EF3-A67F-393CC4F323EB}.Release|iPhone.Build.0 = Release|Any CPU
{A6B1C7CB-D48E-4EF3-A67F-393CC4F323EB}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{A6B1C7CB-D48E-4EF3-A67F-393CC4F323EB}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{AD5C1A1E-2E26-47F6-8A5C-60569ACD808A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AD5C1A1E-2E26-47F6-8A5C-60569ACD808A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AD5C1A1E-2E26-47F6-8A5C-60569ACD808A}.Debug|iPhone.ActiveCfg = Debug|Any CPU
Expand Down Expand Up @@ -204,6 +218,7 @@ Global
{3780F071-D298-4861-84DB-3B990987C58D} = {31E555E1-5B47-4D05-919B-8BD584F42679}
{603CCCD7-58D1-48E1-8090-68D2FE459273} = {31E555E1-5B47-4D05-919B-8BD584F42679}
{AD5C1A1E-2E26-47F6-8A5C-60569ACD808A} = {31E555E1-5B47-4D05-919B-8BD584F42679}
{A6B1C7CB-D48E-4EF3-A67F-393CC4F323EB} = {31E555E1-5B47-4D05-919B-8BD584F42679}
{05950D26-8F4C-4517-9295-C30655190F99} = {6698F986-19E8-4C6B-A98E-7B7A2882BDA3}
{418F7F98-8509-4210-983E-7DEE1198426A} = {6698F986-19E8-4C6B-A98E-7B7A2882BDA3}
{58B41F20-DAE7-40C2-9021-980EED46857F} = {6698F986-19E8-4C6B-A98E-7B7A2882BDA3}
Expand Down
Loading