Ejemplo n.º 1
0
    private unsafe List <(Texture2D, int)> LoadAnimation(string loadPath)
    {
        List <(Texture2D, int)> ret = new List <(Texture2D, int)>();
        TextAsset textasset         = Resources.Load <TextAsset>(loadPath);

        byte[] bytes = textasset.bytes;
        WebPAnimDecoderOptions option = new WebPAnimDecoderOptions
        {
            use_threads = 1,
            color_mode  = (unity.libwebp.Interop.WEBP_CSP_MODE)WEBP_CSP_MODE.MODE_RGBA
        };

        NativeLibwebpdemux.WebPAnimDecoderOptionsInit(&option);
        fixed(byte *p = bytes)
        {
            WebPData webpdata = new WebPData
            {
                bytes = p,
                size  = new UIntPtr((uint)bytes.Length)
            };
            WebPAnimDecoder *dec       = NativeLibwebpdemux.WebPAnimDecoderNew(&webpdata, &option);
            WebPAnimInfo     anim_info = new WebPAnimInfo();

            NativeLibwebpdemux.WebPAnimDecoderGetInfo(dec, &anim_info);

            Debug.LogWarning($"{anim_info.frame_count} {anim_info.canvas_width}/{anim_info.canvas_height}");

            int size = (int)anim_info.canvas_width * 4 * (int)anim_info.canvas_height;

            dec->config_.options.flip = 1;
            dec->config_.options.no_fancy_upsampling = 1;

            int timestamp = 0;

            IntPtr pp = new IntPtr();
            byte **unmanagedPointer = (byte **)&pp;

            for (int i = 0; i < anim_info.frame_count; ++i)
            {
                int result = NativeLibwebpdemux.WebPAnimDecoderGetNext(dec, unmanagedPointer, &timestamp);
                Assert.AreEqual(1, result);

                int  lWidth   = (int)anim_info.canvas_width;
                int  lHeight  = (int)anim_info.canvas_height;
                bool lMipmaps = false;
                bool lLinear  = false;

                Texture2D texture = Texture2DExt.CreateWebpTexture2D(lWidth, lHeight, lMipmaps, lLinear);
                texture.LoadRawTextureData(pp, size);
                texture.Apply(updateMipmaps: false, makeNoLongerReadable: true);
                ret.Add((texture, timestamp));
            }
            NativeLibwebpdemux.WebPAnimDecoderReset(dec);
            NativeLibwebpdemux.WebPAnimDecoderDelete(dec);
        }

        return(ret);
    }
Ejemplo n.º 2
0
        /// <summary>
        /// 从字节数组解码webP动画
        /// </summary>
        /// <param name="data">原始的webp图片</param>
        /// <returns>webP动画</returns>
        public WebPAnimation DecodeFromBytes(byte[] data)
        {
            //将原始数据封装成webPData结构体
            WebPData struWebPData = new WebPData(data);
            //调用其它方法来解码
            WebPAnimation animation = DecodeFromWebPData(struWebPData);

            //释放结构体空间
            struWebPData.Dispose();
            return(animation);
        }
Ejemplo n.º 3
0
        /// <summary>
        /// 从WebPData结构体解码动画
        /// </summary>
        /// <param name="webPData">WebPData结构体</param>
        /// <returns>WebP动画</returns>
        public WebPAnimation DecodeFromWebPData(WebPData webPData)
        {
            //从非托管内存空间创建指向webPData结构体的指针
            IntPtr ptrWebPData = Marshal.AllocHGlobal(Marshal.SizeOf(webPData));

            //将webPData复制到非托管内存空间
            Marshal.StructureToPtr(webPData, ptrWebPData, true);
            //解码
            WebPAnimation animation = DecodeFromWebPDataPointer(ptrWebPData);

            //释放开辟的内存
            Marshal.FreeHGlobal(ptrWebPData);
            //将指针置为空
            ptrWebPData = IntPtr.Zero;
            //返回解码后的动画
            return(animation);
        }
Ejemplo n.º 4
0
    unsafe List <(Texture2D, int)> LoadAnimation(string loadPath)
    {
        List <ValueTuple <Texture2D, int> > ret = new List <ValueTuple <Texture2D, int> >();
        TextAsset textasset = Resources.Load <TextAsset>(loadPath);

        byte[] bytes = textasset.bytes;
        WebPAnimDecoderOptions option = new WebPAnimDecoderOptions
        {
            use_threads = 1,
            color_mode  = WEBP_CSP_MODE.MODE_RGBA
        };

        libwebpdemux.WebPAnimDecoderOptionsInit(ref option);
        fixed(byte *p = bytes)
        {
            IntPtr   ptr      = (IntPtr)p;
            WebPData webpdata = new WebPData
            {
                bytes = ptr,
                size  = new UIntPtr((uint)bytes.Length)
            };
            IntPtr       dec       = libwebpdemux.WebPAnimDecoderNew(ref webpdata, ref option);
            WebPAnimInfo anim_info = new WebPAnimInfo();

            libwebpdemux.WebPAnimDecoderGetInfo(dec, ref anim_info);

            Debug.LogWarning($"{anim_info.frame_count} {anim_info.canvas_width}/{anim_info.canvas_height}");

            int size = anim_info.canvas_width * 4 * anim_info.canvas_height;

            IntPtr unmanagedPointer = new IntPtr();
            int    timestamp        = 0;

            for (int i = 0; i < anim_info.frame_count; ++i)
            {
                int  result   = libwebpdemux.WebPAnimDecoderGetNext(dec, ref unmanagedPointer, ref timestamp);
                int  lWidth   = anim_info.canvas_width;
                int  lHeight  = anim_info.canvas_height;
                bool lMipmaps = false;
                bool lLinear  = false;

                Texture2D texture = new Texture2D(lWidth, lHeight, TextureFormat.RGBA32, lMipmaps, lLinear);
                texture.LoadRawTextureData(unmanagedPointer, size);

                {// Flip updown.
                    // ref: https://github.com/netpyoung/unity.webp/issues/18
                    // ref: https://github.com/webmproject/libwebp/blob/master/src/demux/anim_decode.c#L309
                    Color[] pixels        = texture.GetPixels();
                    Color[] pixelsFlipped = new Color[pixels.Length];
                    for (int y = 0; y < anim_info.canvas_height; y++)
                    {
                        Array.Copy(pixels, y * anim_info.canvas_width, pixelsFlipped, (anim_info.canvas_height - y - 1) * anim_info.canvas_width, anim_info.canvas_width);
                    }
                    texture.SetPixels(pixelsFlipped);
                }

                texture.Apply();
                ret.Add((texture, timestamp));
            }
            libwebpdemux.WebPAnimDecoderReset(dec);
            libwebpdemux.WebPAnimDecoderDelete(dec);
        }

        return(ret);
    }
Ejemplo n.º 5
0
    private unsafe List <(Texture2D, int)> LoadAnimation(string loadPath)
    {
        List <(Texture2D, int)> ret = new List <(Texture2D, int)>();
        TextAsset textasset         = Resources.Load <TextAsset>(loadPath);

        byte[] bytes = textasset.bytes;
        WebPAnimDecoderOptions option = new WebPAnimDecoderOptions
        {
            use_threads = 1,
            color_mode  = WEBP_CSP_MODE.MODE_RGBA,
        };

        option.padding[5] = 1;

        NativeLibwebpdemux.WebPAnimDecoderOptionsInit(&option);
        fixed(byte *p = bytes)
        {
            WebPData webpdata = new WebPData
            {
                bytes = p,
                size  = new UIntPtr((uint)bytes.Length)
            };
            WebPAnimDecoder *dec = NativeLibwebpdemux.WebPAnimDecoderNew(&webpdata, &option);
            //dec->config_.options.flip = 1;

            WebPAnimInfo anim_info = new WebPAnimInfo();

            NativeLibwebpdemux.WebPAnimDecoderGetInfo(dec, &anim_info);

            Debug.LogWarning($"{anim_info.frame_count} {anim_info.canvas_width}/{anim_info.canvas_height}");

            uint size = anim_info.canvas_width * 4 * anim_info.canvas_height;

            int timestamp = 0;

            IntPtr pp = new IntPtr();
            byte **unmanagedPointer = (byte **)&pp;

            for (int i = 0; i < anim_info.frame_count; ++i)
            {
                int result = NativeLibwebpdemux.WebPAnimDecoderGetNext(dec, unmanagedPointer, &timestamp);
                Assert.AreEqual(1, result);

                int  lWidth   = (int)anim_info.canvas_width;
                int  lHeight  = (int)anim_info.canvas_height;
                bool lMipmaps = false;
                bool lLinear  = false;

                Texture2D texture = Texture2DExt.CreateWebpTexture2D(lWidth, lHeight, lMipmaps, lLinear);
                texture.LoadRawTextureData(pp, (int)size);

                {// Flip updown.
                 // ref: https://github.com/netpyoung/unity.webp/issues/25
                 // ref: https://github.com/netpyoung/unity.webp/issues/21
                 // ref: https://github.com/webmproject/libwebp/blob/master/src/demux/anim_decode.c#L309
                    Color[] pixels        = texture.GetPixels();
                    Color[] pixelsFlipped = new Color[pixels.Length];
                    for (int y = 0; y < anim_info.canvas_height; y++)
                    {
                        Array.Copy(pixels, y * anim_info.canvas_width, pixelsFlipped, (anim_info.canvas_height - y - 1) * anim_info.canvas_width, anim_info.canvas_width);
                    }
                    texture.SetPixels(pixelsFlipped);
                }

                texture.Apply();
                ret.Add((texture, timestamp));
            }
            NativeLibwebpdemux.WebPAnimDecoderReset(dec);
            NativeLibwebpdemux.WebPAnimDecoderDelete(dec);
        }

        return(ret);
    }
Ejemplo n.º 6
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);
    }
Ejemplo n.º 7
0
    private unsafe List <(Texture2D, int)> LoadAnimation3(string loadPath)
    {
        List <ValueTuple <Texture2D, int> > ret = new List <ValueTuple <Texture2D, int> >();
        TextAsset textasset = Resources.Load <TextAsset>(loadPath);

        byte[] bytes = textasset.bytes;
        WebPAnimDecoderOptions option = new WebPAnimDecoderOptions
        {
            use_threads = 1,
            color_mode  = (unity.libwebp.Interop.WEBP_CSP_MODE)WEBP_CSP_MODE.MODE_RGBA
        };

        unity.libwebp.Interop.WebPDecoderConfig config = new unity.libwebp.Interop.WebPDecoderConfig();
        //if (Decode.WebPInitDecoderConfig(ref config) == 0)
        //{
        //    throw new Exception("WebPInitDecoderConfig failed. Wrong version?");
        //}
        NativeLibwebpdemux.WebPAnimDecoderOptionsInit(&option);

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

            WebPAnimDecoderOptions opt = new WebPAnimDecoderOptions();

            NativeLibwebpdemux.WebPAnimDecoderOptionsInit(&opt);

            WebPAnimDecoder *webPAnimDecoderPtr = NativeLibwebpdemux.WebPAnimDecoderNewInternal(&webpdata, &opt, NativeLibwebpdemux.WEBP_DEMUX_ABI_VERSION);

            //int width = 400;
            //int height = 400;
            {
                //config.input.has_alpha = 1;
                //config.options.bypass_filtering = 1;
                //config.options.no_fancy_upsampling = 1;
                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 = 100;
                config.output.colorspace = (unity.libwebp.Interop.WEBP_CSP_MODE)WEBP_CSP_MODE.MODE_RGBA;
                //config.output.is_external_memory = 1;
                //config.output.width = width;
                //config.output.height = height;
            }
            webPAnimDecoderPtr->config_ = config;
            WebPAnimInfo anim_info = new WebPAnimInfo();

            NativeLibwebpdemux.WebPAnimDecoderGetInfo(webPAnimDecoderPtr, &anim_info);

            Debug.LogWarning($"{anim_info.frame_count} {anim_info.canvas_width}/{anim_info.canvas_height}");

            int size = (int)anim_info.canvas_width * 4 * (int)anim_info.canvas_height;

            int    timestamp        = 0;
            IntPtr pp               = new IntPtr();
            byte **unmanagedPointer = (byte **)&pp;

            for (int i = 0; i < anim_info.frame_count; ++i)
            {
                int  result   = NativeLibwebpdemux.WebPAnimDecoderGetNext(webPAnimDecoderPtr, unmanagedPointer, &timestamp);
                int  lWidth   = (int)anim_info.canvas_width;
                int  lHeight  = (int)anim_info.canvas_height;
                bool lMipmaps = false;
                bool lLinear  = false;

                Texture2D texture = Texture2DExt.CreateWebpTexture2D(lWidth, lHeight, lMipmaps, lLinear);
                texture.LoadRawTextureData((IntPtr)unmanagedPointer, size);
                texture.Apply(updateMipmaps: false, makeNoLongerReadable: true);
                ret.Add((texture, timestamp));
            }

            NativeLibwebpdemux.WebPAnimDecoderReset(webPAnimDecoderPtr);
            NativeLibwebpdemux.WebPAnimDecoderDelete(webPAnimDecoderPtr);
        }

        return(ret);
    }
Ejemplo n.º 8
0
    unsafe List <(Texture2D, int)> LoadAnimation(string loadPath)
    {
        List <ValueTuple <Texture2D, int> > ret = new List <ValueTuple <Texture2D, int> >();
        TextAsset textasset = Resources.Load <TextAsset>(loadPath);

        byte[] bytes = textasset.bytes;
        WebPAnimDecoderOptions option = new WebPAnimDecoderOptions
        {
            use_threads = 1,
            color_mode  = WEBP_CSP_MODE.MODE_RGBA
        };

        Demux.WebPAnimDecoderOptionsInit(ref option);
        fixed(byte *p = bytes)
        {
            IntPtr   ptr      = (IntPtr)p;
            WebPData webpdata = new WebPData
            {
                bytes = ptr,
                size  = new UIntPtr((uint)bytes.Length)
            };
            IntPtr       dec       = Demux.WebPAnimDecoderNew(ref webpdata, ref option);
            WebPAnimInfo anim_info = new WebPAnimInfo();

            Demux.WebPAnimDecoderGetInfo(dec, ref anim_info);

            Debug.LogWarning($"{anim_info.frame_count} {anim_info.canvas_width}/{anim_info.canvas_height}");

            int size = anim_info.canvas_width * 4 * anim_info.canvas_height;

            WebPAnimDecoder decoder = (WebPAnimDecoder)Marshal.PtrToStructure(dec, typeof(WebPAnimDecoder));

            decoder.config_.options.flip = 1;
            decoder.config_.options.no_fancy_upsampling = 1;
            Marshal.StructureToPtr(decoder, dec, true);


            IntPtr unmanagedPointer = new IntPtr();
            int    timestamp        = 0;

            for (int i = 0; i < anim_info.frame_count; ++i)
            {
                int result = Demux.WebPAnimDecoderGetNext(dec, ref unmanagedPointer, ref timestamp);
                if (result != 1)
                {
                    Debug.LogError("WTF");
                }
                int  lWidth   = anim_info.canvas_width;
                int  lHeight  = anim_info.canvas_height;
                bool lMipmaps = false;
                bool lLinear  = false;

                Texture2D texture = new Texture2D(lWidth, lHeight, TextureFormat.RGBA32, lMipmaps, lLinear);
                texture.LoadRawTextureData(unmanagedPointer, size);
                texture.Apply(updateMipmaps: false, makeNoLongerReadable: true);
                ret.Add((texture, timestamp));
            }
            Demux.WebPAnimDecoderReset(dec);
            Demux.WebPAnimDecoderDelete(dec);
        }

        return(ret);
    }
Ejemplo n.º 9
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;

        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);
    }
Ejemplo n.º 10
0
    unsafe List <(Texture2D, int)> LoadAnimation3(string loadPath)
    {
        List <ValueTuple <Texture2D, int> > ret = new List <ValueTuple <Texture2D, int> >();
        TextAsset textasset = Resources.Load <TextAsset>(loadPath);

        byte[] bytes = textasset.bytes;
        WebPAnimDecoderOptions option = new WebPAnimDecoderOptions
        {
            use_threads = 1,
            color_mode  = WEBP_CSP_MODE.MODE_RGBA
        };

        var config = new WebPDecoderConfig();

        if (Decode.WebPInitDecoderConfig(ref config) == 0)
        {
            throw new Exception("WebPInitDecoderConfig failed. Wrong version?");
        }
        Demux.WebPAnimDecoderOptionsInit(ref option);
        fixed(byte *p = bytes)
        {
            IntPtr ptr      = (IntPtr)p;
            var    webpdata = new WebPData
            {
                bytes = ptr,
                size  = new UIntPtr((uint)bytes.Length)
            };

            WebPAnimDecoderOptions opt = new WebPAnimDecoderOptions();

            Demux.WebPAnimDecoderOptionsInit(ref opt);

            IntPtr webPAnimDecoderPtr = Demux.WebPAnimDecoderNewInternal(ref webpdata, ref opt, Demux.WEBP_DEMUX_ABI_VERSION);

            Debug.Log($"webPAnimDecoderPtr = {webPAnimDecoderPtr}");
            WebPAnimDecoder decoder = (WebPAnimDecoder)Marshal.PtrToStructure(webPAnimDecoderPtr, typeof(WebPAnimDecoder));

            //int width = 400;
            //int height = 400;
            {
                //config.input.has_alpha = 1;
                //config.options.bypass_filtering = 1;
                //config.options.no_fancy_upsampling = 1;
                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 = 100;
                config.output.colorspace = WEBP_CSP_MODE.MODE_RGBA;
                //config.output.is_external_memory = 1;
                //config.output.width = width;
                //config.output.height = height;
            }
            decoder.config_ = config;
            Marshal.StructureToPtr(decoder, webPAnimDecoderPtr, true);
            IntPtr       dec       = webPAnimDecoderPtr;
            WebPAnimInfo anim_info = new WebPAnimInfo();

            Demux.WebPAnimDecoderGetInfo(dec, ref anim_info);

            Debug.LogWarning($"{anim_info.frame_count} {anim_info.canvas_width}/{anim_info.canvas_height}");

            int size = anim_info.canvas_width * 4 * anim_info.canvas_height;

            IntPtr unmanagedPointer = new IntPtr();
            int    timestamp        = 0;

            for (int i = 0; i < anim_info.frame_count; ++i)
            {
                int  result   = Demux.WebPAnimDecoderGetNext(dec, ref unmanagedPointer, ref timestamp);
                int  lWidth   = anim_info.canvas_width;
                int  lHeight  = anim_info.canvas_height;
                bool lMipmaps = false;
                bool lLinear  = false;

                Texture2D texture = new Texture2D(lWidth, lHeight, TextureFormat.RGBA32, lMipmaps, lLinear);
                texture.LoadRawTextureData(unmanagedPointer, size);
                texture.Apply(updateMipmaps: false, makeNoLongerReadable: true);
                ret.Add((texture, timestamp));
            }
            Demux.WebPAnimDecoderReset(dec);
            Demux.WebPAnimDecoderDelete(dec);
        }

        return(ret);
    }