Beispiel #1
0
 private static void CheckError(VP8StatusCode code)
 {
     if (code != VP8StatusCode.OK)
     {
         throw new InvalidOperationException($"VP8StatusCode not OK: {code}");
     }
 }
Beispiel #2
0
        /// <summary>
        /// Loads an image from webp into a byte array in RGBA format.
        /// </summary>
        /// <returns>The RGBA from web p.</returns>
        /// <param name="lData">L data.</param>
        /// <param name="lWidth">L width.</param>
        /// <param name="lHeight">L height.</param>
        /// <param name="lMipmaps">If set to <c>true</c> l mipmaps.</param>
        /// <param name="lError">L error.</param>
        /// <param name="scalingFunction">Scaling function.</param>
        public static Status DecodeWebP(byte[] lInput, ref int lWidth, ref int lHeight, ref WEBP_CSP_MODE lColorSpace,
                                        bool lMipmaps, out byte[] lOutput, bool lReducedColorRange = false,
                                        bool lReducedScale = false)
        {
            Status lStatus        = 0;
            int    lLength        = lInput.Length;
            int    lBytesPerTexel = 4;

            GCHandle lHandle = GCHandle.Alloc(lInput, GCHandleType.Pinned);

            {
                IntPtr lDataPtr = lHandle.AddrOfPinnedObject();

                WebPDecoderConfig config = new WebPDecoderConfig();

                if (NativeBindings.WebPInitDecoderConfig(ref config) == 0)
                {
                    throw new Exception("WebPInitDecoderConfig failed. Wrong version?");
                }

                if (lReducedScale == true)
                {
                    lWidth  /= 2;
                    lHeight /= 2;
                }

                // Set up decode options
                config.options.use_threads = 0;
                if (lReducedScale == true)
                {
                    config.options.use_scaling   = 1;
                    config.options.scaled_width  = lWidth;
                    config.options.scaled_height = lHeight;
                }

                // read the .webp input file information
                VP8StatusCode result = NativeBindings.WebPGetFeatures((IntPtr)lDataPtr, (UIntPtr)lLength, ref config.input);
                if (result != VP8StatusCode.VP8_STATUS_OK)
                {
                    throw new Exception(string.Format("Failed WebPGetFeatures with error {0}.", result.ToString()));
                }

                //  confirm colorspace.
                if (config.input.has_alpha > 0)
                {
                    if (lReducedColorRange == true)
                    {
                        lColorSpace    = WEBP_CSP_MODE.MODE_RGBA_4444;
                        lBytesPerTexel = 2;
                    }
                    else
                    {
                        lColorSpace    = WEBP_CSP_MODE.MODE_RGBA;
                        lBytesPerTexel = 4;
                    }
                }
                else
                {
                    if (lReducedColorRange == true)
                    {
                        lColorSpace    = WEBP_CSP_MODE.MODE_RGB_565;
                        lBytesPerTexel = 2;
                    }
                    else
                    {
                        lColorSpace    = WEBP_CSP_MODE.MODE_RGB;
                        lBytesPerTexel = 3;
                    }
                }

                // Bytes per texel can only be calculated at this point...
                int lStride = lBytesPerTexel * lWidth;

                // If mipmaps are requested we need to create 1/3 more memory for the mipmaps to be generated in.
                int lSize = lHeight * lStride;
                if (lMipmaps)   //  don't do this here..
                {
                    //  bit shift instead of this crude approach.,...
                    lSize += Mathf.CeilToInt((float)lSize / 3.0f);
                }

                lOutput = new byte[lSize];
                GCHandle lOutHandle = GCHandle.Alloc(lOutput, GCHandleType.Pinned);
                IntPtr   lOutputPtr = lOutHandle.AddrOfPinnedObject();
                {
                    // As we have to reverse the y order of the data, we pass through a negative stride and
                    // pass through a pointer to the last line of the data.
                    IntPtr lTmpDataPtr = new IntPtr(lOutputPtr.ToInt64() + (lSize - lStride));

                    // specify the output format
                    config.output.colorspace         = lColorSpace;
                    config.output.u.RGBA.rgba        = lTmpDataPtr;
                    config.output.u.RGBA.stride      = -lStride;
                    config.output.u.RGBA.size        = (UIntPtr)lSize;
                    config.output.height             = lHeight;
                    config.output.width              = lWidth;
                    config.output.is_external_memory = 1;

                    // Decode
                    result = NativeBindings.WebPDecode((IntPtr)lDataPtr, (UIntPtr)lLength, ref config);
                    if (result != VP8StatusCode.VP8_STATUS_OK)
                    {
                        throw new Exception(string.Format("Failed WebPDecode with error {0}.", result.ToString()));
                    }
                }
                lOutHandle.Free();
                lStatus = Status.SUCCESS;
            }
            lHandle.Free();

            return(lStatus);
        }
Beispiel #3
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);
            }
        }
Beispiel #4
0
        bool ReadHuffmanCodeLengths(int[] code_length_code_lengths, int num_symbols, int[] code_lengths)
        {
            int prev_code_len = Huffman.DefaultCodeLength;
            var table = new HuffmanCode[1 << Huffman.LengthsTableBits];

            if (0 == Huffman.BuildTable (table, 0, Huffman.LengthsTableBits, code_length_code_lengths, NumCodeLengthCodes))
            {
                status_ = VP8StatusCode.BitstreamError;
                return false;
            }

            int max_symbol;
            if (0 != br_.ReadBits (1))      // use length
            {
                int length_nbits = 2 + 2 * (int)br_.ReadBits (3);
                max_symbol = 2 + (int)br_.ReadBits (length_nbits);
                if (max_symbol > num_symbols)
                {
                    status_ = VP8StatusCode.BitstreamError;
                    return false;
                }
            }
            else
            {
                max_symbol = num_symbols;
            }

            int symbol = 0;
            while (symbol < num_symbols)
            {
                if (max_symbol-- == 0) break;
                br_.FillBitWindow();
                int p = (int)br_.PrefetchBits() & Huffman.LengthsTableMask;
                br_.SkipBits (table[p].bits);
                int code_len = table[p].value;
                if (code_len < kCodeLengthLiterals)
                {
                    code_lengths[symbol++] = code_len;
                    if (code_len != 0) prev_code_len = code_len;
                }
                else
                {
                    bool use_prev = (code_len == kCodeLengthRepeatCode);
                    int slot = code_len - kCodeLengthLiterals;
                    int extra_bits = kCodeLengthExtraBits[slot];
                    int repeat_offset = kCodeLengthRepeatOffsets[slot];
                    int repeat = (int)br_.ReadBits(extra_bits) + repeat_offset;
                    if (symbol + repeat > num_symbols)
                    {
                        status_ = VP8StatusCode.BitstreamError;
                        return false;
                    }
                    else
                    {
                        int length = use_prev ? prev_code_len : 0;
                        while (repeat-- > 0)
                            code_lengths[symbol++] = length;
                    }
                }
            }
            return true;
        }
Beispiel #5
0
		int VP8SetError(VP8Decoder* dec, VP8StatusCode error, char * msg) {
		  dec.status_ = error;
		  dec.error_msg_ = msg;
		  dec.ready_ = 0;
		  return 0;
		}
    private unsafe List <(Texture2D, int)> LoadAnimation2(string loadPath)
    {
        List <ValueTuple <Texture2D, int> > ret = new List <ValueTuple <Texture2D, int> >();

        TextAsset textasset = Resources.Load <TextAsset>(loadPath);

        byte[] bytes = textasset.bytes;

        WebPDecoderConfig config = new WebPDecoderConfig();

        if (NativeLibwebp.WebPInitDecoderConfig(&config) == 0)
        {
            throw new Exception("WebPInitDecoderConfig failed. Wrong version?");
        }

        WebPIterator iter = new WebPIterator();

        fixed(byte *p = bytes)
        {
            WebPData webpdata = new WebPData
            {
                bytes = p,
                size  = new UIntPtr((uint)bytes.Length)
            };
            WebPDemuxer *webPDemuxer = NativeLibwebpdemux.WebPDemuxInternal(&webpdata, 0, (WebPDemuxState *)IntPtr.Zero, NativeLibwebpdemux.WEBP_DEMUX_ABI_VERSION);

            VP8StatusCode result = NativeLibwebp.WebPGetFeatures(webpdata.bytes, webpdata.size, &config.input);

            if (result != VP8StatusCode.VP8_STATUS_OK)
            {
                throw new Exception(string.Format("Failed WebPGetFeatures with error {0}.", result.ToString()));
            }

            int height = config.input.height;
            int width  = config.input.height;

            config.options.bypass_filtering    = 0;
            config.options.use_threads         = 1;
            config.options.no_fancy_upsampling = 0;
            config.options.use_cropping        = 0;
            config.options.use_scaling         = 1;
            config.options.scaled_width        = width;
            config.options.scaled_height       = height;
            config.options.flip = 1;
            config.options.dithering_strength = 0;
            config.output.colorspace          = WEBP_CSP_MODE.MODE_RGBA;
            config.output.width  = width;
            config.output.height = height;

            //byte[] bbb = new byte[width * height];
            //fixed (byte* ppp = bbb)
            //{
            //    config.output.u.RGBA.rgba = (IntPtr)ppp;
            //}
            //config.output.u.RGBA.stride = width * 4;
            //config.output.u.RGBA.size = (UIntPtr)(width * height);
            //config.output.is_external_memory = 1;
            //config.output.is_external_memory = 1;

            int success = NativeLibwebpdemux.WebPDemuxGetFrame(webPDemuxer, 1, &iter);

            if (success != 1)
            {
                return(ret);
            }

            int timestamp = 0;
            int size      = width * height * 4;

            do
            {
                WebPData      frame  = iter.fragment;
                VP8StatusCode status = NativeLibwebp.WebPDecode(frame.bytes, frame.size, &config);
                if (status != VP8StatusCode.VP8_STATUS_OK)
                {
                    Debug.LogError(status);
                    break;
                }

                Texture2D texture = Texture2DExt.CreateWebpTexture2D(width, height, isUseMipmap: false, isLinear: false);
                texture.LoadRawTextureData((IntPtr)config.output.u.RGBA.rgba, size);
                texture.Apply(updateMipmaps: false, makeNoLongerReadable: true);
                timestamp += iter.duration;
                ret.Add((texture, timestamp));
            }while (NativeLibwebpdemux.WebPDemuxNextFrame(&iter) == 1);

            NativeLibwebpdemux.WebPDemuxDelete(webPDemuxer);
            NativeLibwebpdemux.WebPDemuxReleaseIterator(&iter);
        }

        return(ret);
    }
Beispiel #7
0
        int ReadHuffmanCode(int alphabet_size, int[] code_lengths, HuffmanCode[] table, int index)
        {
            bool ok = false;
            int size = 0;
            bool simple_code = br_.ReadBits (1) != 0;

            for (int i = 0; i < alphabet_size; ++i)
                code_lengths[i] = 0;

            if (simple_code)    // Read symbols, codes & code lengths directly.
            {
                int num_symbols = (int)br_.ReadBits (1) + 1;
                int first_symbol_len_code = (int)br_.ReadBits (1);
                // The first code is either 1 bit or 8 bit code.
                int symbol = (int)br_.ReadBits ((first_symbol_len_code == 0) ? 1 : 8);
                code_lengths[symbol] = 1;
                // The second code (if present), is always 8 bit long.
                if (2 == num_symbols)
                {
                    symbol = (int)br_.ReadBits (8);
                    code_lengths[symbol] = 1;
                }
                ok = true;
            }
            else    // Decode Huffman-coded code lengths.
            {
                var code_length_code_lengths = new int[NumCodeLengthCodes];
                int num_codes = (int)br_.ReadBits (4) + 4;
                if (num_codes > NumCodeLengthCodes)
                {
                    status_ = VP8StatusCode.BitstreamError;
                    return 0;
                }
                for (int i = 0; i < num_codes; ++i)
                {
                    code_length_code_lengths[kCodeLengthCodeOrder[i]] = (int)br_.ReadBits (3);
                }
                ok = ReadHuffmanCodeLengths (code_length_code_lengths, alphabet_size, code_lengths);
            }

            ok = ok && !br_.EoS;
            if (ok)
                size = Huffman.BuildTable (table, index, Huffman.TableBits, code_lengths, alphabet_size);
            if (!ok || size == 0)
            {
                status_ = VP8StatusCode.BitstreamError;
                return 0;
            }
            return size;
        }
Beispiel #8
0
        public bool DecodeImageData(uint[] data, int width, int height, int last_row, ProcessRowsFunc process_func)
        {
            int row = last_pixel_ / width;
            int col = last_pixel_ % width;
            var htree_group = GetHtreeGroupForPos (col, row);
            int src = last_pixel_;
            int last_cached = src;
            int src_end = width * height;     // End of data
            int src_last = width * last_row;  // Last pixel to decode
            int len_code_limit = Huffman.NumLiteralCodes + Huffman.NumLengthCodes;
            int color_cache_limit = len_code_limit + hdr_.color_cache_size_;
            int next_sync_row = incremental_ ? row : 1 << 24;
            var color_cache = (hdr_.color_cache_size_ > 0) ? hdr_.color_cache_ : null;
            int mask = hdr_.huffman_mask_;

            while (src < src_last)
            {
                int code;
                if (row >= next_sync_row)
                {
                    SaveState (src);
                    next_sync_row = row + SyncEveryNRows;
                }
                // Only update when changing tile. Note we could use this test:
                // if "((((prev_col ^ col) | prev_row ^ row)) > mask)" -> tile changed
                // but that's actually slower and needs storing the previous col/row.
                if ((col & mask) == 0)
                    htree_group = GetHtreeGroupForPos (col, row);
                if (htree_group.is_trivial_code)
                {
                    data[src] = htree_group.literal_arb;
                    goto AdvanceByOne;
                }
                br_.FillBitWindow();
                if (htree_group.use_packed_table)
                {
                    code = ReadPackedSymbols (htree_group, data, src);
                    if (code == PackedNonLiteralCode)
                        goto AdvanceByOne;
                }
                else
                {
                    code = ReadSymbol (htree_group.Tables, htree_group.GetMeta (HuffIndex.Green));
                }
                if (br_.EoS) break;  // early out
                if (code < Huffman.NumLiteralCodes)    // Literal
                {
                    if (htree_group.is_trivial_literal)
                    {
                        data[src] = htree_group.literal_arb | (uint)(code << 8);
                    }
                    else
                    {
                        int red, blue, alpha;
                        red = ReadSymbol (htree_group.Tables, htree_group.GetMeta (HuffIndex.Red));
                        br_.FillBitWindow();
                        blue = ReadSymbol (htree_group.Tables, htree_group.GetMeta (HuffIndex.Blue));
                        alpha = ReadSymbol (htree_group.Tables, htree_group.GetMeta (HuffIndex.Alpha));
                        if (br_.EoS) break;
                        data[src] = ((uint)alpha << 24) | ((uint)red << 16) | ((uint)code << 8) | (uint)blue;
                    }
                }
                else if (code < len_code_limit)    // Backward reference
                {
                    int length_sym = code - Huffman.NumLiteralCodes;
                    int length = GetCopyLength (length_sym);
                    int dist_symbol = ReadSymbol (htree_group.Tables, htree_group.GetMeta (HuffIndex.Dist));
                    br_.FillBitWindow();
                    int dist_code = GetCopyDistance (dist_symbol);
                    int dist = PlaneCodeToDistance (width, dist_code);
                    if (br_.EoS) break;
                    if (src < dist || src_end - src < length)
                    {
                        status_ = VP8StatusCode.BitstreamError;
                        return false;
                    }
                    else
                    {
                        int dst = src;
                        int s = dst - dist;
                        for (int i = 0; i < length; ++i)
                            data[dst+i] = data[s+i];
                    }
                    src += length;
                    col += length;
                    while (col >= width)
                    {
                        col -= width;
                        ++row;
                        if ((row % NumARGBCacheRows == 0) && (process_func != null))
                            process_func (this, row);
                    }
                    if (0 != (col & mask)) htree_group = GetHtreeGroupForPos (col, row);
                    if (color_cache != null)
                    {
                        while (last_cached < src)
                            color_cache.Insert (data[last_cached++]);
                    }
                    continue;
                }
                else if (code < color_cache_limit)    // Color cache
                {
                    int key = code - len_code_limit;
                    while (last_cached < src)
                        color_cache.Insert (data[last_cached++]);
                    data[src] = color_cache.Lookup ((uint)key);
                }
                else    // Not reached
                {
                    status_ = VP8StatusCode.BitstreamError;
                    return false;
                }

            AdvanceByOne:
                ++src;
                ++col;
                if (col >= width)
                {
                    col = 0;
                    ++row;
                    if ((row % NumARGBCacheRows == 0) && (process_func != null))
                    {
                        process_func (this, row);
                    }
                    if (color_cache != null)
                    {
                        while (last_cached < src)
                            color_cache.Insert (data[last_cached++]);
                    }
                }
            }

            if (incremental_ && br_.EoS && src < src_end)
            {
                RestoreState();
            }
            else if (!br_.EoS)
            {
                // Process the remaining rows corresponding to last row-block.
                if (process_func != null)
                    process_func (this, row);
                status_ = VP8StatusCode.Ok;
                last_pixel_ = src;  // end-of-scan marker
            }
            else
            {
                // if not incremental, and we are past the end of buffer (eos_=1), then this
                // is a real bitstream error.
                status_ = VP8StatusCode.BitstreamError;
                return false;
            }
            return true;
        }
Beispiel #9
0
        public bool DecodeImageStream(int xsize, int ysize, bool is_level0, ref uint[] decoded_data, bool set_data)
        {
            bool ok = true;
            int transform_xsize = xsize;
            int transform_ysize = ysize;
            int color_cache_bits = 0;
            uint[] data = null;

            // Read the transforms (may recurse).
            if (is_level0)
            {
                while (ok && 0 != br_.ReadBits (1))
                    ok = ReadTransform (ref transform_xsize, ref transform_ysize);
            }

            // Color cache
            if (ok && 0 != br_.ReadBits (1))
            {
                color_cache_bits = (int)br_.ReadBits (4);
                ok = (color_cache_bits >= 1 && color_cache_bits <= MaxCacheBits);
                if (!ok)
                {
                    status_ = VP8StatusCode.BitstreamError;
                    return false;
                }
            }

            // Read the Huffman codes (may recurse).
            ok = ok && ReadHuffmanCodes (transform_xsize, transform_ysize, color_cache_bits, is_level0);
            if (!ok)
            {
                status_ = VP8StatusCode.BitstreamError;
                return false;
            }

            // Finish setting up the color-cache
            if (color_cache_bits > 0)
            {
                hdr_.color_cache_size_ = 1 << color_cache_bits;
                hdr_.color_cache_.Init (color_cache_bits);
            }
            else
            {
                hdr_.color_cache_size_ = 0;
            }
            UpdateDecoder (transform_xsize, transform_ysize);

            if (is_level0)     // level 0 complete
            {
                state_ = VP8DecodeState.ReadHdr;
            }
            else
            {
                var total_size = transform_xsize * transform_ysize;
                data = new uint[total_size];

                // Use the Huffman trees to decode the LZ77 encoded data.
                ok = DecodeImageData (data, transform_xsize, transform_ysize, transform_ysize, null);
                ok = ok && !br_.EoS;
            }
            if (ok)
            {
                if (set_data)
                {
                    decoded_data = data;
                }
                last_pixel_ = 0;  // Reset for future DECODE_DATA_FUNC() calls.
                if (!is_level0)
                    hdr_.ClearMetadata();
            }
            return ok;
        }
Beispiel #10
0
        public LosslessDecoder()
        {
            status_ = VP8StatusCode.Ok;
            state_  = VP8DecodeState.ReadDim;

            for (int i = 0; i < transforms_.Length; ++i)
                transforms_[i] = new VP8LTransform();
        }
Beispiel #11
0
        public bool DecodeAlphaData(int width, int height, int last_row)
        {
            var data = pixels8_;
            bool ok = true;
            int row = last_pixel_ / width;
            int col = last_pixel_ % width;
            var htree_group = GetHtreeGroupForPos (col, row);
            int pos = last_pixel_;        // current position
            int end = width * height;     // End of data
            int last = width * last_row;  // Last pixel to decode
            int len_code_limit = Huffman.NumLiteralCodes + Huffman.NumLengthCodes;
            int mask = hdr_.huffman_mask_;

            while (!br_.EoS && pos < last)
            {
                // Only update when changing tile.
                if ((col & mask) == 0)
                    htree_group = GetHtreeGroupForPos (col, row);

                br_.FillBitWindow();
                int code = ReadSymbol (htree_group.Tables, htree_group.GetMeta (HuffIndex.Green));
                if (code < Huffman.NumLiteralCodes)    // Literal
                {
                    data[pos] = (byte)code;
                    ++pos;
                    ++col;
                    if (col >= width)
                    {
                        col = 0;
                        ++row;
                        if (row % NumARGBCacheRows == 0)
                            ExtractPalettedAlphaRows (row);
                    }
                }
                else if (code < len_code_limit)    // Backward reference
                {
                    int length_sym = code - Huffman.NumLiteralCodes;
                    int length = GetCopyLength (length_sym);
                    int dist_symbol = ReadSymbol (htree_group.Tables, htree_group.GetMeta (HuffIndex.Dist));
                    br_.FillBitWindow();
                    int dist_code = GetCopyDistance (dist_symbol);
                    int dist = PlaneCodeToDistance (width, dist_code);
                    if (pos >= dist && end - pos >= length)
                    {
                        Binary.CopyOverlapped (data, pos - dist, pos, length);
                    }
                    else
                    {
                        ok = false;
                        goto End;
                    }
                    pos += length;
                    col += length;
                    while (col >= width)
                    {
                        col -= width;
                        ++row;
                        if (row % NumARGBCacheRows == 0)
                            ExtractPalettedAlphaRows (row);
                    }
                    if (pos < last && 0 != (col & mask))
                        htree_group = GetHtreeGroupForPos (col, row);
                }
                else    // Not reached
                {
                    ok = false;
                    goto End;
                }
            }
            // Process the remaining rows corresponding to last row-block.
            ExtractPalettedAlphaRows (row);

            End:
            if (!ok || (br_.EoS && pos < end))
            {
                ok = false;
                status_ = br_.EoS ? VP8StatusCode.Suspended : VP8StatusCode.BitstreamError;
            }
            else
            {
                last_pixel_ = pos;
            }
            return ok;
        }
Beispiel #12
0
 void RestoreState()
 {
     status_ = VP8StatusCode.Suspended;
     saved_br_.CopyStateTo (br_);
     last_pixel_ = saved_last_pixel_;
     if (hdr_.color_cache_size_ > 0)
         hdr_.saved_color_cache_.Copy (hdr_.color_cache_);
 }
Beispiel #13
0
 /// <summary>Initializes a new instance of the <see cref="WebpDecodeException"/> class with a specified error code.</summary>
 /// <param name="code">The error code of the operation.</param>
 public WebpDecodeException(VP8StatusCode code) : base()
 {
     this.ErrorCode = code;
 }
Beispiel #14
0
 /// <summary>Initializes a new instance of the <see cref="WebpDecodeException"/> class with a specified error code and error message.</summary>
 /// <param name="code">The error code of the operation.</param>
 /// <param name="message">The error message of the operation.</param>
 public WebpDecodeException(VP8StatusCode code, string message) : base(message)
 {
     this.ErrorCode = code;
 }
Beispiel #15
0
        /// <summary>
        /// Loads an image from webp into a byte array in RGBA format.
        /// </summary>
        /// <returns>The RGBA from web p.</returns>
        /// <param name="lData">L data.</param>
        /// <param name="lWidth">L width.</param>
        /// <param name="lHeight">L height.</param>
        /// <param name="lMipmaps">If set to <c>true</c> l mipmaps.</param>
        /// <param name="lError">L error.</param>
        /// <param name="scalingFunction">Scaling function.</param>
        public static unsafe byte[] LoadRGBAFromWebP(byte[] lData, ref int lWidth, ref int lHeight, bool lMipmaps, out Error lError, ScalingFunction scalingFunction = null)
        {
            lError = 0;
            byte[] lRawData = null;
            int    lLength  = lData.Length;

            fixed(byte *lDataPtr = lData)
            {
                // If we've been supplied a function to alter the width and height, use that now.
                scalingFunction?.Invoke(ref lWidth, ref lHeight);

                // If mipmaps are requested we need to create 1/3 more memory for the mipmaps to be generated in.
                int numBytesRequired = lWidth * lHeight * 4;

                if (lMipmaps)
                {
                    numBytesRequired = Mathf.CeilToInt((numBytesRequired * 4.0f) / 3.0f);
                }

                lRawData = new byte[numBytesRequired];
                fixed(byte *lRawDataPtr = lRawData)
                {
                    int lStride = 4 * lWidth;

                    // As we have to reverse the y order of the data, we pass through a negative stride and
                    // pass through a pointer to the last line of the data.
                    byte *lTmpDataPtr = lRawDataPtr + (lHeight - 1) * lStride;

                    WebPDecoderConfig config = new WebPDecoderConfig();

                    if (NativeLibwebp.WebPInitDecoderConfig(&config) == 0)
                    {
                        throw new Exception("WebPInitDecoderConfig failed. Wrong version?");
                    }

                    // Set up decode options
                    config.options.use_threads = 1;
                    if (scalingFunction != null)
                    {
                        config.options.use_scaling = 1;
                    }
                    config.options.scaled_width  = lWidth;
                    config.options.scaled_height = lHeight;

                    // read the .webp input file information
                    VP8StatusCode result = NativeLibwebp.WebPGetFeatures(lDataPtr, (UIntPtr)lLength, &config.input);

                    if (result != VP8StatusCode.VP8_STATUS_OK)
                    {
                        throw new Exception(string.Format("Failed WebPGetFeatures with error {0}.", result.ToString()));
                    }

                    // specify the output format
                    config.output.colorspace         = WEBP_CSP_MODE.MODE_RGBA;
                    config.output.u.RGBA.rgba        = lTmpDataPtr;
                    config.output.u.RGBA.stride      = -lStride;
                    config.output.u.RGBA.size        = (UIntPtr)(lHeight * lStride);
                    config.output.height             = lHeight;
                    config.output.width              = lWidth;
                    config.output.is_external_memory = 1;

                    // Decode
                    result = NativeLibwebp.WebPDecode(lDataPtr, (UIntPtr)lLength, &config);
                    if (result != VP8StatusCode.VP8_STATUS_OK)
                    {
                        throw new Exception(string.Format("Failed WebPDecode with error {0}.", result.ToString()));
                    }
                }

                lError = Error.Success;
            }

            return(lRawData);
        }
Beispiel #16
0
        public void Init(int width, int height, VP8Io io,
                          byte[] data, int data_i, int data_size, byte[] output)
        {
            width_ = width;
            height_ = height;
            status_ = VP8StatusCode.Ok;
            io_ = io;

            io_.opaque = output;
            io_.width = width_;
            io_.height = height_;

            br_.Init (data, data_i, (uint)data_size);
        }
Beispiel #17
0
        public static unsafe void LoadTexture2DFromWebP(byte[] webpBytes, Texture2D texture, bool lMipmaps, bool lLinear, byte[] bytePool, int numBytesRequired, ScalingFunction scalingFunction = null)
        {
            Debug.Assert(bytePool.Length >= numBytesRequired);
            Array.Clear(bytePool, 0, numBytesRequired);

            int width  = texture.width;
            int height = texture.height;

            fixed(byte *lDataPtr = webpBytes)
            {
                fixed(byte *lRawDataPtr = bytePool)
                {
                    int lStride = 4 * width;

                    // As we have to reverse the y order of the data, we pass through a negative stride and
                    // pass through a pointer to the last line of the data.
                    byte *lTmpDataPtr = lRawDataPtr + (height - 1) * lStride;

                    WebPDecoderConfig config = new WebPDecoderConfig();

                    if (NativeLibwebp.WebPInitDecoderConfig(&config) == 0)
                    {
                        throw new Exception("WebPInitDecoderConfig failed. Wrong version?");
                    }

                    // Set up decode options
                    config.options.use_threads = 1;
                    if (scalingFunction != null)
                    {
                        config.options.use_scaling = 1;
                    }
                    config.options.scaled_width  = width;
                    config.options.scaled_height = height;

                    // read the .webp input file information
                    VP8StatusCode result = NativeLibwebp.WebPGetFeatures(lDataPtr, (UIntPtr)webpBytes.Length, &config.input);

                    if (result != VP8StatusCode.VP8_STATUS_OK)
                    {
                        throw new Exception(string.Format("Failed WebPGetFeatures with error {0}.", result.ToString()));
                    }

                    // specify the output format
                    config.output.colorspace         = WEBP_CSP_MODE.MODE_RGBA;
                    config.output.u.RGBA.rgba        = lTmpDataPtr;
                    config.output.u.RGBA.stride      = -lStride;
                    config.output.u.RGBA.size        = (UIntPtr)(height * lStride);
                    config.output.height             = height;
                    config.output.width              = width;
                    config.output.is_external_memory = 1;

                    // Decode
                    result = NativeLibwebp.WebPDecode(lDataPtr, (UIntPtr)webpBytes.Length, &config);
                    if (result != VP8StatusCode.VP8_STATUS_OK)
                    {
                        throw new Exception(string.Format("Failed WebPDecode with error {0}.", result.ToString()));
                    }

                    texture.LoadRawTextureData((IntPtr)lRawDataPtr, numBytesRequired);
                    texture.Apply(lMipmaps, true);
                }
            }
        }
    private unsafe List <(Texture2D, int)> LoadAnimation2(string loadPath)
    {
        List <ValueTuple <Texture2D, int> > ret = new List <ValueTuple <Texture2D, int> >();

        TextAsset textasset = Resources.Load <TextAsset>(loadPath);

        byte[] bytes = textasset.bytes;

        var config = new WebPDecoderConfig();

        if (Decode.WebPInitDecoderConfig(ref config) == 0)
        {
            throw new Exception("WebPInitDecoderConfig failed. Wrong version?");
        }

        var    iter        = new WebPIterator();
        IntPtr webpDataPtr = Marshal.AllocHGlobal(sizeof(WebPData));
        IntPtr configPtr   = Marshal.AllocHGlobal(Marshal.SizeOf(config));
        IntPtr iterPtr     = Marshal.AllocHGlobal(Marshal.SizeOf(iter));

        try
        {
            fixed(byte *p = bytes)
            {
                IntPtr   ptr      = (IntPtr)p;
                WebPData webpdata = new WebPData
                {
                    bytes = ptr,
                    size  = new UIntPtr((uint)bytes.Length)
                };

                Marshal.StructureToPtr(webpdata, webpDataPtr, false);
                Marshal.StructureToPtr(config, configPtr, false);
                Marshal.StructureToPtr(iter, iterPtr, false);
                IntPtr webPDemuxer = Demux.WebPDemuxInternal(webpDataPtr, 0, (IntPtr)0, Demux.WEBP_DEMUX_ABI_VERSION);

                VP8StatusCode result = Decode.WebPGetFeatures(webpdata.bytes, webpdata.size, ref config.input);

                if (result != VP8StatusCode.VP8_STATUS_OK)
                {
                    throw new Exception(string.Format("Failed WebPGetFeatures with error {0}.", result.ToString()));
                }

                var height = config.input.height;
                var width  = config.input.height;

                config.options.bypass_filtering    = 0;
                config.options.use_threads         = 1;
                config.options.no_fancy_upsampling = 0;
                config.options.use_cropping        = 0;
                config.options.use_scaling         = 1;
                config.options.scaled_width        = width;
                config.options.scaled_height       = height;
                config.options.flip = 1;
                config.options.dithering_strength = 0;
                config.output.colorspace          = WEBP_CSP_MODE.MODE_RGBA;
                config.output.width  = width;
                config.output.height = height;

                //byte[] bbb = new byte[width * height];
                //fixed (byte* ppp = bbb)
                //{
                //    config.output.u.RGBA.rgba = (IntPtr)ppp;
                //}
                //config.output.u.RGBA.stride = width * 4;
                //config.output.u.RGBA.size = (UIntPtr)(width * height);
                //config.output.is_external_memory = 1;
                //config.output.is_external_memory = 1;

                int success = Demux.WebPDemuxGetFrame(webPDemuxer, 1, ref iter);

                if (success != 1)
                {
                    return(ret);
                }

                int timestamp = 0;
                int size      = width * height * 4;

                do
                {
                    WebPData      frame  = iter.fragment;
                    VP8StatusCode status = Decode.WebPDecode(frame.bytes, frame.size, ref config);
                    if (status != VP8StatusCode.VP8_STATUS_OK)
                    {
                        Debug.LogError(status);
                        break;
                    }

                    var texture = new Texture2D(width, height, TextureFormat.RGBA32, mipChain: false, linear: false);
                    texture.LoadRawTextureData(config.output.u.RGBA.rgba, size);
                    texture.Apply(updateMipmaps: false, makeNoLongerReadable: true);
                    timestamp += iter.duration;
                    ret.Add((texture, timestamp));
                }while (Demux.WebPDemuxNextFrame(ref iter) == 1);

                Demux.WebPDemuxDelete(webPDemuxer);
                Demux.WebPDemuxReleaseIterator(ref iter);
            }
        }
        finally
        {
            Marshal.FreeHGlobal(webpDataPtr);
            Marshal.FreeHGlobal(configPtr);
            Marshal.FreeHGlobal(iterPtr);
        }

        return(ret);
    }