/// <summary> /// Losslessly transform the JPEG image associated with this transformer instance into one or more JPEG images /// stored in the given destination buffers. Lossless transforms work by moving the raw coefficients from one /// JPEG image structure to another without altering the values of the coefficients. While this is typically /// faster than decompressing the image, transforming it, and re-compressing it, lossless transforms are not /// free. Each lossless transform requires reading and performing Huffman decoding on all of the coefficients /// in the source image, regardless of the size of the destination image, thus this method provides a means of /// generating multiple transformed images from the same source or of applying multiple transformations /// simultaneously, in order to eliminate the need to read the source coefficients multiple times. /// </summary> /// <param name="destBuffers"> /// An array of image buffers. <paramref name="destBuffers" />[i] will receive a JPEG image that has been /// transformed using the parameters in <paramref name="transforms" />[i]. Use /// <see cref="NativeMethods.bufSize" /> to determine the maximum size for each buffer based on the /// transformed or cropped width and height and the chroma subsampling used in the source image. /// </param> /// <param name="transforms">An array of <seealso cref="TurboJpegTransform" /> instances, each of which /// specifies the transform parameters and/or cropping region for the corresponding transformed output image. /// </param> /// <param name="options">The flags to use for the transforms.</param> public void Transform(byte[][] destBuffers, TurboJpegTransform[] transforms, TurboJpegFlags options) { Contract.Requires(destBuffers != null); Contract.Requires(transforms != null); Contract.Requires(transforms.Length == destBuffers.Length); Contract.Requires(Enum.IsDefined(typeof(TransformOptions), options)); Contract.Ensures(Contract.ForAll(destBuffers, _ => _ != null)); Contract.Assume(this.JpegBuffer != null, "JPEG buffer not initialized"); var count = destBuffers.Length; var destBufferPtrs = new IntPtr[count]; this.transformedSizes = new int[count]; if (NativeMethods.transform(this.Handle, this.JpegBuffer, this.JpegSize, count, ref destBufferPtrs, ref this.transformedSizes, transforms, options) != 0) { throw new Exception(TurboJpegInterop.GetLastError()); } // we now have the result in buffers on the unmanaged heap for (var i = 0; i < count; ++i) { destBuffers[i] = new byte[this.transformedSizes[i]]; Marshal.Copy(destBufferPtrs[i], destBuffers[i], 0, this.transformedSizes[i]); } }
/// <summary> /// Decompress the JPEG source image associated with this decompressor instance and output a YUV planar image to the given /// destination buffer. This method performs JPEG decompression but leaves out the colour conversion step, so a planar YUV /// image is generated instead of an RGB image. The padding of the planes in this image is the same as in the images /// generated by <seealso cref="TurboJpegCompressor#EncodeYuv(byte[], int)" />. /// <para> /// NOTE: Technically, the JPEG format uses the YCbCr colourspace, but per the convention of the digital video /// community, the TurboJPEG API uses "YUV" to refer to an image format consisting of Y, Cb, and Cr image planes. /// </para> /// </summary> /// <param name="flags">The <see cref="TurboJpegFlags" /> controlling decompression.</param> public TurboJpegBuffer DecompressToYuv(TurboJpegFlags flags) { Contract.Requires(Enum.IsDefined(typeof(TurboJpegFlags), flags)); Contract.Ensures(Contract.Result <TurboJpegBuffer>() .BufferSize > 0, "output buffer must have non-zero size"); Contract.Assume(this.jpegBuffer != null, "No JPEG image is associated with this instance"); var bufferSize = NativeMethods.bufSizeYUV(this.jpegWidth, 4, this.jpegHeight, this.jpegSubsampling); using (var buffer = new TurboJpegSafeHandle(NativeMethods.alloc(bufferSize))) { var ptr = buffer.DangerousGetHandle(); if (NativeMethods.decompressToYUV(this.Handle, this.jpegBuffer, this.jpegBuffer.Length, ref ptr, this.jpegWidth, 4, this.jpegHeight, flags) != 0) { throw new Exception(TurboJpegInterop.GetLastError()); } // we now have the result in a buffer on the unmanaged heap. return(new TurboJpegBuffer(ptr, bufferSize)); } }
internal static extern int decompressToYUV(IntPtr handle, byte[] sourceBuffer, int size, ref IntPtr destinationBuffer, int width, int pad, int height, [MarshalAs(UnmanagedType.I4)] TurboJpegFlags flags);
internal static extern int transform(IntPtr handle, byte[] sourceBuffer, int sourceSize, int count, ref IntPtr[] destinationBuffers, ref int[] sizes, TurboJpegTransform[] transforms, [MarshalAs(UnmanagedType.I4)] TurboJpegFlags flags);
internal static extern int encodeYUV(IntPtr handle, byte[] sourceBuffer, int width, int pitch, int height, [MarshalAs(UnmanagedType.I4)] PixelFormat pixelFormat, byte[] destinationBuffer, [MarshalAs(UnmanagedType.I4)] Subsampling subsamp, [MarshalAs(UnmanagedType.I4)] TurboJpegFlags flags);
internal static extern int decompress(IntPtr handle, [In][MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U1)] byte[] sourceBuffer, int size, IntPtr destinationBuffer, int desiredWidth, int pitch, int desiredHeight, [MarshalAs(UnmanagedType.I4)] PixelFormat pixelFormat, [MarshalAs(UnmanagedType.I4)] TurboJpegFlags flags);
internal static extern int compress(IntPtr handle, byte[] sourceBuffer, int width, int pitch, int height, [MarshalAs(UnmanagedType.I4)] PixelFormat pixelFormat, ref IntPtr destinationBuffer, ref ulong bufferSize, [MarshalAs(UnmanagedType.I4)] Subsampling jpegSubsamp, int jpegQual, [MarshalAs(UnmanagedType.I4)] TurboJpegFlags flags);
/// <summary> /// Decompress the JPEG source image associated with this decompressor instance and output a decompressed image to /// the given destination buffer. /// </summary> /// <param name="desiredWidth">The desired width (in pixels) of the decompressed image. If the desired image /// dimensions are different than the dimensions of the JPEG image being decompressed, then TurboJPEG will use /// scaling in the JPEG decompressor to generate the largest possible image that will fit within the desired /// dimensions. Setting this to 0 is the same as setting it to the width of the JPEG image; in other words the /// width will not be considered when determining the scaled image size). /// </param> /// <param name="pitch">The number of bytes per line of the destination image. Normally, this should be set to /// <code>scaledWidth * <see cref="TurboJpegUtilities.GetPixelSize" />(pixelFormat)</code> if the decompressed /// image is unpadded, but you can use this to, for instance, pad each line of the decompressed image to a /// 4-byte boundary or to decompress the JPEG image into a region of a larger image. NOTE: <code>scaledWidth</code> /// can be determined by calling /// <code>scalingFactor.<seealso cref="TurboJpegScalingFactor#getScaled getScaled" />(jpegWidth)</code> or by /// calling <seealso cref="GetScaledWidth" />. Setting this parameter to 0 is the equivalent of setting it to /// <code>scaledWidth * <see cref="TurboJpegUtilities.GetPixelSize" />(pixelFormat)</code>.</param> /// <param name="desiredHeight"> /// The desired Height (in pixels) of the decompressed image (or image region.) If the desired /// image dimensions are different than the dimensions of the JPEG image being decompressed, then TurboJPEG will use /// scaling in the JPEG decompressor to generate the largest possible image that will fit within the desired dimensions. /// Setting this to 0 is the same as setting it to the Height of the JPEG image (in other words, the Height will not be /// considered when determining the scaled image size.) /// </param> /// <param name="pixelFormat"> /// pixel format of the decompressed/decoded image (one of /// <seealso cref="TurboJpegUtilities#Rgb TurboJpegUtilities.PF_*" />) /// </param> /// <param name="flags"> /// the bitwise OR of one or more of /// <seealso cref="TurboJpegUtilities#BottomUp TurboJpegUtilities.FLAG_*" /> /// </param> public TurboJpegBuffer Decompress(int desiredWidth, int pitch, int desiredHeight, PixelFormat pixelFormat, TurboJpegFlags flags) { Contract.Requires(desiredWidth >= 0, "desiredWidth must be non-negative"); Contract.Requires(desiredHeight >= 0, "desiredHeight must be non-negative"); Contract.Requires(pitch >= 0, "pitch must be non-negative"); Contract.Requires(Enum.IsDefined(typeof(TurboJpegFlags), flags)); Contract.Ensures(Contract.Result <TurboJpegBuffer>() .BufferSize > 0, "output buffer must have non-zero size"); Contract.Assume(this.jpegBuffer != null, "No JPEG image is associated with this instance"); var pixelSize = TurboJpegUtilities.GetPixelSize(pixelFormat); var scaledWidth = this.GetScaledWidth(desiredWidth, desiredHeight); var scaledHeight = this.GetScaledHeight(desiredWidth, desiredHeight); if (pitch == 0) { pitch = scaledWidth * pixelSize; } var bufferSize = pitch * scaledHeight; // having allocated memory with the libjpeg-turbo allocator, we must ensure that we release it with the // matching deallocator lest Bad Things happen. Unlike compress, where the initial buffer size is a best // guess, we know the dimensions of the uncompressed image and the number of bits per pixel and so sizing // it appropriately is trivial and the buffer will never be reallocated using (var buffer = new TurboJpegSafeHandle(NativeMethods.alloc(bufferSize))) { var ptr = buffer.DangerousGetHandle(); if (NativeMethods.decompress(this.Handle, this.jpegBuffer, this.jpegBuffer.Length, ptr, desiredWidth, pitch, desiredHeight, pixelFormat, flags) != 0) { throw new Exception(TurboJpegInterop.GetLastError()); } // we now have the result in a buffer on the unmanaged heap. return(new TurboJpegBuffer(ptr, bufferSize)); } }
/// <summary> /// Encode the uncompressed source image associated with this compressor instance and return a buffer containing a /// YUV planar image. See <see cref="EncodeYuv(byte[], TurboJpegFlags)" /> for more detail. /// </summary> /// <param name="compressionOptions">Set of flags controlling compression.</param> /// <returns> a buffer containing a YUV planar image </returns> public byte[] EncodeYuv(TurboJpegFlags compressionOptions) { Contract.Ensures(Contract.Result <byte[]>().Length != 0, "output buffer must not be zero bytes long"); Contract.Assume(this.sourceWidth > 0 && this.sourceHeight > 0, "No source image is associated with this instance"); var buf = new byte[ NativeMethods.bufSizeYUV(this.sourceWidth, this.sourcePitch, this.sourceHeight, this.Subsampling)]; this.EncodeYuv(buf, compressionOptions); return(buf); }
/// <summary> /// Compress the uncompressed source image associated with this compressor instance and output a /// <see cref="TurboJpegBuffer" /> pointing to a JPEG image in memory. /// </summary> /// <param name="compressionOptions">Flags controlling compression.</param> /// <returns>A <see cref="TurboJpegBuffer"/> pointing to a JPEG image.</returns> /// <exception cref="System.InvalidOperationException">The source image has not been set.</exception> public TurboJpegBuffer Compress(TurboJpegFlags compressionOptions) { Contract.Requires(Enum.IsDefined(typeof(TurboJpegFlags), compressionOptions)); Contract.Assume(this.sourceBuffer != null, "No source image is associated with this instance"); Contract.Assume(this.jpegQuality >= 0, "JPEG quality not set"); var bufferSize = (ulong)NativeMethods.bufSize(this.sourceWidth, this.sourceHeight, this.Subsampling); // having allocated memory with the libjpeg-turbo allocator, we must ensure that we release it with the // matching deallocator lest Bad Things happen var buffer = NativeMethods.alloc((int)bufferSize); try { if (this.sourceX >= 0 || this.sourceY >= 0) { if (NativeMethods.compress(this.Handle, this.sourceBuffer, this.sourceWidth, this.sourcePitch, this.sourceHeight, this.sourcePixelFormat, ref buffer, ref bufferSize, this.Subsampling, this.jpegQuality, compressionOptions) != 0) { throw new Exception(Marshal.PtrToStringAnsi(NativeMethods.getErrorMessage())); } } // we now have the result in a buffer on the unmanaged heap. It may have moved from the original, // but libjpeg-turbo has handled the reallocation for us return(new TurboJpegBuffer(buffer, (int)bufferSize)); } catch { NativeMethods.free(buffer); throw; } }
/// <summary> /// Encode the uncompressed source image associated with this compressor instance and output a YUV planar /// image to the given destination buffer. This method uses the accelerated colour conversion routines in /// TurboJPEG's underlying codec to produce a planar YUV image that is suitable for direct video display. /// Specifically, if the chroma components are subsampled along the horizontal dimension, then the width of /// the luma plane is padded to the nearest multiple of 2 in the output image (same goes for the height of the /// luma plane, if the chroma components are subsampled along the vertical dimension.) Also, each line of each /// plane in the output image is padded to 4 bytes. Although this will work with any subsampling option, it is /// really only useful in combination with <see cref="P:Subsampling.Chroma420" />, which produces an image /// compatible with the I420 (AKA "YUV420P") format. /// <para> /// NOTE: Technically, the JPEG format uses the YCbCr colourspace but following the convention of the digital /// videocommunity (who, it's broadly admitted, didn't know anywhere near enough about video when they /// invented digital video), the TurboJPEG API uses "YUV" to refer to an image format consisting of Y, Cb, /// and Cr image planes. /// </para> /// </summary> /// <param name="destinationBuffer">A buffer that will receive the YUV planar image. Use /// <see cref="TurboJpegInterop.bufSizeYUV" /> to determine the appropriate size for this buffer based on the /// image width, height and chroma subsampling.</param> /// <param name="compressionOptions">Set of flags controlling compression.</param> public void EncodeYuv(byte[] destinationBuffer, TurboJpegFlags compressionOptions) { Contract.Requires(destinationBuffer != null, "destinationBuffer must not be null"); Contract.Assume(this.sourceBuffer != null, "No source image is associated with this instance"); NativeMethods.encodeYUV(this.Handle, this.sourceBuffer, this.sourceWidth, this.sourcePitch, this.sourceHeight, this.sourcePixelFormat, destinationBuffer, this.Subsampling, compressionOptions); this.CompressedSize = NativeMethods.bufSizeYUV(this.sourceWidth, this.sourcePitch, this.sourceHeight, this.Subsampling); }
/// <summary> /// Decompress the JPEG source image associated with this decompressor instance and output a decompressed image to /// the given destination buffer. /// </summary> /// <param name="desiredWidth">The desired width (in pixels) of the decompressed image. If the desired image /// dimensions are different than the dimensions of the JPEG image being decompressed, then TurboJPEG will use /// scaling in the JPEG decompressor to generate the largest possible image that will fit within the desired /// dimensions. Setting this to 0 is the same as setting it to the width of the JPEG image; in other words the /// width will not be considered when determining the scaled image size). /// </param> /// <param name="pitch">The number of bytes per line of the destination image. Normally, this should be set to /// <code>scaledWidth * <see cref="TurboJpegUtilities.GetPixelSize" />(pixelFormat)</code> if the decompressed /// image is unpadded, but you can use this to, for instance, pad each line of the decompressed image to a /// 4-byte boundary or to decompress the JPEG image into a region of a larger image. NOTE: <code>scaledWidth</code> /// can be determined by calling /// <code>scalingFactor.<seealso cref="TurboJpegScalingFactor#getScaled getScaled" />(jpegWidth)</code> or by /// calling <seealso cref="GetScaledWidth" />. Setting this parameter to 0 is the equivalent of setting it to /// <code>scaledWidth * <see cref="TurboJpegUtilities.GetPixelSize" />(pixelFormat)</code>.</param> /// <param name="desiredHeight"> /// The desired Height (in pixels) of the decompressed image (or image region.) If the desired /// image dimensions are different than the dimensions of the JPEG image being decompressed, then TurboJPEG will use /// scaling in the JPEG decompressor to generate the largest possible image that will fit within the desired dimensions. /// Setting this to 0 is the same as setting it to the Height of the JPEG image (in other words, the Height will not be /// considered when determining the scaled image size.) /// </param> /// <param name="pixelFormat"> /// pixel format of the decompressed/decoded image (one of /// <seealso cref="TurboJpegUtilities#Rgb TurboJpegUtilities.PF_*" />) /// </param> /// <param name="flags"> /// the bitwise OR of one or more of /// <seealso cref="TurboJpegUtilities#BottomUp TurboJpegUtilities.FLAG_*" /> /// </param> public TurboJpegBuffer Decompress(int desiredWidth, int pitch, int desiredHeight, PixelFormat pixelFormat, TurboJpegFlags flags) { Contract.Requires(desiredWidth >= 0, "desiredWidth must be non-negative"); Contract.Requires(desiredHeight >= 0, "desiredHeight must be non-negative"); Contract.Requires(pitch >= 0, "pitch must be non-negative"); Contract.Requires(Enum.IsDefined(typeof(TurboJpegFlags), flags)); Contract.Ensures(Contract.Result<TurboJpegBuffer>() .BufferSize > 0, "output buffer must have non-zero size"); Contract.Assume(this.jpegBuffer != null, "No JPEG image is associated with this instance"); var pixelSize = TurboJpegUtilities.GetPixelSize(pixelFormat); var scaledWidth = this.GetScaledWidth(desiredWidth, desiredHeight); var scaledHeight = this.GetScaledHeight(desiredWidth, desiredHeight); if (pitch == 0) { pitch = scaledWidth * pixelSize; } var bufferSize = pitch * scaledHeight; // having allocated memory with the libjpeg-turbo allocator, we must ensure that we release it with the // matching deallocator lest Bad Things happen. Unlike compress, where the initial buffer size is a best // guess, we know the dimensions of the uncompressed image and the number of bits per pixel and so sizing // it appropriately is trivial and the buffer will never be reallocated using(var buffer = new TurboJpegSafeHandle(NativeMethods.alloc(bufferSize))) { var ptr = buffer.DangerousGetHandle(); if (NativeMethods.decompress(this.Handle, this.jpegBuffer, this.jpegBuffer.Length, ptr, desiredWidth, pitch, desiredHeight, pixelFormat, flags) != 0) { throw new Exception(TurboJpegInterop.GetLastError()); } // we now have the result in a buffer on the unmanaged heap. return new TurboJpegBuffer(ptr, bufferSize); } }
/// <summary> /// Losslessly transform the JPEG image associated with this transformer instance and return an array of /// <seealso cref="TurboJpegDecompressor" /> instances, each of which has a transformed JPEG image associated /// with it. /// </summary> /// <param name="transforms">An array of <see cref="TurboJpegTransform" /> instances, each of which specifies /// the transform parameters and/or cropping region for the corresponding transformed output image.</param> /// <param name="flags">The flags to use when transforming.</param> /// <param name="preallocateBuffers">If set to <c>true</c>, preallocate buffers.</param> /// <returns> /// An array of <seealso cref="TurboJpegDecompressor" /> instances, each of which has a transformed /// JPEG image associated with it. /// </returns> public TurboJpegDecompressor[] Transform(TurboJpegTransform[] transforms, TurboJpegFlags flags, bool preallocateBuffers) { Contract.Requires(transforms != null); Contract.Requires(Enum.IsDefined(typeof(TransformOptions), flags)); Contract.Ensures(Contract.Result <TurboJpegDecompressor[]>() != null); Contract.Ensures(Contract.Result <TurboJpegDecompressor[]>().Length == transforms.Length); Contract.Ensures(Contract.ForAll(Contract.Result <TurboJpegDecompressor[]>(), _ => _ != null)); var count = transforms.Length; var destinationBuffers = new byte[count][]; Contract.Assume(this.Width > 0, "JPEG buffer not initialized"); Contract.Assume(this.Height > 0, "JPEG buffer not initialized"); var decompressors = new TurboJpegDecompressor[count]; if (preallocateBuffers) { for (var i = 0; i < count; i++) { var width = this.Width; var height = this.Height; if ((transforms[i].Options & TransformOptions.Crop) != 0) { if (transforms[i].Width != 0) { width = transforms[i].Width; } if (transforms[i].Height != 0) { height = transforms[i].Height; } } destinationBuffers[i] = new byte[NativeMethods.bufSize(width, height, this.Subsampling)]; } this.Transform(destinationBuffers, transforms, flags); for (var i = 0; i < count; i++) { decompressors[i] = new TurboJpegDecompressor(destinationBuffers[i]); } } else { var destBufferPtrs = new IntPtr[count]; this.transformedSizes = new int[count]; if (NativeMethods.transform(this.Handle, this.JpegBuffer, this.JpegSize, count, ref destBufferPtrs, ref this.transformedSizes, transforms, flags) != 0) { throw new Exception(TurboJpegInterop.GetLastError()); } // we now have the result in buffers on the unmanaged heap for (var i = 0; i < count; ++i) { decompressors[i] = new TurboJpegDecompressor(); decompressors[i].SetJpegImage(destBufferPtrs[i]); } } return(decompressors); }
/// <summary> /// Encode the uncompressed source image associated with this compressor instance and return a buffer containing a /// YUV planar image. See <see cref="EncodeYuv(byte[], TurboJpegFlags)" /> for more detail. /// </summary> /// <param name="compressionOptions">Set of flags controlling compression.</param> /// <returns> a buffer containing a YUV planar image </returns> public byte[] EncodeYuv(TurboJpegFlags compressionOptions) { Contract.Ensures(Contract.Result<byte[]>().Length != 0, "output buffer must not be zero bytes long"); Contract.Assume(this.sourceWidth > 0 && this.sourceHeight > 0, "No source image is associated with this instance"); var buf = new byte[ NativeMethods.bufSizeYUV(this.sourceWidth, this.sourcePitch, this.sourceHeight, this.Subsampling)]; this.EncodeYuv(buf, compressionOptions); return buf; }
/// <summary> /// Compress the uncompressed source image associated with this compressor instance and output a /// <see cref="TurboJpegBuffer" /> pointing to a JPEG image in memory. /// </summary> /// <param name="compressionOptions">Flags controlling compression.</param> /// <returns>A <see cref="TurboJpegBuffer"/> pointing to a JPEG image.</returns> /// <exception cref="System.InvalidOperationException">The source image has not been set.</exception> public TurboJpegBuffer Compress(TurboJpegFlags compressionOptions) { Contract.Requires(Enum.IsDefined(typeof(TurboJpegFlags), compressionOptions)); Contract.Assume(this.sourceBuffer != null, "No source image is associated with this instance"); Contract.Assume(this.jpegQuality >= 0, "JPEG quality not set"); var bufferSize = (ulong) NativeMethods.bufSize(this.sourceWidth, this.sourceHeight, this.Subsampling); // having allocated memory with the libjpeg-turbo allocator, we must ensure that we release it with the // matching deallocator lest Bad Things happen var buffer = NativeMethods.alloc((int) bufferSize); try { if (this.sourceX >= 0 || this.sourceY >= 0) { if (NativeMethods.compress(this.Handle, this.sourceBuffer, this.sourceWidth, this.sourcePitch, this.sourceHeight, this.sourcePixelFormat, ref buffer, ref bufferSize, this.Subsampling, this.jpegQuality, compressionOptions) != 0) { throw new Exception(Marshal.PtrToStringAnsi(NativeMethods.getErrorMessage())); } } // we now have the result in a buffer on the unmanaged heap. It may have moved from the original, // but libjpeg-turbo has handled the reallocation for us return new TurboJpegBuffer(buffer, (int)bufferSize); } catch { NativeMethods.free(buffer); throw; } }
/// <summary> /// Decompress the JPEG source image associated with this decompressor instance and output a YUV planar image to the given /// destination buffer. This method performs JPEG decompression but leaves out the colour conversion step, so a planar YUV /// image is generated instead of an RGB image. The padding of the planes in this image is the same as in the images /// generated by <seealso cref="TurboJpegCompressor#EncodeYuv(byte[], int)" />. /// <para> /// NOTE: Technically, the JPEG format uses the YCbCr colourspace, but per the convention of the digital video /// community, the TurboJPEG API uses "YUV" to refer to an image format consisting of Y, Cb, and Cr image planes. /// </para> /// </summary> /// <param name="flags">The <see cref="TurboJpegFlags" /> controlling decompression.</param> public TurboJpegBuffer DecompressToYuv(TurboJpegFlags flags) { Contract.Requires(Enum.IsDefined(typeof(TurboJpegFlags), flags)); Contract.Ensures(Contract.Result<TurboJpegBuffer>() .BufferSize > 0, "output buffer must have non-zero size"); Contract.Assume(this.jpegBuffer != null, "No JPEG image is associated with this instance"); var bufferSize = NativeMethods.bufSizeYUV(this.jpegWidth, 4, this.jpegHeight, this.jpegSubsampling); using(var buffer = new TurboJpegSafeHandle(NativeMethods.alloc(bufferSize))) { var ptr = buffer.DangerousGetHandle(); if (NativeMethods.decompressToYUV(this.Handle, this.jpegBuffer, this.jpegBuffer.Length, ref ptr, this.jpegWidth, 4, this.jpegHeight, flags) != 0) { throw new Exception(TurboJpegInterop.GetLastError()); } // we now have the result in a buffer on the unmanaged heap. return new TurboJpegBuffer(ptr, bufferSize); } }
/// <summary> /// Losslessly transform the JPEG image associated with this transformer instance and return an array of /// <seealso cref="TurboJpegDecompressor" /> instances, each of which has a transformed JPEG image associated /// with it. /// </summary> /// <param name="transforms">An array of <see cref="TurboJpegTransform" /> instances, each of which specifies /// the transform parameters and/or cropping region for the corresponding transformed output image.</param> /// <param name="flags">The flags to use when transforming.</param> /// <param name="preallocateBuffers">If set to <c>true</c>, preallocate buffers.</param> /// <returns> /// An array of <seealso cref="TurboJpegDecompressor" /> instances, each of which has a transformed /// JPEG image associated with it. /// </returns> public TurboJpegDecompressor[] Transform(TurboJpegTransform[] transforms, TurboJpegFlags flags, bool preallocateBuffers) { Contract.Requires(transforms != null); Contract.Requires(Enum.IsDefined(typeof(TransformOptions), flags)); Contract.Ensures(Contract.Result<TurboJpegDecompressor[]>() != null); Contract.Ensures(Contract.Result<TurboJpegDecompressor[]>().Length == transforms.Length); Contract.Ensures(Contract.ForAll(Contract.Result<TurboJpegDecompressor[]>(), _ => _ != null)); var count = transforms.Length; var destinationBuffers = new byte[count][]; Contract.Assume(this.Width > 0, "JPEG buffer not initialized"); Contract.Assume(this.Height > 0, "JPEG buffer not initialized"); var decompressors = new TurboJpegDecompressor[count]; if (preallocateBuffers) { for (var i = 0; i < count; i++) { var width = this.Width; var height = this.Height; if ((transforms[i].Options & TransformOptions.Crop) != 0) { if (transforms[i].Width != 0) { width = transforms[i].Width; } if (transforms[i].Height != 0) { height = transforms[i].Height; } } destinationBuffers[i] = new byte[NativeMethods.bufSize(width, height, this.Subsampling)]; } this.Transform(destinationBuffers, transforms, flags); for (var i = 0; i < count; i++) { decompressors[i] = new TurboJpegDecompressor(destinationBuffers[i]); } } else { var destBufferPtrs = new IntPtr[count]; this.transformedSizes = new int[count]; if (NativeMethods.transform(this.Handle, this.JpegBuffer, this.JpegSize, count, ref destBufferPtrs, ref this.transformedSizes, transforms, flags) != 0) { throw new Exception(TurboJpegInterop.GetLastError()); } // we now have the result in buffers on the unmanaged heap for (var i = 0; i < count; ++i) { decompressors[i] = new TurboJpegDecompressor(); decompressors[i].SetJpegImage(destBufferPtrs[i]); } } return decompressors; }