Exemple #1
0
 /// <summary>Decodes Webp data buffer to <seealso cref="Bitmap"/>.</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="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(ReadOnlySpan <byte> data, WindowsDecoderOptions options)
 {
     unsafe
     {
         fixed(byte *b = data)
         {
             return(this.Decode(new IntPtr(b), data.Length, options));
         }
     }
 }
Exemple #2
0
 /// <summary>Decodes Webp data buffer to <seealso cref="Bitmap"/>.</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="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(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));
     }
 }
Exemple #3
0
        /// <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);
            }
        }
Exemple #4
0
        /// <summary>Decodes Webp data stream to <seealso cref="Bitmap"/>.</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="Bitmap"/> 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 Bitmap 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 bitstreaminfo = new WebPBitstreamFeatures();
                    if (this.webp.TryGetImageHeaderInfo(memBuffer.Slice(0, streamRead), 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 decodedBuffer = this.webp.CreateDecodeBuffer();
                        decodedBuffer.colorspace         = Helper.GetWebpPixelFormat(decidedPxFmt);
                        decodedBuffer.is_external_memory = 1;
                        decodedBuffer.width  = width;
                        decodedBuffer.height = height;

                        var pixelFmt   = Helper.GetPixelFormat(decidedPxFmt);
                        var result     = new Bitmap(width, height, pixelFmt);
                        var lockedData = result.LockBits(new Rectangle(0, 0, result.Width, result.Height), ImageLockMode.WriteOnly, pixelFmt);
                        try
                        {
                            decodedBuffer.u.RGBA.rgba   = lockedData.Scan0;
                            decodedBuffer.u.RGBA.size   = new UIntPtr((uint)(lockedData.Stride * lockedData.Height));
                            decodedBuffer.u.RGBA.stride = lockedData.Stride;
                            using (var decoder = this.webp.CreateDecoder(ref decodedBuffer))
                            {
                                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(ref last_scanline_index, out var decodedwidth, out var decodedheight, 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
                                {
                                    result.Dispose();
                                    throw new WebpDecodeException(status);
                                }
                            }
                        }
                        catch (Exception ex) when(!(ex is WebpDecodeException))
                        {
                            result.Dispose();
                            throw;
                        }
                        finally
                        {
                            result.UnlockBits(lockedData);
                            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 (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 pixelFmt   = Helper.GetPixelFormat(decidedPxFmt);
                                    var result     = new Bitmap(width, height, pixelFmt);
                                    var lockedData = result.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, pixelFmt);
                                    try
                                    {
                                        var backBufferSize = stride * height;
                                        unsafe
                                        {
                                            Buffer.MemoryCopy(pointer.ToPointer(), lockedData.Scan0.ToPointer(), backBufferSize, backBufferSize);
                                        }
                                        return(result);
                                    }
                                    catch
                                    {
                                        result.Dispose();
                                        throw;
                                    }
                                    finally
                                    {
                                        result.UnlockBits(lockedData);
                                    }
                                }
                                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);
            }
        }