Ejemplo n.º 1
0
        /// <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);
            }
        }
Ejemplo n.º 2
0
 public UnitTest()
 {
     this.factory = new WebpFactory(Path.Combine("libraries", Environment.Is64BitProcess ? "libwebp-x64.dll" : "libwebp-x86.dll"));
 }
Ejemplo n.º 3
0
 /// <summary>Initialize a new <see cref="Webp"/> instance with the given <seealso cref="WebpFactory"/>.</summary>
 /// <param name="factory">The <seealso cref="WebpFactory"/> to wrap.</param>
 /// <param name="disposeFactory">Determines whether the given <seealso cref="WebpFactory"/> will be disposed when this <seealso cref="Webp"/> instance is disposed.</param>
 public Webp(WebpFactory factory, bool disposeFactory)
 {
     this.disposed = false;
     this._shouldDisposeFactory = disposeFactory;
     this.webp = factory;
 }