Repository navigation
Implement Bitmap.Save with a format - #21455
Conversation
|
You can test this PR using the following package version. |
|
I find it confusing to have a quality parameter that only works with JPEG, in my opinion. BitmapEncoderOptions or similar would be the better solution here. Plus, this will still work in the future when we have our own encoder/decoder infrastructure. |
|
I also vote for EncoderOptions. The base class could could have static fields for PNG, BMP, JPG... |
|
Note that WPF goes an even more powerful route: https://learn.microsoft.com/en-us/dotnet/desktop/wpf/graphics-multimedia/how-to-encode-a-visual-to-an-image-file There are specific encoders for bitmap formats. This keeps the options and format logic all self-contained. IMO we should follow the WPF API entirely here... at least long-term. It is better designed IMO. |
Note that, counter-intuitively, it works with PNG, in fact it was specifically added for it in #9106. Skia uses it as a reverse compression level. 100 = fastest, lower that number to try more compression algorithms (slower). |
The goal of this PR is not to introduce our own encoders; we're still completely dependent on the underlying backend (Skia) here. Having our own encoders is probably something that should happen in the future, but it isn't the goal here. That said, the consensus seems to be to at least have our own options class per format, which could be reused in an encoder infrastructure later. I'll make the changes. |
a3abc65 to
25fcb7d
Compare
|
I've rewritten the PR with proper |
Note that you could still follow the WPF API shape and implement it as a wrapper around the SKIA backend. I never said we should write our own encoders -- just expose the encoder + options together like WPF does. This future proofs the design and follows existing conventions in the XAML space. |
|
You can test this PR using the following package version. |
| public abstract class BitmapEncoderOptions | ||
| { | ||
| internal BitmapEncoderOptions() | ||
| { | ||
| } | ||
| } |
There was a problem hiding this comment.
Alternative API I can think of is:
public abstract class BitmapEncoderOptions
{
public static BitmapEncoderOptions Jpeg { get; }
public static BitmapEncoderOptions Png { get; }
public static BitmapEncoderOptions JpegWithQuality(int quality);
public static BitmapEncoderOptions PngWithCompression(CompressionLevel compressionLevel);
}
Specific implementation might or might not remain public.
I don't think this is a better API, but I previously thought of a similar one, and we can discuss it later.
There was a problem hiding this comment.
I don't really like the methods: it means that to add new options, we would need new overloads. This can become a mess after several iterations (since we can't add parameters, it's a breaking change), whereas with the types we can just add new properties as needed.
# Conflicts: # api/Avalonia.nupkg.xml # src/Avalonia.X11/Clipboard/X11Clipboard.cs
|
You can test this PR using the following package version. |
# Conflicts: # api/Avalonia.nupkg.xml
|
You can test this PR using the following package version. |
What does the pull request do?
This PR implements new overloads for
Bitmap.Save()that accept bitmap options (PNG/JPEG).What is the current behavior?
Bitmap.Save()always saves using PNG.What is the updated/expected behavior with this PR?
The user can pass desired options to save the bitmap in PNG and JPEG formats.
Notes
PNG, and JPEG options are exposed as those are the most commonly used. PNG options provide a compression level and JPEG options a quality.
I've kept only a single
Saveoverload onIBitmapandIBitmapImpl(private APIs) to avoid bloating the interfaces. The public API onBitmaphas 4 overloads. The old overloads forcing PNG have been markedObsolete.API diff
Avalonia.Base (net10.0, net8.0)
namespace Avalonia.Media.Imaging { public class Bitmap : IImage, IImageBrushSource { + public void Save(Stream stream, BitmapEncoderOptions options); + public void Save(string fileName, BitmapEncoderOptions options); } + public abstract class BitmapEncoderOptions + { + } + public sealed class JpegBitmapEncoderOptions : BitmapEncoderOptions + { + public JpegBitmapEncoderOptions(); + public static JpegBitmapEncoderOptions Default { get; } + public int Quality { get; init; } + } + public sealed class PngBitmapEncoderOptions : BitmapEncoderOptions + { + public PngBitmapEncoderOptions(); + public Compression.CompressionLevel CompressionLevel { get; init; } + public static PngBitmapEncoderOptions Default { get; } + } } namespace Avalonia.Platform { public interface IBitmapImpl { - void Save(Stream stream, int? quality = null); - void Save(string fileName, int? quality = null); + void Save(Stream stream, BitmapEncoderOptions options); } }Avalonia.Skia (net10.0, net8.0)
namespace Avalonia.Skia.Helpers { public static class ImageSavingHelper { + public static void SaveImage(SKImage image, Stream stream, BitmapEncoderOptions options); + public static void SaveImage(SKImage image, string fileName, BitmapEncoderOptions options); } }