/// <summary>Decodes Webp data buffer to <seealso cref="BitmapSource"/>.</summary> /// <param name="data">The data buffer which contains WebP image.</param> /// <param name="data_size">The size of the data buffer which contains WebP image.</param> /// <param name="options">The decoder options for webp decoder.</param> /// <returns><seealso cref="BitmapSource"/> which contains the image data.</returns> /// <exception cref="WebpDecodeException">Thrown when the decoder has wrong options or the buffer contains invalid data for Webp Image.</exception> public BitmapSource Decode(IntPtr data, int data_size, WindowsDecoderOptions options) { ReadOnlySpan <byte> buffer; unsafe { buffer = new ReadOnlySpan <byte>(data.ToPointer(), data_size); } var features = new WebPBitstreamFeatures(); if (this.webp.TryGetImageHeaderInfo(buffer, ref features) == VP8StatusCode.VP8_STATUS_OK) { var height = features.height; var decidedPxFmt = Helper.DecideOutputPixelFormat(options.PixelFormat, features.has_alpha != 0); var wbm = new WriteableBitmap(features.width, height, 96, 96, Helper.GetPixelFormat(decidedPxFmt), null); wbm.Lock(); try { uint inputSize = Convert.ToUInt32(data_size), outputSize = Convert.ToUInt32(wbm.BackBufferStride * height); this.webp.DecodeRGB(data, new UIntPtr(inputSize), wbm.BackBuffer, new UIntPtr(outputSize), Helper.GetWebpPixelFormat(decidedPxFmt), options); } finally { wbm.Unlock(); } return(wbm); } else { throw new WebpDecodeException(VP8StatusCode.VP8_STATUS_BITSTREAM_ERROR); } }
/// <summary>Decodes Webp data buffer to <seealso cref="BitmapSource"/>.</summary> /// <param name="data">The data buffer which contains WebP image.</param> /// <param name="options">The decoder options for webp decoder.</param> /// <returns><seealso cref="BitmapSource"/> which contains the image data.</returns> /// <exception cref="WebpDecodeException">Thrown when the decoder has wrong options or the buffer contains invalid data for Webp Image.</exception> public BitmapSource Decode(ReadOnlySpan <byte> data, WindowsDecoderOptions options) { unsafe { fixed(byte *b = data) { return(this.Decode(new IntPtr(b), data.Length, options)); } } }
/// <summary>Decodes Webp data buffer to <seealso cref="BitmapSource"/>.</summary> /// <param name="data">The data buffer which contains WebP image.</param> /// <param name="options">The decoder options for webp decoder.</param> /// <returns><seealso cref="BitmapSource"/> which contains the image data.</returns> /// <exception cref="WebpDecodeException">Thrown when the decoder has wrong options or the buffer contains invalid data for Webp Image.</exception> public BitmapSource Decode(ReadOnlyMemory <byte> data, WindowsDecoderOptions options) { using (var pinned = data.Pin()) { IntPtr pointer; unsafe { pointer = new IntPtr(pinned.Pointer); } return(this.Decode(pointer, data.Length, options)); } }
public void Test_WinForms() { using (var webp = new Webp(this.factory, false)) { string[] test_files = { "Test_lossless.webp" }; foreach (var filename in test_files) { using (var fs = File.OpenRead(filename)) { var decoderOpts = new WindowsDecoderOptions() { PixelFormat = OutputPixelFormat.PreferSmallSize }; var encoderOpts = new EncoderOptions(CompressionType.Lossy, CompressionLevel.Highest, WebPPreset.Default, 90f); using (var bitmap = webp.Decode(fs, decoderOpts)) { using (var output = File.Create(Path.GetFileNameWithoutExtension(filename) + "_re-encode-stream.webp")) { webp.Encode(bitmap, output, encoderOpts); output.Flush(); } } fs.Position = 0; var length = (int)fs.Length; var buffer = ArrayPool <byte> .Shared.Rent(length); try { if (fs.Read(buffer, 0, buffer.Length) == length) { using (var bitmap = webp.Decode(new ReadOnlyMemory <byte>(buffer, 0, length), decoderOpts)) { using (var output = File.Create(Path.GetFileNameWithoutExtension(filename) + "_re-encode-buffer.webp")) { webp.Encode(bitmap, output, encoderOpts); output.Flush(); } } } } finally { ArrayPool <byte> .Shared.Return(buffer); } } } } }
/// <summary>Decodes Webp data stream to <seealso cref="BitmapSource"/>.</summary> /// <param name="dataStream">The data stream which contains WebP image.</param> /// <param name="options">The decoder options for webp decoder.</param> /// <returns><seealso cref="BitmapSource"/> which contains the image data.</returns> /// <remarks>Incomplete API. Therefore, in case when decoder is progressive, only <seealso cref="WindowsDecoderOptions.PixelFormat"/> will be used, all other options will be ignored.</remarks> /// <exception cref="WebpDecodeException">Thrown when the decoder has wrong options or the stream contains invalid data for Webp Image.</exception> public BitmapSource Decode(Stream dataStream, WindowsDecoderOptions options) { if (dataStream == null) { throw new ArgumentNullException(nameof(dataStream)); } if (!dataStream.CanRead) { throw new ArgumentException("The stream must be readable.", nameof(dataStream)); } if (dataStream is MemoryStream memStream) { if (memStream.TryGetBuffer(out var segment)) { var mem = new ReadOnlyMemory <byte>(segment.Array, segment.Offset, segment.Count); return(this.Decode(mem, options)); } } bool canEvenSeek = false; try { canEvenSeek = dataStream.CanSeek; } catch (NotSupportedException) { } catch (NotImplementedException) { } if (canEvenSeek) { int length; long currentPos = -1; try { currentPos = dataStream.Position; // Try get length if it supports. length = (int)dataStream.Length; } catch (NotSupportedException) { length = -1; } // Length is longer than int.MaxValue catch { length = -2; } if (length != -2) { // Try to get the webp's data length starting from the current position of the stream (if possible) var bytes = ArrayPool <byte> .Shared.Rent(4096); try { if (dataStream.Read(bytes, 0, 12) == 12) { if (WebpFactory.TryGetFileSizeFromImage(new ReadOnlyMemory <byte>(bytes), out length)) { length += 8; // full image file's length. if (length > bytes.Length) { ArrayPool <byte> .Shared.Return(bytes); bytes = ArrayPool <byte> .Shared.Rent(length); } dataStream.Position = currentPos; if (dataStream.Read(bytes, 0, length) == length) { var mem = new ReadOnlyMemory <byte>(bytes, 0, length); return(this.Decode(mem, options)); } } } } catch (NotSupportedException) { if (currentPos != -1) { dataStream.Position = currentPos; } } finally { ArrayPool <byte> .Shared.Return(bytes); } } } byte[] currentBuffer = ArrayPool <byte> .Shared.Rent(4096); var memBuffer = new ReadOnlyMemory <byte>(currentBuffer); try { int streamRead = dataStream.Read(currentBuffer, 0, currentBuffer.Length); if (streamRead > 0) { var features = new WebPBitstreamFeatures(); if (this.webp.TryGetImageHeaderInfo(new ReadOnlyMemory <byte>(currentBuffer), ref features) == VP8StatusCode.VP8_STATUS_OK) { int width = features.width, height = features.height; var decodedBuffer = this.webp.CreateDecodeBuffer(); var decidedPxFmt = Helper.DecideOutputPixelFormat(options.PixelFormat, features.has_alpha != 0); decodedBuffer.colorspace = Helper.GetWebpPixelFormat(decidedPxFmt); decodedBuffer.is_external_memory = 1; decodedBuffer.width = width; decodedBuffer.height = height; var pixelFmt = Helper.GetPixelFormat(decidedPxFmt); var result = new WriteableBitmap(width, height, 96, 96, pixelFmt, null); result.Lock(); try { decodedBuffer.u.RGBA.rgba = result.BackBuffer; decodedBuffer.u.RGBA.size = new UIntPtr((uint)(result.BackBufferStride * result.PixelHeight)); decodedBuffer.u.RGBA.stride = result.BackBufferStride; using (var decoder = this.webp.CreateDecoder(ref decodedBuffer)) { VP8StatusCode status = VP8StatusCode.VP8_STATUS_NOT_ENOUGH_DATA; while (streamRead != 0) { status = decoder.AppendEncodedData(memBuffer.Slice(0, streamRead)); // if (decoder.GetDecodedImage(out last_scanline_index, out var width, out var height, out var stride, out IntPtr pointer) == VP8StatusCode.VP8_STATUS_OK) { } if (status != VP8StatusCode.VP8_STATUS_SUSPENDED) { break; } streamRead = dataStream.Read(currentBuffer, 0, currentBuffer.Length); } if (status == VP8StatusCode.VP8_STATUS_OK) { return(result); } else { throw new WebpDecodeException(status); } } } finally { result.Unlock(); this.webp.Free(ref decodedBuffer); } } else { var decidedPxFmt = Helper.DecideOutputPixelFormat(options.PixelFormat, null); using (var decoder = this.webp.CreateDecoderForRGBX(Helper.GetWebpPixelFormat(decidedPxFmt))) { int last_scanline_index = 0; VP8StatusCode status = VP8StatusCode.VP8_STATUS_NOT_ENOUGH_DATA; while (streamRead != 0) { status = decoder.AppendEncodedData(memBuffer.Slice(0, streamRead)); // if (decoder.GetDecodedImage(out last_scanline_index, out var width, out var height, out var stride, out IntPtr pointer) == VP8StatusCode.VP8_STATUS_OK) { } if (status != VP8StatusCode.VP8_STATUS_SUSPENDED) { break; } streamRead = dataStream.Read(currentBuffer, 0, currentBuffer.Length); } if (status == VP8StatusCode.VP8_STATUS_OK) { status = decoder.GetDecodedImage(ref last_scanline_index, out var width, out var height, out var stride, out IntPtr pointer); if (status == VP8StatusCode.VP8_STATUS_OK) { var result = new WriteableBitmap(width, height, 96, 96, Helper.GetPixelFormat(decidedPxFmt), null); result.Lock(); try { var backBufferSize = stride * height; unsafe { Buffer.MemoryCopy(pointer.ToPointer(), result.BackBuffer.ToPointer(), backBufferSize, backBufferSize); } return(result); } finally { result.Unlock(); } } else { throw new WebpDecodeException(status); } } else { throw new WebpDecodeException(status); } } } } else { throw new WebpDecodeException(VP8StatusCode.VP8_STATUS_BITSTREAM_ERROR); } } finally { ArrayPool <byte> .Shared.Return(currentBuffer); } }