Skip to content

Commit 4c2ce41

Browse files
bijingtonVladislavAntonyukne0rrmatrix
authored
Enhance DrawingView and DrawingViewService to provide the ability to export the full image size (#2193)
* Initial attempt at exposing the ability to export the full image size * Opt for overload methods rather than parameter defaults to avoid breaking changes * Android sizing change * Windows implementation * Tizen support * Attempt to fix the unknowns * More fixes * One more time * Enable selection of the output option in the sample * Move options enum out to a separate file * Tidy up merge * Refactored to make use of some options classes * Attempt to fix unit tests --------- Co-authored-by: Shaun Lawrence <17139988+bijington@users.noreply.github.com> Co-authored-by: Vladislav Antonyuk <33021114+VladislavAntonyuk@users.noreply.github.com> Co-authored-by: James Crutchley <ne0rmatrix@gmail.com>
1 parent 62f4e2d commit 4c2ce41

File tree

17 files changed

+337
-179
lines changed

17 files changed

+337
-179
lines changed

samples/CommunityToolkit.Maui.Sample/Pages/Views/DrawingViewPage.xaml

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
Title="DrawingView">
1111

1212
<ScrollView>
13-
<Grid RowDefinitions="40,40,40,40,40,40,40,40,40,200,200,100" ColumnDefinitions="*,*" RowSpacing="12" >
13+
<Grid RowDefinitions="40,40,40,40,40,40,40,40,40,40,200,200,100" ColumnDefinitions="*,*" RowSpacing="12" >
1414
<Label Grid.Row="0" Grid.Column="0" Text="Clear on Finish" Margin="0,0,5,0" HorizontalOptions="End" HorizontalTextAlignment="End"/>
1515
<Switch Grid.Row="0" Grid.Column="1" x:Name="ClearOnFinishSwitch" HorizontalOptions="Start" />
1616
<Label Grid.Row="1" Grid.Column="0" Text="Multi-Line Mode" Margin="0,0,5,0" HorizontalOptions="End" HorizontalTextAlignment="End"/>
@@ -52,20 +52,28 @@
5252
Text="Generate Image with Random Lines"
5353
TextColor="Black" />
5454

55-
<Button
55+
<Label
5656
Grid.Row="7" Grid.Column="0"
57+
Text="Output" />
58+
<Picker
59+
Grid.Row="7" Grid.Column="1"
60+
ItemsSource="{Binding AvailableOutputOptions}"
61+
SelectedItem="{Binding SelectedOutputOption}" />
62+
63+
<Button
64+
Grid.Row="8" Grid.Column="0"
5765
Grid.ColumnSpan="2"
5866
BackgroundColor="White"
5967
Command="{Binding SaveCommand, Mode=OneTime}"
6068
Text="Save image"
6169
TextColor="Black" />
6270

6371
<Label Text="DrawingView"
64-
Grid.Row="8" Grid.Column="0"
72+
Grid.Row="9" Grid.Column="0"
6573
Grid.ColumnSpan="2"/>
6674
<mct:DrawingView x:Name="DrawingViewControl"
6775
Margin="0,0,0,10"
68-
Grid.Row="9" Grid.Column="0"
76+
Grid.Row="10" Grid.Column="0"
6977
Grid.ColumnSpan="2"
7078
LineColor="Green"
7179
LineWidth="5"
@@ -75,8 +83,10 @@
7583
DrawingLineStartedCommand="{Binding DrawingLineStartedCommand, Mode=OneTime}"
7684
DrawingLineCancelledCommand="{Binding DrawingLineCancelledCommand, Mode=OneTime}"
7785
PointDrawnCommand="{Binding PointDrawnCommand, Mode=OneTime}"
78-
ShouldClearOnFinish="{Binding Source={x:Reference ClearOnFinishSwitch}, Path=IsToggled, x:DataType=Switch}"
79-
IsMultiLineModeEnabled="{Binding Source={x:Reference MultiLineModeSwitch}, Path=IsToggled, x:DataType=Switch}">
86+
ShouldClearOnFinish="{Binding Source={x:Reference ClearOnFinish}, Path=IsToggled, x:DataType=Switch}"
87+
IsMultiLineModeEnabled="{Binding Source={x:Reference MultiLineMode}, Path=IsToggled, x:DataType=Switch}"
88+
Height="{Binding CanvasHeight}"
89+
Width="{Binding CanvasWidth}">
8090
<mct:DrawingView.Background>
8191
<LinearGradientBrush StartPoint="0,0"
8292
EndPoint="0,1">
@@ -89,12 +99,12 @@
8999
</mct:DrawingView>
90100

91101
<Image
92-
Grid.Row="10" Grid.Column="0"
102+
Grid.Row="11" Grid.Column="0"
93103
Grid.ColumnSpan="2"
94104
x:Name="GestureImage"
95105
Margin="0,0,0,10"/>
96106

97-
<Label Grid.Row="11" Grid.Column="0"
107+
<Label Grid.Row="12" Grid.Column="0"
98108
Grid.ColumnSpan="2"
99109
BackgroundColor="Gray"
100110
TextColor="Black"

samples/CommunityToolkit.Maui.Sample/Pages/Views/DrawingViewPage.xaml.cs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ void LoadPointsButtonClicked(object sender, EventArgs e)
3131
async void GetCurrentDrawingViewImageClicked(object sender, EventArgs e)
3232
{
3333
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
34-
var stream = await DrawingViewControl.GetImageStream(GestureImage.Width, GestureImage.Height, cts.Token);
34+
var stream = await DrawingViewControl.GetImageStream(GestureImage.Width, GestureImage.Height, DrawingViewOutputOption.Lines, cts.Token);
3535

3636
GestureImage.Source = ImageSource.FromStream(() => stream);
3737
}
@@ -48,9 +48,12 @@ async Task DrawImage(IEnumerable<DrawingLine> lines, CancellationToken token)
4848
{
4949
var drawingLines = lines.ToList();
5050
var points = drawingLines.SelectMany(x => x.Points).ToList();
51-
var stream = await DrawingView.GetImageStream(drawingLines,
52-
new Size(points.Max(x => x.X) - points.Min(x => x.X), points.Max(x => x.Y) - points.Min(x => x.Y)),
53-
Colors.Gray,
51+
var stream = await DrawingView.GetImageStream(
52+
ImageLineOptions.FullCanvas(
53+
drawingLines.OfType<IDrawingLine>().ToList(),
54+
new Size(points.Max(x => x.X) - points.Min(x => x.X), points.Max(x => x.Y) - points.Min(x => x.Y)),
55+
new SolidPaint(Colors.Gray),
56+
this.DrawingViewControl.Bounds.Size),
5457
token);
5558

5659
GestureImage.Source = ImageSource.FromStream(() => stream);
@@ -79,7 +82,7 @@ async void OnDrawingLineCompleted(object sender, DrawingLineCompletedEventArgs e
7982
var height = GetSide(GestureImage.Height);
8083

8184
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
82-
var stream = await e.LastDrawingLine.GetImageStream(width, height, Colors.Gray.AsPaint(), cts.Token);
85+
var stream = await e.LastDrawingLine.GetImageStream(width, height, Colors.Gray.AsPaint(), this.DrawingViewControl.Bounds.Size, cts.Token);
8386

8487
GestureImage.Source = ImageSource.FromStream(() => stream);
8588
}

samples/CommunityToolkit.Maui.Sample/ViewModels/Views/DrawingViewViewModel.cs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,15 @@ public partial class DrawingViewViewModel : BaseViewModel
1717
[ObservableProperty]
1818
public partial string Logs { get; private set; } = string.Empty;
1919

20+
[ObservableProperty]
21+
DrawingViewOutputOption selectedOutputOption = DrawingViewOutputOption.Lines;
22+
23+
public List<DrawingViewOutputOption> AvailableOutputOptions { get; } = [DrawingViewOutputOption.Lines, DrawingViewOutputOption.FullCanvas];
24+
25+
public double CanvasHeight { get; set; }
26+
27+
public double CanvasWidth { get; set; }
28+
2029
public DrawingViewViewModel(IFileSaver fileSaver)
2130
{
2231
this.fileSaver = fileSaver;
@@ -72,7 +81,11 @@ async Task Save(CancellationToken cancellationToken)
7281
{
7382
try
7483
{
75-
await using var stream = await DrawingView.GetImageStream(Lines, new Size(1920, 1080), Brush.Blue, cancellationToken);
84+
var options = SelectedOutputOption == DrawingViewOutputOption.Lines
85+
? ImageLineOptions.JustLines(Lines.ToList(), new Size(1920, 1080), Brush.Blue)
86+
: ImageLineOptions.FullCanvas(Lines.ToList(), new Size(1920, 1080), Brush.Blue, new Size(CanvasWidth, CanvasHeight));
87+
88+
await using var stream = await DrawingView.GetImageStream(options, cancellationToken);
7689

7790
await Permissions.RequestAsync<Permissions.StorageRead>().WaitAsync(cancellationToken);
7891
await Permissions.RequestAsync<Permissions.StorageWrite>().WaitAsync(cancellationToken);

src/CommunityToolkit.Maui.Core/Interfaces/IDrawingLine.shared.cs

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,26 @@ public interface IDrawingLine
3535
/// <summary>
3636
/// Retrieves a <see cref="Stream"/> containing an image of this line, based on the <see cref="Points"/> data.
3737
/// </summary>
38-
/// <param name="imageSizeWidth">The desired width of the image that is returned.</param>
39-
/// <param name="imageSizeHeight">Desired height of the image that is returned.</param>
38+
/// <param name="desiredSizeWidth">Desired width of the image that is returned.</param>
39+
/// <param name="desiredSizeHeight">Desired height of the image that is returned.</param>
4040
/// <param name="background">Background of the generated image.</param>
41+
/// <param name="token"> <see cref="CancellationToken"/>.</param>
4142
/// <returns><see cref="ValueTask{Stream}"/> containing the data of the requested image with data that's currently on the <see cref="IDrawingLine"/>.</returns>
43+
ValueTask<Stream> GetImageStream(double desiredSizeWidth, double desiredSizeHeight, Paint background, CancellationToken token = default) =>
44+
GetImageStream(desiredSizeWidth, desiredSizeHeight, background, null, token);
45+
46+
/// <summary>
47+
/// Retrieves a <see cref="Stream"/> containing an image of this line, based on the <see cref="Points"/> data.
48+
/// </summary>
49+
/// <param name="desiredSizeWidth">Desired width of the image that is returned.</param>
50+
/// <param name="desiredSizeHeight">Desired height of the image that is returned.</param>
51+
/// <param name="background">Background of the generated image.</param>
52+
/// <param name="canvasSize">
53+
/// The actual size of the canvas being displayed. This is an optional parameter
54+
/// if a value is provided then the contents of this line inside these dimensions will be included in the output,
55+
/// if <c>null</c> is provided then the resulting output will be the area covered by the top-left to the bottom-right most points.
56+
/// </param>
4257
/// <param name="token"> <see cref="CancellationToken"/>.</param>
43-
ValueTask<Stream> GetImageStream(double imageSizeWidth, double imageSizeHeight, Paint background, CancellationToken token = default);
58+
/// <returns><see cref="ValueTask{Stream}"/> containing the data of the requested image with data that's currently on the <see cref="IDrawingLine"/>.</returns>
59+
ValueTask<Stream> GetImageStream(double desiredSizeWidth, double desiredSizeHeight, Paint background, Size? canvasSize = null, CancellationToken token = default);
4460
}

src/CommunityToolkit.Maui.Core/Interfaces/IDrawingView.shared.cs

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,21 @@ public interface IDrawingView : IView
4343
/// <summary>
4444
/// Retrieves a <see cref="Stream"/> containing an image of the <see cref="Lines"/> that are currently drawn on the <see cref="IDrawingView"/>.
4545
/// </summary>
46-
/// <param name="imageSizeWidth">The desired width of the image that is returned. The image will be resized proportionally.</param>
47-
/// <param name="imageSizeHeight">Desired height of the image that is returned. The image will be resized proportionally.</param>
46+
/// <param name="desiredWidth">Desired width of the image that is returned. The image will be resized proportionally.</param>
47+
/// <param name="desiredHeight">Desired height of the image that is returned. The image will be resized proportionally.</param>
4848
/// <param name="token"> <see cref="CancellationToken"/>.</param>
4949
/// <returns><see cref="Task{Stream}"/> containing the data of the requested image with data that's currently on the <see cref="IDrawingView"/>.</returns>
50-
ValueTask<Stream> GetImageStream(double imageSizeWidth, double imageSizeHeight, CancellationToken token = default);
50+
ValueTask<Stream> GetImageStream(double desiredWidth, double desiredHeight, CancellationToken token = default) => GetImageStream(desiredWidth, desiredHeight, DrawingViewOutputOption.Lines, token);
51+
52+
/// <summary>
53+
/// Retrieves a <see cref="Stream"/> containing an image of the <see cref="Lines"/> that are currently drawn on the <see cref="IDrawingView"/>.
54+
/// </summary>
55+
/// <param name="desiredWidth">Desired width of the image that is returned. The image will be resized proportionally.</param>
56+
/// <param name="desiredHeight">Desired height of the image that is returned. The image will be resized proportionally.</param>
57+
/// <param name="imageOutputOption">The <see cref="DrawingViewOutputOption"/> to determine the bounds and the contents of the resulting image.</param>
58+
/// <param name="token"> <see cref="CancellationToken"/>.</param>
59+
/// <returns><see cref="Task{Stream}"/> containing the data of the requested image with data that's currently on the <see cref="IDrawingView"/>.</returns>
60+
ValueTask<Stream> GetImageStream(double desiredWidth, double desiredHeight, DrawingViewOutputOption imageOutputOption, CancellationToken token = default);
5161

5262
/// <summary>
5363
/// Clears the <see cref="Lines"/> that are currently drawn on the <see cref="IDrawingView"/>.
@@ -76,4 +86,4 @@ public interface IDrawingView : IView
7686
/// </summary>
7787
/// <param name="lastDrawingLine">Last drawing line</param>
7888
void OnDrawingLineCompleted(IDrawingLine lastDrawingLine);
79-
}
89+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
namespace CommunityToolkit.Maui.Core;
2+
3+
/// <summary>
4+
/// Enumeration of the options available when generating an image stream using the DrawingView.
5+
/// </summary>
6+
public enum DrawingViewOutputOption
7+
{
8+
/// <summary>
9+
/// Outputs the area covered by the top-left to the bottom-right most points.
10+
/// </summary>
11+
Lines,
12+
13+
/// <summary>
14+
/// Outputs the full area displayed within the drawing view.
15+
/// </summary>
16+
FullCanvas
17+
}

src/CommunityToolkit.Maui.Core/Views/DrawingView/DrawingLine.shared.cs

Lines changed: 10 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -39,33 +39,15 @@ public int Granularity
3939
/// <summary>
4040
/// Retrieves a <see cref="Stream"/> containing an image of the collection of <see cref="Point"/> that is provided as a parameter.
4141
/// </summary>
42-
/// <param name="points">A collection of <see cref="Point"/> that an image is generated from.</param>
43-
/// <param name="imageSize">The desired dimensions of the generated image.</param>
44-
/// <param name="lineWidth">The desired line width to be used in the generated image.</param>
45-
/// <param name="strokeColor">The desired color of the line to be used in the generated image.</param>
46-
/// <param name="background">Background of the generated image.</param>
42+
/// <param name="options">The options controlling how the resulting image is generated.</param>
4743
/// <param name="token"><see cref="CancellationToken"/> </param>
48-
/// <returns><see cref="ValueTask{Stream}"/> containing the data of the requested image with data that's provided through the <paramref name="points"/> parameter.</returns>
49-
public static ValueTask<Stream> GetImageStream(IEnumerable<PointF> points,
50-
Size imageSize,
51-
float lineWidth,
52-
Color strokeColor,
53-
Paint background,
54-
CancellationToken token = default)
55-
{
56-
return DrawingViewService.GetImageStream(points.ToList(), imageSize, lineWidth, strokeColor, background, token);
57-
}
58-
59-
/// <summary>
60-
/// Retrieves a <see cref="Stream"/> containing an image of this line, based on the <see cref="Points"/> data.
61-
/// </summary>
62-
/// <param name="imageSizeWidth">The desired width of the image that is returned.</param>
63-
/// <param name="imageSizeHeight">Desired height of the image that is returned.</param>
64-
/// <param name="background">Background of the generated image.</param>
65-
/// <param name="token"><see cref="CancellationToken"/> </param>
66-
/// <returns><see cref="ValueTask{Stream}"/> containing the data of the requested image with data that's currently on the <see cref="IDrawingView"/>.</returns>
67-
public ValueTask<Stream> GetImageStream(double imageSizeWidth, double imageSizeHeight, Paint background, CancellationToken token = default)
68-
{
69-
return DrawingViewService.GetImageStream([.. Points], new Size(imageSizeWidth, imageSizeHeight), LineWidth, LineColor, background, token);
70-
}
44+
/// <returns><see cref="ValueTask{Stream}"/> containing the data of the requested image with data that's provided through the <paramref name="options"/> parameter.</returns>
45+
public static ValueTask<Stream> GetImageStream(
46+
ImagePointOptions options,
47+
CancellationToken token = default) =>
48+
DrawingViewService.GetImageStream(options, token);
49+
50+
/// <inheritdoc cref="IDrawingLine.GetImageStream(double, double, Paint, Size?, CancellationToken)"/>
51+
public ValueTask<Stream> GetImageStream(double desiredSizeWidth, double desiredSizeHeight, Paint background, Size? canvasSize = null, CancellationToken token = default) =>
52+
DrawingViewService.GetImageStream(new ImagePointOptions([.. Points], new Size(desiredSizeWidth, desiredSizeHeight), LineWidth, LineColor, background, canvasSize), token);
7153
}

0 commit comments

Comments
 (0)