/// <summary> /// Compresses input image to the jpeg format with specified quality. /// </summary> /// <param name="srcBuf"> /// Image buffer containing RGB, grayscale, or CMYK pixels to be compressed. /// This buffer is not modified. /// </param> /// <param name="stride"> /// Bytes per line in the source image. /// Normally, this should be <c>width * BytesPerPixel</c> if the image is unpadded, /// or <c>TJPAD(width * BytesPerPixel</c> if each line of the image /// is padded to the nearest 32-bit boundary, as is the case for Windows bitmaps. /// You can also be clever and use this parameter to skip lines, etc. /// Setting this parameter to 0 is the equivalent of setting it to /// <c>width * BytesPerPixel</c>. /// </param> /// <param name="width">Width (in pixels) of the source image.</param> /// <param name="height">Height (in pixels) of the source image.</param> /// <param name="pixelFormat">Pixel format of the source image (see <see cref="PixelFormat"/> "Pixel formats").</param> /// <param name="subSamp"> /// The level of chrominance subsampling to be used when /// generating the JPEG image (see <see cref="TJSubsamplingOption"/> "Chrominance subsampling options".) /// </param> /// <param name="quality">The image quality of the generated JPEG image (1 = worst, 100 = best).</param> /// <param name="flags">The bitwise OR of one or more of the <see cref="TJFlags"/> "flags".</param> /// <returns> /// A <see cref="byte"/> array containing the compressed image. /// </returns> /// <exception cref="TJException"> /// Throws if compress function failed. /// </exception> /// <exception cref="ObjectDisposedException">Object is disposed and can not be used anymore.</exception> /// <exception cref="NotSupportedException"> /// Some parameters' values are incompatible: /// <list type="bullet"> /// <item><description>Subsampling not equals to <see cref="TJSubsamplingOption.Gray"/> and pixel format <see cref="TJPixelFormat.Gray"/></description></item> /// </list> /// </exception> public unsafe byte[] Compress(byte[] srcBuf, int stride, int width, int height, TJPixelFormat tjPixelFormat, TJSubsamplingOption subSamp, int quality, TJFlags flags) { if (this.isDisposed) { throw new ObjectDisposedException("this"); } CheckOptionsCompatibilityAndThrow(subSamp, tjPixelFormat); var buf = IntPtr.Zero; ulong bufSize = 0; try { fixed(byte *srcBufPtr = srcBuf) { var result = TurboJpegImport.TjCompress2( this.compressorHandle, (IntPtr)srcBufPtr, width, stride, height, (int)tjPixelFormat, ref buf, ref bufSize, (int)subSamp, quality, (int)flags); if (result == -1) { TJUtils.GetErrorAndThrow(); } } var jpegBuf = new byte[bufSize]; Marshal.Copy(buf, jpegBuf, 0, (int)bufSize); return(jpegBuf); } finally { TurboJpegImport.TjFree(buf); } }
/// <summary>Transforms input image into one or several destinations.</summary> /// <param name="jpegBuf">Pointer to a buffer containing the JPEG image to decompress. This buffer is not modified.</param> /// <param name="jpegBufSize">Size of the JPEG image (in bytes).</param> /// <param name="transforms">Array of transform descriptions to be applied to the source image. </param> /// <param name="flags">The bitwise OR of one or more of the <see cref="TJFlags"/> "flags".</param> /// <returns>Array of transformed jpeg images.</returns> /// <exception cref="ArgumentNullException"><paramref name="transforms"/> is <see langword="null" />.</exception> /// <exception cref="ArgumentException">Transforms can not be empty.</exception> /// <exception cref="TJException"> Throws if low level turbo jpeg function fails. </exception> public byte[][] Transform(IntPtr jpegBuf, ulong jpegBufSize, TJTransformDescription[] transforms, TJFlags flags) { if (transforms == null) { throw new ArgumentNullException(nameof(transforms)); } if (transforms.Length == 0) { throw new ArgumentException("Transforms can not be empty", nameof(transforms)); } // ReSharper disable once ExceptionNotDocumented var count = transforms.Length; var destBufs = new IntPtr[count]; var destSizes = new uint[count]; int subsampl; int colorspace; int width; int height; var funcResult = TurboJpegImport.TjDecompressHeader( this.transformHandle, jpegBuf, jpegBufSize, out width, out height, out subsampl, out colorspace); if (funcResult == -1) { TJUtils.GetErrorAndThrow(); } Size mcuSize; if (!TurboJpegImport.MCUSizes.TryGetValue((TJSubsamplingOption)subsampl, out mcuSize)) { throw new TJException("Unable to read Subsampling Options from jpeg header"); } var tjTransforms = new TjTransform[count]; for (var i = 0; i < count; i++) { var x = CorrectRegionCoordinate(transforms[i].Region.X, mcuSize.Width); var y = CorrectRegionCoordinate(transforms[i].Region.Y, mcuSize.Height); var w = CorrectRegionSize(transforms[i].Region.X, x, transforms[i].Region.W, width); var h = CorrectRegionSize(transforms[i].Region.Y, y, transforms[i].Region.H, height); tjTransforms[i] = new TjTransform { Op = (int)transforms[i].Operation, Options = (int)transforms[i].Options, R = new TJRegion { X = x, Y = y, W = w, H = h, }, Data = transforms[i].CallbackData, CustomFilter = transforms[i].CustomFilter, }; } var transformsPtr = TJUtils.StructArrayToIntPtr(tjTransforms); try { funcResult = TurboJpegImport.TjTransform( this.transformHandle, jpegBuf, jpegBufSize, count, destBufs, destSizes, transformsPtr, (int)flags); if (funcResult == -1) { TJUtils.GetErrorAndThrow(); } var result = new List <byte[]>(); for (var i = 0; i < destBufs.Length; i++) { var ptr = destBufs[i]; var size = destSizes[i]; var item = new byte[size]; Marshal.Copy(ptr, item, 0, (int)size); result.Add(item); TurboJpegImport.TjFree(ptr); } return(result.ToArray()); } finally { TJUtils.FreePtr(transformsPtr); } }
/// <summary> /// Free an image buffer previously allocated by TurboJPEG. /// </summary> /// <param name="buffer">Address of the buffer to free.</param> /// <seealso cref="Alloc"/> public static void Free(IntPtr buffer) => TurboJpegImport.TjFree(buffer);