private void ReleaseUnmanagedResources() { if (_decompressorHandle != IntPtr.Zero) { TurboJpeg.DestroyInstance(_decompressorHandle); _decompressorHandle = IntPtr.Zero; } }
private void Initialize() { if (_decompressorHandle != IntPtr.Zero) { return; } _decompressorHandle = TurboJpeg.InitDecompressorInstance(); if (_decompressorHandle == IntPtr.Zero) { throw new RfbProtocolException($"Initializing TurboJPEG decompressor instance failed: {TurboJpeg.GetLastError()}"); } }
public unsafe void DecodeJpegTo32Bit(Span <byte> jpegBuffer, Span <byte> pixelsBuffer, int expectedWidth, int expectedHeight, PixelFormat preferredPixelFormat, out PixelFormat usedPixelFormat, CancellationToken cancellationToken = default) { if (_disposed) { throw new ObjectDisposedException(nameof(TurboJpegDecoder)); } if (!TurboJpeg.IsAvailable) { throw new InvalidOperationException("The TurboJPEG decoder is unavailable on the current system."); } // Initialize on first use if (_decompressorHandle == IntPtr.Zero) { Initialize(); } cancellationToken.ThrowIfCancellationRequested(); int jpegBufferLength = jpegBuffer.Length; int pixelsBufferLength = pixelsBuffer.Length; // NOTE: We only use 32bit pixel formats, so we don't have to deal with padding here. // Find a fitting pixel format that requires the least conversion later var tjPixelFormat = TurboJpegPixelFormat.RGBA; usedPixelFormat = RgbaCompatiblePixelFormat; if (BgraCompatiblePixelFormat.IsBinaryCompatibleTo(preferredPixelFormat)) { tjPixelFormat = TurboJpegPixelFormat.BGRA; usedPixelFormat = BgraCompatiblePixelFormat; } else if (ArgbCompatiblePixelFormat.IsBinaryCompatibleTo(preferredPixelFormat)) { tjPixelFormat = TurboJpegPixelFormat.ARGB; usedPixelFormat = ArgbCompatiblePixelFormat; } else if (AbgrCompatiblePixelFormat.IsBinaryCompatibleTo(preferredPixelFormat)) { tjPixelFormat = TurboJpegPixelFormat.ABGR; usedPixelFormat = AbgrCompatiblePixelFormat; } fixed(byte *jpegBufferPtr = jpegBuffer) fixed(byte *pixelsBufferPtr = pixelsBuffer) { // Retrieve the JPEG header information so we can make sure, that our buffer is large enough if (TurboJpeg.DecompressHeader(_decompressorHandle, (IntPtr)jpegBufferPtr, (ulong)jpegBufferLength, out int width, out int height, out _, out _) == -1) { throw new RfbProtocolException($"Decompressiong JPEG header failed: {TurboJpeg.GetLastError()}"); } // Validate the image size if (width != expectedWidth || height != expectedHeight) { throw new RfbProtocolException( $"Cannot decode JPEG image because it's size of {width}x{height} does not match the expected size {expectedWidth}x{expectedHeight}."); } // Validate the buffer size int stride = width * 4; int requiredBufferLength = stride * height; if (pixelsBufferLength < requiredBufferLength) { throw new RfbProtocolException( $"Cannot decode JPEG image ({width}x{height}) because it's size of {requiredBufferLength} bytes would exceed the pixels buffer size of {pixelsBufferLength} when decompressing. "); } Debug.Assert(pixelsBufferLength == requiredBufferLength, "pixelsBufferLength == requiredBufferLength"); // Decompress the image to the pixels buffer if (TurboJpeg.Decompress(_decompressorHandle, (IntPtr)jpegBufferPtr, (ulong)jpegBufferLength, (IntPtr)pixelsBufferPtr, width, stride, height, (int)tjPixelFormat, (int)(TurboJpegFlags.FastUpsample | TurboJpegFlags.FastDct | TurboJpegFlags.NoRealloc)) == -1) { throw new RfbProtocolException($"Decompressiong JPEG image failed: {TurboJpeg.GetLastError()}"); } } }