/// <summary> /// Compresses the specified image passed in the source pixel buffer. /// </summary> /// <param name="info">The meta info that describes the format and type of the pixels.</param> /// <param name="pixels">An array of bytes that represents the content of a bitmap image.</param> /// <param name="jfifHeader">if set to <c>true</c> a JFIF header will be added to the encoded byte stream.</param> /// <returns>An arraySegment with a reference to the byte array with the compressed data in the JPEG-LS format.</returns> /// <exception cref="ArgumentNullException">info -or- pixels is null.</exception> /// <exception cref="ArgumentOutOfRangeException">info.Width -or- info.Height contains an invalid value.</exception> /// <exception cref="InvalidDataException">The compressed output doesn't fit into the maximum defined output buffer.</exception> public static ArraySegment <byte> Compress(JpegLSMetadataInfo info, byte[] pixels, bool jfifHeader = false) { if (info == null) { throw new ArgumentNullException(nameof(info)); } if (info.Width <= 0 || info.Width > 65535) { throw new ArgumentOutOfRangeException(nameof(info), "info.Width property needs to be in the range <0, 65535>"); } if (info.Height <= 0 || info.Height > 65535) { throw new ArgumentOutOfRangeException(nameof(info), "info.Height property needs to be in the range <0, 65535>"); } if (pixels == null) { throw new ArgumentNullException(nameof(pixels)); } Contract.EndContractBlock(); var pixelCount = pixels.Length; Contract.Assume(pixelCount > 0 && pixelCount <= pixels.Length); return(Compress(info, pixels, pixelCount, jfifHeader)); }
/// <summary> /// Compresses the specified image passed in the source pixel buffer. /// </summary> /// <param name="info">The meta info that describes the format and type of the pixels.</param> /// <param name="pixels">An array of bytes that represents the content of a bitmap image.</param> /// <param name="pixelCount">The number of pixel in the pixel array.</param> /// <param name="jfifHeader">if set to <c>true</c> a JFIF header will be added to the encoded byte stream.</param> /// <returns>An arraySegment with a reference to the byte array with the compressed data in the JPEG-LS format.</returns> /// <exception cref="ArgumentNullException">info -or- pixels is null.</exception> /// <exception cref="ArgumentOutOfRangeException">info.Width -or- info.Height -or- pixelCount contains an invalid value.</exception> /// <exception cref="InvalidDataException">The compressed output doesn't fit into the maximum defined output buffer.</exception> public static ArraySegment <byte> Compress(JpegLSMetadataInfo info, byte[] pixels, int pixelCount, bool jfifHeader = false) { if (info == null) { throw new ArgumentNullException(nameof(info)); } if (pixels == null) { throw new ArgumentNullException(nameof(pixels)); } if (pixelCount <= 0 || pixelCount > pixels.Length) { throw new ArgumentOutOfRangeException(nameof(pixelCount), "pixelCount <= 0 || pixelCount > pixels.Length"); } const int JpegLSHeaderLength = 100; // Assume compressed size <= uncompressed size (covers 99% of the cases). var buffer = new byte[pixels.Length + JpegLSHeaderLength]; if (!TryCompress(info, pixels, pixels.Length, jfifHeader, buffer, buffer.Length, out var compressedCount)) { // Increase output buffer to hold compressed data. buffer = new byte[(int)(pixels.Length * 1.5) + JpegLSHeaderLength]; if (!TryCompress(info, pixels, pixels.Length, jfifHeader, buffer, buffer.Length, out compressedCount)) { throw new InvalidDataException("Compression failed: compressed output larger then 1.5 * input."); } } return(new ArraySegment <byte>(buffer, 0, compressedCount)); }
private static PixelFormat GetPixelFormat(JpegLSMetadataInfo info) { switch (info.ComponentCount) { case 1: if (info.BitsPerComponent == 8) { return(PixelFormats.Gray8); } break; case 3: if (info.BitsPerComponent == 8) { return(PixelFormats.Rgb24); } break; default: throw new NotSupportedException(); } throw new NotSupportedException(); }
// Design notes: // - The words compress/decompress are used as these are the terms used by the .NET BCLs (System.IO.Compression namespace) // The CharLS C API uses the terms encode/decode. // - The input/output buffers parameters are using the common .NET order, which is different the CharLS C API. /// <summary> /// Compresses the specified image passed in the source pixel buffer. /// </summary> /// <param name="info">The meta info that describes the format and type of the pixels.</param> /// <param name="pixels">An array of bytes that represents the content of a bitmap image.</param> /// <param name="jfifHeader">if set to <c>true</c> a JFIF header will be added to the encoded byte stream.</param> /// <returns>An arraySegment with a reference to the byte array with the compressed data in the JPEG-LS format.</returns> /// <exception cref="ArgumentNullException">info -or- pixels is null.</exception> /// <exception cref="ArgumentOutOfRangeException">info.Width -or- info.Height contains an invalid value.</exception> /// <exception cref="InvalidDataException">The compressed output doesn't fit into the maximum defined output buffer.</exception> public static ArraySegment <byte> Compress(JpegLSMetadataInfo info, byte[] pixels, bool jfifHeader = false) { if (pixels == null) { throw new ArgumentNullException(nameof(pixels)); } var pixelCount = pixels.Length; return(Compress(info, pixels, pixelCount, jfifHeader)); }
/// <summary> /// Tries the compress the array with pixels into the provided buffer. /// </summary> /// <param name="info">The meta info that describes the format and type of the pixels.</param> /// <param name="pixels">An array of bytes that represents the content of a bitmap image.</param> /// <param name="pixelCount">The number of pixel in the pixel array.</param> /// <param name="jfifHeader">if set to <c>true</c> a JFIF header will be added to the encoded byte stream.</param> /// <param name="destination">The destination buffer that will hold the JPEG-LS compressed (encoded) bit stream.</param> /// <param name="destinationLength">Length of the destination buffer that can be used (can be less then the length of the destination array).</param> /// <param name="compressedCount">The number of bytes that have been compressed (encoded) into the destination array.</param> /// <returns><c>true</c> when the compressed bit stream fits into the destination array, otherwise <c>false</c>.</returns> /// <exception cref="ArgumentNullException">info -or- pixels is null.</exception> /// <exception cref="ArgumentOutOfRangeException">info.Width -or- info.Height -or- pixelCount -or- destinationLength contains an invalid value.</exception> public static bool TryCompress(JpegLSMetadataInfo info, byte[] pixels, int pixelCount, bool jfifHeader, byte[] destination, int destinationLength, out int compressedCount) { if (info == null) { throw new ArgumentNullException(nameof(info)); } if (pixels == null) { throw new ArgumentNullException(nameof(pixels)); } if (pixelCount <= 0 || pixelCount > pixels.Length) { throw new ArgumentOutOfRangeException(nameof(pixelCount), "pixelCount <= 0 || pixelCount > pixels.Length"); } if (destination == null) { throw new ArgumentNullException(nameof(destination)); } if (destinationLength <= 0 || destinationLength > destination.Length) { throw new ArgumentOutOfRangeException(nameof(destinationLength), "destination <= 0 || destinationCount > destination.Length"); } var parameters = default(JlsParameters); info.CopyTo(ref parameters); if (jfifHeader) { parameters.Jfif.Version = (1 << 8) + 2; // JFIF version 1.02 parameters.Jfif.Units = 0; // No units, aspect ratio only specified parameters.Jfif.DensityX = 1; // use standard 1:1 aspect ratio. (density should always be set to non-zero values). parameters.Jfif.DensityY = 1; } JpegLSError result; if (Environment.Is64BitProcess) { result = SafeNativeMethods.JpegLsEncodeX64(destination, destinationLength, out var count, pixels, pixelCount, ref parameters, IntPtr.Zero); compressedCount = (int)count; } else { result = SafeNativeMethods.JpegLsEncodeX86(destination, destinationLength, out compressedCount, pixels, pixelCount, ref parameters, IntPtr.Zero); } if (result == JpegLSError.SourceBufferTooSmall) { return(false); } HandleResult(result); return(true); }
/// <summary> /// Compresses the specified image passed in the source pixel buffer. /// </summary> /// <param name="info">The meta info that describes the format and type of the pixels.</param> /// <param name="pixels">An array of bytes that represents the content of a bitmap image.</param> /// <param name="pixelCount">The number of pixel in the pixel array.</param> /// <param name="jfifHeader">if set to <c>true</c> a JFIF header will be added to the encoded byte stream.</param> /// <returns>An arraySegment with a reference to the byte array with the compressed data in the JPEG-LS format.</returns> /// <exception cref="ArgumentNullException">info -or- pixels is null.</exception> /// <exception cref="ArgumentOutOfRangeException">info.Width -or- info.Height -or- pixelCount contains an invalid value.</exception> /// <exception cref="InvalidDataException">The compressed output doesn't fit into the maximum defined output buffer.</exception> public static ArraySegment <byte> Compress(JpegLSMetadataInfo info, byte[] pixels, int pixelCount, bool jfifHeader) { if (info == null) { throw new ArgumentNullException(nameof(info)); } if (info.Width <= 0 || info.Width > 65535) { throw new ArgumentOutOfRangeException(nameof(info), "info.Width property needs to be in the range <0, 65535>"); } if (info.Height <= 0 || info.Height > 65535) { throw new ArgumentOutOfRangeException(nameof(info), "info.Height property needs to be in the range <0, 65535>"); } if (pixels == null) { throw new ArgumentNullException(nameof(pixels)); } if (pixelCount <= 0 || pixelCount > pixels.Length) { throw new ArgumentOutOfRangeException(nameof(pixelCount), "pixelCount <= 0 || pixelCount > pixels.Length"); } Contract.EndContractBlock(); const int JpegLSHeaderLength = 100; // Assume compressed size <= uncompressed size (covers 99% of the cases). var buffer = new byte[pixels.Length + JpegLSHeaderLength]; int compressedCount; if (!TryCompress(info, pixels, pixels.Length, jfifHeader, buffer, buffer.Length, out compressedCount)) { // Increase output buffer to hold compressed data. buffer = new byte[(int)(pixels.Length * 1.5) + JpegLSHeaderLength]; Contract.Assume(info.Width <= 65535); Contract.Assume(info.Height <= 65535); if (!TryCompress(info, pixels, pixels.Length, jfifHeader, buffer, buffer.Length, out compressedCount)) { throw new InvalidDataException("Compression failed: compressed output larger then 1.5 * input."); } } Contract.Assume(buffer.Length >= compressedCount); return(new ArraySegment <byte>(buffer, 0, compressedCount)); }
/// <summary> /// Tries the compress the array with pixels into the provided buffer. /// </summary> /// <param name="info">The meta info that describes the format and type of the pixels.</param> /// <param name="pixels">An array of bytes that represents the content of a bitmap image.</param> /// <param name="pixelCount">The number of pixel in the pixel array.</param> /// <param name="jfifHeader">if set to <c>true</c> a JFIF header will be added to the encoded byte stream.</param> /// <param name="destination">The destination buffer that will hold the JPEG-LS compressed (encoded) bit stream.</param> /// <param name="destinationLength">Length of the destination buffer that can be used (can be less then the length of the destination array).</param> /// <param name="compressedCount">The number of bytes that have been compressed (encoded) into the destination array.</param> /// <returns><c>true</c> when the compressed bit stream fits into the destination array, otherwise <c>false</c>.</returns> /// <exception cref="ArgumentNullException">info -or- pixels is null.</exception> /// <exception cref="ArgumentOutOfRangeException">info.Width -or- info.Height -or- pixelCount -or- destinationLength contains an invalid value.</exception> public static bool TryCompress(JpegLSMetadataInfo info, byte[] pixels, int pixelCount, bool jfifHeader, byte[] destination, int destinationLength, out int compressedCount) { if (info == null) { throw new ArgumentNullException(nameof(info)); } if (info.Width <= 0 || info.Width > 65535) { throw new ArgumentOutOfRangeException(nameof(info), "info.Width property needs to be in the range <0, 65535>"); } if (info.Height <= 0 || info.Height > 65535) { throw new ArgumentOutOfRangeException(nameof(info), "info.Height property needs to be in the range <0, 65535>"); } if (pixels == null) { throw new ArgumentNullException(nameof(pixels)); } if (pixelCount <= 0 || pixelCount > pixels.Length) { throw new ArgumentOutOfRangeException(nameof(pixelCount), "pixelCount <= 0 || pixelCount > pixels.Length"); } if (destination == null) { throw new ArgumentNullException(nameof(destination)); } if (destinationLength <= 0 || destinationLength > destination.Length) { throw new ArgumentOutOfRangeException(nameof(destinationLength), "destination <= 0 || destinationCount > destination.Length"); } Contract.Ensures(Contract.ValueAtReturn(out compressedCount) >= 0); var parameters = default(JlsParameters); info.CopyTo(ref parameters); if (jfifHeader) { parameters.Jfif.Version = (1 << 8) + 2; // JFIF version 1.02 parameters.Jfif.Units = 0; // No units, aspect ratio only specified parameters.Jfif.DensityX = 1; // use standard 1:1 aspect ratio. (density should always be set to non-zero values). parameters.Jfif.DensityY = 1; } var errorMessage = new StringBuilder(256); JpegLSError result; if (Is64BitProcess) { long count; result = SafeNativeMethods.JpegLsEncode64(destination, destinationLength, out count, pixels, pixelCount, ref parameters, errorMessage); compressedCount = (int)count; } else { result = SafeNativeMethods.JpegLsEncode(destination, destinationLength, out compressedCount, pixels, pixelCount, ref parameters, errorMessage); } Contract.Assume(compressedCount >= 0); if (result == JpegLSError.CompressedBufferTooSmall) { return(false); } HandleResult(result, errorMessage); return(true); }