Skip to content

Commit eca200c

Browse files
Port ImageCropper (#97)
* Init * Commenting out cursors for now * Fix sample * Fix sample * Sample updates * Xaml styler * Update ImageCropperSample.xaml Fix sample * Updating icon * Remove property table * Fixed async void usage * Reverted nullable logic in UpdateSelectionThumbs * Fixed PackageId --------- Co-authored-by: Arlo <[email protected]>
1 parent 8774760 commit eca200c

29 files changed

+2957
-0
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
@ECHO OFF
2+
3+
powershell ..\..\tooling\ProjectHeads\GenerateSingleSampleHeads.ps1 -componentPath %CD% %*
3.45 KB
Loading
60.2 KB
Loading
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<!--
2+
WinUI 2 under UWP uses TargetFramework uap10.0.*
3+
WinUI 3 under WinAppSdk uses TargetFramework net6.0-windows10.*
4+
However, under Uno-powered platforms, both WinUI 2 and 3 can share the same TargetFramework.
5+
6+
MSBuild doesn't play nicely with this out of the box, so we've made it easy for you.
7+
8+
For .NET Standard packages, you can use the Nuget Package Manager in Visual Studio.
9+
For UWP / WinAppSDK / Uno packages, place the package references here.
10+
-->
11+
<Project>
12+
<!-- WinUI 2 / UWP -->
13+
<ItemGroup Condition="'$(IsUwp)' == 'true'">
14+
<!-- <PackageReference Include="Microsoft.Toolkit.Uwp.UI.Controls.Primitives" Version="7.1.2"/> -->
15+
</ItemGroup>
16+
17+
<!-- WinUI 2 / Uno -->
18+
<ItemGroup Condition="'$(IsUno)' == 'true' AND '$(WinUIMajorVersion)' == '2'">
19+
<!-- <PackageReference Include="Uno.Microsoft.Toolkit.Uwp.UI.Controls.Primitives" Version="7.1.11"/> -->
20+
</ItemGroup>
21+
22+
<!-- WinUI 3 / WinAppSdk -->
23+
<ItemGroup Condition="'$(IsWinAppSdk)' == 'true'">
24+
<!-- <PackageReference Include="CommunityToolkit.WinUI.UI.Controls.Primitives" Version="7.1.2"/> -->
25+
</ItemGroup>
26+
27+
<!-- WinUI 3 / Uno -->
28+
<ItemGroup Condition="'$(IsUno)' == 'true' AND '$(WinUIMajorVersion)' == '3'">
29+
<!-- <PackageReference Include="Uno.CommunityToolkit.WinUI.UI.Controls.Primitives" Version="7.1.100-dev.15.g12261e2626"/> -->
30+
</ItemGroup>
31+
</Project>
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<Project Sdk="MSBuild.Sdk.Extras/3.0.23">
2+
<PropertyGroup>
3+
<ToolkitComponentName>ImageCropper</ToolkitComponentName>
4+
</PropertyGroup>
5+
6+
<!-- Sets this up as a toolkit component's sample project -->
7+
<Import Project="$(ToolingDirectory)\ToolkitComponent.SampleProject.props" />
8+
<ItemGroup>
9+
<None Remove="Assets\Owl.jpg" />
10+
</ItemGroup>
11+
<ItemGroup>
12+
<Content Include="Assets\Owl.jpg">
13+
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
14+
</Content>
15+
</ItemGroup>
16+
</Project>
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
---
2+
title: ImageCropper
3+
author: HHChaos
4+
description: Control to crop rectangular and circular images.
5+
keywords: ImageCropper, Control, Layout
6+
dev_langs:
7+
- csharp
8+
category: Controls
9+
subcategory: Input
10+
discussion-id: 0
11+
issue-id: 0
12+
icon: Assets/ImageCropper.png
13+
---
14+
15+
# ImageCropper
16+
17+
The [ImageCropper Control](/dotnet/api/microsoft.toolkit.uwp.ui.controls.imagecropper) allows user to freely crop an image.
18+
19+
> [!Sample ImageCropperSample]
20+
21+
## Syntax
22+
23+
```xaml
24+
<Page ...
25+
xmlns:controls="using:CommunityToolkit.WinUI.Controls"/>
26+
<controls:ImageCropper x:Name="ImageCropper" />
27+
</Page>
28+
```
29+
30+
## Examples
31+
32+
### Use ImageCropper
33+
34+
You can set the cropped image source by using the `LoadImageFromFile(StorageFile)` method or setting the `Source` property.
35+
36+
```csharp
37+
//Load an image.
38+
await ImageCropper.LoadImageFromFile(file);
39+
40+
//Another way to load an image.
41+
ImageCropper.Source = writeableBitmap;
42+
43+
//Saves the cropped image to a stream.
44+
using (var fileStream = await someFile.OpenAsync(FileAccessMode.ReadWrite, StorageOpenOptions.None))
45+
{
46+
await _imageCropper.SaveAsync(fileStream, BitmapFileFormat.Png);
47+
}
48+
```
49+
50+
51+
### Use Circular ImageCropper
52+
53+
You can set `CropShape` property to use the circular ImageCropper.
54+
55+
```csharp
56+
ImageCropper.CropShape = CropShape.Circular;
57+
```
58+
59+
60+
### Change Aspect Ratio
61+
62+
You can set `AspectRatio` property to change the aspect ratio of the cropped image.
63+
64+
```csharp
65+
ImageCropper.AspectRatio = 16d / 9d;
66+
```
67+
68+
69+
Or you can crop image without aspect ratio.
70+
71+
```csharp
72+
ImageCropper.AspectRatio = null;
73+
```
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<!-- Licensed to the .NET Foundation under one or more agreements. The .NET Foundation licenses this file to you under the MIT license. See the LICENSE file in the project root for more information. -->
2+
<Page x:Class="ImageCropperExperiment.Samples.ImageCropperSample"
3+
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
4+
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
5+
xmlns:controls="using:CommunityToolkit.WinUI.Controls"
6+
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
7+
xmlns:local="using:ImageCropperExperiment.Samples"
8+
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
9+
mc:Ignorable="d">
10+
<Grid RowSpacing="24">
11+
<Grid.RowDefinitions>
12+
<RowDefinition Height="Auto" />
13+
<RowDefinition Height="Auto" />
14+
</Grid.RowDefinitions>
15+
<controls:ImageCropper x:Name="imageCropper"
16+
Width="520"
17+
Height="520"
18+
AspectRatio="{x:Bind local:ImageCropperSample.ConvertStringToAspectRatio(AspectRatioSetting), Mode=OneWay}"
19+
CropShape="{x:Bind local:ImageCropperSample.ConvertStringToCropShape(CropShapeSetting), Mode=OneWay}"
20+
ThumbPlacement="{x:Bind local:ImageCropperSample.ConvertStringToThumbPlacement(ThumbPlacementSetting), Mode=OneWay}" />
21+
<StackPanel Grid.Row="1"
22+
HorizontalAlignment="Center"
23+
Orientation="Horizontal"
24+
Spacing="8">
25+
<Button Click="PickButton_Click"
26+
Content="Pick image"
27+
Style="{StaticResource AccentButtonStyle}" />
28+
<Button Click="SaveButton_Click"
29+
Content="Save"
30+
Style="{StaticResource AccentButtonStyle}" />
31+
<Button Click="ResetButton_Click"
32+
Content="Reset"
33+
Style="{StaticResource AccentButtonStyle}" />
34+
</StackPanel>
35+
</Grid>
36+
</Page>
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using CommunityToolkit.WinUI.Controls;
6+
using Windows.Storage;
7+
using Windows.Storage.Pickers;
8+
9+
namespace ImageCropperExperiment.Samples;
10+
11+
[ToolkitSampleMultiChoiceOption("ThumbPlacementSetting", "All", "Corners", Title = "Thumb Placement")]
12+
[ToolkitSampleMultiChoiceOption("CropShapeSetting", "Rectangular", "Circular", Title = "Crop Shape")]
13+
[ToolkitSampleMultiChoiceOption("AspectRatioSetting", "Custom", "Square", "Landscape(16:9)", "Portrait(9:16)", "4:3", "3:2", Title = "Aspect Ratio")]
14+
15+
[ToolkitSample(id: nameof(ImageCropperSample), "ImageCropper", description: $"A sample for showing how to create and use a {nameof(ImageCropper)}.")]
16+
public sealed partial class ImageCropperSample : Page
17+
{
18+
public ImageCropperSample()
19+
{
20+
this.InitializeComponent();
21+
_ = Load();
22+
}
23+
24+
private async Task Load()
25+
{
26+
var file = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///Assets/Owl.jpg"));
27+
await imageCropper.LoadImageFromFile(file);
28+
}
29+
30+
private async Task PickImage()
31+
{
32+
var filePicker = new FileOpenPicker
33+
{
34+
ViewMode = PickerViewMode.Thumbnail,
35+
SuggestedStartLocation = PickerLocationId.PicturesLibrary,
36+
FileTypeFilter =
37+
{
38+
".png", ".jpg", ".jpeg"
39+
}
40+
};
41+
var file = await filePicker.PickSingleFileAsync();
42+
if (file != null && imageCropper != null)
43+
{
44+
await imageCropper.LoadImageFromFile(file);
45+
}
46+
}
47+
48+
private async Task SaveCroppedImage()
49+
{
50+
var savePicker = new FileSavePicker
51+
{
52+
SuggestedStartLocation = PickerLocationId.PicturesLibrary,
53+
SuggestedFileName = "Cropped_Image",
54+
FileTypeChoices =
55+
{
56+
{ "PNG Picture", new List<string> { ".png" } },
57+
{ "JPEG Picture", new List<string> { ".jpg" } }
58+
}
59+
};
60+
var imageFile = await savePicker.PickSaveFileAsync();
61+
if (imageFile != null)
62+
{
63+
BitmapFileFormat bitmapFileFormat;
64+
switch (imageFile.FileType.ToLower())
65+
{
66+
case ".png":
67+
bitmapFileFormat = BitmapFileFormat.Png;
68+
break;
69+
case ".jpg":
70+
bitmapFileFormat = BitmapFileFormat.Jpeg;
71+
break;
72+
default:
73+
bitmapFileFormat = BitmapFileFormat.Png;
74+
break;
75+
}
76+
77+
using (var fileStream = await imageFile.OpenAsync(FileAccessMode.ReadWrite, StorageOpenOptions.None))
78+
{
79+
await imageCropper.SaveAsync(fileStream, bitmapFileFormat);
80+
}
81+
}
82+
}
83+
84+
public static ThumbPlacement ConvertStringToThumbPlacement(string placement) => placement switch
85+
{
86+
"All" => ThumbPlacement.All,
87+
"Corners" => ThumbPlacement.Corners,
88+
_ => throw new System.NotImplementedException(),
89+
};
90+
91+
92+
public static CropShape ConvertStringToCropShape(string cropshape) => cropshape switch
93+
{
94+
"Circular" => CropShape.Circular,
95+
"Rectangular" => CropShape.Rectangular,
96+
_ => throw new System.NotImplementedException(),
97+
};
98+
99+
public static double? ConvertStringToAspectRatio(string ratio) => ratio switch
100+
{
101+
"Custom" => null,
102+
"Square" => 1,
103+
"Landscape(16:9)" => 16d / 9d,
104+
"Portrait(9:16)" => 9d / 16d,
105+
"4:3" => 4d / 3d,
106+
"3:2" => 3d / 2d,
107+
_ => throw new System.NotImplementedException(),
108+
};
109+
110+
private async void PickButton_Click(object sender, RoutedEventArgs e)
111+
{
112+
await PickImage();
113+
}
114+
115+
private async void SaveButton_Click(object sender, RoutedEventArgs e)
116+
{
117+
await SaveCroppedImage();
118+
}
119+
120+
private void ResetButton_Click(object sender, RoutedEventArgs e)
121+
{
122+
imageCropper.Reset();
123+
}
124+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using System.Runtime.CompilerServices;
6+
7+
// These `InternalsVisibleTo` calls are intended to make it easier for
8+
// for any internal code to be testable in all the different test projects
9+
// used with the Labs infrastructure.
10+
[assembly: InternalsVisibleTo("ImageCropper.Tests.Uwp")]
11+
[assembly: InternalsVisibleTo("ImageCropper.Tests.WinAppSdk")]
12+
[assembly: InternalsVisibleTo("CommunityToolkit.Tests.Uwp")]
13+
[assembly: InternalsVisibleTo("CommunityToolkit.Tests.WinAppSdk")]
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
namespace CommunityToolkit.WinUI.Controls;
6+
7+
/// <summary>
8+
/// This denotes the format used when saving a bitmap to a file.
9+
/// </summary>
10+
public enum BitmapFileFormat
11+
{
12+
/// <summary>
13+
/// Indicates Windows Imaging Component's bitmap encoder.
14+
/// </summary>
15+
Bmp,
16+
17+
/// <summary>
18+
/// Indicates Windows Imaging Component's PNG encoder.
19+
/// </summary>
20+
Png,
21+
22+
/// <summary>
23+
/// Indicates Windows Imaging Component's bitmap JPEG encoder.
24+
/// </summary>
25+
Jpeg,
26+
27+
/// <summary>
28+
/// Indicates Windows Imaging Component's TIFF encoder.
29+
/// </summary>
30+
Tiff,
31+
32+
/// <summary>
33+
/// Indicates Windows Imaging Component's GIF encoder.
34+
/// </summary>
35+
Gif,
36+
37+
/// <summary>
38+
/// Indicates Windows Imaging Component's JPEGXR encoder.
39+
/// </summary>
40+
JpegXR
41+
}

0 commit comments

Comments
 (0)