/// <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); } }
public static IBitmapSource FromPointer(IntPtr data, long length) { var features = new WebPBitstreamFeatures(); if (LibWebpNative.WebPGetFeaturesInternal(data, (UIntPtr)length, ref features, LibWebpNative.WEBP_DECODER_ABI_VERSION) != VP8StatusCode.VP8_STATUS_OK) { throw new Exception("Failed."); } var b = new MemoryBitmapSource(features.width, features.height, 8, 4); var config = new WebPDecoderConfig(); if (LibWebpNative.WebPInitDecoderConfigInternal(ref config, LibWebpNative.WEBP_DECODER_ABI_VERSION) == 0) { throw new Exception("Failed."); } config.output.colorspace = WEBP_CSP_MODE.MODE_BGRA; config.output.u.RGBA.rgba = b.Scan0; config.output.u.RGBA.stride = b.Stride; config.output.u.RGBA.size = (UIntPtr)(b.Stride * features.height); config.output.is_external_memory = 1; var r = LibWebpNative.WebPDecode(data, (UIntPtr)length, ref config); if (r != VP8StatusCode.VP8_STATUS_OK) { throw new Exception("Failed."); } return(b); }
/// <summary>Try get the webp bitstream's feature from data buffer.</summary> /// <param name="data">The buffer contains webp image data.</param> /// <param name="feature">The structure to store the feature values.</param> /// <returns><seealso cref="VP8StatusCode.VP8_STATUS_OK"/> on success. Otherwise the error code.</returns> public VP8StatusCode TryGetImageHeaderInfo(ReadOnlySpan <byte> data, ref WebPBitstreamFeatures feature) { this.ThrowIfDisposed(); VP8StatusCode result; unsafe { fixed(byte *b = data) { result = this.library.WebPGetFeatures(new IntPtr(b), new UIntPtr(Convert.ToUInt32(data.Length)), ref feature); } } return(result); }
/// <summary>Try get the webp bitstream's feature from data buffer.</summary> /// <param name="data">The buffer contains webp image data.</param> /// <param name="feature">The structure to store the feature values.</param> /// <returns><seealso cref="VP8StatusCode.VP8_STATUS_OK"/> on success. Otherwise the error code.</returns> public VP8StatusCode TryGetImageHeaderInfo(ReadOnlyMemory <byte> data, ref WebPBitstreamFeatures feature) { this.ThrowIfDisposed(); VP8StatusCode result; using (var pinned = data.Pin()) { unsafe { result = this.library.WebPGetFeatures(new IntPtr(pinned.Pointer), new UIntPtr(Convert.ToUInt32(data.Length)), ref feature); } } return(result); }
/// <summary>Decodes Webp data buffer to <seealso cref="Bitmap"/>.</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="Bitmap"/> 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 Bitmap Decode(IntPtr data, int data_size, WindowsDecoderOptions options) { ReadOnlySpan <byte> buffer; unsafe { buffer = new ReadOnlySpan <byte>(data.ToPointer(), data_size); } var bitstreaminfo = new WebPBitstreamFeatures(); if (this.webp.TryGetImageHeaderInfo(buffer, ref bitstreaminfo) == VP8StatusCode.VP8_STATUS_OK) { var width = bitstreaminfo.width; var height = bitstreaminfo.height; var decidedPxFmt = Helper.DecideOutputPixelFormat(options.PixelFormat, bitstreaminfo.has_alpha != 0); var pixelFormat = Helper.GetPixelFormat(decidedPxFmt); var bm = new Bitmap(width, height, pixelFormat); var lockedData = bm.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, pixelFormat); try { uint inputSize = Convert.ToUInt32(data_size), outputSize = Convert.ToUInt32(lockedData.Stride * height); this.webp.DecodeRGB(data, new UIntPtr(inputSize), lockedData.Scan0, new UIntPtr(outputSize), Helper.GetWebpPixelFormat(decidedPxFmt), options); } catch { bm.Dispose(); throw; } finally { bm.UnlockBits(lockedData); } return(bm); } else { throw new WebpDecodeException(VP8StatusCode.VP8_STATUS_BITSTREAM_ERROR); } }
/// <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); } }