| File: SkiaImage.cs | Web Access |
| Project: src\src\Graphics\src\Graphics.Skia\Graphics.Skia.csproj (Microsoft.Maui.Graphics.Skia) |
using System; using System.IO; using System.Threading; using System.Threading.Tasks; using SkiaSharp; namespace Microsoft.Maui.Graphics.Skia { public class SkiaImage : IImage { private SKBitmap _image; public SkiaImage(SKBitmap image) { _image = image; } //protected readonly IPlatformGraphics Graphics; public float Width => _image.Width; public float Height => _image.Height; public IImage Downsize(float maxWidthOrHeight, bool disposeOriginal = false) { // todo: implement /* var downsizedImage = image.Downsize ((int)maxWidthOrHeight, disposeOriginal); return new MDImage (downsizedImage); */ return null; } public IImage Downsize(float maxWidth, float maxHeight, bool disposeOriginal = false) { /* var downsizedImage = image.Downsize ((int)maxWidth, (int)maxHeight, disposeOriginal); return new MDImage (downsizedImage); */ return null; } public IImage Resize(float width, float height, ResizeMode resizeMode = ResizeMode.Fit, bool disposeOriginal = false) { using (var context = new SkiaBitmapExportContext((int)width, (int)height, 1)) { var fx = width / Width; var fy = height / Height; var w = Width; var h = Height; var x = 0f; var y = 0f; if (resizeMode == ResizeMode.Fit) { if (fx < fy) { w *= fx; h *= fx; } else { w *= fy; h *= fy; } x = (width - w) / 2; y = (height - h) / 2; } else if (resizeMode == ResizeMode.Bleed) { if (fx > fy) { w *= fx; h *= fx; } else { w *= fy; h *= fy; } x = (width - w) / 2; y = (height - h) / 2; } else { w = width; h = height; } context.Canvas.DrawImage(this, x, y, w, h); return context.Image; } } public SKBitmap PlatformRepresentation => _image; public void Save(Stream stream, ImageFormat format = ImageFormat.Png, float quality = 1) { var skStream = GetSkStream(format, quality); using (skStream) { skStream.CopyTo(stream); } } public async Task SaveAsync(Stream stream, ImageFormat format = ImageFormat.Png, float quality = 1) { var skStream = GetSkStream(format, quality); using (skStream) { await skStream.CopyToAsync(stream); } } private Stream GetSkStream(ImageFormat format, float quality) { if (quality < 0 || quality > 1) throw new ArgumentOutOfRangeException(nameof(quality), "quality must be in the range of 0..1"); // Skia quality range from 0-100, this is supported by jpeg and webp. Higher values correspond to improved visual quality, but less compression. const int MaxSKQuality = 100; var skQuality = (int)(MaxSKQuality * quality); SKEncodedImageFormat skEncodedImageFormat = format switch { ImageFormat.Png => SKEncodedImageFormat.Png, ImageFormat.Jpeg => SKEncodedImageFormat.Jpeg, ImageFormat.Bmp or ImageFormat.Gif or ImageFormat.Tiff => throw new PlatformNotSupportedException($"Skia does not support {format} format."), _ => throw new ArgumentOutOfRangeException(nameof(format), format, null) }; var skData = _image.Encode(skEncodedImageFormat, skQuality); var skStream = skData.AsStream(streamDisposesData: true); return skStream; } public void Dispose() { var previousValue = Interlocked.Exchange(ref _image, null); previousValue?.Dispose(); } public void Draw(ICanvas canvas, RectF dirtyRect) { canvas.DrawImage(this, dirtyRect.Left, dirtyRect.Top, MathF.Round(dirtyRect.Width), MathF.Round(dirtyRect.Height)); } public IImage ToPlatformImage() { throw new NotSupportedException(); } public static IImage FromStream(Stream stream, ImageFormat formatHint = ImageFormat.Png) { using (var s = new SKManagedStream(stream)) { using (var codec = SKCodec.Create(s)) { var info = codec.Info; var bitmap = new SKBitmap(info.Width, info.Height, info.ColorType, info.IsOpaque ? SKAlphaType.Opaque : SKAlphaType.Premul); var result = codec.GetPixels(bitmap.Info, bitmap.GetPixels(out _)); if (result == SKCodecResult.Success || result == SKCodecResult.IncompleteInput) { return new SkiaImage(bitmap); } } } return null; } } }