internal static unsafe void EncodeWebP(Stream writer, Texture2D texture, bool lossless, float quality) { bool hasAlpha = HasAlpha(texture); // Stream stream = fileInfo.Create(); var format = hasAlpha ? UnityEngine.TextureFormat.RGBA32 : UnityEngine.TextureFormat.RGB24; var outputTexture = BlitTexture(texture, format); WebPConfig config = new WebPConfig(); WebPPicture picture = new WebPPicture(); if (WebpEncoderNativeCalls.WebPConfigInitInternal(ref config, WebPPreset.WEBP_PRESET_DEFAULT, quality, WEBP_ENCODER_ABI_VERSION) == 0) { throw new Exception("Failed to initialize WebPConfig."); } if (lossless) { //TODO: refine the level value [0..9] we want to set. 6 seems a good default value WebpEncoderNativeCalls.WebPConfigLosslessPreset(ref config, 6); } if (WebpEncoderNativeCalls.WebPValidateConfig(ref config) == 0) { throw new Exception("Failed to validate WebPConfig."); } if (WebpEncoderNativeCalls.WebPPictureInitInternal(ref picture, WEBP_ENCODER_ABI_VERSION) == 0) { throw new Exception("Failed to initialize WebPPicture, version mismatch."); // version mismatch error } picture.width = outputTexture.width; picture.height = outputTexture.height; if (WebpEncoderNativeCalls.WebPPictureAlloc(ref picture) == 0) { throw new Exception("Failed to allocate WebPPicture"); } GCHandle lPinnedArray = GCHandle.Alloc(outputTexture.GetRawTextureData(), GCHandleType.Pinned); fixed(byte *buffer = outputTexture.GetRawTextureData()) { if (hasAlpha) { int stride = picture.width * 4; byte *lTmpDataPtr = buffer + (picture.height - 1) * stride; WebpEncoderNativeCalls.WebPPictureImportRGBA(ref picture, lTmpDataPtr, -stride); } else { int stride = picture.width * 3; byte *lTmpDataPtr = buffer + (picture.height - 1) * stride; WebpEncoderNativeCalls.WebPPictureImportRGB(ref picture, lTmpDataPtr, -stride); } } //Write the compressed data in WebPWriterFunction picture.writer = (IntPtr data, UIntPtr size, ref WebPPicture pPicture) => { byte[] res = new byte[size.ToUInt32()]; Marshal.Copy(data, res, 0, (int)size.ToUInt32()); try { writer.Write(res, 0, res.Length); } catch (Exception e) { throw new Exception("Failed in writing a WebP compressed image to a stream." + e.Message); } return(1); }; if (WebpEncoderNativeCalls.WebPEncode(ref config, ref picture) == 0) { WebpEncoderNativeCalls.WebPPictureFree(ref picture); throw new Exception($"Failed to encode webp image error: {picture.error_code.ToString()}"); } WebpEncoderNativeCalls.WebPPictureFree(ref picture); writer.Dispose(); lPinnedArray.Free(); }
internal int MyFileWriter([In] IntPtr data, [param: MarshalAs(UnmanagedType.SysUInt)] UIntPtr data_size, ref WebPPicture picture) { int blocksize = (int)data_size; if (this._preallocateOnDisk) { this._preallocateOnDisk = false; // The first memory block that decoder push will be the webp file header (which contains the WebPContent size in bytes). // Total of 12 bytes: // 4: ASCII "RIFF" // 4: WebP Content Size // 4: ASCII "WEBP" // Get the WebPContent size in bytes int contentSizeWithoutRIFFHeader = Marshal.ReadInt32(data, 4); contentSizeWithoutRIFFHeader += 8; // Because the we got above is starting from offset 8 of the real file. try { this.fs.SetLength(contentSizeWithoutRIFFHeader); } catch { // Pre-allocate size on disk failed. Either not enough space or I don't know. return(0); } } unsafe { byte *b = (byte *)(data.ToPointer()); try { for (int i = 0; i < blocksize; i++) { // WriteByte may cause overhead but Marshal.Copy to use Write(byte[], int, int) is not good in case, either. // WriteByte method still uses FileStream's buffer, by the way. this.fs.WriteByte(b[i]); } } catch { // Tell the native library to stop because we got an error here. // Either the file has been removed or the write permission has been cut off by someone (in case it's a network file) // Or locked by anti-virus or anything. return(0); } } return(1); }