Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhance DrawingView and DrawingViewService to provide the ability to export the full image size #2193

Open
wants to merge 20 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
68a712a
Initial attempt at exposing the ability to export the full image size
bijington Sep 10, 2024
149880c
Merge branch 'main' into feature/sl-1397-drawing-view-full-image
VladislavAntonyuk Sep 10, 2024
b6b115a
Merge branch 'main' into feature/sl-1397-drawing-view-full-image
VladislavAntonyuk Sep 10, 2024
1819681
Opt for overload methods rather than parameter defaults to avoid brea…
bijington Sep 10, 2024
4e5312c
Merge branch 'feature/sl-1397-drawing-view-full-image' of github.com:…
bijington Sep 10, 2024
e33ca70
Android sizing change
bijington Sep 10, 2024
2b1894d
Windows implementation
bijington Sep 10, 2024
309268e
Tizen support
bijington Sep 10, 2024
f466774
Attempt to fix the unknowns
bijington Sep 10, 2024
fa5ee83
More fixes
bijington Sep 10, 2024
532c493
One more time
bijington Sep 10, 2024
c7c7d30
Enable selection of the output option in the sample
bijington Sep 10, 2024
856a987
Move options enum out to a separate file
bijington Sep 10, 2024
6db16ef
Merge branch 'main' into feature/sl-1397-drawing-view-full-image
bijington Sep 10, 2024
4c8cfbd
Merge branch 'main' into feature/sl-1397-drawing-view-full-image
bijington Sep 11, 2024
3993369
Merge branch 'main' into feature/sl-1397-drawing-view-full-image
bijington Sep 12, 2024
06589a0
Merge branch 'main' into feature/sl-1397-drawing-view-full-image
bijington Sep 13, 2024
3671aaf
Merge branch 'main' into feature/sl-1397-drawing-view-full-image
bijington Sep 23, 2024
91519ab
Tidy up merge
bijington Sep 23, 2024
d1857f8
Merge branch 'main' into feature/sl-1397-drawing-view-full-image
bijington Oct 1, 2024
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
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
Title="DrawingView">

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

<Button
<Label
Grid.Row="7" Grid.Column="0"
Text="Output" />
<Picker
Grid.Row="7" Grid.Column="1"
ItemsSource="{Binding AvailableOutputOptions}"
SelectedItem="{Binding SelectedOutputOption}" />

<Button
Grid.Row="8" Grid.Column="0"
Grid.ColumnSpan="2"
BackgroundColor="White"
Command="{Binding SaveCommand, Mode=OneTime}"
Text="Save image"
TextColor="Black" />

<Label Text="DrawingView"
Grid.Row="8" Grid.Column="0"
Grid.Row="9" Grid.Column="0"
Grid.ColumnSpan="2"/>
<mct:DrawingView x:Name="DrawingViewControl"
Margin="0,0,0,10"
Grid.Row="9" Grid.Column="0"
Grid.Row="10" Grid.Column="0"
Grid.ColumnSpan="2"
LineColor="Green"
LineWidth="5"
Expand All @@ -76,7 +84,9 @@
DrawingLineCancelledCommand="{Binding DrawingLineCancelledCommand, Mode=OneTime}"
PointDrawnCommand="{Binding PointDrawnCommand, Mode=OneTime}"
ShouldClearOnFinish="{Binding Source={x:Reference ClearOnFinish}, Path=IsToggled}"
IsMultiLineModeEnabled="{Binding Source={x:Reference MultiLineMode}, Path=IsToggled}">
IsMultiLineModeEnabled="{Binding Source={x:Reference MultiLineMode}, Path=IsToggled}"
Height="{Binding CanvasHeight}"
Width="{Binding CanvasWidth}">
<mct:DrawingView.Background>
<LinearGradientBrush StartPoint="0,0"
EndPoint="0,1">
Expand All @@ -89,12 +99,12 @@
</mct:DrawingView>

<Image
Grid.Row="10" Grid.Column="0"
Grid.Row="11" Grid.Column="0"
Grid.ColumnSpan="2"
x:Name="GestureImage"
Margin="0,0,0,10"/>

<Label Grid.Row="11" Grid.Column="0"
<Label Grid.Row="12" Grid.Column="0"
Grid.ColumnSpan="2"
BackgroundColor="Gray"
TextColor="Black"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ void LoadPointsButtonClicked(object sender, EventArgs e)
async void GetCurrentDrawingViewImageClicked(object sender, EventArgs e)
{
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
var stream = await DrawingViewControl.GetImageStream(GestureImage.Width, GestureImage.Height, cts.Token);
var stream = await DrawingViewControl.GetImageStream(GestureImage.Width, GestureImage.Height, DrawingViewOutputOption.Lines, cts.Token);

GestureImage.Source = ImageSource.FromStream(() => stream);
}
Expand All @@ -51,6 +51,7 @@ async Task DrawImage(IEnumerable<DrawingLine> lines, CancellationToken token)
var stream = await DrawingView.GetImageStream(drawingLines,
new Size(points.Max(x => x.X) - points.Min(x => x.X), points.Max(x => x.Y) - points.Min(x => x.Y)),
Colors.Gray,
this.DrawingViewControl.Bounds.Size,
token);

GestureImage.Source = ImageSource.FromStream(() => stream);
Expand Down Expand Up @@ -79,7 +80,7 @@ async void OnDrawingLineCompleted(object sender, DrawingLineCompletedEventArgs e
var height = GetSide(GestureImage.Height);

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

GestureImage.Source = ImageSource.FromStream(() => stream);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,15 @@ public partial class DrawingViewViewModel : BaseViewModel
[ObservableProperty]
string logs = string.Empty;

[ObservableProperty]
DrawingViewOutputOption selectedOutputOption = DrawingViewOutputOption.Lines;

public List<DrawingViewOutputOption> AvailableOutputOptions { get; } = [DrawingViewOutputOption.Lines, DrawingViewOutputOption.FullCanvas];

public double CanvasHeight { get; set; }

public double CanvasWidth { get; set; }

public DrawingViewViewModel(IFileSaver fileSaver)
{
this.fileSaver = fileSaver;
Expand Down Expand Up @@ -72,7 +81,13 @@ async Task Save(CancellationToken cancellationToken)
{
try
{
await using var stream = await DrawingView.GetImageStream(Lines, new Size(1920, 1080), Brush.Blue, cancellationToken);
await using var stream = await DrawingView.GetImageStream(
Lines,
new Size(1920, 1080),
Brush.Blue,
// If the user wants to output the full canvas then we need to pass in the canvas dimensions, otherwise null will use the bounds of the current drawing.
SelectedOutputOption == DrawingViewOutputOption.FullCanvas ? new Size(CanvasWidth, CanvasHeight) : null,
cancellationToken);

await Permissions.RequestAsync<Permissions.StorageRead>().WaitAsync(cancellationToken);
await Permissions.RequestAsync<Permissions.StorageWrite>().WaitAsync(cancellationToken);
Expand Down
22 changes: 19 additions & 3 deletions src/CommunityToolkit.Maui.Core/Interfaces/IDrawingLine.shared.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,26 @@ public interface IDrawingLine
/// <summary>
/// Retrieves a <see cref="Stream"/> containing an image of this line, based on the <see cref="Points"/> data.
/// </summary>
/// <param name="imageSizeWidth">Desired width of the image that is returned.</param>
/// <param name="imageSizeHeight">Desired height of the image that is returned.</param>
/// <param name="desiredSizeWidth">Desired width of the image that is returned.</param>
/// <param name="desiredSizeHeight">Desired height of the image that is returned.</param>
/// <param name="background">Background of the generated image.</param>
/// <param name="token"> <see cref="CancellationToken"/>.</param>
/// <returns><see cref="ValueTask{Stream}"/> containing the data of the requested image with data that's currently on the <see cref="IDrawingLine"/>.</returns>
ValueTask<Stream> GetImageStream(double desiredSizeWidth, double desiredSizeHeight, Paint background, CancellationToken token = default) =>
GetImageStream(desiredSizeWidth, desiredSizeHeight, background, null, token);

/// <summary>
/// Retrieves a <see cref="Stream"/> containing an image of this line, based on the <see cref="Points"/> data.
/// </summary>
/// <param name="desiredSizeWidth">Desired width of the image that is returned.</param>
/// <param name="desiredSizeHeight">Desired height of the image that is returned.</param>
/// <param name="background">Background of the generated image.</param>
/// <param name="canvasSize">
/// The actual size of the canvas being displayed. This is an optional parameter
/// if a value is provided then the contents of this line inside these dimensions will be included in the output,
/// 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.
/// </param>
/// <param name="token"> <see cref="CancellationToken"/>.</param>
ValueTask<Stream> GetImageStream(double imageSizeWidth, double imageSizeHeight, Paint background, CancellationToken token = default);
/// <returns><see cref="ValueTask{Stream}"/> containing the data of the requested image with data that's currently on the <see cref="IDrawingLine"/>.</returns>
ValueTask<Stream> GetImageStream(double desiredSizeWidth, double desiredSizeHeight, Paint background, Size? canvasSize = null, CancellationToken token = default);
}
16 changes: 13 additions & 3 deletions src/CommunityToolkit.Maui.Core/Interfaces/IDrawingView.shared.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,21 @@ public interface IDrawingView : IView
/// <summary>
/// Retrieves a <see cref="Stream"/> containing an image of the <see cref="Lines"/> that are currently drawn on the <see cref="IDrawingView"/>.
/// </summary>
/// <param name="imageSizeWidth">Desired width of the image that is returned. The image will be resized proportionally.</param>
/// <param name="imageSizeHeight">Desired height of the image that is returned. The image will be resized proportionally.</param>
/// <param name="desiredWidth">Desired width of the image that is returned. The image will be resized proportionally.</param>
/// <param name="desiredHeight">Desired height of the image that is returned. The image will be resized proportionally.</param>
/// <param name="token"> <see cref="CancellationToken"/>.</param>
/// <returns><see cref="Task{Stream}"/> containing the data of the requested image with data that's currently on the <see cref="IDrawingView"/>.</returns>
ValueTask<Stream> GetImageStream(double imageSizeWidth, double imageSizeHeight, CancellationToken token = default);
ValueTask<Stream> GetImageStream(double desiredWidth, double desiredHeight, CancellationToken token = default) => GetImageStream(desiredWidth, desiredHeight, DrawingViewOutputOption.Lines, token);

/// <summary>
/// Retrieves a <see cref="Stream"/> containing an image of the <see cref="Lines"/> that are currently drawn on the <see cref="IDrawingView"/>.
/// </summary>
/// <param name="desiredWidth">Desired width of the image that is returned. The image will be resized proportionally.</param>
/// <param name="desiredHeight">Desired height of the image that is returned. The image will be resized proportionally.</param>
/// <param name="imageOutputOption">The <see cref="DrawingViewOutputOption"/> to determine the bounds and the contents of the resulting image.</param>
/// <param name="token"> <see cref="CancellationToken"/>.</param>
/// <returns><see cref="Task{Stream}"/> containing the data of the requested image with data that's currently on the <see cref="IDrawingView"/>.</returns>
ValueTask<Stream> GetImageStream(double desiredWidth, double desiredHeight, DrawingViewOutputOption imageOutputOption, CancellationToken token = default);

/// <summary>
/// Clears the <see cref="Lines"/> that are currently drawn on the <see cref="IDrawingView"/>.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
namespace CommunityToolkit.Maui.Core;

/// <summary>
/// Enumeration of the options available when generating an image stream using the DrawingView.
/// </summary>
public enum DrawingViewOutputOption
{
/// <summary>
/// Outputs the area covered by the top-left to the bottom-right most points.
/// </summary>
Lines,

/// <summary>
/// Outputs the full area displayed within the drawing view.
/// </summary>
FullCanvas
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,32 +42,47 @@ public int Granularity
/// Retrieves a <see cref="Stream"/> containing an image of the collection of <see cref="Point"/> that is provided as a parameter.
/// </summary>
/// <param name="points">A collection of <see cref="Point"/> that a image is generated from.</param>
/// <param name="imageSize">The desired dimensions of the generated image.</param>
/// <param name="desiredSize">The desired dimensions of the generated image.</param>
/// <param name="lineWidth">The desired line width to be used in the generated image.</param>
/// <param name="strokeColor">The desired color of the line to be used in the generated image.</param>
/// <param name="background">Background of the generated image.</param>
/// <param name="token"><see cref="CancellationToken"/> </param>
/// <returns><see cref="ValueTask{Stream}"/> containing the data of the requested image with data that's provided through the <paramref name="points"/> parameter.</returns>
public static ValueTask<Stream> GetImageStream(IEnumerable<PointF> points,
Size imageSize,
float lineWidth,
Color strokeColor,
Paint background,
CancellationToken token = default)
{
return DrawingViewService.GetImageStream(points.ToList(), imageSize, lineWidth, strokeColor, background, token);
}

public static ValueTask<Stream> GetImageStream(
IEnumerable<PointF> points,
Size desiredSize,
float lineWidth,
Color strokeColor,
Paint background,
CancellationToken token = default) =>
GetImageStream(points, desiredSize, lineWidth, strokeColor, background, null, token);

/// <summary>
/// Retrieves a <see cref="Stream"/> containing an image of this line, based on the <see cref="Points"/> data.
/// Retrieves a <see cref="Stream"/> containing an image of the collection of <see cref="Point"/> that is provided as a parameter.
/// </summary>
/// <param name="imageSizeWidth">Desired width of the image that is returned.</param>
/// <param name="imageSizeHeight">Desired height of the image that is returned.</param>
/// <param name="points">A collection of <see cref="Point"/> that a image is generated from.</param>
/// <param name="desiredSize">The desired dimensions of the generated image.</param>
/// <param name="lineWidth">The desired line width to be used in the generated image.</param>
/// <param name="strokeColor">The desired color of the line to be used in the generated image.</param>
/// <param name="background">Background of the generated image.</param>
/// <param name="canvasSize">
/// The actual size of the canvas being displayed. This is an optional parameter
/// if a value is provided then the contents of the <paramref name="points"/> inside these dimensions will be included in the output,
/// 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.
/// </param>
/// <param name="token"><see cref="CancellationToken"/> </param>
/// <returns><see cref="ValueTask{Stream}"/> containing the data of the requested image with data that's currently on the <see cref="IDrawingView"/>.</returns>
public ValueTask<Stream> GetImageStream(double imageSizeWidth, double imageSizeHeight, Paint background, CancellationToken token = default)
{
return DrawingViewService.GetImageStream([.. Points], new Size(imageSizeWidth, imageSizeHeight), LineWidth, LineColor, background, token);
}
/// <returns><see cref="ValueTask{Stream}"/> containing the data of the requested image with data that's provided through the <paramref name="points"/> parameter.</returns>
public static ValueTask<Stream> GetImageStream(
IEnumerable<PointF> points,
Size desiredSize,
float lineWidth,
Color strokeColor,
Paint background,
Size? canvasSize = null,
CancellationToken token = default) =>
DrawingViewService.GetImageStream([.. points], desiredSize, lineWidth, strokeColor, background, canvasSize, token);

/// <inheritdoc cref="IDrawingLine.GetImageStream(double, double, Paint, Size?, CancellationToken)"/>
public ValueTask<Stream> GetImageStream(double desiredSizeWidth, double desiredSizeHeight, Paint background, Size? canvasSize = null, CancellationToken token = default) =>
DrawingViewService.GetImageStream([.. Points], new Size(desiredSizeWidth, desiredSizeHeight), LineWidth, LineColor, background, canvasSize, token);
}
Loading