File: PlatformImage.cs
Web Access
Project: src\src\Graphics\src\Graphics\Graphics.csproj (Microsoft.Maui.Graphics)
using System;
using System.IO;
using System.Threading.Tasks;
 
namespace Microsoft.Maui.Graphics.Platform
{
#if NETSTANDARD || NETSTANDARD2_0 || NETSTANDARD2_1 || (NET6_0_OR_GREATER && !PLATFORM) || TIZEN || TIZEN40
	public class PlatformImage : IImage
	{
		private readonly byte[] _bytes;
		private readonly int _width;
		private readonly int _height;
		private readonly ImageFormat _originalFormat;
 
		public PlatformImage(byte[] bytes, ImageFormat originalFormat = ImageFormat.Png)
		{
			_bytes = bytes;
			_originalFormat = originalFormat;
 
			if (originalFormat == ImageFormat.Jpeg)
			{
				GetJpegDimension(out _width, out _height);
			}
			else
			{
				GetPngDimension(out _width, out _height);
			}
		}
 
		public byte[] Bytes => _bytes;
 
		public IImage Downsize(float maxWidthOrHeight, bool disposeOriginal = false)
		{
			throw new PlatformNotSupportedException();
		}
 
		public IImage Downsize(float maxWidth, float maxHeight, bool disposeOriginal = false)
		{
			throw new PlatformNotSupportedException();
		}
 
		public IImage Resize(float width, float height, ResizeMode resizeMode = ResizeMode.Fit, bool disposeOriginal = false)
		{
			throw new PlatformNotSupportedException();
		}
 
		/// <summary>
		/// Saves the contents of this image to the provided <see cref="Stream"/> object.
		/// </summary>
		/// <param name="stream">The destination stream the bytes of this image will be saved to.</param>
		/// <param name="format">The destination format of the image.</param>
		/// <param name="quality">The destination quality of the image.</param>
		/// <exception cref="ArgumentOutOfRangeException">Thrown when <paramref name="quality"/> is less than 0 or more than 1.</exception>
		/// <exception cref="NotImplementedException">Thrown when the provided value in <paramref name="format"/> does not match with the original format for this image.</exception>
		/// <remarks>The <paramref name="quality"/> value is currently unused.</remarks>
		public void Save(Stream stream, ImageFormat format = ImageFormat.Png, float quality = 1)
		{
			if (quality < 0 || quality > 1)
				throw new ArgumentOutOfRangeException(nameof(quality), "quality must be in the range of 0..1");
 
			if (format == _originalFormat)
			{
				stream.Write(_bytes, 0, _bytes.Length);
			}
			else
			{
				throw new NotImplementedException();
			}
		}
 
		/// <inheritdoc cref="Save"/>
		public Task SaveAsync(Stream stream, ImageFormat format = ImageFormat.Png, float quality = 1)
		{
			if (quality < 0 || quality > 1)
				throw new ArgumentOutOfRangeException(nameof(quality), "quality must be in the range of 0..1");
 
			if (format == _originalFormat)
			{
				Save(stream, format, quality);
				return Task.FromResult(true);
			}
 
			throw new NotImplementedException();
		}
 
		public void Dispose()
		{
			// Do nothing
		}
 
		public float Width => _width;
 
		public float Height => _height;
 
		private void GetPngDimension(out int width, out int height)
		{
			width = 0;
			height = 0;
 
			// Look into the byte array and get the size of the image
			for (int i = 0; i <= 3; i++)
			{
				width = _bytes[i] | width << 8;
				height = _bytes[i + 4] | height << 8;
			}
		}
 
		private void GetJpegDimension(out int width, out int height)
		{
			width = 0;
			height = 0;
 
			bool found = false;
			bool eof = false;
 
			var stream = new MemoryStream(_bytes);
			var reader = new BinaryReader(stream);
 
			while (!found || eof)
			{
				// read 0xFF and the type
				reader.ReadByte();
				byte type = reader.ReadByte();
 
				// get length
				int len;
				switch (type)
				{
					// start and end of the image
					case 0xD8:
					case 0xD9:
						len = 0;
						break;
 
					// restart interval
					case 0xDD:
						len = 2;
						break;
 
					// the next two bytes is the length
					default:
						int lenHi = reader.ReadByte();
						int lenLo = reader.ReadByte();
						len = (lenHi << 8 | lenLo) - 2;
						break;
				}
 
				// EOF?
				if (type == 0xD9)
					eof = true;
 
				// process the data
				if (len > 0)
				{
					// read the data
					byte[] data = reader.ReadBytes(len);
 
					// this is what we are looking for
					if (type == 0xC0)
					{
						width = data[1] << 8 | data[2];
						height = data[3] << 8 | data[4];
						found = true;
					}
				}
			}
 
			reader.Dispose();
			stream.Dispose();
		}
 
		public void Draw(ICanvas canvas, RectF dirtyRect)
		{
			throw new PlatformNotSupportedException();
		}
 
		public static IImage FromStream(Stream stream, ImageFormat format = ImageFormat.Png)
		{
			using (var memoryStream = new MemoryStream())
			{
				using (stream)
				{
					stream.CopyTo(memoryStream);
				}
 
				return new PlatformImage(memoryStream.ToArray(), format);
			}
		}
 
		public IImage ToPlatformImage()
			=> this;
	}
#endif
}