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); } } }
/// <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); }
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); }
/// <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); }
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); }