mirror of https://github.com/SixLabors/ImageSharp
Browse Source
Former-commit-id: 8442b333034f7875080f5f4ea03f1e7b00641bf6 Former-commit-id: 8484ce6c7090f50a43aa5b5a16b4140e7d0c4140 Former-commit-id: 84b0e2b0d1aca5501ae82f66814bbe35ce178abcaf/merge-core
82 changed files with 20069 additions and 6 deletions
@ -0,0 +1,8 @@ |
|||
Encoder/Decoder adapted from: |
|||
|
|||
https://github.com/yufeih/Nine.Imaging/ |
|||
https://imagetools.codeplex.com/ |
|||
|
|||
TODO: |
|||
|
|||
- Add support for all bitmap formats. |
|||
@ -0,0 +1,157 @@ |
|||
// ===============================================================================
|
|||
// JpegDecoder.cs
|
|||
// .NET Image Tools
|
|||
// ===============================================================================
|
|||
// Copyright (c) .NET Image Tools Development Group.
|
|||
// All rights reserved.
|
|||
// ===============================================================================
|
|||
|
|||
namespace ImageProcessor.Formats |
|||
{ |
|||
using System; |
|||
using System.IO; |
|||
using BitMiracle.LibJpeg; |
|||
|
|||
/// <summary>
|
|||
/// Image decoder for generating an image out of an jpg stream.
|
|||
/// </summary>
|
|||
public class JpegDecoder : IImageDecoder |
|||
{ |
|||
#region IImageDecoder Members
|
|||
|
|||
/// <summary>
|
|||
/// Gets the size of the header for this image type.
|
|||
/// </summary>
|
|||
/// <value>The size of the header.</value>
|
|||
public int HeaderSize |
|||
{ |
|||
get { return 11; } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Indicates if the image decoder supports the specified
|
|||
/// file extension.
|
|||
/// </summary>
|
|||
/// <param name="extension">The file extension.</param>
|
|||
/// <returns>
|
|||
/// <c>true</c>, if the decoder supports the specified
|
|||
/// extensions; otherwise <c>false</c>.
|
|||
/// </returns>
|
|||
/// <exception cref="System.ArgumentNullException"><paramref name="extension"/>
|
|||
/// is null (Nothing in Visual Basic).</exception>
|
|||
/// <exception cref="System.ArgumentException"><paramref name="extension"/> is a string
|
|||
/// of length zero or contains only blanks.</exception>
|
|||
public bool IsSupportedFileExtension(string extension) |
|||
{ |
|||
Guard.NotNullOrEmpty(extension, "extension"); |
|||
|
|||
if (extension.StartsWith(".")) extension = extension.Substring(1); |
|||
return extension.Equals("JPG", StringComparison.OrdinalIgnoreCase) || |
|||
extension.Equals("JPEG", StringComparison.OrdinalIgnoreCase) || |
|||
extension.Equals("JFIF", StringComparison.OrdinalIgnoreCase); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Indicates if the image decoder supports the specified
|
|||
/// file header.
|
|||
/// </summary>
|
|||
/// <param name="header">The file header.</param>
|
|||
/// <returns>
|
|||
/// <c>true</c>, if the decoder supports the specified
|
|||
/// file header; otherwise <c>false</c>.
|
|||
/// </returns>
|
|||
/// <exception cref="System.ArgumentNullException"><paramref name="header"/>
|
|||
/// is null (Nothing in Visual Basic).</exception>
|
|||
public bool IsSupportedFileFormat(byte[] header) |
|||
{ |
|||
Guard.NotNull(header, "header"); |
|||
|
|||
bool isSupported = false; |
|||
|
|||
if (header.Length >= 11) |
|||
{ |
|||
bool isJpeg = IsJpeg(header); |
|||
bool isExif = IsExif(header); |
|||
|
|||
isSupported = isJpeg || isExif; |
|||
} |
|||
|
|||
return isSupported; |
|||
} |
|||
|
|||
private bool IsExif(byte[] header) |
|||
{ |
|||
bool isExif = |
|||
header[6] == 0x45 && // E
|
|||
header[7] == 0x78 && // x
|
|||
header[8] == 0x69 && // i
|
|||
header[9] == 0x66 && // f
|
|||
header[10] == 0x00; |
|||
|
|||
return isExif; |
|||
} |
|||
|
|||
private static bool IsJpeg(byte[] header) |
|||
{ |
|||
bool isJpg = |
|||
header[6] == 0x4A && // J
|
|||
header[7] == 0x46 && // F
|
|||
header[8] == 0x49 && // I
|
|||
header[9] == 0x46 && // F
|
|||
header[10] == 0x00; |
|||
|
|||
return isJpg; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Decodes the image from the specified stream and sets
|
|||
/// the data to image.
|
|||
/// </summary>
|
|||
/// <param name="image">The image, where the data should be set to.
|
|||
/// Cannot be null (Nothing in Visual Basic).</param>
|
|||
/// <param name="stream">The stream, where the image should be
|
|||
/// decoded from. Cannot be null (Nothing in Visual Basic).</param>
|
|||
/// <exception cref="System.ArgumentNullException">
|
|||
/// <para><paramref name="image"/> is null (Nothing in Visual Basic).</para>
|
|||
/// <para>- or -</para>
|
|||
/// <para><paramref name="stream"/> is null (Nothing in Visual Basic).</para>
|
|||
/// </exception>
|
|||
public void Decode(Image image, Stream stream) |
|||
{ |
|||
Guard.NotNull(image, "image"); |
|||
Guard.NotNull(stream, "stream"); |
|||
JpegImage jpg = new JpegImage(stream); |
|||
|
|||
int pixelWidth = jpg.Width; |
|||
int pixelHeight = jpg.Height; |
|||
|
|||
byte[] pixels = new byte[pixelWidth * pixelHeight * 4]; |
|||
|
|||
if (!(jpg.Colorspace == Colorspace.RGB && jpg.BitsPerComponent == 8)) |
|||
{ |
|||
throw new NotSupportedException("JpegDecoder only support RGB color space."); |
|||
} |
|||
|
|||
for (int y = 0; y < pixelHeight; y++) |
|||
{ |
|||
SampleRow row = jpg.GetRow(y); |
|||
|
|||
for (int x = 0; x < pixelWidth; x++) |
|||
{ |
|||
Sample sample = row.GetAt(x); |
|||
|
|||
int offset = (y * pixelWidth + x) * 4; |
|||
|
|||
pixels[offset + 0] = (byte)sample[2]; |
|||
pixels[offset + 1] = (byte)sample[1]; |
|||
pixels[offset + 2] = (byte)sample[0]; |
|||
pixels[offset + 3] = (byte)255; |
|||
} |
|||
} |
|||
|
|||
image.SetPixels(pixelWidth, pixelHeight, pixels); |
|||
} |
|||
|
|||
#endregion
|
|||
} |
|||
} |
|||
@ -0,0 +1,120 @@ |
|||
// ===============================================================================
|
|||
// JpegEncoder.cs
|
|||
// .NET Image Tools
|
|||
// ===============================================================================
|
|||
// Copyright (c) .NET Image Tools Development Group.
|
|||
// All rights reserved.
|
|||
// ===============================================================================
|
|||
|
|||
|
|||
namespace ImageProcessor.Formats |
|||
{ |
|||
using System; |
|||
using System.IO; |
|||
|
|||
using BitMiracle.LibJpeg; |
|||
|
|||
/// <summary>
|
|||
/// Encoder for writing the data image to a stream in jpg format.
|
|||
/// </summary>
|
|||
public class JpegEncoder : IImageEncoder |
|||
{ |
|||
#region Properties
|
|||
|
|||
private int _quality = 100; |
|||
/// <summary>
|
|||
/// Gets or sets the quality, that will be used to encode the image. Quality
|
|||
/// index must be between 0 and 100 (compression from max to min).
|
|||
/// </summary>
|
|||
/// <value>The quality of the jpg image from 0 to 100.</value>
|
|||
public int Quality |
|||
{ |
|||
get { return _quality; } |
|||
set { _quality = value; } |
|||
} |
|||
|
|||
#endregion
|
|||
|
|||
#region IImageEncoder Members
|
|||
|
|||
/// <summary>
|
|||
/// Gets the default file extension for this encoder.
|
|||
/// </summary>
|
|||
/// <value>The default file extension for this encoder.</value>
|
|||
public string Extension |
|||
{ |
|||
get { return "JPG"; } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Indicates if the image encoder supports the specified
|
|||
/// file extension.
|
|||
/// </summary>
|
|||
/// <param name="extension">The file extension.</param>
|
|||
/// <returns>
|
|||
/// <c>true</c>, if the encoder supports the specified
|
|||
/// extensions; otherwise <c>false</c>.
|
|||
/// </returns>
|
|||
/// <exception cref="System.ArgumentNullException"><paramref name="extension"/>
|
|||
/// is null (Nothing in Visual Basic).</exception>
|
|||
/// <exception cref="System.ArgumentException"><paramref name="extension"/> is a string
|
|||
/// of length zero or contains only blanks.</exception>
|
|||
public bool IsSupportedFileExtension(string extension) |
|||
{ |
|||
Guard.NotNullOrEmpty(extension, "extension"); |
|||
|
|||
if (extension.StartsWith(".")) extension = extension.Substring(1); |
|||
return extension.Equals("JPG", StringComparison.OrdinalIgnoreCase) || |
|||
extension.Equals("JPEG", StringComparison.OrdinalIgnoreCase) || |
|||
extension.Equals("JFIF", StringComparison.OrdinalIgnoreCase); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Encodes the data of the specified image and writes the result to
|
|||
/// the specified stream.
|
|||
/// </summary>
|
|||
/// <param name="image">The image, where the data should be get from.
|
|||
/// Cannot be null (Nothing in Visual Basic).</param>
|
|||
/// <param name="stream">The stream, where the image data should be written to.
|
|||
/// Cannot be null (Nothing in Visual Basic).</param>
|
|||
/// <exception cref="System.ArgumentNullException">
|
|||
/// <para><paramref name="image"/> is null (Nothing in Visual Basic).</para>
|
|||
/// <para>- or -</para>
|
|||
/// <para><paramref name="stream"/> is null (Nothing in Visual Basic).</para>
|
|||
/// </exception>
|
|||
public void Encode(ImageBase image, Stream stream) |
|||
{ |
|||
Guard.NotNull(image, "image"); |
|||
Guard.NotNull(stream, "stream"); |
|||
|
|||
int pixelWidth = image.PixelWidth; |
|||
int pixelHeight = image.PixelHeight; |
|||
|
|||
byte[] sourcePixels = image.Pixels; |
|||
|
|||
SampleRow[] rows = new SampleRow[pixelHeight]; |
|||
|
|||
for (int y = 0; y < pixelHeight; y++) |
|||
{ |
|||
byte[] samples = new byte[pixelWidth * 3]; |
|||
|
|||
for (int x = 0; x < pixelWidth; x++) |
|||
{ |
|||
int start = x * 3; |
|||
int source = (y * pixelWidth + x) * 4; |
|||
|
|||
samples[start] = sourcePixels[source + 2]; |
|||
samples[start + 1] = sourcePixels[source + 1]; |
|||
samples[start + 2] = sourcePixels[source]; |
|||
} |
|||
|
|||
rows[y] = new SampleRow(samples, pixelWidth, 8, 3); |
|||
} |
|||
|
|||
JpegImage jpg = new JpegImage(rows, Colorspace.RGB); |
|||
jpg.WriteJpeg(stream, new CompressionParameters { Quality = Quality }); |
|||
} |
|||
|
|||
#endregion
|
|||
} |
|||
} |
|||
@ -0,0 +1,370 @@ |
|||
// --------------------------------------------------------------------------------------------------------------------
|
|||
// <copyright file="BitStream.cs" company="James South">
|
|||
// Copyright © James South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
// <summary>
|
|||
// A stream for reading bits in a sequence of bytes.
|
|||
// </summary>
|
|||
// --------------------------------------------------------------------------------------------------------------------
|
|||
|
|||
namespace ImageProcessor.Formats |
|||
{ |
|||
using System; |
|||
using System.IO; |
|||
|
|||
/// <summary>
|
|||
/// A stream for reading bits in a sequence of bytes.
|
|||
/// </summary>
|
|||
internal class BitStream : IDisposable |
|||
{ |
|||
/// <summary>
|
|||
/// The number of bits in byte.
|
|||
/// </summary>
|
|||
private const int BitsInByte = 8; |
|||
|
|||
/// <summary>
|
|||
/// A value indicating whether this instance of the given entity has been disposed.
|
|||
/// </summary>
|
|||
/// <value><see langword="true"/> if this instance has been disposed; otherwise, <see langword="false"/>.</value>
|
|||
/// <remarks>
|
|||
/// If the entity is disposed, it must not be disposed a second
|
|||
/// time. The isDisposed field is set the first time the entity
|
|||
/// is disposed. If the isDisposed field is true, then the Dispose()
|
|||
/// method will not dispose again. This help not to prolong the entity's
|
|||
/// life in the Garbage Collector.
|
|||
/// </remarks>
|
|||
private bool isDisposed; |
|||
|
|||
/// <summary>
|
|||
/// The underlying stream.
|
|||
/// </summary>
|
|||
private Stream stream; |
|||
|
|||
/// <summary>
|
|||
/// The current position.
|
|||
/// </summary>
|
|||
private int currentPosition; |
|||
|
|||
/// <summary>
|
|||
/// The size of the underlying stream.
|
|||
/// </summary>
|
|||
private int size; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="BitStream"/> class.
|
|||
/// </summary>
|
|||
public BitStream() |
|||
{ |
|||
this.stream = new MemoryStream(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="BitStream"/> class based on the
|
|||
/// specified byte array.
|
|||
/// </summary>
|
|||
/// <param name="buffer">
|
|||
/// The <see cref="T:byte[]"/> from which to create the current stream.
|
|||
/// </param>
|
|||
/// <exception cref="ArgumentNullException">
|
|||
/// Thrown if the given buffer is null.
|
|||
/// </exception>
|
|||
public BitStream(byte[] buffer) |
|||
{ |
|||
Guard.NotNull(buffer, "buffer"); |
|||
|
|||
this.stream = new MemoryStream(buffer); |
|||
this.size = this.BitsAllocated(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Finalizes an instance of the <see cref="BitStream"/> class.
|
|||
/// </summary>
|
|||
~BitStream() |
|||
{ |
|||
// Do not re-create Dispose clean-up code here.
|
|||
// Calling Dispose(false) is optimal in terms of
|
|||
// readability and maintainability.
|
|||
this.Dispose(true); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the underlying stream.
|
|||
/// </summary>
|
|||
public Stream UnderlyingStream |
|||
{ |
|||
get |
|||
{ |
|||
return this.stream; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Disposes the object and frees resources for the Garbage Collector.
|
|||
/// </summary>
|
|||
public void Dispose() |
|||
{ |
|||
this.Dispose(true); |
|||
|
|||
// This object will be cleaned up by the Dispose method.
|
|||
// Therefore, you should call GC.SuppressFinalize to
|
|||
// take this object off the finalization queue
|
|||
// and prevent finalization code for this object
|
|||
// from executing a second time.
|
|||
GC.SuppressFinalize(this); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns the size of the stream.
|
|||
/// </summary>
|
|||
/// <returns>
|
|||
/// The <see cref="int"/> representing the size.
|
|||
/// </returns>
|
|||
public int Size() |
|||
{ |
|||
return this.size; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns a number representing the given number of bits read from the stream
|
|||
/// advancing the stream by that number.
|
|||
/// </summary>
|
|||
/// <param name="bitCount">The number of bits to read.</param>
|
|||
/// <returns>
|
|||
/// The <see cref="int"/> representing the total number of bits read.
|
|||
/// </returns>
|
|||
public virtual int Read(int bitCount) |
|||
{ |
|||
Guard.LessEquals(this.Tell() + bitCount, this.BitsAllocated(), "bitCount"); |
|||
return this.ReadBits(bitCount); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes a block of bits represented by an <see cref="int"/> to the current stream.
|
|||
/// </summary>
|
|||
/// <param name="bitStorage">The bits to write.</param>
|
|||
/// <param name="bitCount">The number of bits to write.</param>
|
|||
/// <returns>
|
|||
/// The <see cref="int"/> representing the number of bits written.
|
|||
/// </returns>
|
|||
public int Write(int bitStorage, int bitCount) |
|||
{ |
|||
if (bitCount == 0) |
|||
{ |
|||
return 0; |
|||
} |
|||
|
|||
const int MaxBitsInStorage = sizeof(int) * BitsInByte; |
|||
|
|||
Guard.LessEquals(bitCount, MaxBitsInStorage, "bitCount"); |
|||
|
|||
for (int i = 0; i < bitCount; ++i) |
|||
{ |
|||
byte bit = (byte)((bitStorage << (MaxBitsInStorage - (bitCount - i))) >> (MaxBitsInStorage - 1)); |
|||
if (!this.WriteBits(bit)) |
|||
{ |
|||
return i; |
|||
} |
|||
} |
|||
|
|||
return bitCount; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Sets the position within the current stream to the specified value.
|
|||
/// </summary>
|
|||
/// <param name="position">
|
|||
/// The new position within the stream.
|
|||
/// This is relative to the <paramref name="location"/> parameter, and can be positive or negative.
|
|||
/// </param>
|
|||
/// <param name="location">
|
|||
/// A value of type <see cref="SeekOrigin"/>, which acts as the seek reference point.
|
|||
/// </param>
|
|||
public void Seek(int position, SeekOrigin location) |
|||
{ |
|||
switch (location) |
|||
{ |
|||
case SeekOrigin.Begin: |
|||
this.SeekSet(position); |
|||
break; |
|||
|
|||
case SeekOrigin.Current: |
|||
this.SeekCurrent(position); |
|||
break; |
|||
|
|||
case SeekOrigin.End: |
|||
this.SeekSet(this.Size() + position); |
|||
break; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// TODO: Document this.
|
|||
/// </summary>
|
|||
/// <returns>
|
|||
/// The <see cref="int"/>.
|
|||
/// </returns>
|
|||
public int Tell() |
|||
{ |
|||
return ((int)this.stream.Position * BitsInByte) + this.currentPosition; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Disposes the object and frees resources for the Garbage Collector.
|
|||
/// </summary>
|
|||
/// <param name="disposing">If true, the object gets disposed.</param>
|
|||
protected virtual void Dispose(bool disposing) |
|||
{ |
|||
if (this.isDisposed) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
if (disposing) |
|||
{ |
|||
if (this.stream != null) |
|||
{ |
|||
this.stream.Dispose(); |
|||
this.stream = null; |
|||
} |
|||
} |
|||
|
|||
// Note disposing is done.
|
|||
this.isDisposed = true; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns the number of bits allocated to the stream.
|
|||
/// </summary>
|
|||
/// <returns>
|
|||
/// The <see cref="int"/> representing the number of bits.
|
|||
/// </returns>
|
|||
private int BitsAllocated() |
|||
{ |
|||
return (int)this.stream.Length * BitsInByte; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns a number representing the given number of bits read from the stream.
|
|||
/// </summary>
|
|||
/// <param name="bitsCount">The number of bits to read.</param>
|
|||
/// <returns>
|
|||
/// The <see cref="int"/> representing the total number of bits read.
|
|||
/// </returns>
|
|||
private int ReadBits(int bitsCount) |
|||
{ |
|||
// Codes are packed into a continuous bit stream, high-order bit first.
|
|||
// This stream is then divided into 8-bit bytes, high-order bit first.
|
|||
// Thus, codes can straddle byte boundaries arbitrarily. After the EOD marker (code value 257),
|
|||
// any leftover bits in the final byte are set to 0.
|
|||
Guard.BetweenEquals(bitsCount, 0, 32, "bitsCount"); |
|||
|
|||
if (bitsCount == 0) |
|||
{ |
|||
return 0; |
|||
} |
|||
|
|||
int bitsRead = 0; |
|||
int result = 0; |
|||
byte[] bt = new byte[1]; |
|||
while (bitsRead == 0 || (bitsRead - this.currentPosition < bitsCount)) |
|||
{ |
|||
this.stream.Read(bt, 0, 1); |
|||
|
|||
result = result << BitsInByte; |
|||
result += bt[0]; |
|||
|
|||
bitsRead += 8; |
|||
} |
|||
|
|||
this.currentPosition = (this.currentPosition + bitsCount) % 8; |
|||
if (this.currentPosition != 0) |
|||
{ |
|||
result = result >> (BitsInByte - this.currentPosition); |
|||
|
|||
this.stream.Seek(-1, SeekOrigin.Current); |
|||
} |
|||
|
|||
if (bitsCount < 32) |
|||
{ |
|||
int mask = (1 << bitsCount) - 1; |
|||
result = result & mask; |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes a block of bits represented to the current stream.
|
|||
/// </summary>
|
|||
/// <param name="bits">
|
|||
/// The bits to write.
|
|||
/// </param>
|
|||
/// <returns>
|
|||
/// True. TODO: investigate this as it always returns true.
|
|||
/// </returns>
|
|||
private bool WriteBits(byte bits) |
|||
{ |
|||
if (this.stream.Position == this.stream.Length) |
|||
{ |
|||
byte[] bytes = { (byte)(bits << (BitsInByte - 1)) }; |
|||
this.stream.Write(bytes, 0, 1); |
|||
this.stream.Seek(-1, SeekOrigin.Current); |
|||
} |
|||
else |
|||
{ |
|||
byte[] bytes = { 0 }; |
|||
this.stream.Read(bytes, 0, 1); |
|||
this.stream.Seek(-1, SeekOrigin.Current); |
|||
|
|||
int shift = (BitsInByte - this.currentPosition - 1) % BitsInByte; |
|||
byte maskByte = (byte)(bits << shift); |
|||
|
|||
bytes[0] |= maskByte; |
|||
this.stream.Write(bytes, 0, 1); |
|||
this.stream.Seek(-1, SeekOrigin.Current); |
|||
} |
|||
|
|||
this.Seek(1, SeekOrigin.Current); |
|||
|
|||
int position = this.Tell(); |
|||
if (position > this.size) |
|||
{ |
|||
this.size = position; |
|||
} |
|||
|
|||
return true; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Sets the position within the current stream to the specified value.
|
|||
/// </summary>
|
|||
/// <param name="position">
|
|||
/// The new position within the stream. Can be positive or negative.
|
|||
/// </param>
|
|||
private void SeekSet(int position) |
|||
{ |
|||
Guard.GreaterEquals(position, 0, "position"); |
|||
|
|||
int byteDisplacement = position / BitsInByte; |
|||
this.stream.Seek(byteDisplacement, SeekOrigin.Begin); |
|||
|
|||
int shiftInByte = position - (byteDisplacement * BitsInByte); |
|||
this.currentPosition = shiftInByte; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Sets the position to current position in the current stream.
|
|||
/// </summary>
|
|||
/// <param name="position">
|
|||
/// The new position within the stream. Can be positive or negative.
|
|||
/// </param>
|
|||
private void SeekCurrent(int position) |
|||
{ |
|||
int result = this.Tell() + position; |
|||
Guard.BetweenEquals(position, 0, this.BitsAllocated(), "position"); |
|||
|
|||
this.SeekSet(result); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,326 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Diagnostics; |
|||
using System.IO; |
|||
using System.Text; |
|||
|
|||
using BitMiracle.LibJpeg.Classic; |
|||
|
|||
namespace BitMiracle.LibJpeg |
|||
{ |
|||
class BitmapDestination : IDecompressDestination |
|||
{ |
|||
/* Target file spec; filled in by djpeg.c after object is created. */ |
|||
private Stream m_output; |
|||
|
|||
private byte[][] m_pixels; |
|||
|
|||
private int m_rowWidth; /* physical width of one row in the BMP file */ |
|||
|
|||
private int m_currentRow; /* next row# to write to virtual array */ |
|||
private LoadedImageAttributes m_parameters; |
|||
|
|||
public BitmapDestination(Stream output) |
|||
{ |
|||
m_output = output; |
|||
} |
|||
|
|||
public Stream Output |
|||
{ |
|||
get |
|||
{ |
|||
return m_output; |
|||
} |
|||
} |
|||
|
|||
public void SetImageAttributes(LoadedImageAttributes parameters) |
|||
{ |
|||
if (parameters == null) |
|||
throw new ArgumentNullException("parameters"); |
|||
|
|||
m_parameters = parameters; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Startup: normally writes the file header.
|
|||
/// In this module we may as well postpone everything until finish_output.
|
|||
/// </summary>
|
|||
public void BeginWrite() |
|||
{ |
|||
//Determine width of rows in the BMP file (padded to 4-byte boundary).
|
|||
m_rowWidth = m_parameters.Width * m_parameters.Components; |
|||
while (m_rowWidth % 4 != 0) |
|||
m_rowWidth++; |
|||
|
|||
m_pixels = new byte[m_rowWidth][]; |
|||
for (int i = 0; i < m_rowWidth; i++) |
|||
m_pixels[i] = new byte[m_parameters.Height]; |
|||
|
|||
m_currentRow = 0; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Write some pixel data.
|
|||
/// </summary>
|
|||
public void ProcessPixelsRow(byte[] row) |
|||
{ |
|||
if (m_parameters.Colorspace == Colorspace.Grayscale || m_parameters.QuantizeColors) |
|||
{ |
|||
putGrayRow(row); |
|||
} |
|||
else |
|||
{ |
|||
if (m_parameters.Colorspace == Colorspace.CMYK) |
|||
putCmykRow(row); |
|||
else |
|||
putRgbRow(row); |
|||
} |
|||
|
|||
++m_currentRow; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Finish up at the end of the file.
|
|||
/// Here is where we really output the BMP file.
|
|||
/// </summary>
|
|||
public void EndWrite() |
|||
{ |
|||
writeHeader(); |
|||
writePixels(); |
|||
|
|||
/* Make sure we wrote the output file OK */ |
|||
m_output.Flush(); |
|||
} |
|||
|
|||
|
|||
/// <summary>
|
|||
/// This version is for grayscale OR quantized color output
|
|||
/// </summary>
|
|||
private void putGrayRow(byte[] row) |
|||
{ |
|||
for (int i = 0; i < m_parameters.Width; ++i) |
|||
m_pixels[i][m_currentRow] = row[i]; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// This version is for writing 24-bit pixels
|
|||
/// </summary>
|
|||
private void putRgbRow(byte[] row) |
|||
{ |
|||
/* Transfer data. Note destination values must be in BGR order |
|||
* (even though Microsoft's own documents say the opposite). |
|||
*/ |
|||
for (int i = 0; i < m_parameters.Width; ++i) |
|||
{ |
|||
int firstComponent = i * 3; |
|||
byte red = row[firstComponent]; |
|||
byte green = row[firstComponent + 1]; |
|||
byte blue = row[firstComponent + 2]; |
|||
m_pixels[firstComponent][m_currentRow] = blue; |
|||
m_pixels[firstComponent + 1][m_currentRow] = green; |
|||
m_pixels[firstComponent + 2][m_currentRow] = red; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// This version is for writing 24-bit pixels
|
|||
/// </summary>
|
|||
private void putCmykRow(byte[] row) |
|||
{ |
|||
/* Transfer data. Note destination values must be in BGR order |
|||
* (even though Microsoft's own documents say the opposite). |
|||
*/ |
|||
for (int i = 0; i < m_parameters.Width; ++i) |
|||
{ |
|||
int firstComponent = i * 4; |
|||
m_pixels[firstComponent][m_currentRow] = row[firstComponent + 2]; |
|||
m_pixels[firstComponent + 1][m_currentRow] = row[firstComponent + 1]; |
|||
m_pixels[firstComponent + 2][m_currentRow] = row[firstComponent + 0]; |
|||
m_pixels[firstComponent + 3][m_currentRow] = row[firstComponent + 3]; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Write a Windows-style BMP file header, including colormap if needed
|
|||
/// </summary>
|
|||
private void writeHeader() |
|||
{ |
|||
int bits_per_pixel; |
|||
int cmap_entries; |
|||
|
|||
/* Compute colormap size and total file size */ |
|||
if (m_parameters.Colorspace == Colorspace.Grayscale || m_parameters.QuantizeColors) |
|||
{ |
|||
bits_per_pixel = 8; |
|||
cmap_entries = 256; |
|||
} |
|||
else |
|||
{ |
|||
cmap_entries = 0; |
|||
|
|||
if (m_parameters.Colorspace == Colorspace.RGB) |
|||
bits_per_pixel = 24; |
|||
else if (m_parameters.Colorspace == Colorspace.CMYK) |
|||
bits_per_pixel = 32; |
|||
else |
|||
throw new InvalidOperationException(); |
|||
} |
|||
|
|||
byte[] infoHeader = null; |
|||
if (m_parameters.Colorspace == Colorspace.RGB) |
|||
infoHeader = createBitmapInfoHeader(bits_per_pixel, cmap_entries); |
|||
else |
|||
infoHeader = createBitmapV4InfoHeader(bits_per_pixel); |
|||
|
|||
/* File size */ |
|||
const int fileHeaderSize = 14; |
|||
int infoHeaderSize = infoHeader.Length; |
|||
int paletteSize = cmap_entries * 4; |
|||
int offsetToPixels = fileHeaderSize + infoHeaderSize + paletteSize; /* Header and colormap */ |
|||
int fileSize = offsetToPixels + m_rowWidth * m_parameters.Height; |
|||
|
|||
byte[] fileHeader = createBitmapFileHeader(offsetToPixels, fileSize); |
|||
|
|||
m_output.Write(fileHeader, 0, fileHeader.Length); |
|||
m_output.Write(infoHeader, 0, infoHeader.Length); |
|||
|
|||
if (cmap_entries > 0) |
|||
writeColormap(cmap_entries, 4); |
|||
} |
|||
|
|||
private static byte[] createBitmapFileHeader(int offsetToPixels, int fileSize) |
|||
{ |
|||
byte[] bmpfileheader = new byte[14]; |
|||
bmpfileheader[0] = 0x42; /* first 2 bytes are ASCII 'B', 'M' */ |
|||
bmpfileheader[1] = 0x4D; |
|||
PUT_4B(bmpfileheader, 2, fileSize); |
|||
/* we leave bfReserved1 & bfReserved2 = 0 */ |
|||
PUT_4B(bmpfileheader, 10, offsetToPixels); /* bfOffBits */ |
|||
return bmpfileheader; |
|||
} |
|||
|
|||
private byte[] createBitmapInfoHeader(int bits_per_pixel, int cmap_entries) |
|||
{ |
|||
byte[] bmpinfoheader = new byte[40]; |
|||
fillBitmapInfoHeader(bits_per_pixel, cmap_entries, bmpinfoheader); |
|||
return bmpinfoheader; |
|||
} |
|||
|
|||
private void fillBitmapInfoHeader(int bitsPerPixel, int cmap_entries, byte[] infoHeader) |
|||
{ |
|||
/* Fill the info header (Microsoft calls this a BITMAPINFOHEADER) */ |
|||
PUT_2B(infoHeader, 0, infoHeader.Length); /* biSize */ |
|||
PUT_4B(infoHeader, 4, m_parameters.Width); /* biWidth */ |
|||
PUT_4B(infoHeader, 8, m_parameters.Height); /* biHeight */ |
|||
PUT_2B(infoHeader, 12, 1); /* biPlanes - must be 1 */ |
|||
PUT_2B(infoHeader, 14, bitsPerPixel); /* biBitCount */ |
|||
/* we leave biCompression = 0, for none */ |
|||
/* we leave biSizeImage = 0; this is correct for uncompressed data */ |
|||
|
|||
if (m_parameters.DensityUnit == DensityUnit.DotsCm) |
|||
{ |
|||
/* if have density in dots/cm, then */ |
|||
PUT_4B(infoHeader, 24, m_parameters.DensityX * 100); /* XPels/M */ |
|||
PUT_4B(infoHeader, 28, m_parameters.DensityY * 100); /* XPels/M */ |
|||
} |
|||
PUT_2B(infoHeader, 32, cmap_entries); /* biClrUsed */ |
|||
/* we leave biClrImportant = 0 */ |
|||
} |
|||
|
|||
private byte[] createBitmapV4InfoHeader(int bitsPerPixel) |
|||
{ |
|||
byte[] infoHeader = new byte[40 + 68]; |
|||
fillBitmapInfoHeader(bitsPerPixel, 0, infoHeader); |
|||
|
|||
PUT_4B(infoHeader, 56, 0x02); /* CSType == 0x02 (CMYK) */ |
|||
|
|||
return infoHeader; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Write the colormap.
|
|||
/// Windows uses BGR0 map entries; OS/2 uses BGR entries.
|
|||
/// </summary>
|
|||
private void writeColormap(int map_colors, int map_entry_size) |
|||
{ |
|||
byte[][] colormap = m_parameters.Colormap; |
|||
int num_colors = m_parameters.ActualNumberOfColors; |
|||
|
|||
int i = 0; |
|||
if (colormap != null) |
|||
{ |
|||
if (m_parameters.ComponentsPerSample == 3) |
|||
{ |
|||
/* Normal case with RGB colormap */ |
|||
for (i = 0; i < num_colors; i++) |
|||
{ |
|||
m_output.WriteByte(colormap[2][i]); |
|||
m_output.WriteByte(colormap[1][i]); |
|||
m_output.WriteByte(colormap[0][i]); |
|||
if (map_entry_size == 4) |
|||
m_output.WriteByte(0); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
/* Grayscale colormap (only happens with grayscale quantization) */ |
|||
for (i = 0; i < num_colors; i++) |
|||
{ |
|||
m_output.WriteByte(colormap[0][i]); |
|||
m_output.WriteByte(colormap[0][i]); |
|||
m_output.WriteByte(colormap[0][i]); |
|||
if (map_entry_size == 4) |
|||
m_output.WriteByte(0); |
|||
} |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
/* If no colormap, must be grayscale data. Generate a linear "map". */ |
|||
for (i = 0; i < 256; i++) |
|||
{ |
|||
m_output.WriteByte((byte)i); |
|||
m_output.WriteByte((byte)i); |
|||
m_output.WriteByte((byte)i); |
|||
if (map_entry_size == 4) |
|||
m_output.WriteByte(0); |
|||
} |
|||
} |
|||
|
|||
/* Pad colormap with zeros to ensure specified number of colormap entries */ |
|||
if (i > map_colors) |
|||
throw new InvalidOperationException("Too many colors"); |
|||
|
|||
for (; i < map_colors; i++) |
|||
{ |
|||
m_output.WriteByte(0); |
|||
m_output.WriteByte(0); |
|||
m_output.WriteByte(0); |
|||
if (map_entry_size == 4) |
|||
m_output.WriteByte(0); |
|||
} |
|||
} |
|||
|
|||
private void writePixels() |
|||
{ |
|||
for (int row = m_parameters.Height - 1; row >= 0; --row) |
|||
for (int col = 0; col < m_rowWidth; ++col) |
|||
m_output.WriteByte(m_pixels[col][row]); |
|||
} |
|||
|
|||
|
|||
private static void PUT_2B(byte[] array, int offset, int value) |
|||
{ |
|||
array[offset] = (byte)((value) & 0xFF); |
|||
array[offset + 1] = (byte)(((value) >> 8) & 0xFF); |
|||
} |
|||
|
|||
private static void PUT_4B(byte[] array, int offset, int value) |
|||
{ |
|||
array[offset] = (byte)((value) & 0xFF); |
|||
array[offset + 1] = (byte)(((value) >> 8) & 0xFF); |
|||
array[offset + 2] = (byte)(((value) >> 16) & 0xFF); |
|||
array[offset + 3] = (byte)(((value) >> 24) & 0xFF); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,32 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
|
|||
namespace BitMiracle.LibJpeg.Classic |
|||
{ |
|||
/// <summary>
|
|||
/// The unit of density.
|
|||
/// </summary>
|
|||
/// <seealso cref="jpeg_compress_struct.Density_unit"/>
|
|||
/// <seealso cref="jpeg_decompress_struct.Density_unit"/>
|
|||
#if EXPOSE_LIBJPEG
|
|||
public |
|||
#endif
|
|||
enum DensityUnit |
|||
{ |
|||
/// <summary>
|
|||
/// Unknown density
|
|||
/// </summary>
|
|||
Unknown = 0, |
|||
|
|||
/// <summary>
|
|||
/// Dots/inch
|
|||
/// </summary>
|
|||
DotsInch = 1, |
|||
|
|||
/// <summary>
|
|||
/// Dots/cm
|
|||
/// </summary>
|
|||
DotsCm = 2 |
|||
} |
|||
} |
|||
@ -0,0 +1,55 @@ |
|||
/* Copyright (C) 2008-2011, Bit Miracle |
|||
* http://www.bitmiracle.com
|
|||
*/ |
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
|
|||
namespace BitMiracle.LibJpeg.Classic.Internal |
|||
{ |
|||
/// <summary>
|
|||
/// Encapsulates buffer of image samples for one color component
|
|||
/// When provided with funny indices (see jpeg_d_main_controller for
|
|||
/// explanation of what it is) uses them for non-linear row access.
|
|||
/// </summary>
|
|||
class ComponentBuffer |
|||
{ |
|||
private byte[][] m_buffer; |
|||
|
|||
// array of funny indices
|
|||
private int[] m_funnyIndices; |
|||
|
|||
// index of "first funny index" (used because some code uses negative
|
|||
// indices when retrieve rows)
|
|||
// see for example my_upsampler.h2v2_fancy_upsample
|
|||
private int m_funnyOffset; |
|||
|
|||
public ComponentBuffer() |
|||
{ |
|||
} |
|||
|
|||
public ComponentBuffer(byte[][] buf, int[] funnyIndices, int funnyOffset) |
|||
{ |
|||
SetBuffer(buf, funnyIndices, funnyOffset); |
|||
} |
|||
|
|||
public void SetBuffer(byte[][] buf, int[] funnyIndices, int funnyOffset) |
|||
{ |
|||
m_buffer = buf; |
|||
m_funnyIndices = funnyIndices; |
|||
m_funnyOffset = funnyOffset; |
|||
} |
|||
|
|||
public byte[] this[int i] |
|||
{ |
|||
get |
|||
{ |
|||
if (m_funnyIndices == null) |
|||
return m_buffer[i]; |
|||
|
|||
return m_buffer[m_funnyIndices[i + m_funnyOffset]]; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,29 @@ |
|||
/* Copyright (C) 2008-2011, Bit Miracle |
|||
* http://www.bitmiracle.com
|
|||
* |
|||
* Copyright (C) 1994-1996, Thomas G. Lane. |
|||
* This file is part of the Independent JPEG Group's software. |
|||
* For conditions of distribution and use, see the accompanying README file. |
|||
* |
|||
*/ |
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
|
|||
namespace BitMiracle.LibJpeg.Classic.Internal |
|||
{ |
|||
/// <summary>
|
|||
/// Operating modes for buffer controllers
|
|||
/// </summary>
|
|||
enum J_BUF_MODE |
|||
{ |
|||
JBUF_PASS_THRU, /* Plain stripwise operation */ |
|||
|
|||
/* Remaining modes require a full-image buffer to have been created */ |
|||
|
|||
JBUF_SAVE_SOURCE, /* Run source subobject only, save output */ |
|||
JBUF_CRANK_DEST, /* Run dest subobject only, using saved data */ |
|||
JBUF_SAVE_AND_PASS /* Run both subobjects, save output */ |
|||
} |
|||
} |
|||
@ -0,0 +1,116 @@ |
|||
/* Copyright (C) 2008-2011, Bit Miracle |
|||
* http://www.bitmiracle.com
|
|||
* |
|||
* Copyright (C) 1994-1996, Thomas G. Lane. |
|||
* This file is part of the Independent JPEG Group's software. |
|||
* For conditions of distribution and use, see the accompanying README file. |
|||
* |
|||
*/ |
|||
|
|||
/* |
|||
* This file contains tables and miscellaneous utility routines needed |
|||
* for both compression and decompression. |
|||
* Note we prefix all global names with "j" to minimize conflicts with |
|||
* a surrounding application. |
|||
*/ |
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
|
|||
namespace BitMiracle.LibJpeg.Classic.Internal |
|||
{ |
|||
class JpegUtils |
|||
{ |
|||
/* |
|||
* jpeg_natural_order[i] is the natural-order position of the i'th element |
|||
* of zigzag order. |
|||
* |
|||
* When reading corrupted data, the Huffman decoders could attempt |
|||
* to reference an entry beyond the end of this array (if the decoded |
|||
* zero run length reaches past the end of the block). To prevent |
|||
* wild stores without adding an inner-loop test, we put some extra |
|||
* "63"s after the real entries. This will cause the extra coefficient |
|||
* to be stored in location 63 of the block, not somewhere random. |
|||
* The worst case would be a run-length of 15, which means we need 16 |
|||
* fake entries. |
|||
*/ |
|||
public static int[] jpeg_natural_order = |
|||
{ |
|||
0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, 12, |
|||
19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, 35, |
|||
42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51, |
|||
58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, |
|||
63, 63, 63, 63, 63, 63, 63, 63, 63, |
|||
/* extra entries for safety in decoder */ |
|||
63, 63, 63, 63, 63, 63, 63, 63 |
|||
}; |
|||
|
|||
/* We assume that right shift corresponds to signed division by 2 with |
|||
* rounding towards minus infinity. This is correct for typical "arithmetic
|
|||
* shift" instructions that shift in copies of the sign bit.
|
|||
* RIGHT_SHIFT provides a proper signed right shift of an int quantity. |
|||
* It is only applied with constant shift counts. SHIFT_TEMPS must be |
|||
* included in the variables of any routine using RIGHT_SHIFT. |
|||
*/ |
|||
public static int RIGHT_SHIFT(int x, int shft) |
|||
{ |
|||
return (x >> shft); |
|||
} |
|||
|
|||
/* Descale and correctly round an int value that's scaled by N bits. |
|||
* We assume RIGHT_SHIFT rounds towards minus infinity, so adding |
|||
* the fudge factor is correct for either sign of X. |
|||
*/ |
|||
public static int DESCALE(int x, int n) |
|||
{ |
|||
return RIGHT_SHIFT(x + (1 << (n - 1)), n); |
|||
} |
|||
|
|||
//////////////////////////////////////////////////////////////////////////
|
|||
// Arithmetic utilities
|
|||
|
|||
/// <summary>
|
|||
/// Compute a/b rounded up to next integer, ie, ceil(a/b)
|
|||
/// Assumes a >= 0, b > 0
|
|||
/// </summary>
|
|||
public static int jdiv_round_up(int a, int b) |
|||
{ |
|||
return (a + b - 1) / b; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Compute a rounded up to next multiple of b, ie, ceil(a/b)*b
|
|||
/// Assumes a >= 0, b > 0
|
|||
/// </summary>
|
|||
public static int jround_up(int a, int b) |
|||
{ |
|||
a += b - 1; |
|||
return a - (a % b); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Copy some rows of samples from one place to another.
|
|||
/// num_rows rows are copied from input_array[source_row++]
|
|||
/// to output_array[dest_row++]; these areas may overlap for duplication.
|
|||
/// The source and destination arrays must be at least as wide as num_cols.
|
|||
/// </summary>
|
|||
public static void jcopy_sample_rows(ComponentBuffer input_array, int source_row, byte[][] output_array, int dest_row, int num_rows, int num_cols) |
|||
{ |
|||
for (int row = 0; row < num_rows; row++) |
|||
Buffer.BlockCopy(input_array[source_row + row], 0, output_array[dest_row + row], 0, num_cols); |
|||
} |
|||
|
|||
public static void jcopy_sample_rows(ComponentBuffer input_array, int source_row, ComponentBuffer output_array, int dest_row, int num_rows, int num_cols) |
|||
{ |
|||
for (int row = 0; row < num_rows; row++) |
|||
Buffer.BlockCopy(input_array[source_row + row], 0, output_array[dest_row + row], 0, num_cols); |
|||
} |
|||
|
|||
public static void jcopy_sample_rows(byte[][] input_array, int source_row, byte[][] output_array, int dest_row, int num_rows, int num_cols) |
|||
{ |
|||
for (int row = 0; row < num_rows; row++) |
|||
Buffer.BlockCopy(input_array[source_row++], 0, output_array[dest_row++], 0, num_cols); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
/* Copyright (C) 2008-2011, Bit Miracle |
|||
* http://www.bitmiracle.com
|
|||
* |
|||
* Copyright (C) 1994-1996, Thomas G. Lane. |
|||
* This file is part of the Independent JPEG Group's software. |
|||
* For conditions of distribution and use, see the accompanying README file. |
|||
* |
|||
*/ |
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
|
|||
namespace BitMiracle.LibJpeg.Classic.Internal |
|||
{ |
|||
/// <summary>
|
|||
/// Bitreading state saved across MCUs
|
|||
/// </summary>
|
|||
struct bitread_perm_state |
|||
{ |
|||
public int get_buffer; /* current bit-extraction buffer */ |
|||
public int bits_left; /* # of unused bits in it */ |
|||
} |
|||
} |
|||
@ -0,0 +1,27 @@ |
|||
/* Copyright (C) 2008-2011, Bit Miracle |
|||
* http://www.bitmiracle.com
|
|||
* |
|||
* Copyright (C) 1994-1996, Thomas G. Lane. |
|||
* This file is part of the Independent JPEG Group's software. |
|||
* For conditions of distribution and use, see the accompanying README file. |
|||
* |
|||
*/ |
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
|
|||
namespace BitMiracle.LibJpeg.Classic.Internal |
|||
{ |
|||
/// <summary>
|
|||
/// Bitreading working state within an MCU
|
|||
/// </summary>
|
|||
struct bitread_working_state |
|||
{ |
|||
public int get_buffer; /* current bit-extraction buffer */ |
|||
public int bits_left; /* # of unused bits in it */ |
|||
|
|||
/* Pointer needed by jpeg_fill_bit_buffer. */ |
|||
public jpeg_decompress_struct cinfo; /* back link to decompress master record */ |
|||
} |
|||
} |
|||
@ -0,0 +1,41 @@ |
|||
/* Copyright (C) 2008-2011, Bit Miracle |
|||
* http://www.bitmiracle.com
|
|||
* |
|||
* Copyright (C) 1994-1996, Thomas G. Lane. |
|||
* This file is part of the Independent JPEG Group's software. |
|||
* For conditions of distribution and use, see the accompanying README file. |
|||
* |
|||
*/ |
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
|
|||
namespace BitMiracle.LibJpeg.Classic.Internal |
|||
{ |
|||
/// <summary>
|
|||
/// Derived data constructed for each Huffman table
|
|||
/// </summary>
|
|||
class d_derived_tbl |
|||
{ |
|||
/* Basic tables: (element [0] of each array is unused) */ |
|||
public int[] maxcode = new int[18]; /* largest code of length k (-1 if none) */ |
|||
/* (maxcode[17] is a sentinel to ensure jpeg_huff_decode terminates) */ |
|||
public int[] valoffset = new int[17]; /* huffval[] offset for codes of length k */ |
|||
/* valoffset[k] = huffval[] index of 1st symbol of code length k, less |
|||
* the smallest code of length k; so given a code of length k, the |
|||
* corresponding symbol is huffval[code + valoffset[k]] |
|||
*/ |
|||
|
|||
/* Link to public Huffman table (needed only in jpeg_huff_decode) */ |
|||
public JHUFF_TBL pub; |
|||
|
|||
/* Lookahead tables: indexed by the next HUFF_LOOKAHEAD bits of |
|||
* the input data stream. If the next Huffman code is no more |
|||
* than HUFF_LOOKAHEAD bits long, we can obtain its length and |
|||
* the corresponding symbol directly from these tables. |
|||
*/ |
|||
public int[] look_nbits = new int[1 << JpegConstants.HUFF_LOOKAHEAD]; /* # bits, or 0 if too long */ |
|||
public byte[] look_sym = new byte[1 << JpegConstants.HUFF_LOOKAHEAD]; /* symbol, or unused */ |
|||
} |
|||
} |
|||
@ -0,0 +1,316 @@ |
|||
/* Copyright (C) 2008-2011, Bit Miracle |
|||
* http://www.bitmiracle.com
|
|||
* |
|||
* Copyright (C) 1994-1996, Thomas G. Lane. |
|||
* This file is part of the Independent JPEG Group's software. |
|||
* For conditions of distribution and use, see the accompanying README file. |
|||
* |
|||
*/ |
|||
|
|||
/* |
|||
* This file contains Huffman entropy decoding routines. |
|||
* |
|||
* Much of the complexity here has to do with supporting input suspension. |
|||
* If the data source module demands suspension, we want to be able to back |
|||
* up to the start of the current MCU. To do this, we copy state variables |
|||
* into local working storage, and update them back to the permanent |
|||
* storage only upon successful completion of an MCU. |
|||
*/ |
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
|
|||
namespace BitMiracle.LibJpeg.Classic.Internal |
|||
{ |
|||
/// <summary>
|
|||
/// Expanded entropy decoder object for Huffman decoding.
|
|||
///
|
|||
/// The savable_state subrecord contains fields that change within an MCU,
|
|||
/// but must not be updated permanently until we complete the MCU.
|
|||
/// </summary>
|
|||
class huff_entropy_decoder : jpeg_entropy_decoder |
|||
{ |
|||
private class savable_state |
|||
{ |
|||
public int[] last_dc_val = new int[JpegConstants.MAX_COMPS_IN_SCAN]; /* last DC coef for each component */ |
|||
|
|||
public void Assign(savable_state ss) |
|||
{ |
|||
Buffer.BlockCopy(ss.last_dc_val, 0, last_dc_val, 0, last_dc_val.Length * sizeof(int)); |
|||
} |
|||
} |
|||
|
|||
/* These fields are loaded into local variables at start of each MCU. |
|||
* In case of suspension, we exit WITHOUT updating them. |
|||
*/ |
|||
private bitread_perm_state m_bitstate; /* Bit buffer at start of MCU */ |
|||
private savable_state m_saved = new savable_state(); /* Other state at start of MCU */ |
|||
|
|||
/* These fields are NOT loaded into local working state. */ |
|||
private int m_restarts_to_go; /* MCUs left in this restart interval */ |
|||
|
|||
/* Pointers to derived tables (these workspaces have image lifespan) */ |
|||
private d_derived_tbl[] m_dc_derived_tbls = new d_derived_tbl[JpegConstants.NUM_HUFF_TBLS]; |
|||
private d_derived_tbl[] m_ac_derived_tbls = new d_derived_tbl[JpegConstants.NUM_HUFF_TBLS]; |
|||
|
|||
/* Precalculated info set up by start_pass for use in decode_mcu: */ |
|||
|
|||
/* Pointers to derived tables to be used for each block within an MCU */ |
|||
private d_derived_tbl[] m_dc_cur_tbls = new d_derived_tbl[JpegConstants.D_MAX_BLOCKS_IN_MCU]; |
|||
private d_derived_tbl[] m_ac_cur_tbls = new d_derived_tbl[JpegConstants.D_MAX_BLOCKS_IN_MCU]; |
|||
|
|||
/* Whether we care about the DC and AC coefficient values for each block */ |
|||
private bool[] m_dc_needed = new bool[JpegConstants.D_MAX_BLOCKS_IN_MCU]; |
|||
private bool[] m_ac_needed = new bool[JpegConstants.D_MAX_BLOCKS_IN_MCU]; |
|||
|
|||
public huff_entropy_decoder(jpeg_decompress_struct cinfo) |
|||
{ |
|||
m_cinfo = cinfo; |
|||
|
|||
/* Mark tables unallocated */ |
|||
for (int i = 0; i < JpegConstants.NUM_HUFF_TBLS; i++) |
|||
m_dc_derived_tbls[i] = m_ac_derived_tbls[i] = null; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initialize for a Huffman-compressed scan.
|
|||
/// </summary>
|
|||
public override void start_pass() |
|||
{ |
|||
/* Check that the scan parameters Ss, Se, Ah/Al are OK for sequential JPEG. |
|||
* This ought to be an error condition, but we make it a warning because |
|||
* there are some baseline files out there with all zeroes in these bytes. |
|||
*/ |
|||
if (m_cinfo.m_Ss != 0 || m_cinfo.m_Se != JpegConstants.DCTSIZE2 - 1 || m_cinfo.m_Ah != 0 || m_cinfo.m_Al != 0) |
|||
m_cinfo.WARNMS(J_MESSAGE_CODE.JWRN_NOT_SEQUENTIAL); |
|||
|
|||
for (int ci = 0; ci < m_cinfo.m_comps_in_scan; ci++) |
|||
{ |
|||
jpeg_component_info componentInfo = m_cinfo.Comp_info[m_cinfo.m_cur_comp_info[ci]]; |
|||
int dctbl = componentInfo.Dc_tbl_no; |
|||
int actbl = componentInfo.Ac_tbl_no; |
|||
|
|||
/* Compute derived values for Huffman tables */ |
|||
/* We may do this more than once for a table, but it's not expensive */ |
|||
jpeg_make_d_derived_tbl(true, dctbl, ref m_dc_derived_tbls[dctbl]); |
|||
jpeg_make_d_derived_tbl(false, actbl, ref m_ac_derived_tbls[actbl]); |
|||
|
|||
/* Initialize DC predictions to 0 */ |
|||
m_saved.last_dc_val[ci] = 0; |
|||
} |
|||
|
|||
/* Precalculate decoding info for each block in an MCU of this scan */ |
|||
for (int blkn = 0; blkn < m_cinfo.m_blocks_in_MCU; blkn++) |
|||
{ |
|||
int ci = m_cinfo.m_MCU_membership[blkn]; |
|||
jpeg_component_info componentInfo = m_cinfo.Comp_info[m_cinfo.m_cur_comp_info[ci]]; |
|||
|
|||
/* Precalculate which table to use for each block */ |
|||
m_dc_cur_tbls[blkn] = m_dc_derived_tbls[componentInfo.Dc_tbl_no]; |
|||
m_ac_cur_tbls[blkn] = m_ac_derived_tbls[componentInfo.Ac_tbl_no]; |
|||
|
|||
/* Decide whether we really care about the coefficient values */ |
|||
if (componentInfo.component_needed) |
|||
{ |
|||
m_dc_needed[blkn] = true; |
|||
/* we don't need the ACs if producing a 1/8th-size image */ |
|||
m_ac_needed[blkn] = (componentInfo.DCT_scaled_size > 1); |
|||
} |
|||
else |
|||
{ |
|||
m_dc_needed[blkn] = m_ac_needed[blkn] = false; |
|||
} |
|||
} |
|||
|
|||
/* Initialize bitread state variables */ |
|||
m_bitstate.bits_left = 0; |
|||
m_bitstate.get_buffer = 0; |
|||
m_insufficient_data = false; |
|||
|
|||
/* Initialize restart counter */ |
|||
m_restarts_to_go = m_cinfo.m_restart_interval; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Decode and return one MCU's worth of Huffman-compressed coefficients.
|
|||
/// The coefficients are reordered from zigzag order into natural array order,
|
|||
/// but are not dequantized.
|
|||
///
|
|||
/// The i'th block of the MCU is stored into the block pointed to by
|
|||
/// MCU_data[i]. WE ASSUME THIS AREA HAS BEEN ZEROED BY THE CALLER.
|
|||
/// (Wholesale zeroing is usually a little faster than retail...)
|
|||
///
|
|||
/// Returns false if data source requested suspension. In that case no
|
|||
/// changes have been made to permanent state. (Exception: some output
|
|||
/// coefficients may already have been assigned. This is harmless for
|
|||
/// this module, since we'll just re-assign them on the next call.)
|
|||
/// </summary>
|
|||
public override bool decode_mcu(JBLOCK[] MCU_data) |
|||
{ |
|||
/* Process restart marker if needed; may have to suspend */ |
|||
if (m_cinfo.m_restart_interval != 0) |
|||
{ |
|||
if (m_restarts_to_go == 0) |
|||
{ |
|||
if (!process_restart()) |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
/* If we've run out of data, just leave the MCU set to zeroes. |
|||
* This way, we return uniform gray for the remainder of the segment. |
|||
*/ |
|||
if (!m_insufficient_data) |
|||
{ |
|||
/* Load up working state */ |
|||
int get_buffer; |
|||
int bits_left; |
|||
bitread_working_state br_state = new bitread_working_state(); |
|||
BITREAD_LOAD_STATE(m_bitstate, out get_buffer, out bits_left, ref br_state); |
|||
savable_state state = new savable_state(); |
|||
state.Assign(m_saved); |
|||
|
|||
/* Outer loop handles each block in the MCU */ |
|||
|
|||
for (int blkn = 0; blkn < m_cinfo.m_blocks_in_MCU; blkn++) |
|||
{ |
|||
/* Decode a single block's worth of coefficients */ |
|||
|
|||
/* Section F.2.2.1: decode the DC coefficient difference */ |
|||
int s; |
|||
if (!HUFF_DECODE(out s, ref br_state, m_dc_cur_tbls[blkn], ref get_buffer, ref bits_left)) |
|||
return false; |
|||
|
|||
if (s != 0) |
|||
{ |
|||
if (!CHECK_BIT_BUFFER(ref br_state, s, ref get_buffer, ref bits_left)) |
|||
return false; |
|||
|
|||
int r = GET_BITS(s, get_buffer, ref bits_left); |
|||
s = HUFF_EXTEND(r, s); |
|||
} |
|||
|
|||
if (m_dc_needed[blkn]) |
|||
{ |
|||
/* Convert DC difference to actual value, update last_dc_val */ |
|||
int ci = m_cinfo.m_MCU_membership[blkn]; |
|||
s += state.last_dc_val[ci]; |
|||
state.last_dc_val[ci] = s; |
|||
|
|||
/* Output the DC coefficient (assumes jpeg_natural_order[0] = 0) */ |
|||
MCU_data[blkn][0] = (short) s; |
|||
} |
|||
|
|||
if (m_ac_needed[blkn]) |
|||
{ |
|||
/* Section F.2.2.2: decode the AC coefficients */ |
|||
/* Since zeroes are skipped, output area must be cleared beforehand */ |
|||
for (int k = 1; k < JpegConstants.DCTSIZE2; k++) |
|||
{ |
|||
if (!HUFF_DECODE(out s, ref br_state, m_ac_cur_tbls[blkn], ref get_buffer, ref bits_left)) |
|||
return false; |
|||
|
|||
int r = s >> 4; |
|||
s &= 15; |
|||
|
|||
if (s != 0) |
|||
{ |
|||
k += r; |
|||
if (!CHECK_BIT_BUFFER(ref br_state, s, ref get_buffer, ref bits_left)) |
|||
return false; |
|||
r = GET_BITS(s, get_buffer, ref bits_left); |
|||
s = HUFF_EXTEND(r, s); |
|||
|
|||
/* Output coefficient in natural (dezigzagged) order. |
|||
* Note: the extra entries in jpeg_natural_order[] will save us |
|||
* if k >= DCTSIZE2, which could happen if the data is corrupted. |
|||
*/ |
|||
MCU_data[blkn][JpegUtils.jpeg_natural_order[k]] = (short) s; |
|||
} |
|||
else |
|||
{ |
|||
if (r != 15) |
|||
break; |
|||
|
|||
k += 15; |
|||
} |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
/* Section F.2.2.2: decode the AC coefficients */ |
|||
/* In this path we just discard the values */ |
|||
for (int k = 1; k < JpegConstants.DCTSIZE2; k++) |
|||
{ |
|||
if (!HUFF_DECODE(out s, ref br_state, m_ac_cur_tbls[blkn], ref get_buffer, ref bits_left)) |
|||
return false; |
|||
|
|||
int r = s >> 4; |
|||
s &= 15; |
|||
|
|||
if (s != 0) |
|||
{ |
|||
k += r; |
|||
if (!CHECK_BIT_BUFFER(ref br_state, s, ref get_buffer, ref bits_left)) |
|||
return false; |
|||
|
|||
DROP_BITS(s, ref bits_left); |
|||
} |
|||
else |
|||
{ |
|||
if (r != 15) |
|||
break; |
|||
|
|||
k += 15; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
/* Completed MCU, so update state */ |
|||
BITREAD_SAVE_STATE(ref m_bitstate, get_buffer, bits_left); |
|||
m_saved.Assign(state); |
|||
} |
|||
|
|||
/* Account for restart interval (no-op if not using restarts) */ |
|||
m_restarts_to_go--; |
|||
|
|||
return true; |
|||
|
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Check for a restart marker and resynchronize decoder.
|
|||
/// Returns false if must suspend.
|
|||
/// </summary>
|
|||
private bool process_restart() |
|||
{ |
|||
/* Throw away any unused bits remaining in bit buffer; */ |
|||
/* include any full bytes in next_marker's count of discarded bytes */ |
|||
m_cinfo.m_marker.SkipBytes(m_bitstate.bits_left / 8); |
|||
m_bitstate.bits_left = 0; |
|||
|
|||
/* Advance past the RSTn marker */ |
|||
if (!m_cinfo.m_marker.read_restart_marker()) |
|||
return false; |
|||
|
|||
/* Re-initialize DC predictions to 0 */ |
|||
for (int ci = 0; ci < m_cinfo.m_comps_in_scan; ci++) |
|||
m_saved.last_dc_val[ci] = 0; |
|||
|
|||
/* Reset restart counter */ |
|||
m_restarts_to_go = m_cinfo.m_restart_interval; |
|||
|
|||
/* Reset out-of-data flag, unless read_restart_marker left us smack up |
|||
* against a marker. In that case we will end up treating the next data |
|||
* segment as empty, and we can avoid producing bogus output pixels by |
|||
* leaving the flag set. |
|||
*/ |
|||
if (m_cinfo.m_unread_marker == 0) |
|||
m_insufficient_data = false; |
|||
|
|||
return true; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,542 @@ |
|||
/* Copyright (C) 2008-2011, Bit Miracle |
|||
* http://www.bitmiracle.com
|
|||
* |
|||
* Copyright (C) 1994-1996, Thomas G. Lane. |
|||
* This file is part of the Independent JPEG Group's software. |
|||
* For conditions of distribution and use, see the accompanying README file. |
|||
* |
|||
*/ |
|||
|
|||
/* |
|||
* This file contains Huffman entropy encoding routines. |
|||
* |
|||
* Much of the complexity here has to do with supporting output suspension. |
|||
* If the data destination module demands suspension, we want to be able to |
|||
* back up to the start of the current MCU. To do this, we copy state |
|||
* variables into local working storage, and update them back to the |
|||
* permanent JPEG objects only upon successful completion of an MCU. |
|||
*/ |
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
|
|||
namespace BitMiracle.LibJpeg.Classic.Internal |
|||
{ |
|||
/// <summary>
|
|||
/// Expanded entropy encoder object for Huffman encoding.
|
|||
/// </summary>
|
|||
class huff_entropy_encoder : jpeg_entropy_encoder |
|||
{ |
|||
/* The savable_state subrecord contains fields that change within an MCU, |
|||
* but must not be updated permanently until we complete the MCU. |
|||
*/ |
|||
private class savable_state |
|||
{ |
|||
public int put_buffer; /* current bit-accumulation buffer */ |
|||
public int put_bits; /* # of bits now in it */ |
|||
public int[] last_dc_val = new int[JpegConstants.MAX_COMPS_IN_SCAN]; /* last DC coef for each component */ |
|||
} |
|||
|
|||
private bool m_gather_statistics; |
|||
|
|||
private savable_state m_saved = new savable_state(); /* Bit buffer & DC state at start of MCU */ |
|||
|
|||
/* These fields are NOT loaded into local working state. */ |
|||
private int m_restarts_to_go; /* MCUs left in this restart interval */ |
|||
private int m_next_restart_num; /* next restart number to write (0-7) */ |
|||
|
|||
/* Pointers to derived tables (these workspaces have image lifespan) */ |
|||
private c_derived_tbl[] m_dc_derived_tbls = new c_derived_tbl[JpegConstants.NUM_HUFF_TBLS]; |
|||
private c_derived_tbl[] m_ac_derived_tbls = new c_derived_tbl[JpegConstants.NUM_HUFF_TBLS]; |
|||
|
|||
/* Statistics tables for optimization */ |
|||
private long[][] m_dc_count_ptrs = new long[JpegConstants.NUM_HUFF_TBLS][]; |
|||
private long[][] m_ac_count_ptrs = new long[JpegConstants.NUM_HUFF_TBLS][]; |
|||
|
|||
public huff_entropy_encoder(jpeg_compress_struct cinfo) |
|||
{ |
|||
m_cinfo = cinfo; |
|||
|
|||
/* Mark tables unallocated */ |
|||
for (int i = 0; i < JpegConstants.NUM_HUFF_TBLS; i++) |
|||
{ |
|||
m_dc_derived_tbls[i] = m_ac_derived_tbls[i] = null; |
|||
m_dc_count_ptrs[i] = m_ac_count_ptrs[i] = null; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initialize for a Huffman-compressed scan.
|
|||
/// If gather_statistics is true, we do not output anything during the scan,
|
|||
/// just count the Huffman symbols used and generate Huffman code tables.
|
|||
/// </summary>
|
|||
public override void start_pass(bool gather_statistics) |
|||
{ |
|||
m_gather_statistics = gather_statistics; |
|||
|
|||
for (int ci = 0; ci < m_cinfo.m_comps_in_scan; ci++) |
|||
{ |
|||
int dctbl = m_cinfo.Component_info[m_cinfo.m_cur_comp_info[ci]].Dc_tbl_no; |
|||
int actbl = m_cinfo.Component_info[m_cinfo.m_cur_comp_info[ci]].Ac_tbl_no; |
|||
if (m_gather_statistics) |
|||
{ |
|||
/* Check for invalid table indexes */ |
|||
/* (make_c_derived_tbl does this in the other path) */ |
|||
if (dctbl < 0 || dctbl >= JpegConstants.NUM_HUFF_TBLS) |
|||
m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_NO_HUFF_TABLE, dctbl); |
|||
|
|||
if (actbl < 0 || actbl >= JpegConstants.NUM_HUFF_TBLS) |
|||
m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_NO_HUFF_TABLE, actbl); |
|||
|
|||
/* Allocate and zero the statistics tables */ |
|||
/* Note that jpeg_gen_optimal_table expects 257 entries in each table! */ |
|||
if (m_dc_count_ptrs[dctbl] == null) |
|||
m_dc_count_ptrs[dctbl] = new long[257]; |
|||
|
|||
Array.Clear(m_dc_count_ptrs[dctbl], 0, m_dc_count_ptrs[dctbl].Length); |
|||
|
|||
if (m_ac_count_ptrs[actbl] == null) |
|||
m_ac_count_ptrs[actbl] = new long[257]; |
|||
|
|||
Array.Clear(m_ac_count_ptrs[actbl], 0, m_ac_count_ptrs[actbl].Length); |
|||
} |
|||
else |
|||
{ |
|||
/* Compute derived values for Huffman tables */ |
|||
/* We may do this more than once for a table, but it's not expensive */ |
|||
jpeg_make_c_derived_tbl(true, dctbl, ref m_dc_derived_tbls[dctbl]); |
|||
jpeg_make_c_derived_tbl(false, actbl, ref m_ac_derived_tbls[actbl]); |
|||
} |
|||
|
|||
/* Initialize DC predictions to 0 */ |
|||
m_saved.last_dc_val[ci] = 0; |
|||
} |
|||
|
|||
/* Initialize bit buffer to empty */ |
|||
m_saved.put_buffer = 0; |
|||
m_saved.put_bits = 0; |
|||
|
|||
/* Initialize restart stuff */ |
|||
m_restarts_to_go = m_cinfo.m_restart_interval; |
|||
m_next_restart_num = 0; |
|||
} |
|||
|
|||
public override bool encode_mcu(JBLOCK[][] MCU_data) |
|||
{ |
|||
if (m_gather_statistics) |
|||
return encode_mcu_gather(MCU_data); |
|||
|
|||
return encode_mcu_huff(MCU_data); |
|||
} |
|||
|
|||
public override void finish_pass() |
|||
{ |
|||
if (m_gather_statistics) |
|||
finish_pass_gather(); |
|||
else |
|||
finish_pass_huff(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Encode and output one MCU's worth of Huffman-compressed coefficients.
|
|||
/// </summary>
|
|||
private bool encode_mcu_huff(JBLOCK[][] MCU_data) |
|||
{ |
|||
/* Load up working state */ |
|||
savable_state state; |
|||
state = m_saved; |
|||
|
|||
/* Emit restart marker if needed */ |
|||
if (m_cinfo.m_restart_interval != 0) |
|||
{ |
|||
if (m_restarts_to_go == 0) |
|||
{ |
|||
if (!emit_restart(state, m_next_restart_num)) |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
/* Encode the MCU data blocks */ |
|||
for (int blkn = 0; blkn < m_cinfo.m_blocks_in_MCU; blkn++) |
|||
{ |
|||
int ci = m_cinfo.m_MCU_membership[blkn]; |
|||
if (!encode_one_block(state, MCU_data[blkn][0].data, state.last_dc_val[ci], |
|||
m_dc_derived_tbls[m_cinfo.Component_info[m_cinfo.m_cur_comp_info[ci]].Dc_tbl_no], |
|||
m_ac_derived_tbls[m_cinfo.Component_info[m_cinfo.m_cur_comp_info[ci]].Ac_tbl_no])) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
/* Update last_dc_val */ |
|||
state.last_dc_val[ci] = MCU_data[blkn][0][0]; |
|||
} |
|||
|
|||
/* Completed MCU, so update state */ |
|||
m_saved = state; |
|||
|
|||
/* Update restart-interval state too */ |
|||
if (m_cinfo.m_restart_interval != 0) |
|||
{ |
|||
if (m_restarts_to_go == 0) |
|||
{ |
|||
m_restarts_to_go = m_cinfo.m_restart_interval; |
|||
m_next_restart_num++; |
|||
m_next_restart_num &= 7; |
|||
} |
|||
|
|||
m_restarts_to_go--; |
|||
} |
|||
|
|||
return true; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Finish up at the end of a Huffman-compressed scan.
|
|||
/// </summary>
|
|||
private void finish_pass_huff() |
|||
{ |
|||
/* Load up working state ... flush_bits needs it */ |
|||
savable_state state; |
|||
state = m_saved; |
|||
|
|||
/* Flush out the last data */ |
|||
if (!flush_bits(state)) |
|||
m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CANT_SUSPEND); |
|||
|
|||
/* Update state */ |
|||
m_saved = state; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Trial-encode one MCU's worth of Huffman-compressed coefficients.
|
|||
/// No data is actually output, so no suspension return is possible.
|
|||
/// </summary>
|
|||
private bool encode_mcu_gather(JBLOCK[][] MCU_data) |
|||
{ |
|||
/* Take care of restart intervals if needed */ |
|||
if (m_cinfo.m_restart_interval != 0) |
|||
{ |
|||
if (m_restarts_to_go == 0) |
|||
{ |
|||
/* Re-initialize DC predictions to 0 */ |
|||
for (int ci = 0; ci < m_cinfo.m_comps_in_scan; ci++) |
|||
m_saved.last_dc_val[ci] = 0; |
|||
|
|||
/* Update restart state */ |
|||
m_restarts_to_go = m_cinfo.m_restart_interval; |
|||
} |
|||
|
|||
m_restarts_to_go--; |
|||
} |
|||
|
|||
for (int blkn = 0; blkn < m_cinfo.m_blocks_in_MCU; blkn++) |
|||
{ |
|||
int ci = m_cinfo.m_MCU_membership[blkn]; |
|||
htest_one_block(MCU_data[blkn][0].data, m_saved.last_dc_val[ci], |
|||
m_dc_count_ptrs[m_cinfo.Component_info[m_cinfo.m_cur_comp_info[ci]].Dc_tbl_no], |
|||
m_ac_count_ptrs[m_cinfo.Component_info[m_cinfo.m_cur_comp_info[ci]].Ac_tbl_no]); |
|||
m_saved.last_dc_val[ci] = MCU_data[blkn][0][0]; |
|||
} |
|||
|
|||
return true; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Finish up a statistics-gathering pass and create the new Huffman tables.
|
|||
/// </summary>
|
|||
private void finish_pass_gather() |
|||
{ |
|||
/* It's important not to apply jpeg_gen_optimal_table more than once |
|||
* per table, because it clobbers the input frequency counts! |
|||
*/ |
|||
bool[] did_dc = new bool [JpegConstants.NUM_HUFF_TBLS]; |
|||
bool[] did_ac = new bool[JpegConstants.NUM_HUFF_TBLS]; |
|||
|
|||
for (int ci = 0; ci < m_cinfo.m_comps_in_scan; ci++) |
|||
{ |
|||
int dctbl = m_cinfo.Component_info[m_cinfo.m_cur_comp_info[ci]].Dc_tbl_no; |
|||
if (!did_dc[dctbl]) |
|||
{ |
|||
if (m_cinfo.m_dc_huff_tbl_ptrs[dctbl] == null) |
|||
m_cinfo.m_dc_huff_tbl_ptrs[dctbl] = new JHUFF_TBL(); |
|||
|
|||
jpeg_gen_optimal_table(m_cinfo.m_dc_huff_tbl_ptrs[dctbl], m_dc_count_ptrs[dctbl]); |
|||
did_dc[dctbl] = true; |
|||
} |
|||
|
|||
int actbl = m_cinfo.Component_info[m_cinfo.m_cur_comp_info[ci]].Ac_tbl_no; |
|||
if (!did_ac[actbl]) |
|||
{ |
|||
if (m_cinfo.m_ac_huff_tbl_ptrs[actbl] == null) |
|||
m_cinfo.m_ac_huff_tbl_ptrs[actbl] = new JHUFF_TBL(); |
|||
|
|||
jpeg_gen_optimal_table(m_cinfo.m_ac_huff_tbl_ptrs[actbl], m_ac_count_ptrs[actbl]); |
|||
did_ac[actbl] = true; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Encode a single block's worth of coefficients
|
|||
/// </summary>
|
|||
private bool encode_one_block(savable_state state, short[] block, int last_dc_val, c_derived_tbl dctbl, c_derived_tbl actbl) |
|||
{ |
|||
/* Encode the DC coefficient difference per section F.1.2.1 */ |
|||
int temp = block[0] - last_dc_val; |
|||
int temp2 = temp; |
|||
if (temp < 0) |
|||
{ |
|||
temp = -temp; /* temp is abs value of input */ |
|||
/* For a negative input, want temp2 = bitwise complement of abs(input) */ |
|||
/* This code assumes we are on a two's complement machine */ |
|||
temp2--; |
|||
} |
|||
|
|||
/* Find the number of bits needed for the magnitude of the coefficient */ |
|||
int nbits = 0; |
|||
while (temp != 0) |
|||
{ |
|||
nbits++; |
|||
temp >>= 1; |
|||
} |
|||
|
|||
/* Check for out-of-range coefficient values. |
|||
* Since we're encoding a difference, the range limit is twice as much. |
|||
*/ |
|||
if (nbits > MAX_HUFFMAN_COEF_BITS + 1) |
|||
m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_DCT_COEF); |
|||
|
|||
/* Emit the Huffman-coded symbol for the number of bits */ |
|||
if (!emit_bits(state, dctbl.ehufco[nbits], dctbl.ehufsi[nbits])) |
|||
return false; |
|||
|
|||
/* Emit that number of bits of the value, if positive, */ |
|||
/* or the complement of its magnitude, if negative. */ |
|||
if (nbits != 0) |
|||
{ |
|||
/* emit_bits rejects calls with size 0 */ |
|||
if (!emit_bits(state, temp2, nbits)) |
|||
return false; |
|||
} |
|||
|
|||
/* Encode the AC coefficients per section F.1.2.2 */ |
|||
int r = 0; /* r = run length of zeros */ |
|||
for (int k = 1; k < JpegConstants.DCTSIZE2; k++) |
|||
{ |
|||
temp = block[JpegUtils.jpeg_natural_order[k]]; |
|||
if (temp == 0) |
|||
{ |
|||
r++; |
|||
} |
|||
else |
|||
{ |
|||
/* if run length > 15, must emit special run-length-16 codes (0xF0) */ |
|||
while (r > 15) |
|||
{ |
|||
if (!emit_bits(state, actbl.ehufco[0xF0], actbl.ehufsi[0xF0])) |
|||
return false; |
|||
r -= 16; |
|||
} |
|||
|
|||
temp2 = temp; |
|||
if (temp < 0) |
|||
{ |
|||
temp = -temp; /* temp is abs value of input */ |
|||
/* This code assumes we are on a two's complement machine */ |
|||
temp2--; |
|||
} |
|||
|
|||
/* Find the number of bits needed for the magnitude of the coefficient */ |
|||
nbits = 1; /* there must be at least one 1 bit */ |
|||
while ((temp >>= 1) != 0) |
|||
nbits++; |
|||
|
|||
/* Check for out-of-range coefficient values */ |
|||
if (nbits > MAX_HUFFMAN_COEF_BITS) |
|||
m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_DCT_COEF); |
|||
|
|||
/* Emit Huffman symbol for run length / number of bits */ |
|||
int i = (r << 4) + nbits; |
|||
if (!emit_bits(state, actbl.ehufco[i], actbl.ehufsi[i])) |
|||
return false; |
|||
|
|||
/* Emit that number of bits of the value, if positive, */ |
|||
/* or the complement of its magnitude, if negative. */ |
|||
if (!emit_bits(state, temp2, nbits)) |
|||
return false; |
|||
|
|||
r = 0; |
|||
} |
|||
} |
|||
|
|||
/* If the last coef(s) were zero, emit an end-of-block code */ |
|||
if (r > 0) |
|||
{ |
|||
if (!emit_bits(state, actbl.ehufco[0], actbl.ehufsi[0])) |
|||
return false; |
|||
} |
|||
|
|||
return true; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Huffman coding optimization.
|
|||
///
|
|||
/// We first scan the supplied data and count the number of uses of each symbol
|
|||
/// that is to be Huffman-coded. (This process MUST agree with the code above.)
|
|||
/// Then we build a Huffman coding tree for the observed counts.
|
|||
/// Symbols which are not needed at all for the particular image are not
|
|||
/// assigned any code, which saves space in the DHT marker as well as in
|
|||
/// the compressed data.
|
|||
/// </summary>
|
|||
private void htest_one_block(short[] block, int last_dc_val, long[] dc_counts, long[] ac_counts) |
|||
{ |
|||
/* Encode the DC coefficient difference per section F.1.2.1 */ |
|||
int temp = block[0] - last_dc_val; |
|||
if (temp < 0) |
|||
temp = -temp; |
|||
|
|||
/* Find the number of bits needed for the magnitude of the coefficient */ |
|||
int nbits = 0; |
|||
while (temp != 0) |
|||
{ |
|||
nbits++; |
|||
temp >>= 1; |
|||
} |
|||
|
|||
/* Check for out-of-range coefficient values. |
|||
* Since we're encoding a difference, the range limit is twice as much. |
|||
*/ |
|||
if (nbits > MAX_HUFFMAN_COEF_BITS + 1) |
|||
m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_DCT_COEF); |
|||
|
|||
/* Count the Huffman symbol for the number of bits */ |
|||
dc_counts[nbits]++; |
|||
|
|||
/* Encode the AC coefficients per section F.1.2.2 */ |
|||
int r = 0; /* r = run length of zeros */ |
|||
for (int k = 1; k < JpegConstants.DCTSIZE2; k++) |
|||
{ |
|||
temp = block[JpegUtils.jpeg_natural_order[k]]; |
|||
if (temp == 0) |
|||
{ |
|||
r++; |
|||
} |
|||
else |
|||
{ |
|||
/* if run length > 15, must emit special run-length-16 codes (0xF0) */ |
|||
while (r > 15) |
|||
{ |
|||
ac_counts[0xF0]++; |
|||
r -= 16; |
|||
} |
|||
|
|||
/* Find the number of bits needed for the magnitude of the coefficient */ |
|||
if (temp < 0) |
|||
temp = -temp; |
|||
|
|||
/* Find the number of bits needed for the magnitude of the coefficient */ |
|||
nbits = 1; /* there must be at least one 1 bit */ |
|||
while ((temp >>= 1) != 0) |
|||
nbits++; |
|||
|
|||
/* Check for out-of-range coefficient values */ |
|||
if (nbits > MAX_HUFFMAN_COEF_BITS) |
|||
m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_DCT_COEF); |
|||
|
|||
/* Count Huffman symbol for run length / number of bits */ |
|||
ac_counts[(r << 4) + nbits]++; |
|||
|
|||
r = 0; |
|||
} |
|||
} |
|||
|
|||
/* If the last coef(s) were zero, emit an end-of-block code */ |
|||
if (r > 0) |
|||
ac_counts[0]++; |
|||
} |
|||
|
|||
private bool emit_byte(int val) |
|||
{ |
|||
return m_cinfo.m_dest.emit_byte(val); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Only the right 24 bits of put_buffer are used; the valid bits are
|
|||
/// left-justified in this part. At most 16 bits can be passed to emit_bits
|
|||
/// in one call, and we never retain more than 7 bits in put_buffer
|
|||
/// between calls, so 24 bits are sufficient.
|
|||
/// </summary>
|
|||
private bool emit_bits(savable_state state, int code, int size) |
|||
{ |
|||
// Emit some bits; return true if successful, false if must suspend
|
|||
/* This routine is heavily used, so it's worth coding tightly. */ |
|||
int put_buffer = code; |
|||
int put_bits = state.put_bits; |
|||
|
|||
/* if size is 0, caller used an invalid Huffman table entry */ |
|||
if (size == 0) |
|||
m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_HUFF_MISSING_CODE); |
|||
|
|||
put_buffer &= (1 << size) - 1; /* mask off any extra bits in code */ |
|||
put_bits += size; /* new number of bits in buffer */ |
|||
put_buffer <<= 24 - put_bits; /* align incoming bits */ |
|||
put_buffer |= state.put_buffer; /* and merge with old buffer contents */ |
|||
|
|||
while (put_bits >= 8) |
|||
{ |
|||
int c = (put_buffer >> 16) & 0xFF; |
|||
if (!emit_byte(c)) |
|||
return false; |
|||
|
|||
if (c == 0xFF) |
|||
{ |
|||
/* need to stuff a zero byte? */ |
|||
if (!emit_byte(0)) |
|||
return false; |
|||
} |
|||
|
|||
put_buffer <<= 8; |
|||
put_bits -= 8; |
|||
} |
|||
|
|||
state.put_buffer = put_buffer; /* update state variables */ |
|||
state.put_bits = put_bits; |
|||
|
|||
return true; |
|||
} |
|||
|
|||
private bool flush_bits(savable_state state) |
|||
{ |
|||
if (!emit_bits(state, 0x7F, 7)) /* fill any partial byte with ones */ |
|||
return false; |
|||
|
|||
state.put_buffer = 0; /* and reset bit-buffer to empty */ |
|||
state.put_bits = 0; |
|||
return true; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Emit a restart marker and resynchronize predictions.
|
|||
/// </summary>
|
|||
private bool emit_restart(savable_state state, int restart_num) |
|||
{ |
|||
if (!flush_bits(state)) |
|||
return false; |
|||
|
|||
if (!emit_byte(0xFF)) |
|||
return false; |
|||
|
|||
if (!emit_byte((int)(JPEG_MARKER.RST0 + restart_num))) |
|||
return false; |
|||
|
|||
/* Re-initialize DC predictions to 0 */ |
|||
for (int ci = 0; ci < m_cinfo.m_comps_in_scan; ci++) |
|||
state.last_dc_val[ci] = 0; |
|||
|
|||
/* The restart counter is not updated until we successfully write the MCU. */ |
|||
return true; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
/* Copyright (C) 2008-2011, Bit Miracle |
|||
* http://www.bitmiracle.com
|
|||
* |
|||
* Copyright (C) 1994-1996, Thomas G. Lane. |
|||
* This file is part of the Independent JPEG Group's software. |
|||
* For conditions of distribution and use, see the accompanying README file. |
|||
* |
|||
*/ |
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
|
|||
namespace BitMiracle.LibJpeg.Classic.Internal |
|||
{ |
|||
/// <summary>
|
|||
/// Coefficient buffer control
|
|||
/// </summary>
|
|||
interface jpeg_c_coef_controller |
|||
{ |
|||
void start_pass(J_BUF_MODE pass_mode); |
|||
bool compress_data(byte[][][] input_buf); |
|||
} |
|||
} |
|||
@ -0,0 +1,119 @@ |
|||
/* Copyright (C) 2008-2011, Bit Miracle |
|||
* http://www.bitmiracle.com
|
|||
* |
|||
* Copyright (C) 1994-1996, Thomas G. Lane. |
|||
* This file is part of the Independent JPEG Group's software. |
|||
* For conditions of distribution and use, see the accompanying README file. |
|||
* |
|||
*/ |
|||
|
|||
/* |
|||
* This file contains the main buffer controller for compression. |
|||
* The main buffer lies between the pre-processor and the JPEG |
|||
* compressor proper; it holds downsampled data in the JPEG colorspace. |
|||
*/ |
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
|
|||
namespace BitMiracle.LibJpeg.Classic.Internal |
|||
{ |
|||
/// <summary>
|
|||
/// Main buffer control (downsampled-data buffer)
|
|||
/// </summary>
|
|||
class jpeg_c_main_controller |
|||
{ |
|||
private jpeg_compress_struct m_cinfo; |
|||
|
|||
private int m_cur_iMCU_row; /* number of current iMCU row */ |
|||
private int m_rowgroup_ctr; /* counts row groups received in iMCU row */ |
|||
private bool m_suspended; /* remember if we suspended output */ |
|||
|
|||
/* If using just a strip buffer, this points to the entire set of buffers |
|||
* (we allocate one for each component). In the full-image case, this |
|||
* points to the currently accessible strips of the virtual arrays. |
|||
*/ |
|||
private byte[][][] m_buffer = new byte[JpegConstants.MAX_COMPONENTS][][]; |
|||
|
|||
public jpeg_c_main_controller(jpeg_compress_struct cinfo) |
|||
{ |
|||
m_cinfo = cinfo; |
|||
|
|||
/* Allocate a strip buffer for each component */ |
|||
for (int ci = 0; ci < cinfo.m_num_components; ci++) |
|||
{ |
|||
m_buffer[ci] = jpeg_common_struct.AllocJpegSamples( |
|||
cinfo.Component_info[ci].Width_in_blocks * JpegConstants.DCTSIZE, |
|||
cinfo.Component_info[ci].V_samp_factor * JpegConstants.DCTSIZE); |
|||
} |
|||
} |
|||
|
|||
// Initialize for a processing pass.
|
|||
public void start_pass(J_BUF_MODE pass_mode) |
|||
{ |
|||
/* Do nothing in raw-data mode. */ |
|||
if (m_cinfo.m_raw_data_in) |
|||
return; |
|||
|
|||
m_cur_iMCU_row = 0; /* initialize counters */ |
|||
m_rowgroup_ctr = 0; |
|||
m_suspended = false; |
|||
|
|||
if (pass_mode != J_BUF_MODE.JBUF_PASS_THRU) |
|||
m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_BUFFER_MODE); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Process some data.
|
|||
/// This routine handles the simple pass-through mode,
|
|||
/// where we have only a strip buffer.
|
|||
/// </summary>
|
|||
public void process_data(byte[][] input_buf, ref int in_row_ctr, int in_rows_avail) |
|||
{ |
|||
while (m_cur_iMCU_row < m_cinfo.m_total_iMCU_rows) |
|||
{ |
|||
/* Read input data if we haven't filled the main buffer yet */ |
|||
if (m_rowgroup_ctr < JpegConstants.DCTSIZE) |
|||
m_cinfo.m_prep.pre_process_data(input_buf, ref in_row_ctr, in_rows_avail, m_buffer, ref m_rowgroup_ctr, JpegConstants.DCTSIZE); |
|||
|
|||
/* If we don't have a full iMCU row buffered, return to application for |
|||
* more data. Note that preprocessor will always pad to fill the iMCU row |
|||
* at the bottom of the image. |
|||
*/ |
|||
if (m_rowgroup_ctr != JpegConstants.DCTSIZE) |
|||
return; |
|||
|
|||
/* Send the completed row to the compressor */ |
|||
if (!m_cinfo.m_coef.compress_data(m_buffer)) |
|||
{ |
|||
/* If compressor did not consume the whole row, then we must need to |
|||
* suspend processing and return to the application. In this situation |
|||
* we pretend we didn't yet consume the last input row; otherwise, if |
|||
* it happened to be the last row of the image, the application would |
|||
* think we were done. |
|||
*/ |
|||
if (!m_suspended) |
|||
{ |
|||
in_row_ctr--; |
|||
m_suspended = true; |
|||
} |
|||
|
|||
return; |
|||
} |
|||
|
|||
/* We did finish the row. Undo our little suspension hack if a previous |
|||
* call suspended; then mark the main buffer empty. |
|||
*/ |
|||
if (m_suspended) |
|||
{ |
|||
in_row_ctr++; |
|||
m_suspended = false; |
|||
} |
|||
|
|||
m_rowgroup_ctr = 0; |
|||
m_cur_iMCU_row++; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,287 @@ |
|||
/* Copyright (C) 2008-2011, Bit Miracle |
|||
* http://www.bitmiracle.com
|
|||
* |
|||
* Copyright (C) 1994-1996, Thomas G. Lane. |
|||
* This file is part of the Independent JPEG Group's software. |
|||
* For conditions of distribution and use, see the accompanying README file. |
|||
* |
|||
*/ |
|||
|
|||
/* |
|||
* This file contains the compression preprocessing controller. |
|||
* This controller manages the color conversion, downsampling, |
|||
* and edge expansion steps. |
|||
* |
|||
* Most of the complexity here is associated with buffering input rows |
|||
* as required by the downsampler. See the comments at the head of |
|||
* my_downsampler for the downsampler's needs. |
|||
*/ |
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
|
|||
namespace BitMiracle.LibJpeg.Classic.Internal |
|||
{ |
|||
/// <summary>
|
|||
/// Compression preprocessing (downsampling input buffer control).
|
|||
///
|
|||
/// For the simple (no-context-row) case, we just need to buffer one
|
|||
/// row group's worth of pixels for the downsampling step. At the bottom of
|
|||
/// the image, we pad to a full row group by replicating the last pixel row.
|
|||
/// The downsampler's last output row is then replicated if needed to pad
|
|||
/// out to a full iMCU row.
|
|||
///
|
|||
/// When providing context rows, we must buffer three row groups' worth of
|
|||
/// pixels. Three row groups are physically allocated, but the row pointer
|
|||
/// arrays are made five row groups high, with the extra pointers above and
|
|||
/// below "wrapping around" to point to the last and first real row groups.
|
|||
/// This allows the downsampler to access the proper context rows.
|
|||
/// At the top and bottom of the image, we create dummy context rows by
|
|||
/// copying the first or last real pixel row. This copying could be avoided
|
|||
/// by pointer hacking as is done in jdmainct.c, but it doesn't seem worth the
|
|||
/// trouble on the compression side.
|
|||
/// </summary>
|
|||
class jpeg_c_prep_controller |
|||
{ |
|||
private jpeg_compress_struct m_cinfo; |
|||
|
|||
/* Downsampling input buffer. This buffer holds color-converted data |
|||
* until we have enough to do a downsample step. |
|||
*/ |
|||
private byte[][][] m_color_buf = new byte[JpegConstants.MAX_COMPONENTS][][]; |
|||
private int m_colorBufRowsOffset; |
|||
|
|||
private int m_rows_to_go; /* counts rows remaining in source image */ |
|||
private int m_next_buf_row; /* index of next row to store in color_buf */ |
|||
|
|||
private int m_this_row_group; /* starting row index of group to process */ |
|||
private int m_next_buf_stop; /* downsample when we reach this index */ |
|||
|
|||
public jpeg_c_prep_controller(jpeg_compress_struct cinfo) |
|||
{ |
|||
m_cinfo = cinfo; |
|||
|
|||
/* Allocate the color conversion buffer. |
|||
* We make the buffer wide enough to allow the downsampler to edge-expand |
|||
* horizontally within the buffer, if it so chooses. |
|||
*/ |
|||
if (cinfo.m_downsample.NeedContextRows()) |
|||
{ |
|||
/* Set up to provide context rows */ |
|||
create_context_buffer(); |
|||
} |
|||
else |
|||
{ |
|||
/* No context, just make it tall enough for one row group */ |
|||
for (int ci = 0; ci < cinfo.m_num_components; ci++) |
|||
{ |
|||
m_colorBufRowsOffset = 0; |
|||
m_color_buf[ci] = jpeg_compress_struct.AllocJpegSamples( |
|||
(cinfo.Component_info[ci].Width_in_blocks * JpegConstants.DCTSIZE * cinfo.m_max_h_samp_factor) / cinfo.Component_info[ci].H_samp_factor, |
|||
cinfo.m_max_v_samp_factor); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initialize for a processing pass.
|
|||
/// </summary>
|
|||
public void start_pass(J_BUF_MODE pass_mode) |
|||
{ |
|||
if (pass_mode != J_BUF_MODE.JBUF_PASS_THRU) |
|||
m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_BUFFER_MODE); |
|||
|
|||
/* Initialize total-height counter for detecting bottom of image */ |
|||
m_rows_to_go = m_cinfo.m_image_height; |
|||
|
|||
/* Mark the conversion buffer empty */ |
|||
m_next_buf_row = 0; |
|||
|
|||
/* Preset additional state variables for context mode. |
|||
* These aren't used in non-context mode, so we needn't test which mode. |
|||
*/ |
|||
m_this_row_group = 0; |
|||
|
|||
/* Set next_buf_stop to stop after two row groups have been read in. */ |
|||
m_next_buf_stop = 2 * m_cinfo.m_max_v_samp_factor; |
|||
} |
|||
|
|||
public void pre_process_data(byte[][] input_buf, ref int in_row_ctr, int in_rows_avail, byte[][][] output_buf, ref int out_row_group_ctr, int out_row_groups_avail) |
|||
{ |
|||
if (m_cinfo.m_downsample.NeedContextRows()) |
|||
pre_process_context(input_buf, ref in_row_ctr, in_rows_avail, output_buf, ref out_row_group_ctr, out_row_groups_avail); |
|||
else |
|||
pre_process_WithoutContext(input_buf, ref in_row_ctr, in_rows_avail, output_buf, ref out_row_group_ctr, out_row_groups_avail); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Create the wrapped-around downsampling input buffer needed for context mode.
|
|||
/// </summary>
|
|||
private void create_context_buffer() |
|||
{ |
|||
int rgroup_height = m_cinfo.m_max_v_samp_factor; |
|||
for (int ci = 0; ci < m_cinfo.m_num_components; ci++) |
|||
{ |
|||
int samplesPerRow = (m_cinfo.Component_info[ci].Width_in_blocks * JpegConstants.DCTSIZE * m_cinfo.m_max_h_samp_factor) / m_cinfo.Component_info[ci].H_samp_factor; |
|||
|
|||
byte[][] fake_buffer = new byte[5 * rgroup_height][]; |
|||
for (int i = 1; i < 4 * rgroup_height; i++) |
|||
fake_buffer[i] = new byte [samplesPerRow]; |
|||
|
|||
/* Allocate the actual buffer space (3 row groups) for this component. |
|||
* We make the buffer wide enough to allow the downsampler to edge-expand |
|||
* horizontally within the buffer, if it so chooses. |
|||
*/ |
|||
byte[][] true_buffer = jpeg_common_struct.AllocJpegSamples(samplesPerRow, 3 * rgroup_height); |
|||
|
|||
/* Copy true buffer row pointers into the middle of the fake row array */ |
|||
for (int i = 0; i < 3 * rgroup_height; i++) |
|||
fake_buffer[rgroup_height + i] = true_buffer[i]; |
|||
|
|||
/* Fill in the above and below wraparound pointers */ |
|||
for (int i = 0; i < rgroup_height; i++) |
|||
{ |
|||
fake_buffer[i] = true_buffer[2 * rgroup_height + i]; |
|||
fake_buffer[4 * rgroup_height + i] = true_buffer[i]; |
|||
} |
|||
|
|||
m_color_buf[ci] = fake_buffer; |
|||
m_colorBufRowsOffset = rgroup_height; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Process some data in the simple no-context case.
|
|||
///
|
|||
/// Preprocessor output data is counted in "row groups". A row group
|
|||
/// is defined to be v_samp_factor sample rows of each component.
|
|||
/// Downsampling will produce this much data from each max_v_samp_factor
|
|||
/// input rows.
|
|||
/// </summary>
|
|||
private void pre_process_WithoutContext(byte[][] input_buf, ref int in_row_ctr, int in_rows_avail, byte[][][] output_buf, ref int out_row_group_ctr, int out_row_groups_avail) |
|||
{ |
|||
while (in_row_ctr < in_rows_avail && out_row_group_ctr < out_row_groups_avail) |
|||
{ |
|||
/* Do color conversion to fill the conversion buffer. */ |
|||
int inrows = in_rows_avail - in_row_ctr; |
|||
int numrows = m_cinfo.m_max_v_samp_factor - m_next_buf_row; |
|||
numrows = Math.Min(numrows, inrows); |
|||
m_cinfo.m_cconvert.color_convert(input_buf, in_row_ctr, m_color_buf, m_colorBufRowsOffset + m_next_buf_row, numrows); |
|||
in_row_ctr += numrows; |
|||
m_next_buf_row += numrows; |
|||
m_rows_to_go -= numrows; |
|||
|
|||
/* If at bottom of image, pad to fill the conversion buffer. */ |
|||
if (m_rows_to_go == 0 && m_next_buf_row < m_cinfo.m_max_v_samp_factor) |
|||
{ |
|||
for (int ci = 0; ci < m_cinfo.m_num_components; ci++) |
|||
expand_bottom_edge(m_color_buf[ci], m_colorBufRowsOffset, m_cinfo.m_image_width, m_next_buf_row, m_cinfo.m_max_v_samp_factor); |
|||
|
|||
m_next_buf_row = m_cinfo.m_max_v_samp_factor; |
|||
} |
|||
|
|||
/* If we've filled the conversion buffer, empty it. */ |
|||
if (m_next_buf_row == m_cinfo.m_max_v_samp_factor) |
|||
{ |
|||
m_cinfo.m_downsample.downsample(m_color_buf, m_colorBufRowsOffset, output_buf, out_row_group_ctr); |
|||
m_next_buf_row = 0; |
|||
out_row_group_ctr++; |
|||
} |
|||
|
|||
/* If at bottom of image, pad the output to a full iMCU height. |
|||
* Note we assume the caller is providing a one-iMCU-height output buffer! |
|||
*/ |
|||
if (m_rows_to_go == 0 && out_row_group_ctr < out_row_groups_avail) |
|||
{ |
|||
for (int ci = 0; ci < m_cinfo.m_num_components; ci++) |
|||
{ |
|||
jpeg_component_info componentInfo = m_cinfo.Component_info[ci]; |
|||
expand_bottom_edge(output_buf[ci], 0, componentInfo.Width_in_blocks * JpegConstants.DCTSIZE, |
|||
out_row_group_ctr * componentInfo.V_samp_factor, |
|||
out_row_groups_avail * componentInfo.V_samp_factor); |
|||
} |
|||
|
|||
out_row_group_ctr = out_row_groups_avail; |
|||
break; /* can exit outer loop without test */ |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Process some data in the context case.
|
|||
/// </summary>
|
|||
private void pre_process_context(byte[][] input_buf, ref int in_row_ctr, int in_rows_avail, byte[][][] output_buf, ref int out_row_group_ctr, int out_row_groups_avail) |
|||
{ |
|||
while (out_row_group_ctr < out_row_groups_avail) |
|||
{ |
|||
if (in_row_ctr < in_rows_avail) |
|||
{ |
|||
/* Do color conversion to fill the conversion buffer. */ |
|||
int inrows = in_rows_avail - in_row_ctr; |
|||
int numrows = m_next_buf_stop - m_next_buf_row; |
|||
numrows = Math.Min(numrows, inrows); |
|||
m_cinfo.m_cconvert.color_convert(input_buf, in_row_ctr, m_color_buf, m_colorBufRowsOffset + m_next_buf_row, numrows); |
|||
|
|||
/* Pad at top of image, if first time through */ |
|||
if (m_rows_to_go == m_cinfo.m_image_height) |
|||
{ |
|||
for (int ci = 0; ci < m_cinfo.m_num_components; ci++) |
|||
{ |
|||
for (int row = 1; row <= m_cinfo.m_max_v_samp_factor; row++) |
|||
JpegUtils.jcopy_sample_rows(m_color_buf[ci], m_colorBufRowsOffset, m_color_buf[ci], m_colorBufRowsOffset - row, 1, m_cinfo.m_image_width); |
|||
} |
|||
} |
|||
|
|||
in_row_ctr += numrows; |
|||
m_next_buf_row += numrows; |
|||
m_rows_to_go -= numrows; |
|||
} |
|||
else |
|||
{ |
|||
/* Return for more data, unless we are at the bottom of the image. */ |
|||
if (m_rows_to_go != 0) |
|||
break; |
|||
|
|||
/* When at bottom of image, pad to fill the conversion buffer. */ |
|||
if (m_next_buf_row < m_next_buf_stop) |
|||
{ |
|||
for (int ci = 0; ci < m_cinfo.m_num_components; ci++) |
|||
expand_bottom_edge(m_color_buf[ci], m_colorBufRowsOffset, m_cinfo.m_image_width, m_next_buf_row, m_next_buf_stop); |
|||
|
|||
m_next_buf_row = m_next_buf_stop; |
|||
} |
|||
} |
|||
|
|||
/* If we've gotten enough data, downsample a row group. */ |
|||
if (m_next_buf_row == m_next_buf_stop) |
|||
{ |
|||
m_cinfo.m_downsample.downsample(m_color_buf, m_colorBufRowsOffset + m_this_row_group, output_buf, out_row_group_ctr); |
|||
out_row_group_ctr++; |
|||
|
|||
/* Advance pointers with wraparound as necessary. */ |
|||
m_this_row_group += m_cinfo.m_max_v_samp_factor; |
|||
int buf_height = m_cinfo.m_max_v_samp_factor * 3; |
|||
|
|||
if (m_this_row_group >= buf_height) |
|||
m_this_row_group = 0; |
|||
|
|||
if (m_next_buf_row >= buf_height) |
|||
m_next_buf_row = 0; |
|||
|
|||
m_next_buf_stop = m_next_buf_row + m_cinfo.m_max_v_samp_factor; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Expand an image vertically from height input_rows to height output_rows,
|
|||
/// by duplicating the bottom row.
|
|||
/// </summary>
|
|||
private static void expand_bottom_edge(byte[][] image_data, int rowsOffset, int num_cols, int input_rows, int output_rows) |
|||
{ |
|||
for (int row = input_rows; row < output_rows; row++) |
|||
JpegUtils.jcopy_sample_rows(image_data, rowsOffset + input_rows - 1, image_data, row, 1, num_cols); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,419 @@ |
|||
/* Copyright (C) 2008-2011, Bit Miracle |
|||
* http://www.bitmiracle.com
|
|||
* |
|||
* Copyright (C) 1994-1996, Thomas G. Lane. |
|||
* This file is part of the Independent JPEG Group's software. |
|||
* For conditions of distribution and use, see the accompanying README file. |
|||
* |
|||
*/ |
|||
|
|||
/* |
|||
* This file contains input colorspace conversion routines. |
|||
*/ |
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
|
|||
namespace BitMiracle.LibJpeg.Classic.Internal |
|||
{ |
|||
/// <summary>
|
|||
/// Colorspace conversion
|
|||
/// </summary>
|
|||
class jpeg_color_converter |
|||
{ |
|||
private const int SCALEBITS = 16; /* speediest right-shift on some machines */ |
|||
private const int CBCR_OFFSET = JpegConstants.CENTERJSAMPLE << SCALEBITS; |
|||
private const int ONE_HALF = 1 << (SCALEBITS - 1); |
|||
|
|||
// We allocate one big table and divide it up into eight parts, instead of
|
|||
// doing eight alloc_small requests. This lets us use a single table base
|
|||
// address, which can be held in a register in the inner loops on many
|
|||
// machines (more than can hold all eight addresses, anyway).
|
|||
private const int R_Y_OFF = 0; /* offset to R => Y section */ |
|||
private const int G_Y_OFF = (1 * (JpegConstants.MAXJSAMPLE+1)); /* offset to G => Y section */ |
|||
private const int B_Y_OFF = (2 * (JpegConstants.MAXJSAMPLE+1)); /* etc. */ |
|||
private const int R_CB_OFF = (3 * (JpegConstants.MAXJSAMPLE+1)); |
|||
private const int G_CB_OFF = (4 * (JpegConstants.MAXJSAMPLE+1)); |
|||
private const int B_CB_OFF = (5 * (JpegConstants.MAXJSAMPLE+1)); |
|||
private const int R_CR_OFF = B_CB_OFF; /* B=>Cb, R=>Cr are the same */ |
|||
private const int G_CR_OFF = (6 * (JpegConstants.MAXJSAMPLE+1)); |
|||
private const int B_CR_OFF = (7 * (JpegConstants.MAXJSAMPLE+1)); |
|||
private const int TABLE_SIZE = (8 * (JpegConstants.MAXJSAMPLE + 1)); |
|||
|
|||
private jpeg_compress_struct m_cinfo; |
|||
|
|||
private bool m_useNullStart; |
|||
|
|||
private bool m_useCmykYcckConvert; |
|||
private bool m_useGrayscaleConvert; |
|||
private bool m_useNullConvert; |
|||
private bool m_useRgbGrayConvert; |
|||
private bool m_useRgbYccConvert; |
|||
|
|||
private int[] m_rgb_ycc_tab; /* => table for RGB to YCbCr conversion */ |
|||
|
|||
public jpeg_color_converter(jpeg_compress_struct cinfo) |
|||
{ |
|||
m_cinfo = cinfo; |
|||
|
|||
/* set start_pass to null method until we find out differently */ |
|||
m_useNullStart = true; |
|||
|
|||
/* Make sure input_components agrees with in_color_space */ |
|||
switch (cinfo.m_in_color_space) |
|||
{ |
|||
case J_COLOR_SPACE.JCS_GRAYSCALE: |
|||
if (cinfo.m_input_components != 1) |
|||
cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_IN_COLORSPACE); |
|||
break; |
|||
|
|||
case J_COLOR_SPACE.JCS_RGB: |
|||
case J_COLOR_SPACE.JCS_YCbCr: |
|||
if (cinfo.m_input_components != 3) |
|||
cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_IN_COLORSPACE); |
|||
break; |
|||
|
|||
case J_COLOR_SPACE.JCS_CMYK: |
|||
case J_COLOR_SPACE.JCS_YCCK: |
|||
if (cinfo.m_input_components != 4) |
|||
cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_IN_COLORSPACE); |
|||
break; |
|||
|
|||
default: |
|||
/* JCS_UNKNOWN can be anything */ |
|||
if (cinfo.m_input_components < 1) |
|||
cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_IN_COLORSPACE); |
|||
break; |
|||
} |
|||
|
|||
/* Check num_components, set conversion method based on requested space */ |
|||
clearConvertFlags(); |
|||
switch (cinfo.m_jpeg_color_space) |
|||
{ |
|||
case J_COLOR_SPACE.JCS_GRAYSCALE: |
|||
if (cinfo.m_num_components != 1) |
|||
cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_J_COLORSPACE); |
|||
|
|||
if (cinfo.m_in_color_space == J_COLOR_SPACE.JCS_GRAYSCALE) |
|||
m_useGrayscaleConvert = true; |
|||
else if (cinfo.m_in_color_space == J_COLOR_SPACE.JCS_RGB) |
|||
{ |
|||
m_useNullStart = false; // use rgb_ycc_start
|
|||
m_useRgbGrayConvert = true; |
|||
} |
|||
else if (cinfo.m_in_color_space == J_COLOR_SPACE.JCS_YCbCr) |
|||
m_useGrayscaleConvert = true; |
|||
else |
|||
cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CONVERSION_NOTIMPL); |
|||
break; |
|||
|
|||
case J_COLOR_SPACE.JCS_RGB: |
|||
if (cinfo.m_num_components != 3) |
|||
cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_J_COLORSPACE); |
|||
|
|||
if (cinfo.m_in_color_space == J_COLOR_SPACE.JCS_RGB) |
|||
m_useNullConvert = true; |
|||
else |
|||
cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CONVERSION_NOTIMPL); |
|||
break; |
|||
|
|||
case J_COLOR_SPACE.JCS_YCbCr: |
|||
if (cinfo.m_num_components != 3) |
|||
cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_J_COLORSPACE); |
|||
|
|||
if (cinfo.m_in_color_space == J_COLOR_SPACE.JCS_RGB) |
|||
{ |
|||
m_useNullStart = false; // use rgb_ycc_start
|
|||
m_useRgbYccConvert = true; |
|||
} |
|||
else if (cinfo.m_in_color_space == J_COLOR_SPACE.JCS_YCbCr) |
|||
m_useNullConvert = true; |
|||
else |
|||
cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CONVERSION_NOTIMPL); |
|||
break; |
|||
|
|||
case J_COLOR_SPACE.JCS_CMYK: |
|||
if (cinfo.m_num_components != 4) |
|||
cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_J_COLORSPACE); |
|||
|
|||
if (cinfo.m_in_color_space == J_COLOR_SPACE.JCS_CMYK) |
|||
m_useNullConvert = true; |
|||
else |
|||
cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CONVERSION_NOTIMPL); |
|||
break; |
|||
|
|||
case J_COLOR_SPACE.JCS_YCCK: |
|||
if (cinfo.m_num_components != 4) |
|||
cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_J_COLORSPACE); |
|||
|
|||
if (cinfo.m_in_color_space == J_COLOR_SPACE.JCS_CMYK) |
|||
{ |
|||
m_useNullStart = false; // use rgb_ycc_start
|
|||
m_useCmykYcckConvert = true; |
|||
} |
|||
else if (cinfo.m_in_color_space == J_COLOR_SPACE.JCS_YCCK) |
|||
m_useNullConvert = true; |
|||
else |
|||
cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CONVERSION_NOTIMPL); |
|||
break; |
|||
|
|||
default: |
|||
/* allow null conversion of JCS_UNKNOWN */ |
|||
if (cinfo.m_jpeg_color_space != cinfo.m_in_color_space || cinfo.m_num_components != cinfo.m_input_components) |
|||
cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CONVERSION_NOTIMPL); |
|||
|
|||
m_useNullConvert = true; |
|||
break; |
|||
} |
|||
} |
|||
|
|||
public void start_pass() |
|||
{ |
|||
if (!m_useNullStart) |
|||
rgb_ycc_start(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Convert some rows of samples to the JPEG colorspace.
|
|||
///
|
|||
/// Note that we change from the application's interleaved-pixel format
|
|||
/// to our internal noninterleaved, one-plane-per-component format.
|
|||
/// The input buffer is therefore three times as wide as the output buffer.
|
|||
///
|
|||
/// A starting row offset is provided only for the output buffer. The caller
|
|||
/// can easily adjust the passed input_buf value to accommodate any row
|
|||
/// offset required on that side.
|
|||
/// </summary>
|
|||
public void color_convert(byte[][] input_buf, int input_row, byte[][][] output_buf, int output_row, int num_rows) |
|||
{ |
|||
if (m_useCmykYcckConvert) |
|||
cmyk_ycck_convert(input_buf, input_row, output_buf, output_row, num_rows); |
|||
else if (m_useGrayscaleConvert) |
|||
grayscale_convert(input_buf, input_row, output_buf, output_row, num_rows); |
|||
else if (m_useRgbGrayConvert) |
|||
rgb_gray_convert(input_buf, input_row, output_buf, output_row, num_rows); |
|||
else if (m_useRgbYccConvert) |
|||
rgb_ycc_convert(input_buf, input_row, output_buf, output_row, num_rows); |
|||
else if (m_useNullConvert) |
|||
null_convert(input_buf, input_row, output_buf, output_row, num_rows); |
|||
else |
|||
m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CONVERSION_NOTIMPL); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initialize for RGB->YCC colorspace conversion.
|
|||
/// </summary>
|
|||
private void rgb_ycc_start() |
|||
{ |
|||
/* Allocate and fill in the conversion tables. */ |
|||
m_rgb_ycc_tab = new int[TABLE_SIZE]; |
|||
|
|||
for (int i = 0; i <= JpegConstants.MAXJSAMPLE; i++) |
|||
{ |
|||
m_rgb_ycc_tab[i + R_Y_OFF] = FIX(0.29900) * i; |
|||
m_rgb_ycc_tab[i + G_Y_OFF] = FIX(0.58700) * i; |
|||
m_rgb_ycc_tab[i + B_Y_OFF] = FIX(0.11400) * i + ONE_HALF; |
|||
m_rgb_ycc_tab[i + R_CB_OFF] = (-FIX(0.16874)) * i; |
|||
m_rgb_ycc_tab[i + G_CB_OFF] = (-FIX(0.33126)) * i; |
|||
|
|||
/* We use a rounding fudge-factor of 0.5-epsilon for Cb and Cr. |
|||
* This ensures that the maximum output will round to MAXJSAMPLE |
|||
* not MAXJSAMPLE+1, and thus that we don't have to range-limit. |
|||
*/ |
|||
m_rgb_ycc_tab[i + B_CB_OFF] = FIX(0.50000) * i + CBCR_OFFSET + ONE_HALF - 1; |
|||
|
|||
/* B=>Cb and R=>Cr tables are the same |
|||
rgb_ycc_tab[i+R_CR_OFF] = FIX(0.50000) * i + CBCR_OFFSET + ONE_HALF-1; |
|||
*/ |
|||
m_rgb_ycc_tab[i + G_CR_OFF] = (-FIX(0.41869)) * i; |
|||
m_rgb_ycc_tab[i + B_CR_OFF] = (-FIX(0.08131)) * i; |
|||
} |
|||
} |
|||
|
|||
private void clearConvertFlags() |
|||
{ |
|||
m_useCmykYcckConvert = false; |
|||
m_useGrayscaleConvert = false; |
|||
m_useNullConvert = false; |
|||
m_useRgbGrayConvert = false; |
|||
m_useRgbYccConvert = false; |
|||
} |
|||
|
|||
private static int FIX(double x) |
|||
{ |
|||
return (int)(x * (1L << SCALEBITS) + 0.5); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// RGB -> YCbCr conversion: most common case
|
|||
/// YCbCr is defined per CCIR 601-1, except that Cb and Cr are
|
|||
/// normalized to the range 0..MAXJSAMPLE rather than -0.5 .. 0.5.
|
|||
/// The conversion equations to be implemented are therefore
|
|||
/// Y = 0.29900 * R + 0.58700 * G + 0.11400 * B
|
|||
/// Cb = -0.16874 * R - 0.33126 * G + 0.50000 * B + CENTERJSAMPLE
|
|||
/// Cr = 0.50000 * R - 0.41869 * G - 0.08131 * B + CENTERJSAMPLE
|
|||
/// (These numbers are derived from TIFF 6.0 section 21, dated 3-June-92.)
|
|||
/// To avoid floating-point arithmetic, we represent the fractional constants
|
|||
/// as integers scaled up by 2^16 (about 4 digits precision); we have to divide
|
|||
/// the products by 2^16, with appropriate rounding, to get the correct answer.
|
|||
/// For even more speed, we avoid doing any multiplications in the inner loop
|
|||
/// by precalculating the constants times R,G,B for all possible values.
|
|||
/// For 8-bit JSAMPLEs this is very reasonable (only 256 entries per table);
|
|||
/// for 12-bit samples it is still acceptable. It's not very reasonable for
|
|||
/// 16-bit samples, but if you want lossless storage you shouldn't be changing
|
|||
/// colorspace anyway.
|
|||
/// The CENTERJSAMPLE offsets and the rounding fudge-factor of 0.5 are included
|
|||
/// in the tables to save adding them separately in the inner loop.
|
|||
/// </summary>
|
|||
private void rgb_ycc_convert(byte[][] input_buf, int input_row, byte[][][] output_buf, int output_row, int num_rows) |
|||
{ |
|||
int num_cols = m_cinfo.m_image_width; |
|||
for (int row = 0; row < num_rows; row++) |
|||
{ |
|||
int columnOffset = 0; |
|||
for (int col = 0; col < num_cols; col++) |
|||
{ |
|||
int r = input_buf[input_row + row][columnOffset + JpegConstants.RGB_RED]; |
|||
int g = input_buf[input_row + row][columnOffset + JpegConstants.RGB_GREEN]; |
|||
int b = input_buf[input_row + row][columnOffset + JpegConstants.RGB_BLUE]; |
|||
columnOffset += JpegConstants.RGB_PIXELSIZE; |
|||
|
|||
/* If the inputs are 0..MAXJSAMPLE, the outputs of these equations |
|||
* must be too; we do not need an explicit range-limiting operation. |
|||
* Hence the value being shifted is never negative, and we don't |
|||
* need the general RIGHT_SHIFT macro. |
|||
*/ |
|||
/* Y */ |
|||
output_buf[0][output_row][col] = (byte)((m_rgb_ycc_tab[r + R_Y_OFF] + m_rgb_ycc_tab[g + G_Y_OFF] + m_rgb_ycc_tab[b + B_Y_OFF]) >> SCALEBITS); |
|||
/* Cb */ |
|||
output_buf[1][output_row][col] = (byte)((m_rgb_ycc_tab[r + R_CB_OFF] + m_rgb_ycc_tab[g + G_CB_OFF] + m_rgb_ycc_tab[b + B_CB_OFF]) >> SCALEBITS); |
|||
/* Cr */ |
|||
output_buf[2][output_row][col] = (byte)((m_rgb_ycc_tab[r + R_CR_OFF] + m_rgb_ycc_tab[g + G_CR_OFF] + m_rgb_ycc_tab[b + B_CR_OFF]) >> SCALEBITS); |
|||
} |
|||
|
|||
output_row++; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Convert some rows of samples to the JPEG colorspace.
|
|||
/// This version handles RGB->grayscale conversion, which is the same
|
|||
/// as the RGB->Y portion of RGB->YCbCr.
|
|||
/// We assume rgb_ycc_start has been called (we only use the Y tables).
|
|||
/// </summary>
|
|||
private void rgb_gray_convert(byte[][] input_buf, int input_row, byte[][][] output_buf, int output_row, int num_rows) |
|||
{ |
|||
int num_cols = m_cinfo.m_image_width; |
|||
for (int row = 0; row < num_rows; row++) |
|||
{ |
|||
int columnOffset = 0; |
|||
for (int col = 0; col < num_cols; col++) |
|||
{ |
|||
int r = input_buf[input_row + row][columnOffset + JpegConstants.RGB_RED]; |
|||
int g = input_buf[input_row + row][columnOffset + JpegConstants.RGB_GREEN]; |
|||
int b = input_buf[input_row + row][columnOffset + JpegConstants.RGB_BLUE]; |
|||
columnOffset += JpegConstants.RGB_PIXELSIZE; |
|||
|
|||
/* Y */ |
|||
output_buf[0][output_row][col] = (byte)((m_rgb_ycc_tab[r + R_Y_OFF] + m_rgb_ycc_tab[g + G_Y_OFF] + m_rgb_ycc_tab[b + B_Y_OFF]) >> SCALEBITS); |
|||
} |
|||
|
|||
output_row++; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Convert some rows of samples to the JPEG colorspace.
|
|||
/// This version handles Adobe-style CMYK->YCCK conversion,
|
|||
/// where we convert R=1-C, G=1-M, and B=1-Y to YCbCr using the same
|
|||
/// conversion as above, while passing K (black) unchanged.
|
|||
/// We assume rgb_ycc_start has been called.
|
|||
/// </summary>
|
|||
private void cmyk_ycck_convert(byte[][] input_buf, int input_row, byte[][][] output_buf, int output_row, int num_rows) |
|||
{ |
|||
int num_cols = m_cinfo.m_image_width; |
|||
for (int row = 0; row < num_rows; row++) |
|||
{ |
|||
int columnOffset = 0; |
|||
for (int col = 0; col < num_cols; col++) |
|||
{ |
|||
int r = JpegConstants.MAXJSAMPLE - input_buf[input_row + row][columnOffset]; |
|||
int g = JpegConstants.MAXJSAMPLE - input_buf[input_row + row][columnOffset + 1]; |
|||
int b = JpegConstants.MAXJSAMPLE - input_buf[input_row + row][columnOffset + 2]; |
|||
|
|||
/* K passes through as-is */ |
|||
/* don't need GETJSAMPLE here */ |
|||
output_buf[3][output_row][col] = input_buf[input_row + row][columnOffset + 3]; |
|||
columnOffset += 4; |
|||
|
|||
/* If the inputs are 0..MAXJSAMPLE, the outputs of these equations |
|||
* must be too; we do not need an explicit range-limiting operation. |
|||
* Hence the value being shifted is never negative, and we don't |
|||
* need the general RIGHT_SHIFT macro. |
|||
*/ |
|||
/* Y */ |
|||
output_buf[0][output_row][col] = (byte)((m_rgb_ycc_tab[r + R_Y_OFF] + m_rgb_ycc_tab[g + G_Y_OFF] + m_rgb_ycc_tab[b + B_Y_OFF]) >> SCALEBITS); |
|||
/* Cb */ |
|||
output_buf[1][output_row][col] = (byte)((m_rgb_ycc_tab[r + R_CB_OFF] + m_rgb_ycc_tab[g + G_CB_OFF] + m_rgb_ycc_tab[b + B_CB_OFF]) >> SCALEBITS); |
|||
/* Cr */ |
|||
output_buf[2][output_row][col] = (byte)((m_rgb_ycc_tab[r + R_CR_OFF] + m_rgb_ycc_tab[g + G_CR_OFF] + m_rgb_ycc_tab[b + B_CR_OFF]) >> SCALEBITS); |
|||
} |
|||
|
|||
output_row++; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Convert some rows of samples to the JPEG colorspace.
|
|||
/// This version handles grayscale output with no conversion.
|
|||
/// The source can be either plain grayscale or YCbCr (since Y == gray).
|
|||
/// </summary>
|
|||
private void grayscale_convert(byte[][] input_buf, int input_row, byte[][][] output_buf, int output_row, int num_rows) |
|||
{ |
|||
int num_cols = m_cinfo.m_image_width; |
|||
int instride = m_cinfo.m_input_components; |
|||
|
|||
for (int row = 0; row < num_rows; row++) |
|||
{ |
|||
int columnOffset = 0; |
|||
for (int col = 0; col < num_cols; col++) |
|||
{ |
|||
/* don't need GETJSAMPLE() here */ |
|||
output_buf[0][output_row][col] = input_buf[input_row + row][columnOffset]; |
|||
columnOffset += instride; |
|||
} |
|||
|
|||
output_row++; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Convert some rows of samples to the JPEG colorspace.
|
|||
/// This version handles multi-component colorspaces without conversion.
|
|||
/// We assume input_components == num_components.
|
|||
/// </summary>
|
|||
private void null_convert(byte[][] input_buf, int input_row, byte[][][] output_buf, int output_row, int num_rows) |
|||
{ |
|||
int nc = m_cinfo.m_num_components; |
|||
int num_cols = m_cinfo.m_image_width; |
|||
|
|||
for (int row = 0; row < num_rows; row++) |
|||
{ |
|||
/* It seems fastest to make a separate pass for each component. */ |
|||
for (int ci = 0; ci < nc; ci++) |
|||
{ |
|||
int columnOffset = 0; |
|||
for (int col = 0; col < num_cols; col++) |
|||
{ |
|||
/* don't need GETJSAMPLE() here */ |
|||
output_buf[ci][output_row][col] = input_buf[input_row + row][columnOffset + ci]; |
|||
columnOffset += nc; |
|||
} |
|||
} |
|||
|
|||
output_row++; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,388 @@ |
|||
/* Copyright (C) 2008-2011, Bit Miracle |
|||
* http://www.bitmiracle.com
|
|||
* |
|||
* Copyright (C) 1994-1996, Thomas G. Lane. |
|||
* This file is part of the Independent JPEG Group's software. |
|||
* For conditions of distribution and use, see the accompanying README file. |
|||
* |
|||
*/ |
|||
|
|||
/* |
|||
* This file contains output colorspace conversion routines. |
|||
*/ |
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
|
|||
namespace BitMiracle.LibJpeg.Classic.Internal |
|||
{ |
|||
/// <summary>
|
|||
/// Colorspace conversion
|
|||
/// </summary>
|
|||
class jpeg_color_deconverter |
|||
{ |
|||
private const int SCALEBITS = 16; /* speediest right-shift on some machines */ |
|||
private const int ONE_HALF = 1 << (SCALEBITS - 1); |
|||
|
|||
private enum ColorConverter |
|||
{ |
|||
grayscale_converter, |
|||
ycc_rgb_converter, |
|||
gray_rgb_converter, |
|||
null_converter, |
|||
ycck_cmyk_converter |
|||
} |
|||
|
|||
private ColorConverter m_converter; |
|||
private jpeg_decompress_struct m_cinfo; |
|||
|
|||
private int[] m_perComponentOffsets; |
|||
|
|||
/* Private state for YCC->RGB conversion */ |
|||
private int[] m_Cr_r_tab; /* => table for Cr to R conversion */ |
|||
private int[] m_Cb_b_tab; /* => table for Cb to B conversion */ |
|||
private int[] m_Cr_g_tab; /* => table for Cr to G conversion */ |
|||
private int[] m_Cb_g_tab; /* => table for Cb to G conversion */ |
|||
|
|||
/// <summary>
|
|||
/// Module initialization routine for output colorspace conversion.
|
|||
/// </summary>
|
|||
public jpeg_color_deconverter(jpeg_decompress_struct cinfo) |
|||
{ |
|||
m_cinfo = cinfo; |
|||
|
|||
/* Make sure num_components agrees with jpeg_color_space */ |
|||
switch (cinfo.m_jpeg_color_space) |
|||
{ |
|||
case J_COLOR_SPACE.JCS_GRAYSCALE: |
|||
if (cinfo.m_num_components != 1) |
|||
cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_J_COLORSPACE); |
|||
break; |
|||
|
|||
case J_COLOR_SPACE.JCS_RGB: |
|||
case J_COLOR_SPACE.JCS_YCbCr: |
|||
if (cinfo.m_num_components != 3) |
|||
cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_J_COLORSPACE); |
|||
break; |
|||
|
|||
case J_COLOR_SPACE.JCS_CMYK: |
|||
case J_COLOR_SPACE.JCS_YCCK: |
|||
if (cinfo.m_num_components != 4) |
|||
cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_J_COLORSPACE); |
|||
break; |
|||
|
|||
default: |
|||
/* JCS_UNKNOWN can be anything */ |
|||
if (cinfo.m_num_components < 1) |
|||
cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_J_COLORSPACE); |
|||
break; |
|||
} |
|||
|
|||
/* Set out_color_components and conversion method based on requested space. |
|||
* Also clear the component_needed flags for any unused components, |
|||
* so that earlier pipeline stages can avoid useless computation. |
|||
*/ |
|||
|
|||
switch (cinfo.m_out_color_space) |
|||
{ |
|||
case J_COLOR_SPACE.JCS_GRAYSCALE: |
|||
cinfo.m_out_color_components = 1; |
|||
if (cinfo.m_jpeg_color_space == J_COLOR_SPACE.JCS_GRAYSCALE || cinfo.m_jpeg_color_space == J_COLOR_SPACE.JCS_YCbCr) |
|||
{ |
|||
m_converter = ColorConverter.grayscale_converter; |
|||
/* For color->grayscale conversion, only the Y (0) component is needed */ |
|||
for (int ci = 1; ci < cinfo.m_num_components; ci++) |
|||
cinfo.Comp_info[ci].component_needed = false; |
|||
} |
|||
else |
|||
cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CONVERSION_NOTIMPL); |
|||
break; |
|||
|
|||
case J_COLOR_SPACE.JCS_RGB: |
|||
cinfo.m_out_color_components = JpegConstants.RGB_PIXELSIZE; |
|||
if (cinfo.m_jpeg_color_space == J_COLOR_SPACE.JCS_YCbCr) |
|||
{ |
|||
m_converter = ColorConverter.ycc_rgb_converter; |
|||
build_ycc_rgb_table(); |
|||
} |
|||
else if (cinfo.m_jpeg_color_space == J_COLOR_SPACE.JCS_GRAYSCALE) |
|||
m_converter = ColorConverter.gray_rgb_converter; |
|||
else if (cinfo.m_jpeg_color_space == J_COLOR_SPACE.JCS_RGB) |
|||
m_converter = ColorConverter.null_converter; |
|||
else |
|||
cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CONVERSION_NOTIMPL); |
|||
break; |
|||
|
|||
case J_COLOR_SPACE.JCS_CMYK: |
|||
cinfo.m_out_color_components = 4; |
|||
if (cinfo.m_jpeg_color_space == J_COLOR_SPACE.JCS_YCCK) |
|||
{ |
|||
m_converter = ColorConverter.ycck_cmyk_converter; |
|||
build_ycc_rgb_table(); |
|||
} |
|||
else if (cinfo.m_jpeg_color_space == J_COLOR_SPACE.JCS_CMYK) |
|||
m_converter = ColorConverter.null_converter; |
|||
else |
|||
cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CONVERSION_NOTIMPL); |
|||
break; |
|||
|
|||
default: |
|||
/* Permit null conversion to same output space */ |
|||
if (cinfo.m_out_color_space == cinfo.m_jpeg_color_space) |
|||
{ |
|||
cinfo.m_out_color_components = cinfo.m_num_components; |
|||
m_converter = ColorConverter.null_converter; |
|||
} |
|||
else |
|||
{ |
|||
/* unsupported non-null conversion */ |
|||
cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CONVERSION_NOTIMPL); |
|||
} |
|||
break; |
|||
} |
|||
|
|||
if (cinfo.m_quantize_colors) |
|||
cinfo.m_output_components = 1; /* single colormapped output component */ |
|||
else |
|||
cinfo.m_output_components = cinfo.m_out_color_components; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Convert some rows of samples to the output colorspace.
|
|||
///
|
|||
/// Note that we change from noninterleaved, one-plane-per-component format
|
|||
/// to interleaved-pixel format. The output buffer is therefore three times
|
|||
/// as wide as the input buffer.
|
|||
/// A starting row offset is provided only for the input buffer. The caller
|
|||
/// can easily adjust the passed output_buf value to accommodate any row
|
|||
/// offset required on that side.
|
|||
/// </summary>
|
|||
public void color_convert(ComponentBuffer[] input_buf, int[] perComponentOffsets, int input_row, byte[][] output_buf, int output_row, int num_rows) |
|||
{ |
|||
m_perComponentOffsets = perComponentOffsets; |
|||
|
|||
switch (m_converter) |
|||
{ |
|||
case ColorConverter.grayscale_converter: |
|||
grayscale_convert(input_buf, input_row, output_buf, output_row, num_rows); |
|||
break; |
|||
case ColorConverter.ycc_rgb_converter: |
|||
ycc_rgb_convert(input_buf, input_row, output_buf, output_row, num_rows); |
|||
break; |
|||
case ColorConverter.gray_rgb_converter: |
|||
gray_rgb_convert(input_buf, input_row, output_buf, output_row, num_rows); |
|||
break; |
|||
case ColorConverter.null_converter: |
|||
null_convert(input_buf, input_row, output_buf, output_row, num_rows); |
|||
break; |
|||
case ColorConverter.ycck_cmyk_converter: |
|||
ycck_cmyk_convert(input_buf, input_row, output_buf, output_row, num_rows); |
|||
break; |
|||
default: |
|||
m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CONVERSION_NOTIMPL); |
|||
break; |
|||
} |
|||
} |
|||
|
|||
/**************** YCbCr -> RGB conversion: most common case **************/ |
|||
|
|||
/* |
|||
* YCbCr is defined per CCIR 601-1, except that Cb and Cr are |
|||
* normalized to the range 0..MAXJSAMPLE rather than -0.5 .. 0.5. |
|||
* The conversion equations to be implemented are therefore |
|||
* R = Y + 1.40200 * Cr |
|||
* G = Y - 0.34414 * Cb - 0.71414 * Cr |
|||
* B = Y + 1.77200 * Cb |
|||
* where Cb and Cr represent the incoming values less CENTERJSAMPLE. |
|||
* (These numbers are derived from TIFF 6.0 section 21, dated 3-June-92.) |
|||
* |
|||
* To avoid floating-point arithmetic, we represent the fractional constants |
|||
* as integers scaled up by 2^16 (about 4 digits precision); we have to divide |
|||
* the products by 2^16, with appropriate rounding, to get the correct answer. |
|||
* Notice that Y, being an integral input, does not contribute any fraction |
|||
* so it need not participate in the rounding. |
|||
* |
|||
* For even more speed, we avoid doing any multiplications in the inner loop |
|||
* by precalculating the constants times Cb and Cr for all possible values. |
|||
* For 8-bit JSAMPLEs this is very reasonable (only 256 entries per table); |
|||
* for 12-bit samples it is still acceptable. It's not very reasonable for |
|||
* 16-bit samples, but if you want lossless storage you shouldn't be changing |
|||
* colorspace anyway. |
|||
* The Cr=>R and Cb=>B values can be rounded to integers in advance; the |
|||
* values for the G calculation are left scaled up, since we must add them |
|||
* together before rounding. |
|||
*/ |
|||
|
|||
/// <summary>
|
|||
/// Initialize tables for YCC->RGB colorspace conversion.
|
|||
/// </summary>
|
|||
private void build_ycc_rgb_table() |
|||
{ |
|||
m_Cr_r_tab = new int[JpegConstants.MAXJSAMPLE + 1]; |
|||
m_Cb_b_tab = new int[JpegConstants.MAXJSAMPLE + 1]; |
|||
m_Cr_g_tab = new int[JpegConstants.MAXJSAMPLE + 1]; |
|||
m_Cb_g_tab = new int[JpegConstants.MAXJSAMPLE + 1]; |
|||
|
|||
for (int i = 0, x = -JpegConstants.CENTERJSAMPLE; i <= JpegConstants.MAXJSAMPLE; i++, x++) |
|||
{ |
|||
/* i is the actual input pixel value, in the range 0..MAXJSAMPLE */ |
|||
/* The Cb or Cr value we are thinking of is x = i - CENTERJSAMPLE */ |
|||
/* Cr=>R value is nearest int to 1.40200 * x */ |
|||
m_Cr_r_tab[i] = JpegUtils.RIGHT_SHIFT(FIX(1.40200) * x + ONE_HALF, SCALEBITS); |
|||
|
|||
/* Cb=>B value is nearest int to 1.77200 * x */ |
|||
m_Cb_b_tab[i] = JpegUtils.RIGHT_SHIFT(FIX(1.77200) * x + ONE_HALF, SCALEBITS); |
|||
|
|||
/* Cr=>G value is scaled-up -0.71414 * x */ |
|||
m_Cr_g_tab[i] = (-FIX(0.71414)) * x; |
|||
|
|||
/* Cb=>G value is scaled-up -0.34414 * x */ |
|||
/* We also add in ONE_HALF so that need not do it in inner loop */ |
|||
m_Cb_g_tab[i] = (-FIX(0.34414)) * x + ONE_HALF; |
|||
} |
|||
} |
|||
|
|||
private void ycc_rgb_convert(ComponentBuffer[] input_buf, int input_row, byte[][] output_buf, int output_row, int num_rows) |
|||
{ |
|||
int component0RowOffset = m_perComponentOffsets[0]; |
|||
int component1RowOffset = m_perComponentOffsets[1]; |
|||
int component2RowOffset = m_perComponentOffsets[2]; |
|||
|
|||
byte[] limit = m_cinfo.m_sample_range_limit; |
|||
int limitOffset = m_cinfo.m_sampleRangeLimitOffset; |
|||
|
|||
for (int row = 0; row < num_rows; row++) |
|||
{ |
|||
int columnOffset = 0; |
|||
for (int col = 0; col < m_cinfo.m_output_width; col++) |
|||
{ |
|||
int y = input_buf[0][input_row + component0RowOffset][col]; |
|||
int cb = input_buf[1][input_row + component1RowOffset][col]; |
|||
int cr = input_buf[2][input_row + component2RowOffset][col]; |
|||
|
|||
/* Range-limiting is essential due to noise introduced by DCT losses. */ |
|||
output_buf[output_row + row][columnOffset + JpegConstants.RGB_RED] = limit[limitOffset + y + m_Cr_r_tab[cr]]; |
|||
output_buf[output_row + row][columnOffset + JpegConstants.RGB_GREEN] = limit[limitOffset + y + JpegUtils.RIGHT_SHIFT(m_Cb_g_tab[cb] + m_Cr_g_tab[cr], SCALEBITS)]; |
|||
output_buf[output_row + row][columnOffset + JpegConstants.RGB_BLUE] = limit[limitOffset + y + m_Cb_b_tab[cb]]; |
|||
columnOffset += JpegConstants.RGB_PIXELSIZE; |
|||
} |
|||
|
|||
input_row++; |
|||
} |
|||
} |
|||
|
|||
/**************** Cases other than YCbCr -> RGB **************/ |
|||
|
|||
/// <summary>
|
|||
/// Adobe-style YCCK->CMYK conversion.
|
|||
/// We convert YCbCr to R=1-C, G=1-M, and B=1-Y using the same
|
|||
/// conversion as above, while passing K (black) unchanged.
|
|||
/// We assume build_ycc_rgb_table has been called.
|
|||
/// </summary>
|
|||
private void ycck_cmyk_convert(ComponentBuffer[] input_buf, int input_row, byte[][] output_buf, int output_row, int num_rows) |
|||
{ |
|||
int component0RowOffset = m_perComponentOffsets[0]; |
|||
int component1RowOffset = m_perComponentOffsets[1]; |
|||
int component2RowOffset = m_perComponentOffsets[2]; |
|||
int component3RowOffset = m_perComponentOffsets[3]; |
|||
|
|||
byte[] limit = m_cinfo.m_sample_range_limit; |
|||
int limitOffset = m_cinfo.m_sampleRangeLimitOffset; |
|||
|
|||
int num_cols = m_cinfo.m_output_width; |
|||
for (int row = 0; row < num_rows; row++) |
|||
{ |
|||
int columnOffset = 0; |
|||
for (int col = 0; col < num_cols; col++) |
|||
{ |
|||
int y = input_buf[0][input_row + component0RowOffset][col]; |
|||
int cb = input_buf[1][input_row + component1RowOffset][col]; |
|||
int cr = input_buf[2][input_row + component2RowOffset][col]; |
|||
|
|||
/* Range-limiting is essential due to noise introduced by DCT losses. */ |
|||
output_buf[output_row + row][columnOffset] = limit[limitOffset + JpegConstants.MAXJSAMPLE - (y + m_Cr_r_tab[cr])]; /* red */ |
|||
output_buf[output_row + row][columnOffset + 1] = limit[limitOffset + JpegConstants.MAXJSAMPLE - (y + JpegUtils.RIGHT_SHIFT(m_Cb_g_tab[cb] + m_Cr_g_tab[cr], SCALEBITS))]; /* green */ |
|||
output_buf[output_row + row][columnOffset + 2] = limit[limitOffset + JpegConstants.MAXJSAMPLE - (y + m_Cb_b_tab[cb])]; /* blue */ |
|||
|
|||
/* K passes through unchanged */ |
|||
/* don't need GETJSAMPLE here */ |
|||
output_buf[output_row + row][columnOffset + 3] = input_buf[3][input_row + component3RowOffset][col]; |
|||
columnOffset += 4; |
|||
} |
|||
|
|||
input_row++; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Convert grayscale to RGB: just duplicate the graylevel three times.
|
|||
/// This is provided to support applications that don't want to cope
|
|||
/// with grayscale as a separate case.
|
|||
/// </summary>
|
|||
private void gray_rgb_convert(ComponentBuffer[] input_buf, int input_row, byte[][] output_buf, int output_row, int num_rows) |
|||
{ |
|||
int component0RowOffset = m_perComponentOffsets[0]; |
|||
int component1RowOffset = m_perComponentOffsets[1]; |
|||
int component2RowOffset = m_perComponentOffsets[2]; |
|||
|
|||
int num_cols = m_cinfo.m_output_width; |
|||
for (int row = 0; row < num_rows; row++) |
|||
{ |
|||
int columnOffset = 0; |
|||
for (int col = 0; col < num_cols; col++) |
|||
{ |
|||
/* We can dispense with GETJSAMPLE() here */ |
|||
output_buf[output_row + row][columnOffset + JpegConstants.RGB_RED] = input_buf[0][input_row + component0RowOffset][col]; |
|||
output_buf[output_row + row][columnOffset + JpegConstants.RGB_GREEN] = input_buf[0][input_row + component1RowOffset][col]; |
|||
output_buf[output_row + row][columnOffset + JpegConstants.RGB_BLUE] = input_buf[0][input_row + component2RowOffset][col]; |
|||
columnOffset += JpegConstants.RGB_PIXELSIZE; |
|||
} |
|||
|
|||
input_row++; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Color conversion for grayscale: just copy the data.
|
|||
/// This also works for YCbCr -> grayscale conversion, in which
|
|||
/// we just copy the Y (luminance) component and ignore chrominance.
|
|||
/// </summary>
|
|||
private void grayscale_convert(ComponentBuffer[] input_buf, int input_row, byte[][] output_buf, int output_row, int num_rows) |
|||
{ |
|||
JpegUtils.jcopy_sample_rows(input_buf[0], input_row + m_perComponentOffsets[0], output_buf, output_row, num_rows, m_cinfo.m_output_width); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Color conversion for no colorspace change: just copy the data,
|
|||
/// converting from separate-planes to interleaved representation.
|
|||
/// </summary>
|
|||
private void null_convert(ComponentBuffer[] input_buf, int input_row, byte[][] output_buf, int output_row, int num_rows) |
|||
{ |
|||
for (int row = 0; row < num_rows; row++) |
|||
{ |
|||
for (int ci = 0; ci < m_cinfo.m_num_components; ci++) |
|||
{ |
|||
int columnIndex = 0; |
|||
int componentOffset = 0; |
|||
int perComponentOffset = m_perComponentOffsets[ci]; |
|||
|
|||
for (int count = m_cinfo.m_output_width; count > 0; count--) |
|||
{ |
|||
/* needn't bother with GETJSAMPLE() here */ |
|||
output_buf[output_row + row][ci + componentOffset] = input_buf[ci][input_row + perComponentOffset][columnIndex]; |
|||
componentOffset += m_cinfo.m_num_components; |
|||
columnIndex++; |
|||
} |
|||
} |
|||
|
|||
input_row++; |
|||
} |
|||
} |
|||
|
|||
private static int FIX(double x) |
|||
{ |
|||
return (int)(x * (1L << SCALEBITS) + 0.5); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,28 @@ |
|||
/* Copyright (C) 2008-2011, Bit Miracle |
|||
* http://www.bitmiracle.com
|
|||
* |
|||
* Copyright (C) 1994-1996, Thomas G. Lane. |
|||
* This file is part of the Independent JPEG Group's software. |
|||
* For conditions of distribution and use, see the accompanying README file. |
|||
* |
|||
*/ |
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
|
|||
namespace BitMiracle.LibJpeg.Classic.Internal |
|||
{ |
|||
/// <summary>
|
|||
/// Color quantization or color precision reduction
|
|||
/// </summary>
|
|||
interface jpeg_color_quantizer |
|||
{ |
|||
void start_pass(bool is_pre_scan); |
|||
|
|||
void color_quantize(byte[][] input_buf, int in_row, byte[][] output_buf, int out_row, int num_rows); |
|||
|
|||
void finish_pass(); |
|||
void new_color_map(); |
|||
} |
|||
} |
|||
@ -0,0 +1,364 @@ |
|||
/* Copyright (C) 2008-2011, Bit Miracle |
|||
* http://www.bitmiracle.com
|
|||
* |
|||
* Copyright (C) 1994-1996, Thomas G. Lane. |
|||
* This file is part of the Independent JPEG Group's software. |
|||
* For conditions of distribution and use, see the accompanying README file. |
|||
* |
|||
*/ |
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
|
|||
namespace BitMiracle.LibJpeg.Classic.Internal |
|||
{ |
|||
/// <summary>
|
|||
/// Master control module
|
|||
/// </summary>
|
|||
class jpeg_comp_master |
|||
{ |
|||
private enum c_pass_type |
|||
{ |
|||
main_pass, /* input data, also do first output step */ |
|||
huff_opt_pass, /* Huffman code optimization pass */ |
|||
output_pass /* data output pass */ |
|||
} |
|||
|
|||
private jpeg_compress_struct m_cinfo; |
|||
|
|||
private bool m_call_pass_startup; /* True if pass_startup must be called */ |
|||
private bool m_is_last_pass; /* True during last pass */ |
|||
|
|||
private c_pass_type m_pass_type; /* the type of the current pass */ |
|||
|
|||
private int m_pass_number; /* # of passes completed */ |
|||
private int m_total_passes; /* total # of passes needed */ |
|||
|
|||
private int m_scan_number; /* current index in scan_info[] */ |
|||
|
|||
public jpeg_comp_master(jpeg_compress_struct cinfo, bool transcode_only) |
|||
{ |
|||
m_cinfo = cinfo; |
|||
|
|||
if (transcode_only) |
|||
{ |
|||
/* no main pass in transcoding */ |
|||
if (cinfo.m_optimize_coding) |
|||
m_pass_type = c_pass_type.huff_opt_pass; |
|||
else |
|||
m_pass_type = c_pass_type.output_pass; |
|||
} |
|||
else |
|||
{ |
|||
/* for normal compression, first pass is always this type: */ |
|||
m_pass_type = c_pass_type.main_pass; |
|||
} |
|||
|
|||
if (cinfo.m_optimize_coding) |
|||
m_total_passes = cinfo.m_num_scans * 2; |
|||
else |
|||
m_total_passes = cinfo.m_num_scans; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Per-pass setup.
|
|||
///
|
|||
/// This is called at the beginning of each pass. We determine which
|
|||
/// modules will be active during this pass and give them appropriate
|
|||
/// start_pass calls.
|
|||
/// We also set is_last_pass to indicate whether any more passes will
|
|||
/// be required.
|
|||
/// </summary>
|
|||
public void prepare_for_pass() |
|||
{ |
|||
switch (m_pass_type) |
|||
{ |
|||
case c_pass_type.main_pass: |
|||
prepare_for_main_pass(); |
|||
break; |
|||
case c_pass_type.huff_opt_pass: |
|||
if (!prepare_for_huff_opt_pass()) |
|||
break; |
|||
prepare_for_output_pass(); |
|||
break; |
|||
case c_pass_type.output_pass: |
|||
prepare_for_output_pass(); |
|||
break; |
|||
default: |
|||
m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_NOT_COMPILED); |
|||
break; |
|||
} |
|||
|
|||
m_is_last_pass = (m_pass_number == m_total_passes - 1); |
|||
|
|||
/* Set up progress monitor's pass info if present */ |
|||
if (m_cinfo.m_progress != null) |
|||
{ |
|||
m_cinfo.m_progress.Completed_passes = m_pass_number; |
|||
m_cinfo.m_progress.Total_passes = m_total_passes; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Special start-of-pass hook.
|
|||
///
|
|||
/// This is called by jpeg_write_scanlines if call_pass_startup is true.
|
|||
/// In single-pass processing, we need this hook because we don't want to
|
|||
/// write frame/scan headers during jpeg_start_compress; we want to let the
|
|||
/// application write COM markers etc. between jpeg_start_compress and the
|
|||
/// jpeg_write_scanlines loop.
|
|||
/// In multi-pass processing, this routine is not used.
|
|||
/// </summary>
|
|||
public void pass_startup() |
|||
{ |
|||
m_cinfo.m_master.m_call_pass_startup = false; /* reset flag so call only once */ |
|||
|
|||
m_cinfo.m_marker.write_frame_header(); |
|||
m_cinfo.m_marker.write_scan_header(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Finish up at end of pass.
|
|||
/// </summary>
|
|||
public void finish_pass() |
|||
{ |
|||
/* The entropy coder always needs an end-of-pass call, |
|||
* either to analyze statistics or to flush its output buffer. |
|||
*/ |
|||
m_cinfo.m_entropy.finish_pass(); |
|||
|
|||
/* Update state for next pass */ |
|||
switch (m_pass_type) |
|||
{ |
|||
case c_pass_type.main_pass: |
|||
/* next pass is either output of scan 0 (after optimization) |
|||
* or output of scan 1 (if no optimization). |
|||
*/ |
|||
m_pass_type = c_pass_type.output_pass; |
|||
if (!m_cinfo.m_optimize_coding) |
|||
m_scan_number++; |
|||
break; |
|||
case c_pass_type.huff_opt_pass: |
|||
/* next pass is always output of current scan */ |
|||
m_pass_type = c_pass_type.output_pass; |
|||
break; |
|||
case c_pass_type.output_pass: |
|||
/* next pass is either optimization or output of next scan */ |
|||
if (m_cinfo.m_optimize_coding) |
|||
m_pass_type = c_pass_type.huff_opt_pass; |
|||
m_scan_number++; |
|||
break; |
|||
} |
|||
|
|||
m_pass_number++; |
|||
} |
|||
|
|||
public bool IsLastPass() |
|||
{ |
|||
return m_is_last_pass; |
|||
} |
|||
|
|||
public bool MustCallPassStartup() |
|||
{ |
|||
return m_call_pass_startup; |
|||
} |
|||
|
|||
private void prepare_for_main_pass() |
|||
{ |
|||
/* Initial pass: will collect input data, and do either Huffman |
|||
* optimization or data output for the first scan. |
|||
*/ |
|||
select_scan_parameters(); |
|||
per_scan_setup(); |
|||
|
|||
if (!m_cinfo.m_raw_data_in) |
|||
{ |
|||
m_cinfo.m_cconvert.start_pass(); |
|||
m_cinfo.m_prep.start_pass(J_BUF_MODE.JBUF_PASS_THRU); |
|||
} |
|||
|
|||
m_cinfo.m_fdct.start_pass(); |
|||
m_cinfo.m_entropy.start_pass(m_cinfo.m_optimize_coding); |
|||
m_cinfo.m_coef.start_pass((m_total_passes > 1 ? J_BUF_MODE.JBUF_SAVE_AND_PASS : J_BUF_MODE.JBUF_PASS_THRU)); |
|||
m_cinfo.m_main.start_pass(J_BUF_MODE.JBUF_PASS_THRU); |
|||
|
|||
if (m_cinfo.m_optimize_coding) |
|||
{ |
|||
/* No immediate data output; postpone writing frame/scan headers */ |
|||
m_call_pass_startup = false; |
|||
} |
|||
else |
|||
{ |
|||
/* Will write frame/scan headers at first jpeg_write_scanlines call */ |
|||
m_call_pass_startup = true; |
|||
} |
|||
} |
|||
|
|||
private bool prepare_for_huff_opt_pass() |
|||
{ |
|||
/* Do Huffman optimization for a scan after the first one. */ |
|||
select_scan_parameters(); |
|||
per_scan_setup(); |
|||
|
|||
if (m_cinfo.m_Ss != 0 || m_cinfo.m_Ah == 0) |
|||
{ |
|||
m_cinfo.m_entropy.start_pass(true); |
|||
m_cinfo.m_coef.start_pass(J_BUF_MODE.JBUF_CRANK_DEST); |
|||
m_call_pass_startup = false; |
|||
return false; |
|||
} |
|||
|
|||
/* Special case: Huffman DC refinement scans need no Huffman table |
|||
* and therefore we can skip the optimization pass for them. |
|||
*/ |
|||
m_pass_type = c_pass_type.output_pass; |
|||
m_pass_number++; |
|||
return true; |
|||
} |
|||
|
|||
private void prepare_for_output_pass() |
|||
{ |
|||
/* Do a data-output pass. */ |
|||
/* We need not repeat per-scan setup if prior optimization pass did it. */ |
|||
if (!m_cinfo.m_optimize_coding) |
|||
{ |
|||
select_scan_parameters(); |
|||
per_scan_setup(); |
|||
} |
|||
|
|||
m_cinfo.m_entropy.start_pass(false); |
|||
m_cinfo.m_coef.start_pass(J_BUF_MODE.JBUF_CRANK_DEST); |
|||
|
|||
/* We emit frame/scan headers now */ |
|||
if (m_scan_number == 0) |
|||
m_cinfo.m_marker.write_frame_header(); |
|||
|
|||
m_cinfo.m_marker.write_scan_header(); |
|||
m_call_pass_startup = false; |
|||
} |
|||
|
|||
// Set up the scan parameters for the current scan
|
|||
private void select_scan_parameters() |
|||
{ |
|||
if (m_cinfo.m_scan_info != null) |
|||
{ |
|||
/* Prepare for current scan --- the script is already validated */ |
|||
jpeg_scan_info scanInfo = m_cinfo.m_scan_info[m_scan_number]; |
|||
|
|||
m_cinfo.m_comps_in_scan = scanInfo.comps_in_scan; |
|||
for (int ci = 0; ci < scanInfo.comps_in_scan; ci++) |
|||
m_cinfo.m_cur_comp_info[ci] = scanInfo.component_index[ci]; |
|||
|
|||
m_cinfo.m_Ss = scanInfo.Ss; |
|||
m_cinfo.m_Se = scanInfo.Se; |
|||
m_cinfo.m_Ah = scanInfo.Ah; |
|||
m_cinfo.m_Al = scanInfo.Al; |
|||
} |
|||
else |
|||
{ |
|||
/* Prepare for single sequential-JPEG scan containing all components */ |
|||
if (m_cinfo.m_num_components > JpegConstants.MAX_COMPS_IN_SCAN) |
|||
m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_COMPONENT_COUNT, m_cinfo.m_num_components, JpegConstants.MAX_COMPS_IN_SCAN); |
|||
|
|||
m_cinfo.m_comps_in_scan = m_cinfo.m_num_components; |
|||
for (int ci = 0; ci < m_cinfo.m_num_components; ci++) |
|||
m_cinfo.m_cur_comp_info[ci] = ci; |
|||
|
|||
m_cinfo.m_Ss = 0; |
|||
m_cinfo.m_Se = JpegConstants.DCTSIZE2 - 1; |
|||
m_cinfo.m_Ah = 0; |
|||
m_cinfo.m_Al = 0; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Do computations that are needed before processing a JPEG scan
|
|||
/// cinfo.comps_in_scan and cinfo.cur_comp_info[] are already set
|
|||
/// </summary>
|
|||
private void per_scan_setup() |
|||
{ |
|||
if (m_cinfo.m_comps_in_scan == 1) |
|||
{ |
|||
/* Noninterleaved (single-component) scan */ |
|||
int compIndex = m_cinfo.m_cur_comp_info[0]; |
|||
|
|||
/* Overall image size in MCUs */ |
|||
m_cinfo.m_MCUs_per_row = m_cinfo.Component_info[compIndex].Width_in_blocks; |
|||
m_cinfo.m_MCU_rows_in_scan = m_cinfo.Component_info[compIndex].height_in_blocks; |
|||
|
|||
/* For noninterleaved scan, always one block per MCU */ |
|||
m_cinfo.Component_info[compIndex].MCU_width = 1; |
|||
m_cinfo.Component_info[compIndex].MCU_height = 1; |
|||
m_cinfo.Component_info[compIndex].MCU_blocks = 1; |
|||
m_cinfo.Component_info[compIndex].MCU_sample_width = JpegConstants.DCTSIZE; |
|||
m_cinfo.Component_info[compIndex].last_col_width = 1; |
|||
|
|||
/* For noninterleaved scans, it is convenient to define last_row_height |
|||
* as the number of block rows present in the last iMCU row. |
|||
*/ |
|||
int tmp = m_cinfo.Component_info[compIndex].height_in_blocks % m_cinfo.Component_info[compIndex].V_samp_factor; |
|||
if (tmp == 0) |
|||
tmp = m_cinfo.Component_info[compIndex].V_samp_factor; |
|||
m_cinfo.Component_info[compIndex].last_row_height = tmp; |
|||
|
|||
/* Prepare array describing MCU composition */ |
|||
m_cinfo.m_blocks_in_MCU = 1; |
|||
m_cinfo.m_MCU_membership[0] = 0; |
|||
} |
|||
else |
|||
{ |
|||
/* Interleaved (multi-component) scan */ |
|||
if (m_cinfo.m_comps_in_scan <= 0 || m_cinfo.m_comps_in_scan > JpegConstants.MAX_COMPS_IN_SCAN) |
|||
m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_COMPONENT_COUNT, m_cinfo.m_comps_in_scan, JpegConstants.MAX_COMPS_IN_SCAN); |
|||
|
|||
/* Overall image size in MCUs */ |
|||
m_cinfo.m_MCUs_per_row = JpegUtils.jdiv_round_up( |
|||
m_cinfo.m_image_width, m_cinfo.m_max_h_samp_factor * JpegConstants.DCTSIZE); |
|||
|
|||
m_cinfo.m_MCU_rows_in_scan = JpegUtils.jdiv_round_up(m_cinfo.m_image_height, |
|||
m_cinfo.m_max_v_samp_factor * JpegConstants.DCTSIZE); |
|||
|
|||
m_cinfo.m_blocks_in_MCU = 0; |
|||
|
|||
for (int ci = 0; ci < m_cinfo.m_comps_in_scan; ci++) |
|||
{ |
|||
int compIndex = m_cinfo.m_cur_comp_info[ci]; |
|||
|
|||
/* Sampling factors give # of blocks of component in each MCU */ |
|||
m_cinfo.Component_info[compIndex].MCU_width = m_cinfo.Component_info[compIndex].H_samp_factor; |
|||
m_cinfo.Component_info[compIndex].MCU_height = m_cinfo.Component_info[compIndex].V_samp_factor; |
|||
m_cinfo.Component_info[compIndex].MCU_blocks = m_cinfo.Component_info[compIndex].MCU_width * m_cinfo.Component_info[compIndex].MCU_height; |
|||
m_cinfo.Component_info[compIndex].MCU_sample_width = m_cinfo.Component_info[compIndex].MCU_width * JpegConstants.DCTSIZE; |
|||
|
|||
/* Figure number of non-dummy blocks in last MCU column & row */ |
|||
int tmp = m_cinfo.Component_info[compIndex].Width_in_blocks % m_cinfo.Component_info[compIndex].MCU_width; |
|||
if (tmp == 0) |
|||
tmp = m_cinfo.Component_info[compIndex].MCU_width; |
|||
m_cinfo.Component_info[compIndex].last_col_width = tmp; |
|||
|
|||
tmp = m_cinfo.Component_info[compIndex].height_in_blocks % m_cinfo.Component_info[compIndex].MCU_height; |
|||
if (tmp == 0) |
|||
tmp = m_cinfo.Component_info[compIndex].MCU_height; |
|||
m_cinfo.Component_info[compIndex].last_row_height = tmp; |
|||
|
|||
/* Prepare array describing MCU composition */ |
|||
int mcublks = m_cinfo.Component_info[compIndex].MCU_blocks; |
|||
if (m_cinfo.m_blocks_in_MCU + mcublks > JpegConstants.C_MAX_BLOCKS_IN_MCU) |
|||
m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_MCU_SIZE); |
|||
|
|||
while (mcublks-- > 0) |
|||
m_cinfo.m_MCU_membership[m_cinfo.m_blocks_in_MCU++] = ci; |
|||
} |
|||
} |
|||
|
|||
/* Convert restart specified in rows to actual MCU count. */ |
|||
/* Note that count must fit in 16 bits, so we provide limiting. */ |
|||
if (m_cinfo.m_restart_in_rows > 0) |
|||
{ |
|||
int nominal = m_cinfo.m_restart_in_rows * m_cinfo.m_MCUs_per_row; |
|||
m_cinfo.m_restart_interval = Math.Min(nominal, 65535); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,761 @@ |
|||
/* Copyright (C) 2008-2011, Bit Miracle |
|||
* http://www.bitmiracle.com
|
|||
* |
|||
* Copyright (C) 1994-1996, Thomas G. Lane. |
|||
* This file is part of the Independent JPEG Group's software. |
|||
* For conditions of distribution and use, see the accompanying README file. |
|||
* |
|||
*/ |
|||
|
|||
/* |
|||
* This file contains the coefficient buffer controller for decompression. |
|||
* This controller is the top level of the JPEG decompressor proper. |
|||
* The coefficient buffer lies between entropy decoding and inverse-DCT steps. |
|||
* |
|||
* In buffered-image mode, this controller is the interface between |
|||
* input-oriented processing and output-oriented processing. |
|||
* Also, the input side (only) is used when reading a file for transcoding. |
|||
*/ |
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
|
|||
namespace BitMiracle.LibJpeg.Classic.Internal |
|||
{ |
|||
/// <summary>
|
|||
/// Coefficient buffer control
|
|||
///
|
|||
/// This code applies interblock smoothing as described by section K.8
|
|||
/// of the JPEG standard: the first 5 AC coefficients are estimated from
|
|||
/// the DC values of a DCT block and its 8 neighboring blocks.
|
|||
/// We apply smoothing only for progressive JPEG decoding, and only if
|
|||
/// the coefficients it can estimate are not yet known to full precision.
|
|||
/// </summary>
|
|||
class jpeg_d_coef_controller |
|||
{ |
|||
private const int SAVED_COEFS = 6; /* we save coef_bits[0..5] */ |
|||
|
|||
/* Natural-order array positions of the first 5 zigzag-order coefficients */ |
|||
private const int Q01_POS = 1; |
|||
private const int Q10_POS = 8; |
|||
private const int Q20_POS = 16; |
|||
private const int Q11_POS = 9; |
|||
private const int Q02_POS = 2; |
|||
|
|||
private enum DecompressorType |
|||
{ |
|||
Ordinary, |
|||
Smooth, |
|||
OnePass |
|||
} |
|||
|
|||
private jpeg_decompress_struct m_cinfo; |
|||
private bool m_useDummyConsumeData; |
|||
private DecompressorType m_decompressor; |
|||
|
|||
/* These variables keep track of the current location of the input side. */ |
|||
/* cinfo.input_iMCU_row is also used for this. */ |
|||
private int m_MCU_ctr; /* counts MCUs processed in current row */ |
|||
private int m_MCU_vert_offset; /* counts MCU rows within iMCU row */ |
|||
private int m_MCU_rows_per_iMCU_row; /* number of such rows needed */ |
|||
|
|||
/* The output side's location is represented by cinfo.output_iMCU_row. */ |
|||
|
|||
/* In single-pass modes, it's sufficient to buffer just one MCU. |
|||
* We allocate a workspace of D_MAX_BLOCKS_IN_MCU coefficient blocks, |
|||
* and let the entropy decoder write into that workspace each time. |
|||
* (On 80x86, the workspace is FAR even though it's not really very big; |
|||
* this is to keep the module interfaces unchanged when a large coefficient |
|||
* buffer is necessary.) |
|||
* In multi-pass modes, this array points to the current MCU's blocks |
|||
* within the virtual arrays; it is used only by the input side. |
|||
*/ |
|||
private JBLOCK[] m_MCU_buffer = new JBLOCK[JpegConstants.D_MAX_BLOCKS_IN_MCU]; |
|||
|
|||
/* In multi-pass modes, we need a virtual block array for each component. */ |
|||
private jvirt_array<JBLOCK>[] m_whole_image = new jvirt_array<JBLOCK>[JpegConstants.MAX_COMPONENTS]; |
|||
private jvirt_array<JBLOCK>[] m_coef_arrays; |
|||
|
|||
/* When doing block smoothing, we latch coefficient Al values here */ |
|||
private int[] m_coef_bits_latch; |
|||
private int m_coef_bits_savedOffset; |
|||
|
|||
public jpeg_d_coef_controller(jpeg_decompress_struct cinfo, bool need_full_buffer) |
|||
{ |
|||
m_cinfo = cinfo; |
|||
|
|||
/* Create the coefficient buffer. */ |
|||
if (need_full_buffer) |
|||
{ |
|||
/* Allocate a full-image virtual array for each component, */ |
|||
/* padded to a multiple of samp_factor DCT blocks in each direction. */ |
|||
/* Note we ask for a pre-zeroed array. */ |
|||
for (int ci = 0; ci < cinfo.m_num_components; ci++) |
|||
{ |
|||
m_whole_image[ci] = jpeg_common_struct.CreateBlocksArray( |
|||
JpegUtils.jround_up(cinfo.Comp_info[ci].Width_in_blocks, cinfo.Comp_info[ci].H_samp_factor), |
|||
JpegUtils.jround_up(cinfo.Comp_info[ci].height_in_blocks, cinfo.Comp_info[ci].V_samp_factor)); |
|||
m_whole_image[ci].ErrorProcessor = cinfo; |
|||
} |
|||
|
|||
m_useDummyConsumeData = false; |
|||
m_decompressor = DecompressorType.Ordinary; |
|||
m_coef_arrays = m_whole_image; /* link to virtual arrays */ |
|||
} |
|||
else |
|||
{ |
|||
/* We only need a single-MCU buffer. */ |
|||
JBLOCK[] buffer = new JBLOCK[JpegConstants.D_MAX_BLOCKS_IN_MCU]; |
|||
for (int i = 0; i < JpegConstants.D_MAX_BLOCKS_IN_MCU; i++) |
|||
{ |
|||
buffer[i] = new JBLOCK(); |
|||
for (int ii = 0; ii < buffer[i].data.Length; ii++) |
|||
buffer[i].data[ii] = -12851; |
|||
|
|||
m_MCU_buffer[i] = buffer[i]; |
|||
} |
|||
|
|||
m_useDummyConsumeData = true; |
|||
m_decompressor = DecompressorType.OnePass; |
|||
m_coef_arrays = null; /* flag for no virtual arrays */ |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initialize for an input processing pass.
|
|||
/// </summary>
|
|||
public void start_input_pass() |
|||
{ |
|||
m_cinfo.m_input_iMCU_row = 0; |
|||
start_iMCU_row(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Consume input data and store it in the full-image coefficient buffer.
|
|||
/// We read as much as one fully interleaved MCU row ("iMCU" row) per call,
|
|||
/// ie, v_samp_factor block rows for each component in the scan.
|
|||
/// </summary>
|
|||
public ReadResult consume_data() |
|||
{ |
|||
if (m_useDummyConsumeData) |
|||
return ReadResult.JPEG_SUSPENDED; /* Always indicate nothing was done */ |
|||
|
|||
JBLOCK[][][] buffer = new JBLOCK[JpegConstants.MAX_COMPS_IN_SCAN][][]; |
|||
|
|||
/* Align the virtual buffers for the components used in this scan. */ |
|||
for (int ci = 0; ci < m_cinfo.m_comps_in_scan; ci++) |
|||
{ |
|||
jpeg_component_info componentInfo = m_cinfo.Comp_info[m_cinfo.m_cur_comp_info[ci]]; |
|||
|
|||
buffer[ci] = m_whole_image[componentInfo.Component_index].Access( |
|||
m_cinfo.m_input_iMCU_row * componentInfo.V_samp_factor, componentInfo.V_samp_factor); |
|||
|
|||
/* Note: entropy decoder expects buffer to be zeroed, |
|||
* but this is handled automatically by the memory manager |
|||
* because we requested a pre-zeroed array. |
|||
*/ |
|||
} |
|||
|
|||
/* Loop to process one whole iMCU row */ |
|||
for (int yoffset = m_MCU_vert_offset; yoffset < m_MCU_rows_per_iMCU_row; yoffset++) |
|||
{ |
|||
for (int MCU_col_num = m_MCU_ctr; MCU_col_num < m_cinfo.m_MCUs_per_row; MCU_col_num++) |
|||
{ |
|||
/* Construct list of pointers to DCT blocks belonging to this MCU */ |
|||
int blkn = 0; /* index of current DCT block within MCU */ |
|||
for (int ci = 0; ci < m_cinfo.m_comps_in_scan; ci++) |
|||
{ |
|||
jpeg_component_info componentInfo = m_cinfo.Comp_info[m_cinfo.m_cur_comp_info[ci]]; |
|||
int start_col = MCU_col_num * componentInfo.MCU_width; |
|||
for (int yindex = 0; yindex < componentInfo.MCU_height; yindex++) |
|||
{ |
|||
for (int xindex = 0; xindex < componentInfo.MCU_width; xindex++) |
|||
{ |
|||
m_MCU_buffer[blkn] = buffer[ci][yindex + yoffset][start_col + xindex]; |
|||
blkn++; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/* Try to fetch the MCU. */ |
|||
if (!m_cinfo.m_entropy.decode_mcu(m_MCU_buffer)) |
|||
{ |
|||
/* Suspension forced; update state counters and exit */ |
|||
m_MCU_vert_offset = yoffset; |
|||
m_MCU_ctr = MCU_col_num; |
|||
return ReadResult.JPEG_SUSPENDED; |
|||
} |
|||
} |
|||
|
|||
/* Completed an MCU row, but perhaps not an iMCU row */ |
|||
m_MCU_ctr = 0; |
|||
} |
|||
|
|||
/* Completed the iMCU row, advance counters for next one */ |
|||
m_cinfo.m_input_iMCU_row++; |
|||
if (m_cinfo.m_input_iMCU_row < m_cinfo.m_total_iMCU_rows) |
|||
{ |
|||
start_iMCU_row(); |
|||
return ReadResult.JPEG_ROW_COMPLETED; |
|||
} |
|||
|
|||
/* Completed the scan */ |
|||
m_cinfo.m_inputctl.finish_input_pass(); |
|||
return ReadResult.JPEG_SCAN_COMPLETED; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initialize for an output processing pass.
|
|||
/// </summary>
|
|||
public void start_output_pass() |
|||
{ |
|||
/* If multipass, check to see whether to use block smoothing on this pass */ |
|||
if (m_coef_arrays != null) |
|||
{ |
|||
if (m_cinfo.m_do_block_smoothing && smoothing_ok()) |
|||
m_decompressor = DecompressorType.Smooth; |
|||
else |
|||
m_decompressor = DecompressorType.Ordinary; |
|||
} |
|||
|
|||
m_cinfo.m_output_iMCU_row = 0; |
|||
} |
|||
|
|||
public ReadResult decompress_data(ComponentBuffer[] output_buf) |
|||
{ |
|||
switch (m_decompressor) |
|||
{ |
|||
case DecompressorType.Ordinary: |
|||
return decompress_data_ordinary(output_buf); |
|||
|
|||
case DecompressorType.Smooth: |
|||
return decompress_smooth_data(output_buf); |
|||
|
|||
case DecompressorType.OnePass: |
|||
return decompress_onepass(output_buf); |
|||
} |
|||
|
|||
m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_NOTIMPL); |
|||
return 0; |
|||
} |
|||
|
|||
/* Pointer to array of coefficient virtual arrays, or null if none */ |
|||
public jvirt_array<JBLOCK>[] GetCoefArrays() |
|||
{ |
|||
return m_coef_arrays; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Decompress and return some data in the single-pass case.
|
|||
/// Always attempts to emit one fully interleaved MCU row ("iMCU" row).
|
|||
/// Input and output must run in lockstep since we have only a one-MCU buffer.
|
|||
/// Return value is JPEG_ROW_COMPLETED, JPEG_SCAN_COMPLETED, or JPEG_SUSPENDED.
|
|||
///
|
|||
/// NB: output_buf contains a plane for each component in image,
|
|||
/// which we index according to the component's SOF position.
|
|||
/// </summary>
|
|||
private ReadResult decompress_onepass(ComponentBuffer[] output_buf) |
|||
{ |
|||
int last_MCU_col = m_cinfo.m_MCUs_per_row - 1; |
|||
int last_iMCU_row = m_cinfo.m_total_iMCU_rows - 1; |
|||
|
|||
/* Loop to process as much as one whole iMCU row */ |
|||
for (int yoffset = m_MCU_vert_offset; yoffset < m_MCU_rows_per_iMCU_row; yoffset++) |
|||
{ |
|||
for (int MCU_col_num = m_MCU_ctr; MCU_col_num <= last_MCU_col; MCU_col_num++) |
|||
{ |
|||
/* Try to fetch an MCU. Entropy decoder expects buffer to be zeroed. */ |
|||
for (int i = 0; i < m_cinfo.m_blocks_in_MCU; i++) |
|||
Array.Clear(m_MCU_buffer[i].data, 0, m_MCU_buffer[i].data.Length); |
|||
|
|||
if (!m_cinfo.m_entropy.decode_mcu(m_MCU_buffer)) |
|||
{ |
|||
/* Suspension forced; update state counters and exit */ |
|||
m_MCU_vert_offset = yoffset; |
|||
m_MCU_ctr = MCU_col_num; |
|||
return ReadResult.JPEG_SUSPENDED; |
|||
} |
|||
|
|||
/* Determine where data should go in output_buf and do the IDCT thing. |
|||
* We skip dummy blocks at the right and bottom edges (but blkn gets |
|||
* incremented past them!). Note the inner loop relies on having |
|||
* allocated the MCU_buffer[] blocks sequentially. |
|||
*/ |
|||
int blkn = 0; /* index of current DCT block within MCU */ |
|||
for (int ci = 0; ci < m_cinfo.m_comps_in_scan; ci++) |
|||
{ |
|||
jpeg_component_info componentInfo = m_cinfo.Comp_info[m_cinfo.m_cur_comp_info[ci]]; |
|||
|
|||
/* Don't bother to IDCT an uninteresting component. */ |
|||
if (!componentInfo.component_needed) |
|||
{ |
|||
blkn += componentInfo.MCU_blocks; |
|||
continue; |
|||
} |
|||
|
|||
int useful_width = (MCU_col_num < last_MCU_col) ? componentInfo.MCU_width : componentInfo.last_col_width; |
|||
int outputIndex = yoffset * componentInfo.DCT_scaled_size; |
|||
int start_col = MCU_col_num * componentInfo.MCU_sample_width; |
|||
for (int yindex = 0; yindex < componentInfo.MCU_height; yindex++) |
|||
{ |
|||
if (m_cinfo.m_input_iMCU_row < last_iMCU_row || yoffset + yindex < componentInfo.last_row_height) |
|||
{ |
|||
int output_col = start_col; |
|||
for (int xindex = 0; xindex < useful_width; xindex++) |
|||
{ |
|||
m_cinfo.m_idct.inverse(componentInfo.Component_index, |
|||
m_MCU_buffer[blkn + xindex].data, output_buf[componentInfo.Component_index], |
|||
outputIndex, output_col); |
|||
|
|||
output_col += componentInfo.DCT_scaled_size; |
|||
} |
|||
} |
|||
|
|||
blkn += componentInfo.MCU_width; |
|||
outputIndex += componentInfo.DCT_scaled_size; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/* Completed an MCU row, but perhaps not an iMCU row */ |
|||
m_MCU_ctr = 0; |
|||
} |
|||
|
|||
/* Completed the iMCU row, advance counters for next one */ |
|||
m_cinfo.m_output_iMCU_row++; |
|||
m_cinfo.m_input_iMCU_row++; |
|||
if (m_cinfo.m_input_iMCU_row < m_cinfo.m_total_iMCU_rows) |
|||
{ |
|||
start_iMCU_row(); |
|||
return ReadResult.JPEG_ROW_COMPLETED; |
|||
} |
|||
|
|||
/* Completed the scan */ |
|||
m_cinfo.m_inputctl.finish_input_pass(); |
|||
return ReadResult.JPEG_SCAN_COMPLETED; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Decompress and return some data in the multi-pass case.
|
|||
/// Always attempts to emit one fully interleaved MCU row ("iMCU" row).
|
|||
/// Return value is JPEG_ROW_COMPLETED, JPEG_SCAN_COMPLETED, or JPEG_SUSPENDED.
|
|||
///
|
|||
/// NB: output_buf contains a plane for each component in image.
|
|||
/// </summary>
|
|||
private ReadResult decompress_data_ordinary(ComponentBuffer[] output_buf) |
|||
{ |
|||
/* Force some input to be done if we are getting ahead of the input. */ |
|||
while (m_cinfo.m_input_scan_number < m_cinfo.m_output_scan_number || |
|||
(m_cinfo.m_input_scan_number == m_cinfo.m_output_scan_number && |
|||
m_cinfo.m_input_iMCU_row <= m_cinfo.m_output_iMCU_row)) |
|||
{ |
|||
if (m_cinfo.m_inputctl.consume_input() == ReadResult.JPEG_SUSPENDED) |
|||
return ReadResult.JPEG_SUSPENDED; |
|||
} |
|||
|
|||
int last_iMCU_row = m_cinfo.m_total_iMCU_rows - 1; |
|||
|
|||
/* OK, output from the virtual arrays. */ |
|||
for (int ci = 0; ci < m_cinfo.m_num_components; ci++) |
|||
{ |
|||
jpeg_component_info componentInfo = m_cinfo.Comp_info[ci]; |
|||
|
|||
/* Don't bother to IDCT an uninteresting component. */ |
|||
if (!componentInfo.component_needed) |
|||
continue; |
|||
|
|||
/* Align the virtual buffer for this component. */ |
|||
JBLOCK[][] buffer = m_whole_image[ci].Access(m_cinfo.m_output_iMCU_row * componentInfo.V_samp_factor, |
|||
componentInfo.V_samp_factor); |
|||
|
|||
/* Count non-dummy DCT block rows in this iMCU row. */ |
|||
int block_rows; |
|||
if (m_cinfo.m_output_iMCU_row < last_iMCU_row) |
|||
block_rows = componentInfo.V_samp_factor; |
|||
else |
|||
{ |
|||
/* NB: can't use last_row_height here; it is input-side-dependent! */ |
|||
block_rows = componentInfo.height_in_blocks % componentInfo.V_samp_factor; |
|||
if (block_rows == 0) |
|||
block_rows = componentInfo.V_samp_factor; |
|||
} |
|||
|
|||
/* Loop over all DCT blocks to be processed. */ |
|||
int rowIndex = 0; |
|||
for (int block_row = 0; block_row < block_rows; block_row++) |
|||
{ |
|||
int output_col = 0; |
|||
for (int block_num = 0; block_num < componentInfo.Width_in_blocks; block_num++) |
|||
{ |
|||
m_cinfo.m_idct.inverse(componentInfo.Component_index, |
|||
buffer[block_row][block_num].data, output_buf[ci], rowIndex, output_col); |
|||
|
|||
output_col += componentInfo.DCT_scaled_size; |
|||
} |
|||
|
|||
rowIndex += componentInfo.DCT_scaled_size; |
|||
} |
|||
} |
|||
|
|||
m_cinfo.m_output_iMCU_row++; |
|||
if (m_cinfo.m_output_iMCU_row < m_cinfo.m_total_iMCU_rows) |
|||
return ReadResult.JPEG_ROW_COMPLETED; |
|||
|
|||
return ReadResult.JPEG_SCAN_COMPLETED; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Variant of decompress_data for use when doing block smoothing.
|
|||
/// </summary>
|
|||
private ReadResult decompress_smooth_data(ComponentBuffer[] output_buf) |
|||
{ |
|||
/* Force some input to be done if we are getting ahead of the input. */ |
|||
while (m_cinfo.m_input_scan_number <= m_cinfo.m_output_scan_number && !m_cinfo.m_inputctl.EOIReached()) |
|||
{ |
|||
if (m_cinfo.m_input_scan_number == m_cinfo.m_output_scan_number) |
|||
{ |
|||
/* If input is working on current scan, we ordinarily want it to |
|||
* have completed the current row. But if input scan is DC, |
|||
* we want it to keep one row ahead so that next block row's DC |
|||
* values are up to date. |
|||
*/ |
|||
int delta = (m_cinfo.m_Ss == 0) ? 1 : 0; |
|||
if (m_cinfo.m_input_iMCU_row > m_cinfo.m_output_iMCU_row + delta) |
|||
break; |
|||
} |
|||
|
|||
if (m_cinfo.m_inputctl.consume_input() == ReadResult.JPEG_SUSPENDED) |
|||
return ReadResult.JPEG_SUSPENDED; |
|||
} |
|||
|
|||
int last_iMCU_row = m_cinfo.m_total_iMCU_rows - 1; |
|||
|
|||
/* OK, output from the virtual arrays. */ |
|||
for (int ci = 0; ci < m_cinfo.m_num_components; ci++) |
|||
{ |
|||
jpeg_component_info componentInfo = m_cinfo.Comp_info[ci]; |
|||
|
|||
/* Don't bother to IDCT an uninteresting component. */ |
|||
if (!componentInfo.component_needed) |
|||
continue; |
|||
|
|||
int block_rows; |
|||
int access_rows; |
|||
bool last_row; |
|||
/* Count non-dummy DCT block rows in this iMCU row. */ |
|||
if (m_cinfo.m_output_iMCU_row < last_iMCU_row) |
|||
{ |
|||
block_rows = componentInfo.V_samp_factor; |
|||
access_rows = block_rows * 2; /* this and next iMCU row */ |
|||
last_row = false; |
|||
} |
|||
else |
|||
{ |
|||
/* NB: can't use last_row_height here; it is input-side-dependent! */ |
|||
block_rows = componentInfo.height_in_blocks % componentInfo.V_samp_factor; |
|||
if (block_rows == 0) |
|||
block_rows = componentInfo.V_samp_factor; |
|||
access_rows = block_rows; /* this iMCU row only */ |
|||
last_row = true; |
|||
} |
|||
|
|||
/* Align the virtual buffer for this component. */ |
|||
JBLOCK[][] buffer = null; |
|||
bool first_row; |
|||
int bufferRowOffset = 0; |
|||
if (m_cinfo.m_output_iMCU_row > 0) |
|||
{ |
|||
access_rows += componentInfo.V_samp_factor; /* prior iMCU row too */ |
|||
buffer = m_whole_image[ci].Access((m_cinfo.m_output_iMCU_row - 1) * componentInfo.V_samp_factor, access_rows); |
|||
bufferRowOffset = componentInfo.V_samp_factor; /* point to current iMCU row */ |
|||
first_row = false; |
|||
} |
|||
else |
|||
{ |
|||
buffer = m_whole_image[ci].Access(0, access_rows); |
|||
first_row = true; |
|||
} |
|||
|
|||
/* Fetch component-dependent info */ |
|||
int coefBitsOffset = ci * SAVED_COEFS; |
|||
int Q00 = componentInfo.quant_table.quantval[0]; |
|||
int Q01 = componentInfo.quant_table.quantval[Q01_POS]; |
|||
int Q10 = componentInfo.quant_table.quantval[Q10_POS]; |
|||
int Q20 = componentInfo.quant_table.quantval[Q20_POS]; |
|||
int Q11 = componentInfo.quant_table.quantval[Q11_POS]; |
|||
int Q02 = componentInfo.quant_table.quantval[Q02_POS]; |
|||
int outputIndex = ci; |
|||
|
|||
/* Loop over all DCT blocks to be processed. */ |
|||
for (int block_row = 0; block_row < block_rows; block_row++) |
|||
{ |
|||
int bufferIndex = bufferRowOffset + block_row; |
|||
|
|||
int prev_block_row = 0; |
|||
if (first_row && block_row == 0) |
|||
prev_block_row = bufferIndex; |
|||
else |
|||
prev_block_row = bufferIndex - 1; |
|||
|
|||
int next_block_row = 0; |
|||
if (last_row && block_row == block_rows - 1) |
|||
next_block_row = bufferIndex; |
|||
else |
|||
next_block_row = bufferIndex + 1; |
|||
|
|||
/* We fetch the surrounding DC values using a sliding-register approach. |
|||
* Initialize all nine here so as to do the right thing on narrow pics. |
|||
*/ |
|||
int DC1 = buffer[prev_block_row][0][0]; |
|||
int DC2 = DC1; |
|||
int DC3 = DC1; |
|||
|
|||
int DC4 = buffer[bufferIndex][0][0]; |
|||
int DC5 = DC4; |
|||
int DC6 = DC4; |
|||
|
|||
int DC7 = buffer[next_block_row][0][0]; |
|||
int DC8 = DC7; |
|||
int DC9 = DC7; |
|||
|
|||
int output_col = 0; |
|||
int last_block_column = componentInfo.Width_in_blocks - 1; |
|||
for (int block_num = 0; block_num <= last_block_column; block_num++) |
|||
{ |
|||
/* Fetch current DCT block into workspace so we can modify it. */ |
|||
JBLOCK workspace = new JBLOCK(); |
|||
Buffer.BlockCopy(buffer[bufferIndex][0].data, 0, workspace.data, 0, workspace.data.Length * sizeof(short)); |
|||
|
|||
/* Update DC values */ |
|||
if (block_num < last_block_column) |
|||
{ |
|||
DC3 = buffer[prev_block_row][1][0]; |
|||
DC6 = buffer[bufferIndex][1][0]; |
|||
DC9 = buffer[next_block_row][1][0]; |
|||
} |
|||
|
|||
/* Compute coefficient estimates per K.8. |
|||
* An estimate is applied only if coefficient is still zero, |
|||
* and is not known to be fully accurate. |
|||
*/ |
|||
/* AC01 */ |
|||
int Al = m_coef_bits_latch[m_coef_bits_savedOffset + coefBitsOffset + 1]; |
|||
if (Al != 0 && workspace[1] == 0) |
|||
{ |
|||
int pred; |
|||
int num = 36 * Q00 * (DC4 - DC6); |
|||
if (num >= 0) |
|||
{ |
|||
pred = ((Q01 << 7) + num) / (Q01 << 8); |
|||
if (Al > 0 && pred >= (1 << Al)) |
|||
pred = (1 << Al) - 1; |
|||
} |
|||
else |
|||
{ |
|||
pred = ((Q01 << 7) - num) / (Q01 << 8); |
|||
if (Al > 0 && pred >= (1 << Al)) |
|||
pred = (1 << Al) - 1; |
|||
pred = -pred; |
|||
} |
|||
workspace[1] = (short) pred; |
|||
} |
|||
|
|||
/* AC10 */ |
|||
Al = m_coef_bits_latch[m_coef_bits_savedOffset + coefBitsOffset + 2]; |
|||
if (Al != 0 && workspace[8] == 0) |
|||
{ |
|||
int pred; |
|||
int num = 36 * Q00 * (DC2 - DC8); |
|||
if (num >= 0) |
|||
{ |
|||
pred = ((Q10 << 7) + num) / (Q10 << 8); |
|||
if (Al > 0 && pred >= (1 << Al)) |
|||
pred = (1 << Al) - 1; |
|||
} |
|||
else |
|||
{ |
|||
pred = ((Q10 << 7) - num) / (Q10 << 8); |
|||
if (Al > 0 && pred >= (1 << Al)) |
|||
pred = (1 << Al) - 1; |
|||
pred = -pred; |
|||
} |
|||
workspace[8] = (short) pred; |
|||
} |
|||
|
|||
/* AC20 */ |
|||
Al = m_coef_bits_latch[m_coef_bits_savedOffset + coefBitsOffset + 3]; |
|||
if (Al != 0 && workspace[16] == 0) |
|||
{ |
|||
int pred; |
|||
int num = 9 * Q00 * (DC2 + DC8 - 2 * DC5); |
|||
if (num >= 0) |
|||
{ |
|||
pred = ((Q20 << 7) + num) / (Q20 << 8); |
|||
if (Al > 0 && pred >= (1 << Al)) |
|||
pred = (1 << Al) - 1; |
|||
} |
|||
else |
|||
{ |
|||
pred = ((Q20 << 7) - num) / (Q20 << 8); |
|||
if (Al > 0 && pred >= (1 << Al)) |
|||
pred = (1 << Al) - 1; |
|||
pred = -pred; |
|||
} |
|||
workspace[16] = (short) pred; |
|||
} |
|||
|
|||
/* AC11 */ |
|||
Al = m_coef_bits_latch[m_coef_bits_savedOffset + coefBitsOffset + 4]; |
|||
if (Al != 0 && workspace[9] == 0) |
|||
{ |
|||
int pred; |
|||
int num = 5 * Q00 * (DC1 - DC3 - DC7 + DC9); |
|||
if (num >= 0) |
|||
{ |
|||
pred = ((Q11 << 7) + num) / (Q11 << 8); |
|||
if (Al > 0 && pred >= (1 << Al)) |
|||
pred = (1 << Al) - 1; |
|||
} |
|||
else |
|||
{ |
|||
pred = ((Q11 << 7) - num) / (Q11 << 8); |
|||
if (Al > 0 && pred >= (1 << Al)) |
|||
pred = (1 << Al) - 1; |
|||
pred = -pred; |
|||
} |
|||
workspace[9] = (short) pred; |
|||
} |
|||
|
|||
/* AC02 */ |
|||
Al = m_coef_bits_latch[m_coef_bits_savedOffset + coefBitsOffset + 5]; |
|||
if (Al != 0 && workspace[2] == 0) |
|||
{ |
|||
int pred; |
|||
int num = 9 * Q00 * (DC4 + DC6 - 2 * DC5); |
|||
if (num >= 0) |
|||
{ |
|||
pred = ((Q02 << 7) + num) / (Q02 << 8); |
|||
if (Al > 0 && pred >= (1 << Al)) |
|||
pred = (1 << Al) - 1; |
|||
} |
|||
else |
|||
{ |
|||
pred = ((Q02 << 7) - num) / (Q02 << 8); |
|||
if (Al > 0 && pred >= (1 << Al)) |
|||
pred = (1 << Al) - 1; |
|||
pred = -pred; |
|||
} |
|||
workspace[2] = (short) pred; |
|||
} |
|||
|
|||
/* OK, do the IDCT */ |
|||
m_cinfo.m_idct.inverse(componentInfo.Component_index, workspace.data, output_buf[outputIndex], 0, output_col); |
|||
|
|||
/* Advance for next column */ |
|||
DC1 = DC2; |
|||
DC2 = DC3; |
|||
DC4 = DC5; |
|||
DC5 = DC6; |
|||
DC7 = DC8; |
|||
DC8 = DC9; |
|||
|
|||
bufferIndex++; |
|||
prev_block_row++; |
|||
next_block_row++; |
|||
|
|||
output_col += componentInfo.DCT_scaled_size; |
|||
} |
|||
|
|||
outputIndex += componentInfo.DCT_scaled_size; |
|||
} |
|||
} |
|||
|
|||
m_cinfo.m_output_iMCU_row++; |
|||
if (m_cinfo.m_output_iMCU_row < m_cinfo.m_total_iMCU_rows) |
|||
return ReadResult.JPEG_ROW_COMPLETED; |
|||
|
|||
return ReadResult.JPEG_SCAN_COMPLETED; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Determine whether block smoothing is applicable and safe.
|
|||
/// We also latch the current states of the coef_bits[] entries for the
|
|||
/// AC coefficients; otherwise, if the input side of the decompressor
|
|||
/// advances into a new scan, we might think the coefficients are known
|
|||
/// more accurately than they really are.
|
|||
/// </summary>
|
|||
private bool smoothing_ok() |
|||
{ |
|||
if (!m_cinfo.m_progressive_mode || m_cinfo.m_coef_bits == null) |
|||
return false; |
|||
|
|||
/* Allocate latch area if not already done */ |
|||
if (m_coef_bits_latch == null) |
|||
{ |
|||
m_coef_bits_latch = new int[m_cinfo.m_num_components * SAVED_COEFS]; |
|||
m_coef_bits_savedOffset = 0; |
|||
} |
|||
|
|||
bool smoothing_useful = false; |
|||
for (int ci = 0; ci < m_cinfo.m_num_components; ci++) |
|||
{ |
|||
/* All components' quantization values must already be latched. */ |
|||
JQUANT_TBL qtable = m_cinfo.Comp_info[ci].quant_table; |
|||
if (qtable == null) |
|||
return false; |
|||
|
|||
/* Verify DC & first 5 AC quantizers are nonzero to avoid zero-divide. */ |
|||
if (qtable.quantval[0] == 0 || qtable.quantval[Q01_POS] == 0 || |
|||
qtable.quantval[Q10_POS] == 0 || qtable.quantval[Q20_POS] == 0 || |
|||
qtable.quantval[Q11_POS] == 0 || qtable.quantval[Q02_POS] == 0) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
/* DC values must be at least partly known for all components. */ |
|||
if (m_cinfo.m_coef_bits[ci][0] < 0) |
|||
return false; |
|||
|
|||
/* Block smoothing is helpful if some AC coefficients remain inaccurate. */ |
|||
for (int coefi = 1; coefi <= 5; coefi++) |
|||
{ |
|||
m_coef_bits_latch[m_coef_bits_savedOffset + coefi] = m_cinfo.m_coef_bits[ci][coefi]; |
|||
if (m_cinfo.m_coef_bits[ci][coefi] != 0) |
|||
smoothing_useful = true; |
|||
} |
|||
|
|||
m_coef_bits_savedOffset += SAVED_COEFS; |
|||
} |
|||
|
|||
return smoothing_useful; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reset within-iMCU-row counters for a new row (input side)
|
|||
/// </summary>
|
|||
private void start_iMCU_row() |
|||
{ |
|||
/* In an interleaved scan, an MCU row is the same as an iMCU row. |
|||
* In a noninterleaved scan, an iMCU row has v_samp_factor MCU rows. |
|||
* But at the bottom of the image, process only what's left. |
|||
*/ |
|||
if (m_cinfo.m_comps_in_scan > 1) |
|||
{ |
|||
m_MCU_rows_per_iMCU_row = 1; |
|||
} |
|||
else |
|||
{ |
|||
jpeg_component_info componentInfo = m_cinfo.Comp_info[m_cinfo.m_cur_comp_info[0]]; |
|||
|
|||
if (m_cinfo.m_input_iMCU_row < (m_cinfo.m_total_iMCU_rows - 1)) |
|||
m_MCU_rows_per_iMCU_row = componentInfo.V_samp_factor; |
|||
else |
|||
m_MCU_rows_per_iMCU_row = componentInfo.last_row_height; |
|||
} |
|||
|
|||
m_MCU_ctr = 0; |
|||
m_MCU_vert_offset = 0; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,510 @@ |
|||
/* Copyright (C) 2008-2011, Bit Miracle |
|||
* http://www.bitmiracle.com
|
|||
* |
|||
* Copyright (C) 1994-1996, Thomas G. Lane. |
|||
* This file is part of the Independent JPEG Group's software. |
|||
* For conditions of distribution and use, see the accompanying README file. |
|||
* |
|||
*/ |
|||
|
|||
/* |
|||
* This file contains the main buffer controller for decompression. |
|||
* The main buffer lies between the JPEG decompressor proper and the |
|||
* post-processor; it holds downsampled data in the JPEG colorspace. |
|||
* |
|||
* Note that this code is bypassed in raw-data mode, since the application |
|||
* supplies the equivalent of the main buffer in that case. |
|||
*/ |
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
|
|||
namespace BitMiracle.LibJpeg.Classic.Internal |
|||
{ |
|||
/// <summary>
|
|||
/// Main buffer control (downsampled-data buffer)
|
|||
///
|
|||
/// In the current system design, the main buffer need never be a full-image
|
|||
/// buffer; any full-height buffers will be found inside the coefficient or
|
|||
/// postprocessing controllers. Nonetheless, the main controller is not
|
|||
/// trivial. Its responsibility is to provide context rows for upsampling/
|
|||
/// rescaling, and doing this in an efficient fashion is a bit tricky.
|
|||
///
|
|||
/// Postprocessor input data is counted in "row groups". A row group
|
|||
/// is defined to be (v_samp_factor * DCT_scaled_size / min_DCT_scaled_size)
|
|||
/// sample rows of each component. (We require DCT_scaled_size values to be
|
|||
/// chosen such that these numbers are integers. In practice DCT_scaled_size
|
|||
/// values will likely be powers of two, so we actually have the stronger
|
|||
/// condition that DCT_scaled_size / min_DCT_scaled_size is an integer.)
|
|||
/// Upsampling will typically produce max_v_samp_factor pixel rows from each
|
|||
/// row group (times any additional scale factor that the upsampler is
|
|||
/// applying).
|
|||
///
|
|||
/// The coefficient controller will deliver data to us one iMCU row at a time;
|
|||
/// each iMCU row contains v_samp_factor * DCT_scaled_size sample rows, or
|
|||
/// exactly min_DCT_scaled_size row groups. (This amount of data corresponds
|
|||
/// to one row of MCUs when the image is fully interleaved.) Note that the
|
|||
/// number of sample rows varies across components, but the number of row
|
|||
/// groups does not. Some garbage sample rows may be included in the last iMCU
|
|||
/// row at the bottom of the image.
|
|||
///
|
|||
/// Depending on the vertical scaling algorithm used, the upsampler may need
|
|||
/// access to the sample row(s) above and below its current input row group.
|
|||
/// The upsampler is required to set need_context_rows true at global selection
|
|||
/// time if so. When need_context_rows is false, this controller can simply
|
|||
/// obtain one iMCU row at a time from the coefficient controller and dole it
|
|||
/// out as row groups to the postprocessor.
|
|||
///
|
|||
/// When need_context_rows is true, this controller guarantees that the buffer
|
|||
/// passed to postprocessing contains at least one row group's worth of samples
|
|||
/// above and below the row group(s) being processed. Note that the context
|
|||
/// rows "above" the first passed row group appear at negative row offsets in
|
|||
/// the passed buffer. At the top and bottom of the image, the required
|
|||
/// context rows are manufactured by duplicating the first or last real sample
|
|||
/// row; this avoids having special cases in the upsampling inner loops.
|
|||
///
|
|||
/// The amount of context is fixed at one row group just because that's a
|
|||
/// convenient number for this controller to work with. The existing
|
|||
/// upsamplers really only need one sample row of context. An upsampler
|
|||
/// supporting arbitrary output rescaling might wish for more than one row
|
|||
/// group of context when shrinking the image; tough, we don't handle that.
|
|||
/// (This is justified by the assumption that downsizing will be handled mostly
|
|||
/// by adjusting the DCT_scaled_size values, so that the actual scale factor at
|
|||
/// the upsample step needn't be much less than one.)
|
|||
///
|
|||
/// To provide the desired context, we have to retain the last two row groups
|
|||
/// of one iMCU row while reading in the next iMCU row. (The last row group
|
|||
/// can't be processed until we have another row group for its below-context,
|
|||
/// and so we have to save the next-to-last group too for its above-context.)
|
|||
/// We could do this most simply by copying data around in our buffer, but
|
|||
/// that'd be very slow. We can avoid copying any data by creating a rather
|
|||
/// strange pointer structure. Here's how it works. We allocate a workspace
|
|||
/// consisting of M+2 row groups (where M = min_DCT_scaled_size is the number
|
|||
/// of row groups per iMCU row). We create two sets of redundant pointers to
|
|||
/// the workspace. Labeling the physical row groups 0 to M+1, the synthesized
|
|||
/// pointer lists look like this:
|
|||
/// M+1 M-1
|
|||
/// master pointer --> 0 master pointer --> 0
|
|||
/// 1 1
|
|||
/// ... ...
|
|||
/// M-3 M-3
|
|||
/// M-2 M
|
|||
/// M-1 M+1
|
|||
/// M M-2
|
|||
/// M+1 M-1
|
|||
/// 0 0
|
|||
/// We read alternate iMCU rows using each master pointer; thus the last two
|
|||
/// row groups of the previous iMCU row remain un-overwritten in the workspace.
|
|||
/// The pointer lists are set up so that the required context rows appear to
|
|||
/// be adjacent to the proper places when we pass the pointer lists to the
|
|||
/// upsampler.
|
|||
///
|
|||
/// The above pictures describe the normal state of the pointer lists.
|
|||
/// At top and bottom of the image, we diddle the pointer lists to duplicate
|
|||
/// the first or last sample row as necessary (this is cheaper than copying
|
|||
/// sample rows around).
|
|||
///
|
|||
/// This scheme breaks down if M less than 2, ie, min_DCT_scaled_size is 1. In that
|
|||
/// situation each iMCU row provides only one row group so the buffering logic
|
|||
/// must be different (eg, we must read two iMCU rows before we can emit the
|
|||
/// first row group). For now, we simply do not support providing context
|
|||
/// rows when min_DCT_scaled_size is 1. That combination seems unlikely to
|
|||
/// be worth providing --- if someone wants a 1/8th-size preview, they probably
|
|||
/// want it quick and dirty, so a context-free upsampler is sufficient.
|
|||
/// </summary>
|
|||
class jpeg_d_main_controller |
|||
{ |
|||
private enum DataProcessor |
|||
{ |
|||
context_main, |
|||
simple_main, |
|||
crank_post |
|||
} |
|||
|
|||
/* context_state values: */ |
|||
private const int CTX_PREPARE_FOR_IMCU = 0; /* need to prepare for MCU row */ |
|||
private const int CTX_PROCESS_IMCU = 1; /* feeding iMCU to postprocessor */ |
|||
private const int CTX_POSTPONED_ROW = 2; /* feeding postponed row group */ |
|||
|
|||
private DataProcessor m_dataProcessor; |
|||
private jpeg_decompress_struct m_cinfo; |
|||
|
|||
/* Pointer to allocated workspace (M or M+2 row groups). */ |
|||
private byte[][][] m_buffer = new byte[JpegConstants.MAX_COMPONENTS][][]; |
|||
|
|||
private bool m_buffer_full; /* Have we gotten an iMCU row from decoder? */ |
|||
private int m_rowgroup_ctr; /* counts row groups output to postprocessor */ |
|||
|
|||
/* Remaining fields are only used in the context case. */ |
|||
|
|||
private int[][][] m_funnyIndices = new int[2][][] { new int[JpegConstants.MAX_COMPONENTS][], new int[JpegConstants.MAX_COMPONENTS][]}; |
|||
private int[] m_funnyOffsets = new int[JpegConstants.MAX_COMPONENTS]; |
|||
private int m_whichFunny; /* indicates which funny indices set is now in use */ |
|||
|
|||
private int m_context_state; /* process_data state machine status */ |
|||
private int m_rowgroups_avail; /* row groups available to postprocessor */ |
|||
private int m_iMCU_row_ctr; /* counts iMCU rows to detect image top/bot */ |
|||
|
|||
public jpeg_d_main_controller(jpeg_decompress_struct cinfo) |
|||
{ |
|||
m_cinfo = cinfo; |
|||
|
|||
/* Allocate the workspace. |
|||
* ngroups is the number of row groups we need. |
|||
*/ |
|||
int ngroups = cinfo.m_min_DCT_scaled_size; |
|||
if (cinfo.m_upsample.NeedContextRows()) |
|||
{ |
|||
if (cinfo.m_min_DCT_scaled_size < 2) /* unsupported, see comments above */ |
|||
cinfo.ERREXIT(J_MESSAGE_CODE.JERR_NOTIMPL); |
|||
|
|||
alloc_funny_pointers(); /* Alloc space for xbuffer[] lists */ |
|||
ngroups = cinfo.m_min_DCT_scaled_size + 2; |
|||
} |
|||
|
|||
for (int ci = 0; ci < cinfo.m_num_components; ci++) |
|||
{ |
|||
/* height of a row group of component */ |
|||
int rgroup = (cinfo.Comp_info[ci].V_samp_factor * cinfo.Comp_info[ci].DCT_scaled_size) / cinfo.m_min_DCT_scaled_size; |
|||
|
|||
m_buffer[ci] = jpeg_common_struct.AllocJpegSamples( |
|||
cinfo.Comp_info[ci].Width_in_blocks * cinfo.Comp_info[ci].DCT_scaled_size, |
|||
rgroup * ngroups); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initialize for a processing pass.
|
|||
/// </summary>
|
|||
public void start_pass(J_BUF_MODE pass_mode) |
|||
{ |
|||
switch (pass_mode) |
|||
{ |
|||
case J_BUF_MODE.JBUF_PASS_THRU: |
|||
if (m_cinfo.m_upsample.NeedContextRows()) |
|||
{ |
|||
m_dataProcessor = DataProcessor.context_main; |
|||
make_funny_pointers(); /* Create the xbuffer[] lists */ |
|||
m_whichFunny = 0; /* Read first iMCU row into xbuffer[0] */ |
|||
m_context_state = CTX_PREPARE_FOR_IMCU; |
|||
m_iMCU_row_ctr = 0; |
|||
} |
|||
else |
|||
{ |
|||
/* Simple case with no context needed */ |
|||
m_dataProcessor = DataProcessor.simple_main; |
|||
} |
|||
m_buffer_full = false; /* Mark buffer empty */ |
|||
m_rowgroup_ctr = 0; |
|||
break; |
|||
case J_BUF_MODE.JBUF_CRANK_DEST: |
|||
/* For last pass of 2-pass quantization, just crank the postprocessor */ |
|||
m_dataProcessor = DataProcessor.crank_post; |
|||
break; |
|||
default: |
|||
m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_BUFFER_MODE); |
|||
break; |
|||
} |
|||
} |
|||
|
|||
public void process_data(byte[][] output_buf, ref int out_row_ctr, int out_rows_avail) |
|||
{ |
|||
switch (m_dataProcessor) |
|||
{ |
|||
case DataProcessor.simple_main: |
|||
process_data_simple_main(output_buf, ref out_row_ctr, out_rows_avail); |
|||
break; |
|||
|
|||
case DataProcessor.context_main: |
|||
process_data_context_main(output_buf, ref out_row_ctr, out_rows_avail); |
|||
break; |
|||
|
|||
case DataProcessor.crank_post: |
|||
process_data_crank_post(output_buf, ref out_row_ctr, out_rows_avail); |
|||
break; |
|||
|
|||
default: |
|||
m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_NOTIMPL); |
|||
break; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Process some data.
|
|||
/// This handles the simple case where no context is required.
|
|||
/// </summary>
|
|||
private void process_data_simple_main(byte[][] output_buf, ref int out_row_ctr, int out_rows_avail) |
|||
{ |
|||
ComponentBuffer[] cb = new ComponentBuffer[JpegConstants.MAX_COMPONENTS]; |
|||
for (int i = 0; i < JpegConstants.MAX_COMPONENTS; i++) |
|||
{ |
|||
cb[i] = new ComponentBuffer(); |
|||
cb[i].SetBuffer(m_buffer[i], null, 0); |
|||
} |
|||
|
|||
/* Read input data if we haven't filled the main buffer yet */ |
|||
if (!m_buffer_full) |
|||
{ |
|||
if (m_cinfo.m_coef.decompress_data(cb) == ReadResult.JPEG_SUSPENDED) |
|||
{ |
|||
/* suspension forced, can do nothing more */ |
|||
return; |
|||
} |
|||
|
|||
/* OK, we have an iMCU row to work with */ |
|||
m_buffer_full = true; |
|||
} |
|||
|
|||
/* There are always min_DCT_scaled_size row groups in an iMCU row. */ |
|||
int rowgroups_avail = m_cinfo.m_min_DCT_scaled_size; |
|||
|
|||
/* Note: at the bottom of the image, we may pass extra garbage row groups |
|||
* to the postprocessor. The postprocessor has to check for bottom |
|||
* of image anyway (at row resolution), so no point in us doing it too. |
|||
*/ |
|||
|
|||
/* Feed the postprocessor */ |
|||
m_cinfo.m_post.post_process_data(cb, ref m_rowgroup_ctr, rowgroups_avail, output_buf, ref out_row_ctr, out_rows_avail); |
|||
|
|||
/* Has postprocessor consumed all the data yet? If so, mark buffer empty */ |
|||
if (m_rowgroup_ctr >= rowgroups_avail) |
|||
{ |
|||
m_buffer_full = false; |
|||
m_rowgroup_ctr = 0; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Process some data.
|
|||
/// This handles the case where context rows must be provided.
|
|||
/// </summary>
|
|||
private void process_data_context_main(byte[][] output_buf, ref int out_row_ctr, int out_rows_avail) |
|||
{ |
|||
ComponentBuffer[] cb = new ComponentBuffer[m_cinfo.m_num_components]; |
|||
for (int i = 0; i < m_cinfo.m_num_components; i++) |
|||
{ |
|||
cb[i] = new ComponentBuffer(); |
|||
cb[i].SetBuffer(m_buffer[i], m_funnyIndices[m_whichFunny][i], m_funnyOffsets[i]); |
|||
} |
|||
|
|||
/* Read input data if we haven't filled the main buffer yet */ |
|||
if (!m_buffer_full) |
|||
{ |
|||
if (m_cinfo.m_coef.decompress_data(cb) == ReadResult.JPEG_SUSPENDED) |
|||
{ |
|||
/* suspension forced, can do nothing more */ |
|||
return; |
|||
} |
|||
|
|||
/* OK, we have an iMCU row to work with */ |
|||
m_buffer_full = true; |
|||
|
|||
/* count rows received */ |
|||
m_iMCU_row_ctr++; |
|||
} |
|||
|
|||
/* Postprocessor typically will not swallow all the input data it is handed |
|||
* in one call (due to filling the output buffer first). Must be prepared |
|||
* to exit and restart. |
|||
|
|||
|
|||
This switch lets us keep track of how far we got. |
|||
* Note that each case falls through to the next on successful completion. |
|||
*/ |
|||
if (m_context_state == CTX_POSTPONED_ROW) |
|||
{ |
|||
/* Call postprocessor using previously set pointers for postponed row */ |
|||
m_cinfo.m_post.post_process_data(cb, ref m_rowgroup_ctr, |
|||
m_rowgroups_avail, output_buf, ref out_row_ctr, out_rows_avail); |
|||
|
|||
if (m_rowgroup_ctr < m_rowgroups_avail) |
|||
{ |
|||
/* Need to suspend */ |
|||
return; |
|||
} |
|||
|
|||
m_context_state = CTX_PREPARE_FOR_IMCU; |
|||
|
|||
if (out_row_ctr >= out_rows_avail) |
|||
{ |
|||
/* Postprocessor exactly filled output buf */ |
|||
return; |
|||
} |
|||
} |
|||
|
|||
if (m_context_state == CTX_PREPARE_FOR_IMCU) |
|||
{ |
|||
/* Prepare to process first M-1 row groups of this iMCU row */ |
|||
m_rowgroup_ctr = 0; |
|||
m_rowgroups_avail = m_cinfo.m_min_DCT_scaled_size - 1; |
|||
|
|||
/* Check for bottom of image: if so, tweak pointers to "duplicate" |
|||
* the last sample row, and adjust rowgroups_avail to ignore padding rows. |
|||
*/ |
|||
if (m_iMCU_row_ctr == m_cinfo.m_total_iMCU_rows) |
|||
set_bottom_pointers(); |
|||
|
|||
m_context_state = CTX_PROCESS_IMCU; |
|||
} |
|||
|
|||
if (m_context_state == CTX_PROCESS_IMCU) |
|||
{ |
|||
/* Call postprocessor using previously set pointers */ |
|||
m_cinfo.m_post.post_process_data(cb, ref m_rowgroup_ctr, |
|||
m_rowgroups_avail, output_buf, ref out_row_ctr, out_rows_avail); |
|||
|
|||
if (m_rowgroup_ctr < m_rowgroups_avail) |
|||
{ |
|||
/* Need to suspend */ |
|||
return; |
|||
} |
|||
|
|||
/* After the first iMCU, change wraparound pointers to normal state */ |
|||
if (m_iMCU_row_ctr == 1) |
|||
set_wraparound_pointers(); |
|||
|
|||
/* Prepare to load new iMCU row using other xbuffer list */ |
|||
m_whichFunny ^= 1; /* 0=>1 or 1=>0 */ |
|||
m_buffer_full = false; |
|||
|
|||
/* Still need to process last row group of this iMCU row, */ |
|||
/* which is saved at index M+1 of the other xbuffer */ |
|||
m_rowgroup_ctr = m_cinfo.m_min_DCT_scaled_size + 1; |
|||
m_rowgroups_avail = m_cinfo.m_min_DCT_scaled_size + 2; |
|||
m_context_state = CTX_POSTPONED_ROW; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Process some data.
|
|||
/// Final pass of two-pass quantization: just call the postprocessor.
|
|||
/// Source data will be the postprocessor controller's internal buffer.
|
|||
/// </summary>
|
|||
private void process_data_crank_post(byte[][] output_buf, ref int out_row_ctr, int out_rows_avail) |
|||
{ |
|||
int dummy = 0; |
|||
m_cinfo.m_post.post_process_data(null, ref dummy, 0, output_buf, ref out_row_ctr, out_rows_avail); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Allocate space for the funny pointer lists.
|
|||
/// This is done only once, not once per pass.
|
|||
/// </summary>
|
|||
private void alloc_funny_pointers() |
|||
{ |
|||
int M = m_cinfo.m_min_DCT_scaled_size; |
|||
for (int ci = 0; ci < m_cinfo.m_num_components; ci++) |
|||
{ |
|||
/* height of a row group of component */ |
|||
int rgroup = (m_cinfo.Comp_info[ci].V_samp_factor * m_cinfo.Comp_info[ci].DCT_scaled_size) / m_cinfo.m_min_DCT_scaled_size; |
|||
|
|||
/* Get space for pointer lists --- M+4 row groups in each list. |
|||
*/ |
|||
m_funnyIndices[0][ci] = new int[rgroup * (M + 4)]; |
|||
m_funnyIndices[1][ci] = new int[rgroup * (M + 4)]; |
|||
m_funnyOffsets[ci] = rgroup; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Create the funny pointer lists discussed in the comments above.
|
|||
/// The actual workspace is already allocated (in main.buffer),
|
|||
/// and the space for the pointer lists is allocated too.
|
|||
/// This routine just fills in the curiously ordered lists.
|
|||
/// This will be repeated at the beginning of each pass.
|
|||
/// </summary>
|
|||
private void make_funny_pointers() |
|||
{ |
|||
int M = m_cinfo.m_min_DCT_scaled_size; |
|||
for (int ci = 0; ci < m_cinfo.m_num_components; ci++) |
|||
{ |
|||
/* height of a row group of component */ |
|||
int rgroup = (m_cinfo.Comp_info[ci].V_samp_factor * m_cinfo.Comp_info[ci].DCT_scaled_size) / m_cinfo.m_min_DCT_scaled_size; |
|||
|
|||
int[] ind0 = m_funnyIndices[0][ci]; |
|||
int[] ind1 = m_funnyIndices[1][ci]; |
|||
|
|||
/* First copy the workspace pointers as-is */ |
|||
for (int i = 0; i < rgroup * (M + 2); i++) |
|||
{ |
|||
ind0[i + rgroup] = i; |
|||
ind1[i + rgroup] = i; |
|||
} |
|||
|
|||
/* In the second list, put the last four row groups in swapped order */ |
|||
for (int i = 0; i < rgroup * 2; i++) |
|||
{ |
|||
ind1[rgroup * (M - 1) + i] = rgroup * M + i; |
|||
ind1[rgroup * (M + 1) + i] = rgroup * (M - 2) + i; |
|||
} |
|||
|
|||
/* The wraparound pointers at top and bottom will be filled later |
|||
* (see set_wraparound_pointers, below). Initially we want the "above" |
|||
* pointers to duplicate the first actual data line. This only needs |
|||
* to happen in xbuffer[0]. |
|||
*/ |
|||
for (int i = 0; i < rgroup; i++) |
|||
ind0[i] = ind0[rgroup]; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Set up the "wraparound" pointers at top and bottom of the pointer lists.
|
|||
/// This changes the pointer list state from top-of-image to the normal state.
|
|||
/// </summary>
|
|||
private void set_wraparound_pointers() |
|||
{ |
|||
int M = m_cinfo.m_min_DCT_scaled_size; |
|||
for (int ci = 0; ci < m_cinfo.m_num_components; ci++) |
|||
{ |
|||
/* height of a row group of component */ |
|||
int rgroup = (m_cinfo.Comp_info[ci].V_samp_factor * m_cinfo.Comp_info[ci].DCT_scaled_size) / m_cinfo.m_min_DCT_scaled_size; |
|||
|
|||
int[] ind0 = m_funnyIndices[0][ci]; |
|||
int[] ind1 = m_funnyIndices[1][ci]; |
|||
|
|||
for (int i = 0; i < rgroup; i++) |
|||
{ |
|||
ind0[i] = ind0[rgroup * (M + 2) + i]; |
|||
ind1[i] = ind1[rgroup * (M + 2) + i]; |
|||
|
|||
ind0[rgroup * (M + 3) + i] = ind0[i + rgroup]; |
|||
ind1[rgroup * (M + 3) + i] = ind1[i + rgroup]; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Change the pointer lists to duplicate the last sample row at the bottom
|
|||
/// of the image. m_whichFunny indicates which m_funnyIndices holds the final iMCU row.
|
|||
/// Also sets rowgroups_avail to indicate number of nondummy row groups in row.
|
|||
/// </summary>
|
|||
private void set_bottom_pointers() |
|||
{ |
|||
for (int ci = 0; ci < m_cinfo.m_num_components; ci++) |
|||
{ |
|||
/* Count sample rows in one iMCU row and in one row group */ |
|||
int iMCUheight = m_cinfo.Comp_info[ci].V_samp_factor * m_cinfo.Comp_info[ci].DCT_scaled_size; |
|||
int rgroup = iMCUheight / m_cinfo.m_min_DCT_scaled_size; |
|||
|
|||
/* Count nondummy sample rows remaining for this component */ |
|||
int rows_left = m_cinfo.Comp_info[ci].downsampled_height % iMCUheight; |
|||
if (rows_left == 0) |
|||
rows_left = iMCUheight; |
|||
|
|||
/* Count nondummy row groups. Should get same answer for each component, |
|||
* so we need only do it once. |
|||
*/ |
|||
if (ci == 0) |
|||
m_rowgroups_avail = (rows_left - 1) / rgroup + 1; |
|||
|
|||
/* Duplicate the last real sample row rgroup*2 times; this pads out the |
|||
* last partial rowgroup and ensures at least one full rowgroup of context. |
|||
*/ |
|||
for (int i = 0; i < rgroup * 2; i++) |
|||
m_funnyIndices[m_whichFunny][ci][rows_left + i + rgroup] = m_funnyIndices[m_whichFunny][ci][rows_left - 1 + rgroup]; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,248 @@ |
|||
/* Copyright (C) 2008-2011, Bit Miracle |
|||
* http://www.bitmiracle.com
|
|||
* |
|||
* Copyright (C) 1994-1996, Thomas G. Lane. |
|||
* This file is part of the Independent JPEG Group's software. |
|||
* For conditions of distribution and use, see the accompanying README file. |
|||
* |
|||
*/ |
|||
|
|||
/* |
|||
* This file contains the decompression postprocessing controller. |
|||
* This controller manages the upsampling, color conversion, and color |
|||
* quantization/reduction steps; specifically, it controls the buffering |
|||
* between upsample/color conversion and color quantization/reduction. |
|||
* |
|||
* If no color quantization/reduction is required, then this module has no |
|||
* work to do, and it just hands off to the upsample/color conversion code. |
|||
* An integrated upsample/convert/quantize process would replace this module |
|||
* entirely. |
|||
*/ |
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
|
|||
namespace BitMiracle.LibJpeg.Classic.Internal |
|||
{ |
|||
/// <summary>
|
|||
/// Decompression postprocessing (color quantization buffer control)
|
|||
/// </summary>
|
|||
class jpeg_d_post_controller |
|||
{ |
|||
private enum ProcessorType |
|||
{ |
|||
OnePass, |
|||
PrePass, |
|||
Upsample, |
|||
SecondPass |
|||
} |
|||
|
|||
private ProcessorType m_processor; |
|||
|
|||
private jpeg_decompress_struct m_cinfo; |
|||
|
|||
/* Color quantization source buffer: this holds output data from |
|||
* the upsample/color conversion step to be passed to the quantizer. |
|||
* For two-pass color quantization, we need a full-image buffer; |
|||
* for one-pass operation, a strip buffer is sufficient. |
|||
*/ |
|||
private jvirt_array<byte> m_whole_image; /* virtual array, or null if one-pass */ |
|||
private byte[][] m_buffer; /* strip buffer, or current strip of virtual */ |
|||
private int m_strip_height; /* buffer size in rows */ |
|||
/* for two-pass mode only: */ |
|||
private int m_starting_row; /* row # of first row in current strip */ |
|||
private int m_next_row; /* index of next row to fill/empty in strip */ |
|||
|
|||
/// <summary>
|
|||
/// Initialize postprocessing controller.
|
|||
/// </summary>
|
|||
public jpeg_d_post_controller(jpeg_decompress_struct cinfo, bool need_full_buffer) |
|||
{ |
|||
m_cinfo = cinfo; |
|||
|
|||
/* Create the quantization buffer, if needed */ |
|||
if (cinfo.m_quantize_colors) |
|||
{ |
|||
/* The buffer strip height is max_v_samp_factor, which is typically |
|||
* an efficient number of rows for upsampling to return. |
|||
* (In the presence of output rescaling, we might want to be smarter?) |
|||
*/ |
|||
m_strip_height = cinfo.m_max_v_samp_factor; |
|||
|
|||
if (need_full_buffer) |
|||
{ |
|||
/* Two-pass color quantization: need full-image storage. */ |
|||
/* We round up the number of rows to a multiple of the strip height. */ |
|||
m_whole_image = jpeg_common_struct.CreateSamplesArray( |
|||
cinfo.m_output_width * cinfo.m_out_color_components, |
|||
JpegUtils.jround_up(cinfo.m_output_height, m_strip_height)); |
|||
m_whole_image.ErrorProcessor = cinfo; |
|||
} |
|||
else |
|||
{ |
|||
/* One-pass color quantization: just make a strip buffer. */ |
|||
m_buffer = jpeg_common_struct.AllocJpegSamples( |
|||
cinfo.m_output_width * cinfo.m_out_color_components, m_strip_height); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initialize for a processing pass.
|
|||
/// </summary>
|
|||
public void start_pass(J_BUF_MODE pass_mode) |
|||
{ |
|||
switch (pass_mode) |
|||
{ |
|||
case J_BUF_MODE.JBUF_PASS_THRU: |
|||
if (m_cinfo.m_quantize_colors) |
|||
{ |
|||
/* Single-pass processing with color quantization. */ |
|||
m_processor = ProcessorType.OnePass; |
|||
/* We could be doing buffered-image output before starting a 2-pass |
|||
* color quantization; in that case, jinit_d_post_controller did not |
|||
* allocate a strip buffer. Use the virtual-array buffer as workspace. |
|||
*/ |
|||
if (m_buffer == null) |
|||
m_buffer = m_whole_image.Access(0, m_strip_height); |
|||
} |
|||
else |
|||
{ |
|||
/* For single-pass processing without color quantization, |
|||
* I have no work to do; just call the upsampler directly. |
|||
*/ |
|||
m_processor = ProcessorType.Upsample; |
|||
} |
|||
break; |
|||
case J_BUF_MODE.JBUF_SAVE_AND_PASS: |
|||
/* First pass of 2-pass quantization */ |
|||
if (m_whole_image == null) |
|||
m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_BUFFER_MODE); |
|||
|
|||
m_processor = ProcessorType.PrePass; |
|||
break; |
|||
case J_BUF_MODE.JBUF_CRANK_DEST: |
|||
/* Second pass of 2-pass quantization */ |
|||
if (m_whole_image == null) |
|||
m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_BUFFER_MODE); |
|||
|
|||
m_processor = ProcessorType.SecondPass; |
|||
break; |
|||
default: |
|||
m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_BUFFER_MODE); |
|||
break; |
|||
} |
|||
m_starting_row = m_next_row = 0; |
|||
} |
|||
|
|||
public void post_process_data(ComponentBuffer[] input_buf, ref int in_row_group_ctr, int in_row_groups_avail, byte[][] output_buf, ref int out_row_ctr, int out_rows_avail) |
|||
{ |
|||
switch (m_processor) |
|||
{ |
|||
case ProcessorType.OnePass: |
|||
post_process_1pass(input_buf, ref in_row_group_ctr, in_row_groups_avail, output_buf, ref out_row_ctr, out_rows_avail); |
|||
break; |
|||
case ProcessorType.PrePass: |
|||
post_process_prepass(input_buf, ref in_row_group_ctr, in_row_groups_avail, ref out_row_ctr); |
|||
break; |
|||
case ProcessorType.Upsample: |
|||
m_cinfo.m_upsample.upsample(input_buf, ref in_row_group_ctr, in_row_groups_avail, output_buf, ref out_row_ctr, out_rows_avail); |
|||
break; |
|||
case ProcessorType.SecondPass: |
|||
post_process_2pass(output_buf, ref out_row_ctr, out_rows_avail); |
|||
break; |
|||
default: |
|||
m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_NOTIMPL); |
|||
break; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Process some data in the one-pass (strip buffer) case.
|
|||
/// This is used for color precision reduction as well as one-pass quantization.
|
|||
/// </summary>
|
|||
private void post_process_1pass(ComponentBuffer[] input_buf, ref int in_row_group_ctr, int in_row_groups_avail, byte[][] output_buf, ref int out_row_ctr, int out_rows_avail) |
|||
{ |
|||
/* Fill the buffer, but not more than what we can dump out in one go. */ |
|||
/* Note we rely on the upsampler to detect bottom of image. */ |
|||
int max_rows = out_rows_avail - out_row_ctr; |
|||
if (max_rows > m_strip_height) |
|||
max_rows = m_strip_height; |
|||
|
|||
int num_rows = 0; |
|||
m_cinfo.m_upsample.upsample(input_buf, ref in_row_group_ctr, in_row_groups_avail, m_buffer, ref num_rows, max_rows); |
|||
|
|||
/* Quantize and emit data. */ |
|||
m_cinfo.m_cquantize.color_quantize(m_buffer, 0, output_buf, out_row_ctr, num_rows); |
|||
out_row_ctr += num_rows; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Process some data in the first pass of 2-pass quantization.
|
|||
/// </summary>
|
|||
private void post_process_prepass(ComponentBuffer[] input_buf, ref int in_row_group_ctr, int in_row_groups_avail, ref int out_row_ctr) |
|||
{ |
|||
int old_next_row, num_rows; |
|||
|
|||
/* Reposition virtual buffer if at start of strip. */ |
|||
if (m_next_row == 0) |
|||
m_buffer = m_whole_image.Access(m_starting_row, m_strip_height); |
|||
|
|||
/* Upsample some data (up to a strip height's worth). */ |
|||
old_next_row = m_next_row; |
|||
m_cinfo.m_upsample.upsample(input_buf, ref in_row_group_ctr, in_row_groups_avail, m_buffer, ref m_next_row, m_strip_height); |
|||
|
|||
/* Allow quantizer to scan new data. No data is emitted, */ |
|||
/* but we advance out_row_ctr so outer loop can tell when we're done. */ |
|||
if (m_next_row > old_next_row) |
|||
{ |
|||
num_rows = m_next_row - old_next_row; |
|||
m_cinfo.m_cquantize.color_quantize(m_buffer, old_next_row, null, 0, num_rows); |
|||
out_row_ctr += num_rows; |
|||
} |
|||
|
|||
/* Advance if we filled the strip. */ |
|||
if (m_next_row >= m_strip_height) |
|||
{ |
|||
m_starting_row += m_strip_height; |
|||
m_next_row = 0; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Process some data in the second pass of 2-pass quantization.
|
|||
/// </summary>
|
|||
private void post_process_2pass(byte[][] output_buf, ref int out_row_ctr, int out_rows_avail) |
|||
{ |
|||
int num_rows, max_rows; |
|||
|
|||
/* Reposition virtual buffer if at start of strip. */ |
|||
if (m_next_row == 0) |
|||
m_buffer = m_whole_image.Access(m_starting_row, m_strip_height); |
|||
|
|||
/* Determine number of rows to emit. */ |
|||
num_rows = m_strip_height - m_next_row; /* available in strip */ |
|||
max_rows = out_rows_avail - out_row_ctr; /* available in output area */ |
|||
if (num_rows > max_rows) |
|||
num_rows = max_rows; |
|||
|
|||
/* We have to check bottom of image here, can't depend on upsampler. */ |
|||
max_rows = m_cinfo.m_output_height - m_starting_row; |
|||
if (num_rows > max_rows) |
|||
num_rows = max_rows; |
|||
|
|||
/* Quantize and emit data. */ |
|||
m_cinfo.m_cquantize.color_quantize(m_buffer, m_next_row, output_buf, out_row_ctr, num_rows); |
|||
out_row_ctr += num_rows; |
|||
|
|||
/* Advance if we filled the strip. */ |
|||
m_next_row += num_rows; |
|||
if (m_next_row >= m_strip_height) |
|||
{ |
|||
m_starting_row += m_strip_height; |
|||
m_next_row = 0; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,344 @@ |
|||
/* Copyright (C) 2008-2011, Bit Miracle |
|||
* http://www.bitmiracle.com
|
|||
* |
|||
* Copyright (C) 1994-1996, Thomas G. Lane. |
|||
* This file is part of the Independent JPEG Group's software. |
|||
* For conditions of distribution and use, see the accompanying README file. |
|||
* |
|||
*/ |
|||
|
|||
/* |
|||
* This file contains master control logic for the JPEG decompressor. |
|||
* These routines are concerned with selecting the modules to be executed |
|||
* and with determining the number of passes and the work to be done in each |
|||
* pass. |
|||
*/ |
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
|
|||
namespace BitMiracle.LibJpeg.Classic.Internal |
|||
{ |
|||
/// <summary>
|
|||
/// Master control module
|
|||
/// </summary>
|
|||
class jpeg_decomp_master |
|||
{ |
|||
private jpeg_decompress_struct m_cinfo; |
|||
|
|||
private int m_pass_number; /* # of passes completed */ |
|||
private bool m_is_dummy_pass; /* True during 1st pass for 2-pass quant */ |
|||
|
|||
private bool m_using_merged_upsample; /* true if using merged upsample/cconvert */ |
|||
|
|||
/* Saved references to initialized quantizer modules, |
|||
* in case we need to switch modes. |
|||
*/ |
|||
private jpeg_color_quantizer m_quantizer_1pass; |
|||
private jpeg_color_quantizer m_quantizer_2pass; |
|||
|
|||
public jpeg_decomp_master(jpeg_decompress_struct cinfo) |
|||
{ |
|||
m_cinfo = cinfo; |
|||
master_selection(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Per-pass setup.
|
|||
/// This is called at the beginning of each output pass. We determine which
|
|||
/// modules will be active during this pass and give them appropriate
|
|||
/// start_pass calls. We also set is_dummy_pass to indicate whether this
|
|||
/// is a "real" output pass or a dummy pass for color quantization.
|
|||
/// (In the latter case, we will crank the pass to completion.)
|
|||
/// </summary>
|
|||
public void prepare_for_output_pass() |
|||
{ |
|||
if (m_is_dummy_pass) |
|||
{ |
|||
/* Final pass of 2-pass quantization */ |
|||
m_is_dummy_pass = false; |
|||
m_cinfo.m_cquantize.start_pass(false); |
|||
m_cinfo.m_post.start_pass(J_BUF_MODE.JBUF_CRANK_DEST); |
|||
m_cinfo.m_main.start_pass(J_BUF_MODE.JBUF_CRANK_DEST); |
|||
} |
|||
else |
|||
{ |
|||
if (m_cinfo.m_quantize_colors && m_cinfo.m_colormap == null) |
|||
{ |
|||
/* Select new quantization method */ |
|||
if (m_cinfo.m_two_pass_quantize && m_cinfo.m_enable_2pass_quant) |
|||
{ |
|||
m_cinfo.m_cquantize = m_quantizer_2pass; |
|||
m_is_dummy_pass = true; |
|||
} |
|||
else if (m_cinfo.m_enable_1pass_quant) |
|||
m_cinfo.m_cquantize = m_quantizer_1pass; |
|||
else |
|||
m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_MODE_CHANGE); |
|||
} |
|||
|
|||
m_cinfo.m_idct.start_pass(); |
|||
m_cinfo.m_coef.start_output_pass(); |
|||
|
|||
if (!m_cinfo.m_raw_data_out) |
|||
{ |
|||
m_cinfo.m_upsample.start_pass(); |
|||
|
|||
if (m_cinfo.m_quantize_colors) |
|||
m_cinfo.m_cquantize.start_pass(m_is_dummy_pass); |
|||
|
|||
m_cinfo.m_post.start_pass((m_is_dummy_pass ? J_BUF_MODE.JBUF_SAVE_AND_PASS : J_BUF_MODE.JBUF_PASS_THRU)); |
|||
m_cinfo.m_main.start_pass(J_BUF_MODE.JBUF_PASS_THRU); |
|||
} |
|||
} |
|||
|
|||
/* Set up progress monitor's pass info if present */ |
|||
if (m_cinfo.m_progress != null) |
|||
{ |
|||
m_cinfo.m_progress.Completed_passes = m_pass_number; |
|||
m_cinfo.m_progress.Total_passes = m_pass_number + (m_is_dummy_pass ? 2 : 1); |
|||
|
|||
/* In buffered-image mode, we assume one more output pass if EOI not |
|||
* yet reached, but no more passes if EOI has been reached. |
|||
*/ |
|||
if (m_cinfo.m_buffered_image && !m_cinfo.m_inputctl.EOIReached()) |
|||
m_cinfo.m_progress.Total_passes += (m_cinfo.m_enable_2pass_quant ? 2 : 1); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Finish up at end of an output pass.
|
|||
/// </summary>
|
|||
public void finish_output_pass() |
|||
{ |
|||
if (m_cinfo.m_quantize_colors) |
|||
m_cinfo.m_cquantize.finish_pass(); |
|||
|
|||
m_pass_number++; |
|||
} |
|||
|
|||
public bool IsDummyPass() |
|||
{ |
|||
return m_is_dummy_pass; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Master selection of decompression modules.
|
|||
/// This is done once at jpeg_start_decompress time. We determine
|
|||
/// which modules will be used and give them appropriate initialization calls.
|
|||
/// We also initialize the decompressor input side to begin consuming data.
|
|||
///
|
|||
/// Since jpeg_read_header has finished, we know what is in the SOF
|
|||
/// and (first) SOS markers. We also have all the application parameter
|
|||
/// settings.
|
|||
/// </summary>
|
|||
private void master_selection() |
|||
{ |
|||
/* Initialize dimensions and other stuff */ |
|||
m_cinfo.jpeg_calc_output_dimensions(); |
|||
prepare_range_limit_table(); |
|||
|
|||
/* Width of an output scanline must be representable as int. */ |
|||
long samplesperrow = m_cinfo.m_output_width * m_cinfo.m_out_color_components; |
|||
int jd_samplesperrow = (int)samplesperrow; |
|||
if ((long)jd_samplesperrow != samplesperrow) |
|||
m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_WIDTH_OVERFLOW); |
|||
|
|||
/* Initialize my private state */ |
|||
m_pass_number = 0; |
|||
m_using_merged_upsample = m_cinfo.use_merged_upsample(); |
|||
|
|||
/* Color quantizer selection */ |
|||
m_quantizer_1pass = null; |
|||
m_quantizer_2pass = null; |
|||
|
|||
/* No mode changes if not using buffered-image mode. */ |
|||
if (!m_cinfo.m_quantize_colors || !m_cinfo.m_buffered_image) |
|||
{ |
|||
m_cinfo.m_enable_1pass_quant = false; |
|||
m_cinfo.m_enable_external_quant = false; |
|||
m_cinfo.m_enable_2pass_quant = false; |
|||
} |
|||
|
|||
if (m_cinfo.m_quantize_colors) |
|||
{ |
|||
if (m_cinfo.m_raw_data_out) |
|||
m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_NOTIMPL); |
|||
|
|||
/* 2-pass quantizer only works in 3-component color space. */ |
|||
if (m_cinfo.m_out_color_components != 3) |
|||
{ |
|||
m_cinfo.m_enable_1pass_quant = true; |
|||
m_cinfo.m_enable_external_quant = false; |
|||
m_cinfo.m_enable_2pass_quant = false; |
|||
m_cinfo.m_colormap = null; |
|||
} |
|||
else if (m_cinfo.m_colormap != null) |
|||
m_cinfo.m_enable_external_quant = true; |
|||
else if (m_cinfo.m_two_pass_quantize) |
|||
m_cinfo.m_enable_2pass_quant = true; |
|||
else |
|||
m_cinfo.m_enable_1pass_quant = true; |
|||
|
|||
if (m_cinfo.m_enable_1pass_quant) |
|||
{ |
|||
m_cinfo.m_cquantize = new my_1pass_cquantizer(m_cinfo); |
|||
m_quantizer_1pass = m_cinfo.m_cquantize; |
|||
} |
|||
|
|||
/* We use the 2-pass code to map to external colormaps. */ |
|||
if (m_cinfo.m_enable_2pass_quant || m_cinfo.m_enable_external_quant) |
|||
{ |
|||
m_cinfo.m_cquantize = new my_2pass_cquantizer(m_cinfo); |
|||
m_quantizer_2pass = m_cinfo.m_cquantize; |
|||
} |
|||
/* If both quantizers are initialized, the 2-pass one is left active; |
|||
* this is necessary for starting with quantization to an external map. |
|||
*/ |
|||
} |
|||
|
|||
/* Post-processing: in particular, color conversion first */ |
|||
if (!m_cinfo.m_raw_data_out) |
|||
{ |
|||
if (m_using_merged_upsample) |
|||
{ |
|||
/* does color conversion too */ |
|||
m_cinfo.m_upsample = new my_merged_upsampler(m_cinfo); |
|||
} |
|||
else |
|||
{ |
|||
m_cinfo.m_cconvert = new jpeg_color_deconverter(m_cinfo); |
|||
m_cinfo.m_upsample = new my_upsampler(m_cinfo); |
|||
} |
|||
|
|||
m_cinfo.m_post = new jpeg_d_post_controller(m_cinfo, m_cinfo.m_enable_2pass_quant); |
|||
} |
|||
|
|||
/* Inverse DCT */ |
|||
m_cinfo.m_idct = new jpeg_inverse_dct(m_cinfo); |
|||
|
|||
if (m_cinfo.m_progressive_mode) |
|||
m_cinfo.m_entropy = new phuff_entropy_decoder(m_cinfo); |
|||
else |
|||
m_cinfo.m_entropy = new huff_entropy_decoder(m_cinfo); |
|||
|
|||
/* Initialize principal buffer controllers. */ |
|||
bool use_c_buffer = m_cinfo.m_inputctl.HasMultipleScans() || m_cinfo.m_buffered_image; |
|||
m_cinfo.m_coef = new jpeg_d_coef_controller(m_cinfo, use_c_buffer); |
|||
|
|||
if (!m_cinfo.m_raw_data_out) |
|||
m_cinfo.m_main = new jpeg_d_main_controller(m_cinfo); |
|||
|
|||
/* Initialize input side of decompressor to consume first scan. */ |
|||
m_cinfo.m_inputctl.start_input_pass(); |
|||
|
|||
/* If jpeg_start_decompress will read the whole file, initialize |
|||
* progress monitoring appropriately. The input step is counted |
|||
* as one pass. |
|||
*/ |
|||
if (m_cinfo.m_progress != null && !m_cinfo.m_buffered_image && m_cinfo.m_inputctl.HasMultipleScans()) |
|||
{ |
|||
/* Estimate number of scans to set pass_limit. */ |
|||
int nscans; |
|||
if (m_cinfo.m_progressive_mode) |
|||
{ |
|||
/* Arbitrarily estimate 2 interleaved DC scans + 3 AC scans/component. */ |
|||
nscans = 2 + 3 * m_cinfo.m_num_components; |
|||
} |
|||
else |
|||
{ |
|||
/* For a non progressive multiscan file, estimate 1 scan per component. */ |
|||
nscans = m_cinfo.m_num_components; |
|||
} |
|||
|
|||
m_cinfo.m_progress.Pass_counter = 0; |
|||
m_cinfo.m_progress.Pass_limit = m_cinfo.m_total_iMCU_rows * nscans; |
|||
m_cinfo.m_progress.Completed_passes = 0; |
|||
m_cinfo.m_progress.Total_passes = (m_cinfo.m_enable_2pass_quant ? 3 : 2); |
|||
|
|||
/* Count the input pass as done */ |
|||
m_pass_number++; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Allocate and fill in the sample_range_limit table.
|
|||
///
|
|||
/// Several decompression processes need to range-limit values to the range
|
|||
/// 0..MAXJSAMPLE; the input value may fall somewhat outside this range
|
|||
/// due to noise introduced by quantization, roundoff error, etc. These
|
|||
/// processes are inner loops and need to be as fast as possible. On most
|
|||
/// machines, particularly CPUs with pipelines or instruction prefetch,
|
|||
/// a (subscript-check-less) C table lookup
|
|||
/// x = sample_range_limit[x];
|
|||
/// is faster than explicit tests
|
|||
/// <c>
|
|||
/// if (x & 0)
|
|||
/// x = 0;
|
|||
/// else if (x > MAXJSAMPLE)
|
|||
/// x = MAXJSAMPLE;
|
|||
/// </c>
|
|||
/// These processes all use a common table prepared by the routine below.
|
|||
///
|
|||
/// For most steps we can mathematically guarantee that the initial value
|
|||
/// of x is within MAXJSAMPLE + 1 of the legal range, so a table running from
|
|||
/// -(MAXJSAMPLE + 1) to 2 * MAXJSAMPLE + 1 is sufficient. But for the initial
|
|||
/// limiting step (just after the IDCT), a wildly out-of-range value is
|
|||
/// possible if the input data is corrupt. To avoid any chance of indexing
|
|||
/// off the end of memory and getting a bad-pointer trap, we perform the
|
|||
/// post-IDCT limiting thus: <c>x = range_limit[x & MASK];</c>
|
|||
/// where MASK is 2 bits wider than legal sample data, ie 10 bits for 8-bit
|
|||
/// samples. Under normal circumstances this is more than enough range and
|
|||
/// a correct output will be generated; with bogus input data the mask will
|
|||
/// cause wraparound, and we will safely generate a bogus-but-in-range output.
|
|||
/// For the post-IDCT step, we want to convert the data from signed to unsigned
|
|||
/// representation by adding CENTERJSAMPLE at the same time that we limit it.
|
|||
/// So the post-IDCT limiting table ends up looking like this:
|
|||
/// <pre>
|
|||
/// CENTERJSAMPLE, CENTERJSAMPLE + 1, ..., MAXJSAMPLE,
|
|||
/// MAXJSAMPLE (repeat 2 * (MAXJSAMPLE + 1) - CENTERJSAMPLE times),
|
|||
/// 0 (repeat 2 * (MAXJSAMPLE + 1) - CENTERJSAMPLE times),
|
|||
/// 0, 1, ..., CENTERJSAMPLE - 1
|
|||
/// </pre>
|
|||
/// Negative inputs select values from the upper half of the table after
|
|||
/// masking.
|
|||
///
|
|||
/// We can save some space by overlapping the start of the post-IDCT table
|
|||
/// with the simpler range limiting table. The post-IDCT table begins at
|
|||
/// sample_range_limit + CENTERJSAMPLE.
|
|||
///
|
|||
/// Note that the table is allocated in near data space on PCs; it's small
|
|||
/// enough and used often enough to justify this.
|
|||
/// </summary>
|
|||
private void prepare_range_limit_table() |
|||
{ |
|||
byte[] table = new byte[5 * (JpegConstants.MAXJSAMPLE + 1) + JpegConstants.CENTERJSAMPLE]; |
|||
|
|||
/* allow negative subscripts of simple table */ |
|||
int tableOffset = JpegConstants.MAXJSAMPLE + 1; |
|||
m_cinfo.m_sample_range_limit = table; |
|||
m_cinfo.m_sampleRangeLimitOffset = tableOffset; |
|||
|
|||
/* First segment of "simple" table: limit[x] = 0 for x < 0 */ |
|||
Array.Clear(table, 0, JpegConstants.MAXJSAMPLE + 1); |
|||
|
|||
/* Main part of "simple" table: limit[x] = x */ |
|||
for (int i = 0; i <= JpegConstants.MAXJSAMPLE; i++) |
|||
table[tableOffset + i] = (byte) i; |
|||
|
|||
tableOffset += JpegConstants.CENTERJSAMPLE; /* Point to where post-IDCT table starts */ |
|||
|
|||
/* End of simple table, rest of first half of post-IDCT table */ |
|||
for (int i = JpegConstants.CENTERJSAMPLE; i < 2 * (JpegConstants.MAXJSAMPLE + 1); i++) |
|||
table[tableOffset + i] = JpegConstants.MAXJSAMPLE; |
|||
|
|||
/* Second half of post-IDCT table */ |
|||
Array.Clear(table, tableOffset + 2 * (JpegConstants.MAXJSAMPLE + 1), |
|||
2 * (JpegConstants.MAXJSAMPLE + 1) - JpegConstants.CENTERJSAMPLE); |
|||
|
|||
Buffer.BlockCopy(m_cinfo.m_sample_range_limit, 0, table, |
|||
tableOffset + 4 * (JpegConstants.MAXJSAMPLE + 1) - JpegConstants.CENTERJSAMPLE, JpegConstants.CENTERJSAMPLE); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,546 @@ |
|||
/* Copyright (C) 2008-2011, Bit Miracle |
|||
* http://www.bitmiracle.com
|
|||
* |
|||
* Copyright (C) 1994-1996, Thomas G. Lane. |
|||
* This file is part of the Independent JPEG Group's software. |
|||
* For conditions of distribution and use, see the accompanying README file. |
|||
* |
|||
*/ |
|||
|
|||
/* |
|||
* This file contains downsampling routines. |
|||
* |
|||
* Downsampling input data is counted in "row groups". A row group |
|||
* is defined to be max_v_samp_factor pixel rows of each component, |
|||
* from which the downsampler produces v_samp_factor sample rows. |
|||
* A single row group is processed in each call to the downsampler module. |
|||
* |
|||
* The downsampler is responsible for edge-expansion of its output data |
|||
* to fill an integral number of DCT blocks horizontally. The source buffer |
|||
* may be modified if it is helpful for this purpose (the source buffer is |
|||
* allocated wide enough to correspond to the desired output width). |
|||
* The caller (the prep controller) is responsible for vertical padding. |
|||
* |
|||
* The downsampler may request "context rows" by setting need_context_rows |
|||
* during startup. In this case, the input arrays will contain at least |
|||
* one row group's worth of pixels above and below the passed-in data; |
|||
* the caller will create dummy rows at image top and bottom by replicating |
|||
* the first or last real pixel row. |
|||
* |
|||
* An excellent reference for image resampling is |
|||
* Digital Image Warping, George Wolberg, 1990. |
|||
* Pub. by IEEE Computer Society Press, Los Alamitos, CA. ISBN 0-8186-8944-7. |
|||
* |
|||
* The downsampling algorithm used here is a simple average of the source |
|||
* pixels covered by the output pixel. The hi-falutin sampling literature |
|||
* refers to this as a "box filter". In general the characteristics of a box |
|||
* filter are not very good, but for the specific cases we normally use (1:1 |
|||
* and 2:1 ratios) the box is equivalent to a "triangle filter" which is not |
|||
* nearly so bad. If you intend to use other sampling ratios, you'd be well |
|||
* advised to improve this code. |
|||
* |
|||
* A simple input-smoothing capability is provided. This is mainly intended |
|||
* for cleaning up color-dithered GIF input files (if you find it inadequate, |
|||
* we suggest using an external filtering program such as pnmconvol). When |
|||
* enabled, each input pixel P is replaced by a weighted sum of itself and its |
|||
* eight neighbors. P's weight is 1-8*SF and each neighbor's weight is SF, |
|||
* where SF = (smoothing_factor / 1024). |
|||
* Currently, smoothing is only supported for 2h2v sampling factors. |
|||
*/ |
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
|
|||
namespace BitMiracle.LibJpeg.Classic.Internal |
|||
{ |
|||
/// <summary>
|
|||
/// Downsampling
|
|||
/// </summary>
|
|||
class jpeg_downsampler |
|||
{ |
|||
private enum downSampleMethod |
|||
{ |
|||
fullsize_smooth_downsampler, |
|||
fullsize_downsampler, |
|||
h2v1_downsampler, |
|||
h2v2_smooth_downsampler, |
|||
h2v2_downsampler, |
|||
int_downsampler |
|||
}; |
|||
|
|||
/* Downsamplers, one per component */ |
|||
private downSampleMethod[] m_downSamplers = new downSampleMethod[JpegConstants.MAX_COMPONENTS]; |
|||
|
|||
private jpeg_compress_struct m_cinfo; |
|||
private bool m_need_context_rows; /* true if need rows above & below */ |
|||
|
|||
public jpeg_downsampler(jpeg_compress_struct cinfo) |
|||
{ |
|||
m_cinfo = cinfo; |
|||
m_need_context_rows = false; |
|||
|
|||
if (cinfo.m_CCIR601_sampling) |
|||
cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CCIR601_NOTIMPL); |
|||
|
|||
/* Verify we can handle the sampling factors, and set up method pointers */ |
|||
bool smoothok = true; |
|||
for (int ci = 0; ci < cinfo.m_num_components; ci++) |
|||
{ |
|||
jpeg_component_info componentInfo = cinfo.Component_info[ci]; |
|||
|
|||
if (componentInfo.H_samp_factor == cinfo.m_max_h_samp_factor && |
|||
componentInfo.V_samp_factor == cinfo.m_max_v_samp_factor) |
|||
{ |
|||
if (cinfo.m_smoothing_factor != 0) |
|||
{ |
|||
m_downSamplers[ci] = downSampleMethod.fullsize_smooth_downsampler; |
|||
m_need_context_rows = true; |
|||
} |
|||
else |
|||
{ |
|||
m_downSamplers[ci] = downSampleMethod.fullsize_downsampler; |
|||
} |
|||
} |
|||
else if (componentInfo.H_samp_factor * 2 == cinfo.m_max_h_samp_factor && |
|||
componentInfo.V_samp_factor == cinfo.m_max_v_samp_factor) |
|||
{ |
|||
smoothok = false; |
|||
m_downSamplers[ci] = downSampleMethod.h2v1_downsampler; |
|||
} |
|||
else if (componentInfo.H_samp_factor * 2 == cinfo.m_max_h_samp_factor && |
|||
componentInfo.V_samp_factor * 2 == cinfo.m_max_v_samp_factor) |
|||
{ |
|||
if (cinfo.m_smoothing_factor != 0) |
|||
{ |
|||
m_downSamplers[ci] = downSampleMethod.h2v2_smooth_downsampler; |
|||
m_need_context_rows = true; |
|||
} |
|||
else |
|||
{ |
|||
m_downSamplers[ci] = downSampleMethod.h2v2_downsampler; |
|||
} |
|||
} |
|||
else if ((cinfo.m_max_h_samp_factor % componentInfo.H_samp_factor) == 0 && |
|||
(cinfo.m_max_v_samp_factor % componentInfo.V_samp_factor) == 0) |
|||
{ |
|||
smoothok = false; |
|||
m_downSamplers[ci] = downSampleMethod.int_downsampler; |
|||
} |
|||
else |
|||
cinfo.ERREXIT(J_MESSAGE_CODE.JERR_FRACT_SAMPLE_NOTIMPL); |
|||
} |
|||
|
|||
if (cinfo.m_smoothing_factor != 0 && !smoothok) |
|||
cinfo.TRACEMS(0, J_MESSAGE_CODE.JTRC_SMOOTH_NOTIMPL); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Do downsampling for a whole row group (all components).
|
|||
///
|
|||
/// In this version we simply downsample each component independently.
|
|||
/// </summary>
|
|||
public void downsample(byte[][][] input_buf, int in_row_index, byte[][][] output_buf, int out_row_group_index) |
|||
{ |
|||
for (int ci = 0; ci < m_cinfo.m_num_components; ci++) |
|||
{ |
|||
int outIndex = out_row_group_index * m_cinfo.Component_info[ci].V_samp_factor; |
|||
switch (m_downSamplers[ci]) |
|||
{ |
|||
case downSampleMethod.fullsize_smooth_downsampler: |
|||
fullsize_smooth_downsample(ci, input_buf[ci], in_row_index, output_buf[ci], outIndex); |
|||
break; |
|||
|
|||
case downSampleMethod.fullsize_downsampler: |
|||
fullsize_downsample(ci, input_buf[ci], in_row_index, output_buf[ci], outIndex); |
|||
break; |
|||
|
|||
case downSampleMethod.h2v1_downsampler: |
|||
h2v1_downsample(ci, input_buf[ci], in_row_index, output_buf[ci], outIndex); |
|||
break; |
|||
|
|||
case downSampleMethod.h2v2_smooth_downsampler: |
|||
h2v2_smooth_downsample(ci, input_buf[ci], in_row_index, output_buf[ci], outIndex); |
|||
break; |
|||
|
|||
case downSampleMethod.h2v2_downsampler: |
|||
h2v2_downsample(ci, input_buf[ci], in_row_index, output_buf[ci], outIndex); |
|||
break; |
|||
|
|||
case downSampleMethod.int_downsampler: |
|||
int_downsample(ci, input_buf[ci], in_row_index, output_buf[ci], outIndex); |
|||
break; |
|||
}; |
|||
} |
|||
} |
|||
|
|||
public bool NeedContextRows() |
|||
{ |
|||
return m_need_context_rows; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Downsample pixel values of a single component.
|
|||
/// One row group is processed per call.
|
|||
/// This version handles arbitrary integral sampling ratios, without smoothing.
|
|||
/// Note that this version is not actually used for customary sampling ratios.
|
|||
/// </summary>
|
|||
private void int_downsample(int componentIndex, byte[][] input_data, int startInputRow, byte[][] output_data, int startOutRow) |
|||
{ |
|||
/* Expand input data enough to let all the output samples be generated |
|||
* by the standard loop. Special-casing padded output would be more |
|||
* efficient. |
|||
*/ |
|||
int output_cols = m_cinfo.Component_info[componentIndex].Width_in_blocks * JpegConstants.DCTSIZE; |
|||
int h_expand = m_cinfo.m_max_h_samp_factor / m_cinfo.Component_info[componentIndex].H_samp_factor; |
|||
expand_right_edge(input_data, startInputRow, m_cinfo.m_max_v_samp_factor, m_cinfo.m_image_width, output_cols * h_expand); |
|||
|
|||
int v_expand = m_cinfo.m_max_v_samp_factor / m_cinfo.Component_info[componentIndex].V_samp_factor; |
|||
int numpix = h_expand * v_expand; |
|||
int numpix2 = numpix / 2; |
|||
int inrow = 0; |
|||
for (int outrow = 0; outrow < m_cinfo.Component_info[componentIndex].V_samp_factor; outrow++) |
|||
{ |
|||
for (int outcol = 0, outcol_h = 0; outcol < output_cols; outcol++, outcol_h += h_expand) |
|||
{ |
|||
int outvalue = 0; |
|||
for (int v = 0; v < v_expand; v++) |
|||
{ |
|||
for (int h = 0; h < h_expand; h++) |
|||
outvalue += input_data[startInputRow + inrow + v][outcol_h + h]; |
|||
} |
|||
|
|||
output_data[startOutRow + outrow][outcol] = (byte)((outvalue + numpix2) / numpix); |
|||
} |
|||
|
|||
inrow += v_expand; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Downsample pixel values of a single component.
|
|||
/// This version handles the special case of a full-size component,
|
|||
/// without smoothing.
|
|||
/// </summary>
|
|||
private void fullsize_downsample(int componentIndex, byte[][] input_data, int startInputRow, byte[][] output_data, int startOutRow) |
|||
{ |
|||
/* Copy the data */ |
|||
JpegUtils.jcopy_sample_rows(input_data, startInputRow, output_data, startOutRow, m_cinfo.m_max_v_samp_factor, m_cinfo.m_image_width); |
|||
|
|||
/* Edge-expand */ |
|||
expand_right_edge(output_data, startOutRow, m_cinfo.m_max_v_samp_factor, m_cinfo.m_image_width, m_cinfo.Component_info[componentIndex].Width_in_blocks * JpegConstants.DCTSIZE); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Downsample pixel values of a single component.
|
|||
/// This version handles the common case of 2:1 horizontal and 1:1 vertical,
|
|||
/// without smoothing.
|
|||
///
|
|||
/// A note about the "bias" calculations: when rounding fractional values to
|
|||
/// integer, we do not want to always round 0.5 up to the next integer.
|
|||
/// If we did that, we'd introduce a noticeable bias towards larger values.
|
|||
/// Instead, this code is arranged so that 0.5 will be rounded up or down at
|
|||
/// alternate pixel locations (a simple ordered dither pattern).
|
|||
/// </summary>
|
|||
private void h2v1_downsample(int componentIndex, byte[][] input_data, int startInputRow, byte[][] output_data, int startOutRow) |
|||
{ |
|||
/* Expand input data enough to let all the output samples be generated |
|||
* by the standard loop. Special-casing padded output would be more |
|||
* efficient. |
|||
*/ |
|||
int output_cols = m_cinfo.Component_info[componentIndex].Width_in_blocks * JpegConstants.DCTSIZE; |
|||
expand_right_edge(input_data, startInputRow, m_cinfo.m_max_v_samp_factor, m_cinfo.m_image_width, output_cols * 2); |
|||
|
|||
for (int outrow = 0; outrow < m_cinfo.Component_info[componentIndex].V_samp_factor; outrow++) |
|||
{ |
|||
/* bias = 0,1,0,1,... for successive samples */ |
|||
int bias = 0; |
|||
int inputColumn = 0; |
|||
for (int outcol = 0; outcol < output_cols; outcol++) |
|||
{ |
|||
output_data[startOutRow + outrow][outcol] = (byte)( |
|||
((int)input_data[startInputRow + outrow][inputColumn] + |
|||
(int)input_data[startInputRow + outrow][inputColumn + 1] + bias) >> 1); |
|||
|
|||
bias ^= 1; /* 0=>1, 1=>0 */ |
|||
inputColumn += 2; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Downsample pixel values of a single component.
|
|||
/// This version handles the standard case of 2:1 horizontal and 2:1 vertical,
|
|||
/// without smoothing.
|
|||
/// </summary>
|
|||
private void h2v2_downsample(int componentIndex, byte[][] input_data, int startInputRow, byte[][] output_data, int startOutRow) |
|||
{ |
|||
/* Expand input data enough to let all the output samples be generated |
|||
* by the standard loop. Special-casing padded output would be more |
|||
* efficient. |
|||
*/ |
|||
int output_cols = m_cinfo.Component_info[componentIndex].Width_in_blocks * JpegConstants.DCTSIZE; |
|||
expand_right_edge(input_data, startInputRow, m_cinfo.m_max_v_samp_factor, m_cinfo.m_image_width, output_cols * 2); |
|||
|
|||
int inrow = 0; |
|||
for (int outrow = 0; outrow < m_cinfo.Component_info[componentIndex].V_samp_factor; outrow++) |
|||
{ |
|||
/* bias = 1,2,1,2,... for successive samples */ |
|||
int bias = 1; |
|||
int inputColumn = 0; |
|||
for (int outcol = 0; outcol < output_cols; outcol++) |
|||
{ |
|||
output_data[startOutRow + outrow][outcol] = (byte)(( |
|||
(int)input_data[startInputRow + inrow][inputColumn] + |
|||
(int)input_data[startInputRow + inrow][inputColumn + 1] + |
|||
(int)input_data[startInputRow + inrow + 1][inputColumn] + |
|||
(int)input_data[startInputRow + inrow + 1][inputColumn + 1] + bias) >> 2); |
|||
|
|||
bias ^= 3; /* 1=>2, 2=>1 */ |
|||
inputColumn += 2; |
|||
} |
|||
|
|||
inrow += 2; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Downsample pixel values of a single component.
|
|||
/// This version handles the standard case of 2:1 horizontal and 2:1 vertical,
|
|||
/// with smoothing. One row of context is required.
|
|||
/// </summary>
|
|||
private void h2v2_smooth_downsample(int componentIndex, byte[][] input_data, int startInputRow, byte[][] output_data, int startOutRow) |
|||
{ |
|||
/* Expand input data enough to let all the output samples be generated |
|||
* by the standard loop. Special-casing padded output would be more |
|||
* efficient. |
|||
*/ |
|||
int output_cols = m_cinfo.Component_info[componentIndex].Width_in_blocks * JpegConstants.DCTSIZE; |
|||
expand_right_edge(input_data, startInputRow - 1, m_cinfo.m_max_v_samp_factor + 2, m_cinfo.m_image_width, output_cols * 2); |
|||
|
|||
/* We don't bother to form the individual "smoothed" input pixel values; |
|||
* we can directly compute the output which is the average of the four |
|||
* smoothed values. Each of the four member pixels contributes a fraction |
|||
* (1-8*SF) to its own smoothed image and a fraction SF to each of the three |
|||
* other smoothed pixels, therefore a total fraction (1-5*SF)/4 to the final |
|||
* output. The four corner-adjacent neighbor pixels contribute a fraction |
|||
* SF to just one smoothed pixel, or SF/4 to the final output; while the |
|||
* eight edge-adjacent neighbors contribute SF to each of two smoothed |
|||
* pixels, or SF/2 overall. In order to use integer arithmetic, these |
|||
* factors are scaled by 2^16 = 65536. |
|||
* Also recall that SF = smoothing_factor / 1024. |
|||
*/ |
|||
|
|||
int memberscale = 16384 - m_cinfo.m_smoothing_factor * 80; /* scaled (1-5*SF)/4 */ |
|||
int neighscale = m_cinfo.m_smoothing_factor * 16; /* scaled SF/4 */ |
|||
|
|||
for (int inrow = 0, outrow = 0; outrow < m_cinfo.Component_info[componentIndex].V_samp_factor; outrow++) |
|||
{ |
|||
int outIndex = 0; |
|||
int inIndex0 = 0; |
|||
int inIndex1 = 0; |
|||
int aboveIndex = 0; |
|||
int belowIndex = 0; |
|||
|
|||
/* Special case for first column: pretend column -1 is same as column 0 */ |
|||
int membersum = input_data[startInputRow + inrow][inIndex0] + |
|||
input_data[startInputRow + inrow][inIndex0 + 1] + |
|||
input_data[startInputRow + inrow + 1][inIndex1] + |
|||
input_data[startInputRow + inrow + 1][inIndex1 + 1]; |
|||
|
|||
int neighsum = input_data[startInputRow + inrow - 1][aboveIndex] + |
|||
input_data[startInputRow + inrow - 1][aboveIndex + 1] + |
|||
input_data[startInputRow + inrow + 2][belowIndex] + |
|||
input_data[startInputRow + inrow + 2][belowIndex + 1] + |
|||
input_data[startInputRow + inrow][inIndex0] + |
|||
input_data[startInputRow + inrow][inIndex0 + 2] + |
|||
input_data[startInputRow + inrow + 1][inIndex1] + |
|||
input_data[startInputRow + inrow + 1][inIndex1 + 2]; |
|||
|
|||
neighsum += neighsum; |
|||
neighsum += input_data[startInputRow + inrow - 1][aboveIndex] + |
|||
input_data[startInputRow + inrow - 1][aboveIndex + 2] + |
|||
input_data[startInputRow + inrow + 2][belowIndex] + |
|||
input_data[startInputRow + inrow + 2][belowIndex + 2]; |
|||
|
|||
membersum = membersum * memberscale + neighsum * neighscale; |
|||
output_data[startOutRow + outrow][outIndex] = (byte)((membersum + 32768) >> 16); |
|||
outIndex++; |
|||
|
|||
inIndex0 += 2; |
|||
inIndex1 += 2; |
|||
aboveIndex += 2; |
|||
belowIndex += 2; |
|||
|
|||
for (int colctr = output_cols - 2; colctr > 0; colctr--) |
|||
{ |
|||
/* sum of pixels directly mapped to this output element */ |
|||
membersum = input_data[startInputRow + inrow][inIndex0] + |
|||
input_data[startInputRow + inrow][inIndex0 + 1] + |
|||
input_data[startInputRow + inrow + 1][inIndex1] + |
|||
input_data[startInputRow + inrow + 1][inIndex1 + 1]; |
|||
|
|||
/* sum of edge-neighbor pixels */ |
|||
neighsum = input_data[startInputRow + inrow - 1][aboveIndex] + |
|||
input_data[startInputRow + inrow - 1][aboveIndex + 1] + |
|||
input_data[startInputRow + inrow + 2][belowIndex] + |
|||
input_data[startInputRow + inrow + 2][belowIndex + 1] + |
|||
input_data[startInputRow + inrow][inIndex0 - 1] + |
|||
input_data[startInputRow + inrow][inIndex0 + 2] + |
|||
input_data[startInputRow + inrow + 1][inIndex1 - 1] + |
|||
input_data[startInputRow + inrow + 1][inIndex1 + 2]; |
|||
|
|||
/* The edge-neighbors count twice as much as corner-neighbors */ |
|||
neighsum += neighsum; |
|||
|
|||
/* Add in the corner-neighbors */ |
|||
neighsum += input_data[startInputRow + inrow - 1][aboveIndex - 1] + |
|||
input_data[startInputRow + inrow - 1][aboveIndex + 2] + |
|||
input_data[startInputRow + inrow + 2][belowIndex - 1] + |
|||
input_data[startInputRow + inrow + 2][belowIndex + 2]; |
|||
|
|||
/* form final output scaled up by 2^16 */ |
|||
membersum = membersum * memberscale + neighsum * neighscale; |
|||
|
|||
/* round, descale and output it */ |
|||
output_data[startOutRow + outrow][outIndex] = (byte)((membersum + 32768) >> 16); |
|||
outIndex++; |
|||
|
|||
inIndex0 += 2; |
|||
inIndex1 += 2; |
|||
aboveIndex += 2; |
|||
belowIndex += 2; |
|||
} |
|||
|
|||
/* Special case for last column */ |
|||
membersum = input_data[startInputRow + inrow][inIndex0] + |
|||
input_data[startInputRow + inrow][inIndex0 + 1] + |
|||
input_data[startInputRow + inrow + 1][inIndex1] + |
|||
input_data[startInputRow + inrow + 1][inIndex1 + 1]; |
|||
|
|||
neighsum = input_data[startInputRow + inrow - 1][aboveIndex] + |
|||
input_data[startInputRow + inrow - 1][aboveIndex + 1] + |
|||
input_data[startInputRow + inrow + 2][belowIndex] + |
|||
input_data[startInputRow + inrow + 2][belowIndex + 1] + |
|||
input_data[startInputRow + inrow][inIndex0 - 1] + |
|||
input_data[startInputRow + inrow][inIndex0 + 1] + |
|||
input_data[startInputRow + inrow + 1][inIndex1 - 1] + |
|||
input_data[startInputRow + inrow + 1][inIndex1 + 1]; |
|||
|
|||
neighsum += neighsum; |
|||
neighsum += input_data[startInputRow + inrow - 1][aboveIndex - 1] + |
|||
input_data[startInputRow + inrow - 1][aboveIndex + 1] + |
|||
input_data[startInputRow + inrow + 2][belowIndex - 1] + |
|||
input_data[startInputRow + inrow + 2][belowIndex + 1]; |
|||
|
|||
membersum = membersum * memberscale + neighsum * neighscale; |
|||
output_data[startOutRow + outrow][outIndex] = (byte)((membersum + 32768) >> 16); |
|||
|
|||
inrow += 2; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Downsample pixel values of a single component.
|
|||
/// This version handles the special case of a full-size component,
|
|||
/// with smoothing. One row of context is required.
|
|||
/// </summary>
|
|||
private void fullsize_smooth_downsample(int componentIndex, byte[][] input_data, int startInputRow, byte[][] output_data, int startOutRow) |
|||
{ |
|||
/* Expand input data enough to let all the output samples be generated |
|||
* by the standard loop. Special-casing padded output would be more |
|||
* efficient. |
|||
*/ |
|||
int output_cols = m_cinfo.Component_info[componentIndex].Width_in_blocks * JpegConstants.DCTSIZE; |
|||
expand_right_edge(input_data, startInputRow - 1, m_cinfo.m_max_v_samp_factor + 2, m_cinfo.m_image_width, output_cols); |
|||
|
|||
/* Each of the eight neighbor pixels contributes a fraction SF to the |
|||
* smoothed pixel, while the main pixel contributes (1-8*SF). In order |
|||
* to use integer arithmetic, these factors are multiplied by 2^16 = 65536. |
|||
* Also recall that SF = smoothing_factor / 1024. |
|||
*/ |
|||
|
|||
int memberscale = 65536 - m_cinfo.m_smoothing_factor * 512; /* scaled 1-8*SF */ |
|||
int neighscale = m_cinfo.m_smoothing_factor * 64; /* scaled SF */ |
|||
|
|||
for (int outrow = 0; outrow < m_cinfo.Component_info[componentIndex].V_samp_factor; outrow++) |
|||
{ |
|||
int outIndex = 0; |
|||
int inIndex = 0; |
|||
int aboveIndex = 0; |
|||
int belowIndex = 0; |
|||
|
|||
/* Special case for first column */ |
|||
int colsum = input_data[startInputRow + outrow - 1][aboveIndex] + |
|||
input_data[startInputRow + outrow + 1][belowIndex] + |
|||
input_data[startInputRow + outrow][inIndex]; |
|||
|
|||
aboveIndex++; |
|||
belowIndex++; |
|||
|
|||
int membersum = input_data[startInputRow + outrow][inIndex]; |
|||
inIndex++; |
|||
|
|||
int nextcolsum = input_data[startInputRow + outrow - 1][aboveIndex] + |
|||
input_data[startInputRow + outrow + 1][belowIndex] + |
|||
input_data[startInputRow + outrow][inIndex]; |
|||
|
|||
int neighsum = colsum + (colsum - membersum) + nextcolsum; |
|||
|
|||
membersum = membersum * memberscale + neighsum * neighscale; |
|||
output_data[startOutRow + outrow][outIndex] = (byte)((membersum + 32768) >> 16); |
|||
outIndex++; |
|||
|
|||
int lastcolsum = colsum; |
|||
colsum = nextcolsum; |
|||
|
|||
for (int colctr = output_cols - 2; colctr > 0; colctr--) |
|||
{ |
|||
membersum = input_data[startInputRow + outrow][inIndex]; |
|||
|
|||
inIndex++; |
|||
aboveIndex++; |
|||
belowIndex++; |
|||
|
|||
nextcolsum = input_data[startInputRow + outrow - 1][aboveIndex] + |
|||
input_data[startInputRow + outrow + 1][belowIndex] + |
|||
input_data[startInputRow + outrow][inIndex]; |
|||
|
|||
neighsum = lastcolsum + (colsum - membersum) + nextcolsum; |
|||
membersum = membersum * memberscale + neighsum * neighscale; |
|||
|
|||
output_data[startOutRow + outrow][outIndex] = (byte)((membersum + 32768) >> 16); |
|||
outIndex++; |
|||
|
|||
lastcolsum = colsum; |
|||
colsum = nextcolsum; |
|||
} |
|||
|
|||
/* Special case for last column */ |
|||
membersum = input_data[startInputRow + outrow][inIndex]; |
|||
neighsum = lastcolsum + (colsum - membersum) + colsum; |
|||
membersum = membersum * memberscale + neighsum * neighscale; |
|||
output_data[startOutRow + outrow][outIndex] = (byte)((membersum + 32768) >> 16); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Expand a component horizontally from width input_cols to width output_cols,
|
|||
/// by duplicating the rightmost samples.
|
|||
/// </summary>
|
|||
private static void expand_right_edge(byte[][] image_data, int startInputRow, int num_rows, int input_cols, int output_cols) |
|||
{ |
|||
int numcols = output_cols - input_cols; |
|||
if (numcols > 0) |
|||
{ |
|||
for (int row = startInputRow; row < (startInputRow + num_rows); row++) |
|||
{ |
|||
/* don't need GETJSAMPLE() here */ |
|||
byte pixval = image_data[row][input_cols - 1]; |
|||
for (int count = 0; count < numcols; count++) |
|||
image_data[row][input_cols + count] = pixval; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,473 @@ |
|||
/* Copyright (C) 2008-2011, Bit Miracle |
|||
* http://www.bitmiracle.com
|
|||
* |
|||
* Copyright (C) 1994-1996, Thomas G. Lane. |
|||
* This file is part of the Independent JPEG Group's software. |
|||
* For conditions of distribution and use, see the accompanying README file. |
|||
* |
|||
*/ |
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
|
|||
namespace BitMiracle.LibJpeg.Classic.Internal |
|||
{ |
|||
/// <summary>
|
|||
/// Entropy decoding
|
|||
/// </summary>
|
|||
abstract class jpeg_entropy_decoder |
|||
{ |
|||
// Figure F.12: extend sign bit.
|
|||
// entry n is 2**(n-1)
|
|||
private static int[] extend_test = |
|||
{ |
|||
0, 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, |
|||
0x0040, 0x0080, 0x0100, 0x0200, 0x0400, 0x0800, |
|||
0x1000, 0x2000, 0x4000 |
|||
}; |
|||
|
|||
// entry n is (-1 << n) + 1
|
|||
private static int[] extend_offset = |
|||
{ |
|||
0, (-1 << 1) + 1, (-1 << 2) + 1, |
|||
(-1 << 3) + 1, (-1 << 4) + 1, (-1 << 5) + 1, |
|||
(-1 << 6) + 1, (-1 << 7) + 1, (-1 << 8) + 1, |
|||
(-1 << 9) + 1, (-1 << 10) + 1, |
|||
(-1 << 11) + 1, (-1 << 12) + 1, |
|||
(-1 << 13) + 1, (-1 << 14) + 1, |
|||
(-1 << 15) + 1 |
|||
}; |
|||
|
|||
/* Fetching the next N bits from the input stream is a time-critical operation |
|||
* for the Huffman decoders. We implement it with a combination of inline |
|||
* macros and out-of-line subroutines. Note that N (the number of bits |
|||
* demanded at one time) never exceeds 15 for JPEG use. |
|||
* |
|||
* We read source bytes into get_buffer and dole out bits as needed. |
|||
* If get_buffer already contains enough bits, they are fetched in-line |
|||
* by the macros CHECK_BIT_BUFFER and GET_BITS. When there aren't enough |
|||
* bits, jpeg_fill_bit_buffer is called; it will attempt to fill get_buffer |
|||
* as full as possible (not just to the number of bits needed; this |
|||
* prefetching reduces the overhead cost of calling jpeg_fill_bit_buffer). |
|||
* Note that jpeg_fill_bit_buffer may return false to indicate suspension. |
|||
* On true return, jpeg_fill_bit_buffer guarantees that get_buffer contains |
|||
* at least the requested number of bits --- dummy zeroes are inserted if |
|||
* necessary. |
|||
*/ |
|||
protected const int BIT_BUF_SIZE = 32; /* size of buffer in bits */ |
|||
|
|||
/* |
|||
* Out-of-line code for bit fetching (shared with jdphuff.c). |
|||
* See jdhuff.h for info about usage. |
|||
* Note: current values of get_buffer and bits_left are passed as parameters, |
|||
* but are returned in the corresponding fields of the state struct. |
|||
* |
|||
* On most machines MIN_GET_BITS should be 25 to allow the full 32-bit width |
|||
* of get_buffer to be used. (On machines with wider words, an even larger |
|||
* buffer could be used.) However, on some machines 32-bit shifts are |
|||
* quite slow and take time proportional to the number of places shifted. |
|||
* (This is true with most PC compilers, for instance.) In this case it may |
|||
* be a win to set MIN_GET_BITS to the minimum value of 15. This reduces the |
|||
* average shift distance at the cost of more calls to jpeg_fill_bit_buffer. |
|||
*/ |
|||
|
|||
protected const int MIN_GET_BITS = BIT_BUF_SIZE - 7; |
|||
|
|||
protected jpeg_decompress_struct m_cinfo; |
|||
|
|||
/* This is here to share code between baseline and progressive decoders; */ |
|||
/* other modules probably should not use it */ |
|||
protected bool m_insufficient_data; /* set true after emitting warning */ |
|||
|
|||
public abstract void start_pass(); |
|||
public abstract bool decode_mcu(JBLOCK[] MCU_data); |
|||
|
|||
protected static int HUFF_EXTEND(int x, int s) |
|||
{ |
|||
return ((x) < extend_test[s] ? (x) + extend_offset[s] : (x)); |
|||
} |
|||
|
|||
protected void BITREAD_LOAD_STATE(bitread_perm_state bitstate, out int get_buffer, out int bits_left, ref bitread_working_state br_state) |
|||
{ |
|||
br_state.cinfo = m_cinfo; |
|||
get_buffer = bitstate.get_buffer; |
|||
bits_left = bitstate.bits_left; |
|||
} |
|||
|
|||
protected static void BITREAD_SAVE_STATE(ref bitread_perm_state bitstate, int get_buffer, int bits_left) |
|||
{ |
|||
bitstate.get_buffer = get_buffer; |
|||
bitstate.bits_left = bits_left; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Expand a Huffman table definition into the derived format
|
|||
/// This routine also performs some validation checks on the table.
|
|||
/// </summary>
|
|||
protected void jpeg_make_d_derived_tbl(bool isDC, int tblno, ref d_derived_tbl dtbl) |
|||
{ |
|||
/* Note that huffsize[] and huffcode[] are filled in code-length order, |
|||
* paralleling the order of the symbols themselves in htbl.huffval[]. |
|||
*/ |
|||
|
|||
/* Find the input Huffman table */ |
|||
if (tblno < 0 || tblno >= JpegConstants.NUM_HUFF_TBLS) |
|||
m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_NO_HUFF_TABLE, tblno); |
|||
|
|||
JHUFF_TBL htbl = isDC ? m_cinfo.m_dc_huff_tbl_ptrs[tblno] : m_cinfo.m_ac_huff_tbl_ptrs[tblno]; |
|||
if (htbl == null) |
|||
m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_NO_HUFF_TABLE, tblno); |
|||
|
|||
/* Allocate a workspace if we haven't already done so. */ |
|||
if (dtbl == null) |
|||
dtbl = new d_derived_tbl(); |
|||
|
|||
dtbl.pub = htbl; /* fill in back link */ |
|||
|
|||
/* Figure C.1: make table of Huffman code length for each symbol */ |
|||
|
|||
int p = 0; |
|||
char[] huffsize = new char[257]; |
|||
for (int l = 1; l <= 16; l++) |
|||
{ |
|||
int i = htbl.Bits[l]; |
|||
if (i < 0 || p + i> 256) /* protect against table overrun */ |
|||
m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_HUFF_TABLE); |
|||
|
|||
while ((i--) != 0) |
|||
huffsize[p++] = (char) l; |
|||
} |
|||
huffsize[p] = (char)0; |
|||
int numsymbols = p; |
|||
|
|||
/* Figure C.2: generate the codes themselves */ |
|||
/* We also validate that the counts represent a legal Huffman code tree. */ |
|||
|
|||
int code = 0; |
|||
int si = huffsize[0]; |
|||
int[] huffcode = new int[257]; |
|||
p = 0; |
|||
while (huffsize[p] != 0) |
|||
{ |
|||
while (((int)huffsize[p]) == si) |
|||
{ |
|||
huffcode[p++] = code; |
|||
code++; |
|||
} |
|||
|
|||
/* code is now 1 more than the last code used for codelength si; but |
|||
* it must still fit in si bits, since no code is allowed to be all ones. |
|||
*/ |
|||
if (code >= (1 << si)) |
|||
m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_HUFF_TABLE); |
|||
code <<= 1; |
|||
si++; |
|||
} |
|||
|
|||
/* Figure F.15: generate decoding tables for bit-sequential decoding */ |
|||
|
|||
p = 0; |
|||
for (int l = 1; l <= 16; l++) |
|||
{ |
|||
if (htbl.Bits[l] != 0) |
|||
{ |
|||
/* valoffset[l] = huffval[] index of 1st symbol of code length l, |
|||
* minus the minimum code of length l |
|||
*/ |
|||
dtbl.valoffset[l] = p - huffcode[p]; |
|||
p += htbl.Bits[l]; |
|||
dtbl.maxcode[l] = huffcode[p - 1]; /* maximum code of length l */ |
|||
} |
|||
else |
|||
{ |
|||
/* -1 if no codes of this length */ |
|||
dtbl.maxcode[l] = -1; |
|||
} |
|||
} |
|||
dtbl.maxcode[17] = 0xFFFFF; /* ensures jpeg_huff_decode terminates */ |
|||
|
|||
/* Compute lookahead tables to speed up decoding. |
|||
* First we set all the table entries to 0, indicating "too long"; |
|||
* then we iterate through the Huffman codes that are short enough and |
|||
* fill in all the entries that correspond to bit sequences starting |
|||
* with that code. |
|||
*/ |
|||
|
|||
Array.Clear(dtbl.look_nbits, 0, dtbl.look_nbits.Length); |
|||
p = 0; |
|||
for (int l = 1; l <= JpegConstants.HUFF_LOOKAHEAD; l++) |
|||
{ |
|||
for (int i = 1; i <= htbl.Bits[l]; i++, p++) |
|||
{ |
|||
/* l = current code's length, p = its index in huffcode[] & huffval[]. */ |
|||
/* Generate left-justified code followed by all possible bit sequences */ |
|||
int lookbits = huffcode[p] << (JpegConstants.HUFF_LOOKAHEAD - l); |
|||
for (int ctr = 1 << (JpegConstants.HUFF_LOOKAHEAD - l); ctr > 0; ctr--) |
|||
{ |
|||
dtbl.look_nbits[lookbits] = l; |
|||
dtbl.look_sym[lookbits] = htbl.Huffval[p]; |
|||
lookbits++; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/* Validate symbols as being reasonable. |
|||
* For AC tables, we make no check, but accept all byte values 0..255. |
|||
* For DC tables, we require the symbols to be in range 0..15. |
|||
* (Tighter bounds could be applied depending on the data depth and mode, |
|||
* but this is sufficient to ensure safe decoding.) |
|||
*/ |
|||
if (isDC) |
|||
{ |
|||
for (int i = 0; i < numsymbols; i++) |
|||
{ |
|||
int sym = htbl.Huffval[i]; |
|||
if (sym < 0 || sym> 15) |
|||
m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_HUFF_TABLE); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/* |
|||
* These methods provide the in-line portion of bit fetching. |
|||
* Use CHECK_BIT_BUFFER to ensure there are N bits in get_buffer |
|||
* before using GET_BITS, PEEK_BITS, or DROP_BITS. |
|||
* The variables get_buffer and bits_left are assumed to be locals, |
|||
* but the state struct might not be (jpeg_huff_decode needs this). |
|||
* CHECK_BIT_BUFFER(state,n,action); |
|||
* Ensure there are N bits in get_buffer; if suspend, take action. |
|||
* val = GET_BITS(n); |
|||
* Fetch next N bits. |
|||
* val = PEEK_BITS(n); |
|||
* Fetch next N bits without removing them from the buffer. |
|||
* DROP_BITS(n); |
|||
* Discard next N bits. |
|||
* The value N should be a simple variable, not an expression, because it |
|||
* is evaluated multiple times. |
|||
*/ |
|||
|
|||
protected static bool CHECK_BIT_BUFFER(ref bitread_working_state state, int nbits, ref int get_buffer, ref int bits_left) |
|||
{ |
|||
if (bits_left < nbits) |
|||
{ |
|||
if (!jpeg_fill_bit_buffer(ref state, get_buffer, bits_left, nbits)) |
|||
return false; |
|||
|
|||
get_buffer = state.get_buffer; |
|||
bits_left = state.bits_left; |
|||
} |
|||
|
|||
return true; |
|||
} |
|||
|
|||
protected static int GET_BITS(int nbits, int get_buffer, ref int bits_left) |
|||
{ |
|||
return (((int)(get_buffer >> (bits_left -= nbits))) & ((1 << nbits) - 1)); |
|||
} |
|||
|
|||
protected static int PEEK_BITS(int nbits, int get_buffer, int bits_left) |
|||
{ |
|||
return (((int)(get_buffer >> (bits_left - nbits))) & ((1 << nbits) - 1)); |
|||
} |
|||
|
|||
protected static void DROP_BITS(int nbits, ref int bits_left) |
|||
{ |
|||
bits_left -= nbits; |
|||
} |
|||
|
|||
/* Load up the bit buffer to a depth of at least nbits */ |
|||
protected static bool jpeg_fill_bit_buffer(ref bitread_working_state state, int get_buffer, int bits_left, int nbits) |
|||
{ |
|||
/* Attempt to load at least MIN_GET_BITS bits into get_buffer. */ |
|||
/* (It is assumed that no request will be for more than that many bits.) */ |
|||
/* We fail to do so only if we hit a marker or are forced to suspend. */ |
|||
|
|||
bool noMoreBytes = false; |
|||
|
|||
if (state.cinfo.m_unread_marker == 0) |
|||
{ |
|||
/* cannot advance past a marker */ |
|||
while (bits_left < MIN_GET_BITS) |
|||
{ |
|||
int c; |
|||
state.cinfo.m_src.GetByte(out c); |
|||
|
|||
/* If it's 0xFF, check and discard stuffed zero byte */ |
|||
if (c == 0xFF) |
|||
{ |
|||
/* Loop here to discard any padding FF's on terminating marker, |
|||
* so that we can save a valid unread_marker value. NOTE: we will |
|||
* accept multiple FF's followed by a 0 as meaning a single FF data |
|||
* byte. This data pattern is not valid according to the standard. |
|||
*/ |
|||
do |
|||
{ |
|||
state.cinfo.m_src.GetByte(out c); |
|||
} |
|||
while (c == 0xFF); |
|||
|
|||
if (c == 0) |
|||
{ |
|||
/* Found FF/00, which represents an FF data byte */ |
|||
c = 0xFF; |
|||
} |
|||
else |
|||
{ |
|||
/* Oops, it's actually a marker indicating end of compressed data. |
|||
* Save the marker code for later use. |
|||
* Fine point: it might appear that we should save the marker into |
|||
* bitread working state, not straight into permanent state. But |
|||
* once we have hit a marker, we cannot need to suspend within the |
|||
* current MCU, because we will read no more bytes from the data |
|||
* source. So it is OK to update permanent state right away. |
|||
*/ |
|||
state.cinfo.m_unread_marker = c; |
|||
/* See if we need to insert some fake zero bits. */ |
|||
noMoreBytes = true; |
|||
break; |
|||
} |
|||
} |
|||
|
|||
/* OK, load c into get_buffer */ |
|||
get_buffer = (get_buffer << 8) | c; |
|||
bits_left += 8; |
|||
} /* end while */ |
|||
} |
|||
else |
|||
noMoreBytes = true; |
|||
|
|||
if (noMoreBytes) |
|||
{ |
|||
/* We get here if we've read the marker that terminates the compressed |
|||
* data segment. There should be enough bits in the buffer register |
|||
* to satisfy the request; if so, no problem. |
|||
*/ |
|||
if (nbits > bits_left) |
|||
{ |
|||
/* Uh-oh. Report corrupted data to user and stuff zeroes into |
|||
* the data stream, so that we can produce some kind of image. |
|||
* We use a nonvolatile flag to ensure that only one warning message |
|||
* appears per data segment. |
|||
*/ |
|||
if (!state.cinfo.m_entropy.m_insufficient_data) |
|||
{ |
|||
state.cinfo.WARNMS(J_MESSAGE_CODE.JWRN_HIT_MARKER); |
|||
state.cinfo.m_entropy.m_insufficient_data = true; |
|||
} |
|||
|
|||
/* Fill the buffer with zero bits */ |
|||
get_buffer <<= MIN_GET_BITS - bits_left; |
|||
bits_left = MIN_GET_BITS; |
|||
} |
|||
} |
|||
|
|||
/* Unload the local registers */ |
|||
state.get_buffer = get_buffer; |
|||
state.bits_left = bits_left; |
|||
|
|||
return true; |
|||
} |
|||
|
|||
/* |
|||
* Code for extracting next Huffman-coded symbol from input bit stream. |
|||
* Again, this is time-critical and we make the main paths be macros. |
|||
* |
|||
* We use a lookahead table to process codes of up to HUFF_LOOKAHEAD bits |
|||
* without looping. Usually, more than 95% of the Huffman codes will be 8 |
|||
* or fewer bits long. The few overlength codes are handled with a loop, |
|||
* which need not be inline code. |
|||
* |
|||
* Notes about the HUFF_DECODE macro: |
|||
* 1. Near the end of the data segment, we may fail to get enough bits |
|||
* for a lookahead. In that case, we do it the hard way. |
|||
* 2. If the lookahead table contains no entry, the next code must be |
|||
* more than HUFF_LOOKAHEAD bits long. |
|||
* 3. jpeg_huff_decode returns -1 if forced to suspend. |
|||
*/ |
|||
protected static bool HUFF_DECODE(out int result, ref bitread_working_state state, d_derived_tbl htbl, ref int get_buffer, ref int bits_left) |
|||
{ |
|||
int nb = 0; |
|||
bool doSlow = false; |
|||
|
|||
if (bits_left < JpegConstants.HUFF_LOOKAHEAD) |
|||
{ |
|||
if (!jpeg_fill_bit_buffer(ref state, get_buffer, bits_left, 0)) |
|||
{ |
|||
result = -1; |
|||
return false; |
|||
} |
|||
|
|||
get_buffer = state.get_buffer; |
|||
bits_left = state.bits_left; |
|||
if (bits_left < JpegConstants.HUFF_LOOKAHEAD) |
|||
{ |
|||
nb = 1; |
|||
doSlow = true; |
|||
} |
|||
} |
|||
|
|||
if (!doSlow) |
|||
{ |
|||
int look = PEEK_BITS(JpegConstants.HUFF_LOOKAHEAD, get_buffer, bits_left); |
|||
if ((nb = htbl.look_nbits[look]) != 0) |
|||
{ |
|||
DROP_BITS(nb, ref bits_left); |
|||
result = htbl.look_sym[look]; |
|||
return true; |
|||
} |
|||
|
|||
nb = JpegConstants.HUFF_LOOKAHEAD + 1; |
|||
} |
|||
|
|||
result = jpeg_huff_decode(ref state, get_buffer, bits_left, htbl, nb); |
|||
if (result < 0) |
|||
return false; |
|||
|
|||
get_buffer = state.get_buffer; |
|||
bits_left = state.bits_left; |
|||
|
|||
return true; |
|||
} |
|||
|
|||
/* Out-of-line case for Huffman code fetching */ |
|||
protected static int jpeg_huff_decode(ref bitread_working_state state, int get_buffer, int bits_left, d_derived_tbl htbl, int min_bits) |
|||
{ |
|||
/* HUFF_DECODE has determined that the code is at least min_bits */ |
|||
/* bits long, so fetch that many bits in one swoop. */ |
|||
int l = min_bits; |
|||
if (!CHECK_BIT_BUFFER(ref state, l, ref get_buffer, ref bits_left)) |
|||
return -1; |
|||
|
|||
int code = GET_BITS(l, get_buffer, ref bits_left); |
|||
|
|||
/* Collect the rest of the Huffman code one bit at a time. */ |
|||
/* This is per Figure F.16 in the JPEG spec. */ |
|||
|
|||
while (code > htbl.maxcode[l]) |
|||
{ |
|||
code <<= 1; |
|||
if (!CHECK_BIT_BUFFER(ref state, 1, ref get_buffer, ref bits_left)) |
|||
return -1; |
|||
|
|||
code |= GET_BITS(1, get_buffer, ref bits_left); |
|||
l++; |
|||
} |
|||
|
|||
/* Unload the local registers */ |
|||
state.get_buffer = get_buffer; |
|||
state.bits_left = bits_left; |
|||
|
|||
/* With garbage input we may reach the sentinel value l = 17. */ |
|||
|
|||
if (l > 16) |
|||
{ |
|||
state.cinfo.WARNMS(J_MESSAGE_CODE.JWRN_HUFF_BAD_CODE); |
|||
/* fake a zero as the safest result */ |
|||
return 0; |
|||
} |
|||
|
|||
return htbl.pub.Huffval[code + htbl.valoffset[l]]; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,303 @@ |
|||
/* Copyright (C) 2008-2011, Bit Miracle |
|||
* http://www.bitmiracle.com
|
|||
* |
|||
* Copyright (C) 1994-1996, Thomas G. Lane. |
|||
* This file is part of the Independent JPEG Group's software. |
|||
* For conditions of distribution and use, see the accompanying README file. |
|||
* |
|||
*/ |
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
|
|||
namespace BitMiracle.LibJpeg.Classic.Internal |
|||
{ |
|||
/// <summary>
|
|||
/// Entropy encoding
|
|||
/// </summary>
|
|||
abstract class jpeg_entropy_encoder |
|||
{ |
|||
/* Derived data constructed for each Huffman table */ |
|||
protected class c_derived_tbl |
|||
{ |
|||
public int[] ehufco = new int[256]; /* code for each symbol */ |
|||
public char[] ehufsi = new char[256]; /* length of code for each symbol */ |
|||
/* If no code has been allocated for a symbol S, ehufsi[S] contains 0 */ |
|||
} |
|||
|
|||
/* The legal range of a DCT coefficient is |
|||
* -1024 .. +1023 for 8-bit data; |
|||
* -16384 .. +16383 for 12-bit data. |
|||
* Hence the magnitude should always fit in 10 or 14 bits respectively. |
|||
*/ |
|||
protected static int MAX_HUFFMAN_COEF_BITS = 10; |
|||
private static int MAX_CLEN = 32; /* assumed maximum initial code length */ |
|||
|
|||
protected jpeg_compress_struct m_cinfo; |
|||
|
|||
public abstract void start_pass(bool gather_statistics); |
|||
public abstract bool encode_mcu(JBLOCK[][] MCU_data); |
|||
public abstract void finish_pass(); |
|||
|
|||
/// <summary>
|
|||
/// Expand a Huffman table definition into the derived format
|
|||
/// Compute the derived values for a Huffman table.
|
|||
/// This routine also performs some validation checks on the table.
|
|||
/// </summary>
|
|||
protected void jpeg_make_c_derived_tbl(bool isDC, int tblno, ref c_derived_tbl dtbl) |
|||
{ |
|||
/* Note that huffsize[] and huffcode[] are filled in code-length order, |
|||
* paralleling the order of the symbols themselves in htbl.huffval[]. |
|||
*/ |
|||
|
|||
/* Find the input Huffman table */ |
|||
if (tblno < 0 || tblno >= JpegConstants.NUM_HUFF_TBLS) |
|||
m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_NO_HUFF_TABLE, tblno); |
|||
|
|||
JHUFF_TBL htbl = isDC ? m_cinfo.m_dc_huff_tbl_ptrs[tblno] : m_cinfo.m_ac_huff_tbl_ptrs[tblno]; |
|||
if (htbl == null) |
|||
m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_NO_HUFF_TABLE, tblno); |
|||
|
|||
/* Allocate a workspace if we haven't already done so. */ |
|||
if (dtbl == null) |
|||
dtbl = new c_derived_tbl(); |
|||
|
|||
/* Figure C.1: make table of Huffman code length for each symbol */ |
|||
|
|||
int p = 0; |
|||
char[] huffsize = new char[257]; |
|||
for (int l = 1; l <= 16; l++) |
|||
{ |
|||
int i = htbl.Bits[l]; |
|||
if (i < 0 || p + i> 256) /* protect against table overrun */ |
|||
m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_HUFF_TABLE); |
|||
|
|||
while ((i--) != 0) |
|||
huffsize[p++] = (char) l; |
|||
} |
|||
huffsize[p] = (char)0; |
|||
int lastp = p; |
|||
|
|||
/* Figure C.2: generate the codes themselves */ |
|||
/* We also validate that the counts represent a legal Huffman code tree. */ |
|||
|
|||
int code = 0; |
|||
int si = huffsize[0]; |
|||
p = 0; |
|||
int[] huffcode = new int[257]; |
|||
while (huffsize[p] != 0) |
|||
{ |
|||
while (((int)huffsize[p]) == si) |
|||
{ |
|||
huffcode[p++] = code; |
|||
code++; |
|||
} |
|||
/* code is now 1 more than the last code used for codelength si; but |
|||
* it must still fit in si bits, since no code is allowed to be all ones. |
|||
*/ |
|||
if (code >= (1 << si)) |
|||
m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_HUFF_TABLE); |
|||
code <<= 1; |
|||
si++; |
|||
} |
|||
|
|||
/* Figure C.3: generate encoding tables */ |
|||
/* These are code and size indexed by symbol value */ |
|||
|
|||
/* Set all codeless symbols to have code length 0; |
|||
* this lets us detect duplicate VAL entries here, and later |
|||
* allows emit_bits to detect any attempt to emit such symbols. |
|||
*/ |
|||
Array.Clear(dtbl.ehufsi, 0, dtbl.ehufsi.Length); |
|||
|
|||
/* This is also a convenient place to check for out-of-range |
|||
* and duplicated VAL entries. We allow 0..255 for AC symbols |
|||
* but only 0..15 for DC. (We could constrain them further |
|||
* based on data depth and mode, but this seems enough.) |
|||
*/ |
|||
int maxsymbol = isDC ? 15 : 255; |
|||
|
|||
for (p = 0; p < lastp; p++) |
|||
{ |
|||
int i = htbl.Huffval[p]; |
|||
if (i < 0 || i> maxsymbol || dtbl.ehufsi[i] != 0) |
|||
m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_HUFF_TABLE); |
|||
|
|||
dtbl.ehufco[i] = huffcode[p]; |
|||
dtbl.ehufsi[i] = huffsize[p]; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Generate the best Huffman code table for the given counts, fill htbl.
|
|||
///
|
|||
/// The JPEG standard requires that no symbol be assigned a codeword of all
|
|||
/// one bits (so that padding bits added at the end of a compressed segment
|
|||
/// can't look like a valid code). Because of the canonical ordering of
|
|||
/// codewords, this just means that there must be an unused slot in the
|
|||
/// longest codeword length category. Section K.2 of the JPEG spec suggests
|
|||
/// reserving such a slot by pretending that symbol 256 is a valid symbol
|
|||
/// with count 1. In theory that's not optimal; giving it count zero but
|
|||
/// including it in the symbol set anyway should give a better Huffman code.
|
|||
/// But the theoretically better code actually seems to come out worse in
|
|||
/// practice, because it produces more all-ones bytes (which incur stuffed
|
|||
/// zero bytes in the final file). In any case the difference is tiny.
|
|||
///
|
|||
/// The JPEG standard requires Huffman codes to be no more than 16 bits long.
|
|||
/// If some symbols have a very small but nonzero probability, the Huffman tree
|
|||
/// must be adjusted to meet the code length restriction. We currently use
|
|||
/// the adjustment method suggested in JPEG section K.2. This method is *not*
|
|||
/// optimal; it may not choose the best possible limited-length code. But
|
|||
/// typically only very-low-frequency symbols will be given less-than-optimal
|
|||
/// lengths, so the code is almost optimal. Experimental comparisons against
|
|||
/// an optimal limited-length-code algorithm indicate that the difference is
|
|||
/// microscopic --- usually less than a hundredth of a percent of total size.
|
|||
/// So the extra complexity of an optimal algorithm doesn't seem worthwhile.
|
|||
/// </summary>
|
|||
protected void jpeg_gen_optimal_table(JHUFF_TBL htbl, long[] freq) |
|||
{ |
|||
byte[] bits = new byte[MAX_CLEN + 1]; /* bits[k] = # of symbols with code length k */ |
|||
int[] codesize = new int[257]; /* codesize[k] = code length of symbol k */ |
|||
int[] others = new int[257]; /* next symbol in current branch of tree */ |
|||
int c1, c2; |
|||
int p, i, j; |
|||
long v; |
|||
|
|||
/* This algorithm is explained in section K.2 of the JPEG standard */ |
|||
for (i = 0; i < 257; i++) |
|||
others[i] = -1; /* init links to empty */ |
|||
|
|||
freq[256] = 1; /* make sure 256 has a nonzero count */ |
|||
/* Including the pseudo-symbol 256 in the Huffman procedure guarantees |
|||
* that no real symbol is given code-value of all ones, because 256 |
|||
* will be placed last in the largest codeword category. |
|||
*/ |
|||
|
|||
/* Huffman's basic algorithm to assign optimal code lengths to symbols */ |
|||
|
|||
for (; ;) |
|||
{ |
|||
/* Find the smallest nonzero frequency, set c1 = its symbol */ |
|||
/* In case of ties, take the larger symbol number */ |
|||
c1 = -1; |
|||
v = 1000000000L; |
|||
for (i = 0; i <= 256; i++) |
|||
{ |
|||
if (freq[i] != 0 && freq[i] <= v) |
|||
{ |
|||
v = freq[i]; |
|||
c1 = i; |
|||
} |
|||
} |
|||
|
|||
/* Find the next smallest nonzero frequency, set c2 = its symbol */ |
|||
/* In case of ties, take the larger symbol number */ |
|||
c2 = -1; |
|||
v = 1000000000L; |
|||
for (i = 0; i <= 256; i++) |
|||
{ |
|||
if (freq[i] != 0 && freq[i] <= v && i != c1) |
|||
{ |
|||
v = freq[i]; |
|||
c2 = i; |
|||
} |
|||
} |
|||
|
|||
/* Done if we've merged everything into one frequency */ |
|||
if (c2 < 0) |
|||
break; |
|||
|
|||
/* Else merge the two counts/trees */ |
|||
freq[c1] += freq[c2]; |
|||
freq[c2] = 0; |
|||
|
|||
/* Increment the codesize of everything in c1's tree branch */ |
|||
codesize[c1]++; |
|||
while (others[c1] >= 0) |
|||
{ |
|||
c1 = others[c1]; |
|||
codesize[c1]++; |
|||
} |
|||
|
|||
others[c1] = c2; /* chain c2 onto c1's tree branch */ |
|||
|
|||
/* Increment the codesize of everything in c2's tree branch */ |
|||
codesize[c2]++; |
|||
while (others[c2] >= 0) |
|||
{ |
|||
c2 = others[c2]; |
|||
codesize[c2]++; |
|||
} |
|||
} |
|||
|
|||
/* Now count the number of symbols of each code length */ |
|||
for (i = 0; i <= 256; i++) |
|||
{ |
|||
if (codesize[i] != 0) |
|||
{ |
|||
/* The JPEG standard seems to think that this can't happen, */ |
|||
/* but I'm paranoid... */ |
|||
if (codesize[i] > MAX_CLEN) |
|||
m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_HUFF_CLEN_OVERFLOW); |
|||
|
|||
bits[codesize[i]]++; |
|||
} |
|||
} |
|||
|
|||
/* JPEG doesn't allow symbols with code lengths over 16 bits, so if the pure |
|||
* Huffman procedure assigned any such lengths, we must adjust the coding. |
|||
* Here is what the JPEG spec says about how this next bit works: |
|||
* Since symbols are paired for the longest Huffman code, the symbols are |
|||
* removed from this length category two at a time. The prefix for the pair |
|||
* (which is one bit shorter) is allocated to one of the pair; then, |
|||
* skipping the BITS entry for that prefix length, a code word from the next |
|||
* shortest nonzero BITS entry is converted into a prefix for two code words |
|||
* one bit longer. |
|||
*/ |
|||
|
|||
for (i = MAX_CLEN; i > 16; i--) |
|||
{ |
|||
while (bits[i] > 0) |
|||
{ |
|||
j = i - 2; /* find length of new prefix to be used */ |
|||
while (bits[j] == 0) |
|||
j--; |
|||
|
|||
bits[i] -= 2; /* remove two symbols */ |
|||
bits[i - 1]++; /* one goes in this length */ |
|||
bits[j + 1] += 2; /* two new symbols in this length */ |
|||
bits[j]--; /* symbol of this length is now a prefix */ |
|||
} |
|||
} |
|||
|
|||
/* Remove the count for the pseudo-symbol 256 from the largest codelength */ |
|||
while (bits[i] == 0) /* find largest codelength still in use */ |
|||
i--; |
|||
bits[i]--; |
|||
|
|||
/* Return final symbol counts (only for lengths 0..16) */ |
|||
Buffer.BlockCopy(bits, 0, htbl.Bits, 0, htbl.Bits.Length); |
|||
|
|||
/* Return a list of the symbols sorted by code length */ |
|||
/* It's not real clear to me why we don't need to consider the codelength |
|||
* changes made above, but the JPEG spec seems to think this works. |
|||
*/ |
|||
p = 0; |
|||
for (i = 1; i <= MAX_CLEN; i++) |
|||
{ |
|||
for (j = 0; j <= 255; j++) |
|||
{ |
|||
if (codesize[j] == i) |
|||
{ |
|||
htbl.Huffval[p] = (byte) j; |
|||
p++; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/* Set sent_table false so updated table will be written to JPEG file. */ |
|||
htbl.Sent_table = false; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,813 @@ |
|||
/* Copyright (C) 2008-2011, Bit Miracle |
|||
* http://www.bitmiracle.com
|
|||
* |
|||
* Copyright (C) 1994-1996, Thomas G. Lane. |
|||
* This file is part of the Independent JPEG Group's software. |
|||
* For conditions of distribution and use, see the accompanying README file. |
|||
* |
|||
*/ |
|||
|
|||
/* |
|||
* This file contains the forward-DCT management logic. |
|||
* This code selects a particular DCT implementation to be used, |
|||
* and it performs related housekeeping chores including coefficient |
|||
* quantization. |
|||
*/ |
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
|
|||
namespace BitMiracle.LibJpeg.Classic.Internal |
|||
{ |
|||
/// <summary>
|
|||
/// Forward DCT (also controls coefficient quantization)
|
|||
///
|
|||
/// A forward DCT routine is given a pointer to a work area of type DCTELEM[];
|
|||
/// the DCT is to be performed in-place in that buffer. Type DCTELEM is int
|
|||
/// for 8-bit samples, int for 12-bit samples. (NOTE: Floating-point DCT
|
|||
/// implementations use an array of type float, instead.)
|
|||
/// The DCT inputs are expected to be signed (range +-CENTERJSAMPLE).
|
|||
/// The DCT outputs are returned scaled up by a factor of 8; they therefore
|
|||
/// have a range of +-8K for 8-bit data, +-128K for 12-bit data. This
|
|||
/// convention improves accuracy in integer implementations and saves some
|
|||
/// work in floating-point ones.
|
|||
///
|
|||
/// Each IDCT routine has its own ideas about the best dct_table element type.
|
|||
/// </summary>
|
|||
class jpeg_forward_dct |
|||
{ |
|||
private const int FAST_INTEGER_CONST_BITS = 8; |
|||
|
|||
/* We use the following pre-calculated constants. |
|||
* If you change FAST_INTEGER_CONST_BITS you may want to add appropriate values. |
|||
* |
|||
* Convert a positive real constant to an integer scaled by CONST_SCALE. |
|||
* static int FAST_INTEGER_FIX(double x) |
|||
*{ |
|||
* return ((int) ((x) * (((int) 1) << FAST_INTEGER_CONST_BITS) + 0.5)); |
|||
*} |
|||
*/ |
|||
private const int FAST_INTEGER_FIX_0_382683433 = 98; /* FIX(0.382683433) */ |
|||
private const int FAST_INTEGER_FIX_0_541196100 = 139; /* FIX(0.541196100) */ |
|||
private const int FAST_INTEGER_FIX_0_707106781 = 181; /* FIX(0.707106781) */ |
|||
private const int FAST_INTEGER_FIX_1_306562965 = 334; /* FIX(1.306562965) */ |
|||
|
|||
private const int SLOW_INTEGER_CONST_BITS = 13; |
|||
private const int SLOW_INTEGER_PASS1_BITS = 2; |
|||
|
|||
/* We use the following pre-calculated constants. |
|||
* If you change SLOW_INTEGER_CONST_BITS you may want to add appropriate values. |
|||
* |
|||
* Convert a positive real constant to an integer scaled by CONST_SCALE. |
|||
* |
|||
* static int SLOW_INTEGER_FIX(double x) |
|||
* { |
|||
* return ((int) ((x) * (((int) 1) << SLOW_INTEGER_CONST_BITS) + 0.5)); |
|||
* } |
|||
*/ |
|||
private const int SLOW_INTEGER_FIX_0_298631336 = 2446; /* FIX(0.298631336) */ |
|||
private const int SLOW_INTEGER_FIX_0_390180644 = 3196; /* FIX(0.390180644) */ |
|||
private const int SLOW_INTEGER_FIX_0_541196100 = 4433; /* FIX(0.541196100) */ |
|||
private const int SLOW_INTEGER_FIX_0_765366865 = 6270; /* FIX(0.765366865) */ |
|||
private const int SLOW_INTEGER_FIX_0_899976223 = 7373; /* FIX(0.899976223) */ |
|||
private const int SLOW_INTEGER_FIX_1_175875602 = 9633; /* FIX(1.175875602) */ |
|||
private const int SLOW_INTEGER_FIX_1_501321110 = 12299; /* FIX(1.501321110) */ |
|||
private const int SLOW_INTEGER_FIX_1_847759065 = 15137; /* FIX(1.847759065) */ |
|||
private const int SLOW_INTEGER_FIX_1_961570560 = 16069; /* FIX(1.961570560) */ |
|||
private const int SLOW_INTEGER_FIX_2_053119869 = 16819; /* FIX(2.053119869) */ |
|||
private const int SLOW_INTEGER_FIX_2_562915447 = 20995; /* FIX(2.562915447) */ |
|||
private const int SLOW_INTEGER_FIX_3_072711026 = 25172; /* FIX(3.072711026) */ |
|||
|
|||
/* For AA&N IDCT method, divisors are equal to quantization |
|||
* coefficients scaled by scalefactor[row]*scalefactor[col], where |
|||
* scalefactor[0] = 1 |
|||
* scalefactor[k] = cos(k*PI/16) * sqrt(2) for k=1..7 |
|||
* We apply a further scale factor of 8. |
|||
*/ |
|||
private const int CONST_BITS = 14; |
|||
|
|||
/* precomputed values scaled up by 14 bits */ |
|||
private static short[] aanscales = { |
|||
16384, 22725, 21407, 19266, 16384, 12873, 8867, 4520, 22725, 31521, 29692, 26722, 22725, 17855, |
|||
12299, 6270, 21407, 29692, 27969, 25172, 21407, 16819, 11585, |
|||
5906, 19266, 26722, 25172, 22654, 19266, 15137, 10426, 5315, |
|||
16384, 22725, 21407, 19266, 16384, 12873, 8867, 4520, 12873, |
|||
17855, 16819, 15137, 12873, 10114, 6967, 3552, 8867, 12299, |
|||
11585, 10426, 8867, 6967, 4799, 2446, 4520, 6270, 5906, 5315, |
|||
4520, 3552, 2446, 1247 }; |
|||
|
|||
/* For float AA&N IDCT method, divisors are equal to quantization |
|||
* coefficients scaled by scalefactor[row]*scalefactor[col], where |
|||
* scalefactor[0] = 1 |
|||
* scalefactor[k] = cos(k*PI/16) * sqrt(2) for k=1..7 |
|||
* We apply a further scale factor of 8. |
|||
* What's actually stored is 1/divisor so that the inner loop can |
|||
* use a multiplication rather than a division. |
|||
*/ |
|||
private static double[] aanscalefactor = { |
|||
1.0, 1.387039845, 1.306562965, 1.175875602, 1.0, |
|||
0.785694958, 0.541196100, 0.275899379 }; |
|||
|
|||
private jpeg_compress_struct m_cinfo; |
|||
private bool m_useSlowMethod; |
|||
private bool m_useFloatMethod; |
|||
|
|||
/* The actual post-DCT divisors --- not identical to the quant table |
|||
* entries, because of scaling (especially for an unnormalized DCT). |
|||
* Each table is given in normal array order. |
|||
*/ |
|||
private int[][] m_divisors = new int [JpegConstants.NUM_QUANT_TBLS][]; |
|||
|
|||
/* Same as above for the floating-point case. */ |
|||
private float[][] m_float_divisors = new float[JpegConstants.NUM_QUANT_TBLS][]; |
|||
|
|||
public jpeg_forward_dct(jpeg_compress_struct cinfo) |
|||
{ |
|||
m_cinfo = cinfo; |
|||
|
|||
switch (cinfo.m_dct_method) |
|||
{ |
|||
case J_DCT_METHOD.JDCT_ISLOW: |
|||
m_useFloatMethod = false; |
|||
m_useSlowMethod = true; |
|||
break; |
|||
case J_DCT_METHOD.JDCT_IFAST: |
|||
m_useFloatMethod = false; |
|||
m_useSlowMethod = false; |
|||
break; |
|||
case J_DCT_METHOD.JDCT_FLOAT: |
|||
m_useFloatMethod = true; |
|||
break; |
|||
default: |
|||
cinfo.ERREXIT(J_MESSAGE_CODE.JERR_NOT_COMPILED); |
|||
break; |
|||
} |
|||
|
|||
/* Mark divisor tables unallocated */ |
|||
for (int i = 0; i < JpegConstants.NUM_QUANT_TBLS; i++) |
|||
{ |
|||
m_divisors[i] = null; |
|||
m_float_divisors[i] = null; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initialize for a processing pass.
|
|||
/// Verify that all referenced Q-tables are present, and set up
|
|||
/// the divisor table for each one.
|
|||
/// In the current implementation, DCT of all components is done during
|
|||
/// the first pass, even if only some components will be output in the
|
|||
/// first scan. Hence all components should be examined here.
|
|||
/// </summary>
|
|||
public virtual void start_pass() |
|||
{ |
|||
for (int ci = 0; ci < m_cinfo.m_num_components; ci++) |
|||
{ |
|||
int qtblno = m_cinfo.Component_info[ci].Quant_tbl_no; |
|||
|
|||
/* Make sure specified quantization table is present */ |
|||
if (qtblno < 0 || qtblno >= JpegConstants.NUM_QUANT_TBLS || m_cinfo.m_quant_tbl_ptrs[qtblno] == null) |
|||
m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_NO_QUANT_TABLE, qtblno); |
|||
|
|||
JQUANT_TBL qtbl = m_cinfo.m_quant_tbl_ptrs[qtblno]; |
|||
|
|||
/* Compute divisors for this quant table */ |
|||
/* We may do this more than once for same table, but it's not a big deal */ |
|||
int i = 0; |
|||
switch (m_cinfo.m_dct_method) |
|||
{ |
|||
case J_DCT_METHOD.JDCT_ISLOW: |
|||
/* For LL&M IDCT method, divisors are equal to raw quantization |
|||
* coefficients multiplied by 8 (to counteract scaling). |
|||
*/ |
|||
if (m_divisors[qtblno] == null) |
|||
m_divisors[qtblno] = new int [JpegConstants.DCTSIZE2]; |
|||
|
|||
for (i = 0; i < JpegConstants.DCTSIZE2; i++) |
|||
m_divisors[qtblno][i] = ((int)qtbl.quantval[i]) << 3; |
|||
|
|||
break; |
|||
case J_DCT_METHOD.JDCT_IFAST: |
|||
if (m_divisors[qtblno] == null) |
|||
m_divisors[qtblno] = new int [JpegConstants.DCTSIZE2]; |
|||
|
|||
for (i = 0; i < JpegConstants.DCTSIZE2; i++) |
|||
m_divisors[qtblno][i] = JpegUtils.DESCALE((int)qtbl.quantval[i] * (int)aanscales[i], CONST_BITS - 3); |
|||
break; |
|||
case J_DCT_METHOD.JDCT_FLOAT: |
|||
if (m_float_divisors[qtblno] == null) |
|||
m_float_divisors[qtblno] = new float [JpegConstants.DCTSIZE2]; |
|||
|
|||
float[] fdtbl = m_float_divisors[qtblno]; |
|||
i = 0; |
|||
for (int row = 0; row < JpegConstants.DCTSIZE; row++) |
|||
{ |
|||
for (int col = 0; col < JpegConstants.DCTSIZE; col++) |
|||
{ |
|||
fdtbl[i] = (float)(1.0 / (((double) qtbl.quantval[i] * aanscalefactor[row] * aanscalefactor[col] * 8.0))); |
|||
i++; |
|||
} |
|||
} |
|||
break; |
|||
default: |
|||
m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_NOT_COMPILED); |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Perform forward DCT on one or more blocks of a component.
|
|||
///
|
|||
/// The input samples are taken from the sample_data[] array starting at
|
|||
/// position start_row/start_col, and moving to the right for any additional
|
|||
/// blocks. The quantized coefficients are returned in coef_blocks[].
|
|||
/// </summary>
|
|||
public virtual void forward_DCT(int quant_tbl_no, byte[][] sample_data, JBLOCK[] coef_blocks, int start_row, int start_col, int num_blocks) |
|||
{ |
|||
if (m_useFloatMethod) |
|||
forwardDCTFloatImpl(quant_tbl_no, sample_data, coef_blocks, start_row, start_col, num_blocks); |
|||
else |
|||
forwardDCTImpl(quant_tbl_no, sample_data, coef_blocks, start_row, start_col, num_blocks); |
|||
} |
|||
|
|||
// This version is used for integer DCT implementations.
|
|||
private void forwardDCTImpl(int quant_tbl_no, byte[][] sample_data, JBLOCK[] coef_blocks, int start_row, int start_col, int num_blocks) |
|||
{ |
|||
/* This routine is heavily used, so it's worth coding it tightly. */ |
|||
int[] workspace = new int [JpegConstants.DCTSIZE2]; /* work area for FDCT subroutine */ |
|||
for (int bi = 0; bi < num_blocks; bi++, start_col += JpegConstants.DCTSIZE) |
|||
{ |
|||
/* Load data into workspace, applying unsigned->signed conversion */ |
|||
int workspaceIndex = 0; |
|||
for (int elemr = 0; elemr < JpegConstants.DCTSIZE; elemr++) |
|||
{ |
|||
for (int column = 0; column < JpegConstants.DCTSIZE; column++) |
|||
{ |
|||
workspace[workspaceIndex] = (int)sample_data[start_row + elemr][start_col + column] - JpegConstants.CENTERJSAMPLE; |
|||
workspaceIndex++; |
|||
} |
|||
} |
|||
|
|||
/* Perform the DCT */ |
|||
if (m_useSlowMethod) |
|||
jpeg_fdct_islow(workspace); |
|||
else |
|||
jpeg_fdct_ifast(workspace); |
|||
|
|||
/* Quantize/descale the coefficients, and store into coef_blocks[] */ |
|||
for (int i = 0; i < JpegConstants.DCTSIZE2; i++) |
|||
{ |
|||
int qval = m_divisors[quant_tbl_no][i]; |
|||
int temp = workspace[i]; |
|||
|
|||
if (temp < 0) |
|||
{ |
|||
temp = -temp; |
|||
temp += qval >> 1; /* for rounding */ |
|||
|
|||
if (temp >= qval) |
|||
temp /= qval; |
|||
else |
|||
temp = 0; |
|||
|
|||
temp = -temp; |
|||
} |
|||
else |
|||
{ |
|||
temp += qval >> 1; /* for rounding */ |
|||
|
|||
if (temp >= qval) |
|||
temp /= qval; |
|||
else |
|||
temp = 0; |
|||
} |
|||
|
|||
coef_blocks[bi][i] = (short) temp; |
|||
} |
|||
} |
|||
} |
|||
|
|||
// This version is used for floating-point DCT implementations.
|
|||
private void forwardDCTFloatImpl(int quant_tbl_no, byte[][] sample_data, JBLOCK[] coef_blocks, int start_row, int start_col, int num_blocks) |
|||
{ |
|||
/* This routine is heavily used, so it's worth coding it tightly. */ |
|||
float[] workspace = new float [JpegConstants.DCTSIZE2]; /* work area for FDCT subroutine */ |
|||
for (int bi = 0; bi < num_blocks; bi++, start_col += JpegConstants.DCTSIZE) |
|||
{ |
|||
/* Load data into workspace, applying unsigned->signed conversion */ |
|||
int workspaceIndex = 0; |
|||
for (int elemr = 0; elemr < JpegConstants.DCTSIZE; elemr++) |
|||
{ |
|||
for (int column = 0; column < JpegConstants.DCTSIZE; column++) |
|||
{ |
|||
workspace[workspaceIndex] = (float)((int)sample_data[start_row + elemr][start_col + column] - JpegConstants.CENTERJSAMPLE); |
|||
workspaceIndex++; |
|||
} |
|||
} |
|||
|
|||
/* Perform the DCT */ |
|||
jpeg_fdct_float(workspace); |
|||
|
|||
/* Quantize/descale the coefficients, and store into coef_blocks[] */ |
|||
for (int i = 0; i < JpegConstants.DCTSIZE2; i++) |
|||
{ |
|||
/* Apply the quantization and scaling factor */ |
|||
float temp = workspace[i] * m_float_divisors[quant_tbl_no][i]; |
|||
|
|||
/* Round to nearest integer. |
|||
* Since C does not specify the direction of rounding for negative |
|||
* quotients, we have to force the dividend positive for portability. |
|||
* The maximum coefficient size is +-16K (for 12-bit data), so this |
|||
* code should work for either 16-bit or 32-bit ints. |
|||
*/ |
|||
coef_blocks[bi][i] = (short)((int)(temp + (float)16384.5) - 16384); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Perform the forward DCT on one block of samples.
|
|||
/// NOTE: this code only copes with 8x8 DCTs.
|
|||
///
|
|||
/// A floating-point implementation of the
|
|||
/// forward DCT (Discrete Cosine Transform).
|
|||
///
|
|||
/// This implementation should be more accurate than either of the integer
|
|||
/// DCT implementations. However, it may not give the same results on all
|
|||
/// machines because of differences in roundoff behavior. Speed will depend
|
|||
/// on the hardware's floating point capacity.
|
|||
///
|
|||
/// A 2-D DCT can be done by 1-D DCT on each row followed by 1-D DCT
|
|||
/// on each column. Direct algorithms are also available, but they are
|
|||
/// much more complex and seem not to be any faster when reduced to code.
|
|||
///
|
|||
/// This implementation is based on Arai, Agui, and Nakajima's algorithm for
|
|||
/// scaled DCT. Their original paper (Trans. IEICE E-71(11):1095) is in
|
|||
/// Japanese, but the algorithm is described in the Pennebaker & Mitchell
|
|||
/// JPEG textbook (see REFERENCES section in file README). The following code
|
|||
/// is based directly on figure 4-8 in P&M.
|
|||
/// While an 8-point DCT cannot be done in less than 11 multiplies, it is
|
|||
/// possible to arrange the computation so that many of the multiplies are
|
|||
/// simple scalings of the final outputs. These multiplies can then be
|
|||
/// folded into the multiplications or divisions by the JPEG quantization
|
|||
/// table entries. The AA&N method leaves only 5 multiplies and 29 adds
|
|||
/// to be done in the DCT itself.
|
|||
/// The primary disadvantage of this method is that with a fixed-point
|
|||
/// implementation, accuracy is lost due to imprecise representation of the
|
|||
/// scaled quantization values. However, that problem does not arise if
|
|||
/// we use floating point arithmetic.
|
|||
/// </summary>
|
|||
private static void jpeg_fdct_float(float[] data) |
|||
{ |
|||
/* Pass 1: process rows. */ |
|||
int dataIndex = 0; |
|||
for (int ctr = JpegConstants.DCTSIZE - 1; ctr >= 0; ctr--) |
|||
{ |
|||
float tmp0 = data[dataIndex + 0] + data[dataIndex + 7]; |
|||
float tmp7 = data[dataIndex + 0] - data[dataIndex + 7]; |
|||
float tmp1 = data[dataIndex + 1] + data[dataIndex + 6]; |
|||
float tmp6 = data[dataIndex + 1] - data[dataIndex + 6]; |
|||
float tmp2 = data[dataIndex + 2] + data[dataIndex + 5]; |
|||
float tmp5 = data[dataIndex + 2] - data[dataIndex + 5]; |
|||
float tmp3 = data[dataIndex + 3] + data[dataIndex + 4]; |
|||
float tmp4 = data[dataIndex + 3] - data[dataIndex + 4]; |
|||
|
|||
/* Even part */ |
|||
|
|||
float tmp10 = tmp0 + tmp3; /* phase 2 */ |
|||
float tmp13 = tmp0 - tmp3; |
|||
float tmp11 = tmp1 + tmp2; |
|||
float tmp12 = tmp1 - tmp2; |
|||
|
|||
data[dataIndex + 0] = tmp10 + tmp11; /* phase 3 */ |
|||
data[dataIndex + 4] = tmp10 - tmp11; |
|||
|
|||
float z1 = (tmp12 + tmp13) * ((float)0.707106781); /* c4 */ |
|||
data[dataIndex + 2] = tmp13 + z1; /* phase 5 */ |
|||
data[dataIndex + 6] = tmp13 - z1; |
|||
|
|||
/* Odd part */ |
|||
|
|||
tmp10 = tmp4 + tmp5; /* phase 2 */ |
|||
tmp11 = tmp5 + tmp6; |
|||
tmp12 = tmp6 + tmp7; |
|||
|
|||
/* The rotator is modified from fig 4-8 to avoid extra negations. */ |
|||
float z5 = (tmp10 - tmp12) * ((float)0.382683433); /* c6 */ |
|||
float z2 = ((float)0.541196100) * tmp10 + z5; /* c2-c6 */ |
|||
float z4 = ((float)1.306562965) * tmp12 + z5; /* c2+c6 */ |
|||
float z3 = tmp11 * ((float)0.707106781); /* c4 */ |
|||
|
|||
float z11 = tmp7 + z3; /* phase 5 */ |
|||
float z13 = tmp7 - z3; |
|||
|
|||
data[dataIndex + 5] = z13 + z2; /* phase 6 */ |
|||
data[dataIndex + 3] = z13 - z2; |
|||
data[dataIndex + 1] = z11 + z4; |
|||
data[dataIndex + 7] = z11 - z4; |
|||
|
|||
dataIndex += JpegConstants.DCTSIZE; /* advance pointer to next row */ |
|||
} |
|||
|
|||
/* Pass 2: process columns. */ |
|||
|
|||
dataIndex = 0; |
|||
for (int ctr = JpegConstants.DCTSIZE - 1; ctr >= 0; ctr--) |
|||
{ |
|||
float tmp0 = data[dataIndex + JpegConstants.DCTSIZE * 0] + data[dataIndex + JpegConstants.DCTSIZE * 7]; |
|||
float tmp7 = data[dataIndex + JpegConstants.DCTSIZE * 0] - data[dataIndex + JpegConstants.DCTSIZE * 7]; |
|||
float tmp1 = data[dataIndex + JpegConstants.DCTSIZE * 1] + data[dataIndex + JpegConstants.DCTSIZE * 6]; |
|||
float tmp6 = data[dataIndex + JpegConstants.DCTSIZE * 1] - data[dataIndex + JpegConstants.DCTSIZE * 6]; |
|||
float tmp2 = data[dataIndex + JpegConstants.DCTSIZE * 2] + data[dataIndex + JpegConstants.DCTSIZE * 5]; |
|||
float tmp5 = data[dataIndex + JpegConstants.DCTSIZE * 2] - data[dataIndex + JpegConstants.DCTSIZE * 5]; |
|||
float tmp3 = data[dataIndex + JpegConstants.DCTSIZE * 3] + data[dataIndex + JpegConstants.DCTSIZE * 4]; |
|||
float tmp4 = data[dataIndex + JpegConstants.DCTSIZE * 3] - data[dataIndex + JpegConstants.DCTSIZE * 4]; |
|||
|
|||
/* Even part */ |
|||
|
|||
float tmp10 = tmp0 + tmp3; /* phase 2 */ |
|||
float tmp13 = tmp0 - tmp3; |
|||
float tmp11 = tmp1 + tmp2; |
|||
float tmp12 = tmp1 - tmp2; |
|||
|
|||
data[dataIndex + JpegConstants.DCTSIZE * 0] = tmp10 + tmp11; /* phase 3 */ |
|||
data[dataIndex + JpegConstants.DCTSIZE * 4] = tmp10 - tmp11; |
|||
|
|||
float z1 = (tmp12 + tmp13) * ((float)0.707106781); /* c4 */ |
|||
data[dataIndex + JpegConstants.DCTSIZE * 2] = tmp13 + z1; /* phase 5 */ |
|||
data[dataIndex + JpegConstants.DCTSIZE * 6] = tmp13 - z1; |
|||
|
|||
/* Odd part */ |
|||
|
|||
tmp10 = tmp4 + tmp5; /* phase 2 */ |
|||
tmp11 = tmp5 + tmp6; |
|||
tmp12 = tmp6 + tmp7; |
|||
|
|||
/* The rotator is modified from fig 4-8 to avoid extra negations. */ |
|||
float z5 = (tmp10 - tmp12) * ((float)0.382683433); /* c6 */ |
|||
float z2 = ((float)0.541196100) * tmp10 + z5; /* c2-c6 */ |
|||
float z4 = ((float)1.306562965) * tmp12 + z5; /* c2+c6 */ |
|||
float z3 = tmp11 * ((float)0.707106781); /* c4 */ |
|||
|
|||
float z11 = tmp7 + z3; /* phase 5 */ |
|||
float z13 = tmp7 - z3; |
|||
|
|||
data[dataIndex + JpegConstants.DCTSIZE * 5] = z13 + z2; /* phase 6 */ |
|||
data[dataIndex + JpegConstants.DCTSIZE * 3] = z13 - z2; |
|||
data[dataIndex + JpegConstants.DCTSIZE * 1] = z11 + z4; |
|||
data[dataIndex + JpegConstants.DCTSIZE * 7] = z11 - z4; |
|||
|
|||
dataIndex++; /* advance pointer to next column */ |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Perform the forward DCT on one block of samples.
|
|||
/// NOTE: this code only copes with 8x8 DCTs.
|
|||
/// This file contains a fast, not so accurate integer implementation of the
|
|||
/// forward DCT (Discrete Cosine Transform).
|
|||
///
|
|||
/// A 2-D DCT can be done by 1-D DCT on each row followed by 1-D DCT
|
|||
/// on each column. Direct algorithms are also available, but they are
|
|||
/// much more complex and seem not to be any faster when reduced to code.
|
|||
///
|
|||
/// This implementation is based on Arai, Agui, and Nakajima's algorithm for
|
|||
/// scaled DCT. Their original paper (Trans. IEICE E-71(11):1095) is in
|
|||
/// Japanese, but the algorithm is described in the Pennebaker & Mitchell
|
|||
/// JPEG textbook (see REFERENCES section in file README). The following code
|
|||
/// is based directly on figure 4-8 in P&M.
|
|||
/// While an 8-point DCT cannot be done in less than 11 multiplies, it is
|
|||
/// possible to arrange the computation so that many of the multiplies are
|
|||
/// simple scalings of the final outputs. These multiplies can then be
|
|||
/// folded into the multiplications or divisions by the JPEG quantization
|
|||
/// table entries. The AA&N method leaves only 5 multiplies and 29 adds
|
|||
/// to be done in the DCT itself.
|
|||
/// The primary disadvantage of this method is that with fixed-point math,
|
|||
/// accuracy is lost due to imprecise representation of the scaled
|
|||
/// quantization values. The smaller the quantization table entry, the less
|
|||
/// precise the scaled value, so this implementation does worse with high-
|
|||
/// quality-setting files than with low-quality ones.
|
|||
///
|
|||
/// Scaling decisions are generally the same as in the LL&M algorithm;
|
|||
/// see jpeg_fdct_islow for more details. However, we choose to descale
|
|||
/// (right shift) multiplication products as soon as they are formed,
|
|||
/// rather than carrying additional fractional bits into subsequent additions.
|
|||
/// This compromises accuracy slightly, but it lets us save a few shifts.
|
|||
/// More importantly, 16-bit arithmetic is then adequate (for 8-bit samples)
|
|||
/// everywhere except in the multiplications proper; this saves a good deal
|
|||
/// of work on 16-bit-int machines.
|
|||
///
|
|||
/// Again to save a few shifts, the intermediate results between pass 1 and
|
|||
/// pass 2 are not upscaled, but are represented only to integral precision.
|
|||
///
|
|||
/// A final compromise is to represent the multiplicative constants to only
|
|||
/// 8 fractional bits, rather than 13. This saves some shifting work on some
|
|||
/// machines, and may also reduce the cost of multiplication (since there
|
|||
/// are fewer one-bits in the constants).
|
|||
/// </summary>
|
|||
private static void jpeg_fdct_ifast(int[] data) |
|||
{ |
|||
/* Pass 1: process rows. */ |
|||
int dataIndex = 0; |
|||
for (int ctr = JpegConstants.DCTSIZE - 1; ctr >= 0; ctr--) |
|||
{ |
|||
int tmp0 = data[dataIndex + 0] + data[dataIndex + 7]; |
|||
int tmp7 = data[dataIndex + 0] - data[dataIndex + 7]; |
|||
int tmp1 = data[dataIndex + 1] + data[dataIndex + 6]; |
|||
int tmp6 = data[dataIndex + 1] - data[dataIndex + 6]; |
|||
int tmp2 = data[dataIndex + 2] + data[dataIndex + 5]; |
|||
int tmp5 = data[dataIndex + 2] - data[dataIndex + 5]; |
|||
int tmp3 = data[dataIndex + 3] + data[dataIndex + 4]; |
|||
int tmp4 = data[dataIndex + 3] - data[dataIndex + 4]; |
|||
|
|||
/* Even part */ |
|||
|
|||
int tmp10 = tmp0 + tmp3; /* phase 2 */ |
|||
int tmp13 = tmp0 - tmp3; |
|||
int tmp11 = tmp1 + tmp2; |
|||
int tmp12 = tmp1 - tmp2; |
|||
|
|||
data[dataIndex + 0] = tmp10 + tmp11; /* phase 3 */ |
|||
data[dataIndex + 4] = tmp10 - tmp11; |
|||
|
|||
int z1 = FAST_INTEGER_MULTIPLY(tmp12 + tmp13, FAST_INTEGER_FIX_0_707106781); /* c4 */ |
|||
data[dataIndex + 2] = tmp13 + z1; /* phase 5 */ |
|||
data[dataIndex + 6] = tmp13 - z1; |
|||
|
|||
/* Odd part */ |
|||
|
|||
tmp10 = tmp4 + tmp5; /* phase 2 */ |
|||
tmp11 = tmp5 + tmp6; |
|||
tmp12 = tmp6 + tmp7; |
|||
|
|||
/* The rotator is modified from fig 4-8 to avoid extra negations. */ |
|||
int z5 = FAST_INTEGER_MULTIPLY(tmp10 - tmp12, FAST_INTEGER_FIX_0_382683433); /* c6 */ |
|||
int z2 = FAST_INTEGER_MULTIPLY(tmp10, FAST_INTEGER_FIX_0_541196100) + z5; /* c2-c6 */ |
|||
int z4 = FAST_INTEGER_MULTIPLY(tmp12, FAST_INTEGER_FIX_1_306562965) + z5; /* c2+c6 */ |
|||
int z3 = FAST_INTEGER_MULTIPLY(tmp11, FAST_INTEGER_FIX_0_707106781); /* c4 */ |
|||
|
|||
int z11 = tmp7 + z3; /* phase 5 */ |
|||
int z13 = tmp7 - z3; |
|||
|
|||
data[dataIndex + 5] = z13 + z2; /* phase 6 */ |
|||
data[dataIndex + 3] = z13 - z2; |
|||
data[dataIndex + 1] = z11 + z4; |
|||
data[dataIndex + 7] = z11 - z4; |
|||
|
|||
dataIndex += JpegConstants.DCTSIZE; /* advance pointer to next row */ |
|||
} |
|||
|
|||
/* Pass 2: process columns. */ |
|||
|
|||
dataIndex = 0; |
|||
for (int ctr = JpegConstants.DCTSIZE - 1; ctr >= 0; ctr--) |
|||
{ |
|||
int tmp0 = data[dataIndex + JpegConstants.DCTSIZE * 0] + data[dataIndex + JpegConstants.DCTSIZE * 7]; |
|||
int tmp7 = data[dataIndex + JpegConstants.DCTSIZE * 0] - data[dataIndex + JpegConstants.DCTSIZE * 7]; |
|||
int tmp1 = data[dataIndex + JpegConstants.DCTSIZE * 1] + data[dataIndex + JpegConstants.DCTSIZE * 6]; |
|||
int tmp6 = data[dataIndex + JpegConstants.DCTSIZE * 1] - data[dataIndex + JpegConstants.DCTSIZE * 6]; |
|||
int tmp2 = data[dataIndex + JpegConstants.DCTSIZE * 2] + data[dataIndex + JpegConstants.DCTSIZE * 5]; |
|||
int tmp5 = data[dataIndex + JpegConstants.DCTSIZE * 2] - data[dataIndex + JpegConstants.DCTSIZE * 5]; |
|||
int tmp3 = data[dataIndex + JpegConstants.DCTSIZE * 3] + data[dataIndex + JpegConstants.DCTSIZE * 4]; |
|||
int tmp4 = data[dataIndex + JpegConstants.DCTSIZE * 3] - data[dataIndex + JpegConstants.DCTSIZE * 4]; |
|||
|
|||
/* Even part */ |
|||
|
|||
int tmp10 = tmp0 + tmp3; /* phase 2 */ |
|||
int tmp13 = tmp0 - tmp3; |
|||
int tmp11 = tmp1 + tmp2; |
|||
int tmp12 = tmp1 - tmp2; |
|||
|
|||
data[dataIndex + JpegConstants.DCTSIZE * 0] = tmp10 + tmp11; /* phase 3 */ |
|||
data[dataIndex + JpegConstants.DCTSIZE * 4] = tmp10 - tmp11; |
|||
|
|||
int z1 = FAST_INTEGER_MULTIPLY(tmp12 + tmp13, FAST_INTEGER_FIX_0_707106781); /* c4 */ |
|||
data[dataIndex + JpegConstants.DCTSIZE * 2] = tmp13 + z1; /* phase 5 */ |
|||
data[dataIndex + JpegConstants.DCTSIZE * 6] = tmp13 - z1; |
|||
|
|||
/* Odd part */ |
|||
|
|||
tmp10 = tmp4 + tmp5; /* phase 2 */ |
|||
tmp11 = tmp5 + tmp6; |
|||
tmp12 = tmp6 + tmp7; |
|||
|
|||
/* The rotator is modified from fig 4-8 to avoid extra negations. */ |
|||
int z5 = FAST_INTEGER_MULTIPLY(tmp10 - tmp12, FAST_INTEGER_FIX_0_382683433); /* c6 */ |
|||
int z2 = FAST_INTEGER_MULTIPLY(tmp10, FAST_INTEGER_FIX_0_541196100) + z5; /* c2-c6 */ |
|||
int z4 = FAST_INTEGER_MULTIPLY(tmp12, FAST_INTEGER_FIX_1_306562965) + z5; /* c2+c6 */ |
|||
int z3 = FAST_INTEGER_MULTIPLY(tmp11, FAST_INTEGER_FIX_0_707106781); /* c4 */ |
|||
|
|||
int z11 = tmp7 + z3; /* phase 5 */ |
|||
int z13 = tmp7 - z3; |
|||
|
|||
data[dataIndex + JpegConstants.DCTSIZE * 5] = z13 + z2; /* phase 6 */ |
|||
data[dataIndex + JpegConstants.DCTSIZE * 3] = z13 - z2; |
|||
data[dataIndex + JpegConstants.DCTSIZE * 1] = z11 + z4; |
|||
data[dataIndex + JpegConstants.DCTSIZE * 7] = z11 - z4; |
|||
|
|||
dataIndex++; /* advance pointer to next column */ |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Perform the forward DCT on one block of samples.
|
|||
/// NOTE: this code only copes with 8x8 DCTs.
|
|||
///
|
|||
/// A slow-but-accurate integer implementation of the
|
|||
/// forward DCT (Discrete Cosine Transform).
|
|||
///
|
|||
/// A 2-D DCT can be done by 1-D DCT on each row followed by 1-D DCT
|
|||
/// on each column. Direct algorithms are also available, but they are
|
|||
/// much more complex and seem not to be any faster when reduced to code.
|
|||
///
|
|||
/// This implementation is based on an algorithm described in
|
|||
/// C. Loeffler, A. Ligtenberg and G. Moschytz, "Practical Fast 1-D DCT
|
|||
/// Algorithms with 11 Multiplications", Proc. Int'l. Conf. on Acoustics,
|
|||
/// Speech, and Signal Processing 1989 (ICASSP '89), pp. 988-991.
|
|||
/// The primary algorithm described there uses 11 multiplies and 29 adds.
|
|||
/// We use their alternate method with 12 multiplies and 32 adds.
|
|||
/// The advantage of this method is that no data path contains more than one
|
|||
/// multiplication; this allows a very simple and accurate implementation in
|
|||
/// scaled fixed-point arithmetic, with a minimal number of shifts.
|
|||
///
|
|||
/// The poop on this scaling stuff is as follows:
|
|||
///
|
|||
/// Each 1-D DCT step produces outputs which are a factor of sqrt(N)
|
|||
/// larger than the true DCT outputs. The final outputs are therefore
|
|||
/// a factor of N larger than desired; since N=8 this can be cured by
|
|||
/// a simple right shift at the end of the algorithm. The advantage of
|
|||
/// this arrangement is that we save two multiplications per 1-D DCT,
|
|||
/// because the y0 and y4 outputs need not be divided by sqrt(N).
|
|||
/// In the IJG code, this factor of 8 is removed by the quantization
|
|||
/// step, NOT here.
|
|||
///
|
|||
/// We have to do addition and subtraction of the integer inputs, which
|
|||
/// is no problem, and multiplication by fractional constants, which is
|
|||
/// a problem to do in integer arithmetic. We multiply all the constants
|
|||
/// by CONST_SCALE and convert them to integer constants (thus retaining
|
|||
/// SLOW_INTEGER_CONST_BITS bits of precision in the constants). After doing a
|
|||
/// multiplication we have to divide the product by CONST_SCALE, with proper
|
|||
/// rounding, to produce the correct output. This division can be done
|
|||
/// cheaply as a right shift of SLOW_INTEGER_CONST_BITS bits. We postpone shifting
|
|||
/// as long as possible so that partial sums can be added together with
|
|||
/// full fractional precision.
|
|||
///
|
|||
/// The outputs of the first pass are scaled up by SLOW_INTEGER_PASS1_BITS bits so that
|
|||
/// they are represented to better-than-integral precision. These outputs
|
|||
/// require BITS_IN_JSAMPLE + SLOW_INTEGER_PASS1_BITS + 3 bits; this fits in a 16-bit word
|
|||
/// with the recommended scaling. (For 12-bit sample data, the intermediate
|
|||
/// array is int anyway.)
|
|||
///
|
|||
/// To avoid overflow of the 32-bit intermediate results in pass 2, we must
|
|||
/// have BITS_IN_JSAMPLE + SLOW_INTEGER_CONST_BITS + SLOW_INTEGER_PASS1_BITS <= 26. Error analysis
|
|||
/// shows that the values given below are the most effective.
|
|||
/// </summary>
|
|||
private static void jpeg_fdct_islow(int[] data) |
|||
{ |
|||
/* Pass 1: process rows. */ |
|||
/* Note results are scaled up by sqrt(8) compared to a true DCT; */ |
|||
/* furthermore, we scale the results by 2**SLOW_INTEGER_PASS1_BITS. */ |
|||
int dataIndex = 0; |
|||
for (int ctr = JpegConstants.DCTSIZE - 1; ctr >= 0; ctr--) |
|||
{ |
|||
int tmp0 = data[dataIndex + 0] + data[dataIndex + 7]; |
|||
int tmp7 = data[dataIndex + 0] - data[dataIndex + 7]; |
|||
int tmp1 = data[dataIndex + 1] + data[dataIndex + 6]; |
|||
int tmp6 = data[dataIndex + 1] - data[dataIndex + 6]; |
|||
int tmp2 = data[dataIndex + 2] + data[dataIndex + 5]; |
|||
int tmp5 = data[dataIndex + 2] - data[dataIndex + 5]; |
|||
int tmp3 = data[dataIndex + 3] + data[dataIndex + 4]; |
|||
int tmp4 = data[dataIndex + 3] - data[dataIndex + 4]; |
|||
|
|||
/* Even part per LL&M figure 1 --- note that published figure is faulty; |
|||
* rotator "sqrt(2)*c1" should be "sqrt(2)*c6". |
|||
*/ |
|||
|
|||
int tmp10 = tmp0 + tmp3; |
|||
int tmp13 = tmp0 - tmp3; |
|||
int tmp11 = tmp1 + tmp2; |
|||
int tmp12 = tmp1 - tmp2; |
|||
|
|||
data[dataIndex + 0] = (tmp10 + tmp11) << SLOW_INTEGER_PASS1_BITS; |
|||
data[dataIndex + 4] = (tmp10 - tmp11) << SLOW_INTEGER_PASS1_BITS; |
|||
|
|||
int z1 = (tmp12 + tmp13) * SLOW_INTEGER_FIX_0_541196100; |
|||
data[dataIndex + 2] = JpegUtils.DESCALE(z1 + tmp13 * SLOW_INTEGER_FIX_0_765366865, |
|||
SLOW_INTEGER_CONST_BITS - SLOW_INTEGER_PASS1_BITS); |
|||
data[dataIndex + 6] = JpegUtils.DESCALE(z1 + tmp12 * (-SLOW_INTEGER_FIX_1_847759065), |
|||
SLOW_INTEGER_CONST_BITS - SLOW_INTEGER_PASS1_BITS); |
|||
|
|||
/* Odd part per figure 8 --- note paper omits factor of sqrt(2). |
|||
* cK represents cos(K*pi/16). |
|||
* i0..i3 in the paper are tmp4..tmp7 here. |
|||
*/ |
|||
|
|||
z1 = tmp4 + tmp7; |
|||
int z2 = tmp5 + tmp6; |
|||
int z3 = tmp4 + tmp6; |
|||
int z4 = tmp5 + tmp7; |
|||
int z5 = (z3 + z4) * SLOW_INTEGER_FIX_1_175875602; /* sqrt(2) * c3 */ |
|||
|
|||
tmp4 = tmp4 * SLOW_INTEGER_FIX_0_298631336; /* sqrt(2) * (-c1+c3+c5-c7) */ |
|||
tmp5 = tmp5 * SLOW_INTEGER_FIX_2_053119869; /* sqrt(2) * ( c1+c3-c5+c7) */ |
|||
tmp6 = tmp6 * SLOW_INTEGER_FIX_3_072711026; /* sqrt(2) * ( c1+c3+c5-c7) */ |
|||
tmp7 = tmp7 * SLOW_INTEGER_FIX_1_501321110; /* sqrt(2) * ( c1+c3-c5-c7) */ |
|||
z1 = z1 * (-SLOW_INTEGER_FIX_0_899976223); /* sqrt(2) * (c7-c3) */ |
|||
z2 = z2 * (-SLOW_INTEGER_FIX_2_562915447); /* sqrt(2) * (-c1-c3) */ |
|||
z3 = z3 * (-SLOW_INTEGER_FIX_1_961570560); /* sqrt(2) * (-c3-c5) */ |
|||
z4 = z4 * (-SLOW_INTEGER_FIX_0_390180644); /* sqrt(2) * (c5-c3) */ |
|||
|
|||
z3 += z5; |
|||
z4 += z5; |
|||
|
|||
data[dataIndex + 7] = JpegUtils.DESCALE(tmp4 + z1 + z3, SLOW_INTEGER_CONST_BITS - SLOW_INTEGER_PASS1_BITS); |
|||
data[dataIndex + 5] = JpegUtils.DESCALE(tmp5 + z2 + z4, SLOW_INTEGER_CONST_BITS - SLOW_INTEGER_PASS1_BITS); |
|||
data[dataIndex + 3] = JpegUtils.DESCALE(tmp6 + z2 + z3, SLOW_INTEGER_CONST_BITS - SLOW_INTEGER_PASS1_BITS); |
|||
data[dataIndex + 1] = JpegUtils.DESCALE(tmp7 + z1 + z4, SLOW_INTEGER_CONST_BITS - SLOW_INTEGER_PASS1_BITS); |
|||
|
|||
dataIndex += JpegConstants.DCTSIZE; /* advance pointer to next row */ |
|||
} |
|||
|
|||
/* Pass 2: process columns. |
|||
* We remove the SLOW_INTEGER_PASS1_BITS scaling, but leave the results scaled up |
|||
* by an overall factor of 8. |
|||
*/ |
|||
|
|||
dataIndex = 0; |
|||
for (int ctr = JpegConstants.DCTSIZE - 1; ctr >= 0; ctr--) |
|||
{ |
|||
int tmp0 = data[dataIndex + JpegConstants.DCTSIZE * 0] + data[dataIndex + JpegConstants.DCTSIZE * 7]; |
|||
int tmp7 = data[dataIndex + JpegConstants.DCTSIZE * 0] - data[dataIndex + JpegConstants.DCTSIZE * 7]; |
|||
int tmp1 = data[dataIndex + JpegConstants.DCTSIZE * 1] + data[dataIndex + JpegConstants.DCTSIZE * 6]; |
|||
int tmp6 = data[dataIndex + JpegConstants.DCTSIZE * 1] - data[dataIndex + JpegConstants.DCTSIZE * 6]; |
|||
int tmp2 = data[dataIndex + JpegConstants.DCTSIZE * 2] + data[dataIndex + JpegConstants.DCTSIZE * 5]; |
|||
int tmp5 = data[dataIndex + JpegConstants.DCTSIZE * 2] - data[dataIndex + JpegConstants.DCTSIZE * 5]; |
|||
int tmp3 = data[dataIndex + JpegConstants.DCTSIZE * 3] + data[dataIndex + JpegConstants.DCTSIZE * 4]; |
|||
int tmp4 = data[dataIndex + JpegConstants.DCTSIZE * 3] - data[dataIndex + JpegConstants.DCTSIZE * 4]; |
|||
|
|||
/* Even part per LL&M figure 1 --- note that published figure is faulty; |
|||
* rotator "sqrt(2)*c1" should be "sqrt(2)*c6". |
|||
*/ |
|||
|
|||
int tmp10 = tmp0 + tmp3; |
|||
int tmp13 = tmp0 - tmp3; |
|||
int tmp11 = tmp1 + tmp2; |
|||
int tmp12 = tmp1 - tmp2; |
|||
|
|||
data[dataIndex + JpegConstants.DCTSIZE * 0] = JpegUtils.DESCALE(tmp10 + tmp11, SLOW_INTEGER_PASS1_BITS); |
|||
data[dataIndex + JpegConstants.DCTSIZE * 4] = JpegUtils.DESCALE(tmp10 - tmp11, SLOW_INTEGER_PASS1_BITS); |
|||
|
|||
int z1 = (tmp12 + tmp13) * SLOW_INTEGER_FIX_0_541196100; |
|||
data[dataIndex + JpegConstants.DCTSIZE * 2] = JpegUtils.DESCALE(z1 + tmp13 * SLOW_INTEGER_FIX_0_765366865, |
|||
SLOW_INTEGER_CONST_BITS + SLOW_INTEGER_PASS1_BITS); |
|||
data[dataIndex + JpegConstants.DCTSIZE * 6] = JpegUtils.DESCALE(z1 + tmp12 * (-SLOW_INTEGER_FIX_1_847759065), |
|||
SLOW_INTEGER_CONST_BITS + SLOW_INTEGER_PASS1_BITS); |
|||
|
|||
/* Odd part per figure 8 --- note paper omits factor of sqrt(2). |
|||
* cK represents cos(K*pi/16). |
|||
* i0..i3 in the paper are tmp4..tmp7 here. |
|||
*/ |
|||
|
|||
z1 = tmp4 + tmp7; |
|||
int z2 = tmp5 + tmp6; |
|||
int z3 = tmp4 + tmp6; |
|||
int z4 = tmp5 + tmp7; |
|||
int z5 = (z3 + z4) * SLOW_INTEGER_FIX_1_175875602; /* sqrt(2) * c3 */ |
|||
|
|||
tmp4 = tmp4 * SLOW_INTEGER_FIX_0_298631336; /* sqrt(2) * (-c1+c3+c5-c7) */ |
|||
tmp5 = tmp5 * SLOW_INTEGER_FIX_2_053119869; /* sqrt(2) * ( c1+c3-c5+c7) */ |
|||
tmp6 = tmp6 * SLOW_INTEGER_FIX_3_072711026; /* sqrt(2) * ( c1+c3+c5-c7) */ |
|||
tmp7 = tmp7 * SLOW_INTEGER_FIX_1_501321110; /* sqrt(2) * ( c1+c3-c5-c7) */ |
|||
z1 = z1 * (-SLOW_INTEGER_FIX_0_899976223); /* sqrt(2) * (c7-c3) */ |
|||
z2 = z2 * (-SLOW_INTEGER_FIX_2_562915447); /* sqrt(2) * (-c1-c3) */ |
|||
z3 = z3 * (-SLOW_INTEGER_FIX_1_961570560); /* sqrt(2) * (-c3-c5) */ |
|||
z4 = z4 * (-SLOW_INTEGER_FIX_0_390180644); /* sqrt(2) * (c5-c3) */ |
|||
|
|||
z3 += z5; |
|||
z4 += z5; |
|||
|
|||
data[dataIndex + JpegConstants.DCTSIZE * 7] = JpegUtils.DESCALE(tmp4 + z1 + z3, SLOW_INTEGER_CONST_BITS + SLOW_INTEGER_PASS1_BITS); |
|||
data[dataIndex + JpegConstants.DCTSIZE * 5] = JpegUtils.DESCALE(tmp5 + z2 + z4, SLOW_INTEGER_CONST_BITS + SLOW_INTEGER_PASS1_BITS); |
|||
data[dataIndex + JpegConstants.DCTSIZE * 3] = JpegUtils.DESCALE(tmp6 + z2 + z3, SLOW_INTEGER_CONST_BITS + SLOW_INTEGER_PASS1_BITS); |
|||
data[dataIndex + JpegConstants.DCTSIZE * 1] = JpegUtils.DESCALE(tmp7 + z1 + z4, SLOW_INTEGER_CONST_BITS + SLOW_INTEGER_PASS1_BITS); |
|||
|
|||
dataIndex++; /* advance pointer to next column */ |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Multiply a DCTELEM variable by an int constant, and immediately
|
|||
/// descale to yield a DCTELEM result.
|
|||
/// </summary>
|
|||
private static int FAST_INTEGER_MULTIPLY(int var, int c) |
|||
{ |
|||
#if !USE_ACCURATE_ROUNDING
|
|||
return (JpegUtils.RIGHT_SHIFT((var) * (c), FAST_INTEGER_CONST_BITS)); |
|||
#else
|
|||
return (JpegUtils.DESCALE((var) * (c), FAST_INTEGER_CONST_BITS)); |
|||
#endif
|
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,394 @@ |
|||
/* Copyright (C) 2008-2011, Bit Miracle |
|||
* http://www.bitmiracle.com
|
|||
* |
|||
* Copyright (C) 1994-1996, Thomas G. Lane. |
|||
* This file is part of the Independent JPEG Group's software. |
|||
* For conditions of distribution and use, see the accompanying README file. |
|||
* |
|||
*/ |
|||
|
|||
/* |
|||
* This file contains input control logic for the JPEG decompressor. |
|||
* These routines are concerned with controlling the decompressor's input |
|||
* processing (marker reading and coefficient decoding). |
|||
*/ |
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
|
|||
namespace BitMiracle.LibJpeg.Classic.Internal |
|||
{ |
|||
/// <summary>
|
|||
/// Input control module
|
|||
/// </summary>
|
|||
class jpeg_input_controller |
|||
{ |
|||
private jpeg_decompress_struct m_cinfo; |
|||
private bool m_consumeData; |
|||
private bool m_inheaders; /* true until first SOS is reached */ |
|||
private bool m_has_multiple_scans; /* True if file has multiple scans */ |
|||
private bool m_eoi_reached; /* True when EOI has been consumed */ |
|||
|
|||
/// <summary>
|
|||
/// Initialize the input controller module.
|
|||
/// This is called only once, when the decompression object is created.
|
|||
/// </summary>
|
|||
public jpeg_input_controller(jpeg_decompress_struct cinfo) |
|||
{ |
|||
m_cinfo = cinfo; |
|||
|
|||
/* Initialize state: can't use reset_input_controller since we don't |
|||
* want to try to reset other modules yet. |
|||
*/ |
|||
m_inheaders = true; |
|||
} |
|||
|
|||
public ReadResult consume_input() |
|||
{ |
|||
if (m_consumeData) |
|||
return m_cinfo.m_coef.consume_data(); |
|||
|
|||
return consume_markers(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reset state to begin a fresh datastream.
|
|||
/// </summary>
|
|||
public void reset_input_controller() |
|||
{ |
|||
m_consumeData = false; |
|||
m_has_multiple_scans = false; /* "unknown" would be better */ |
|||
m_eoi_reached = false; |
|||
m_inheaders = true; |
|||
|
|||
/* Reset other modules */ |
|||
m_cinfo.m_err.reset_error_mgr(); |
|||
m_cinfo.m_marker.reset_marker_reader(); |
|||
|
|||
/* Reset progression state -- would be cleaner if entropy decoder did this */ |
|||
m_cinfo.m_coef_bits = null; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initialize the input modules to read a scan of compressed data.
|
|||
/// The first call to this is done after initializing
|
|||
/// the entire decompressor (during jpeg_start_decompress).
|
|||
/// Subsequent calls come from consume_markers, below.
|
|||
/// </summary>
|
|||
public void start_input_pass() |
|||
{ |
|||
per_scan_setup(); |
|||
latch_quant_tables(); |
|||
m_cinfo.m_entropy.start_pass(); |
|||
m_cinfo.m_coef.start_input_pass(); |
|||
m_consumeData = true; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Finish up after inputting a compressed-data scan.
|
|||
/// This is called by the coefficient controller after it's read all
|
|||
/// the expected data of the scan.
|
|||
/// </summary>
|
|||
public void finish_input_pass() |
|||
{ |
|||
m_consumeData = false; |
|||
} |
|||
|
|||
public bool HasMultipleScans() |
|||
{ |
|||
return m_has_multiple_scans; |
|||
} |
|||
|
|||
public bool EOIReached() |
|||
{ |
|||
return m_eoi_reached; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Read JPEG markers before, between, or after compressed-data scans.
|
|||
/// Change state as necessary when a new scan is reached.
|
|||
/// Return value is JPEG_SUSPENDED, JPEG_REACHED_SOS, or JPEG_REACHED_EOI.
|
|||
///
|
|||
/// The consume_input method pointer points either here or to the
|
|||
/// coefficient controller's consume_data routine, depending on whether
|
|||
/// we are reading a compressed data segment or inter-segment markers.
|
|||
/// </summary>
|
|||
private ReadResult consume_markers() |
|||
{ |
|||
ReadResult val; |
|||
|
|||
if (m_eoi_reached) /* After hitting EOI, read no further */ |
|||
return ReadResult.JPEG_REACHED_EOI; |
|||
|
|||
val = m_cinfo.m_marker.read_markers(); |
|||
|
|||
switch (val) |
|||
{ |
|||
case ReadResult.JPEG_REACHED_SOS: |
|||
/* Found SOS */ |
|||
if (m_inheaders) |
|||
{ |
|||
/* 1st SOS */ |
|||
initial_setup(); |
|||
m_inheaders = false; |
|||
/* Note: start_input_pass must be called by jpeg_decomp_master |
|||
* before any more input can be consumed. |
|||
*/ |
|||
} |
|||
else |
|||
{ |
|||
/* 2nd or later SOS marker */ |
|||
if (!m_has_multiple_scans) |
|||
{ |
|||
/* Oops, I wasn't expecting this! */ |
|||
m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_EOI_EXPECTED); |
|||
} |
|||
|
|||
m_cinfo.m_inputctl.start_input_pass(); |
|||
} |
|||
break; |
|||
case ReadResult.JPEG_REACHED_EOI: |
|||
/* Found EOI */ |
|||
m_eoi_reached = true; |
|||
if (m_inheaders) |
|||
{ |
|||
/* Tables-only datastream, apparently */ |
|||
if (m_cinfo.m_marker.SawSOF()) |
|||
m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_SOF_NO_SOS); |
|||
} |
|||
else |
|||
{ |
|||
/* Prevent infinite loop in coef ctlr's decompress_data routine |
|||
* if user set output_scan_number larger than number of scans. |
|||
*/ |
|||
if (m_cinfo.m_output_scan_number > m_cinfo.m_input_scan_number) |
|||
m_cinfo.m_output_scan_number = m_cinfo.m_input_scan_number; |
|||
} |
|||
break; |
|||
case ReadResult.JPEG_SUSPENDED: |
|||
break; |
|||
} |
|||
|
|||
return val; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Routines to calculate various quantities related to the size of the image.
|
|||
/// Called once, when first SOS marker is reached
|
|||
/// </summary>
|
|||
private void initial_setup() |
|||
{ |
|||
/* Make sure image isn't bigger than I can handle */ |
|||
if (m_cinfo.m_image_height > JpegConstants.JPEG_MAX_DIMENSION || |
|||
m_cinfo.m_image_width > JpegConstants.JPEG_MAX_DIMENSION) |
|||
{ |
|||
m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_IMAGE_TOO_BIG, (int)JpegConstants.JPEG_MAX_DIMENSION); |
|||
|
|||
} |
|||
|
|||
/* For now, precision must match compiled-in value... */ |
|||
if (m_cinfo.m_data_precision != JpegConstants.BITS_IN_JSAMPLE) |
|||
m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_PRECISION, m_cinfo.m_data_precision); |
|||
|
|||
/* Check that number of components won't exceed internal array sizes */ |
|||
if (m_cinfo.m_num_components > JpegConstants.MAX_COMPONENTS) |
|||
m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_COMPONENT_COUNT, m_cinfo.m_num_components, JpegConstants.MAX_COMPONENTS); |
|||
|
|||
/* Compute maximum sampling factors; check factor validity */ |
|||
m_cinfo.m_max_h_samp_factor = 1; |
|||
m_cinfo.m_max_v_samp_factor = 1; |
|||
|
|||
for (int ci = 0; ci < m_cinfo.m_num_components; ci++) |
|||
{ |
|||
if (m_cinfo.Comp_info[ci].H_samp_factor <= 0 || m_cinfo.Comp_info[ci].H_samp_factor > JpegConstants.MAX_SAMP_FACTOR || |
|||
m_cinfo.Comp_info[ci].V_samp_factor <= 0 || m_cinfo.Comp_info[ci].V_samp_factor > JpegConstants.MAX_SAMP_FACTOR) |
|||
{ |
|||
m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_SAMPLING); |
|||
} |
|||
|
|||
m_cinfo.m_max_h_samp_factor = Math.Max(m_cinfo.m_max_h_samp_factor, m_cinfo.Comp_info[ci].H_samp_factor); |
|||
m_cinfo.m_max_v_samp_factor = Math.Max(m_cinfo.m_max_v_samp_factor, m_cinfo.Comp_info[ci].V_samp_factor); |
|||
} |
|||
|
|||
/* We initialize DCT_scaled_size and min_DCT_scaled_size to DCTSIZE. |
|||
* In the full decompressor, this will be overridden jpeg_decomp_master; |
|||
* but in the transcoder, jpeg_decomp_master is not used, so we must do it here. |
|||
*/ |
|||
m_cinfo.m_min_DCT_scaled_size = JpegConstants.DCTSIZE; |
|||
|
|||
/* Compute dimensions of components */ |
|||
for (int ci = 0; ci < m_cinfo.m_num_components; ci++) |
|||
{ |
|||
m_cinfo.Comp_info[ci].DCT_scaled_size = JpegConstants.DCTSIZE; |
|||
|
|||
/* Size in DCT blocks */ |
|||
m_cinfo.Comp_info[ci].Width_in_blocks = JpegUtils.jdiv_round_up( |
|||
m_cinfo.m_image_width * m_cinfo.Comp_info[ci].H_samp_factor, |
|||
m_cinfo.m_max_h_samp_factor * JpegConstants.DCTSIZE); |
|||
|
|||
m_cinfo.Comp_info[ci].height_in_blocks = JpegUtils.jdiv_round_up( |
|||
m_cinfo.m_image_height * m_cinfo.Comp_info[ci].V_samp_factor, |
|||
m_cinfo.m_max_v_samp_factor * JpegConstants.DCTSIZE); |
|||
|
|||
/* downsampled_width and downsampled_height will also be overridden by |
|||
* jpeg_decomp_master if we are doing full decompression. The transcoder library |
|||
* doesn't use these values, but the calling application might. |
|||
*/ |
|||
/* Size in samples */ |
|||
m_cinfo.Comp_info[ci].downsampled_width = JpegUtils.jdiv_round_up( |
|||
m_cinfo.m_image_width * m_cinfo.Comp_info[ci].H_samp_factor, |
|||
m_cinfo.m_max_h_samp_factor); |
|||
|
|||
m_cinfo.Comp_info[ci].downsampled_height = JpegUtils.jdiv_round_up( |
|||
m_cinfo.m_image_height * m_cinfo.Comp_info[ci].V_samp_factor, |
|||
m_cinfo.m_max_v_samp_factor); |
|||
|
|||
/* Mark component needed, until color conversion says otherwise */ |
|||
m_cinfo.Comp_info[ci].component_needed = true; |
|||
|
|||
/* Mark no quantization table yet saved for component */ |
|||
m_cinfo.Comp_info[ci].quant_table = null; |
|||
} |
|||
|
|||
/* Compute number of fully interleaved MCU rows. */ |
|||
m_cinfo.m_total_iMCU_rows = JpegUtils.jdiv_round_up( |
|||
m_cinfo.m_image_height, m_cinfo.m_max_v_samp_factor * JpegConstants.DCTSIZE); |
|||
|
|||
/* Decide whether file contains multiple scans */ |
|||
if (m_cinfo.m_comps_in_scan < m_cinfo.m_num_components || m_cinfo.m_progressive_mode) |
|||
m_cinfo.m_inputctl.m_has_multiple_scans = true; |
|||
else |
|||
m_cinfo.m_inputctl.m_has_multiple_scans = false; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Save away a copy of the Q-table referenced by each component present
|
|||
/// in the current scan, unless already saved during a prior scan.
|
|||
///
|
|||
/// In a multiple-scan JPEG file, the encoder could assign different components
|
|||
/// the same Q-table slot number, but change table definitions between scans
|
|||
/// so that each component uses a different Q-table. (The IJG encoder is not
|
|||
/// currently capable of doing this, but other encoders might.) Since we want
|
|||
/// to be able to dequantize all the components at the end of the file, this
|
|||
/// means that we have to save away the table actually used for each component.
|
|||
/// We do this by copying the table at the start of the first scan containing
|
|||
/// the component.
|
|||
/// The JPEG spec prohibits the encoder from changing the contents of a Q-table
|
|||
/// slot between scans of a component using that slot. If the encoder does so
|
|||
/// anyway, this decoder will simply use the Q-table values that were current
|
|||
/// at the start of the first scan for the component.
|
|||
///
|
|||
/// The decompressor output side looks only at the saved quant tables,
|
|||
/// not at the current Q-table slots.
|
|||
/// </summary>
|
|||
private void latch_quant_tables() |
|||
{ |
|||
for (int ci = 0; ci < m_cinfo.m_comps_in_scan; ci++) |
|||
{ |
|||
jpeg_component_info componentInfo = m_cinfo.Comp_info[m_cinfo.m_cur_comp_info[ci]]; |
|||
|
|||
/* No work if we already saved Q-table for this component */ |
|||
if (componentInfo.quant_table != null) |
|||
continue; |
|||
|
|||
/* Make sure specified quantization table is present */ |
|||
int qtblno = componentInfo.Quant_tbl_no; |
|||
if (qtblno < 0 || qtblno >= JpegConstants.NUM_QUANT_TBLS || m_cinfo.m_quant_tbl_ptrs[qtblno] == null) |
|||
m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_NO_QUANT_TABLE, qtblno); |
|||
|
|||
/* OK, save away the quantization table */ |
|||
JQUANT_TBL qtbl = new JQUANT_TBL(); |
|||
Buffer.BlockCopy(m_cinfo.m_quant_tbl_ptrs[qtblno].quantval, 0, |
|||
qtbl.quantval, 0, qtbl.quantval.Length * sizeof(short)); |
|||
qtbl.Sent_table = m_cinfo.m_quant_tbl_ptrs[qtblno].Sent_table; |
|||
componentInfo.quant_table = qtbl; |
|||
m_cinfo.Comp_info[m_cinfo.m_cur_comp_info[ci]] = componentInfo; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Do computations that are needed before processing a JPEG scan
|
|||
/// cinfo.comps_in_scan and cinfo.cur_comp_info[] were set from SOS marker
|
|||
/// </summary>
|
|||
private void per_scan_setup() |
|||
{ |
|||
if (m_cinfo.m_comps_in_scan == 1) |
|||
{ |
|||
/* Noninterleaved (single-component) scan */ |
|||
jpeg_component_info componentInfo = m_cinfo.Comp_info[m_cinfo.m_cur_comp_info[0]]; |
|||
|
|||
/* Overall image size in MCUs */ |
|||
m_cinfo.m_MCUs_per_row = componentInfo.Width_in_blocks; |
|||
m_cinfo.m_MCU_rows_in_scan = componentInfo.height_in_blocks; |
|||
|
|||
/* For noninterleaved scan, always one block per MCU */ |
|||
componentInfo.MCU_width = 1; |
|||
componentInfo.MCU_height = 1; |
|||
componentInfo.MCU_blocks = 1; |
|||
componentInfo.MCU_sample_width = componentInfo.DCT_scaled_size; |
|||
componentInfo.last_col_width = 1; |
|||
|
|||
/* For noninterleaved scans, it is convenient to define last_row_height |
|||
* as the number of block rows present in the last iMCU row. |
|||
*/ |
|||
int tmp = componentInfo.height_in_blocks % componentInfo.V_samp_factor; |
|||
if (tmp == 0) |
|||
tmp = componentInfo.V_samp_factor; |
|||
componentInfo.last_row_height = tmp; |
|||
m_cinfo.Comp_info[m_cinfo.m_cur_comp_info[0]] = componentInfo; |
|||
|
|||
/* Prepare array describing MCU composition */ |
|||
m_cinfo.m_blocks_in_MCU = 1; |
|||
m_cinfo.m_MCU_membership[0] = 0; |
|||
} |
|||
else |
|||
{ |
|||
/* Interleaved (multi-component) scan */ |
|||
if (m_cinfo.m_comps_in_scan <= 0 || m_cinfo.m_comps_in_scan > JpegConstants.MAX_COMPS_IN_SCAN) |
|||
m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_COMPONENT_COUNT, m_cinfo.m_comps_in_scan, JpegConstants.MAX_COMPS_IN_SCAN); |
|||
|
|||
/* Overall image size in MCUs */ |
|||
m_cinfo.m_MCUs_per_row = JpegUtils.jdiv_round_up( |
|||
m_cinfo.m_image_width, m_cinfo.m_max_h_samp_factor * JpegConstants.DCTSIZE); |
|||
|
|||
m_cinfo.m_MCU_rows_in_scan = JpegUtils.jdiv_round_up( |
|||
m_cinfo.m_image_height, m_cinfo.m_max_v_samp_factor * JpegConstants.DCTSIZE); |
|||
|
|||
m_cinfo.m_blocks_in_MCU = 0; |
|||
|
|||
for (int ci = 0; ci < m_cinfo.m_comps_in_scan; ci++) |
|||
{ |
|||
jpeg_component_info componentInfo = m_cinfo.Comp_info[m_cinfo.m_cur_comp_info[ci]]; |
|||
|
|||
/* Sampling factors give # of blocks of component in each MCU */ |
|||
componentInfo.MCU_width = componentInfo.H_samp_factor; |
|||
componentInfo.MCU_height = componentInfo.V_samp_factor; |
|||
componentInfo.MCU_blocks = componentInfo.MCU_width * componentInfo.MCU_height; |
|||
componentInfo.MCU_sample_width = componentInfo.MCU_width * componentInfo.DCT_scaled_size; |
|||
|
|||
/* Figure number of non-dummy blocks in last MCU column & row */ |
|||
int tmp = componentInfo.Width_in_blocks % componentInfo.MCU_width; |
|||
if (tmp == 0) |
|||
tmp = componentInfo.MCU_width; |
|||
componentInfo.last_col_width = tmp; |
|||
|
|||
tmp = componentInfo.height_in_blocks % componentInfo.MCU_height; |
|||
if (tmp == 0) |
|||
tmp = componentInfo.MCU_height; |
|||
componentInfo.last_row_height = tmp; |
|||
|
|||
/* Prepare array describing MCU composition */ |
|||
int mcublks = componentInfo.MCU_blocks; |
|||
if (m_cinfo.m_blocks_in_MCU + mcublks > JpegConstants.D_MAX_BLOCKS_IN_MCU) |
|||
m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_MCU_SIZE); |
|||
|
|||
m_cinfo.Comp_info[m_cinfo.m_cur_comp_info[ci]] = componentInfo; |
|||
|
|||
while (mcublks-- > 0) |
|||
m_cinfo.m_MCU_membership[m_cinfo.m_blocks_in_MCU++] = ci; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1 @@ |
|||
db377809a2636d474a6da2cbb6fe7478f2100495 |
|||
File diff suppressed because it is too large
@ -0,0 +1,515 @@ |
|||
/* Copyright (C) 2008-2011, Bit Miracle |
|||
* http://www.bitmiracle.com
|
|||
* |
|||
* Copyright (C) 1994-1996, Thomas G. Lane. |
|||
* This file is part of the Independent JPEG Group's software. |
|||
* For conditions of distribution and use, see the accompanying README file. |
|||
* |
|||
*/ |
|||
|
|||
/* |
|||
* This file contains routines to write JPEG datastream markers. |
|||
*/ |
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
|
|||
namespace BitMiracle.LibJpeg.Classic.Internal |
|||
{ |
|||
/// <summary>
|
|||
/// Marker writing
|
|||
/// </summary>
|
|||
class jpeg_marker_writer |
|||
{ |
|||
private jpeg_compress_struct m_cinfo; |
|||
private int m_last_restart_interval; /* last DRI value emitted; 0 after SOI */ |
|||
|
|||
public jpeg_marker_writer(jpeg_compress_struct cinfo) |
|||
{ |
|||
m_cinfo = cinfo; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Write datastream header.
|
|||
/// This consists of an SOI and optional APPn markers.
|
|||
/// We recommend use of the JFIF marker, but not the Adobe marker,
|
|||
/// when using YCbCr or grayscale data. The JFIF marker should NOT
|
|||
/// be used for any other JPEG colorspace. The Adobe marker is helpful
|
|||
/// to distinguish RGB, CMYK, and YCCK colorspaces.
|
|||
/// Note that an application can write additional header markers after
|
|||
/// jpeg_start_compress returns.
|
|||
/// </summary>
|
|||
public void write_file_header() |
|||
{ |
|||
emit_marker(JPEG_MARKER.SOI); /* first the SOI */ |
|||
|
|||
/* SOI is defined to reset restart interval to 0 */ |
|||
m_last_restart_interval = 0; |
|||
|
|||
if (m_cinfo.m_write_JFIF_header) /* next an optional JFIF APP0 */ |
|||
emit_jfif_app0(); |
|||
if (m_cinfo.m_write_Adobe_marker) /* next an optional Adobe APP14 */ |
|||
emit_adobe_app14(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Write frame header.
|
|||
/// This consists of DQT and SOFn markers.
|
|||
/// Note that we do not emit the SOF until we have emitted the DQT(s).
|
|||
/// This avoids compatibility problems with incorrect implementations that
|
|||
/// try to error-check the quant table numbers as soon as they see the SOF.
|
|||
/// </summary>
|
|||
public void write_frame_header() |
|||
{ |
|||
/* Emit DQT for each quantization table. |
|||
* Note that emit_dqt() suppresses any duplicate tables. |
|||
*/ |
|||
int prec = 0; |
|||
for (int ci = 0; ci < m_cinfo.m_num_components; ci++) |
|||
prec += emit_dqt(m_cinfo.Component_info[ci].Quant_tbl_no); |
|||
|
|||
/* now prec is nonzero iff there are any 16-bit quant tables. */ |
|||
|
|||
/* Check for a non-baseline specification. |
|||
* Note we assume that Huffman table numbers won't be changed later. |
|||
*/ |
|||
bool is_baseline; |
|||
if (m_cinfo.m_progressive_mode || m_cinfo.m_data_precision != 8) |
|||
{ |
|||
is_baseline = false; |
|||
} |
|||
else |
|||
{ |
|||
is_baseline = true; |
|||
for (int ci = 0; ci < m_cinfo.m_num_components; ci++) |
|||
{ |
|||
if (m_cinfo.Component_info[ci].Dc_tbl_no > 1 || m_cinfo.Component_info[ci].Ac_tbl_no > 1) |
|||
is_baseline = false; |
|||
} |
|||
|
|||
if (prec != 0 && is_baseline) |
|||
{ |
|||
is_baseline = false; |
|||
/* If it's baseline except for quantizer size, warn the user */ |
|||
m_cinfo.TRACEMS(0, J_MESSAGE_CODE.JTRC_16BIT_TABLES); |
|||
} |
|||
} |
|||
|
|||
/* Emit the proper SOF marker */ |
|||
if (m_cinfo.m_progressive_mode) |
|||
emit_sof(JPEG_MARKER.SOF2); /* SOF code for progressive Huffman */ |
|||
else if (is_baseline) |
|||
emit_sof(JPEG_MARKER.SOF0); /* SOF code for baseline implementation */ |
|||
else |
|||
emit_sof(JPEG_MARKER.SOF1); /* SOF code for non-baseline Huffman file */ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Write scan header.
|
|||
/// This consists of DHT or DAC markers, optional DRI, and SOS.
|
|||
/// Compressed data will be written following the SOS.
|
|||
/// </summary>
|
|||
public void write_scan_header() |
|||
{ |
|||
/* Emit Huffman tables. |
|||
* Note that emit_dht() suppresses any duplicate tables. |
|||
*/ |
|||
for (int i = 0; i < m_cinfo.m_comps_in_scan; i++) |
|||
{ |
|||
int ac_tbl_no = m_cinfo.Component_info[m_cinfo.m_cur_comp_info[i]].Ac_tbl_no; |
|||
int dc_tbl_no = m_cinfo.Component_info[m_cinfo.m_cur_comp_info[i]].Dc_tbl_no; |
|||
if (m_cinfo.m_progressive_mode) |
|||
{ |
|||
/* Progressive mode: only DC or only AC tables are used in one scan */ |
|||
if (m_cinfo.m_Ss == 0) |
|||
{ |
|||
if (m_cinfo.m_Ah == 0) |
|||
{ |
|||
/* DC needs no table for refinement scan */ |
|||
emit_dht(dc_tbl_no, false); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
emit_dht(ac_tbl_no, true); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
/* Sequential mode: need both DC and AC tables */ |
|||
emit_dht(dc_tbl_no, false); |
|||
emit_dht(ac_tbl_no, true); |
|||
} |
|||
} |
|||
|
|||
/* Emit DRI if required --- note that DRI value could change for each scan. |
|||
* We avoid wasting space with unnecessary DRIs, however. |
|||
*/ |
|||
if (m_cinfo.m_restart_interval != m_last_restart_interval) |
|||
{ |
|||
emit_dri(); |
|||
m_last_restart_interval = m_cinfo.m_restart_interval; |
|||
} |
|||
|
|||
emit_sos(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Write datastream trailer.
|
|||
/// </summary>
|
|||
public void write_file_trailer() |
|||
{ |
|||
emit_marker(JPEG_MARKER.EOI); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Write an abbreviated table-specification datastream.
|
|||
/// This consists of SOI, DQT and DHT tables, and EOI.
|
|||
/// Any table that is defined and not marked sent_table = true will be
|
|||
/// emitted. Note that all tables will be marked sent_table = true at exit.
|
|||
/// </summary>
|
|||
public void write_tables_only() |
|||
{ |
|||
emit_marker(JPEG_MARKER.SOI); |
|||
|
|||
for (int i = 0; i < JpegConstants.NUM_QUANT_TBLS; i++) |
|||
{ |
|||
if (m_cinfo.m_quant_tbl_ptrs[i] != null) |
|||
emit_dqt(i); |
|||
} |
|||
|
|||
for (int i = 0; i < JpegConstants.NUM_HUFF_TBLS; i++) |
|||
{ |
|||
if (m_cinfo.m_dc_huff_tbl_ptrs[i] != null) |
|||
emit_dht(i, false); |
|||
if (m_cinfo.m_ac_huff_tbl_ptrs[i] != null) |
|||
emit_dht(i, true); |
|||
} |
|||
|
|||
emit_marker(JPEG_MARKER.EOI); |
|||
} |
|||
|
|||
//////////////////////////////////////////////////////////////////////////
|
|||
// These routines allow writing an arbitrary marker with parameters.
|
|||
// The only intended use is to emit COM or APPn markers after calling
|
|||
// write_file_header and before calling write_frame_header.
|
|||
// Other uses are not guaranteed to produce desirable results.
|
|||
// Counting the parameter bytes properly is the caller's responsibility.
|
|||
|
|||
/// <summary>
|
|||
/// Emit an arbitrary marker header
|
|||
/// </summary>
|
|||
public void write_marker_header(int marker, int datalen) |
|||
{ |
|||
if (datalen > 65533) /* safety check */ |
|||
m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_LENGTH); |
|||
|
|||
emit_marker((JPEG_MARKER) marker); |
|||
|
|||
emit_2bytes(datalen + 2); /* total length */ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Emit one byte of marker parameters following write_marker_header
|
|||
/// </summary>
|
|||
public void write_marker_byte(byte val) |
|||
{ |
|||
emit_byte(val); |
|||
} |
|||
|
|||
//////////////////////////////////////////////////////////////////////////
|
|||
// Routines to write specific marker types.
|
|||
//
|
|||
|
|||
/// <summary>
|
|||
/// Emit a SOS marker
|
|||
/// </summary>
|
|||
private void emit_sos() |
|||
{ |
|||
emit_marker(JPEG_MARKER.SOS); |
|||
|
|||
emit_2bytes(2 * m_cinfo.m_comps_in_scan + 2 + 1 + 3); /* length */ |
|||
|
|||
emit_byte(m_cinfo.m_comps_in_scan); |
|||
|
|||
for (int i = 0; i < m_cinfo.m_comps_in_scan; i++) |
|||
{ |
|||
int componentIndex = m_cinfo.m_cur_comp_info[i]; |
|||
emit_byte(m_cinfo.Component_info[componentIndex].Component_id); |
|||
|
|||
int td = m_cinfo.Component_info[componentIndex].Dc_tbl_no; |
|||
int ta = m_cinfo.Component_info[componentIndex].Ac_tbl_no; |
|||
if (m_cinfo.m_progressive_mode) |
|||
{ |
|||
/* Progressive mode: only DC or only AC tables are used in one scan; |
|||
* furthermore, Huffman coding of DC refinement uses no table at all. |
|||
* We emit 0 for unused field(s); this is recommended by the P&M text |
|||
* but does not seem to be specified in the standard. |
|||
*/ |
|||
if (m_cinfo.m_Ss == 0) |
|||
{ |
|||
/* DC scan */ |
|||
ta = 0; |
|||
if (m_cinfo.m_Ah != 0) |
|||
{ |
|||
/* no DC table either */ |
|||
td = 0; |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
/* AC scan */ |
|||
td = 0; |
|||
} |
|||
} |
|||
|
|||
emit_byte((td << 4) + ta); |
|||
} |
|||
|
|||
emit_byte(m_cinfo.m_Ss); |
|||
emit_byte(m_cinfo.m_Se); |
|||
emit_byte((m_cinfo.m_Ah << 4) + m_cinfo.m_Al); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Emit a SOF marker
|
|||
/// </summary>
|
|||
private void emit_sof(JPEG_MARKER code) |
|||
{ |
|||
emit_marker(code); |
|||
|
|||
emit_2bytes(3 * m_cinfo.m_num_components + 2 + 5 + 1); /* length */ |
|||
|
|||
/* Make sure image isn't bigger than SOF field can handle */ |
|||
if (m_cinfo.m_image_height > 65535 || m_cinfo.m_image_width > 65535) |
|||
m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_IMAGE_TOO_BIG, 65535); |
|||
|
|||
emit_byte(m_cinfo.m_data_precision); |
|||
emit_2bytes(m_cinfo.m_image_height); |
|||
emit_2bytes(m_cinfo.m_image_width); |
|||
|
|||
emit_byte(m_cinfo.m_num_components); |
|||
|
|||
for (int ci = 0; ci < m_cinfo.m_num_components; ci++) |
|||
{ |
|||
jpeg_component_info componentInfo = m_cinfo.Component_info[ci]; |
|||
emit_byte(componentInfo.Component_id); |
|||
emit_byte((componentInfo.H_samp_factor << 4) + componentInfo.V_samp_factor); |
|||
emit_byte(componentInfo.Quant_tbl_no); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Emit an Adobe APP14 marker
|
|||
/// </summary>
|
|||
private void emit_adobe_app14() |
|||
{ |
|||
/* |
|||
* Length of APP14 block (2 bytes) |
|||
* Block ID (5 bytes - ASCII "Adobe") |
|||
* Version Number (2 bytes - currently 100) |
|||
* Flags0 (2 bytes - currently 0) |
|||
* Flags1 (2 bytes - currently 0) |
|||
* Color transform (1 byte) |
|||
* |
|||
* Although Adobe TN 5116 mentions Version = 101, all the Adobe files |
|||
* now in circulation seem to use Version = 100, so that's what we write. |
|||
* |
|||
* We write the color transform byte as 1 if the JPEG color space is |
|||
* YCbCr, 2 if it's YCCK, 0 otherwise. Adobe's definition has to do with |
|||
* whether the encoder performed a transformation, which is pretty useless. |
|||
*/ |
|||
|
|||
emit_marker(JPEG_MARKER.APP14); |
|||
|
|||
emit_2bytes(2 + 5 + 2 + 2 + 2 + 1); /* length */ |
|||
|
|||
emit_byte(0x41); /* Identifier: ASCII "Adobe" */ |
|||
emit_byte(0x64); |
|||
emit_byte(0x6F); |
|||
emit_byte(0x62); |
|||
emit_byte(0x65); |
|||
emit_2bytes(100); /* Version */ |
|||
emit_2bytes(0); /* Flags0 */ |
|||
emit_2bytes(0); /* Flags1 */ |
|||
switch (m_cinfo.m_jpeg_color_space) |
|||
{ |
|||
case J_COLOR_SPACE.JCS_YCbCr: |
|||
emit_byte(1); /* Color transform = 1 */ |
|||
break; |
|||
case J_COLOR_SPACE.JCS_YCCK: |
|||
emit_byte(2); /* Color transform = 2 */ |
|||
break; |
|||
default: |
|||
emit_byte(0); /* Color transform = 0 */ |
|||
break; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Emit a DRI marker
|
|||
/// </summary>
|
|||
private void emit_dri() |
|||
{ |
|||
emit_marker(JPEG_MARKER.DRI); |
|||
|
|||
emit_2bytes(4); /* fixed length */ |
|||
|
|||
emit_2bytes(m_cinfo.m_restart_interval); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Emit a DHT marker
|
|||
/// </summary>
|
|||
private void emit_dht(int index, bool is_ac) |
|||
{ |
|||
JHUFF_TBL htbl = m_cinfo.m_dc_huff_tbl_ptrs[index]; |
|||
if (is_ac) |
|||
{ |
|||
htbl = m_cinfo.m_ac_huff_tbl_ptrs[index]; |
|||
index += 0x10; /* output index has AC bit set */ |
|||
} |
|||
|
|||
if (htbl == null) |
|||
m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_NO_HUFF_TABLE, index); |
|||
|
|||
if (!htbl.Sent_table) |
|||
{ |
|||
emit_marker(JPEG_MARKER.DHT); |
|||
|
|||
int length = 0; |
|||
for (int i = 1; i <= 16; i++) |
|||
length += htbl.Bits[i]; |
|||
|
|||
emit_2bytes(length + 2 + 1 + 16); |
|||
emit_byte(index); |
|||
|
|||
for (int i = 1; i <= 16; i++) |
|||
emit_byte(htbl.Bits[i]); |
|||
|
|||
for (int i = 0; i < length; i++) |
|||
emit_byte(htbl.Huffval[i]); |
|||
|
|||
htbl.Sent_table = true; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Emit a DQT marker
|
|||
/// </summary>
|
|||
/// <param name="index">The index.</param>
|
|||
/// <returns>the precision used (0 = 8bits, 1 = 16bits) for baseline checking</returns>
|
|||
private int emit_dqt(int index) |
|||
{ |
|||
JQUANT_TBL qtbl = m_cinfo.m_quant_tbl_ptrs[index]; |
|||
if (qtbl == null) |
|||
m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_NO_QUANT_TABLE, index); |
|||
|
|||
int prec = 0; |
|||
for (int i = 0; i < JpegConstants.DCTSIZE2; i++) |
|||
{ |
|||
if (qtbl.quantval[i] > 255) |
|||
prec = 1; |
|||
} |
|||
|
|||
if (!qtbl.Sent_table) |
|||
{ |
|||
emit_marker(JPEG_MARKER.DQT); |
|||
|
|||
emit_2bytes(prec != 0 ? JpegConstants.DCTSIZE2 * 2 + 1 + 2 : JpegConstants.DCTSIZE2 + 1 + 2); |
|||
|
|||
emit_byte(index + (prec << 4)); |
|||
|
|||
for (int i = 0; i < JpegConstants.DCTSIZE2; i++) |
|||
{ |
|||
/* The table entries must be emitted in zigzag order. */ |
|||
int qval = qtbl.quantval[JpegUtils.jpeg_natural_order[i]]; |
|||
|
|||
if (prec != 0) |
|||
emit_byte(qval >> 8); |
|||
|
|||
emit_byte(qval & 0xFF); |
|||
} |
|||
|
|||
qtbl.Sent_table = true; |
|||
} |
|||
|
|||
return prec; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Emit a JFIF-compliant APP0 marker
|
|||
/// </summary>
|
|||
private void emit_jfif_app0() |
|||
{ |
|||
/* |
|||
* Length of APP0 block (2 bytes) |
|||
* Block ID (4 bytes - ASCII "JFIF") |
|||
* Zero byte (1 byte to terminate the ID string) |
|||
* Version Major, Minor (2 bytes - major first) |
|||
* Units (1 byte - 0x00 = none, 0x01 = inch, 0x02 = cm) |
|||
* Xdpu (2 bytes - dots per unit horizontal) |
|||
* Ydpu (2 bytes - dots per unit vertical) |
|||
* Thumbnail X size (1 byte) |
|||
* Thumbnail Y size (1 byte) |
|||
*/ |
|||
|
|||
emit_marker(JPEG_MARKER.APP0); |
|||
|
|||
emit_2bytes(2 + 4 + 1 + 2 + 1 + 2 + 2 + 1 + 1); /* length */ |
|||
|
|||
emit_byte(0x4A); /* Identifier: ASCII "JFIF" */ |
|||
emit_byte(0x46); |
|||
emit_byte(0x49); |
|||
emit_byte(0x46); |
|||
emit_byte(0); |
|||
emit_byte(m_cinfo.m_JFIF_major_version); /* Version fields */ |
|||
emit_byte(m_cinfo.m_JFIF_minor_version); |
|||
emit_byte((int)m_cinfo.m_density_unit); /* Pixel size information */ |
|||
emit_2bytes(m_cinfo.m_X_density); |
|||
emit_2bytes(m_cinfo.m_Y_density); |
|||
emit_byte(0); /* No thumbnail image */ |
|||
emit_byte(0); |
|||
} |
|||
|
|||
//////////////////////////////////////////////////////////////////////////
|
|||
// Basic output routines.
|
|||
//
|
|||
// Note that we do not support suspension while writing a marker.
|
|||
// Therefore, an application using suspension must ensure that there is
|
|||
// enough buffer space for the initial markers (typ. 600-700 bytes) before
|
|||
// calling jpeg_start_compress, and enough space to write the trailing EOI
|
|||
// (a few bytes) before calling jpeg_finish_compress. Multipass compression
|
|||
// modes are not supported at all with suspension, so those two are the only
|
|||
// points where markers will be written.
|
|||
|
|||
|
|||
/// <summary>
|
|||
/// Emit a marker code
|
|||
/// </summary>
|
|||
private void emit_marker(JPEG_MARKER mark) |
|||
{ |
|||
emit_byte(0xFF); |
|||
emit_byte((int)mark); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Emit a 2-byte integer; these are always MSB first in JPEG files
|
|||
/// </summary>
|
|||
private void emit_2bytes(int value) |
|||
{ |
|||
emit_byte((value >> 8) & 0xFF); |
|||
emit_byte(value & 0xFF); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Emit a byte
|
|||
/// </summary>
|
|||
private void emit_byte(int val) |
|||
{ |
|||
if (!m_cinfo.m_dest.emit_byte(val)) |
|||
m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CANT_SUSPEND); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,28 @@ |
|||
/* Copyright (C) 2008-2011, Bit Miracle |
|||
* http://www.bitmiracle.com
|
|||
* |
|||
* Copyright (C) 1994-1996, Thomas G. Lane. |
|||
* This file is part of the Independent JPEG Group's software. |
|||
* For conditions of distribution and use, see the accompanying README file. |
|||
* |
|||
*/ |
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
|
|||
namespace BitMiracle.LibJpeg.Classic.Internal |
|||
{ |
|||
/// <summary>
|
|||
/// The script for encoding a multiple-scan file is an array of these:
|
|||
/// </summary>
|
|||
class jpeg_scan_info |
|||
{ |
|||
public int comps_in_scan; /* number of components encoded in this scan */ |
|||
public int[] component_index = new int[JpegConstants.MAX_COMPS_IN_SCAN]; /* their SOF/comp_info[] indexes */ |
|||
public int Ss; |
|||
public int Se; /* progressive JPEG spectral selection parms */ |
|||
public int Ah; |
|||
public int Al; /* progressive JPEG successive approx. parms */ |
|||
} |
|||
} |
|||
@ -0,0 +1,31 @@ |
|||
/* Copyright (C) 2008-2011, Bit Miracle |
|||
* http://www.bitmiracle.com
|
|||
* |
|||
* Copyright (C) 1994-1996, Thomas G. Lane. |
|||
* This file is part of the Independent JPEG Group's software. |
|||
* For conditions of distribution and use, see the accompanying README file. |
|||
* |
|||
*/ |
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
|
|||
namespace BitMiracle.LibJpeg.Classic.Internal |
|||
{ |
|||
/// <summary>
|
|||
/// Upsampling (note that upsampler must also call color converter)
|
|||
/// </summary>
|
|||
abstract class jpeg_upsampler |
|||
{ |
|||
protected bool m_need_context_rows; /* true if need rows above & below */ |
|||
|
|||
public abstract void start_pass(); |
|||
public abstract void upsample(ComponentBuffer[] input_buf, ref int in_row_group_ctr, int in_row_groups_avail, byte[][] output_buf, ref int out_row_ctr, int out_rows_avail); |
|||
|
|||
public bool NeedContextRows() |
|||
{ |
|||
return m_need_context_rows; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,854 @@ |
|||
/* Copyright (C) 2008-2011, Bit Miracle |
|||
* http://www.bitmiracle.com
|
|||
* |
|||
* Copyright (C) 1994-1996, Thomas G. Lane. |
|||
* This file is part of the Independent JPEG Group's software. |
|||
* For conditions of distribution and use, see the accompanying README file. |
|||
* |
|||
*/ |
|||
|
|||
/* |
|||
* This file contains 1-pass color quantization (color mapping) routines. |
|||
* These routines provide mapping to a fixed color map using equally spaced |
|||
* color values. Optional Floyd-Steinberg or ordered dithering is available. |
|||
*/ |
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
|
|||
namespace BitMiracle.LibJpeg.Classic.Internal |
|||
{ |
|||
/// <summary>
|
|||
/// The main purpose of 1-pass quantization is to provide a fast, if not very
|
|||
/// high quality, colormapped output capability. A 2-pass quantizer usually
|
|||
/// gives better visual quality; however, for quantized grayscale output this
|
|||
/// quantizer is perfectly adequate. Dithering is highly recommended with this
|
|||
/// quantizer, though you can turn it off if you really want to.
|
|||
///
|
|||
/// In 1-pass quantization the colormap must be chosen in advance of seeing the
|
|||
/// image. We use a map consisting of all combinations of Ncolors[i] color
|
|||
/// values for the i'th component. The Ncolors[] values are chosen so that
|
|||
/// their product, the total number of colors, is no more than that requested.
|
|||
/// (In most cases, the product will be somewhat less.)
|
|||
///
|
|||
/// Since the colormap is orthogonal, the representative value for each color
|
|||
/// component can be determined without considering the other components;
|
|||
/// then these indexes can be combined into a colormap index by a standard
|
|||
/// N-dimensional-array-subscript calculation. Most of the arithmetic involved
|
|||
/// can be precalculated and stored in the lookup table colorindex[].
|
|||
/// colorindex[i][j] maps pixel value j in component i to the nearest
|
|||
/// representative value (grid plane) for that component; this index is
|
|||
/// multiplied by the array stride for component i, so that the
|
|||
/// index of the colormap entry closest to a given pixel value is just
|
|||
/// sum( colorindex[component-number][pixel-component-value] )
|
|||
/// Aside from being fast, this scheme allows for variable spacing between
|
|||
/// representative values with no additional lookup cost.
|
|||
///
|
|||
/// If gamma correction has been applied in color conversion, it might be wise
|
|||
/// to adjust the color grid spacing so that the representative colors are
|
|||
/// equidistant in linear space. At this writing, gamma correction is not
|
|||
/// implemented, so nothing is done here.
|
|||
///
|
|||
///
|
|||
/// Declarations for Floyd-Steinberg dithering.
|
|||
///
|
|||
/// Errors are accumulated into the array fserrors[], at a resolution of
|
|||
/// 1/16th of a pixel count. The error at a given pixel is propagated
|
|||
/// to its not-yet-processed neighbors using the standard F-S fractions,
|
|||
/// ... (here) 7/16
|
|||
/// 3/16 5/16 1/16
|
|||
/// We work left-to-right on even rows, right-to-left on odd rows.
|
|||
///
|
|||
/// We can get away with a single array (holding one row's worth of errors)
|
|||
/// by using it to store the current row's errors at pixel columns not yet
|
|||
/// processed, but the next row's errors at columns already processed. We
|
|||
/// need only a few extra variables to hold the errors immediately around the
|
|||
/// current column. (If we are lucky, those variables are in registers, but
|
|||
/// even if not, they're probably cheaper to access than array elements are.)
|
|||
///
|
|||
/// The fserrors[] array is indexed [component#][position].
|
|||
/// We provide (#columns + 2) entries per component; the extra entry at each
|
|||
/// end saves us from special-casing the first and last pixels.
|
|||
///
|
|||
///
|
|||
/// Declarations for ordered dithering.
|
|||
///
|
|||
/// We use a standard 16x16 ordered dither array. The basic concept of ordered
|
|||
/// dithering is described in many references, for instance Dale Schumacher's
|
|||
/// chapter II.2 of Graphics Gems II (James Arvo, ed. Academic Press, 1991).
|
|||
/// In place of Schumacher's comparisons against a "threshold" value, we add a
|
|||
/// "dither" value to the input pixel and then round the result to the nearest
|
|||
/// output value. The dither value is equivalent to (0.5 - threshold) times
|
|||
/// the distance between output values. For ordered dithering, we assume that
|
|||
/// the output colors are equally spaced; if not, results will probably be
|
|||
/// worse, since the dither may be too much or too little at a given point.
|
|||
///
|
|||
/// The normal calculation would be to form pixel value + dither, range-limit
|
|||
/// this to 0..MAXJSAMPLE, and then index into the colorindex table as usual.
|
|||
/// We can skip the separate range-limiting step by extending the colorindex
|
|||
/// table in both directions.
|
|||
/// </summary>
|
|||
class my_1pass_cquantizer : jpeg_color_quantizer |
|||
{ |
|||
private enum QuantizerType |
|||
{ |
|||
color_quantizer3, |
|||
color_quantizer, |
|||
quantize3_ord_dither_quantizer, |
|||
quantize_ord_dither_quantizer, |
|||
quantize_fs_dither_quantizer |
|||
} |
|||
|
|||
private static int[] RGB_order = { JpegConstants.RGB_GREEN, JpegConstants.RGB_RED, JpegConstants.RGB_BLUE }; |
|||
private const int MAX_Q_COMPS = 4; /* max components I can handle */ |
|||
|
|||
private const int ODITHER_SIZE = 16; /* dimension of dither matrix */ |
|||
|
|||
/* NB: if ODITHER_SIZE is not a power of 2, ODITHER_MASK uses will break */ |
|||
private const int ODITHER_CELLS = (ODITHER_SIZE * ODITHER_SIZE); /* # cells in matrix */ |
|||
private const int ODITHER_MASK = (ODITHER_SIZE-1); /* mask for wrapping around counters */ |
|||
|
|||
/* Bayer's order-4 dither array. Generated by the code given in |
|||
* Stephen Hawley's article "Ordered Dithering" in Graphics Gems I. |
|||
* The values in this array must range from 0 to ODITHER_CELLS-1. |
|||
*/ |
|||
private static byte[][] base_dither_matrix = new byte[][] |
|||
{ |
|||
new byte[] { 0,192, 48,240, 12,204, 60,252, 3,195, 51,243, 15,207, 63,255 }, |
|||
new byte[] { 128, 64,176,112,140, 76,188,124,131, 67,179,115,143, 79,191,127 }, |
|||
new byte[] { 32,224, 16,208, 44,236, 28,220, 35,227, 19,211, 47,239, 31,223 }, |
|||
new byte[] { 160, 96,144, 80,172,108,156, 92,163, 99,147, 83,175,111,159, 95 }, |
|||
new byte[] { 8,200, 56,248, 4,196, 52,244, 11,203, 59,251, 7,199, 55,247 }, |
|||
new byte[] { 136, 72,184,120,132, 68,180,116,139, 75,187,123,135, 71,183,119 }, |
|||
new byte[] { 40,232, 24,216, 36,228, 20,212, 43,235, 27,219, 39,231, 23,215 }, |
|||
new byte[] { 168,104,152, 88,164,100,148, 84,171,107,155, 91,167,103,151, 87 }, |
|||
new byte[] { 2,194, 50,242, 14,206, 62,254, 1,193, 49,241, 13,205, 61,253 }, |
|||
new byte[] { 130, 66,178,114,142, 78,190,126,129, 65,177,113,141, 77,189,125 }, |
|||
new byte[] { 34,226, 18,210, 46,238, 30,222, 33,225, 17,209, 45,237, 29,221 }, |
|||
new byte[] { 162, 98,146, 82,174,110,158, 94,161, 97,145, 81,173,109,157, 93 }, |
|||
new byte[] { 10,202, 58,250, 6,198, 54,246, 9,201, 57,249, 5,197, 53,245 }, |
|||
new byte[] { 138, 74,186,122,134, 70,182,118,137, 73,185,121,133, 69,181,117 }, |
|||
new byte[] { 42,234, 26,218, 38,230, 22,214, 41,233, 25,217, 37,229, 21,213 }, |
|||
new byte[] { 170,106,154, 90,166,102,150, 86,169,105,153, 89,165,101,149, 85 } |
|||
}; |
|||
|
|||
private QuantizerType m_quantizer; |
|||
|
|||
private jpeg_decompress_struct m_cinfo; |
|||
|
|||
/* Initially allocated colormap is saved here */ |
|||
private byte[][] m_sv_colormap; /* The color map as a 2-D pixel array */ |
|||
private int m_sv_actual; /* number of entries in use */ |
|||
|
|||
private byte[][] m_colorindex; /* Precomputed mapping for speed */ |
|||
private int[] m_colorindexOffset; |
|||
|
|||
/* colorindex[i][j] = index of color closest to pixel value j in component i, |
|||
* premultiplied as described above. Since colormap indexes must fit into |
|||
* bytes, the entries of this array will too. |
|||
*/ |
|||
private bool m_is_padded; /* is the colorindex padded for odither? */ |
|||
|
|||
private int[] m_Ncolors = new int[MAX_Q_COMPS]; /* # of values alloced to each component */ |
|||
|
|||
/* Variables for ordered dithering */ |
|||
private int m_row_index; /* cur row's vertical index in dither matrix */ |
|||
private int[][][] m_odither = new int[MAX_Q_COMPS][][]; /* one dither array per component */ |
|||
|
|||
/* Variables for Floyd-Steinberg dithering */ |
|||
private short[][] m_fserrors = new short[MAX_Q_COMPS][]; /* accumulated errors */ |
|||
private bool m_on_odd_row; /* flag to remember which row we are on */ |
|||
|
|||
/// <summary>
|
|||
/// Module initialization routine for 1-pass color quantization.
|
|||
/// </summary>
|
|||
/// <param name="cinfo">The cinfo.</param>
|
|||
public my_1pass_cquantizer(jpeg_decompress_struct cinfo) |
|||
{ |
|||
m_cinfo = cinfo; |
|||
|
|||
m_fserrors[0] = null; /* Flag FS workspace not allocated */ |
|||
m_odither[0] = null; /* Also flag odither arrays not allocated */ |
|||
|
|||
/* Make sure my internal arrays won't overflow */ |
|||
if (cinfo.m_out_color_components > MAX_Q_COMPS) |
|||
cinfo.ERREXIT(J_MESSAGE_CODE.JERR_QUANT_COMPONENTS, MAX_Q_COMPS); |
|||
|
|||
/* Make sure colormap indexes can be represented by JSAMPLEs */ |
|||
if (cinfo.m_desired_number_of_colors > (JpegConstants.MAXJSAMPLE + 1)) |
|||
cinfo.ERREXIT(J_MESSAGE_CODE.JERR_QUANT_MANY_COLORS, JpegConstants.MAXJSAMPLE + 1); |
|||
|
|||
/* Create the colormap and color index table. */ |
|||
create_colormap(); |
|||
create_colorindex(); |
|||
|
|||
/* Allocate Floyd-Steinberg workspace now if requested. |
|||
* We do this now since it is FAR storage and may affect the memory |
|||
* manager's space calculations. If the user changes to FS dither |
|||
* mode in a later pass, we will allocate the space then, and will |
|||
* possibly overrun the max_memory_to_use setting. |
|||
*/ |
|||
if (cinfo.m_dither_mode == J_DITHER_MODE.JDITHER_FS) |
|||
alloc_fs_workspace(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initialize for one-pass color quantization.
|
|||
/// </summary>
|
|||
public virtual void start_pass(bool is_pre_scan) |
|||
{ |
|||
/* Install my colormap. */ |
|||
m_cinfo.m_colormap = m_sv_colormap; |
|||
m_cinfo.m_actual_number_of_colors = m_sv_actual; |
|||
|
|||
/* Initialize for desired dithering mode. */ |
|||
switch (m_cinfo.m_dither_mode) |
|||
{ |
|||
case J_DITHER_MODE.JDITHER_NONE: |
|||
if (m_cinfo.m_out_color_components == 3) |
|||
m_quantizer = QuantizerType.color_quantizer3; |
|||
else |
|||
m_quantizer = QuantizerType.color_quantizer; |
|||
|
|||
break; |
|||
case J_DITHER_MODE.JDITHER_ORDERED: |
|||
if (m_cinfo.m_out_color_components == 3) |
|||
m_quantizer = QuantizerType.quantize3_ord_dither_quantizer; |
|||
else |
|||
m_quantizer = QuantizerType.quantize3_ord_dither_quantizer; |
|||
|
|||
/* initialize state for ordered dither */ |
|||
m_row_index = 0; |
|||
|
|||
/* If user changed to ordered dither from another mode, |
|||
* we must recreate the color index table with padding. |
|||
* This will cost extra space, but probably isn't very likely. |
|||
*/ |
|||
if (!m_is_padded) |
|||
create_colorindex(); |
|||
|
|||
/* Create ordered-dither tables if we didn't already. */ |
|||
if (m_odither[0] == null) |
|||
create_odither_tables(); |
|||
|
|||
break; |
|||
case J_DITHER_MODE.JDITHER_FS: |
|||
m_quantizer = QuantizerType.quantize_fs_dither_quantizer; |
|||
|
|||
/* initialize state for F-S dither */ |
|||
m_on_odd_row = false; |
|||
|
|||
/* Allocate Floyd-Steinberg workspace if didn't already. */ |
|||
if (m_fserrors[0] == null) |
|||
alloc_fs_workspace(); |
|||
|
|||
/* Initialize the propagated errors to zero. */ |
|||
int arraysize = m_cinfo.m_output_width + 2; |
|||
for (int i = 0; i < m_cinfo.m_out_color_components; i++) |
|||
Array.Clear(m_fserrors[i], 0, arraysize); |
|||
|
|||
break; |
|||
default: |
|||
m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_NOT_COMPILED); |
|||
break; |
|||
} |
|||
} |
|||
|
|||
public virtual void color_quantize(byte[][] input_buf, int in_row, byte[][] output_buf, int out_row, int num_rows) |
|||
{ |
|||
switch (m_quantizer) |
|||
{ |
|||
case QuantizerType.color_quantizer3: |
|||
quantize3(input_buf, in_row, output_buf, out_row, num_rows); |
|||
break; |
|||
case QuantizerType.color_quantizer: |
|||
quantize(input_buf, in_row, output_buf, out_row, num_rows); |
|||
break; |
|||
case QuantizerType.quantize3_ord_dither_quantizer: |
|||
quantize3_ord_dither(input_buf, in_row, output_buf, out_row, num_rows); |
|||
break; |
|||
case QuantizerType.quantize_ord_dither_quantizer: |
|||
quantize_ord_dither(input_buf, in_row, output_buf, out_row, num_rows); |
|||
break; |
|||
case QuantizerType.quantize_fs_dither_quantizer: |
|||
quantize_fs_dither(input_buf, in_row, output_buf, out_row, num_rows); |
|||
break; |
|||
default: |
|||
m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_NOTIMPL); |
|||
break; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Finish up at the end of the pass.
|
|||
/// </summary>
|
|||
public virtual void finish_pass() |
|||
{ |
|||
/* no work in 1-pass case */ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Switch to a new external colormap between output passes.
|
|||
/// Shouldn't get to this!
|
|||
/// </summary>
|
|||
public virtual void new_color_map() |
|||
{ |
|||
m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_MODE_CHANGE); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Map some rows of pixels to the output colormapped representation.
|
|||
/// General case, no dithering.
|
|||
/// </summary>
|
|||
private void quantize(byte[][] input_buf, int in_row, byte[][] output_buf, int out_row, int num_rows) |
|||
{ |
|||
int nc = m_cinfo.m_out_color_components; |
|||
|
|||
for (int row = 0; row < num_rows; row++) |
|||
{ |
|||
int inIndex = 0; |
|||
int inRow = in_row + row; |
|||
|
|||
int outIndex = 0; |
|||
int outRow = out_row + row; |
|||
|
|||
for (int col = m_cinfo.m_output_width; col > 0; col--) |
|||
{ |
|||
int pixcode = 0; |
|||
for (int ci = 0; ci < nc; ci++) |
|||
{ |
|||
pixcode += m_colorindex[ci][m_colorindexOffset[ci] + input_buf[inRow][inIndex]]; |
|||
inIndex++; |
|||
} |
|||
|
|||
output_buf[outRow][outIndex] = (byte)pixcode; |
|||
outIndex++; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Map some rows of pixels to the output colormapped representation.
|
|||
/// Fast path for out_color_components==3, no dithering
|
|||
/// </summary>
|
|||
private void quantize3(byte[][] input_buf, int in_row, byte[][] output_buf, int out_row, int num_rows) |
|||
{ |
|||
int width = m_cinfo.m_output_width; |
|||
|
|||
for (int row = 0; row < num_rows; row++) |
|||
{ |
|||
int inIndex = 0; |
|||
int inRow = in_row + row; |
|||
|
|||
int outIndex = 0; |
|||
int outRow = out_row + row; |
|||
|
|||
for (int col = width; col > 0; col--) |
|||
{ |
|||
int pixcode = m_colorindex[0][m_colorindexOffset[0] + input_buf[inRow][inIndex]]; |
|||
inIndex++; |
|||
|
|||
pixcode += m_colorindex[1][m_colorindexOffset[1] + input_buf[inRow][inIndex]]; |
|||
inIndex++; |
|||
|
|||
pixcode += m_colorindex[2][m_colorindexOffset[2] + input_buf[inRow][inIndex]]; |
|||
inIndex++; |
|||
|
|||
output_buf[outRow][outIndex] = (byte)pixcode; |
|||
outIndex++; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Map some rows of pixels to the output colormapped representation.
|
|||
/// General case, with ordered dithering.
|
|||
/// </summary>
|
|||
private void quantize_ord_dither(byte[][] input_buf, int in_row, byte[][] output_buf, int out_row, int num_rows) |
|||
{ |
|||
int nc = m_cinfo.m_out_color_components; |
|||
int width = m_cinfo.m_output_width; |
|||
|
|||
for (int row = 0; row < num_rows; row++) |
|||
{ |
|||
/* Initialize output values to 0 so can process components separately */ |
|||
Array.Clear(output_buf[out_row + row], 0, width); |
|||
|
|||
int row_index = m_row_index; |
|||
for (int ci = 0; ci < nc; ci++) |
|||
{ |
|||
int inputIndex = ci; |
|||
int outIndex = 0; |
|||
int outRow = out_row + row; |
|||
|
|||
int col_index = 0; |
|||
for (int col = width; col > 0; col--) |
|||
{ |
|||
/* Form pixel value + dither, range-limit to 0..MAXJSAMPLE, |
|||
* select output value, accumulate into output code for this pixel. |
|||
* Range-limiting need not be done explicitly, as we have extended |
|||
* the colorindex table to produce the right answers for out-of-range |
|||
* inputs. The maximum dither is +- MAXJSAMPLE; this sets the |
|||
* required amount of padding. |
|||
*/ |
|||
output_buf[outRow][outIndex] += m_colorindex[ci][m_colorindexOffset[ci] + input_buf[in_row + row][inputIndex] + m_odither[ci][row_index][col_index]]; |
|||
inputIndex += nc; |
|||
outIndex++; |
|||
col_index = (col_index + 1) & ODITHER_MASK; |
|||
} |
|||
} |
|||
|
|||
/* Advance row index for next row */ |
|||
row_index = (row_index + 1) & ODITHER_MASK; |
|||
m_row_index = row_index; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Map some rows of pixels to the output colormapped representation.
|
|||
/// Fast path for out_color_components==3, with ordered dithering
|
|||
/// </summary>
|
|||
private void quantize3_ord_dither(byte[][] input_buf, int in_row, byte[][] output_buf, int out_row, int num_rows) |
|||
{ |
|||
int width = m_cinfo.m_output_width; |
|||
|
|||
for (int row = 0; row < num_rows; row++) |
|||
{ |
|||
int row_index = m_row_index; |
|||
int inRow = in_row + row; |
|||
int inIndex = 0; |
|||
|
|||
int outIndex = 0; |
|||
int outRow = out_row + row; |
|||
|
|||
int col_index = 0; |
|||
for (int col = width; col > 0; col--) |
|||
{ |
|||
int pixcode = m_colorindex[0][m_colorindexOffset[0] + input_buf[inRow][inIndex] + m_odither[0][row_index][col_index]]; |
|||
inIndex++; |
|||
|
|||
pixcode += m_colorindex[1][m_colorindexOffset[1] + input_buf[inRow][inIndex] + m_odither[1][row_index][col_index]]; |
|||
inIndex++; |
|||
|
|||
pixcode += m_colorindex[2][m_colorindexOffset[2] + input_buf[inRow][inIndex] + m_odither[2][row_index][col_index]]; |
|||
inIndex++; |
|||
|
|||
output_buf[outRow][outIndex] = (byte)pixcode; |
|||
outIndex++; |
|||
|
|||
col_index = (col_index + 1) & ODITHER_MASK; |
|||
} |
|||
|
|||
row_index = (row_index + 1) & ODITHER_MASK; |
|||
m_row_index = row_index; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Map some rows of pixels to the output colormapped representation.
|
|||
/// General case, with Floyd-Steinberg dithering
|
|||
/// </summary>
|
|||
private void quantize_fs_dither(byte[][] input_buf, int in_row, byte[][] output_buf, int out_row, int num_rows) |
|||
{ |
|||
int nc = m_cinfo.m_out_color_components; |
|||
int width = m_cinfo.m_output_width; |
|||
|
|||
byte[] limit = m_cinfo.m_sample_range_limit; |
|||
int limitOffset = m_cinfo.m_sampleRangeLimitOffset; |
|||
|
|||
for (int row = 0; row < num_rows; row++) |
|||
{ |
|||
/* Initialize output values to 0 so can process components separately */ |
|||
Array.Clear(output_buf[out_row + row], 0, width); |
|||
|
|||
for (int ci = 0; ci < nc; ci++) |
|||
{ |
|||
int inRow = in_row + row; |
|||
int inIndex = ci; |
|||
|
|||
int outIndex = 0; |
|||
int outRow = out_row + row; |
|||
|
|||
int errorIndex = 0; |
|||
int dir; /* 1 for left-to-right, -1 for right-to-left */ |
|||
if (m_on_odd_row) |
|||
{ |
|||
/* work right to left in this row */ |
|||
inIndex += (width - 1) * nc; /* so point to rightmost pixel */ |
|||
outIndex += width - 1; |
|||
dir = -1; |
|||
errorIndex = width + 1; /* => entry after last column */ |
|||
} |
|||
else |
|||
{ |
|||
/* work left to right in this row */ |
|||
dir = 1; |
|||
errorIndex = 0; /* => entry before first column */ |
|||
} |
|||
int dirnc = dir * nc; |
|||
|
|||
/* Preset error values: no error propagated to first pixel from left */ |
|||
int cur = 0; |
|||
/* and no error propagated to row below yet */ |
|||
int belowerr = 0; |
|||
int bpreverr = 0; |
|||
|
|||
for (int col = width; col > 0; col--) |
|||
{ |
|||
/* cur holds the error propagated from the previous pixel on the |
|||
* current line. Add the error propagated from the previous line |
|||
* to form the complete error correction term for this pixel, and |
|||
* round the error term (which is expressed * 16) to an integer. |
|||
* RIGHT_SHIFT rounds towards minus infinity, so adding 8 is correct |
|||
* for either sign of the error value. |
|||
* Note: errorIndex is for *previous* column's array entry. |
|||
*/ |
|||
cur = JpegUtils.RIGHT_SHIFT(cur + m_fserrors[ci][errorIndex + dir] + 8, 4); |
|||
|
|||
/* Form pixel value + error, and range-limit to 0..MAXJSAMPLE. |
|||
* The maximum error is +- MAXJSAMPLE; this sets the required size |
|||
* of the range_limit array. |
|||
*/ |
|||
cur += input_buf[inRow][inIndex]; |
|||
cur = limit[limitOffset + cur]; |
|||
|
|||
/* Select output value, accumulate into output code for this pixel */ |
|||
int pixcode = m_colorindex[ci][m_colorindexOffset[ci] + cur]; |
|||
output_buf[outRow][outIndex] += (byte)pixcode; |
|||
|
|||
/* Compute actual representation error at this pixel */ |
|||
/* Note: we can do this even though we don't have the final */ |
|||
/* pixel code, because the colormap is orthogonal. */ |
|||
cur -= m_sv_colormap[ci][pixcode]; |
|||
|
|||
/* Compute error fractions to be propagated to adjacent pixels. |
|||
* Add these into the running sums, and simultaneously shift the |
|||
* next-line error sums left by 1 column. |
|||
*/ |
|||
int bnexterr = cur; |
|||
int delta = cur * 2; |
|||
cur += delta; /* form error * 3 */ |
|||
m_fserrors[ci][errorIndex + 0] = (short) (bpreverr + cur); |
|||
cur += delta; /* form error * 5 */ |
|||
bpreverr = belowerr + cur; |
|||
belowerr = bnexterr; |
|||
cur += delta; /* form error * 7 */ |
|||
|
|||
/* At this point cur contains the 7/16 error value to be propagated |
|||
* to the next pixel on the current line, and all the errors for the |
|||
* next line have been shifted over. We are therefore ready to move on. |
|||
*/ |
|||
inIndex += dirnc; /* advance input to next column */ |
|||
outIndex += dir; /* advance output to next column */ |
|||
errorIndex += dir; /* advance errorIndex to current column */ |
|||
} |
|||
|
|||
/* Post-loop cleanup: we must unload the final error value into the |
|||
* final fserrors[] entry. Note we need not unload belowerr because |
|||
* it is for the dummy column before or after the actual array. |
|||
*/ |
|||
m_fserrors[ci][errorIndex + 0] = (short) bpreverr; /* unload prev err into array */ |
|||
} |
|||
|
|||
m_on_odd_row = (m_on_odd_row ? false : true); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Create the colormap.
|
|||
/// </summary>
|
|||
private void create_colormap() |
|||
{ |
|||
/* Select number of colors for each component */ |
|||
int total_colors = select_ncolors(m_Ncolors); |
|||
|
|||
/* Report selected color counts */ |
|||
if (m_cinfo.m_out_color_components == 3) |
|||
m_cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_QUANT_3_NCOLORS, total_colors, m_Ncolors[0], m_Ncolors[1], m_Ncolors[2]); |
|||
else |
|||
m_cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_QUANT_NCOLORS, total_colors); |
|||
|
|||
/* Allocate and fill in the colormap. */ |
|||
/* The colors are ordered in the map in standard row-major order, */ |
|||
/* i.e. rightmost (highest-indexed) color changes most rapidly. */ |
|||
byte[][] colormap = jpeg_common_struct.AllocJpegSamples(total_colors, m_cinfo.m_out_color_components); |
|||
|
|||
/* blksize is number of adjacent repeated entries for a component */ |
|||
/* blkdist is distance between groups of identical entries for a component */ |
|||
int blkdist = total_colors; |
|||
for (int i = 0; i < m_cinfo.m_out_color_components; i++) |
|||
{ |
|||
/* fill in colormap entries for i'th color component */ |
|||
int nci = m_Ncolors[i]; /* # of distinct values for this color */ |
|||
int blksize = blkdist / nci; |
|||
for (int j = 0; j < nci; j++) |
|||
{ |
|||
/* Compute j'th output value (out of nci) for component */ |
|||
int val = output_value(j, nci - 1); |
|||
|
|||
/* Fill in all colormap entries that have this value of this component */ |
|||
for (int ptr = j * blksize; ptr < total_colors; ptr += blkdist) |
|||
{ |
|||
/* fill in blksize entries beginning at ptr */ |
|||
for (int k = 0; k < blksize; k++) |
|||
colormap[i][ptr + k] = (byte)val; |
|||
} |
|||
} |
|||
|
|||
/* blksize of this color is blkdist of next */ |
|||
blkdist = blksize; |
|||
} |
|||
|
|||
/* Save the colormap in private storage, |
|||
* where it will survive color quantization mode changes. |
|||
*/ |
|||
m_sv_colormap = colormap; |
|||
m_sv_actual = total_colors; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Create the color index table.
|
|||
/// </summary>
|
|||
private void create_colorindex() |
|||
{ |
|||
/* For ordered dither, we pad the color index tables by MAXJSAMPLE in |
|||
* each direction (input index values can be -MAXJSAMPLE .. 2*MAXJSAMPLE). |
|||
* This is not necessary in the other dithering modes. However, we |
|||
* flag whether it was done in case user changes dithering mode. |
|||
*/ |
|||
int pad; |
|||
if (m_cinfo.m_dither_mode == J_DITHER_MODE.JDITHER_ORDERED) |
|||
{ |
|||
pad = JpegConstants.MAXJSAMPLE * 2; |
|||
m_is_padded = true; |
|||
} |
|||
else |
|||
{ |
|||
pad = 0; |
|||
m_is_padded = false; |
|||
} |
|||
|
|||
m_colorindex = jpeg_common_struct.AllocJpegSamples(JpegConstants.MAXJSAMPLE + 1 + pad, m_cinfo.m_out_color_components); |
|||
m_colorindexOffset = new int[m_cinfo.m_out_color_components]; |
|||
|
|||
/* blksize is number of adjacent repeated entries for a component */ |
|||
int blksize = m_sv_actual; |
|||
for (int i = 0; i < m_cinfo.m_out_color_components; i++) |
|||
{ |
|||
/* fill in colorindex entries for i'th color component */ |
|||
int nci = m_Ncolors[i]; /* # of distinct values for this color */ |
|||
blksize = blksize / nci; |
|||
|
|||
/* adjust colorindex pointers to provide padding at negative indexes. */ |
|||
if (pad != 0) |
|||
m_colorindexOffset[i] += JpegConstants.MAXJSAMPLE; |
|||
|
|||
/* in loop, val = index of current output value, */ |
|||
/* and k = largest j that maps to current val */ |
|||
int val = 0; |
|||
int k = largest_input_value(0, nci - 1); |
|||
for (int j = 0; j <= JpegConstants.MAXJSAMPLE; j++) |
|||
{ |
|||
while (j > k) |
|||
{ |
|||
/* advance val if past boundary */ |
|||
k = largest_input_value(++val, nci - 1); |
|||
} |
|||
|
|||
/* premultiply so that no multiplication needed in main processing */ |
|||
m_colorindex[i][m_colorindexOffset[i] + j] = (byte)(val * blksize); |
|||
} |
|||
|
|||
/* Pad at both ends if necessary */ |
|||
if (pad != 0) |
|||
{ |
|||
for (int j = 1; j <= JpegConstants.MAXJSAMPLE; j++) |
|||
{ |
|||
m_colorindex[i][m_colorindexOffset[i] + -j] = m_colorindex[i][m_colorindexOffset[i]]; |
|||
m_colorindex[i][m_colorindexOffset[i] + JpegConstants.MAXJSAMPLE + j] = m_colorindex[i][m_colorindexOffset[i] + JpegConstants.MAXJSAMPLE]; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Create the ordered-dither tables.
|
|||
/// Components having the same number of representative colors may
|
|||
/// share a dither table.
|
|||
/// </summary>
|
|||
private void create_odither_tables() |
|||
{ |
|||
for (int i = 0; i < m_cinfo.m_out_color_components; i++) |
|||
{ |
|||
int nci = m_Ncolors[i]; /* # of distinct values for this color */ |
|||
|
|||
/* search for matching prior component */ |
|||
int foundPos = -1; |
|||
for (int j = 0; j < i; j++) |
|||
{ |
|||
if (nci == m_Ncolors[j]) |
|||
{ |
|||
foundPos = j; |
|||
break; |
|||
} |
|||
} |
|||
|
|||
if (foundPos == -1) |
|||
{ |
|||
/* need a new table? */ |
|||
m_odither[i] = make_odither_array(nci); |
|||
} |
|||
else |
|||
m_odither[i] = m_odither[foundPos]; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Allocate workspace for Floyd-Steinberg errors.
|
|||
/// </summary>
|
|||
private void alloc_fs_workspace() |
|||
{ |
|||
for (int i = 0; i < m_cinfo.m_out_color_components; i++) |
|||
m_fserrors[i] = new short[m_cinfo.m_output_width + 2]; |
|||
} |
|||
|
|||
/* |
|||
* Policy-making subroutines for create_colormap and create_colorindex. |
|||
* These routines determine the colormap to be used. The rest of the module |
|||
* only assumes that the colormap is orthogonal. |
|||
* |
|||
* * select_ncolors decides how to divvy up the available colors |
|||
* among the components. |
|||
* * output_value defines the set of representative values for a component. |
|||
* * largest_input_value defines the mapping from input values to |
|||
* representative values for a component. |
|||
* Note that the latter two routines may impose different policies for |
|||
* different components, though this is not currently done. |
|||
*/ |
|||
|
|||
/// <summary>
|
|||
/// Return largest input value that should map to j'th output value
|
|||
/// Must have largest(j=0) >= 0, and largest(j=maxj) >= MAXJSAMPLE
|
|||
/// </summary>
|
|||
private static int largest_input_value(int j, int maxj) |
|||
{ |
|||
/* Breakpoints are halfway between values returned by output_value */ |
|||
return (int)(((2 * j + 1) * JpegConstants.MAXJSAMPLE + maxj) / (2 * maxj)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Return j'th output value, where j will range from 0 to maxj
|
|||
/// The output values must fall in 0..MAXJSAMPLE in increasing order
|
|||
/// </summary>
|
|||
private static int output_value(int j, int maxj) |
|||
{ |
|||
/* We always provide values 0 and MAXJSAMPLE for each component; |
|||
* any additional values are equally spaced between these limits. |
|||
* (Forcing the upper and lower values to the limits ensures that |
|||
* dithering can't produce a color outside the selected gamut.) |
|||
*/ |
|||
return (int)((j * JpegConstants.MAXJSAMPLE + maxj / 2) / maxj); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Determine allocation of desired colors to components,
|
|||
/// and fill in Ncolors[] array to indicate choice.
|
|||
/// Return value is total number of colors (product of Ncolors[] values).
|
|||
/// </summary>
|
|||
private int select_ncolors(int[] Ncolors) |
|||
{ |
|||
int nc = m_cinfo.m_out_color_components; /* number of color components */ |
|||
int max_colors = m_cinfo.m_desired_number_of_colors; |
|||
|
|||
/* We can allocate at least the nc'th root of max_colors per component. */ |
|||
/* Compute floor(nc'th root of max_colors). */ |
|||
int iroot = 1; |
|||
long temp = 0; |
|||
do |
|||
{ |
|||
iroot++; |
|||
temp = iroot; /* set temp = iroot ** nc */ |
|||
for (int i = 1; i < nc; i++) |
|||
temp *= iroot; |
|||
} |
|||
while (temp <= max_colors); /* repeat till iroot exceeds root */ |
|||
|
|||
/* now iroot = floor(root) */ |
|||
iroot--; |
|||
|
|||
/* Must have at least 2 color values per component */ |
|||
if (iroot < 2) |
|||
m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_QUANT_FEW_COLORS, (int)temp); |
|||
|
|||
/* Initialize to iroot color values for each component */ |
|||
int total_colors = 1; |
|||
for (int i = 0; i < nc; i++) |
|||
{ |
|||
Ncolors[i] = iroot; |
|||
total_colors *= iroot; |
|||
} |
|||
|
|||
/* We may be able to increment the count for one or more components without |
|||
* exceeding max_colors, though we know not all can be incremented. |
|||
* Sometimes, the first component can be incremented more than once! |
|||
* (Example: for 16 colors, we start at 2*2*2, go to 3*2*2, then 4*2*2.) |
|||
* In RGB colorspace, try to increment G first, then R, then B. |
|||
*/ |
|||
bool changed = false; |
|||
do |
|||
{ |
|||
changed = false; |
|||
for (int i = 0; i < nc; i++) |
|||
{ |
|||
int j = (m_cinfo.m_out_color_space == J_COLOR_SPACE.JCS_RGB ? RGB_order[i] : i); |
|||
/* calculate new total_colors if Ncolors[j] is incremented */ |
|||
temp = total_colors / Ncolors[j]; |
|||
temp *= Ncolors[j] + 1; /* done in long arith to avoid oflo */ |
|||
|
|||
if (temp > max_colors) |
|||
break; /* won't fit, done with this pass */ |
|||
|
|||
Ncolors[j]++; /* OK, apply the increment */ |
|||
total_colors = (int)temp; |
|||
changed = true; |
|||
} |
|||
} |
|||
while (changed); |
|||
|
|||
return total_colors; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Create an ordered-dither array for a component having ncolors
|
|||
/// distinct output values.
|
|||
/// </summary>
|
|||
private static int[][] make_odither_array(int ncolors) |
|||
{ |
|||
int[][] odither = new int[ODITHER_SIZE][]; |
|||
for (int i = 0; i < ODITHER_SIZE; i++) |
|||
odither[i] = new int[ODITHER_SIZE]; |
|||
|
|||
/* The inter-value distance for this color is MAXJSAMPLE/(ncolors-1). |
|||
* Hence the dither value for the matrix cell with fill order f |
|||
* (f=0..N-1) should be (N-1-2*f)/(2*N) * MAXJSAMPLE/(ncolors-1). |
|||
* On 16-bit-int machine, be careful to avoid overflow. |
|||
*/ |
|||
int den = 2 * ODITHER_CELLS * (ncolors - 1); |
|||
for (int j = 0; j < ODITHER_SIZE; j++) |
|||
{ |
|||
for (int k = 0; k < ODITHER_SIZE; k++) |
|||
{ |
|||
int num = ((int)(ODITHER_CELLS - 1 - 2 * ((int)base_dither_matrix[j][k]))) * JpegConstants.MAXJSAMPLE; |
|||
|
|||
/* Ensure round towards zero despite C's lack of consistency |
|||
* about rounding negative values in integer division... |
|||
*/ |
|||
odither[j][k] = num < 0 ? -((-num) / den) : num / den; |
|||
} |
|||
} |
|||
|
|||
return odither; |
|||
} |
|||
} |
|||
} |
|||
File diff suppressed because it is too large
@ -0,0 +1,424 @@ |
|||
/* Copyright (C) 2008-2011, Bit Miracle |
|||
* http://www.bitmiracle.com
|
|||
* |
|||
* Copyright (C) 1994-1996, Thomas G. Lane. |
|||
* This file is part of the Independent JPEG Group's software. |
|||
* For conditions of distribution and use, see the accompanying README file. |
|||
* |
|||
*/ |
|||
|
|||
/* |
|||
* This file contains the coefficient buffer controller for compression. |
|||
* This controller is the top level of the JPEG compressor proper. |
|||
* The coefficient buffer lies between forward-DCT and entropy encoding steps. |
|||
*/ |
|||
|
|||
/* We use a full-image coefficient buffer when doing Huffman optimization, |
|||
* and also for writing multiple-scan JPEG files. In all cases, the DCT |
|||
* step is run during the first pass, and subsequent passes need only read |
|||
* the buffered coefficients. |
|||
*/ |
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
|
|||
namespace BitMiracle.LibJpeg.Classic.Internal |
|||
{ |
|||
class my_c_coef_controller : jpeg_c_coef_controller |
|||
{ |
|||
private J_BUF_MODE m_passModeSetByLastStartPass; |
|||
private jpeg_compress_struct m_cinfo; |
|||
|
|||
private int m_iMCU_row_num; /* iMCU row # within image */ |
|||
private int m_mcu_ctr; /* counts MCUs processed in current row */ |
|||
private int m_MCU_vert_offset; /* counts MCU rows within iMCU row */ |
|||
private int m_MCU_rows_per_iMCU_row; /* number of such rows needed */ |
|||
|
|||
/* For single-pass compression, it's sufficient to buffer just one MCU |
|||
* (although this may prove a bit slow in practice). We allocate a |
|||
* workspace of C_MAX_BLOCKS_IN_MCU coefficient blocks, and reuse it for each |
|||
* MCU constructed and sent. (On 80x86, the workspace is FAR even though |
|||
* it's not really very big; this is to keep the module interfaces unchanged |
|||
* when a large coefficient buffer is necessary.) |
|||
* In multi-pass modes, this array points to the current MCU's blocks |
|||
* within the virtual arrays. |
|||
*/ |
|||
private JBLOCK[][] m_MCU_buffer = new JBLOCK[JpegConstants.C_MAX_BLOCKS_IN_MCU][]; |
|||
|
|||
/* In multi-pass modes, we need a virtual block array for each component. */ |
|||
private jvirt_array<JBLOCK>[] m_whole_image = new jvirt_array<JBLOCK>[JpegConstants.MAX_COMPONENTS]; |
|||
|
|||
public my_c_coef_controller(jpeg_compress_struct cinfo, bool need_full_buffer) |
|||
{ |
|||
m_cinfo = cinfo; |
|||
|
|||
/* Create the coefficient buffer. */ |
|||
if (need_full_buffer) |
|||
{ |
|||
/* Allocate a full-image virtual array for each component, */ |
|||
/* padded to a multiple of samp_factor DCT blocks in each direction. */ |
|||
for (int ci = 0; ci < cinfo.m_num_components; ci++) |
|||
{ |
|||
m_whole_image[ci] = jpeg_common_struct.CreateBlocksArray( |
|||
JpegUtils.jround_up(cinfo.Component_info[ci].Width_in_blocks, cinfo.Component_info[ci].H_samp_factor), |
|||
JpegUtils.jround_up(cinfo.Component_info[ci].height_in_blocks, cinfo.Component_info[ci].V_samp_factor)); |
|||
m_whole_image[ci].ErrorProcessor = cinfo; |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
/* We only need a single-MCU buffer. */ |
|||
JBLOCK[] buffer = new JBLOCK[JpegConstants.C_MAX_BLOCKS_IN_MCU]; |
|||
for (int i = 0; i < JpegConstants.C_MAX_BLOCKS_IN_MCU; i++) |
|||
buffer[i] = new JBLOCK(); |
|||
|
|||
for (int i = 0; i < JpegConstants.C_MAX_BLOCKS_IN_MCU; i++) |
|||
{ |
|||
m_MCU_buffer[i] = new JBLOCK[JpegConstants.C_MAX_BLOCKS_IN_MCU - i]; |
|||
for (int j = i; j < JpegConstants.C_MAX_BLOCKS_IN_MCU; j++) |
|||
m_MCU_buffer[i][j - i] = buffer[j]; |
|||
} |
|||
|
|||
/* flag for no virtual arrays */ |
|||
m_whole_image[0] = null; |
|||
} |
|||
} |
|||
|
|||
// Initialize for a processing pass.
|
|||
public virtual void start_pass(J_BUF_MODE pass_mode) |
|||
{ |
|||
m_iMCU_row_num = 0; |
|||
start_iMCU_row(); |
|||
|
|||
switch (pass_mode) |
|||
{ |
|||
case J_BUF_MODE.JBUF_PASS_THRU: |
|||
if (m_whole_image[0] != null) |
|||
m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_BUFFER_MODE); |
|||
break; |
|||
|
|||
case J_BUF_MODE.JBUF_SAVE_AND_PASS: |
|||
if (m_whole_image[0] == null) |
|||
m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_BUFFER_MODE); |
|||
break; |
|||
|
|||
case J_BUF_MODE.JBUF_CRANK_DEST: |
|||
if (m_whole_image[0] == null) |
|||
m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_BUFFER_MODE); |
|||
break; |
|||
|
|||
default: |
|||
m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_BUFFER_MODE); |
|||
break; |
|||
} |
|||
|
|||
m_passModeSetByLastStartPass = pass_mode; |
|||
} |
|||
|
|||
public virtual bool compress_data(byte[][][] input_buf) |
|||
{ |
|||
switch (m_passModeSetByLastStartPass) |
|||
{ |
|||
case J_BUF_MODE.JBUF_PASS_THRU: |
|||
return compressDataImpl(input_buf); |
|||
|
|||
case J_BUF_MODE.JBUF_SAVE_AND_PASS: |
|||
return compressFirstPass(input_buf); |
|||
|
|||
case J_BUF_MODE.JBUF_CRANK_DEST: |
|||
return compressOutput(); |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Process some data in the single-pass case.
|
|||
/// We process the equivalent of one fully interleaved MCU row ("iMCU" row)
|
|||
/// per call, ie, v_samp_factor block rows for each component in the image.
|
|||
/// Returns true if the iMCU row is completed, false if suspended.
|
|||
///
|
|||
/// NB: input_buf contains a plane for each component in image,
|
|||
/// which we index according to the component's SOF position.
|
|||
/// </summary>
|
|||
private bool compressDataImpl(byte[][][] input_buf) |
|||
{ |
|||
int last_MCU_col = m_cinfo.m_MCUs_per_row - 1; |
|||
int last_iMCU_row = m_cinfo.m_total_iMCU_rows - 1; |
|||
|
|||
/* Loop to write as much as one whole iMCU row */ |
|||
for (int yoffset = m_MCU_vert_offset; yoffset < m_MCU_rows_per_iMCU_row; yoffset++) |
|||
{ |
|||
for (int MCU_col_num = m_mcu_ctr; MCU_col_num <= last_MCU_col; MCU_col_num++) |
|||
{ |
|||
/* Determine where data comes from in input_buf and do the DCT thing. |
|||
* Each call on forward_DCT processes a horizontal row of DCT blocks |
|||
* as wide as an MCU; we rely on having allocated the MCU_buffer[] blocks |
|||
* sequentially. Dummy blocks at the right or bottom edge are filled in |
|||
* specially. The data in them does not matter for image reconstruction, |
|||
* so we fill them with values that will encode to the smallest amount of |
|||
* data, viz: all zeroes in the AC entries, DC entries equal to previous |
|||
* block's DC value. (Thanks to Thomas Kinsman for this idea.) |
|||
*/ |
|||
int blkn = 0; |
|||
for (int ci = 0; ci < m_cinfo.m_comps_in_scan; ci++) |
|||
{ |
|||
jpeg_component_info componentInfo = m_cinfo.Component_info[m_cinfo.m_cur_comp_info[ci]]; |
|||
int blockcnt = (MCU_col_num < last_MCU_col) ? componentInfo.MCU_width : componentInfo.last_col_width; |
|||
int xpos = MCU_col_num * componentInfo.MCU_sample_width; |
|||
int ypos = yoffset * JpegConstants.DCTSIZE; |
|||
|
|||
for (int yindex = 0; yindex < componentInfo.MCU_height; yindex++) |
|||
{ |
|||
if (m_iMCU_row_num < last_iMCU_row || yoffset + yindex < componentInfo.last_row_height) |
|||
{ |
|||
m_cinfo.m_fdct.forward_DCT(componentInfo.Quant_tbl_no, input_buf[componentInfo.Component_index], |
|||
m_MCU_buffer[blkn], ypos, xpos, blockcnt); |
|||
|
|||
if (blockcnt < componentInfo.MCU_width) |
|||
{ |
|||
/* Create some dummy blocks at the right edge of the image. */ |
|||
for (int i = 0; i < (componentInfo.MCU_width - blockcnt); i++) |
|||
Array.Clear(m_MCU_buffer[blkn + blockcnt][i].data, 0, m_MCU_buffer[blkn + blockcnt][i].data.Length); |
|||
|
|||
for (int bi = blockcnt; bi < componentInfo.MCU_width; bi++) |
|||
m_MCU_buffer[blkn + bi][0][0] = m_MCU_buffer[blkn + bi - 1][0][0]; |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
/* Create a row of dummy blocks at the bottom of the image. */ |
|||
for (int i = 0; i < componentInfo.MCU_width; i++) |
|||
Array.Clear(m_MCU_buffer[blkn][i].data, 0, m_MCU_buffer[blkn][i].data.Length); |
|||
|
|||
for (int bi = 0; bi < componentInfo.MCU_width; bi++) |
|||
m_MCU_buffer[blkn + bi][0][0] = m_MCU_buffer[blkn - 1][0][0]; |
|||
} |
|||
|
|||
blkn += componentInfo.MCU_width; |
|||
ypos += JpegConstants.DCTSIZE; |
|||
} |
|||
} |
|||
|
|||
/* Try to write the MCU. In event of a suspension failure, we will |
|||
* re-DCT the MCU on restart (a bit inefficient, could be fixed...) |
|||
*/ |
|||
if (!m_cinfo.m_entropy.encode_mcu(m_MCU_buffer)) |
|||
{ |
|||
/* Suspension forced; update state counters and exit */ |
|||
m_MCU_vert_offset = yoffset; |
|||
m_mcu_ctr = MCU_col_num; |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
/* Completed an MCU row, but perhaps not an iMCU row */ |
|||
m_mcu_ctr = 0; |
|||
} |
|||
|
|||
/* Completed the iMCU row, advance counters for next one */ |
|||
m_iMCU_row_num++; |
|||
start_iMCU_row(); |
|||
return true; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Process some data in the first pass of a multi-pass case.
|
|||
/// We process the equivalent of one fully interleaved MCU row ("iMCU" row)
|
|||
/// per call, ie, v_samp_factor block rows for each component in the image.
|
|||
/// This amount of data is read from the source buffer, DCT'd and quantized,
|
|||
/// and saved into the virtual arrays. We also generate suitable dummy blocks
|
|||
/// as needed at the right and lower edges. (The dummy blocks are constructed
|
|||
/// in the virtual arrays, which have been padded appropriately.) This makes
|
|||
/// it possible for subsequent passes not to worry about real vs. dummy blocks.
|
|||
///
|
|||
/// We must also emit the data to the entropy encoder. This is conveniently
|
|||
/// done by calling compress_output() after we've loaded the current strip
|
|||
/// of the virtual arrays.
|
|||
///
|
|||
/// NB: input_buf contains a plane for each component in image. All
|
|||
/// components are DCT'd and loaded into the virtual arrays in this pass.
|
|||
/// However, it may be that only a subset of the components are emitted to
|
|||
/// the entropy encoder during this first pass; be careful about looking
|
|||
/// at the scan-dependent variables (MCU dimensions, etc).
|
|||
/// </summary>
|
|||
private bool compressFirstPass(byte[][][] input_buf) |
|||
{ |
|||
int last_iMCU_row = m_cinfo.m_total_iMCU_rows - 1; |
|||
|
|||
for (int ci = 0; ci < m_cinfo.m_num_components; ci++) |
|||
{ |
|||
jpeg_component_info componentInfo = m_cinfo.Component_info[ci]; |
|||
|
|||
/* Align the virtual buffer for this component. */ |
|||
JBLOCK[][] buffer = m_whole_image[ci].Access(m_iMCU_row_num * componentInfo.V_samp_factor, |
|||
componentInfo.V_samp_factor); |
|||
|
|||
/* Count non-dummy DCT block rows in this iMCU row. */ |
|||
int block_rows; |
|||
if (m_iMCU_row_num < last_iMCU_row) |
|||
{ |
|||
block_rows = componentInfo.V_samp_factor; |
|||
} |
|||
else |
|||
{ |
|||
/* NB: can't use last_row_height here, since may not be set! */ |
|||
block_rows = componentInfo.height_in_blocks % componentInfo.V_samp_factor; |
|||
if (block_rows == 0) |
|||
block_rows = componentInfo.V_samp_factor; |
|||
} |
|||
|
|||
int blocks_across = componentInfo.Width_in_blocks; |
|||
int h_samp_factor = componentInfo.H_samp_factor; |
|||
|
|||
/* Count number of dummy blocks to be added at the right margin. */ |
|||
int ndummy = blocks_across % h_samp_factor; |
|||
if (ndummy > 0) |
|||
ndummy = h_samp_factor - ndummy; |
|||
|
|||
/* Perform DCT for all non-dummy blocks in this iMCU row. Each call |
|||
* on forward_DCT processes a complete horizontal row of DCT blocks. |
|||
*/ |
|||
for (int block_row = 0; block_row < block_rows; block_row++) |
|||
{ |
|||
m_cinfo.m_fdct.forward_DCT(componentInfo.Quant_tbl_no, input_buf[ci], |
|||
buffer[block_row], block_row * JpegConstants.DCTSIZE, 0, blocks_across); |
|||
|
|||
if (ndummy > 0) |
|||
{ |
|||
/* Create dummy blocks at the right edge of the image. */ |
|||
Array.Clear(buffer[block_row][blocks_across].data, 0, buffer[block_row][blocks_across].data.Length); |
|||
|
|||
short lastDC = buffer[block_row][blocks_across - 1][0]; |
|||
for (int bi = 0; bi < ndummy; bi++) |
|||
buffer[block_row][blocks_across + bi][0] = lastDC; |
|||
} |
|||
} |
|||
|
|||
/* If at end of image, create dummy block rows as needed. |
|||
* The tricky part here is that within each MCU, we want the DC values |
|||
* of the dummy blocks to match the last real block's DC value. |
|||
* This squeezes a few more bytes out of the resulting file... |
|||
*/ |
|||
if (m_iMCU_row_num == last_iMCU_row) |
|||
{ |
|||
blocks_across += ndummy; /* include lower right corner */ |
|||
int MCUs_across = blocks_across / h_samp_factor; |
|||
for (int block_row = block_rows; block_row < componentInfo.V_samp_factor; block_row++) |
|||
{ |
|||
for (int i = 0; i < blocks_across; i++) |
|||
Array.Clear(buffer[block_row][i].data, 0, buffer[block_row][i].data.Length); |
|||
|
|||
int thisOffset = 0; |
|||
int lastOffset = 0; |
|||
for (int MCUindex = 0; MCUindex < MCUs_across; MCUindex++) |
|||
{ |
|||
short lastDC = buffer[block_row - 1][lastOffset + h_samp_factor - 1][0]; |
|||
for (int bi = 0; bi < h_samp_factor; bi++) |
|||
buffer[block_row][thisOffset + bi][0] = lastDC; |
|||
|
|||
thisOffset += h_samp_factor; /* advance to next MCU in row */ |
|||
lastOffset += h_samp_factor; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
/* NB: compress_output will increment iMCU_row_num if successful. |
|||
* A suspension return will result in redoing all the work above next time. |
|||
*/ |
|||
|
|||
/* Emit data to the entropy encoder, sharing code with subsequent passes */ |
|||
return compressOutput(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Process some data in subsequent passes of a multi-pass case.
|
|||
/// We process the equivalent of one fully interleaved MCU row ("iMCU" row)
|
|||
/// per call, ie, v_samp_factor block rows for each component in the scan.
|
|||
/// The data is obtained from the virtual arrays and fed to the entropy coder.
|
|||
/// Returns true if the iMCU row is completed, false if suspended.
|
|||
/// </summary>
|
|||
private bool compressOutput() |
|||
{ |
|||
/* Align the virtual buffers for the components used in this scan. |
|||
*/ |
|||
JBLOCK[][][] buffer = new JBLOCK[JpegConstants.MAX_COMPS_IN_SCAN][][]; |
|||
for (int ci = 0; ci < m_cinfo.m_comps_in_scan; ci++) |
|||
{ |
|||
jpeg_component_info componentInfo = m_cinfo.Component_info[m_cinfo.m_cur_comp_info[ci]]; |
|||
buffer[ci] = m_whole_image[componentInfo.Component_index].Access( |
|||
m_iMCU_row_num * componentInfo.V_samp_factor, componentInfo.V_samp_factor); |
|||
} |
|||
|
|||
/* Loop to process one whole iMCU row */ |
|||
for (int yoffset = m_MCU_vert_offset; yoffset < m_MCU_rows_per_iMCU_row; yoffset++) |
|||
{ |
|||
for (int MCU_col_num = m_mcu_ctr; MCU_col_num < m_cinfo.m_MCUs_per_row; MCU_col_num++) |
|||
{ |
|||
/* Construct list of pointers to DCT blocks belonging to this MCU */ |
|||
int blkn = 0; /* index of current DCT block within MCU */ |
|||
for (int ci = 0; ci < m_cinfo.m_comps_in_scan; ci++) |
|||
{ |
|||
jpeg_component_info componentInfo = m_cinfo.Component_info[m_cinfo.m_cur_comp_info[ci]]; |
|||
int start_col = MCU_col_num * componentInfo.MCU_width; |
|||
for (int yindex = 0; yindex < componentInfo.MCU_height; yindex++) |
|||
{ |
|||
for (int xindex = 0; xindex < componentInfo.MCU_width; xindex++) |
|||
{ |
|||
int bufLength = buffer[ci][yindex + yoffset].Length; |
|||
int start = start_col + xindex; |
|||
m_MCU_buffer[blkn] = new JBLOCK[bufLength - start]; |
|||
for (int j = start; j < bufLength; j++) |
|||
m_MCU_buffer[blkn][j - start] = buffer[ci][yindex + yoffset][j]; |
|||
|
|||
blkn++; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/* Try to write the MCU. */ |
|||
if (!m_cinfo.m_entropy.encode_mcu(m_MCU_buffer)) |
|||
{ |
|||
/* Suspension forced; update state counters and exit */ |
|||
m_MCU_vert_offset = yoffset; |
|||
m_mcu_ctr = MCU_col_num; |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
/* Completed an MCU row, but perhaps not an iMCU row */ |
|||
m_mcu_ctr = 0; |
|||
} |
|||
|
|||
/* Completed the iMCU row, advance counters for next one */ |
|||
m_iMCU_row_num++; |
|||
start_iMCU_row(); |
|||
return true; |
|||
} |
|||
|
|||
// Reset within-iMCU-row counters for a new row
|
|||
private void start_iMCU_row() |
|||
{ |
|||
/* In an interleaved scan, an MCU row is the same as an iMCU row. |
|||
* In a noninterleaved scan, an iMCU row has v_samp_factor MCU rows. |
|||
* But at the bottom of the image, process only what's left. |
|||
*/ |
|||
if (m_cinfo.m_comps_in_scan > 1) |
|||
{ |
|||
m_MCU_rows_per_iMCU_row = 1; |
|||
} |
|||
else |
|||
{ |
|||
if (m_iMCU_row_num < (m_cinfo.m_total_iMCU_rows - 1)) |
|||
m_MCU_rows_per_iMCU_row = m_cinfo.Component_info[m_cinfo.m_cur_comp_info[0]].V_samp_factor; |
|||
else |
|||
m_MCU_rows_per_iMCU_row = m_cinfo.Component_info[m_cinfo.m_cur_comp_info[0]].last_row_height; |
|||
} |
|||
|
|||
m_mcu_ctr = 0; |
|||
m_MCU_vert_offset = 0; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,127 @@ |
|||
/* Copyright (C) 2008-2011, Bit Miracle |
|||
* http://www.bitmiracle.com
|
|||
* |
|||
* Copyright (C) 1994-1996, Thomas G. Lane. |
|||
* This file is part of the Independent JPEG Group's software. |
|||
* For conditions of distribution and use, see the accompanying README file. |
|||
* |
|||
*/ |
|||
|
|||
/* |
|||
* This file contains compression data destination routines for the case of |
|||
* emitting JPEG data to a file (or any stdio stream). While these routines |
|||
* are sufficient for most applications, some will want to use a different |
|||
* destination manager. |
|||
* IMPORTANT: we assume that fwrite() will correctly transcribe an array of |
|||
* bytes into 8-bit-wide elements on external storage. If char is wider |
|||
* than 8 bits on your machine, you may need to do some tweaking. |
|||
*/ |
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
using System.IO; |
|||
|
|||
namespace BitMiracle.LibJpeg.Classic.Internal |
|||
{ |
|||
/// <summary>
|
|||
/// Expanded data destination object for output to Stream
|
|||
/// </summary>
|
|||
class my_destination_mgr : jpeg_destination_mgr |
|||
{ |
|||
private const int OUTPUT_BUF_SIZE = 4096; /* choose an efficiently fwrite'able size */ |
|||
|
|||
private jpeg_compress_struct m_cinfo; |
|||
|
|||
private Stream m_outfile; /* target stream */ |
|||
private byte[] m_buffer; /* start of buffer */ |
|||
|
|||
public my_destination_mgr(jpeg_compress_struct cinfo, Stream alreadyOpenFile) |
|||
{ |
|||
m_cinfo = cinfo; |
|||
m_outfile = alreadyOpenFile; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initialize destination --- called by jpeg_start_compress
|
|||
/// before any data is actually written.
|
|||
/// </summary>
|
|||
public override void init_destination() |
|||
{ |
|||
/* Allocate the output buffer --- it will be released when done with image */ |
|||
m_buffer = new byte[OUTPUT_BUF_SIZE]; |
|||
initInternalBuffer(m_buffer, 0); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Empty the output buffer --- called whenever buffer fills up.
|
|||
///
|
|||
/// In typical applications, this should write the entire output buffer
|
|||
/// (ignoring the current state of next_output_byte and free_in_buffer),
|
|||
/// reset the pointer and count to the start of the buffer, and return true
|
|||
/// indicating that the buffer has been dumped.
|
|||
///
|
|||
/// In applications that need to be able to suspend compression due to output
|
|||
/// overrun, a false return indicates that the buffer cannot be emptied now.
|
|||
/// In this situation, the compressor will return to its caller (possibly with
|
|||
/// an indication that it has not accepted all the supplied scanlines). The
|
|||
/// application should resume compression after it has made more room in the
|
|||
/// output buffer. Note that there are substantial restrictions on the use of
|
|||
/// suspension --- see the documentation.
|
|||
///
|
|||
/// When suspending, the compressor will back up to a convenient restart point
|
|||
/// (typically the start of the current MCU). next_output_byte and free_in_buffer
|
|||
/// indicate where the restart point will be if the current call returns false.
|
|||
/// Data beyond this point will be regenerated after resumption, so do not
|
|||
/// write it out when emptying the buffer externally.
|
|||
/// </summary>
|
|||
public override bool empty_output_buffer() |
|||
{ |
|||
writeBuffer(m_buffer.Length); |
|||
initInternalBuffer(m_buffer, 0); |
|||
return true; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Terminate destination --- called by jpeg_finish_compress
|
|||
/// after all data has been written. Usually needs to flush buffer.
|
|||
///
|
|||
/// NB: *not* called by jpeg_abort or jpeg_destroy; surrounding
|
|||
/// application must deal with any cleanup that should happen even
|
|||
/// for error exit.
|
|||
/// </summary>
|
|||
public override void term_destination() |
|||
{ |
|||
int datacount = m_buffer.Length - freeInBuffer; |
|||
|
|||
/* Write any data remaining in the buffer */ |
|||
if (datacount > 0) |
|||
writeBuffer(datacount); |
|||
|
|||
m_outfile.Flush(); |
|||
} |
|||
|
|||
private void writeBuffer(int dataCount) |
|||
{ |
|||
try |
|||
{ |
|||
m_outfile.Write(m_buffer, 0, dataCount); |
|||
} |
|||
catch (IOException e) |
|||
{ |
|||
m_cinfo.TRACEMS(0, J_MESSAGE_CODE.JERR_FILE_WRITE, e.Message); |
|||
m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_FILE_WRITE); |
|||
} |
|||
catch (NotSupportedException e) |
|||
{ |
|||
m_cinfo.TRACEMS(0, J_MESSAGE_CODE.JERR_FILE_WRITE, e.Message); |
|||
m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_FILE_WRITE); |
|||
} |
|||
catch (ObjectDisposedException e) |
|||
{ |
|||
m_cinfo.TRACEMS(0, J_MESSAGE_CODE.JERR_FILE_WRITE, e.Message); |
|||
m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_FILE_WRITE); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,377 @@ |
|||
/* Copyright (C) 2008-2011, Bit Miracle |
|||
* http://www.bitmiracle.com
|
|||
* |
|||
* Copyright (C) 1994-1996, Thomas G. Lane. |
|||
* This file is part of the Independent JPEG Group's software. |
|||
* For conditions of distribution and use, see the accompanying README file. |
|||
* |
|||
*/ |
|||
|
|||
/* |
|||
* This file contains code for merged upsampling/color conversion. |
|||
* |
|||
* This file combines functions from my_upsampler and jpeg_color_deconverter; |
|||
* read those files first to understand what's going on. |
|||
* |
|||
* When the chroma components are to be upsampled by simple replication |
|||
* (ie, box filtering), we can save some work in color conversion by |
|||
* calculating all the output pixels corresponding to a pair of chroma |
|||
* samples at one time. In the conversion equations |
|||
* R = Y + K1 * Cr |
|||
* G = Y + K2 * Cb + K3 * Cr |
|||
* B = Y + K4 * Cb |
|||
* only the Y term varies among the group of pixels corresponding to a pair |
|||
* of chroma samples, so the rest of the terms can be calculated just once. |
|||
* At typical sampling ratios, this eliminates half or three-quarters of the |
|||
* multiplications needed for color conversion. |
|||
* |
|||
* This file currently provides implementations for the following cases: |
|||
* YCbCr => RGB color conversion only. |
|||
* Sampling ratios of 2h1v or 2h2v. |
|||
* No scaling needed at upsample time. |
|||
* Corner-aligned (non-CCIR601) sampling alignment. |
|||
* Other special cases could be added, but in most applications these are |
|||
* the only common cases. (For uncommon cases we fall back on the more |
|||
* general code in my_upsampler and jpeg_color_deconverter) |
|||
*/ |
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
|
|||
namespace BitMiracle.LibJpeg.Classic.Internal |
|||
{ |
|||
class my_merged_upsampler : jpeg_upsampler |
|||
{ |
|||
private const int SCALEBITS = 16; /* speediest right-shift on some machines */ |
|||
private const int ONE_HALF = 1 << (SCALEBITS - 1); |
|||
|
|||
private jpeg_decompress_struct m_cinfo; |
|||
|
|||
private bool m_use_2v_upsample; |
|||
|
|||
/* Private state for YCC->RGB conversion */ |
|||
private int[] m_Cr_r_tab; /* => table for Cr to R conversion */ |
|||
private int[] m_Cb_b_tab; /* => table for Cb to B conversion */ |
|||
private int[] m_Cr_g_tab; /* => table for Cr to G conversion */ |
|||
private int[] m_Cb_g_tab; /* => table for Cb to G conversion */ |
|||
|
|||
/* For 2:1 vertical sampling, we produce two output rows at a time. |
|||
* We need a "spare" row buffer to hold the second output row if the |
|||
* application provides just a one-row buffer; we also use the spare |
|||
* to discard the dummy last row if the image height is odd. |
|||
*/ |
|||
private byte[] m_spare_row; |
|||
private bool m_spare_full; /* T if spare buffer is occupied */ |
|||
|
|||
private int m_out_row_width; /* samples per output row */ |
|||
private int m_rows_to_go; /* counts rows remaining in image */ |
|||
|
|||
public my_merged_upsampler(jpeg_decompress_struct cinfo) |
|||
{ |
|||
m_cinfo = cinfo; |
|||
m_need_context_rows = false; |
|||
|
|||
m_out_row_width = cinfo.m_output_width * cinfo.m_out_color_components; |
|||
|
|||
if (cinfo.m_max_v_samp_factor == 2) |
|||
{ |
|||
m_use_2v_upsample = true; |
|||
/* Allocate a spare row buffer */ |
|||
m_spare_row = new byte[m_out_row_width]; |
|||
} |
|||
else |
|||
{ |
|||
m_use_2v_upsample = false; |
|||
} |
|||
|
|||
build_ycc_rgb_table(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initialize for an upsampling pass.
|
|||
/// </summary>
|
|||
public override void start_pass() |
|||
{ |
|||
/* Mark the spare buffer empty */ |
|||
m_spare_full = false; |
|||
|
|||
/* Initialize total-height counter for detecting bottom of image */ |
|||
m_rows_to_go = m_cinfo.m_output_height; |
|||
} |
|||
|
|||
public override void upsample(ComponentBuffer[] input_buf, ref int in_row_group_ctr, int in_row_groups_avail, byte[][] output_buf, ref int out_row_ctr, int out_rows_avail) |
|||
{ |
|||
if (m_use_2v_upsample) |
|||
merged_2v_upsample(input_buf, ref in_row_group_ctr, output_buf, ref out_row_ctr, out_rows_avail); |
|||
else |
|||
merged_1v_upsample(input_buf, ref in_row_group_ctr, output_buf, ref out_row_ctr); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Control routine to do upsampling (and color conversion).
|
|||
/// The control routine just handles the row buffering considerations.
|
|||
/// 1:1 vertical sampling case: much easier, never need a spare row.
|
|||
/// </summary>
|
|||
private void merged_1v_upsample(ComponentBuffer[] input_buf, ref int in_row_group_ctr, byte[][] output_buf, ref int out_row_ctr) |
|||
{ |
|||
/* Just do the upsampling. */ |
|||
h2v1_merged_upsample(input_buf, in_row_group_ctr, output_buf, out_row_ctr); |
|||
|
|||
/* Adjust counts */ |
|||
out_row_ctr++; |
|||
in_row_group_ctr++; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Control routine to do upsampling (and color conversion).
|
|||
/// The control routine just handles the row buffering considerations.
|
|||
/// 2:1 vertical sampling case: may need a spare row.
|
|||
/// </summary>
|
|||
private void merged_2v_upsample(ComponentBuffer[] input_buf, ref int in_row_group_ctr, byte[][] output_buf, ref int out_row_ctr, int out_rows_avail) |
|||
{ |
|||
int num_rows; /* number of rows returned to caller */ |
|||
if (m_spare_full) |
|||
{ |
|||
/* If we have a spare row saved from a previous cycle, just return it. */ |
|||
byte[][] temp = new byte[1][]; |
|||
temp[0] = m_spare_row; |
|||
JpegUtils.jcopy_sample_rows(temp, 0, output_buf, out_row_ctr, 1, m_out_row_width); |
|||
num_rows = 1; |
|||
m_spare_full = false; |
|||
} |
|||
else |
|||
{ |
|||
/* Figure number of rows to return to caller. */ |
|||
num_rows = 2; |
|||
|
|||
/* Not more than the distance to the end of the image. */ |
|||
if (num_rows > m_rows_to_go) |
|||
num_rows = m_rows_to_go; |
|||
|
|||
/* And not more than what the client can accept: */ |
|||
out_rows_avail -= out_row_ctr; |
|||
if (num_rows > out_rows_avail) |
|||
num_rows = out_rows_avail; |
|||
|
|||
/* Create output pointer array for upsampler. */ |
|||
byte[][] work_ptrs = new byte[2][]; |
|||
work_ptrs[0] = output_buf[out_row_ctr]; |
|||
if (num_rows > 1) |
|||
{ |
|||
work_ptrs[1] = output_buf[out_row_ctr + 1]; |
|||
} |
|||
else |
|||
{ |
|||
work_ptrs[1] = m_spare_row; |
|||
m_spare_full = true; |
|||
} |
|||
|
|||
/* Now do the upsampling. */ |
|||
h2v2_merged_upsample(input_buf, in_row_group_ctr, work_ptrs); |
|||
} |
|||
|
|||
/* Adjust counts */ |
|||
out_row_ctr += num_rows; |
|||
m_rows_to_go -= num_rows; |
|||
|
|||
/* When the buffer is emptied, declare this input row group consumed */ |
|||
if (!m_spare_full) |
|||
in_row_group_ctr++; |
|||
} |
|||
|
|||
/* |
|||
* These are the routines invoked by the control routines to do |
|||
* the actual upsampling/conversion. One row group is processed per call. |
|||
* |
|||
* Note: since we may be writing directly into application-supplied buffers, |
|||
* we have to be honest about the output width; we can't assume the buffer |
|||
* has been rounded up to an even width. |
|||
*/ |
|||
|
|||
/// <summary>
|
|||
/// Upsample and color convert for the case of 2:1 horizontal and 1:1 vertical.
|
|||
/// </summary>
|
|||
private void h2v1_merged_upsample(ComponentBuffer[] input_buf, int in_row_group_ctr, byte[][] output_buf, int outRow) |
|||
{ |
|||
int inputIndex0 = 0; |
|||
int inputIndex1 = 0; |
|||
int inputIndex2 = 0; |
|||
int outputIndex = 0; |
|||
|
|||
byte[] limit = m_cinfo.m_sample_range_limit; |
|||
int limitOffset = m_cinfo.m_sampleRangeLimitOffset; |
|||
|
|||
/* Loop for each pair of output pixels */ |
|||
for (int col = m_cinfo.m_output_width >> 1; col > 0; col--) |
|||
{ |
|||
/* Do the chroma part of the calculation */ |
|||
int cb = input_buf[1][in_row_group_ctr][inputIndex1]; |
|||
inputIndex1++; |
|||
|
|||
int cr = input_buf[2][in_row_group_ctr][inputIndex2]; |
|||
inputIndex2++; |
|||
|
|||
int cred = m_Cr_r_tab[cr]; |
|||
int cgreen = JpegUtils.RIGHT_SHIFT(m_Cb_g_tab[cb] + m_Cr_g_tab[cr], SCALEBITS); |
|||
int cblue = m_Cb_b_tab[cb]; |
|||
|
|||
/* Fetch 2 Y values and emit 2 pixels */ |
|||
int y = input_buf[0][in_row_group_ctr][inputIndex0]; |
|||
inputIndex0++; |
|||
|
|||
output_buf[outRow][outputIndex + JpegConstants.RGB_RED] = limit[limitOffset + y + cred]; |
|||
output_buf[outRow][outputIndex + JpegConstants.RGB_GREEN] = limit[limitOffset + y + cgreen]; |
|||
output_buf[outRow][outputIndex + JpegConstants.RGB_BLUE] = limit[limitOffset + y + cblue]; |
|||
outputIndex += JpegConstants.RGB_PIXELSIZE; |
|||
|
|||
y = input_buf[0][in_row_group_ctr][inputIndex0]; |
|||
inputIndex0++; |
|||
|
|||
output_buf[outRow][outputIndex + JpegConstants.RGB_RED] = limit[limitOffset + y + cred]; |
|||
output_buf[outRow][outputIndex + JpegConstants.RGB_GREEN] = limit[limitOffset + y + cgreen]; |
|||
output_buf[outRow][outputIndex + JpegConstants.RGB_BLUE] = limit[limitOffset + y + cblue]; |
|||
outputIndex += JpegConstants.RGB_PIXELSIZE; |
|||
} |
|||
|
|||
/* If image width is odd, do the last output column separately */ |
|||
if ((m_cinfo.m_output_width & 1) != 0) |
|||
{ |
|||
int cb = input_buf[1][in_row_group_ctr][inputIndex1]; |
|||
int cr = input_buf[2][in_row_group_ctr][inputIndex2]; |
|||
int cred = m_Cr_r_tab[cr]; |
|||
int cgreen = JpegUtils.RIGHT_SHIFT(m_Cb_g_tab[cb] + m_Cr_g_tab[cr], SCALEBITS); |
|||
int cblue = m_Cb_b_tab[cb]; |
|||
|
|||
int y = input_buf[0][in_row_group_ctr][inputIndex0]; |
|||
output_buf[outRow][outputIndex + JpegConstants.RGB_RED] = limit[limitOffset + y + cred]; |
|||
output_buf[outRow][outputIndex + JpegConstants.RGB_GREEN] = limit[limitOffset + y + cgreen]; |
|||
output_buf[outRow][outputIndex + JpegConstants.RGB_BLUE] = limit[limitOffset + y + cblue]; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Upsample and color convert for the case of 2:1 horizontal and 2:1 vertical.
|
|||
/// </summary>
|
|||
private void h2v2_merged_upsample(ComponentBuffer[] input_buf, int in_row_group_ctr, byte[][] output_buf) |
|||
{ |
|||
int inputRow00 = in_row_group_ctr * 2; |
|||
int inputIndex00 = 0; |
|||
|
|||
int inputRow01 = in_row_group_ctr * 2 + 1; |
|||
int inputIndex01 = 0; |
|||
|
|||
int inputIndex1 = 0; |
|||
int inputIndex2 = 0; |
|||
|
|||
int outIndex0 = 0; |
|||
int outIndex1 = 0; |
|||
|
|||
byte[] limit = m_cinfo.m_sample_range_limit; |
|||
int limitOffset = m_cinfo.m_sampleRangeLimitOffset; |
|||
|
|||
/* Loop for each group of output pixels */ |
|||
for (int col = m_cinfo.m_output_width >> 1; col > 0; col--) |
|||
{ |
|||
/* Do the chroma part of the calculation */ |
|||
int cb = input_buf[1][in_row_group_ctr][inputIndex1]; |
|||
inputIndex1++; |
|||
|
|||
int cr = input_buf[2][in_row_group_ctr][inputIndex2]; |
|||
inputIndex2++; |
|||
|
|||
int cred = m_Cr_r_tab[cr]; |
|||
int cgreen = JpegUtils.RIGHT_SHIFT(m_Cb_g_tab[cb] + m_Cr_g_tab[cr], SCALEBITS); |
|||
int cblue = m_Cb_b_tab[cb]; |
|||
|
|||
/* Fetch 4 Y values and emit 4 pixels */ |
|||
int y = input_buf[0][inputRow00][inputIndex00]; |
|||
inputIndex00++; |
|||
|
|||
output_buf[0][outIndex0 + JpegConstants.RGB_RED] = limit[limitOffset + y + cred]; |
|||
output_buf[0][outIndex0 + JpegConstants.RGB_GREEN] = limit[limitOffset + y + cgreen]; |
|||
output_buf[0][outIndex0 + JpegConstants.RGB_BLUE] = limit[limitOffset + y + cblue]; |
|||
outIndex0 += JpegConstants.RGB_PIXELSIZE; |
|||
|
|||
y = input_buf[0][inputRow00][inputIndex00]; |
|||
inputIndex00++; |
|||
|
|||
output_buf[0][outIndex0 + JpegConstants.RGB_RED] = limit[limitOffset + y + cred]; |
|||
output_buf[0][outIndex0 + JpegConstants.RGB_GREEN] = limit[limitOffset + y + cgreen]; |
|||
output_buf[0][outIndex0 + JpegConstants.RGB_BLUE] = limit[limitOffset + y + cblue]; |
|||
outIndex0 += JpegConstants.RGB_PIXELSIZE; |
|||
|
|||
y = input_buf[0][inputRow01][inputIndex01]; |
|||
inputIndex01++; |
|||
|
|||
output_buf[1][outIndex1 + JpegConstants.RGB_RED] = limit[limitOffset + y + cred]; |
|||
output_buf[1][outIndex1 + JpegConstants.RGB_GREEN] = limit[limitOffset + y + cgreen]; |
|||
output_buf[1][outIndex1 + JpegConstants.RGB_BLUE] = limit[limitOffset + y + cblue]; |
|||
outIndex1 += JpegConstants.RGB_PIXELSIZE; |
|||
|
|||
y = input_buf[0][inputRow01][inputIndex01]; |
|||
inputIndex01++; |
|||
|
|||
output_buf[1][outIndex1 + JpegConstants.RGB_RED] = limit[limitOffset + y + cred]; |
|||
output_buf[1][outIndex1 + JpegConstants.RGB_GREEN] = limit[limitOffset + y + cgreen]; |
|||
output_buf[1][outIndex1 + JpegConstants.RGB_BLUE] = limit[limitOffset + y + cblue]; |
|||
outIndex1 += JpegConstants.RGB_PIXELSIZE; |
|||
} |
|||
|
|||
/* If image width is odd, do the last output column separately */ |
|||
if ((m_cinfo.m_output_width & 1) != 0) |
|||
{ |
|||
int cb = input_buf[1][in_row_group_ctr][inputIndex1]; |
|||
int cr = input_buf[2][in_row_group_ctr][inputIndex2]; |
|||
int cred = m_Cr_r_tab[cr]; |
|||
int cgreen = JpegUtils.RIGHT_SHIFT(m_Cb_g_tab[cb] + m_Cr_g_tab[cr], SCALEBITS); |
|||
int cblue = m_Cb_b_tab[cb]; |
|||
|
|||
int y = input_buf[0][inputRow00][inputIndex00]; |
|||
output_buf[0][outIndex0 + JpegConstants.RGB_RED] = limit[limitOffset + y + cred]; |
|||
output_buf[0][outIndex0 + JpegConstants.RGB_GREEN] = limit[limitOffset + y + cgreen]; |
|||
output_buf[0][outIndex0 + JpegConstants.RGB_BLUE] = limit[limitOffset + y + cblue]; |
|||
|
|||
y = input_buf[0][inputRow01][inputIndex01]; |
|||
output_buf[1][outIndex1 + JpegConstants.RGB_RED] = limit[limitOffset + y + cred]; |
|||
output_buf[1][outIndex1 + JpegConstants.RGB_GREEN] = limit[limitOffset + y + cgreen]; |
|||
output_buf[1][outIndex1 + JpegConstants.RGB_BLUE] = limit[limitOffset + y + cblue]; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initialize tables for YCC->RGB colorspace conversion.
|
|||
/// This is taken directly from jpeg_color_deconverter; see that file for more info.
|
|||
/// </summary>
|
|||
private void build_ycc_rgb_table() |
|||
{ |
|||
m_Cr_r_tab = new int[JpegConstants.MAXJSAMPLE + 1]; |
|||
m_Cb_b_tab = new int[JpegConstants.MAXJSAMPLE + 1]; |
|||
m_Cr_g_tab = new int[JpegConstants.MAXJSAMPLE + 1]; |
|||
m_Cb_g_tab = new int[JpegConstants.MAXJSAMPLE + 1]; |
|||
|
|||
for (int i = 0, x = -JpegConstants.CENTERJSAMPLE; i <= JpegConstants.MAXJSAMPLE; i++, x++) |
|||
{ |
|||
/* i is the actual input pixel value, in the range 0..MAXJSAMPLE */ |
|||
/* The Cb or Cr value we are thinking of is x = i - CENTERJSAMPLE */ |
|||
/* Cr=>R value is nearest int to 1.40200 * x */ |
|||
m_Cr_r_tab[i] = JpegUtils.RIGHT_SHIFT(FIX(1.40200) * x + ONE_HALF, SCALEBITS); |
|||
|
|||
/* Cb=>B value is nearest int to 1.77200 * x */ |
|||
m_Cb_b_tab[i] = JpegUtils.RIGHT_SHIFT(FIX(1.77200) * x + ONE_HALF, SCALEBITS); |
|||
|
|||
/* Cr=>G value is scaled-up -0.71414 * x */ |
|||
m_Cr_g_tab[i] = (-FIX(0.71414)) * x; |
|||
|
|||
/* Cb=>G value is scaled-up -0.34414 * x */ |
|||
/* We also add in ONE_HALF so that need not do it in inner loop */ |
|||
m_Cb_g_tab[i] = (-FIX(0.34414)) * x + ONE_HALF; |
|||
} |
|||
} |
|||
|
|||
private static int FIX(double x) |
|||
{ |
|||
return ((int)((x) * (1L << SCALEBITS) + 0.5)); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,119 @@ |
|||
/* Copyright (C) 2008-2011, Bit Miracle |
|||
* http://www.bitmiracle.com
|
|||
* |
|||
* Copyright (C) 1994-1996, Thomas G. Lane. |
|||
* This file is part of the Independent JPEG Group's software. |
|||
* For conditions of distribution and use, see the accompanying README file. |
|||
* |
|||
*/ |
|||
|
|||
/* |
|||
* This file contains decompression data source routines for the case of |
|||
* reading JPEG data from a file (or any stdio stream). While these routines |
|||
* are sufficient for most applications, some will want to use a different |
|||
* source manager. |
|||
* IMPORTANT: we assume that fread() will correctly transcribe an array of |
|||
* bytes from 8-bit-wide elements on external storage. If char is wider |
|||
* than 8 bits on your machine, you may need to do some tweaking. |
|||
*/ |
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
using System.IO; |
|||
|
|||
namespace BitMiracle.LibJpeg.Classic.Internal |
|||
{ |
|||
/// <summary>
|
|||
/// Expanded data source object for stdio input
|
|||
/// </summary>
|
|||
class my_source_mgr : jpeg_source_mgr |
|||
{ |
|||
private const int INPUT_BUF_SIZE = 4096; |
|||
|
|||
private jpeg_decompress_struct m_cinfo; |
|||
|
|||
private Stream m_infile; /* source stream */ |
|||
private byte[] m_buffer; /* start of buffer */ |
|||
private bool m_start_of_file; /* have we gotten any data yet? */ |
|||
|
|||
/// <summary>
|
|||
/// Initialize source - called by jpeg_read_header
|
|||
/// before any data is actually read.
|
|||
/// </summary>
|
|||
public my_source_mgr(jpeg_decompress_struct cinfo) |
|||
{ |
|||
m_cinfo = cinfo; |
|||
m_buffer = new byte[INPUT_BUF_SIZE]; |
|||
} |
|||
|
|||
public void Attach(Stream infile) |
|||
{ |
|||
m_infile = infile; |
|||
m_infile.Seek(0, SeekOrigin.Begin); |
|||
initInternalBuffer(null, 0); |
|||
} |
|||
|
|||
public override void init_source() |
|||
{ |
|||
/* We reset the empty-input-file flag for each image, |
|||
* but we don't clear the input buffer. |
|||
* This is correct behavior for reading a series of images from one source. |
|||
*/ |
|||
m_start_of_file = true; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Fill the input buffer - called whenever buffer is emptied.
|
|||
///
|
|||
/// In typical applications, this should read fresh data into the buffer
|
|||
/// (ignoring the current state of next_input_byte and bytes_in_buffer),
|
|||
/// reset the pointer and count to the start of the buffer, and return true
|
|||
/// indicating that the buffer has been reloaded. It is not necessary to
|
|||
/// fill the buffer entirely, only to obtain at least one more byte.
|
|||
///
|
|||
/// There is no such thing as an EOF return. If the end of the file has been
|
|||
/// reached, the routine has a choice of ERREXIT() or inserting fake data into
|
|||
/// the buffer. In most cases, generating a warning message and inserting a
|
|||
/// fake EOI marker is the best course of action --- this will allow the
|
|||
/// decompressor to output however much of the image is there. However,
|
|||
/// the resulting error message is misleading if the real problem is an empty
|
|||
/// input file, so we handle that case specially.
|
|||
///
|
|||
/// In applications that need to be able to suspend compression due to input
|
|||
/// not being available yet, a false return indicates that no more data can be
|
|||
/// obtained right now, but more may be forthcoming later. In this situation,
|
|||
/// the decompressor will return to its caller (with an indication of the
|
|||
/// number of scanlines it has read, if any). The application should resume
|
|||
/// decompression after it has loaded more data into the input buffer. Note
|
|||
/// that there are substantial restrictions on the use of suspension --- see
|
|||
/// the documentation.
|
|||
///
|
|||
/// When suspending, the decompressor will back up to a convenient restart point
|
|||
/// (typically the start of the current MCU). next_input_byte and bytes_in_buffer
|
|||
/// indicate where the restart point will be if the current call returns false.
|
|||
/// Data beyond this point must be rescanned after resumption, so move it to
|
|||
/// the front of the buffer rather than discarding it.
|
|||
/// </summary>
|
|||
public override bool fill_input_buffer() |
|||
{ |
|||
int nbytes = m_infile.Read(m_buffer, 0, INPUT_BUF_SIZE); |
|||
if (nbytes <= 0) |
|||
{ |
|||
if (m_start_of_file) /* Treat empty input file as fatal error */ |
|||
m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_INPUT_EMPTY); |
|||
|
|||
m_cinfo.WARNMS(J_MESSAGE_CODE.JWRN_JPEG_EOF); |
|||
/* Insert a fake EOI marker */ |
|||
m_buffer[0] = (byte)0xFF; |
|||
m_buffer[1] = (byte)JPEG_MARKER.EOI; |
|||
nbytes = 2; |
|||
} |
|||
|
|||
initInternalBuffer(m_buffer, nbytes); |
|||
m_start_of_file = false; |
|||
|
|||
return true; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,200 @@ |
|||
/* Copyright (C) 2008-2011, Bit Miracle |
|||
* http://www.bitmiracle.com
|
|||
* |
|||
* Copyright (C) 1994-1996, Thomas G. Lane. |
|||
* This file is part of the Independent JPEG Group's software. |
|||
* For conditions of distribution and use, see the accompanying README file. |
|||
* |
|||
*/ |
|||
|
|||
/* |
|||
* This file contains library routines for transcoding compression, |
|||
* that is, writing raw DCT coefficient arrays to an output JPEG file. |
|||
*/ |
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
|
|||
namespace BitMiracle.LibJpeg.Classic.Internal |
|||
{ |
|||
/// <summary>
|
|||
/// This is a special implementation of the coefficient
|
|||
/// buffer controller. This is similar to jccoefct.c, but it handles only
|
|||
/// output from presupplied virtual arrays. Furthermore, we generate any
|
|||
/// dummy padding blocks on-the-fly rather than expecting them to be present
|
|||
/// in the arrays.
|
|||
/// </summary>
|
|||
class my_trans_c_coef_controller : jpeg_c_coef_controller |
|||
{ |
|||
private jpeg_compress_struct m_cinfo; |
|||
|
|||
private int m_iMCU_row_num; /* iMCU row # within image */ |
|||
private int m_mcu_ctr; /* counts MCUs processed in current row */ |
|||
private int m_MCU_vert_offset; /* counts MCU rows within iMCU row */ |
|||
private int m_MCU_rows_per_iMCU_row; /* number of such rows needed */ |
|||
|
|||
/* Virtual block array for each component. */ |
|||
private jvirt_array<JBLOCK>[] m_whole_image; |
|||
|
|||
/* Workspace for constructing dummy blocks at right/bottom edges. */ |
|||
private JBLOCK[][] m_dummy_buffer = new JBLOCK[JpegConstants.C_MAX_BLOCKS_IN_MCU][]; |
|||
|
|||
/// <summary>
|
|||
/// Initialize coefficient buffer controller.
|
|||
///
|
|||
/// Each passed coefficient array must be the right size for that
|
|||
/// coefficient: width_in_blocks wide and height_in_blocks high,
|
|||
/// with unit height at least v_samp_factor.
|
|||
/// </summary>
|
|||
public my_trans_c_coef_controller(jpeg_compress_struct cinfo, jvirt_array<JBLOCK>[] coef_arrays) |
|||
{ |
|||
m_cinfo = cinfo; |
|||
|
|||
/* Save pointer to virtual arrays */ |
|||
m_whole_image = coef_arrays; |
|||
|
|||
/* Allocate and pre-zero space for dummy DCT blocks. */ |
|||
JBLOCK[] buffer = new JBLOCK[JpegConstants.C_MAX_BLOCKS_IN_MCU]; |
|||
for (int i = 0; i < JpegConstants.C_MAX_BLOCKS_IN_MCU; i++) |
|||
buffer[i] = new JBLOCK(); |
|||
|
|||
for (int i = 0; i < JpegConstants.C_MAX_BLOCKS_IN_MCU; i++) |
|||
{ |
|||
m_dummy_buffer[i] = new JBLOCK[JpegConstants.C_MAX_BLOCKS_IN_MCU - i]; |
|||
for (int j = i; j < JpegConstants.C_MAX_BLOCKS_IN_MCU; j++) |
|||
m_dummy_buffer[i][j - i] = buffer[j]; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initialize for a processing pass.
|
|||
/// </summary>
|
|||
public virtual void start_pass(J_BUF_MODE pass_mode) |
|||
{ |
|||
if (pass_mode != J_BUF_MODE.JBUF_CRANK_DEST) |
|||
m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_BUFFER_MODE); |
|||
|
|||
m_iMCU_row_num = 0; |
|||
start_iMCU_row(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Process some data.
|
|||
/// We process the equivalent of one fully interleaved MCU row ("iMCU" row)
|
|||
/// per call, ie, v_samp_factor block rows for each component in the scan.
|
|||
/// The data is obtained from the virtual arrays and fed to the entropy coder.
|
|||
/// Returns true if the iMCU row is completed, false if suspended.
|
|||
///
|
|||
/// NB: input_buf is ignored; it is likely to be a null pointer.
|
|||
/// </summary>
|
|||
public virtual bool compress_data(byte[][][] input_buf) |
|||
{ |
|||
/* Align the virtual buffers for the components used in this scan. */ |
|||
JBLOCK[][][] buffer = new JBLOCK[JpegConstants.MAX_COMPS_IN_SCAN][][]; |
|||
for (int ci = 0; ci < m_cinfo.m_comps_in_scan; ci++) |
|||
{ |
|||
jpeg_component_info componentInfo = m_cinfo.Component_info[m_cinfo.m_cur_comp_info[ci]]; |
|||
buffer[ci] = m_whole_image[componentInfo.Component_index].Access( |
|||
m_iMCU_row_num * componentInfo.V_samp_factor, componentInfo.V_samp_factor); |
|||
} |
|||
|
|||
/* Loop to process one whole iMCU row */ |
|||
int last_MCU_col = m_cinfo.m_MCUs_per_row - 1; |
|||
int last_iMCU_row = m_cinfo.m_total_iMCU_rows - 1; |
|||
JBLOCK[][] MCU_buffer = new JBLOCK[JpegConstants.C_MAX_BLOCKS_IN_MCU][]; |
|||
for (int yoffset = m_MCU_vert_offset; yoffset < m_MCU_rows_per_iMCU_row; yoffset++) |
|||
{ |
|||
for (int MCU_col_num = m_mcu_ctr; MCU_col_num < m_cinfo.m_MCUs_per_row; MCU_col_num++) |
|||
{ |
|||
/* Construct list of pointers to DCT blocks belonging to this MCU */ |
|||
int blkn = 0; /* index of current DCT block within MCU */ |
|||
for (int ci = 0; ci < m_cinfo.m_comps_in_scan; ci++) |
|||
{ |
|||
jpeg_component_info componentInfo = m_cinfo.Component_info[m_cinfo.m_cur_comp_info[ci]]; |
|||
int start_col = MCU_col_num * componentInfo.MCU_width; |
|||
int blockcnt = (MCU_col_num < last_MCU_col) ? componentInfo.MCU_width : componentInfo.last_col_width; |
|||
for (int yindex = 0; yindex < componentInfo.MCU_height; yindex++) |
|||
{ |
|||
int xindex = 0; |
|||
if (m_iMCU_row_num < last_iMCU_row || yindex + yoffset < componentInfo.last_row_height) |
|||
{ |
|||
/* Fill in pointers to real blocks in this row */ |
|||
for (xindex = 0; xindex < blockcnt; xindex++) |
|||
{ |
|||
int bufLength = buffer[ci][yindex + yoffset].Length; |
|||
int start = start_col + xindex; |
|||
MCU_buffer[blkn] = new JBLOCK[bufLength - start]; |
|||
for (int j = start; j < bufLength; j++) |
|||
MCU_buffer[blkn][j - start] = buffer[ci][yindex + yoffset][j]; |
|||
|
|||
blkn++; |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
/* At bottom of image, need a whole row of dummy blocks */ |
|||
xindex = 0; |
|||
} |
|||
|
|||
/* Fill in any dummy blocks needed in this row. |
|||
* Dummy blocks are filled in the same way as in jccoefct.c: |
|||
* all zeroes in the AC entries, DC entries equal to previous |
|||
* block's DC value. The init routine has already zeroed the |
|||
* AC entries, so we need only set the DC entries correctly. |
|||
*/ |
|||
for (; xindex < componentInfo.MCU_width; xindex++) |
|||
{ |
|||
MCU_buffer[blkn] = m_dummy_buffer[blkn]; |
|||
MCU_buffer[blkn][0][0] = MCU_buffer[blkn - 1][0][0]; |
|||
blkn++; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/* Try to write the MCU. */ |
|||
if (!m_cinfo.m_entropy.encode_mcu(MCU_buffer)) |
|||
{ |
|||
/* Suspension forced; update state counters and exit */ |
|||
m_MCU_vert_offset = yoffset; |
|||
m_mcu_ctr = MCU_col_num; |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
/* Completed an MCU row, but perhaps not an iMCU row */ |
|||
m_mcu_ctr = 0; |
|||
} |
|||
|
|||
/* Completed the iMCU row, advance counters for next one */ |
|||
m_iMCU_row_num++; |
|||
start_iMCU_row(); |
|||
return true; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reset within-iMCU-row counters for a new row
|
|||
/// </summary>
|
|||
private void start_iMCU_row() |
|||
{ |
|||
/* In an interleaved scan, an MCU row is the same as an iMCU row. |
|||
* In a noninterleaved scan, an iMCU row has v_samp_factor MCU rows. |
|||
* But at the bottom of the image, process only what's left. |
|||
*/ |
|||
if (m_cinfo.m_comps_in_scan > 1) |
|||
{ |
|||
m_MCU_rows_per_iMCU_row = 1; |
|||
} |
|||
else |
|||
{ |
|||
if (m_iMCU_row_num < (m_cinfo.m_total_iMCU_rows - 1)) |
|||
m_MCU_rows_per_iMCU_row = m_cinfo.Component_info[m_cinfo.m_cur_comp_info[0]].V_samp_factor; |
|||
else |
|||
m_MCU_rows_per_iMCU_row = m_cinfo.Component_info[m_cinfo.m_cur_comp_info[0]].last_row_height; |
|||
} |
|||
|
|||
m_mcu_ctr = 0; |
|||
m_MCU_vert_offset = 0; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,521 @@ |
|||
/* Copyright (C) 2008-2011, Bit Miracle |
|||
* http://www.bitmiracle.com
|
|||
* |
|||
* Copyright (C) 1994-1996, Thomas G. Lane. |
|||
* This file is part of the Independent JPEG Group's software. |
|||
* For conditions of distribution and use, see the accompanying README file. |
|||
* |
|||
*/ |
|||
|
|||
/* |
|||
* This file contains upsampling routines. |
|||
* |
|||
* Upsampling input data is counted in "row groups". A row group |
|||
* is defined to be (v_samp_factor * DCT_scaled_size / min_DCT_scaled_size) |
|||
* sample rows of each component. Upsampling will normally produce |
|||
* max_v_samp_factor pixel rows from each row group (but this could vary |
|||
* if the upsampler is applying a scale factor of its own). |
|||
* |
|||
* An excellent reference for image resampling is |
|||
* Digital Image Warping, George Wolberg, 1990. |
|||
* Pub. by IEEE Computer Society Press, Los Alamitos, CA. ISBN 0-8186-8944-7. |
|||
*/ |
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
|
|||
namespace BitMiracle.LibJpeg.Classic.Internal |
|||
{ |
|||
class my_upsampler : jpeg_upsampler |
|||
{ |
|||
private enum ComponentUpsampler |
|||
{ |
|||
noop_upsampler, |
|||
fullsize_upsampler, |
|||
h2v1_fancy_upsampler, |
|||
h2v1_upsampler, |
|||
h2v2_fancy_upsampler, |
|||
h2v2_upsampler, |
|||
int_upsampler |
|||
} |
|||
|
|||
private jpeg_decompress_struct m_cinfo; |
|||
|
|||
/* Color conversion buffer. When using separate upsampling and color |
|||
* conversion steps, this buffer holds one upsampled row group until it |
|||
* has been color converted and output. |
|||
* Note: we do not allocate any storage for component(s) which are full-size, |
|||
* ie do not need rescaling. The corresponding entry of color_buf[] is |
|||
* simply set to point to the input data array, thereby avoiding copying. |
|||
*/ |
|||
private ComponentBuffer[] m_color_buf = new ComponentBuffer[JpegConstants.MAX_COMPONENTS]; |
|||
|
|||
// used only for fullsize_upsampler mode
|
|||
private int[] m_perComponentOffsets = new int[JpegConstants.MAX_COMPONENTS]; |
|||
|
|||
/* Per-component upsampling method pointers */ |
|||
private ComponentUpsampler[] m_upsampleMethods = new ComponentUpsampler[JpegConstants.MAX_COMPONENTS]; |
|||
private int m_currentComponent; // component being upsampled
|
|||
private int m_upsampleRowOffset; |
|||
|
|||
private int m_next_row_out; /* counts rows emitted from color_buf */ |
|||
private int m_rows_to_go; /* counts rows remaining in image */ |
|||
|
|||
/* Height of an input row group for each component. */ |
|||
private int[] m_rowgroup_height = new int[JpegConstants.MAX_COMPONENTS]; |
|||
|
|||
/* These arrays save pixel expansion factors so that int_expand need not |
|||
* recompute them each time. They are unused for other upsampling methods. |
|||
*/ |
|||
private byte[] m_h_expand = new byte[JpegConstants.MAX_COMPONENTS]; |
|||
private byte[] m_v_expand = new byte[JpegConstants.MAX_COMPONENTS]; |
|||
|
|||
public my_upsampler(jpeg_decompress_struct cinfo) |
|||
{ |
|||
m_cinfo = cinfo; |
|||
m_need_context_rows = false; /* until we find out differently */ |
|||
|
|||
if (cinfo.m_CCIR601_sampling) /* this isn't supported */ |
|||
cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CCIR601_NOTIMPL); |
|||
|
|||
/* jpeg_d_main_controller doesn't support context rows when min_DCT_scaled_size = 1, |
|||
* so don't ask for it. |
|||
*/ |
|||
bool do_fancy = cinfo.m_do_fancy_upsampling && cinfo.m_min_DCT_scaled_size > 1; |
|||
|
|||
/* Verify we can handle the sampling factors, select per-component methods, |
|||
* and create storage as needed. |
|||
*/ |
|||
for (int ci = 0; ci < cinfo.m_num_components; ci++) |
|||
{ |
|||
jpeg_component_info componentInfo = cinfo.Comp_info[ci]; |
|||
|
|||
/* Compute size of an "input group" after IDCT scaling. This many samples |
|||
* are to be converted to max_h_samp_factor * max_v_samp_factor pixels. |
|||
*/ |
|||
int h_in_group = (componentInfo.H_samp_factor * componentInfo.DCT_scaled_size) / cinfo.m_min_DCT_scaled_size; |
|||
int v_in_group = (componentInfo.V_samp_factor * componentInfo.DCT_scaled_size) / cinfo.m_min_DCT_scaled_size; |
|||
int h_out_group = cinfo.m_max_h_samp_factor; |
|||
int v_out_group = cinfo.m_max_v_samp_factor; |
|||
|
|||
/* save for use later */ |
|||
m_rowgroup_height[ci] = v_in_group; |
|||
bool need_buffer = true; |
|||
if (!componentInfo.component_needed) |
|||
{ |
|||
/* Don't bother to upsample an uninteresting component. */ |
|||
m_upsampleMethods[ci] = ComponentUpsampler.noop_upsampler; |
|||
need_buffer = false; |
|||
} |
|||
else if (h_in_group == h_out_group && v_in_group == v_out_group) |
|||
{ |
|||
/* Fullsize components can be processed without any work. */ |
|||
m_upsampleMethods[ci] = ComponentUpsampler.fullsize_upsampler; |
|||
need_buffer = false; |
|||
} |
|||
else if (h_in_group * 2 == h_out_group && v_in_group == v_out_group) |
|||
{ |
|||
/* Special cases for 2h1v upsampling */ |
|||
if (do_fancy && componentInfo.downsampled_width > 2) |
|||
m_upsampleMethods[ci] = ComponentUpsampler.h2v1_fancy_upsampler; |
|||
else |
|||
m_upsampleMethods[ci] = ComponentUpsampler.h2v1_upsampler; |
|||
} |
|||
else if (h_in_group * 2 == h_out_group && v_in_group * 2 == v_out_group) |
|||
{ |
|||
/* Special cases for 2h2v upsampling */ |
|||
if (do_fancy && componentInfo.downsampled_width > 2) |
|||
{ |
|||
m_upsampleMethods[ci] = ComponentUpsampler.h2v2_fancy_upsampler; |
|||
m_need_context_rows = true; |
|||
} |
|||
else |
|||
{ |
|||
m_upsampleMethods[ci] = ComponentUpsampler.h2v2_upsampler; |
|||
} |
|||
} |
|||
else if ((h_out_group % h_in_group) == 0 && (v_out_group % v_in_group) == 0) |
|||
{ |
|||
/* Generic integral-factors upsampling method */ |
|||
m_upsampleMethods[ci] = ComponentUpsampler.int_upsampler; |
|||
m_h_expand[ci] = (byte) (h_out_group / h_in_group); |
|||
m_v_expand[ci] = (byte) (v_out_group / v_in_group); |
|||
} |
|||
else |
|||
cinfo.ERREXIT(J_MESSAGE_CODE.JERR_FRACT_SAMPLE_NOTIMPL); |
|||
|
|||
if (need_buffer) |
|||
{ |
|||
ComponentBuffer cb = new ComponentBuffer(); |
|||
cb.SetBuffer(jpeg_common_struct.AllocJpegSamples(JpegUtils.jround_up(cinfo.m_output_width, |
|||
cinfo.m_max_h_samp_factor), cinfo.m_max_v_samp_factor), null, 0); |
|||
|
|||
m_color_buf[ci] = cb; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initialize for an upsampling pass.
|
|||
/// </summary>
|
|||
public override void start_pass() |
|||
{ |
|||
/* Mark the conversion buffer empty */ |
|||
m_next_row_out = m_cinfo.m_max_v_samp_factor; |
|||
|
|||
/* Initialize total-height counter for detecting bottom of image */ |
|||
m_rows_to_go = m_cinfo.m_output_height; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Control routine to do upsampling (and color conversion).
|
|||
///
|
|||
/// In this version we upsample each component independently.
|
|||
/// We upsample one row group into the conversion buffer, then apply
|
|||
/// color conversion a row at a time.
|
|||
/// </summary>
|
|||
public override void upsample(ComponentBuffer[] input_buf, ref int in_row_group_ctr, int in_row_groups_avail, byte[][] output_buf, ref int out_row_ctr, int out_rows_avail) |
|||
{ |
|||
/* Fill the conversion buffer, if it's empty */ |
|||
if (m_next_row_out >= m_cinfo.m_max_v_samp_factor) |
|||
{ |
|||
for (int ci = 0; ci < m_cinfo.m_num_components; ci++) |
|||
{ |
|||
m_perComponentOffsets[ci] = 0; |
|||
|
|||
/* Invoke per-component upsample method.*/ |
|||
m_currentComponent = ci; |
|||
m_upsampleRowOffset = in_row_group_ctr * m_rowgroup_height[ci]; |
|||
upsampleComponent(ref input_buf[ci]); |
|||
} |
|||
|
|||
m_next_row_out = 0; |
|||
} |
|||
|
|||
/* Color-convert and emit rows */ |
|||
|
|||
/* How many we have in the buffer: */ |
|||
int num_rows = m_cinfo.m_max_v_samp_factor - m_next_row_out; |
|||
|
|||
/* Not more than the distance to the end of the image. Need this test |
|||
* in case the image height is not a multiple of max_v_samp_factor: |
|||
*/ |
|||
if (num_rows > m_rows_to_go) |
|||
num_rows = m_rows_to_go; |
|||
|
|||
/* And not more than what the client can accept: */ |
|||
out_rows_avail -= out_row_ctr; |
|||
if (num_rows > out_rows_avail) |
|||
num_rows = out_rows_avail; |
|||
|
|||
m_cinfo.m_cconvert.color_convert(m_color_buf, m_perComponentOffsets, m_next_row_out, output_buf, out_row_ctr, num_rows); |
|||
|
|||
/* Adjust counts */ |
|||
out_row_ctr += num_rows; |
|||
m_rows_to_go -= num_rows; |
|||
m_next_row_out += num_rows; |
|||
|
|||
/* When the buffer is emptied, declare this input row group consumed */ |
|||
if (m_next_row_out >= m_cinfo.m_max_v_samp_factor) |
|||
in_row_group_ctr++; |
|||
} |
|||
|
|||
private void upsampleComponent(ref ComponentBuffer input_data) |
|||
{ |
|||
switch (m_upsampleMethods[m_currentComponent]) |
|||
{ |
|||
case ComponentUpsampler.noop_upsampler: |
|||
noop_upsample(); |
|||
break; |
|||
case ComponentUpsampler.fullsize_upsampler: |
|||
fullsize_upsample(ref input_data); |
|||
break; |
|||
case ComponentUpsampler.h2v1_fancy_upsampler: |
|||
h2v1_fancy_upsample(m_cinfo.Comp_info[m_currentComponent].downsampled_width, ref input_data); |
|||
break; |
|||
case ComponentUpsampler.h2v1_upsampler: |
|||
h2v1_upsample(ref input_data); |
|||
break; |
|||
case ComponentUpsampler.h2v2_fancy_upsampler: |
|||
h2v2_fancy_upsample(m_cinfo.Comp_info[m_currentComponent].downsampled_width, ref input_data); |
|||
break; |
|||
case ComponentUpsampler.h2v2_upsampler: |
|||
h2v2_upsample(ref input_data); |
|||
break; |
|||
case ComponentUpsampler.int_upsampler: |
|||
int_upsample(ref input_data); |
|||
break; |
|||
default: |
|||
m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_NOTIMPL); |
|||
break; |
|||
} |
|||
} |
|||
|
|||
/* |
|||
* These are the routines invoked to upsample pixel values |
|||
* of a single component. One row group is processed per call. |
|||
*/ |
|||
|
|||
/// <summary>
|
|||
/// This is a no-op version used for "uninteresting" components.
|
|||
/// These components will not be referenced by color conversion.
|
|||
/// </summary>
|
|||
private static void noop_upsample() |
|||
{ |
|||
// do nothing
|
|||
} |
|||
|
|||
/// <summary>
|
|||
/// For full-size components, we just make color_buf[ci] point at the
|
|||
/// input buffer, and thus avoid copying any data. Note that this is
|
|||
/// safe only because sep_upsample doesn't declare the input row group
|
|||
/// "consumed" until we are done color converting and emitting it.
|
|||
/// </summary>
|
|||
private void fullsize_upsample(ref ComponentBuffer input_data) |
|||
{ |
|||
m_color_buf[m_currentComponent] = input_data; |
|||
m_perComponentOffsets[m_currentComponent] = m_upsampleRowOffset; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Fancy processing for the common case of 2:1 horizontal and 1:1 vertical.
|
|||
///
|
|||
/// The upsampling algorithm is linear interpolation between pixel centers,
|
|||
/// also known as a "triangle filter". This is a good compromise between
|
|||
/// speed and visual quality. The centers of the output pixels are 1/4 and 3/4
|
|||
/// of the way between input pixel centers.
|
|||
///
|
|||
/// A note about the "bias" calculations: when rounding fractional values to
|
|||
/// integer, we do not want to always round 0.5 up to the next integer.
|
|||
/// If we did that, we'd introduce a noticeable bias towards larger values.
|
|||
/// Instead, this code is arranged so that 0.5 will be rounded up or down at
|
|||
/// alternate pixel locations (a simple ordered dither pattern).
|
|||
/// </summary>
|
|||
private void h2v1_fancy_upsample(int downsampled_width, ref ComponentBuffer input_data) |
|||
{ |
|||
ComponentBuffer output_data = m_color_buf[m_currentComponent]; |
|||
|
|||
for (int inrow = 0; inrow < m_cinfo.m_max_v_samp_factor; inrow++) |
|||
{ |
|||
int row = m_upsampleRowOffset + inrow; |
|||
int inIndex = 0; |
|||
|
|||
int outIndex = 0; |
|||
|
|||
/* Special case for first column */ |
|||
int invalue = input_data[row][inIndex]; |
|||
inIndex++; |
|||
|
|||
output_data[inrow][outIndex] = (byte)invalue; |
|||
outIndex++; |
|||
output_data[inrow][outIndex] = (byte)((invalue * 3 + (int)input_data[row][inIndex] + 2) >> 2); |
|||
outIndex++; |
|||
|
|||
for (int colctr = downsampled_width - 2; colctr > 0; colctr--) |
|||
{ |
|||
/* General case: 3/4 * nearer pixel + 1/4 * further pixel */ |
|||
invalue = (int)input_data[row][inIndex] * 3; |
|||
inIndex++; |
|||
|
|||
output_data[inrow][outIndex] = (byte)((invalue + (int)input_data[row][inIndex - 2] + 1) >> 2); |
|||
outIndex++; |
|||
|
|||
output_data[inrow][outIndex] = (byte)((invalue + (int)input_data[row][inIndex] + 2) >> 2); |
|||
outIndex++; |
|||
} |
|||
|
|||
/* Special case for last column */ |
|||
invalue = input_data[row][inIndex]; |
|||
output_data[inrow][outIndex] = (byte)((invalue * 3 + (int)input_data[row][inIndex - 1] + 1) >> 2); |
|||
outIndex++; |
|||
output_data[inrow][outIndex] = (byte)invalue; |
|||
outIndex++; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Fast processing for the common case of 2:1 horizontal and 1:1 vertical.
|
|||
/// It's still a box filter.
|
|||
/// </summary>
|
|||
private void h2v1_upsample(ref ComponentBuffer input_data) |
|||
{ |
|||
ComponentBuffer output_data = m_color_buf[m_currentComponent]; |
|||
|
|||
for (int inrow = 0; inrow < m_cinfo.m_max_v_samp_factor; inrow++) |
|||
{ |
|||
int row = m_upsampleRowOffset + inrow; |
|||
int outIndex = 0; |
|||
|
|||
for (int col = 0; col < m_cinfo.m_output_width; col++) |
|||
{ |
|||
byte invalue = input_data[row][col]; /* don't need GETJSAMPLE() here */ |
|||
output_data[inrow][outIndex] = invalue; |
|||
outIndex++; |
|||
output_data[inrow][outIndex] = invalue; |
|||
outIndex++; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Fancy processing for the common case of 2:1 horizontal and 2:1 vertical.
|
|||
/// Again a triangle filter; see comments for h2v1 case, above.
|
|||
///
|
|||
/// It is OK for us to reference the adjacent input rows because we demanded
|
|||
/// context from the main buffer controller (see initialization code).
|
|||
/// </summary>
|
|||
private void h2v2_fancy_upsample(int downsampled_width, ref ComponentBuffer input_data) |
|||
{ |
|||
ComponentBuffer output_data = m_color_buf[m_currentComponent]; |
|||
|
|||
int inrow = m_upsampleRowOffset; |
|||
int outrow = 0; |
|||
while (outrow < m_cinfo.m_max_v_samp_factor) |
|||
{ |
|||
for (int v = 0; v < 2; v++) |
|||
{ |
|||
// nearest input row index
|
|||
int inIndex0 = 0; |
|||
|
|||
//next nearest input row index
|
|||
int inIndex1 = 0; |
|||
int inRow1 = -1; |
|||
if (v == 0) |
|||
{ |
|||
/* next nearest is row above */ |
|||
inRow1 = inrow - 1; |
|||
} |
|||
else |
|||
{ |
|||
/* next nearest is row below */ |
|||
inRow1 = inrow + 1; |
|||
} |
|||
|
|||
int row = outrow; |
|||
int outIndex = 0; |
|||
outrow++; |
|||
|
|||
/* Special case for first column */ |
|||
int thiscolsum = (int)input_data[inrow][inIndex0] * 3 + (int)input_data[inRow1][inIndex1]; |
|||
inIndex0++; |
|||
inIndex1++; |
|||
|
|||
int nextcolsum = (int)input_data[inrow][inIndex0] * 3 + (int)input_data[inRow1][inIndex1]; |
|||
inIndex0++; |
|||
inIndex1++; |
|||
|
|||
output_data[row][outIndex] = (byte)((thiscolsum * 4 + 8) >> 4); |
|||
outIndex++; |
|||
|
|||
output_data[row][outIndex] = (byte)((thiscolsum * 3 + nextcolsum + 7) >> 4); |
|||
outIndex++; |
|||
|
|||
int lastcolsum = thiscolsum; |
|||
thiscolsum = nextcolsum; |
|||
|
|||
for (int colctr = downsampled_width - 2; colctr > 0; colctr--) |
|||
{ |
|||
/* General case: 3/4 * nearer pixel + 1/4 * further pixel in each */ |
|||
/* dimension, thus 9/16, 3/16, 3/16, 1/16 overall */ |
|||
nextcolsum = (int)input_data[inrow][inIndex0] * 3 + (int)input_data[inRow1][inIndex1]; |
|||
inIndex0++; |
|||
inIndex1++; |
|||
|
|||
output_data[row][outIndex] = (byte)((thiscolsum * 3 + lastcolsum + 8) >> 4); |
|||
outIndex++; |
|||
|
|||
output_data[row][outIndex] = (byte)((thiscolsum * 3 + nextcolsum + 7) >> 4); |
|||
outIndex++; |
|||
|
|||
lastcolsum = thiscolsum; |
|||
thiscolsum = nextcolsum; |
|||
} |
|||
|
|||
/* Special case for last column */ |
|||
output_data[row][outIndex] = (byte)((thiscolsum * 3 + lastcolsum + 8) >> 4); |
|||
outIndex++; |
|||
output_data[row][outIndex] = (byte)((thiscolsum * 4 + 7) >> 4); |
|||
outIndex++; |
|||
} |
|||
|
|||
inrow++; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Fast processing for the common case of 2:1 horizontal and 2:1 vertical.
|
|||
/// It's still a box filter.
|
|||
/// </summary>
|
|||
private void h2v2_upsample(ref ComponentBuffer input_data) |
|||
{ |
|||
ComponentBuffer output_data = m_color_buf[m_currentComponent]; |
|||
|
|||
int inrow = 0; |
|||
int outrow = 0; |
|||
while (outrow < m_cinfo.m_max_v_samp_factor) |
|||
{ |
|||
int row = m_upsampleRowOffset + inrow; |
|||
int outIndex = 0; |
|||
|
|||
for (int col = 0; col < m_cinfo.m_output_width; col++) |
|||
{ |
|||
byte invalue = input_data[row][col]; /* don't need GETJSAMPLE() here */ |
|||
output_data[outrow][outIndex] = invalue; |
|||
outIndex++; |
|||
output_data[outrow][outIndex] = invalue; |
|||
outIndex++; |
|||
} |
|||
|
|||
JpegUtils.jcopy_sample_rows(output_data, outrow, output_data, outrow + 1, 1, m_cinfo.m_output_width); |
|||
inrow++; |
|||
outrow += 2; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// This version handles any integral sampling ratios.
|
|||
/// This is not used for typical JPEG files, so it need not be fast.
|
|||
/// Nor, for that matter, is it particularly accurate: the algorithm is
|
|||
/// simple replication of the input pixel onto the corresponding output
|
|||
/// pixels. The hi-falutin sampling literature refers to this as a
|
|||
/// "box filter". A box filter tends to introduce visible artifacts,
|
|||
/// so if you are actually going to use 3:1 or 4:1 sampling ratios
|
|||
/// you would be well advised to improve this code.
|
|||
/// </summary>
|
|||
private void int_upsample(ref ComponentBuffer input_data) |
|||
{ |
|||
ComponentBuffer output_data = m_color_buf[m_currentComponent]; |
|||
int h_expand = m_h_expand[m_currentComponent]; |
|||
int v_expand = m_v_expand[m_currentComponent]; |
|||
|
|||
int inrow = 0; |
|||
int outrow = 0; |
|||
while (outrow < m_cinfo.m_max_v_samp_factor) |
|||
{ |
|||
/* Generate one output row with proper horizontal expansion */ |
|||
int row = m_upsampleRowOffset + inrow; |
|||
for (int col = 0; col < m_cinfo.m_output_width; col++) |
|||
{ |
|||
byte invalue = input_data[row][col]; /* don't need GETJSAMPLE() here */ |
|||
int outIndex = 0; |
|||
for (int h = h_expand; h > 0; h--) |
|||
{ |
|||
output_data[outrow][outIndex] = invalue; |
|||
outIndex++; |
|||
} |
|||
} |
|||
|
|||
/* Generate any additional output rows by duplicating the first one */ |
|||
if (v_expand > 1) |
|||
{ |
|||
JpegUtils.jcopy_sample_rows(output_data, outrow, output_data, |
|||
outrow + 1, v_expand - 1, m_cinfo.m_output_width); |
|||
} |
|||
|
|||
inrow++; |
|||
outrow += v_expand; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,716 @@ |
|||
/* Copyright (C) 2008-2011, Bit Miracle |
|||
* http://www.bitmiracle.com
|
|||
* |
|||
* Copyright (C) 1994-1996, Thomas G. Lane. |
|||
* This file is part of the Independent JPEG Group's software. |
|||
* For conditions of distribution and use, see the accompanying README file. |
|||
* |
|||
*/ |
|||
|
|||
/* |
|||
* This file contains Huffman entropy decoding routines for progressive JPEG. |
|||
* |
|||
* Much of the complexity here has to do with supporting input suspension. |
|||
* If the data source module demands suspension, we want to be able to back |
|||
* up to the start of the current MCU. To do this, we copy state variables |
|||
* into local working storage, and update them back to the permanent |
|||
* storage only upon successful completion of an MCU. |
|||
*/ |
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
|
|||
namespace BitMiracle.LibJpeg.Classic.Internal |
|||
{ |
|||
/// <summary>
|
|||
/// Expanded entropy decoder object for progressive Huffman decoding.
|
|||
///
|
|||
/// The savable_state subrecord contains fields that change within an MCU,
|
|||
/// but must not be updated permanently until we complete the MCU.
|
|||
/// </summary>
|
|||
class phuff_entropy_decoder : jpeg_entropy_decoder |
|||
{ |
|||
private class savable_state |
|||
{ |
|||
//savable_state operator=(savable_state src);
|
|||
public int EOBRUN; /* remaining EOBs in EOBRUN */ |
|||
public int[] last_dc_val = new int[JpegConstants.MAX_COMPS_IN_SCAN]; /* last DC coef for each component */ |
|||
|
|||
public void Assign(savable_state ss) |
|||
{ |
|||
EOBRUN = ss.EOBRUN; |
|||
Buffer.BlockCopy(ss.last_dc_val, 0, last_dc_val, 0, last_dc_val.Length * sizeof(int)); |
|||
} |
|||
} |
|||
|
|||
private enum MCUDecoder |
|||
{ |
|||
mcu_DC_first_decoder, |
|||
mcu_AC_first_decoder, |
|||
mcu_DC_refine_decoder, |
|||
mcu_AC_refine_decoder |
|||
} |
|||
|
|||
private MCUDecoder m_decoder; |
|||
|
|||
/* These fields are loaded into local variables at start of each MCU. |
|||
* In case of suspension, we exit WITHOUT updating them. |
|||
*/ |
|||
private bitread_perm_state m_bitstate; /* Bit buffer at start of MCU */ |
|||
private savable_state m_saved = new savable_state(); /* Other state at start of MCU */ |
|||
|
|||
/* These fields are NOT loaded into local working state. */ |
|||
private int m_restarts_to_go; /* MCUs left in this restart interval */ |
|||
|
|||
/* Pointers to derived tables (these workspaces have image lifespan) */ |
|||
private d_derived_tbl[] m_derived_tbls = new d_derived_tbl[JpegConstants.NUM_HUFF_TBLS]; |
|||
|
|||
private d_derived_tbl m_ac_derived_tbl; /* active table during an AC scan */ |
|||
|
|||
public phuff_entropy_decoder(jpeg_decompress_struct cinfo) |
|||
{ |
|||
m_cinfo = cinfo; |
|||
|
|||
/* Mark derived tables unallocated */ |
|||
for (int i = 0; i < JpegConstants.NUM_HUFF_TBLS; i++) |
|||
m_derived_tbls[i] = null; |
|||
|
|||
/* Create progression status table */ |
|||
cinfo.m_coef_bits = new int[cinfo.m_num_components][]; |
|||
for (int i = 0; i < cinfo.m_num_components; i++) |
|||
cinfo.m_coef_bits[i] = new int[JpegConstants.DCTSIZE2]; |
|||
|
|||
for (int ci = 0; ci < cinfo.m_num_components; ci++) |
|||
{ |
|||
for (int i = 0; i < JpegConstants.DCTSIZE2; i++) |
|||
cinfo.m_coef_bits[ci][i] = -1; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initialize for a Huffman-compressed scan.
|
|||
/// </summary>
|
|||
public override void start_pass() |
|||
{ |
|||
/* Validate scan parameters */ |
|||
bool bad = false; |
|||
bool is_DC_band = (m_cinfo.m_Ss == 0); |
|||
if (is_DC_band) |
|||
{ |
|||
if (m_cinfo.m_Se != 0) |
|||
bad = true; |
|||
} |
|||
else |
|||
{ |
|||
/* need not check Ss/Se < 0 since they came from unsigned bytes */ |
|||
if (m_cinfo.m_Ss > m_cinfo.m_Se || m_cinfo.m_Se >= JpegConstants.DCTSIZE2) |
|||
bad = true; |
|||
|
|||
/* AC scans may have only one component */ |
|||
if (m_cinfo.m_comps_in_scan != 1) |
|||
bad = true; |
|||
} |
|||
|
|||
if (m_cinfo.m_Ah != 0) |
|||
{ |
|||
/* Successive approximation refinement scan: must have Al = Ah-1. */ |
|||
if (m_cinfo.m_Al != m_cinfo.m_Ah - 1) |
|||
bad = true; |
|||
} |
|||
|
|||
if (m_cinfo.m_Al > 13) |
|||
{ |
|||
/* need not check for < 0 */ |
|||
bad = true; |
|||
} |
|||
|
|||
/* Arguably the maximum Al value should be less than 13 for 8-bit precision, |
|||
* but the spec doesn't say so, and we try to be liberal about what we |
|||
* accept. Note: large Al values could result in out-of-range DC |
|||
* coefficients during early scans, leading to bizarre displays due to |
|||
* overflows in the IDCT math. But we won't crash. |
|||
*/ |
|||
if (bad) |
|||
m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_PROGRESSION, m_cinfo.m_Ss, m_cinfo.m_Se, m_cinfo.m_Ah, m_cinfo.m_Al); |
|||
|
|||
/* Update progression status, and verify that scan order is legal. |
|||
* Note that inter-scan inconsistencies are treated as warnings |
|||
* not fatal errors ... not clear if this is right way to behave. |
|||
*/ |
|||
for (int ci = 0; ci < m_cinfo.m_comps_in_scan; ci++) |
|||
{ |
|||
int cindex = m_cinfo.Comp_info[m_cinfo.m_cur_comp_info[ci]].Component_index; |
|||
if (!is_DC_band && m_cinfo.m_coef_bits[cindex][0] < 0) /* AC without prior DC scan */ |
|||
m_cinfo.WARNMS(J_MESSAGE_CODE.JWRN_BOGUS_PROGRESSION, cindex, 0); |
|||
|
|||
for (int coefi = m_cinfo.m_Ss; coefi <= m_cinfo.m_Se; coefi++) |
|||
{ |
|||
int expected = m_cinfo.m_coef_bits[cindex][coefi]; |
|||
if (expected < 0) |
|||
expected = 0; |
|||
|
|||
if (m_cinfo.m_Ah != expected) |
|||
m_cinfo.WARNMS(J_MESSAGE_CODE.JWRN_BOGUS_PROGRESSION, cindex, coefi); |
|||
|
|||
m_cinfo.m_coef_bits[cindex][coefi] = m_cinfo.m_Al; |
|||
} |
|||
} |
|||
|
|||
/* Select MCU decoding routine */ |
|||
if (m_cinfo.m_Ah == 0) |
|||
{ |
|||
if (is_DC_band) |
|||
m_decoder = MCUDecoder.mcu_DC_first_decoder; |
|||
else |
|||
m_decoder = MCUDecoder.mcu_AC_first_decoder; |
|||
} |
|||
else |
|||
{ |
|||
if (is_DC_band) |
|||
m_decoder = MCUDecoder.mcu_DC_refine_decoder; |
|||
else |
|||
m_decoder = MCUDecoder.mcu_AC_refine_decoder; |
|||
} |
|||
|
|||
for (int ci = 0; ci < m_cinfo.m_comps_in_scan; ci++) |
|||
{ |
|||
jpeg_component_info componentInfo = m_cinfo.Comp_info[m_cinfo.m_cur_comp_info[ci]]; |
|||
/* Make sure requested tables are present, and compute derived tables. |
|||
* We may build same derived table more than once, but it's not expensive. |
|||
*/ |
|||
if (is_DC_band) |
|||
{ |
|||
if (m_cinfo.m_Ah == 0) |
|||
{ |
|||
/* DC refinement needs no table */ |
|||
jpeg_make_d_derived_tbl(true, componentInfo.Dc_tbl_no, ref m_derived_tbls[componentInfo.Dc_tbl_no]); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
jpeg_make_d_derived_tbl(false, componentInfo.Ac_tbl_no, ref m_derived_tbls[componentInfo.Ac_tbl_no]); |
|||
|
|||
/* remember the single active table */ |
|||
m_ac_derived_tbl = m_derived_tbls[componentInfo.Ac_tbl_no]; |
|||
} |
|||
|
|||
/* Initialize DC predictions to 0 */ |
|||
m_saved.last_dc_val[ci] = 0; |
|||
} |
|||
|
|||
/* Initialize bitread state variables */ |
|||
m_bitstate.bits_left = 0; |
|||
m_bitstate.get_buffer = 0; /* unnecessary, but keeps Purify quiet */ |
|||
m_insufficient_data = false; |
|||
|
|||
/* Initialize private state variables */ |
|||
m_saved.EOBRUN = 0; |
|||
|
|||
/* Initialize restart counter */ |
|||
m_restarts_to_go = m_cinfo.m_restart_interval; |
|||
} |
|||
|
|||
public override bool decode_mcu(JBLOCK[] MCU_data) |
|||
{ |
|||
switch (m_decoder) |
|||
{ |
|||
case MCUDecoder.mcu_DC_first_decoder: |
|||
return decode_mcu_DC_first(MCU_data); |
|||
case MCUDecoder.mcu_AC_first_decoder: |
|||
return decode_mcu_AC_first(MCU_data); |
|||
case MCUDecoder.mcu_DC_refine_decoder: |
|||
return decode_mcu_DC_refine(MCU_data); |
|||
case MCUDecoder.mcu_AC_refine_decoder: |
|||
return decode_mcu_AC_refine(MCU_data); |
|||
} |
|||
|
|||
m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_NOTIMPL); |
|||
return false; |
|||
} |
|||
|
|||
/* |
|||
* Huffman MCU decoding. |
|||
* Each of these routines decodes and returns one MCU's worth of |
|||
* Huffman-compressed coefficients. |
|||
* The coefficients are reordered from zigzag order into natural array order, |
|||
* but are not dequantized. |
|||
* |
|||
* The i'th block of the MCU is stored into the block pointed to by |
|||
* MCU_data[i]. WE ASSUME THIS AREA IS INITIALLY ZEROED BY THE CALLER. |
|||
* |
|||
* We return false if data source requested suspension. In that case no |
|||
* changes have been made to permanent state. (Exception: some output |
|||
* coefficients may already have been assigned. This is harmless for |
|||
* spectral selection, since we'll just re-assign them on the next call. |
|||
* Successive approximation AC refinement has to be more careful, however.) |
|||
*/ |
|||
|
|||
/// <summary>
|
|||
/// MCU decoding for DC initial scan (either spectral selection,
|
|||
/// or first pass of successive approximation).
|
|||
/// </summary>
|
|||
private bool decode_mcu_DC_first(JBLOCK[] MCU_data) |
|||
{ |
|||
/* Process restart marker if needed; may have to suspend */ |
|||
if (m_cinfo.m_restart_interval != 0) |
|||
{ |
|||
if (m_restarts_to_go == 0) |
|||
{ |
|||
if (!process_restart()) |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
/* If we've run out of data, just leave the MCU set to zeroes. |
|||
* This way, we return uniform gray for the remainder of the segment. |
|||
*/ |
|||
if (!m_insufficient_data) |
|||
{ |
|||
/* Load up working state */ |
|||
int get_buffer; |
|||
int bits_left; |
|||
bitread_working_state br_state = new bitread_working_state(); |
|||
BITREAD_LOAD_STATE(m_bitstate, out get_buffer, out bits_left, ref br_state); |
|||
savable_state state = new savable_state(); |
|||
state.Assign(m_saved); |
|||
|
|||
/* Outer loop handles each block in the MCU */ |
|||
for (int blkn = 0; blkn < m_cinfo.m_blocks_in_MCU; blkn++) |
|||
{ |
|||
int ci = m_cinfo.m_MCU_membership[blkn]; |
|||
|
|||
/* Decode a single block's worth of coefficients */ |
|||
|
|||
/* Section F.2.2.1: decode the DC coefficient difference */ |
|||
int s; |
|||
if (!HUFF_DECODE(out s, ref br_state, m_derived_tbls[m_cinfo.Comp_info[m_cinfo.m_cur_comp_info[ci]].Dc_tbl_no], ref get_buffer, ref bits_left)) |
|||
return false; |
|||
|
|||
if (s != 0) |
|||
{ |
|||
if (!CHECK_BIT_BUFFER(ref br_state, s, ref get_buffer, ref bits_left)) |
|||
return false; |
|||
|
|||
int r = GET_BITS(s, get_buffer, ref bits_left); |
|||
s = HUFF_EXTEND(r, s); |
|||
} |
|||
|
|||
/* Convert DC difference to actual value, update last_dc_val */ |
|||
s += state.last_dc_val[ci]; |
|||
state.last_dc_val[ci] = s; |
|||
|
|||
/* Scale and output the coefficient (assumes jpeg_natural_order[0]=0) */ |
|||
MCU_data[blkn][0] = (short)(s << m_cinfo.m_Al); |
|||
} |
|||
|
|||
/* Completed MCU, so update state */ |
|||
BITREAD_SAVE_STATE(ref m_bitstate, get_buffer, bits_left); |
|||
m_saved.Assign(state); |
|||
} |
|||
|
|||
/* Account for restart interval (no-op if not using restarts) */ |
|||
m_restarts_to_go--; |
|||
|
|||
return true; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// MCU decoding for AC initial scan (either spectral selection,
|
|||
/// or first pass of successive approximation).
|
|||
/// </summary>
|
|||
private bool decode_mcu_AC_first(JBLOCK[] MCU_data) |
|||
{ |
|||
/* Process restart marker if needed; may have to suspend */ |
|||
if (m_cinfo.m_restart_interval != 0) |
|||
{ |
|||
if (m_restarts_to_go == 0) |
|||
{ |
|||
if (!process_restart()) |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
/* If we've run out of data, just leave the MCU set to zeroes. |
|||
* This way, we return uniform gray for the remainder of the segment. |
|||
*/ |
|||
if (!m_insufficient_data) |
|||
{ |
|||
/* Load up working state. |
|||
* We can avoid loading/saving bitread state if in an EOB run. |
|||
*/ |
|||
int EOBRUN = m_saved.EOBRUN; /* only part of saved state we need */ |
|||
|
|||
/* There is always only one block per MCU */ |
|||
|
|||
if (EOBRUN > 0) |
|||
{ |
|||
/* if it's a band of zeroes... */ |
|||
/* ...process it now (we do nothing) */ |
|||
EOBRUN--; |
|||
} |
|||
else |
|||
{ |
|||
int get_buffer; |
|||
int bits_left; |
|||
bitread_working_state br_state = new bitread_working_state(); |
|||
BITREAD_LOAD_STATE(m_bitstate, out get_buffer, out bits_left, ref br_state); |
|||
|
|||
for (int k = m_cinfo.m_Ss; k <= m_cinfo.m_Se; k++) |
|||
{ |
|||
int s; |
|||
if (!HUFF_DECODE(out s, ref br_state, m_ac_derived_tbl, ref get_buffer, ref bits_left)) |
|||
return false; |
|||
|
|||
int r = s >> 4; |
|||
s &= 15; |
|||
if (s != 0) |
|||
{ |
|||
k += r; |
|||
|
|||
if (!CHECK_BIT_BUFFER(ref br_state, s, ref get_buffer, ref bits_left)) |
|||
return false; |
|||
|
|||
r = GET_BITS(s, get_buffer, ref bits_left); |
|||
s = HUFF_EXTEND(r, s); |
|||
|
|||
/* Scale and output coefficient in natural (dezigzagged) order */ |
|||
MCU_data[0][JpegUtils.jpeg_natural_order[k]] = (short) (s << m_cinfo.m_Al); |
|||
} |
|||
else |
|||
{ |
|||
if (r == 15) |
|||
{ |
|||
/* ZRL */ |
|||
k += 15; /* skip 15 zeroes in band */ |
|||
} |
|||
else |
|||
{ |
|||
/* EOBr, run length is 2^r + appended bits */ |
|||
EOBRUN = 1 << r; |
|||
if (r != 0) |
|||
{ |
|||
/* EOBr, r > 0 */ |
|||
if (!CHECK_BIT_BUFFER(ref br_state, r, ref get_buffer, ref bits_left)) |
|||
return false; |
|||
|
|||
r = GET_BITS(r, get_buffer, ref bits_left); |
|||
EOBRUN += r; |
|||
} |
|||
|
|||
EOBRUN--; /* this band is processed at this moment */ |
|||
break; /* force end-of-band */ |
|||
} |
|||
} |
|||
} |
|||
|
|||
BITREAD_SAVE_STATE(ref m_bitstate, get_buffer, bits_left); |
|||
} |
|||
|
|||
/* Completed MCU, so update state */ |
|||
m_saved.EOBRUN = EOBRUN; /* only part of saved state we need */ |
|||
} |
|||
|
|||
/* Account for restart interval (no-op if not using restarts) */ |
|||
m_restarts_to_go--; |
|||
|
|||
return true; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// MCU decoding for DC successive approximation refinement scan.
|
|||
/// Note: we assume such scans can be multi-component, although the spec
|
|||
/// is not very clear on the point.
|
|||
/// </summary>
|
|||
private bool decode_mcu_DC_refine(JBLOCK[] MCU_data) |
|||
{ |
|||
/* Process restart marker if needed; may have to suspend */ |
|||
if (m_cinfo.m_restart_interval != 0) |
|||
{ |
|||
if (m_restarts_to_go == 0) |
|||
{ |
|||
if (!process_restart()) |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
/* Not worth the cycles to check insufficient_data here, |
|||
* since we will not change the data anyway if we read zeroes. |
|||
*/ |
|||
|
|||
/* Load up working state */ |
|||
int get_buffer; |
|||
int bits_left; |
|||
bitread_working_state br_state = new bitread_working_state(); |
|||
BITREAD_LOAD_STATE(m_bitstate, out get_buffer, out bits_left, ref br_state); |
|||
|
|||
/* Outer loop handles each block in the MCU */ |
|||
|
|||
for (int blkn = 0; blkn < m_cinfo.m_blocks_in_MCU; blkn++) |
|||
{ |
|||
/* Encoded data is simply the next bit of the two's-complement DC value */ |
|||
if (!CHECK_BIT_BUFFER(ref br_state, 1, ref get_buffer, ref bits_left)) |
|||
return false; |
|||
|
|||
if (GET_BITS(1, get_buffer, ref bits_left) != 0) |
|||
{ |
|||
/* 1 in the bit position being coded */ |
|||
MCU_data[blkn][0] |= (short)(1 << m_cinfo.m_Al); |
|||
} |
|||
|
|||
/* Note: since we use |=, repeating the assignment later is safe */ |
|||
} |
|||
|
|||
/* Completed MCU, so update state */ |
|||
BITREAD_SAVE_STATE(ref m_bitstate, get_buffer, bits_left); |
|||
|
|||
/* Account for restart interval (no-op if not using restarts) */ |
|||
m_restarts_to_go--; |
|||
|
|||
return true; |
|||
} |
|||
|
|||
// There is always only one block per MCU
|
|||
private bool decode_mcu_AC_refine(JBLOCK[] MCU_data) |
|||
{ |
|||
int p1 = 1 << m_cinfo.m_Al; /* 1 in the bit position being coded */ |
|||
int m1 = -1 << m_cinfo.m_Al; /* -1 in the bit position being coded */ |
|||
|
|||
/* Process restart marker if needed; may have to suspend */ |
|||
if (m_cinfo.m_restart_interval != 0) |
|||
{ |
|||
if (m_restarts_to_go == 0) |
|||
{ |
|||
if (!process_restart()) |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
/* If we've run out of data, don't modify the MCU. |
|||
*/ |
|||
if (!m_insufficient_data) |
|||
{ |
|||
/* Load up working state */ |
|||
int get_buffer; |
|||
int bits_left; |
|||
bitread_working_state br_state = new bitread_working_state(); |
|||
BITREAD_LOAD_STATE(m_bitstate, out get_buffer, out bits_left, ref br_state); |
|||
int EOBRUN = m_saved.EOBRUN; /* only part of saved state we need */ |
|||
|
|||
/* If we are forced to suspend, we must undo the assignments to any newly |
|||
* nonzero coefficients in the block, because otherwise we'd get confused |
|||
* next time about which coefficients were already nonzero. |
|||
* But we need not undo addition of bits to already-nonzero coefficients; |
|||
* instead, we can test the current bit to see if we already did it. |
|||
*/ |
|||
int num_newnz = 0; |
|||
int[] newnz_pos = new int[JpegConstants.DCTSIZE2]; |
|||
|
|||
/* initialize coefficient loop counter to start of band */ |
|||
int k = m_cinfo.m_Ss; |
|||
|
|||
if (EOBRUN == 0) |
|||
{ |
|||
for (; k <= m_cinfo.m_Se; k++) |
|||
{ |
|||
int s; |
|||
if (!HUFF_DECODE(out s, ref br_state, m_ac_derived_tbl, ref get_buffer, ref bits_left)) |
|||
{ |
|||
undo_decode_mcu_AC_refine(MCU_data, newnz_pos, num_newnz); |
|||
return false; |
|||
} |
|||
|
|||
int r = s >> 4; |
|||
s &= 15; |
|||
if (s != 0) |
|||
{ |
|||
if (s != 1) |
|||
{ |
|||
/* size of new coef should always be 1 */ |
|||
m_cinfo.WARNMS(J_MESSAGE_CODE.JWRN_HUFF_BAD_CODE); |
|||
} |
|||
|
|||
if (!CHECK_BIT_BUFFER(ref br_state, 1, ref get_buffer, ref bits_left)) |
|||
{ |
|||
undo_decode_mcu_AC_refine(MCU_data, newnz_pos, num_newnz); |
|||
return false; |
|||
} |
|||
|
|||
if (GET_BITS(1, get_buffer, ref bits_left) != 0) |
|||
{ |
|||
/* newly nonzero coef is positive */ |
|||
s = p1; |
|||
} |
|||
else |
|||
{ |
|||
/* newly nonzero coef is negative */ |
|||
s = m1; |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
if (r != 15) |
|||
{ |
|||
EOBRUN = 1 << r; /* EOBr, run length is 2^r + appended bits */ |
|||
if (r != 0) |
|||
{ |
|||
if (!CHECK_BIT_BUFFER(ref br_state, r, ref get_buffer, ref bits_left)) |
|||
{ |
|||
undo_decode_mcu_AC_refine(MCU_data, newnz_pos, num_newnz); |
|||
return false; |
|||
} |
|||
|
|||
r = GET_BITS(r, get_buffer, ref bits_left); |
|||
EOBRUN += r; |
|||
} |
|||
break; /* rest of block is handled by EOB logic */ |
|||
} |
|||
/* note s = 0 for processing ZRL */ |
|||
} |
|||
/* Advance over already-nonzero coefs and r still-zero coefs, |
|||
* appending correction bits to the nonzeroes. A correction bit is 1 |
|||
* if the absolute value of the coefficient must be increased. |
|||
*/ |
|||
do |
|||
{ |
|||
int blockIndex = JpegUtils.jpeg_natural_order[k]; |
|||
short thiscoef = MCU_data[0][blockIndex]; |
|||
if (thiscoef != 0) |
|||
{ |
|||
if (!CHECK_BIT_BUFFER(ref br_state, 1, ref get_buffer, ref bits_left)) |
|||
{ |
|||
undo_decode_mcu_AC_refine(MCU_data, newnz_pos, num_newnz); |
|||
return false; |
|||
} |
|||
|
|||
if (GET_BITS(1, get_buffer, ref bits_left) != 0) |
|||
{ |
|||
if ((thiscoef & p1) == 0) |
|||
{ |
|||
/* do nothing if already set it */ |
|||
if (thiscoef >= 0) |
|||
MCU_data[0][blockIndex] += (short)p1; |
|||
else |
|||
MCU_data[0][blockIndex] += (short)m1; |
|||
} |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
if (--r < 0) |
|||
break; /* reached target zero coefficient */ |
|||
} |
|||
|
|||
k++; |
|||
} |
|||
while (k <= m_cinfo.m_Se); |
|||
|
|||
if (s != 0) |
|||
{ |
|||
int pos = JpegUtils.jpeg_natural_order[k]; |
|||
|
|||
/* Output newly nonzero coefficient */ |
|||
MCU_data[0][pos] = (short) s; |
|||
|
|||
/* Remember its position in case we have to suspend */ |
|||
newnz_pos[num_newnz++] = pos; |
|||
} |
|||
} |
|||
} |
|||
|
|||
if (EOBRUN > 0) |
|||
{ |
|||
/* Scan any remaining coefficient positions after the end-of-band |
|||
* (the last newly nonzero coefficient, if any). Append a correction |
|||
* bit to each already-nonzero coefficient. A correction bit is 1 |
|||
* if the absolute value of the coefficient must be increased. |
|||
*/ |
|||
for (; k <= m_cinfo.m_Se; k++) |
|||
{ |
|||
int blockIndex = JpegUtils.jpeg_natural_order[k]; |
|||
short thiscoef = MCU_data[0][blockIndex]; |
|||
if (thiscoef != 0) |
|||
{ |
|||
if (!CHECK_BIT_BUFFER(ref br_state, 1, ref get_buffer, ref bits_left)) |
|||
{ |
|||
//undo_decode_mcu_AC_refine(MCU_data[0], newnz_pos, num_newnz);
|
|||
undo_decode_mcu_AC_refine(MCU_data, newnz_pos, num_newnz); |
|||
return false; |
|||
} |
|||
|
|||
if (GET_BITS(1, get_buffer, ref bits_left) != 0) |
|||
{ |
|||
if ((thiscoef & p1) == 0) |
|||
{ |
|||
/* do nothing if already changed it */ |
|||
if (thiscoef >= 0) |
|||
MCU_data[0][blockIndex] += (short)p1; |
|||
else |
|||
MCU_data[0][blockIndex] += (short)m1; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
/* Count one block completed in EOB run */ |
|||
EOBRUN--; |
|||
} |
|||
|
|||
/* Completed MCU, so update state */ |
|||
BITREAD_SAVE_STATE(ref m_bitstate, get_buffer, bits_left); |
|||
m_saved.EOBRUN = EOBRUN; /* only part of saved state we need */ |
|||
} |
|||
|
|||
/* Account for restart interval (no-op if not using restarts) */ |
|||
m_restarts_to_go--; |
|||
|
|||
return true; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Check for a restart marker and resynchronize decoder.
|
|||
/// Returns false if must suspend.
|
|||
/// </summary>
|
|||
private bool process_restart() |
|||
{ |
|||
/* Throw away any unused bits remaining in bit buffer; */ |
|||
/* include any full bytes in next_marker's count of discarded bytes */ |
|||
m_cinfo.m_marker.SkipBytes(m_bitstate.bits_left / 8); |
|||
m_bitstate.bits_left = 0; |
|||
|
|||
/* Advance past the RSTn marker */ |
|||
if (!m_cinfo.m_marker.read_restart_marker()) |
|||
return false; |
|||
|
|||
/* Re-initialize DC predictions to 0 */ |
|||
for (int ci = 0; ci < m_cinfo.m_comps_in_scan; ci++) |
|||
m_saved.last_dc_val[ci] = 0; |
|||
|
|||
/* Re-init EOB run count, too */ |
|||
m_saved.EOBRUN = 0; |
|||
|
|||
/* Reset restart counter */ |
|||
m_restarts_to_go = m_cinfo.m_restart_interval; |
|||
|
|||
/* Reset out-of-data flag, unless read_restart_marker left us smack up |
|||
* against a marker. In that case we will end up treating the next data |
|||
* segment as empty, and we can avoid producing bogus output pixels by |
|||
* leaving the flag set. |
|||
*/ |
|||
if (m_cinfo.m_unread_marker == 0) |
|||
m_insufficient_data = false; |
|||
|
|||
return true; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// MCU decoding for AC successive approximation refinement scan.
|
|||
/// </summary>
|
|||
private static void undo_decode_mcu_AC_refine(JBLOCK[] block, int[] newnz_pos, int num_newnz) |
|||
{ |
|||
/* Re-zero any output coefficients that we made newly nonzero */ |
|||
while (num_newnz > 0) |
|||
block[0][newnz_pos[--num_newnz]] = 0; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,769 @@ |
|||
/* Copyright (C) 2008-2011, Bit Miracle |
|||
* http://www.bitmiracle.com
|
|||
* |
|||
* Copyright (C) 1994-1996, Thomas G. Lane. |
|||
* This file is part of the Independent JPEG Group's software. |
|||
* For conditions of distribution and use, see the accompanying README file. |
|||
* |
|||
*/ |
|||
|
|||
/* |
|||
* This file contains Huffman entropy encoding routines for progressive JPEG. |
|||
* |
|||
* We do not support output suspension in this module, since the library |
|||
* currently does not allow multiple-scan files to be written with output |
|||
* suspension. |
|||
*/ |
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
|
|||
namespace BitMiracle.LibJpeg.Classic.Internal |
|||
{ |
|||
/// <summary>
|
|||
/// Expanded entropy encoder object for progressive Huffman encoding.
|
|||
/// </summary>
|
|||
class phuff_entropy_encoder : jpeg_entropy_encoder |
|||
{ |
|||
private enum MCUEncoder |
|||
{ |
|||
mcu_DC_first_encoder, |
|||
mcu_AC_first_encoder, |
|||
mcu_DC_refine_encoder, |
|||
mcu_AC_refine_encoder |
|||
} |
|||
|
|||
/* MAX_CORR_BITS is the number of bits the AC refinement correction-bit |
|||
* buffer can hold. Larger sizes may slightly improve compression, but |
|||
* 1000 is already well into the realm of overkill. |
|||
* The minimum safe size is 64 bits. |
|||
*/ |
|||
private const int MAX_CORR_BITS = 1000; /* Max # of correction bits I can buffer */ |
|||
|
|||
private MCUEncoder m_MCUEncoder; |
|||
|
|||
/* Mode flag: true for optimization, false for actual data output */ |
|||
private bool m_gather_statistics; |
|||
|
|||
// Bit-level coding status.
|
|||
private int m_put_buffer; /* current bit-accumulation buffer */ |
|||
private int m_put_bits; /* # of bits now in it */ |
|||
|
|||
/* Coding status for DC components */ |
|||
private int[] m_last_dc_val = new int[JpegConstants.MAX_COMPS_IN_SCAN]; /* last DC coef for each component */ |
|||
|
|||
/* Coding status for AC components */ |
|||
private int m_ac_tbl_no; /* the table number of the single component */ |
|||
private int m_EOBRUN; /* run length of EOBs */ |
|||
private int m_BE; /* # of buffered correction bits before MCU */ |
|||
private char[] m_bit_buffer; /* buffer for correction bits (1 per char) */ |
|||
/* packing correction bits tightly would save some space but cost time... */ |
|||
|
|||
private int m_restarts_to_go; /* MCUs left in this restart interval */ |
|||
private int m_next_restart_num; /* next restart number to write (0-7) */ |
|||
|
|||
/* Pointers to derived tables (these workspaces have image lifespan). |
|||
* Since any one scan codes only DC or only AC, we only need one set |
|||
* of tables, not one for DC and one for AC. |
|||
*/ |
|||
private c_derived_tbl[] m_derived_tbls = new c_derived_tbl[JpegConstants.NUM_HUFF_TBLS]; |
|||
|
|||
/* Statistics tables for optimization; again, one set is enough */ |
|||
private long[][] m_count_ptrs = new long[JpegConstants.NUM_HUFF_TBLS][]; |
|||
|
|||
public phuff_entropy_encoder(jpeg_compress_struct cinfo) |
|||
{ |
|||
m_cinfo = cinfo; |
|||
|
|||
/* Mark tables unallocated */ |
|||
for (int i = 0; i < JpegConstants.NUM_HUFF_TBLS; i++) |
|||
{ |
|||
m_derived_tbls[i] = null; |
|||
m_count_ptrs[i] = null; |
|||
} |
|||
} |
|||
|
|||
// Initialize for a Huffman-compressed scan using progressive JPEG.
|
|||
public override void start_pass(bool gather_statistics) |
|||
{ |
|||
m_gather_statistics = gather_statistics; |
|||
|
|||
/* We assume the scan parameters are already validated. */ |
|||
|
|||
/* Select execution routines */ |
|||
bool is_DC_band = (m_cinfo.m_Ss == 0); |
|||
if (m_cinfo.m_Ah == 0) |
|||
{ |
|||
if (is_DC_band) |
|||
m_MCUEncoder = MCUEncoder.mcu_DC_first_encoder; |
|||
else |
|||
m_MCUEncoder = MCUEncoder.mcu_AC_first_encoder; |
|||
} |
|||
else |
|||
{ |
|||
if (is_DC_band) |
|||
{ |
|||
m_MCUEncoder = MCUEncoder.mcu_DC_refine_encoder; |
|||
} |
|||
else |
|||
{ |
|||
m_MCUEncoder = MCUEncoder.mcu_AC_refine_encoder; |
|||
|
|||
/* AC refinement needs a correction bit buffer */ |
|||
if (m_bit_buffer == null) |
|||
m_bit_buffer = new char[MAX_CORR_BITS]; |
|||
} |
|||
} |
|||
|
|||
/* Only DC coefficients may be interleaved, so m_cinfo.comps_in_scan = 1 |
|||
* for AC coefficients. |
|||
*/ |
|||
for (int ci = 0; ci < m_cinfo.m_comps_in_scan; ci++) |
|||
{ |
|||
jpeg_component_info componentInfo = m_cinfo.Component_info[m_cinfo.m_cur_comp_info[ci]]; |
|||
|
|||
/* Initialize DC predictions to 0 */ |
|||
m_last_dc_val[ci] = 0; |
|||
|
|||
/* Get table index */ |
|||
int tbl; |
|||
if (is_DC_band) |
|||
{ |
|||
if (m_cinfo.m_Ah != 0) /* DC refinement needs no table */ |
|||
continue; |
|||
|
|||
tbl = componentInfo.Dc_tbl_no; |
|||
} |
|||
else |
|||
{ |
|||
m_ac_tbl_no = componentInfo.Ac_tbl_no; |
|||
tbl = componentInfo.Ac_tbl_no; |
|||
} |
|||
|
|||
if (m_gather_statistics) |
|||
{ |
|||
/* Check for invalid table index */ |
|||
/* (make_c_derived_tbl does this in the other path) */ |
|||
if (tbl < 0 || tbl >= JpegConstants.NUM_HUFF_TBLS) |
|||
m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_NO_HUFF_TABLE, tbl); |
|||
|
|||
/* Allocate and zero the statistics tables */ |
|||
/* Note that jpeg_gen_optimal_table expects 257 entries in each table! */ |
|||
if (m_count_ptrs[tbl] == null) |
|||
m_count_ptrs[tbl] = new long[257]; |
|||
|
|||
Array.Clear(m_count_ptrs[tbl], 0, 257); |
|||
} |
|||
else |
|||
{ |
|||
/* Compute derived values for Huffman table */ |
|||
/* We may do this more than once for a table, but it's not expensive */ |
|||
jpeg_make_c_derived_tbl(is_DC_band, tbl, ref m_derived_tbls[tbl]); |
|||
} |
|||
} |
|||
|
|||
/* Initialize AC stuff */ |
|||
m_EOBRUN = 0; |
|||
m_BE = 0; |
|||
|
|||
/* Initialize bit buffer to empty */ |
|||
m_put_buffer = 0; |
|||
m_put_bits = 0; |
|||
|
|||
/* Initialize restart stuff */ |
|||
m_restarts_to_go = m_cinfo.m_restart_interval; |
|||
m_next_restart_num = 0; |
|||
} |
|||
|
|||
public override bool encode_mcu(JBLOCK[][] MCU_data) |
|||
{ |
|||
switch (m_MCUEncoder) |
|||
{ |
|||
case MCUEncoder.mcu_DC_first_encoder: |
|||
return encode_mcu_DC_first(MCU_data); |
|||
case MCUEncoder.mcu_AC_first_encoder: |
|||
return encode_mcu_AC_first(MCU_data); |
|||
case MCUEncoder.mcu_DC_refine_encoder: |
|||
return encode_mcu_DC_refine(MCU_data); |
|||
case MCUEncoder.mcu_AC_refine_encoder: |
|||
return encode_mcu_AC_refine(MCU_data); |
|||
} |
|||
|
|||
m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_NOTIMPL); |
|||
return false; |
|||
} |
|||
|
|||
public override void finish_pass() |
|||
{ |
|||
if (m_gather_statistics) |
|||
finish_pass_gather_phuff(); |
|||
else |
|||
finish_pass_phuff(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// MCU encoding for DC initial scan (either spectral selection,
|
|||
/// or first pass of successive approximation).
|
|||
/// </summary>
|
|||
private bool encode_mcu_DC_first(JBLOCK[][] MCU_data) |
|||
{ |
|||
/* Emit restart marker if needed */ |
|||
if (m_cinfo.m_restart_interval != 0) |
|||
{ |
|||
if (m_restarts_to_go == 0) |
|||
emit_restart(m_next_restart_num); |
|||
} |
|||
|
|||
/* Encode the MCU data blocks */ |
|||
for (int blkn = 0; blkn < m_cinfo.m_blocks_in_MCU; blkn++) |
|||
{ |
|||
/* Compute the DC value after the required point transform by Al. |
|||
* This is simply an arithmetic right shift. |
|||
*/ |
|||
int temp2 = IRIGHT_SHIFT(MCU_data[blkn][0][0], m_cinfo.m_Al); |
|||
|
|||
/* DC differences are figured on the point-transformed values. */ |
|||
int ci = m_cinfo.m_MCU_membership[blkn]; |
|||
int temp = temp2 - m_last_dc_val[ci]; |
|||
m_last_dc_val[ci] = temp2; |
|||
|
|||
/* Encode the DC coefficient difference per section G.1.2.1 */ |
|||
temp2 = temp; |
|||
if (temp < 0) |
|||
{ |
|||
/* temp is abs value of input */ |
|||
temp = -temp; |
|||
|
|||
/* For a negative input, want temp2 = bitwise complement of abs(input) */ |
|||
/* This code assumes we are on a two's complement machine */ |
|||
temp2--; |
|||
} |
|||
|
|||
/* Find the number of bits needed for the magnitude of the coefficient */ |
|||
int nbits = 0; |
|||
while (temp != 0) |
|||
{ |
|||
nbits++; |
|||
temp >>= 1; |
|||
} |
|||
|
|||
/* Check for out-of-range coefficient values. |
|||
* Since we're encoding a difference, the range limit is twice as much. |
|||
*/ |
|||
if (nbits > MAX_HUFFMAN_COEF_BITS + 1) |
|||
m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_DCT_COEF); |
|||
|
|||
/* Count/emit the Huffman-coded symbol for the number of bits */ |
|||
emit_symbol(m_cinfo.Component_info[m_cinfo.m_cur_comp_info[ci]].Dc_tbl_no, nbits); |
|||
|
|||
/* Emit that number of bits of the value, if positive, */ |
|||
/* or the complement of its magnitude, if negative. */ |
|||
if (nbits != 0) |
|||
{ |
|||
/* emit_bits rejects calls with size 0 */ |
|||
emit_bits(temp2, nbits); |
|||
} |
|||
} |
|||
|
|||
/* Update restart-interval state too */ |
|||
if (m_cinfo.m_restart_interval != 0) |
|||
{ |
|||
if (m_restarts_to_go == 0) |
|||
{ |
|||
m_restarts_to_go = m_cinfo.m_restart_interval; |
|||
m_next_restart_num++; |
|||
m_next_restart_num &= 7; |
|||
} |
|||
|
|||
m_restarts_to_go--; |
|||
} |
|||
|
|||
return true; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// MCU encoding for AC initial scan (either spectral selection,
|
|||
/// or first pass of successive approximation).
|
|||
/// </summary>
|
|||
private bool encode_mcu_AC_first(JBLOCK[][] MCU_data) |
|||
{ |
|||
/* Emit restart marker if needed */ |
|||
if (m_cinfo.m_restart_interval != 0) |
|||
{ |
|||
if (m_restarts_to_go == 0) |
|||
emit_restart(m_next_restart_num); |
|||
} |
|||
|
|||
/* Encode the AC coefficients per section G.1.2.2, fig. G.3 */ |
|||
/* r = run length of zeros */ |
|||
int r = 0; |
|||
for (int k = m_cinfo.m_Ss; k <= m_cinfo.m_Se; k++) |
|||
{ |
|||
int temp = MCU_data[0][0][JpegUtils.jpeg_natural_order[k]]; |
|||
if (temp == 0) |
|||
{ |
|||
r++; |
|||
continue; |
|||
} |
|||
|
|||
/* We must apply the point transform by Al. For AC coefficients this |
|||
* is an integer division with rounding towards 0. To do this portably |
|||
* in C, we shift after obtaining the absolute value; so the code is |
|||
* interwoven with finding the abs value (temp) and output bits (temp2). |
|||
*/ |
|||
int temp2; |
|||
if (temp < 0) |
|||
{ |
|||
temp = -temp; /* temp is abs value of input */ |
|||
temp >>= m_cinfo.m_Al; /* apply the point transform */ |
|||
/* For a negative coef, want temp2 = bitwise complement of abs(coef) */ |
|||
temp2 = ~temp; |
|||
} |
|||
else |
|||
{ |
|||
temp >>= m_cinfo.m_Al; /* apply the point transform */ |
|||
temp2 = temp; |
|||
} |
|||
|
|||
/* Watch out for case that nonzero coef is zero after point transform */ |
|||
if (temp == 0) |
|||
{ |
|||
r++; |
|||
continue; |
|||
} |
|||
|
|||
/* Emit any pending EOBRUN */ |
|||
if (m_EOBRUN > 0) |
|||
emit_eobrun(); |
|||
|
|||
/* if run length > 15, must emit special run-length-16 codes (0xF0) */ |
|||
while (r > 15) |
|||
{ |
|||
emit_symbol(m_ac_tbl_no, 0xF0); |
|||
r -= 16; |
|||
} |
|||
|
|||
/* Find the number of bits needed for the magnitude of the coefficient */ |
|||
int nbits = 1; /* there must be at least one 1 bit */ |
|||
while ((temp >>= 1) != 0) |
|||
nbits++; |
|||
|
|||
/* Check for out-of-range coefficient values */ |
|||
if (nbits > MAX_HUFFMAN_COEF_BITS) |
|||
m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_DCT_COEF); |
|||
|
|||
/* Count/emit Huffman symbol for run length / number of bits */ |
|||
emit_symbol(m_ac_tbl_no, (r << 4) + nbits); |
|||
|
|||
/* Emit that number of bits of the value, if positive, */ |
|||
/* or the complement of its magnitude, if negative. */ |
|||
emit_bits(temp2, nbits); |
|||
|
|||
r = 0; /* reset zero run length */ |
|||
} |
|||
|
|||
if (r > 0) |
|||
{ |
|||
/* If there are trailing zeroes, */ |
|||
m_EOBRUN++; /* count an EOB */ |
|||
if (m_EOBRUN == 0x7FFF) |
|||
emit_eobrun(); /* force it out to avoid overflow */ |
|||
} |
|||
|
|||
/* Update restart-interval state too */ |
|||
if (m_cinfo.m_restart_interval != 0) |
|||
{ |
|||
if (m_restarts_to_go == 0) |
|||
{ |
|||
m_restarts_to_go = m_cinfo.m_restart_interval; |
|||
m_next_restart_num++; |
|||
m_next_restart_num &= 7; |
|||
} |
|||
m_restarts_to_go--; |
|||
} |
|||
|
|||
return true; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// MCU encoding for DC successive approximation refinement scan.
|
|||
/// Note: we assume such scans can be multi-component, although the spec
|
|||
/// is not very clear on the point.
|
|||
/// </summary>
|
|||
private bool encode_mcu_DC_refine(JBLOCK[][] MCU_data) |
|||
{ |
|||
/* Emit restart marker if needed */ |
|||
if (m_cinfo.m_restart_interval != 0) |
|||
{ |
|||
if (m_restarts_to_go == 0) |
|||
emit_restart(m_next_restart_num); |
|||
} |
|||
|
|||
/* Encode the MCU data blocks */ |
|||
for (int blkn = 0; blkn < m_cinfo.m_blocks_in_MCU; blkn++) |
|||
{ |
|||
/* We simply emit the Al'th bit of the DC coefficient value. */ |
|||
int temp = MCU_data[blkn][0][0]; |
|||
emit_bits(temp >> m_cinfo.m_Al, 1); |
|||
} |
|||
|
|||
/* Update restart-interval state too */ |
|||
if (m_cinfo.m_restart_interval != 0) |
|||
{ |
|||
if (m_restarts_to_go == 0) |
|||
{ |
|||
m_restarts_to_go = m_cinfo.m_restart_interval; |
|||
m_next_restart_num++; |
|||
m_next_restart_num &= 7; |
|||
} |
|||
m_restarts_to_go--; |
|||
} |
|||
|
|||
return true; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// MCU encoding for AC successive approximation refinement scan.
|
|||
/// </summary>
|
|||
private bool encode_mcu_AC_refine(JBLOCK[][] MCU_data) |
|||
{ |
|||
/* Emit restart marker if needed */ |
|||
if (m_cinfo.m_restart_interval != 0) |
|||
{ |
|||
if (m_restarts_to_go == 0) |
|||
emit_restart(m_next_restart_num); |
|||
} |
|||
|
|||
/* Encode the MCU data block */ |
|||
|
|||
/* It is convenient to make a pre-pass to determine the transformed |
|||
* coefficients' absolute values and the EOB position. |
|||
*/ |
|||
int EOB = 0; |
|||
int[] absvalues = new int[JpegConstants.DCTSIZE2]; |
|||
for (int k = m_cinfo.m_Ss; k <= m_cinfo.m_Se; k++) |
|||
{ |
|||
int temp = MCU_data[0][0][JpegUtils.jpeg_natural_order[k]]; |
|||
|
|||
/* We must apply the point transform by Al. For AC coefficients this |
|||
* is an integer division with rounding towards 0. To do this portably |
|||
* in C, we shift after obtaining the absolute value. |
|||
*/ |
|||
if (temp < 0) |
|||
temp = -temp; /* temp is abs value of input */ |
|||
|
|||
temp >>= m_cinfo.m_Al; /* apply the point transform */ |
|||
absvalues[k] = temp; /* save abs value for main pass */ |
|||
|
|||
if (temp == 1) |
|||
{ |
|||
/* EOB = index of last newly-nonzero coef */ |
|||
EOB = k; |
|||
} |
|||
} |
|||
|
|||
/* Encode the AC coefficients per section G.1.2.3, fig. G.7 */ |
|||
|
|||
int r = 0; /* r = run length of zeros */ |
|||
int BR = 0; /* BR = count of buffered bits added now */ |
|||
int bitBufferOffset = m_BE; /* Append bits to buffer */ |
|||
|
|||
for (int k = m_cinfo.m_Ss; k <= m_cinfo.m_Se; k++) |
|||
{ |
|||
int temp = absvalues[k]; |
|||
if (temp == 0) |
|||
{ |
|||
r++; |
|||
continue; |
|||
} |
|||
|
|||
/* Emit any required ZRLs, but not if they can be folded into EOB */ |
|||
while (r > 15 && k <= EOB) |
|||
{ |
|||
/* emit any pending EOBRUN and the BE correction bits */ |
|||
emit_eobrun(); |
|||
|
|||
/* Emit ZRL */ |
|||
emit_symbol(m_ac_tbl_no, 0xF0); |
|||
r -= 16; |
|||
|
|||
/* Emit buffered correction bits that must be associated with ZRL */ |
|||
emit_buffered_bits(bitBufferOffset, BR); |
|||
bitBufferOffset = 0;/* BE bits are gone now */ |
|||
BR = 0; |
|||
} |
|||
|
|||
/* If the coef was previously nonzero, it only needs a correction bit. |
|||
* NOTE: a straight translation of the spec's figure G.7 would suggest |
|||
* that we also need to test r > 15. But if r > 15, we can only get here |
|||
* if k > EOB, which implies that this coefficient is not 1. |
|||
*/ |
|||
if (temp > 1) |
|||
{ |
|||
/* The correction bit is the next bit of the absolute value. */ |
|||
m_bit_buffer[bitBufferOffset + BR] = (char) (temp & 1); |
|||
BR++; |
|||
continue; |
|||
} |
|||
|
|||
/* Emit any pending EOBRUN and the BE correction bits */ |
|||
emit_eobrun(); |
|||
|
|||
/* Count/emit Huffman symbol for run length / number of bits */ |
|||
emit_symbol(m_ac_tbl_no, (r << 4) + 1); |
|||
|
|||
/* Emit output bit for newly-nonzero coef */ |
|||
temp = (MCU_data[0][0][JpegUtils.jpeg_natural_order[k]] < 0) ? 0 : 1; |
|||
emit_bits(temp, 1); |
|||
|
|||
/* Emit buffered correction bits that must be associated with this code */ |
|||
emit_buffered_bits(bitBufferOffset, BR); |
|||
bitBufferOffset = 0;/* BE bits are gone now */ |
|||
BR = 0; |
|||
r = 0; /* reset zero run length */ |
|||
} |
|||
|
|||
if (r > 0 || BR > 0) |
|||
{ |
|||
/* If there are trailing zeroes, */ |
|||
m_EOBRUN++; /* count an EOB */ |
|||
m_BE += BR; /* concat my correction bits to older ones */ |
|||
|
|||
/* We force out the EOB if we risk either: |
|||
* 1. overflow of the EOB counter; |
|||
* 2. overflow of the correction bit buffer during the next MCU. |
|||
*/ |
|||
if (m_EOBRUN == 0x7FFF || m_BE > (MAX_CORR_BITS - JpegConstants.DCTSIZE2 + 1)) |
|||
emit_eobrun(); |
|||
} |
|||
|
|||
/* Update restart-interval state too */ |
|||
if (m_cinfo.m_restart_interval != 0) |
|||
{ |
|||
if (m_restarts_to_go == 0) |
|||
{ |
|||
m_restarts_to_go = m_cinfo.m_restart_interval; |
|||
m_next_restart_num++; |
|||
m_next_restart_num &= 7; |
|||
} |
|||
m_restarts_to_go--; |
|||
} |
|||
|
|||
return true; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Finish up at the end of a Huffman-compressed progressive scan.
|
|||
/// </summary>
|
|||
private void finish_pass_phuff() |
|||
{ |
|||
/* Flush out any buffered data */ |
|||
emit_eobrun(); |
|||
flush_bits(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Finish up a statistics-gathering pass and create the new Huffman tables.
|
|||
/// </summary>
|
|||
private void finish_pass_gather_phuff() |
|||
{ |
|||
/* Flush out buffered data (all we care about is counting the EOB symbol) */ |
|||
emit_eobrun(); |
|||
|
|||
/* It's important not to apply jpeg_gen_optimal_table more than once |
|||
* per table, because it clobbers the input frequency counts! |
|||
*/ |
|||
bool[] did = new bool [JpegConstants.NUM_HUFF_TBLS]; |
|||
|
|||
bool is_DC_band = (m_cinfo.m_Ss == 0); |
|||
for (int ci = 0; ci < m_cinfo.m_comps_in_scan; ci++) |
|||
{ |
|||
jpeg_component_info componentInfo = m_cinfo.Component_info[m_cinfo.m_cur_comp_info[ci]]; |
|||
int tbl = componentInfo.Ac_tbl_no; |
|||
|
|||
if (is_DC_band) |
|||
{ |
|||
if (m_cinfo.m_Ah != 0) /* DC refinement needs no table */ |
|||
continue; |
|||
|
|||
tbl = componentInfo.Dc_tbl_no; |
|||
} |
|||
|
|||
if (!did[tbl]) |
|||
{ |
|||
JHUFF_TBL htblptr = null; |
|||
if (is_DC_band) |
|||
{ |
|||
if (m_cinfo.m_dc_huff_tbl_ptrs[tbl] == null) |
|||
m_cinfo.m_dc_huff_tbl_ptrs[tbl] = new JHUFF_TBL(); |
|||
|
|||
htblptr = m_cinfo.m_dc_huff_tbl_ptrs[tbl]; |
|||
} |
|||
else |
|||
{ |
|||
if (m_cinfo.m_ac_huff_tbl_ptrs[tbl] == null) |
|||
m_cinfo.m_ac_huff_tbl_ptrs[tbl] = new JHUFF_TBL(); |
|||
|
|||
htblptr = m_cinfo.m_ac_huff_tbl_ptrs[tbl]; |
|||
} |
|||
|
|||
jpeg_gen_optimal_table(htblptr, m_count_ptrs[tbl]); |
|||
did[tbl] = true; |
|||
} |
|||
} |
|||
} |
|||
|
|||
//////////////////////////////////////////////////////////////////////////
|
|||
// Outputting bytes to the file.
|
|||
// NB: these must be called only when actually outputting,
|
|||
// that is, entropy.gather_statistics == false.
|
|||
|
|||
// Emit a byte
|
|||
private void emit_byte(int val) |
|||
{ |
|||
m_cinfo.m_dest.emit_byte(val); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Outputting bits to the file
|
|||
///
|
|||
/// Only the right 24 bits of put_buffer are used; the valid bits are
|
|||
/// left-justified in this part. At most 16 bits can be passed to emit_bits
|
|||
/// in one call, and we never retain more than 7 bits in put_buffer
|
|||
/// between calls, so 24 bits are sufficient.
|
|||
/// </summary>
|
|||
private void emit_bits(int code, int size) |
|||
{ |
|||
// Emit some bits, unless we are in gather mode
|
|||
/* This routine is heavily used, so it's worth coding tightly. */ |
|||
int local_put_buffer = code; |
|||
|
|||
/* if size is 0, caller used an invalid Huffman table entry */ |
|||
if (size == 0) |
|||
m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_HUFF_MISSING_CODE); |
|||
|
|||
if (m_gather_statistics) |
|||
{ |
|||
/* do nothing if we're only getting stats */ |
|||
return; |
|||
} |
|||
|
|||
local_put_buffer &= (1 << size) - 1; /* mask off any extra bits in code */ |
|||
|
|||
m_put_bits += size; /* new number of bits in buffer */ |
|||
|
|||
local_put_buffer <<= 24 - m_put_bits; /* align incoming bits */ |
|||
|
|||
local_put_buffer |= m_put_buffer; /* and merge with old buffer contents */ |
|||
|
|||
while (m_put_bits >= 8) |
|||
{ |
|||
int c = (local_put_buffer >> 16) & 0xFF; |
|||
|
|||
emit_byte(c); |
|||
if (c == 0xFF) |
|||
{ |
|||
/* need to stuff a zero byte? */ |
|||
emit_byte(0); |
|||
} |
|||
local_put_buffer <<= 8; |
|||
m_put_bits -= 8; |
|||
} |
|||
|
|||
m_put_buffer = local_put_buffer; /* update variables */ |
|||
} |
|||
|
|||
private void flush_bits() |
|||
{ |
|||
emit_bits(0x7F, 7); /* fill any partial byte with ones */ |
|||
m_put_buffer = 0; /* and reset bit-buffer to empty */ |
|||
m_put_bits = 0; |
|||
} |
|||
|
|||
// Emit (or just count) a Huffman symbol.
|
|||
private void emit_symbol(int tbl_no, int symbol) |
|||
{ |
|||
if (m_gather_statistics) |
|||
m_count_ptrs[tbl_no][symbol]++; |
|||
else |
|||
emit_bits(m_derived_tbls[tbl_no].ehufco[symbol], m_derived_tbls[tbl_no].ehufsi[symbol]); |
|||
} |
|||
|
|||
// Emit bits from a correction bit buffer.
|
|||
private void emit_buffered_bits(int offset, int nbits) |
|||
{ |
|||
if (m_gather_statistics) |
|||
{ |
|||
/* no real work */ |
|||
return; |
|||
} |
|||
|
|||
for (int i = 0; i < nbits; i++) |
|||
emit_bits(m_bit_buffer[offset + i], 1); |
|||
} |
|||
|
|||
// Emit any pending EOBRUN symbol.
|
|||
private void emit_eobrun() |
|||
{ |
|||
if (m_EOBRUN > 0) |
|||
{ |
|||
/* if there is any pending EOBRUN */ |
|||
int temp = m_EOBRUN; |
|||
int nbits = 0; |
|||
while ((temp >>= 1) != 0) |
|||
nbits++; |
|||
|
|||
/* safety check: shouldn't happen given limited correction-bit buffer */ |
|||
if (nbits > 14) |
|||
m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_HUFF_MISSING_CODE); |
|||
|
|||
emit_symbol(m_ac_tbl_no, nbits << 4); |
|||
if (nbits != 0) |
|||
emit_bits(m_EOBRUN, nbits); |
|||
|
|||
m_EOBRUN = 0; |
|||
|
|||
/* Emit any buffered correction bits */ |
|||
emit_buffered_bits(0, m_BE); |
|||
m_BE = 0; |
|||
} |
|||
} |
|||
|
|||
// Emit a restart marker & resynchronize predictions.
|
|||
private void emit_restart(int restart_num) |
|||
{ |
|||
emit_eobrun(); |
|||
|
|||
if (!m_gather_statistics) |
|||
{ |
|||
flush_bits(); |
|||
emit_byte(0xFF); |
|||
emit_byte((int)(JPEG_MARKER.RST0 + restart_num)); |
|||
} |
|||
|
|||
if (m_cinfo.m_Ss == 0) |
|||
{ |
|||
/* Re-initialize DC predictions to 0 */ |
|||
for (int ci = 0; ci < m_cinfo.m_comps_in_scan; ci++) |
|||
m_last_dc_val[ci] = 0; |
|||
} |
|||
else |
|||
{ |
|||
/* Re-initialize all AC-related fields to 0 */ |
|||
m_EOBRUN = 0; |
|||
m_BE = 0; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// IRIGHT_SHIFT is like RIGHT_SHIFT, but works on int rather than int.
|
|||
/// We assume that int right shift is unsigned if int right shift is,
|
|||
/// which should be safe.
|
|||
/// </summary>
|
|||
private static int IRIGHT_SHIFT(int x, int shft) |
|||
{ |
|||
return (x >> shft); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,43 @@ |
|||
/* Copyright (C) 2008-2011, Bit Miracle |
|||
* http://www.bitmiracle.com
|
|||
* |
|||
* Copyright (C) 1994-1996, Thomas G. Lane. |
|||
* This file is part of the Independent JPEG Group's software. |
|||
* For conditions of distribution and use, see the accompanying README file. |
|||
* |
|||
*/ |
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
|
|||
namespace BitMiracle.LibJpeg.Classic |
|||
{ |
|||
/// <summary>
|
|||
/// One block of coefficients.
|
|||
/// </summary>
|
|||
#if EXPOSE_LIBJPEG
|
|||
public |
|||
#endif
|
|||
class JBLOCK |
|||
{ |
|||
internal short[] data = new short[JpegConstants.DCTSIZE2]; |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the element at the specified index.
|
|||
/// </summary>
|
|||
/// <param name="index">The index of required element.</param>
|
|||
/// <value>The required element.</value>
|
|||
public short this[int index] |
|||
{ |
|||
get |
|||
{ |
|||
return data[index]; |
|||
} |
|||
set |
|||
{ |
|||
data[index] = value; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,65 @@ |
|||
/* Copyright (C) 2008-2011, Bit Miracle |
|||
* http://www.bitmiracle.com
|
|||
* |
|||
* Copyright (C) 1994-1996, Thomas G. Lane. |
|||
* This file is part of the Independent JPEG Group's software. |
|||
* For conditions of distribution and use, see the accompanying README file. |
|||
* |
|||
*/ |
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
|
|||
namespace BitMiracle.LibJpeg.Classic |
|||
{ |
|||
/// <summary>
|
|||
/// Huffman coding table.
|
|||
/// </summary>
|
|||
#if EXPOSE_LIBJPEG
|
|||
public |
|||
#endif
|
|||
class JHUFF_TBL |
|||
{ |
|||
/* These two fields directly represent the contents of a JPEG DHT marker */ |
|||
private readonly byte[] m_bits = new byte[17]; /* bits[k] = # of symbols with codes of */ |
|||
|
|||
/* length k bits; bits[0] is unused */ |
|||
private readonly byte[] m_huffval = new byte[256]; /* The symbols, in order of incr code length */ |
|||
|
|||
private bool m_sent_table; /* true when table has been output */ |
|||
|
|||
|
|||
internal JHUFF_TBL() |
|||
{ |
|||
} |
|||
|
|||
internal byte[] Bits |
|||
{ |
|||
get { return m_bits; } |
|||
} |
|||
|
|||
internal byte[] Huffval |
|||
{ |
|||
get { return m_huffval; } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets a value indicating whether the table has been output to file.
|
|||
/// </summary>
|
|||
/// <value>It's initialized <c>false</c> when the table is created, and set
|
|||
/// <c>true</c> when it's been output to the file. You could suppress output
|
|||
/// of a table by setting this to <c>true</c>.
|
|||
/// </value>
|
|||
/// <remarks>This property is used only during compression. It's initialized
|
|||
/// <c>false</c> when the table is created, and set <c>true</c> when it's been
|
|||
/// output to the file. You could suppress output of a table by setting this to
|
|||
/// <c>true</c>. (See jpeg_suppress_tables for an example.)</remarks>
|
|||
/// <seealso cref="jpeg_compress_struct.jpeg_suppress_tables"/>
|
|||
public bool Sent_table |
|||
{ |
|||
get { return m_sent_table; } |
|||
set { m_sent_table = value; } |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,238 @@ |
|||
/* Copyright (C) 2008-2011, Bit Miracle |
|||
* http://www.bitmiracle.com
|
|||
* |
|||
* Copyright (C) 1994-1996, Thomas G. Lane. |
|||
* This file is part of the Independent JPEG Group's software. |
|||
* For conditions of distribution and use, see the accompanying README file. |
|||
* |
|||
*/ |
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
|
|||
namespace BitMiracle.LibJpeg.Classic |
|||
{ |
|||
/// <summary>
|
|||
/// JPEG marker codes.
|
|||
/// </summary>
|
|||
/// <seealso href="81c88818-a5d7-4550-9ce5-024a768f7b1e.htm" target="_self">Special markers</seealso>
|
|||
#if EXPOSE_LIBJPEG
|
|||
public |
|||
#endif
|
|||
enum JPEG_MARKER |
|||
{ |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
SOF0 = 0xc0, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
SOF1 = 0xc1, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
SOF2 = 0xc2, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
SOF3 = 0xc3, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
SOF5 = 0xc5, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
SOF6 = 0xc6, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
SOF7 = 0xc7, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JPG = 0xc8, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
SOF9 = 0xc9, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
SOF10 = 0xca, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
SOF11 = 0xcb, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
SOF13 = 0xcd, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
SOF14 = 0xce, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
SOF15 = 0xcf, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
DHT = 0xc4, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
DAC = 0xcc, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
RST0 = 0xd0, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
RST1 = 0xd1, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
RST2 = 0xd2, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
RST3 = 0xd3, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
RST4 = 0xd4, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
RST5 = 0xd5, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
RST6 = 0xd6, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
RST7 = 0xd7, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
SOI = 0xd8, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
EOI = 0xd9, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
SOS = 0xda, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
DQT = 0xdb, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
DNL = 0xdc, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
DRI = 0xdd, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
DHP = 0xde, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
EXP = 0xdf, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
APP0 = 0xe0, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
APP1 = 0xe1, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
APP2 = 0xe2, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
APP3 = 0xe3, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
APP4 = 0xe4, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
APP5 = 0xe5, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
APP6 = 0xe6, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
APP7 = 0xe7, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
APP8 = 0xe8, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
APP9 = 0xe9, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
APP10 = 0xea, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
APP11 = 0xeb, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
APP12 = 0xec, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
APP13 = 0xed, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
APP14 = 0xee, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
APP15 = 0xef, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JPG0 = 0xf0, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JPG13 = 0xfd, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
COM = 0xfe, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
TEM = 0x01, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
ERROR = 0x100 |
|||
} |
|||
} |
|||
@ -0,0 +1,55 @@ |
|||
/* Copyright (C) 2008-2011, Bit Miracle |
|||
* http://www.bitmiracle.com
|
|||
* |
|||
* Copyright (C) 1994-1996, Thomas G. Lane. |
|||
* This file is part of the Independent JPEG Group's software. |
|||
* For conditions of distribution and use, see the accompanying README file. |
|||
* |
|||
*/ |
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
|
|||
namespace BitMiracle.LibJpeg.Classic |
|||
{ |
|||
/// <summary>
|
|||
/// DCT coefficient quantization tables.
|
|||
/// </summary>
|
|||
#if EXPOSE_LIBJPEG
|
|||
public |
|||
#endif
|
|||
class JQUANT_TBL |
|||
{ |
|||
/* This field is used only during compression. It's initialized false when |
|||
* the table is created, and set true when it's been output to the file. |
|||
* You could suppress output of a table by setting this to true. |
|||
* (See jpeg_suppress_tables for an example.) |
|||
*/ |
|||
private bool m_sent_table; /* true when table has been output */ |
|||
|
|||
/* This array gives the coefficient quantizers in natural array order |
|||
* (not the zigzag order in which they are stored in a JPEG DQT marker). |
|||
* CAUTION: IJG versions prior to v6a kept this array in zigzag order. |
|||
*/ |
|||
internal readonly short[] quantval = new short[JpegConstants.DCTSIZE2]; /* quantization step for each coefficient */ |
|||
|
|||
internal JQUANT_TBL() |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets a value indicating whether the table has been output to file.
|
|||
/// </summary>
|
|||
/// <value>It's initialized <c>false</c> when the table is created, and set
|
|||
/// <c>true</c> when it's been output to the file. You could suppress output of a table by setting this to <c>true</c>.
|
|||
/// </value>
|
|||
/// <remarks>This property is used only during compression.</remarks>
|
|||
/// <seealso cref="jpeg_compress_struct.jpeg_suppress_tables"/>
|
|||
public bool Sent_table |
|||
{ |
|||
get { return m_sent_table; } |
|||
set { m_sent_table = value; } |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,55 @@ |
|||
/* Copyright (C) 2008-2011, Bit Miracle |
|||
* http://www.bitmiracle.com
|
|||
* |
|||
* Copyright (C) 1994-1996, Thomas G. Lane. |
|||
* This file is part of the Independent JPEG Group's software. |
|||
* For conditions of distribution and use, see the accompanying README file. |
|||
* |
|||
*/ |
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
|
|||
namespace BitMiracle.LibJpeg.Classic |
|||
{ |
|||
/// <summary>
|
|||
/// Known color spaces.
|
|||
/// </summary>
|
|||
/// <seealso href="c90654b9-f3f4-4319-80d1-979c73d84e76.htm" target="_self">Special color spaces</seealso>
|
|||
#if EXPOSE_LIBJPEG
|
|||
public |
|||
#endif
|
|||
enum J_COLOR_SPACE |
|||
{ |
|||
/// <summary>
|
|||
/// Unspecified color space.
|
|||
/// </summary>
|
|||
JCS_UNKNOWN, |
|||
|
|||
/// <summary>
|
|||
/// Grayscale
|
|||
/// </summary>
|
|||
JCS_GRAYSCALE, |
|||
|
|||
/// <summary>
|
|||
/// RGB
|
|||
/// </summary>
|
|||
JCS_RGB, |
|||
|
|||
/// <summary>
|
|||
/// YCbCr (also known as YUV)
|
|||
/// </summary>
|
|||
JCS_YCbCr, |
|||
|
|||
/// <summary>
|
|||
/// CMYK
|
|||
/// </summary>
|
|||
JCS_CMYK, |
|||
|
|||
/// <summary>
|
|||
/// YCbCrK
|
|||
/// </summary>
|
|||
JCS_YCCK |
|||
} |
|||
} |
|||
@ -0,0 +1,47 @@ |
|||
/* Copyright (C) 2008-2011, Bit Miracle |
|||
* http://www.bitmiracle.com
|
|||
* |
|||
* Copyright (C) 1994-1996, Thomas G. Lane. |
|||
* This file is part of the Independent JPEG Group's software. |
|||
* For conditions of distribution and use, see the accompanying README file. |
|||
* |
|||
*/ |
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
|
|||
namespace BitMiracle.LibJpeg.Classic |
|||
{ |
|||
/// <summary>
|
|||
/// Algorithm used for the DCT step.
|
|||
/// </summary>
|
|||
/// <remarks>The <c>FLOAT</c> method is very slightly more accurate than the <c>ISLOW</c> method,
|
|||
/// but may give different results on different machines due to varying roundoff behavior.
|
|||
/// The integer methods should give the same results on all machines. On machines with
|
|||
/// sufficiently fast hardware, the floating-point method may also be the fastest.
|
|||
/// The <c>IFAST</c> method is considerably less accurate than the other two; its use is not recommended
|
|||
/// if high quality is a concern.</remarks>
|
|||
/// <seealso cref="jpeg_compress_struct.Dct_method"/>
|
|||
/// <seealso cref="jpeg_decompress_struct.Dct_method"/>
|
|||
#if EXPOSE_LIBJPEG
|
|||
public |
|||
#endif
|
|||
enum J_DCT_METHOD |
|||
{ |
|||
/// <summary>
|
|||
/// Slow but accurate integer algorithm.
|
|||
/// </summary>
|
|||
JDCT_ISLOW, |
|||
|
|||
/// <summary>
|
|||
/// Faster, less accurate integer method.
|
|||
/// </summary>
|
|||
JDCT_IFAST, |
|||
|
|||
/// <summary>
|
|||
/// Floating-point method.
|
|||
/// </summary>
|
|||
JDCT_FLOAT |
|||
} |
|||
} |
|||
@ -0,0 +1,40 @@ |
|||
/* Copyright (C) 2008-2011, Bit Miracle |
|||
* http://www.bitmiracle.com
|
|||
* |
|||
* Copyright (C) 1994-1996, Thomas G. Lane. |
|||
* This file is part of the Independent JPEG Group's software. |
|||
* For conditions of distribution and use, see the accompanying README file. |
|||
* |
|||
*/ |
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
|
|||
namespace BitMiracle.LibJpeg.Classic |
|||
{ |
|||
/// <summary>
|
|||
/// Dithering options for decompression.
|
|||
/// </summary>
|
|||
/// <seealso cref="jpeg_decompress_struct.Dither_mode"/>
|
|||
#if EXPOSE_LIBJPEG
|
|||
public |
|||
#endif
|
|||
enum J_DITHER_MODE |
|||
{ |
|||
/// <summary>
|
|||
/// No dithering: fast, very low quality
|
|||
/// </summary>
|
|||
JDITHER_NONE, |
|||
|
|||
/// <summary>
|
|||
/// Ordered dither: moderate speed and quality
|
|||
/// </summary>
|
|||
JDITHER_ORDERED, |
|||
|
|||
/// <summary>
|
|||
/// Floyd-Steinberg dither: slow, high quality
|
|||
/// </summary>
|
|||
JDITHER_FS |
|||
} |
|||
} |
|||
@ -0,0 +1,427 @@ |
|||
/* Copyright (C) 2008-2011, Bit Miracle |
|||
* http://www.bitmiracle.com
|
|||
* |
|||
* Copyright (C) 1994-1996, Thomas G. Lane. |
|||
* This file is part of the Independent JPEG Group's software. |
|||
* For conditions of distribution and use, see the accompanying README file. |
|||
* |
|||
*/ |
|||
|
|||
/* |
|||
* This file defines the error and message codes for the JPEG library. |
|||
*/ |
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
|
|||
namespace BitMiracle.LibJpeg.Classic |
|||
{ |
|||
/// <summary>
|
|||
/// Message codes used in code to signal errors, warning and trace messages.
|
|||
/// </summary>
|
|||
/// <seealso cref="jpeg_error_mgr"/>
|
|||
#if EXPOSE_LIBJPEG
|
|||
public |
|||
#endif
|
|||
enum J_MESSAGE_CODE |
|||
{ |
|||
/// <summary>
|
|||
/// Must be first entry!
|
|||
/// </summary>
|
|||
JMSG_NOMESSAGE, |
|||
|
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JERR_ARITH_NOTIMPL, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JERR_BAD_BUFFER_MODE, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JERR_BAD_COMPONENT_ID, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JERR_BAD_DCT_COEF, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JERR_BAD_DCTSIZE, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JERR_BAD_HUFF_TABLE, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JERR_BAD_IN_COLORSPACE, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JERR_BAD_J_COLORSPACE, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JERR_BAD_LENGTH, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JERR_BAD_MCU_SIZE, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JERR_BAD_PRECISION, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JERR_BAD_PROGRESSION, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JERR_BAD_PROG_SCRIPT, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JERR_BAD_SAMPLING, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JERR_BAD_SCAN_SCRIPT, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JERR_BAD_STATE, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JERR_BAD_VIRTUAL_ACCESS, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JERR_BUFFER_SIZE, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JERR_CANT_SUSPEND, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JERR_CCIR601_NOTIMPL, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JERR_COMPONENT_COUNT, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JERR_CONVERSION_NOTIMPL, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JERR_DHT_INDEX, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JERR_DQT_INDEX, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JERR_EMPTY_IMAGE, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JERR_EOI_EXPECTED, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JERR_FILE_WRITE, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JERR_FRACT_SAMPLE_NOTIMPL, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JERR_HUFF_CLEN_OVERFLOW, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JERR_HUFF_MISSING_CODE, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JERR_IMAGE_TOO_BIG, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JERR_INPUT_EMPTY, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JERR_INPUT_EOF, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JERR_MISMATCHED_QUANT_TABLE, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JERR_MISSING_DATA, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JERR_MODE_CHANGE, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JERR_NOTIMPL, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JERR_NOT_COMPILED, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JERR_NO_HUFF_TABLE, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JERR_NO_IMAGE, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JERR_NO_QUANT_TABLE, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JERR_NO_SOI, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JERR_OUT_OF_MEMORY, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JERR_QUANT_COMPONENTS, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JERR_QUANT_FEW_COLORS, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JERR_QUANT_MANY_COLORS, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JERR_SOF_DUPLICATE, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JERR_SOF_NO_SOS, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JERR_SOF_UNSUPPORTED, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JERR_SOI_DUPLICATE, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JERR_SOS_NO_SOF, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JERR_TOO_LITTLE_DATA, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JERR_UNKNOWN_MARKER, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JERR_WIDTH_OVERFLOW, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JTRC_16BIT_TABLES, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JTRC_ADOBE, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JTRC_APP0, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JTRC_APP14, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JTRC_DHT, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JTRC_DQT, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JTRC_DRI, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JTRC_EOI, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JTRC_HUFFBITS, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JTRC_JFIF, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JTRC_JFIF_BADTHUMBNAILSIZE, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JTRC_JFIF_EXTENSION, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JTRC_JFIF_THUMBNAIL, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JTRC_MISC_MARKER, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JTRC_PARMLESS_MARKER, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JTRC_QUANTVALS, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JTRC_QUANT_3_NCOLORS, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JTRC_QUANT_NCOLORS, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JTRC_QUANT_SELECTED, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JTRC_RECOVERY_ACTION, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JTRC_RST, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JTRC_SMOOTH_NOTIMPL, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JTRC_SOF, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JTRC_SOF_COMPONENT, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JTRC_SOI, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JTRC_SOS, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JTRC_SOS_COMPONENT, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JTRC_SOS_PARAMS, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JTRC_THUMB_JPEG, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JTRC_THUMB_PALETTE, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JTRC_THUMB_RGB, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JTRC_UNKNOWN_IDS, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JWRN_ADOBE_XFORM, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JWRN_BOGUS_PROGRESSION, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JWRN_EXTRANEOUS_DATA, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JWRN_HIT_MARKER, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JWRN_HUFF_BAD_CODE, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JWRN_JFIF_MAJOR, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JWRN_JPEG_EOF, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JWRN_MUST_RESYNC, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JWRN_NOT_SEQUENTIAL, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JWRN_TOO_MUCH_DATA, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JMSG_UNKNOWNMSGCODE, |
|||
/// <summary>
|
|||
///
|
|||
/// </summary>
|
|||
JMSG_LASTMSGCODE |
|||
} |
|||
} |
|||
@ -0,0 +1,166 @@ |
|||
/* Copyright (C) 2008-2011, Bit Miracle |
|||
* http://www.bitmiracle.com
|
|||
* |
|||
* Copyright (C) 1994-1996, Thomas G. Lane. |
|||
* This file is part of the Independent JPEG Group's software. |
|||
* For conditions of distribution and use, see the accompanying README file. |
|||
* |
|||
*/ |
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
|
|||
namespace BitMiracle.LibJpeg.Classic |
|||
{ |
|||
/// <summary>
|
|||
/// Defines some JPEG constants.
|
|||
/// </summary>
|
|||
#if EXPOSE_LIBJPEG
|
|||
public |
|||
#endif
|
|||
static class JpegConstants |
|||
{ |
|||
//////////////////////////////////////////////////////////////////////////
|
|||
// All of these are specified by the JPEG standard, so don't change them
|
|||
// if you want to be compatible.
|
|||
//
|
|||
|
|||
/// <summary>
|
|||
/// The basic DCT block is 8x8 samples
|
|||
/// </summary>
|
|||
public const int DCTSIZE = 8; |
|||
|
|||
/// <summary>
|
|||
/// DCTSIZE squared; the number of elements in a block.
|
|||
/// </summary>
|
|||
public const int DCTSIZE2 = DCTSIZE * DCTSIZE; |
|||
|
|||
/// <summary>
|
|||
/// Quantization tables are numbered 0..3
|
|||
/// </summary>
|
|||
public const int NUM_QUANT_TBLS = 4; |
|||
|
|||
/// <summary>
|
|||
/// Huffman tables are numbered 0..3
|
|||
/// </summary>
|
|||
public const int NUM_HUFF_TBLS = 4; |
|||
|
|||
/// <summary>
|
|||
/// JPEG limit on the number of components in one scan.
|
|||
/// </summary>
|
|||
public const int MAX_COMPS_IN_SCAN = 4; |
|||
|
|||
// compressor's limit on blocks per MCU
|
|||
//
|
|||
// Unfortunately, some bozo at Adobe saw no reason to be bound by the standard;
|
|||
// the PostScript DCT filter can emit files with many more than 10 blocks/MCU.
|
|||
// If you happen to run across such a file, you can up D_MAX_BLOCKS_IN_MCU
|
|||
// to handle it. We even let you do this from the jconfig.h file. However,
|
|||
// we strongly discourage changing C_MAX_BLOCKS_IN_MCU; just because Adobe
|
|||
// sometimes emits noncompliant files doesn't mean you should too.
|
|||
|
|||
/// <summary>
|
|||
/// Compressor's limit on blocks per MCU.
|
|||
/// </summary>
|
|||
public const int C_MAX_BLOCKS_IN_MCU = 10; |
|||
|
|||
/// <summary>
|
|||
/// Decompressor's limit on blocks per MCU.
|
|||
/// </summary>
|
|||
public const int D_MAX_BLOCKS_IN_MCU = 10; |
|||
|
|||
/// <summary>
|
|||
/// JPEG limit on sampling factors.
|
|||
/// </summary>
|
|||
public const int MAX_SAMP_FACTOR = 4; |
|||
|
|||
|
|||
//////////////////////////////////////////////////////////////////////////
|
|||
// implementation-specific constants
|
|||
//
|
|||
|
|||
// Maximum number of components (color channels) allowed in JPEG image.
|
|||
// To meet the letter of the JPEG spec, set this to 255. However, darn
|
|||
// few applications need more than 4 channels (maybe 5 for CMYK + alpha
|
|||
// mask). We recommend 10 as a reasonable compromise; use 4 if you are
|
|||
// really short on memory. (Each allowed component costs a hundred or so
|
|||
// bytes of storage, whether actually used in an image or not.)
|
|||
|
|||
/// <summary>
|
|||
/// Maximum number of color channels allowed in JPEG image.
|
|||
/// </summary>
|
|||
public const int MAX_COMPONENTS = 10; |
|||
|
|||
|
|||
|
|||
/// <summary>
|
|||
/// The size of sample.
|
|||
/// </summary>
|
|||
/// <remarks>Are either:
|
|||
/// 8 - for 8-bit sample values (the usual setting)<br/>
|
|||
/// 12 - for 12-bit sample values (not supported by this version)<br/>
|
|||
/// Only 8 and 12 are legal data precisions for lossy JPEG according to the JPEG standard.
|
|||
/// Althought original IJG code claims it supports 12 bit images, our code does not support
|
|||
/// anything except 8-bit images.</remarks>
|
|||
public const int BITS_IN_JSAMPLE = 8; |
|||
|
|||
/// <summary>
|
|||
/// DCT method used by default.
|
|||
/// </summary>
|
|||
public static J_DCT_METHOD JDCT_DEFAULT = J_DCT_METHOD.JDCT_ISLOW; |
|||
|
|||
/// <summary>
|
|||
/// Fastest DCT method.
|
|||
/// </summary>
|
|||
public static J_DCT_METHOD JDCT_FASTEST = J_DCT_METHOD.JDCT_IFAST; |
|||
|
|||
/// <summary>
|
|||
/// A tad under 64K to prevent overflows.
|
|||
/// </summary>
|
|||
public const int JPEG_MAX_DIMENSION = 65500; |
|||
|
|||
/// <summary>
|
|||
/// The maximum sample value.
|
|||
/// </summary>
|
|||
public const int MAXJSAMPLE = 255; |
|||
|
|||
/// <summary>
|
|||
/// The medium sample value.
|
|||
/// </summary>
|
|||
public const int CENTERJSAMPLE = 128; |
|||
|
|||
// Ordering of RGB data in scanlines passed to or from the application.
|
|||
// RESTRICTIONS:
|
|||
// 1. These macros only affect RGB<=>YCbCr color conversion, so they are not
|
|||
// useful if you are using JPEG color spaces other than YCbCr or grayscale.
|
|||
// 2. The color quantizer modules will not behave desirably if RGB_PIXELSIZE
|
|||
// is not 3 (they don't understand about dummy color components!). So you
|
|||
// can't use color quantization if you change that value.
|
|||
|
|||
/// <summary>
|
|||
/// Offset of Red in an RGB scanline element.
|
|||
/// </summary>
|
|||
public const int RGB_RED = 0; |
|||
|
|||
/// <summary>
|
|||
/// Offset of Green in an RGB scanline element.
|
|||
/// </summary>
|
|||
public const int RGB_GREEN = 1; |
|||
|
|||
/// <summary>
|
|||
/// Offset of Blue in an RGB scanline element.
|
|||
/// </summary>
|
|||
public const int RGB_BLUE = 2; |
|||
|
|||
/// <summary>
|
|||
/// Bytes per RGB scanline element.
|
|||
/// </summary>
|
|||
public const int RGB_PIXELSIZE = 3; |
|||
|
|||
/// <summary>
|
|||
/// The number of bits of lookahead.
|
|||
/// </summary>
|
|||
public const int HUFF_LOOKAHEAD = 8; |
|||
} |
|||
} |
|||
@ -0,0 +1,49 @@ |
|||
/* Copyright (C) 2008-2011, Bit Miracle |
|||
* http://www.bitmiracle.com
|
|||
*/ |
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
|
|||
namespace BitMiracle.LibJpeg.Classic |
|||
{ |
|||
/// <summary>
|
|||
/// Describes a result of read operation.
|
|||
/// </summary>
|
|||
/// <seealso cref="jpeg_decompress_struct.jpeg_consume_input"/>
|
|||
#if EXPOSE_LIBJPEG
|
|||
public |
|||
#endif
|
|||
enum ReadResult |
|||
{ |
|||
/// <summary>
|
|||
/// Suspended due to lack of input data. Can occur only if a suspending data source is used.
|
|||
/// </summary>
|
|||
JPEG_SUSPENDED = 0, |
|||
/// <summary>
|
|||
/// Found valid image datastream.
|
|||
/// </summary>
|
|||
JPEG_HEADER_OK = 1, |
|||
/// <summary>
|
|||
/// Found valid table-specs-only datastream.
|
|||
/// </summary>
|
|||
JPEG_HEADER_TABLES_ONLY = 2, |
|||
/// <summary>
|
|||
/// Reached a SOS marker (the start of a new scan)
|
|||
/// </summary>
|
|||
JPEG_REACHED_SOS = 3, |
|||
/// <summary>
|
|||
/// Reached the EOI marker (end of image)
|
|||
/// </summary>
|
|||
JPEG_REACHED_EOI = 4, |
|||
/// <summary>
|
|||
/// Completed reading one MCU row of compressed data.
|
|||
/// </summary>
|
|||
JPEG_ROW_COMPLETED = 5, |
|||
/// <summary>
|
|||
/// Completed reading last MCU row of current scan.
|
|||
/// </summary>
|
|||
JPEG_SCAN_COMPLETED = 6 |
|||
} |
|||
} |
|||
@ -0,0 +1,364 @@ |
|||
/* Copyright (C) 2008-2011, Bit Miracle |
|||
* http://www.bitmiracle.com
|
|||
* |
|||
* Copyright (C) 1994-1996, Thomas G. Lane. |
|||
* This file is part of the Independent JPEG Group's software. |
|||
* For conditions of distribution and use, see the accompanying README file. |
|||
* |
|||
*/ |
|||
|
|||
/* |
|||
* This file contains application interface routines that are used for both |
|||
* compression and decompression. |
|||
*/ |
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
using System.Reflection; |
|||
using System.Globalization; |
|||
|
|||
namespace BitMiracle.LibJpeg.Classic |
|||
{ |
|||
/// <summary>Base class for both JPEG compressor and decompresor.</summary>
|
|||
/// <remarks>
|
|||
/// Routines that are to be used by both halves of the library are declared
|
|||
/// to receive an instance of this class. There are no actual instances of
|
|||
/// <see cref="jpeg_common_struct"/>, only of <see cref="jpeg_compress_struct"/>
|
|||
/// and <see cref="jpeg_decompress_struct"/>
|
|||
/// </remarks>
|
|||
#if EXPOSE_LIBJPEG
|
|||
public |
|||
#endif
|
|||
abstract class jpeg_common_struct |
|||
{ |
|||
internal enum JpegState |
|||
{ |
|||
DESTROYED = 0, |
|||
CSTATE_START = 100, /* after create_compress */ |
|||
CSTATE_SCANNING = 101, /* start_compress done, write_scanlines OK */ |
|||
CSTATE_RAW_OK = 102, /* start_compress done, write_raw_data OK */ |
|||
CSTATE_WRCOEFS = 103, /* jpeg_write_coefficients done */ |
|||
DSTATE_START = 200, /* after create_decompress */ |
|||
DSTATE_INHEADER = 201, /* reading header markers, no SOS yet */ |
|||
DSTATE_READY = 202, /* found SOS, ready for start_decompress */ |
|||
DSTATE_PRELOAD = 203, /* reading multiscan file in start_decompress*/ |
|||
DSTATE_PRESCAN = 204, /* performing dummy pass for 2-pass quant */ |
|||
DSTATE_SCANNING = 205, /* start_decompress done, read_scanlines OK */ |
|||
DSTATE_RAW_OK = 206, /* start_decompress done, read_raw_data OK */ |
|||
DSTATE_BUFIMAGE = 207, /* expecting jpeg_start_output */ |
|||
DSTATE_BUFPOST = 208, /* looking for SOS/EOI in jpeg_finish_output */ |
|||
DSTATE_RDCOEFS = 209, /* reading file in jpeg_read_coefficients */ |
|||
DSTATE_STOPPING = 210 /* looking for EOI in jpeg_finish_decompress */ |
|||
} |
|||
|
|||
// Error handler module
|
|||
internal jpeg_error_mgr m_err; |
|||
|
|||
// Progress monitor, or null if none
|
|||
internal jpeg_progress_mgr m_progress; |
|||
|
|||
internal JpegState m_global_state; /* For checking call sequence validity */ |
|||
|
|||
/// <summary>
|
|||
/// Base constructor.
|
|||
/// </summary>
|
|||
/// <seealso cref="jpeg_compress_struct"/>
|
|||
/// <seealso cref="jpeg_decompress_struct"/>
|
|||
public jpeg_common_struct() : this(new jpeg_error_mgr()) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Base constructor.
|
|||
/// </summary>
|
|||
/// <param name="errorManager">The error manager.</param>
|
|||
/// <seealso cref="jpeg_compress_struct"/>
|
|||
/// <seealso cref="jpeg_decompress_struct"/>
|
|||
public jpeg_common_struct(jpeg_error_mgr errorManager) |
|||
{ |
|||
Err = errorManager; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets a value indicating whether this instance is Jpeg decompressor.
|
|||
/// </summary>
|
|||
/// <value>
|
|||
/// <c>true</c> if this is Jpeg decompressor; otherwise, <c>false</c>.
|
|||
/// </value>
|
|||
public abstract bool IsDecompressor |
|||
{ |
|||
get; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Progress monitor.
|
|||
/// </summary>
|
|||
/// <value>The progress manager.</value>
|
|||
/// <remarks>Default value: <c>null</c>.</remarks>
|
|||
public jpeg_progress_mgr Progress |
|||
{ |
|||
get |
|||
{ |
|||
return m_progress; |
|||
} |
|||
set |
|||
{ |
|||
if (value == null) |
|||
throw new ArgumentNullException("value"); |
|||
|
|||
m_progress = value; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Error handler module.
|
|||
/// </summary>
|
|||
/// <value>The error manager.</value>
|
|||
/// <seealso href="41dc1a3b-0dea-4594-87d2-c213ab1049e1.htm" target="_self">Error handling</seealso>
|
|||
public jpeg_error_mgr Err |
|||
{ |
|||
get |
|||
{ |
|||
return m_err; |
|||
} |
|||
set |
|||
{ |
|||
if (value == null) |
|||
throw new ArgumentNullException("value"); |
|||
|
|||
m_err = value; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the version of LibJpeg.
|
|||
/// </summary>
|
|||
/// <value>The version of LibJpeg.</value>
|
|||
public static string Version |
|||
{ |
|||
get |
|||
{ |
|||
Version version = typeof(jpeg_common_struct).GetTypeInfo().Assembly.GetName().Version; |
|||
string versionString = version.Major.ToString(CultureInfo.InvariantCulture) + |
|||
"." + version.Minor.ToString(CultureInfo.InvariantCulture); |
|||
|
|||
versionString += "." + version.Build.ToString(CultureInfo.InvariantCulture); |
|||
versionString += "." + version.Revision.ToString(CultureInfo.InvariantCulture); |
|||
|
|||
return versionString; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the LibJpeg's copyright.
|
|||
/// </summary>
|
|||
/// <value>The copyright.</value>
|
|||
public static string Copyright |
|||
{ |
|||
get |
|||
{ |
|||
return "Copyright (C) 2008-2011, Bit Miracle"; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Creates the array of samples.
|
|||
/// </summary>
|
|||
/// <param name="samplesPerRow">The number of samples in row.</param>
|
|||
/// <param name="numberOfRows">The number of rows.</param>
|
|||
/// <returns>The array of samples.</returns>
|
|||
public static jvirt_array<byte> CreateSamplesArray(int samplesPerRow, int numberOfRows) |
|||
{ |
|||
return new jvirt_array<byte>(samplesPerRow, numberOfRows, AllocJpegSamples); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Creates the array of blocks.
|
|||
/// </summary>
|
|||
/// <param name="blocksPerRow">The number of blocks in row.</param>
|
|||
/// <param name="numberOfRows">The number of rows.</param>
|
|||
/// <returns>The array of blocks.</returns>
|
|||
/// <seealso cref="JBLOCK"/>
|
|||
public static jvirt_array<JBLOCK> CreateBlocksArray(int blocksPerRow, int numberOfRows) |
|||
{ |
|||
return new jvirt_array<JBLOCK>(blocksPerRow, numberOfRows, allocJpegBlocks); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Creates 2-D sample array.
|
|||
/// </summary>
|
|||
/// <param name="samplesPerRow">The number of samples per row.</param>
|
|||
/// <param name="numberOfRows">The number of rows.</param>
|
|||
/// <returns>The array of samples.</returns>
|
|||
public static byte[][] AllocJpegSamples(int samplesPerRow, int numberOfRows) |
|||
{ |
|||
byte[][] result = new byte[numberOfRows][]; |
|||
for (int i = 0; i < numberOfRows; ++i) |
|||
result[i] = new byte[samplesPerRow]; |
|||
|
|||
return result; |
|||
} |
|||
|
|||
// Creation of 2-D block arrays.
|
|||
private static JBLOCK[][] allocJpegBlocks(int blocksPerRow, int numberOfRows) |
|||
{ |
|||
JBLOCK[][] result = new JBLOCK[numberOfRows][]; |
|||
for (int i = 0; i < numberOfRows; ++i) |
|||
{ |
|||
result[i] = new JBLOCK[blocksPerRow]; |
|||
for (int j = 0; j < blocksPerRow; ++j) |
|||
result[i][j] = new JBLOCK(); |
|||
} |
|||
return result; |
|||
} |
|||
|
|||
// Generic versions of jpeg_abort and jpeg_destroy that work on either
|
|||
// flavor of JPEG object. These may be more convenient in some places.
|
|||
|
|||
/// <summary>
|
|||
/// Abort processing of a JPEG compression or decompression operation,
|
|||
/// but don't destroy the object itself.
|
|||
///
|
|||
/// Closing a data source or destination, if necessary, is the
|
|||
/// application's responsibility.
|
|||
/// </summary>
|
|||
public void jpeg_abort() |
|||
{ |
|||
/* Reset overall state for possible reuse of object */ |
|||
if (IsDecompressor) |
|||
{ |
|||
m_global_state = JpegState.DSTATE_START; |
|||
|
|||
/* Try to keep application from accessing now-deleted marker list. |
|||
* A bit kludgy to do it here, but this is the most central place. |
|||
*/ |
|||
jpeg_decompress_struct s = this as jpeg_decompress_struct; |
|||
if (s != null) |
|||
s.m_marker_list = null; |
|||
} |
|||
else |
|||
{ |
|||
m_global_state = JpegState.CSTATE_START; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Destruction of a JPEG object.
|
|||
///
|
|||
/// Closing a data source or destination, if necessary, is the
|
|||
/// application's responsibility.
|
|||
/// </summary>
|
|||
public void jpeg_destroy() |
|||
{ |
|||
// mark it destroyed
|
|||
m_global_state = JpegState.DESTROYED; |
|||
} |
|||
|
|||
// Fatal errors (print message and exit)
|
|||
|
|||
/// <summary>
|
|||
/// Used for fatal errors (print message and exit).
|
|||
/// </summary>
|
|||
/// <param name="code">The message code.</param>
|
|||
public void ERREXIT(J_MESSAGE_CODE code) |
|||
{ |
|||
ERREXIT((int)code); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Used for fatal errors (print message and exit).
|
|||
/// </summary>
|
|||
/// <param name="code">The message code.</param>
|
|||
/// <param name="args">The parameters of message.</param>
|
|||
public void ERREXIT(J_MESSAGE_CODE code, params object[] args) |
|||
{ |
|||
ERREXIT((int)code, args); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Used for fatal errors (print message and exit).
|
|||
/// </summary>
|
|||
/// <param name="code">The message code.</param>
|
|||
/// <param name="args">The parameters of message.</param>
|
|||
public void ERREXIT(int code, params object[] args) |
|||
{ |
|||
m_err.m_msg_code = code; |
|||
m_err.m_msg_parm = args; |
|||
m_err.error_exit(); |
|||
} |
|||
|
|||
// Nonfatal errors (we can keep going, but the data is probably corrupt)
|
|||
|
|||
|
|||
/// <summary>
|
|||
/// Used for non-fatal errors (we can keep going, but the data is probably corrupt).
|
|||
/// </summary>
|
|||
/// <param name="code">The message code.</param>
|
|||
public void WARNMS(J_MESSAGE_CODE code) |
|||
{ |
|||
WARNMS((int)code); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Used for non-fatal errors (we can keep going, but the data is probably corrupt).
|
|||
/// </summary>
|
|||
/// <param name="code">The message code.</param>
|
|||
/// <param name="args">The parameters of message.</param>
|
|||
public void WARNMS(J_MESSAGE_CODE code, params object[] args) |
|||
{ |
|||
WARNMS((int)code, args); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Used for non-fatal errors (we can keep going, but the data is probably corrupt).
|
|||
/// </summary>
|
|||
/// <param name="code">The message code.</param>
|
|||
/// <param name="args">The parameters of message.</param>
|
|||
public void WARNMS(int code, params object[] args) |
|||
{ |
|||
m_err.m_msg_code = code; |
|||
m_err.m_msg_parm = args; |
|||
m_err.emit_message(-1); |
|||
} |
|||
|
|||
// Informational/debugging messages
|
|||
|
|||
/// <summary>
|
|||
/// Shows informational and debugging messages.
|
|||
/// </summary>
|
|||
/// <param name="lvl">See <see cref="jpeg_error_mgr.emit_message"/> for description.</param>
|
|||
/// <param name="code">The message code.</param>
|
|||
/// <seealso cref="jpeg_error_mgr.emit_message"/>
|
|||
public void TRACEMS(int lvl, J_MESSAGE_CODE code) |
|||
{ |
|||
TRACEMS(lvl, (int)code); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Shows informational and debugging messages.
|
|||
/// </summary>
|
|||
/// <param name="lvl">See <see cref="jpeg_error_mgr.emit_message"/> for description.</param>
|
|||
/// <param name="code">The message code.</param>
|
|||
/// <param name="args">The parameters of message.</param>
|
|||
/// <seealso cref="jpeg_error_mgr.emit_message"/>
|
|||
public void TRACEMS(int lvl, J_MESSAGE_CODE code, params object[] args) |
|||
{ |
|||
TRACEMS(lvl, (int)code, args); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Shows informational and debugging messages.
|
|||
/// </summary>
|
|||
/// <param name="lvl">See <see cref="jpeg_error_mgr.emit_message"/> for description.</param>
|
|||
/// <param name="code">The message code.</param>
|
|||
/// <param name="args">The parameters of message.</param>
|
|||
/// <seealso cref="jpeg_error_mgr.emit_message"/>
|
|||
public void TRACEMS(int lvl, int code, params object[] args) |
|||
{ |
|||
m_err.m_msg_code = code; |
|||
m_err.m_msg_parm = args; |
|||
m_err.emit_message(lvl); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,218 @@ |
|||
/* Copyright (C) 2008-2011, Bit Miracle |
|||
* http://www.bitmiracle.com
|
|||
* |
|||
* Copyright (C) 1994-1996, Thomas G. Lane. |
|||
* This file is part of the Independent JPEG Group's software. |
|||
* For conditions of distribution and use, see the accompanying README file. |
|||
* |
|||
*/ |
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
|
|||
using BitMiracle.LibJpeg.Classic.Internal; |
|||
|
|||
namespace BitMiracle.LibJpeg.Classic |
|||
{ |
|||
/// <summary>
|
|||
/// Basic info about one component (color channel).
|
|||
/// </summary>
|
|||
#if EXPOSE_LIBJPEG
|
|||
public |
|||
#endif
|
|||
class jpeg_component_info |
|||
{ |
|||
/* These values are fixed over the whole image. */ |
|||
/* For compression, they must be supplied by parameter setup; */ |
|||
/* for decompression, they are read from the SOF marker. */ |
|||
|
|||
private int component_id; |
|||
private int component_index; |
|||
private int h_samp_factor; |
|||
private int v_samp_factor; |
|||
private int quant_tbl_no; |
|||
|
|||
/* These values may vary between scans. */ |
|||
/* For compression, they must be supplied by parameter setup; */ |
|||
/* for decompression, they are read from the SOS marker. */ |
|||
/* The decompressor output side may not use these variables. */ |
|||
private int dc_tbl_no; |
|||
private int ac_tbl_no; |
|||
|
|||
/* Remaining fields should be treated as private by applications. */ |
|||
|
|||
/* These values are computed during compression or decompression startup: */ |
|||
/* Component's size in DCT blocks. |
|||
* Any dummy blocks added to complete an MCU are not counted; therefore |
|||
* these values do not depend on whether a scan is interleaved or not. |
|||
*/ |
|||
private int width_in_blocks; |
|||
internal int height_in_blocks; |
|||
/* Size of a DCT block in samples. Always DCTSIZE for compression. |
|||
* For decompression this is the size of the output from one DCT block, |
|||
* reflecting any scaling we choose to apply during the IDCT step. |
|||
* Values of 1,2,4,8 are likely to be supported. Note that different |
|||
* components may receive different IDCT scalings. |
|||
*/ |
|||
internal int DCT_scaled_size; |
|||
/* The downsampled dimensions are the component's actual, unpadded number |
|||
* of samples at the main buffer (preprocessing/compression interface), thus |
|||
* downsampled_width = ceil(image_width * Hi/Hmax) |
|||
* and similarly for height. For decompression, IDCT scaling is included, so |
|||
* downsampled_width = ceil(image_width * Hi/Hmax * DCT_scaled_size/DCTSIZE) |
|||
*/ |
|||
internal int downsampled_width; /* actual width in samples */ |
|||
|
|||
internal int downsampled_height; /* actual height in samples */ |
|||
/* This flag is used only for decompression. In cases where some of the |
|||
* components will be ignored (eg grayscale output from YCbCr image), |
|||
* we can skip most computations for the unused components. |
|||
*/ |
|||
internal bool component_needed; /* do we need the value of this component? */ |
|||
|
|||
/* These values are computed before starting a scan of the component. */ |
|||
/* The decompressor output side may not use these variables. */ |
|||
internal int MCU_width; /* number of blocks per MCU, horizontally */ |
|||
internal int MCU_height; /* number of blocks per MCU, vertically */ |
|||
internal int MCU_blocks; /* MCU_width * MCU_height */ |
|||
internal int MCU_sample_width; /* MCU width in samples, MCU_width*DCT_scaled_size */ |
|||
internal int last_col_width; /* # of non-dummy blocks across in last MCU */ |
|||
internal int last_row_height; /* # of non-dummy blocks down in last MCU */ |
|||
|
|||
/* Saved quantization table for component; null if none yet saved. |
|||
* See jpeg_input_controller comments about the need for this information. |
|||
* This field is currently used only for decompression. |
|||
*/ |
|||
internal JQUANT_TBL quant_table; |
|||
|
|||
internal jpeg_component_info() |
|||
{ |
|||
} |
|||
|
|||
internal void Assign(jpeg_component_info ci) |
|||
{ |
|||
component_id = ci.component_id; |
|||
component_index = ci.component_index; |
|||
h_samp_factor = ci.h_samp_factor; |
|||
v_samp_factor = ci.v_samp_factor; |
|||
quant_tbl_no = ci.quant_tbl_no; |
|||
dc_tbl_no = ci.dc_tbl_no; |
|||
ac_tbl_no = ci.ac_tbl_no; |
|||
width_in_blocks = ci.width_in_blocks; |
|||
height_in_blocks = ci.height_in_blocks; |
|||
DCT_scaled_size = ci.DCT_scaled_size; |
|||
downsampled_width = ci.downsampled_width; |
|||
downsampled_height = ci.downsampled_height; |
|||
component_needed = ci.component_needed; |
|||
MCU_width = ci.MCU_width; |
|||
MCU_height = ci.MCU_height; |
|||
MCU_blocks = ci.MCU_blocks; |
|||
MCU_sample_width = ci.MCU_sample_width; |
|||
last_col_width = ci.last_col_width; |
|||
last_row_height = ci.last_row_height; |
|||
quant_table = ci.quant_table; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Identifier for this component (0..255)
|
|||
/// </summary>
|
|||
/// <value>The component ID.</value>
|
|||
public int Component_id |
|||
{ |
|||
get { return component_id; } |
|||
set { component_id = value; } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Its index in SOF or <see cref="jpeg_decompress_struct.Comp_info"/>.
|
|||
/// </summary>
|
|||
/// <value>The component index.</value>
|
|||
public int Component_index |
|||
{ |
|||
get { return component_index; } |
|||
set { component_index = value; } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Horizontal sampling factor (1..4)
|
|||
/// </summary>
|
|||
/// <value>The horizontal sampling factor.</value>
|
|||
public int H_samp_factor |
|||
{ |
|||
get { return h_samp_factor; } |
|||
set { h_samp_factor = value; } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Vertical sampling factor (1..4)
|
|||
/// </summary>
|
|||
/// <value>The vertical sampling factor.</value>
|
|||
public int V_samp_factor |
|||
{ |
|||
get { return v_samp_factor; } |
|||
set { v_samp_factor = value; } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Quantization table selector (0..3)
|
|||
/// </summary>
|
|||
/// <value>The quantization table selector.</value>
|
|||
public int Quant_tbl_no |
|||
{ |
|||
get { return quant_tbl_no; } |
|||
set { quant_tbl_no = value; } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// DC entropy table selector (0..3)
|
|||
/// </summary>
|
|||
/// <value>The DC entropy table selector.</value>
|
|||
public int Dc_tbl_no |
|||
{ |
|||
get { return dc_tbl_no; } |
|||
set { dc_tbl_no = value; } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// AC entropy table selector (0..3)
|
|||
/// </summary>
|
|||
/// <value>The AC entropy table selector.</value>
|
|||
public int Ac_tbl_no |
|||
{ |
|||
get { return ac_tbl_no; } |
|||
set { ac_tbl_no = value; } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the width in blocks.
|
|||
/// </summary>
|
|||
/// <value>The width in blocks.</value>
|
|||
public int Width_in_blocks |
|||
{ |
|||
get { return width_in_blocks; } |
|||
set { width_in_blocks = value; } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the downsampled width.
|
|||
/// </summary>
|
|||
/// <value>The downsampled width.</value>
|
|||
public int Downsampled_width |
|||
{ |
|||
get { return downsampled_width; } |
|||
} |
|||
|
|||
internal static jpeg_component_info[] createArrayOfComponents(int length) |
|||
{ |
|||
if (length < 0) |
|||
throw new ArgumentOutOfRangeException("length"); |
|||
|
|||
jpeg_component_info[] result = new jpeg_component_info[length]; |
|||
for (int i = 0; i < result.Length; ++i) |
|||
result[i] = new jpeg_component_info(); |
|||
|
|||
return result; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1 @@ |
|||
efd39fc8e0e3681aeb3da59a67c3ac2ad7b858a2 |
|||
@ -0,0 +1 @@ |
|||
57234e24afc9b4d1637062735248d7e1c7812a48 |
|||
@ -0,0 +1,87 @@ |
|||
/* Copyright (C) 2008-2011, Bit Miracle |
|||
* http://www.bitmiracle.com
|
|||
* |
|||
* Copyright (C) 1994-1996, Thomas G. Lane. |
|||
* This file is part of the Independent JPEG Group's software. |
|||
* For conditions of distribution and use, see the accompanying README file. |
|||
* |
|||
*/ |
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
|
|||
namespace BitMiracle.LibJpeg.Classic |
|||
{ |
|||
/// <summary>
|
|||
/// Data destination object for compression.
|
|||
/// </summary>
|
|||
#if EXPOSE_LIBJPEG
|
|||
public |
|||
#endif
|
|||
abstract class jpeg_destination_mgr |
|||
{ |
|||
private byte[] m_buffer; |
|||
private int m_position; |
|||
private int m_free_in_buffer; /* # of byte spaces remaining in buffer */ |
|||
|
|||
/// <summary>
|
|||
/// Initializes this instance.
|
|||
/// </summary>
|
|||
public abstract void init_destination(); |
|||
|
|||
/// <summary>
|
|||
/// Empties output buffer.
|
|||
/// </summary>
|
|||
/// <returns><c>true</c> if operation succeed; otherwise, <c>false</c></returns>
|
|||
public abstract bool empty_output_buffer(); |
|||
|
|||
/// <summary>
|
|||
/// Term_destinations this instance.
|
|||
/// </summary>
|
|||
public abstract void term_destination(); |
|||
|
|||
/// <summary>
|
|||
/// Emits a byte.
|
|||
/// </summary>
|
|||
/// <param name="val">The byte value.</param>
|
|||
/// <returns><c>true</c> if operation succeed; otherwise, <c>false</c></returns>
|
|||
public virtual bool emit_byte(int val) |
|||
{ |
|||
m_buffer[m_position] = (byte)val; |
|||
m_position++; |
|||
|
|||
if (--m_free_in_buffer == 0) |
|||
{ |
|||
if (!empty_output_buffer()) |
|||
return false; |
|||
} |
|||
|
|||
return true; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes the internal buffer.
|
|||
/// </summary>
|
|||
/// <param name="buffer">The buffer.</param>
|
|||
/// <param name="offset">The offset.</param>
|
|||
protected void initInternalBuffer(byte[] buffer, int offset) |
|||
{ |
|||
m_buffer = buffer; |
|||
m_free_in_buffer = buffer.Length - offset; |
|||
m_position = offset; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the number of free bytes in buffer.
|
|||
/// </summary>
|
|||
/// <value>The number of free bytes in buffer.</value>
|
|||
protected int freeInBuffer |
|||
{ |
|||
get |
|||
{ |
|||
return m_free_in_buffer; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,405 @@ |
|||
/* Copyright (C) 2008-2011, Bit Miracle |
|||
* http://www.bitmiracle.com
|
|||
* |
|||
* Copyright (C) 1994-1996, Thomas G. Lane. |
|||
* This file is part of the Independent JPEG Group's software. |
|||
* For conditions of distribution and use, see the accompanying README file. |
|||
* |
|||
*/ |
|||
|
|||
/* |
|||
* This file contains simple error-reporting and trace-message routines. |
|||
* Many applications will want to override some or all of these routines. |
|||
* |
|||
* These routines are used by both the compression and decompression code. |
|||
*/ |
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
using System.Globalization; |
|||
using System.Diagnostics; |
|||
|
|||
namespace BitMiracle.LibJpeg.Classic |
|||
{ |
|||
/// <summary>
|
|||
/// Contains simple error-reporting and trace-message routines.
|
|||
/// </summary>
|
|||
/// <remarks>This class is used by both the compression and decompression code.</remarks>
|
|||
/// <seealso href="41dc1a3b-0dea-4594-87d2-c213ab1049e1.htm" target="_self">Error handling</seealso>
|
|||
#if EXPOSE_LIBJPEG
|
|||
public |
|||
#endif
|
|||
class jpeg_error_mgr |
|||
{ |
|||
// The message ID code and any parameters are saved in fields below.
|
|||
internal int m_msg_code; |
|||
internal object[] m_msg_parm; |
|||
|
|||
internal int m_trace_level; |
|||
internal int m_num_warnings; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="jpeg_error_mgr"/> class.
|
|||
/// </summary>
|
|||
public jpeg_error_mgr() |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the maximum message level that will be displayed.
|
|||
/// </summary>
|
|||
/// <value>Values are:
|
|||
/// -1: recoverable corrupt-data warning, may want to abort.<br/>
|
|||
/// 0: important advisory messages (always display to user).<br/>
|
|||
/// 1: first level of tracing detail.<br/>
|
|||
/// 2, 3, ...: successively more detailed tracing messages.
|
|||
/// </value>
|
|||
/// <seealso cref="jpeg_error_mgr.emit_message"/>
|
|||
public int Trace_level |
|||
{ |
|||
get { return m_trace_level; } |
|||
set { m_trace_level = value; } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the number of corrupt-data warnings.
|
|||
/// </summary>
|
|||
/// <value>The num_warnings.</value>
|
|||
/// <remarks>For recoverable corrupt-data errors, we emit a warning message, but keep going
|
|||
/// unless <see cref="jpeg_error_mgr.emit_message">emit_message</see> chooses to abort.
|
|||
/// <c>emit_message</c> should count warnings in <c>Num_warnings</c>. The surrounding application
|
|||
/// can check for bad data by seeing if <c>Num_warnings</c> is nonzero at the end of processing.</remarks>
|
|||
public int Num_warnings |
|||
{ |
|||
get { return m_num_warnings; } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Receives control for a fatal error.
|
|||
/// </summary>
|
|||
/// <remarks>This method calls <see cref="jpeg_error_mgr.output_message">output_message</see>
|
|||
/// and then throws an exception.</remarks>
|
|||
/// <seealso href="41dc1a3b-0dea-4594-87d2-c213ab1049e1.htm" target="_self">Error handling</seealso>
|
|||
public virtual void error_exit() |
|||
{ |
|||
// Always display the message
|
|||
output_message(); |
|||
|
|||
string buffer = format_message(); |
|||
throw new Exception(buffer); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Conditionally emit a trace or warning message.
|
|||
/// </summary>
|
|||
/// <param name="msg_level">The message severity level.<br/>
|
|||
/// Values are:<br/>
|
|||
/// -1: recoverable corrupt-data warning, may want to abort.<br/>
|
|||
/// 0: important advisory messages (always display to user).<br/>
|
|||
/// 1: first level of tracing detail.<br/>
|
|||
/// 2, 3, ...: successively more detailed tracing messages.
|
|||
/// </param>
|
|||
/// <remarks>The main reason for overriding this method would be to abort on warnings.
|
|||
/// This method calls <see cref="jpeg_error_mgr.output_message">output_message</see> for message showing.<br/>
|
|||
///
|
|||
/// An application might override this method if it wanted to abort on
|
|||
/// warnings or change the policy about which messages to display.
|
|||
/// </remarks>
|
|||
/// <seealso href="41dc1a3b-0dea-4594-87d2-c213ab1049e1.htm" target="_self">Error handling</seealso>
|
|||
public virtual void emit_message(int msg_level) |
|||
{ |
|||
if (msg_level < 0) |
|||
{ |
|||
/* It's a warning message. Since corrupt files may generate many warnings, |
|||
* the policy implemented here is to show only the first warning, |
|||
* unless trace_level >= 3. |
|||
*/ |
|||
if (m_num_warnings == 0 || m_trace_level >= 3) |
|||
output_message(); |
|||
|
|||
/* Always count warnings in num_warnings. */ |
|||
m_num_warnings++; |
|||
} |
|||
else |
|||
{ |
|||
/* It's a trace message. Show it if trace_level >= msg_level. */ |
|||
if (m_trace_level >= msg_level) |
|||
output_message(); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Actual output of any JPEG message.
|
|||
/// </summary>
|
|||
/// <remarks>Override this to send messages somewhere other than Console.
|
|||
/// Note that this method does not know how to generate a message, only where to send it.
|
|||
/// For extending a generation of messages see <see cref="jpeg_error_mgr.format_message">format_message</see>.
|
|||
/// </remarks>
|
|||
/// <seealso href="41dc1a3b-0dea-4594-87d2-c213ab1049e1.htm" target="_self">Error handling</seealso>
|
|||
public virtual void output_message() |
|||
{ |
|||
// Create the message
|
|||
string buffer = format_message(); |
|||
|
|||
// Send it to console, adding a newline */
|
|||
Debug.WriteLine(buffer); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Constructs a readable error message string.
|
|||
/// </summary>
|
|||
/// <remarks>This method is called by <see cref="jpeg_error_mgr.output_message">output_message</see>.
|
|||
/// Few applications should need to override this method. One possible reason for doing so is to
|
|||
/// implement dynamic switching of error message language.</remarks>
|
|||
/// <returns>The formatted message</returns>
|
|||
/// <seealso href="41dc1a3b-0dea-4594-87d2-c213ab1049e1.htm" target="_self">Error handling</seealso>
|
|||
public virtual string format_message() |
|||
{ |
|||
string msgtext = GetMessageText(m_msg_code); |
|||
if (msgtext == null) |
|||
{ |
|||
m_msg_parm = new object[] { m_msg_code }; |
|||
msgtext = GetMessageText(0); |
|||
} |
|||
|
|||
/* Format the message into the passed buffer */ |
|||
return string.Format(CultureInfo.CurrentCulture, msgtext, m_msg_parm); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Resets error manager to initial state.
|
|||
/// </summary>
|
|||
/// <remarks>This is called during compression startup to reset trace/error
|
|||
/// processing to default state. An application might possibly want to
|
|||
/// override this method if it has additional error processing state.
|
|||
/// </remarks>
|
|||
public virtual void reset_error_mgr() |
|||
{ |
|||
m_num_warnings = 0; |
|||
|
|||
/* trace_level is not reset since it is an application-supplied parameter */ |
|||
|
|||
// may be useful as a flag for "no error"
|
|||
m_msg_code = 0; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the actual message texts.
|
|||
/// </summary>
|
|||
/// <param name="code">The message code. See <see cref="J_MESSAGE_CODE"/> for details.</param>
|
|||
/// <returns>The message text associated with <c>code</c>.</returns>
|
|||
/// <remarks>It may be useful for an application to add its own message texts that are handled
|
|||
/// by the same mechanism. You can override <c>GetMessageText</c> for this purpose. If you number
|
|||
/// the addon messages beginning at 1000 or so, you won't have to worry about conflicts
|
|||
/// with the library's built-in messages.
|
|||
/// </remarks>
|
|||
/// <seealso cref="J_MESSAGE_CODE"/>
|
|||
/// <seealso href="41dc1a3b-0dea-4594-87d2-c213ab1049e1.htm" target="_self">Error handling</seealso>
|
|||
protected virtual string GetMessageText(int code) |
|||
{ |
|||
switch ((J_MESSAGE_CODE)code) |
|||
{ |
|||
default: |
|||
case J_MESSAGE_CODE.JMSG_NOMESSAGE: |
|||
return "Bogus message code {0}"; |
|||
|
|||
/* For maintenance convenience, list is alphabetical by message code name */ |
|||
case J_MESSAGE_CODE.JERR_ARITH_NOTIMPL: |
|||
return "Sorry, there are legal restrictions on arithmetic coding"; |
|||
case J_MESSAGE_CODE.JERR_BAD_BUFFER_MODE: |
|||
return "Bogus buffer control mode"; |
|||
case J_MESSAGE_CODE.JERR_BAD_COMPONENT_ID: |
|||
return "Invalid component ID {0} in SOS"; |
|||
case J_MESSAGE_CODE.JERR_BAD_DCT_COEF: |
|||
return "DCT coefficient out of range"; |
|||
case J_MESSAGE_CODE.JERR_BAD_DCTSIZE: |
|||
return "IDCT output block size {0} not supported"; |
|||
case J_MESSAGE_CODE.JERR_BAD_HUFF_TABLE: |
|||
return "Bogus Huffman table definition"; |
|||
case J_MESSAGE_CODE.JERR_BAD_IN_COLORSPACE: |
|||
return "Bogus input colorspace"; |
|||
case J_MESSAGE_CODE.JERR_BAD_J_COLORSPACE: |
|||
return "Bogus JPEG colorspace"; |
|||
case J_MESSAGE_CODE.JERR_BAD_LENGTH: |
|||
return "Bogus marker length"; |
|||
case J_MESSAGE_CODE.JERR_BAD_MCU_SIZE: |
|||
return "Sampling factors too large for interleaved scan"; |
|||
case J_MESSAGE_CODE.JERR_BAD_PRECISION: |
|||
return "Unsupported JPEG data precision {0}"; |
|||
case J_MESSAGE_CODE.JERR_BAD_PROGRESSION: |
|||
return "Invalid progressive parameters Ss={0} Se={1} Ah={2} Al={3}"; |
|||
case J_MESSAGE_CODE.JERR_BAD_PROG_SCRIPT: |
|||
return "Invalid progressive parameters at scan script entry {0}"; |
|||
case J_MESSAGE_CODE.JERR_BAD_SAMPLING: |
|||
return "Bogus sampling factors"; |
|||
case J_MESSAGE_CODE.JERR_BAD_SCAN_SCRIPT: |
|||
return "Invalid scan script at entry {0}"; |
|||
case J_MESSAGE_CODE.JERR_BAD_STATE: |
|||
return "Improper call to JPEG library in state {0}"; |
|||
case J_MESSAGE_CODE.JERR_BAD_VIRTUAL_ACCESS: |
|||
return "Bogus virtual array access"; |
|||
case J_MESSAGE_CODE.JERR_BUFFER_SIZE: |
|||
return "Buffer passed to JPEG library is too small"; |
|||
case J_MESSAGE_CODE.JERR_CANT_SUSPEND: |
|||
return "Suspension not allowed here"; |
|||
case J_MESSAGE_CODE.JERR_CCIR601_NOTIMPL: |
|||
return "CCIR601 sampling not implemented yet"; |
|||
case J_MESSAGE_CODE.JERR_COMPONENT_COUNT: |
|||
return "Too many color components: {0}, max {1}"; |
|||
case J_MESSAGE_CODE.JERR_CONVERSION_NOTIMPL: |
|||
return "Unsupported color conversion request"; |
|||
case J_MESSAGE_CODE.JERR_DHT_INDEX: |
|||
return "Bogus DHT index {0}"; |
|||
case J_MESSAGE_CODE.JERR_DQT_INDEX: |
|||
return "Bogus DQT index {0}"; |
|||
case J_MESSAGE_CODE.JERR_EMPTY_IMAGE: |
|||
return "Empty JPEG image (DNL not supported)"; |
|||
case J_MESSAGE_CODE.JERR_EOI_EXPECTED: |
|||
return "Didn't expect more than one scan"; |
|||
case J_MESSAGE_CODE.JERR_FILE_WRITE: |
|||
return "Output file write error --- out of disk space?"; |
|||
case J_MESSAGE_CODE.JERR_FRACT_SAMPLE_NOTIMPL: |
|||
return "Fractional sampling not implemented yet"; |
|||
case J_MESSAGE_CODE.JERR_HUFF_CLEN_OVERFLOW: |
|||
return "Huffman code size table overflow"; |
|||
case J_MESSAGE_CODE.JERR_HUFF_MISSING_CODE: |
|||
return "Missing Huffman code table entry"; |
|||
case J_MESSAGE_CODE.JERR_IMAGE_TOO_BIG: |
|||
return "Maximum supported image dimension is {0} pixels"; |
|||
case J_MESSAGE_CODE.JERR_INPUT_EMPTY: |
|||
return "Empty input file"; |
|||
case J_MESSAGE_CODE.JERR_INPUT_EOF: |
|||
return "Premature end of input file"; |
|||
case J_MESSAGE_CODE.JERR_MISMATCHED_QUANT_TABLE: |
|||
return "Cannot transcode due to multiple use of quantization table {0}"; |
|||
case J_MESSAGE_CODE.JERR_MISSING_DATA: |
|||
return "Scan script does not transmit all data"; |
|||
case J_MESSAGE_CODE.JERR_MODE_CHANGE: |
|||
return "Invalid color quantization mode change"; |
|||
case J_MESSAGE_CODE.JERR_NOTIMPL: |
|||
return "Not implemented yet"; |
|||
case J_MESSAGE_CODE.JERR_NOT_COMPILED: |
|||
return "Requested feature was omitted at compile time"; |
|||
case J_MESSAGE_CODE.JERR_NO_HUFF_TABLE: |
|||
return "Huffman table 0x{0:X2} was not defined"; |
|||
case J_MESSAGE_CODE.JERR_NO_IMAGE: |
|||
return "JPEG datastream contains no image"; |
|||
case J_MESSAGE_CODE.JERR_NO_QUANT_TABLE: |
|||
return "Quantization table 0x{0:X2} was not defined"; |
|||
case J_MESSAGE_CODE.JERR_NO_SOI: |
|||
return "Not a JPEG file: starts with 0x{0:X2} 0x{1:X2}"; |
|||
case J_MESSAGE_CODE.JERR_OUT_OF_MEMORY: |
|||
return "Insufficient memory (case {0})"; |
|||
case J_MESSAGE_CODE.JERR_QUANT_COMPONENTS: |
|||
return "Cannot quantize more than {0} color components"; |
|||
case J_MESSAGE_CODE.JERR_QUANT_FEW_COLORS: |
|||
return "Cannot quantize to fewer than {0} colors"; |
|||
case J_MESSAGE_CODE.JERR_QUANT_MANY_COLORS: |
|||
return "Cannot quantize to more than {0} colors"; |
|||
case J_MESSAGE_CODE.JERR_SOF_DUPLICATE: |
|||
return "Invalid JPEG file structure: two SOF markers"; |
|||
case J_MESSAGE_CODE.JERR_SOF_NO_SOS: |
|||
return "Invalid JPEG file structure: missing SOS marker"; |
|||
case J_MESSAGE_CODE.JERR_SOF_UNSUPPORTED: |
|||
return "Unsupported JPEG process: SOF type 0x{0:X2}"; |
|||
case J_MESSAGE_CODE.JERR_SOI_DUPLICATE: |
|||
return "Invalid JPEG file structure: two SOI markers"; |
|||
case J_MESSAGE_CODE.JERR_SOS_NO_SOF: |
|||
return "Invalid JPEG file structure: SOS before SOF"; |
|||
case J_MESSAGE_CODE.JERR_TOO_LITTLE_DATA: |
|||
return "Application transferred too few scanlines"; |
|||
case J_MESSAGE_CODE.JERR_UNKNOWN_MARKER: |
|||
return "Unsupported marker type 0x{0:X2}"; |
|||
case J_MESSAGE_CODE.JERR_WIDTH_OVERFLOW: |
|||
return "Image too wide for this implementation"; |
|||
case J_MESSAGE_CODE.JTRC_16BIT_TABLES: |
|||
return "Caution: quantization tables are too coarse for baseline JPEG"; |
|||
case J_MESSAGE_CODE.JTRC_ADOBE: |
|||
return "Adobe APP14 marker: version {0}, flags 0x{1:X4} 0x{2:X4}, transform {3}"; |
|||
case J_MESSAGE_CODE.JTRC_APP0: |
|||
return "Unknown APP0 marker (not JFIF), length {0}"; |
|||
case J_MESSAGE_CODE.JTRC_APP14: |
|||
return "Unknown APP14 marker (not Adobe), length {0}"; |
|||
case J_MESSAGE_CODE.JTRC_DHT: |
|||
return "Define Huffman Table 0x{0:X2}"; |
|||
case J_MESSAGE_CODE.JTRC_DQT: |
|||
return "Define Quantization Table {0} precision {1}"; |
|||
case J_MESSAGE_CODE.JTRC_DRI: |
|||
return "Define Restart Interval {0}"; |
|||
case J_MESSAGE_CODE.JTRC_EOI: |
|||
return "End Of Image"; |
|||
case J_MESSAGE_CODE.JTRC_HUFFBITS: |
|||
return " {0:D3} {1:D3} {2:D3} {3:D3} {4:D3} {5:D3} {6:D3} {7:D3}"; |
|||
case J_MESSAGE_CODE.JTRC_JFIF: |
|||
return "JFIF APP0 marker: version {0}.{1:D2}, density {2}x{3} {4}"; |
|||
case J_MESSAGE_CODE.JTRC_JFIF_BADTHUMBNAILSIZE: |
|||
return "Warning: thumbnail image size does not match data length {0}"; |
|||
case J_MESSAGE_CODE.JTRC_JFIF_EXTENSION: |
|||
return "JFIF extension marker: type 0x{0:X2}, length {1}"; |
|||
case J_MESSAGE_CODE.JTRC_JFIF_THUMBNAIL: |
|||
return " with {0} x {1} thumbnail image"; |
|||
case J_MESSAGE_CODE.JTRC_MISC_MARKER: |
|||
return "Miscellaneous marker 0x{0:X2}, length {1}"; |
|||
case J_MESSAGE_CODE.JTRC_PARMLESS_MARKER: |
|||
return "Unexpected marker 0x{0:X2}"; |
|||
case J_MESSAGE_CODE.JTRC_QUANTVALS: |
|||
return " {0:D4} {1:D4} {2:D4} {3:D4} {4:D4} {5:D4} {6:D4} {7:D4}"; |
|||
case J_MESSAGE_CODE.JTRC_QUANT_3_NCOLORS: |
|||
return "Quantizing to {0} = {1}*{2}*{3} colors"; |
|||
case J_MESSAGE_CODE.JTRC_QUANT_NCOLORS: |
|||
return "Quantizing to {0} colors"; |
|||
case J_MESSAGE_CODE.JTRC_QUANT_SELECTED: |
|||
return "Selected {0} colors for quantization"; |
|||
case J_MESSAGE_CODE.JTRC_RECOVERY_ACTION: |
|||
return "At marker 0x{0:X2}, recovery action {1}"; |
|||
case J_MESSAGE_CODE.JTRC_RST: |
|||
return "RST{0}"; |
|||
case J_MESSAGE_CODE.JTRC_SMOOTH_NOTIMPL: |
|||
return "Smoothing not supported with nonstandard sampling ratios"; |
|||
case J_MESSAGE_CODE.JTRC_SOF: |
|||
return "Start Of Frame 0x{0:X2}: width={1}, height={2}, components={3}"; |
|||
case J_MESSAGE_CODE.JTRC_SOF_COMPONENT: |
|||
return " Component {0}: {1}hx{2}v q={3}"; |
|||
case J_MESSAGE_CODE.JTRC_SOI: |
|||
return "Start of Image"; |
|||
case J_MESSAGE_CODE.JTRC_SOS: |
|||
return "Start Of Scan: {0} components"; |
|||
case J_MESSAGE_CODE.JTRC_SOS_COMPONENT: |
|||
return " Component {0}: dc={1} ac={2}"; |
|||
case J_MESSAGE_CODE.JTRC_SOS_PARAMS: |
|||
return " Ss={0}, Se={1}, Ah={2}, Al={3}"; |
|||
case J_MESSAGE_CODE.JTRC_THUMB_JPEG: |
|||
return "JFIF extension marker: JPEG-compressed thumbnail image, length {0}"; |
|||
case J_MESSAGE_CODE.JTRC_THUMB_PALETTE: |
|||
return "JFIF extension marker: palette thumbnail image, length {0}"; |
|||
case J_MESSAGE_CODE.JTRC_THUMB_RGB: |
|||
return "JFIF extension marker: RGB thumbnail image, length {0}"; |
|||
case J_MESSAGE_CODE.JTRC_UNKNOWN_IDS: |
|||
return "Unrecognized component IDs {0} {1} {2}, assuming YCbCr"; |
|||
case J_MESSAGE_CODE.JWRN_ADOBE_XFORM: |
|||
return "Unknown Adobe color transform code {0}"; |
|||
case J_MESSAGE_CODE.JWRN_BOGUS_PROGRESSION: |
|||
return "Inconsistent progression sequence for component {0} coefficient {1}"; |
|||
case J_MESSAGE_CODE.JWRN_EXTRANEOUS_DATA: |
|||
return "Corrupt JPEG data: {0} extraneous bytes before marker 0x{1:X2}"; |
|||
case J_MESSAGE_CODE.JWRN_HIT_MARKER: |
|||
return "Corrupt JPEG data: premature end of data segment"; |
|||
case J_MESSAGE_CODE.JWRN_HUFF_BAD_CODE: |
|||
return "Corrupt JPEG data: bad Huffman code"; |
|||
case J_MESSAGE_CODE.JWRN_JFIF_MAJOR: |
|||
return "Warning: unknown JFIF revision number {0}.{1:D2}"; |
|||
case J_MESSAGE_CODE.JWRN_JPEG_EOF: |
|||
return "Premature end of JPEG file"; |
|||
case J_MESSAGE_CODE.JWRN_MUST_RESYNC: |
|||
return "Corrupt JPEG data: found marker 0x{0:X2} instead of RST{1}"; |
|||
case J_MESSAGE_CODE.JWRN_NOT_SEQUENTIAL: |
|||
return "Invalid SOS parameters for sequential JPEG"; |
|||
case J_MESSAGE_CODE.JWRN_TOO_MUCH_DATA: |
|||
return "Application transferred too many scanlines"; |
|||
case J_MESSAGE_CODE.JMSG_UNKNOWNMSGCODE: |
|||
return "Unknown message code (possibly it is an error from application)"; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,84 @@ |
|||
/* Copyright (C) 2008-2011, Bit Miracle |
|||
* http://www.bitmiracle.com
|
|||
* |
|||
* Copyright (C) 1994-1996, Thomas G. Lane. |
|||
* This file is part of the Independent JPEG Group's software. |
|||
* For conditions of distribution and use, see the accompanying README file. |
|||
* |
|||
*/ |
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
|
|||
namespace BitMiracle.LibJpeg.Classic |
|||
{ |
|||
/// <summary>
|
|||
/// Representation of special JPEG marker.
|
|||
/// </summary>
|
|||
/// <remarks>You can't create instance of this class manually.
|
|||
/// Concrete objects are instantiated by library and you can get them
|
|||
/// through <see cref="jpeg_decompress_struct.Marker_list">Marker_list</see> property.
|
|||
/// </remarks>
|
|||
/// <seealso cref="jpeg_decompress_struct.Marker_list"/>
|
|||
/// <seealso href="81c88818-a5d7-4550-9ce5-024a768f7b1e.htm" target="_self">Special markers</seealso>
|
|||
#if EXPOSE_LIBJPEG
|
|||
public |
|||
#endif
|
|||
class jpeg_marker_struct |
|||
{ |
|||
private byte m_marker; /* marker code: JPEG_COM, or JPEG_APP0+n */ |
|||
private int m_originalLength; /* # bytes of data in the file */ |
|||
private byte[] m_data; /* the data contained in the marker */ |
|||
|
|||
internal jpeg_marker_struct(byte marker, int originalDataLength, int lengthLimit) |
|||
{ |
|||
m_marker = marker; |
|||
m_originalLength = originalDataLength; |
|||
m_data = new byte[lengthLimit]; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the special marker.
|
|||
/// </summary>
|
|||
/// <value>The marker value.</value>
|
|||
public byte Marker |
|||
{ |
|||
get |
|||
{ |
|||
return m_marker; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the full length of original data associated with the marker.
|
|||
/// </summary>
|
|||
/// <value>The length of original data associated with the marker.</value>
|
|||
/// <remarks>This length excludes the marker length word, whereas the stored representation
|
|||
/// within the JPEG file includes it. (Hence the maximum data length is really only 65533.)
|
|||
/// </remarks>
|
|||
public int OriginalLength |
|||
{ |
|||
get |
|||
{ |
|||
return m_originalLength; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the data associated with the marker.
|
|||
/// </summary>
|
|||
/// <value>The data associated with the marker.</value>
|
|||
/// <remarks>The length of this array doesn't exceed <c>length_limit</c> for the particular marker type.
|
|||
/// Note that this length excludes the marker length word, whereas the stored representation
|
|||
/// within the JPEG file includes it. (Hence the maximum data length is really only 65533.)
|
|||
/// </remarks>
|
|||
public byte[] Data |
|||
{ |
|||
get |
|||
{ |
|||
return m_data; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,91 @@ |
|||
/* Copyright (C) 2008-2011, Bit Miracle |
|||
* http://www.bitmiracle.com
|
|||
* |
|||
* Copyright (C) 1994-1996, Thomas G. Lane. |
|||
* This file is part of the Independent JPEG Group's software. |
|||
* For conditions of distribution and use, see the accompanying README file. |
|||
* |
|||
*/ |
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
|
|||
namespace BitMiracle.LibJpeg.Classic |
|||
{ |
|||
/// <summary>
|
|||
/// The progress monitor object.
|
|||
/// </summary>
|
|||
/// <seealso href="febdc6af-ca72-4f3b-8cfe-3473ce6a7c7f.htm" target="_self">Progress monitoring</seealso>
|
|||
#if EXPOSE_LIBJPEG
|
|||
public |
|||
#endif
|
|||
class jpeg_progress_mgr |
|||
{ |
|||
private int m_passCounter; |
|||
private int m_passLimit; |
|||
private int m_completedPasses; |
|||
private int m_totalPasses; |
|||
|
|||
/// <summary>
|
|||
/// Occurs when progress is changed.
|
|||
/// </summary>
|
|||
/// <seealso href="febdc6af-ca72-4f3b-8cfe-3473ce6a7c7f.htm" target="_self">Progress monitoring</seealso>
|
|||
public event EventHandler OnProgress; |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the number of work units completed in this pass.
|
|||
/// </summary>
|
|||
/// <value>The number of work units completed in this pass.</value>
|
|||
/// <seealso href="febdc6af-ca72-4f3b-8cfe-3473ce6a7c7f.htm" target="_self">Progress monitoring</seealso>
|
|||
public int Pass_counter |
|||
{ |
|||
get { return m_passCounter; } |
|||
set { m_passCounter = value; } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the total number of work units in this pass.
|
|||
/// </summary>
|
|||
/// <value>The total number of work units in this pass.</value>
|
|||
/// <seealso href="febdc6af-ca72-4f3b-8cfe-3473ce6a7c7f.htm" target="_self">Progress monitoring</seealso>
|
|||
public int Pass_limit |
|||
{ |
|||
get { return m_passLimit; } |
|||
set { m_passLimit = value; } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the number of passes completed so far.
|
|||
/// </summary>
|
|||
/// <value>The number of passes completed so far.</value>
|
|||
/// <seealso href="febdc6af-ca72-4f3b-8cfe-3473ce6a7c7f.htm" target="_self">Progress monitoring</seealso>
|
|||
public int Completed_passes |
|||
{ |
|||
get { return m_completedPasses; } |
|||
set { m_completedPasses = value; } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the total number of passes expected.
|
|||
/// </summary>
|
|||
/// <value>The total number of passes expected.</value>
|
|||
/// <seealso href="febdc6af-ca72-4f3b-8cfe-3473ce6a7c7f.htm" target="_self">Progress monitoring</seealso>
|
|||
public int Total_passes |
|||
{ |
|||
get { return m_totalPasses; } |
|||
set { m_totalPasses = value; } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Indicates that progress was changed.
|
|||
/// </summary>
|
|||
/// <remarks>Call this method if you change some progress parameters manually.
|
|||
/// This method ensures happening of the <see cref="jpeg_progress_mgr.OnProgress">OnProgress</see> event.</remarks>
|
|||
public void Updated() |
|||
{ |
|||
if (OnProgress != null) |
|||
OnProgress(this, new EventArgs()); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,296 @@ |
|||
/* Copyright (C) 2008-2011, Bit Miracle |
|||
* http://www.bitmiracle.com
|
|||
* |
|||
* Copyright (C) 1994-1996, Thomas G. Lane. |
|||
* This file is part of the Independent JPEG Group's software. |
|||
* For conditions of distribution and use, see the accompanying README file. |
|||
* |
|||
*/ |
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
|
|||
namespace BitMiracle.LibJpeg.Classic |
|||
{ |
|||
/// <summary>
|
|||
/// Data source object for decompression.
|
|||
/// </summary>
|
|||
#if EXPOSE_LIBJPEG
|
|||
public |
|||
#endif
|
|||
abstract class jpeg_source_mgr |
|||
{ |
|||
private byte[] m_next_input_byte; |
|||
private int m_bytes_in_buffer; /* # of bytes remaining (unread) in buffer */ |
|||
private int m_position; |
|||
|
|||
/// <summary>
|
|||
/// Initializes this instance.
|
|||
/// </summary>
|
|||
public abstract void init_source(); |
|||
|
|||
/// <summary>
|
|||
/// Fills input buffer
|
|||
/// </summary>
|
|||
/// <returns><c>true</c> if operation succeed; otherwise, <c>false</c></returns>
|
|||
public abstract bool fill_input_buffer(); |
|||
|
|||
/// <summary>
|
|||
/// Initializes the internal buffer.
|
|||
/// </summary>
|
|||
/// <param name="buffer">The buffer.</param>
|
|||
/// <param name="size">The size.</param>
|
|||
protected void initInternalBuffer(byte[] buffer, int size) |
|||
{ |
|||
m_bytes_in_buffer = size; |
|||
m_next_input_byte = buffer; |
|||
m_position = 0; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Skip data - used to skip over a potentially large amount of
|
|||
/// uninteresting data (such as an APPn marker).
|
|||
/// </summary>
|
|||
/// <param name="num_bytes">The number of bytes to skip.</param>
|
|||
/// <remarks>Writers of suspendable-input applications must note that skip_input_data
|
|||
/// is not granted the right to give a suspension return. If the skip extends
|
|||
/// beyond the data currently in the buffer, the buffer can be marked empty so
|
|||
/// that the next read will cause a fill_input_buffer call that can suspend.
|
|||
/// Arranging for additional bytes to be discarded before reloading the input
|
|||
/// buffer is the application writer's problem.</remarks>
|
|||
public virtual void skip_input_data(int num_bytes) |
|||
{ |
|||
/* Just a dumb implementation for now. Could use fseek() except |
|||
* it doesn't work on pipes. Not clear that being smart is worth |
|||
* any trouble anyway --- large skips are infrequent. |
|||
*/ |
|||
if (num_bytes > 0) |
|||
{ |
|||
while (num_bytes > m_bytes_in_buffer) |
|||
{ |
|||
num_bytes -= m_bytes_in_buffer; |
|||
fill_input_buffer(); |
|||
/* note we assume that fill_input_buffer will never return false, |
|||
* so suspension need not be handled. |
|||
*/ |
|||
} |
|||
|
|||
m_position += num_bytes; |
|||
m_bytes_in_buffer -= num_bytes; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// This is the default resync_to_restart method for data source
|
|||
/// managers to use if they don't have any better approach.
|
|||
/// </summary>
|
|||
/// <param name="cinfo">An instance of <see cref="jpeg_decompress_struct"/></param>
|
|||
/// <param name="desired">The desired</param>
|
|||
/// <returns><c>false</c> if suspension is required.</returns>
|
|||
/// <remarks>That method assumes that no backtracking is possible.
|
|||
/// Some data source managers may be able to back up, or may have
|
|||
/// additional knowledge about the data which permits a more
|
|||
/// intelligent recovery strategy; such managers would
|
|||
/// presumably supply their own resync method.<br/><br/>
|
|||
///
|
|||
/// read_restart_marker calls resync_to_restart if it finds a marker other than
|
|||
/// the restart marker it was expecting. (This code is *not* used unless
|
|||
/// a nonzero restart interval has been declared.) cinfo.unread_marker is
|
|||
/// the marker code actually found (might be anything, except 0 or FF).
|
|||
/// The desired restart marker number (0..7) is passed as a parameter.<br/><br/>
|
|||
///
|
|||
/// This routine is supposed to apply whatever error recovery strategy seems
|
|||
/// appropriate in order to position the input stream to the next data segment.
|
|||
/// Note that cinfo.unread_marker is treated as a marker appearing before
|
|||
/// the current data-source input point; usually it should be reset to zero
|
|||
/// before returning.<br/><br/>
|
|||
///
|
|||
/// This implementation is substantially constrained by wanting to treat the
|
|||
/// input as a data stream; this means we can't back up. Therefore, we have
|
|||
/// only the following actions to work with:<br/>
|
|||
/// 1. Simply discard the marker and let the entropy decoder resume at next
|
|||
/// byte of file.<br/>
|
|||
/// 2. Read forward until we find another marker, discarding intervening
|
|||
/// data. (In theory we could look ahead within the current bufferload,
|
|||
/// without having to discard data if we don't find the desired marker.
|
|||
/// This idea is not implemented here, in part because it makes behavior
|
|||
/// dependent on buffer size and chance buffer-boundary positions.)<br/>
|
|||
/// 3. Leave the marker unread (by failing to zero cinfo.unread_marker).
|
|||
/// This will cause the entropy decoder to process an empty data segment,
|
|||
/// inserting dummy zeroes, and then we will reprocess the marker.<br/>
|
|||
///
|
|||
/// #2 is appropriate if we think the desired marker lies ahead, while #3 is
|
|||
/// appropriate if the found marker is a future restart marker (indicating
|
|||
/// that we have missed the desired restart marker, probably because it got
|
|||
/// corrupted).<br/>
|
|||
/// We apply #2 or #3 if the found marker is a restart marker no more than
|
|||
/// two counts behind or ahead of the expected one. We also apply #2 if the
|
|||
/// found marker is not a legal JPEG marker code (it's certainly bogus data).
|
|||
/// If the found marker is a restart marker more than 2 counts away, we do #1
|
|||
/// (too much risk that the marker is erroneous; with luck we will be able to
|
|||
/// resync at some future point).<br/>
|
|||
/// For any valid non-restart JPEG marker, we apply #3. This keeps us from
|
|||
/// overrunning the end of a scan. An implementation limited to single-scan
|
|||
/// files might find it better to apply #2 for markers other than EOI, since
|
|||
/// any other marker would have to be bogus data in that case.</remarks>
|
|||
public virtual bool resync_to_restart(jpeg_decompress_struct cinfo, int desired) |
|||
{ |
|||
/* Always put up a warning. */ |
|||
cinfo.WARNMS(J_MESSAGE_CODE.JWRN_MUST_RESYNC, cinfo.m_unread_marker, desired); |
|||
|
|||
/* Outer loop handles repeated decision after scanning forward. */ |
|||
int action = 1; |
|||
for ( ; ; ) |
|||
{ |
|||
if (cinfo.m_unread_marker < (int)JPEG_MARKER.SOF0) |
|||
{ |
|||
/* invalid marker */ |
|||
action = 2; |
|||
} |
|||
else if (cinfo.m_unread_marker < (int)JPEG_MARKER.RST0 || |
|||
cinfo.m_unread_marker > (int)JPEG_MARKER.RST7) |
|||
{ |
|||
/* valid non-restart marker */ |
|||
action = 3; |
|||
} |
|||
else |
|||
{ |
|||
if (cinfo.m_unread_marker == ((int)JPEG_MARKER.RST0 + ((desired + 1) & 7)) |
|||
|| cinfo.m_unread_marker == ((int)JPEG_MARKER.RST0 + ((desired + 2) & 7))) |
|||
{ |
|||
/* one of the next two expected restarts */ |
|||
action = 3; |
|||
} |
|||
else if (cinfo.m_unread_marker == ((int)JPEG_MARKER.RST0 + ((desired - 1) & 7)) || |
|||
cinfo.m_unread_marker == ((int)JPEG_MARKER.RST0 + ((desired - 2) & 7))) |
|||
{ |
|||
/* a prior restart, so advance */ |
|||
action = 2; |
|||
} |
|||
else |
|||
{ |
|||
/* desired restart or too far away */ |
|||
action = 1; |
|||
} |
|||
} |
|||
|
|||
cinfo.TRACEMS(4, J_MESSAGE_CODE.JTRC_RECOVERY_ACTION, cinfo.m_unread_marker, action); |
|||
|
|||
switch (action) |
|||
{ |
|||
case 1: |
|||
/* Discard marker and let entropy decoder resume processing. */ |
|||
cinfo.m_unread_marker = 0; |
|||
return true; |
|||
case 2: |
|||
/* Scan to the next marker, and repeat the decision loop. */ |
|||
if (!cinfo.m_marker.next_marker()) |
|||
return false; |
|||
break; |
|||
case 3: |
|||
/* Return without advancing past this marker. */ |
|||
/* Entropy decoder will be forced to process an empty segment. */ |
|||
return true; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Terminate source - called by jpeg_finish_decompress
|
|||
/// after all data has been read. Often a no-op.
|
|||
/// </summary>
|
|||
/// <remarks>NB: <b>not</b> called by jpeg_abort or jpeg_destroy; surrounding
|
|||
/// application must deal with any cleanup that should happen even
|
|||
/// for error exit.</remarks>
|
|||
public virtual void term_source() |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reads two bytes interpreted as an unsigned 16-bit integer.
|
|||
/// </summary>
|
|||
/// <param name="V">The result.</param>
|
|||
/// <returns><c>true</c> if operation succeed; otherwise, <c>false</c></returns>
|
|||
public virtual bool GetTwoBytes(out int V) |
|||
{ |
|||
if (!MakeByteAvailable()) |
|||
{ |
|||
V = 0; |
|||
return false; |
|||
} |
|||
|
|||
m_bytes_in_buffer--; |
|||
V = m_next_input_byte[m_position] << 8; |
|||
m_position++; |
|||
|
|||
if (!MakeByteAvailable()) |
|||
return false; |
|||
|
|||
m_bytes_in_buffer--; |
|||
V += m_next_input_byte[m_position]; |
|||
m_position++; |
|||
return true; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Read a byte into variable V.
|
|||
/// If must suspend, take the specified action (typically "return false").
|
|||
/// </summary>
|
|||
/// <param name="V">The result.</param>
|
|||
/// <returns><c>true</c> if operation succeed; otherwise, <c>false</c></returns>
|
|||
public virtual bool GetByte(out int V) |
|||
{ |
|||
if (!MakeByteAvailable()) |
|||
{ |
|||
V = 0; |
|||
return false; |
|||
} |
|||
|
|||
m_bytes_in_buffer--; |
|||
V = m_next_input_byte[m_position]; |
|||
m_position++; |
|||
return true; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the bytes.
|
|||
/// </summary>
|
|||
/// <param name="dest">The destination.</param>
|
|||
/// <param name="amount">The amount.</param>
|
|||
/// <returns>The number of available bytes.</returns>
|
|||
public virtual int GetBytes(byte[] dest, int amount) |
|||
{ |
|||
int avail = amount; |
|||
if (avail > m_bytes_in_buffer) |
|||
avail = m_bytes_in_buffer; |
|||
|
|||
for (int i = 0; i < avail; i++) |
|||
{ |
|||
dest[i] = m_next_input_byte[m_position]; |
|||
m_position++; |
|||
m_bytes_in_buffer--; |
|||
} |
|||
|
|||
return avail; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Functions for fetching data from the data source module.
|
|||
/// </summary>
|
|||
/// <returns><c>true</c> if operation succeed; otherwise, <c>false</c></returns>
|
|||
/// <remarks>At all times, cinfo.src.next_input_byte and .bytes_in_buffer reflect
|
|||
/// the current restart point; we update them only when we have reached a
|
|||
/// suitable place to restart if a suspension occurs.</remarks>
|
|||
public virtual bool MakeByteAvailable() |
|||
{ |
|||
if (m_bytes_in_buffer == 0) |
|||
{ |
|||
if (!fill_input_buffer()) |
|||
return false; |
|||
} |
|||
|
|||
return true; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,105 @@ |
|||
/* Copyright (C) 2008-2011, Bit Miracle |
|||
* http://www.bitmiracle.com
|
|||
* |
|||
* Copyright (C) 1994-1996, Thomas G. Lane. |
|||
* This file is part of the Independent JPEG Group's software. |
|||
* For conditions of distribution and use, see the accompanying README file. |
|||
* |
|||
*/ |
|||
|
|||
/* |
|||
* This file contains the JPEG system-independent memory management |
|||
* routines. |
|||
*/ |
|||
|
|||
/* |
|||
* About virtual array management: |
|||
* |
|||
* Full-image-sized buffers are handled as "virtual" arrays. The array is still accessed a strip at a |
|||
* time, but the memory manager must save the whole array for repeated |
|||
* accesses. |
|||
* |
|||
* The Access method is responsible for making a specific strip area accessible. |
|||
*/ |
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Diagnostics; |
|||
using System.Text; |
|||
|
|||
namespace BitMiracle.LibJpeg.Classic |
|||
{ |
|||
/// <summary>
|
|||
/// JPEG virtual array.
|
|||
/// </summary>
|
|||
/// <typeparam name="T">The type of array's elements.</typeparam>
|
|||
/// <remarks>You can't create virtual array manually. For creation use methods
|
|||
/// <see cref="jpeg_common_struct.CreateSamplesArray"/> and
|
|||
/// <see cref="jpeg_common_struct.CreateBlocksArray"/>.
|
|||
/// </remarks>
|
|||
#if EXPOSE_LIBJPEG
|
|||
public |
|||
#endif
|
|||
class jvirt_array<T> |
|||
{ |
|||
internal delegate T[][] Allocator(int width, int height); |
|||
|
|||
private jpeg_common_struct m_cinfo; |
|||
|
|||
private T[][] m_buffer; /* => the in-memory buffer */ |
|||
|
|||
/// <summary>
|
|||
/// Request a virtual 2-D array
|
|||
/// </summary>
|
|||
/// <param name="width">Width of array</param>
|
|||
/// <param name="height">Total virtual array height</param>
|
|||
/// <param name="allocator">The allocator.</param>
|
|||
internal jvirt_array(int width, int height, Allocator allocator) |
|||
{ |
|||
m_cinfo = null; |
|||
m_buffer = allocator(width, height); |
|||
|
|||
Debug.Assert(m_buffer != null); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the error processor.
|
|||
/// </summary>
|
|||
/// <value>The error processor.<br/>
|
|||
/// Default value: <c>null</c>
|
|||
/// </value>
|
|||
/// <remarks>Uses only for calling
|
|||
/// <see cref="M:BitMiracle.LibJpeg.Classic.jpeg_common_struct.ERREXIT(BitMiracle.LibJpeg.Classic.J_MESSAGE_CODE)">jpeg_common_struct.ERREXIT</see>
|
|||
/// on error.</remarks>
|
|||
public jpeg_common_struct ErrorProcessor |
|||
{ |
|||
get { return m_cinfo; } |
|||
set { m_cinfo = value; } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Access the part of a virtual array.
|
|||
/// </summary>
|
|||
/// <param name="startRow">The first row in required block.</param>
|
|||
/// <param name="numberOfRows">The number of required rows.</param>
|
|||
/// <returns>The required part of virtual array.</returns>
|
|||
public T[][] Access(int startRow, int numberOfRows) |
|||
{ |
|||
/* debugging check */ |
|||
if (startRow + numberOfRows > m_buffer.Length) |
|||
{ |
|||
if (m_cinfo != null) |
|||
m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_VIRTUAL_ACCESS); |
|||
else |
|||
throw new InvalidOperationException("Bogus virtual array access"); |
|||
} |
|||
|
|||
/* Return proper part of the buffer */ |
|||
T[][] ret = new T[numberOfRows][]; |
|||
for (int i = 0; i < numberOfRows; i++) |
|||
ret[i] = m_buffer[startRow + i]; |
|||
|
|||
return ret; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,109 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
|
|||
using BitMiracle.LibJpeg.Classic; |
|||
|
|||
namespace BitMiracle.LibJpeg |
|||
{ |
|||
/// <summary>
|
|||
/// Parameters of compression.
|
|||
/// </summary>
|
|||
/// <remarks>Being used in <see cref="M:BitMiracle.LibJpeg.JpegImage.WriteJpeg(System.IO.Stream,BitMiracle.LibJpeg.CompressionParameters)"/></remarks>
|
|||
#if EXPOSE_LIBJPEG
|
|||
public |
|||
#endif
|
|||
class CompressionParameters |
|||
{ |
|||
private int m_quality = 75; |
|||
private int m_smoothingFactor; |
|||
private bool m_simpleProgressive; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="CompressionParameters"/> class.
|
|||
/// </summary>
|
|||
public CompressionParameters() |
|||
{ |
|||
} |
|||
|
|||
internal CompressionParameters(CompressionParameters parameters) |
|||
{ |
|||
if (parameters == null) |
|||
throw new ArgumentNullException("parameters"); |
|||
|
|||
m_quality = parameters.m_quality; |
|||
m_smoothingFactor = parameters.m_smoothingFactor; |
|||
m_simpleProgressive = parameters.m_simpleProgressive; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Determines whether the specified <see cref="System.Object"/> is equal to this instance.
|
|||
/// </summary>
|
|||
/// <param name="obj">The <see cref="System.Object"/> to compare with this instance.</param>
|
|||
/// <returns>
|
|||
/// <c>true</c> if the specified <see cref="System.Object"/> is equal to this instance; otherwise, <c>false</c>.
|
|||
/// </returns>
|
|||
public override bool Equals(object obj) |
|||
{ |
|||
CompressionParameters parameters = obj as CompressionParameters; |
|||
if (parameters == null) |
|||
return false; |
|||
|
|||
return (m_quality == parameters.m_quality && |
|||
m_smoothingFactor == parameters.m_smoothingFactor && |
|||
m_simpleProgressive == parameters.m_simpleProgressive); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns a hash code for this instance.
|
|||
/// </summary>
|
|||
/// <returns>
|
|||
/// A hash code for this instance, suitable for use in hashing algorithms
|
|||
/// and data structures like a hash table.
|
|||
/// </returns>
|
|||
public override int GetHashCode() |
|||
{ |
|||
return base.GetHashCode(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the quality of JPEG image.
|
|||
/// </summary>
|
|||
/// <remarks>Default value: 75<br/>
|
|||
/// The quality value is expressed on the 0..100 scale.
|
|||
/// </remarks>
|
|||
/// <value>The quality of JPEG image.</value>
|
|||
public int Quality |
|||
{ |
|||
get { return m_quality; } |
|||
set { m_quality = value; } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the coefficient of image smoothing.
|
|||
/// </summary>
|
|||
/// <remarks>Default value: 0<br/>
|
|||
/// If non-zero, the input image is smoothed; the value should be 1 for
|
|||
/// minimal smoothing to 100 for maximum smoothing.
|
|||
/// </remarks>
|
|||
/// <value>The coefficient of image smoothing.</value>
|
|||
public int SmoothingFactor |
|||
{ |
|||
get { return m_smoothingFactor; } |
|||
set { m_smoothingFactor = value; } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets a value indicating whether to write a progressive-JPEG file.
|
|||
/// </summary>
|
|||
/// <value>
|
|||
/// <c>true</c> for writing a progressive-JPEG file; <c>false</c>
|
|||
/// for non-progressive JPEG files.
|
|||
/// </value>
|
|||
public bool SimpleProgressive |
|||
{ |
|||
get { return m_simpleProgressive; } |
|||
set { m_simpleProgressive = value; } |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,243 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
|
|||
using BitMiracle.LibJpeg.Classic; |
|||
|
|||
namespace BitMiracle.LibJpeg |
|||
{ |
|||
class DecompressionParameters |
|||
{ |
|||
private Colorspace m_outColorspace = Colorspace.Unknown; |
|||
private int m_scaleNumerator = 1; |
|||
private int m_scaleDenominator = 1; |
|||
private bool m_bufferedImage; |
|||
private bool m_rawDataOut; |
|||
private DCTMethod m_dctMethod = (DCTMethod)JpegConstants.JDCT_DEFAULT; |
|||
private DitherMode m_ditherMode = DitherMode.FloydSteinberg; |
|||
private bool m_doFancyUpsampling = true; |
|||
private bool m_doBlockSmoothing = true; |
|||
private bool m_quantizeColors; |
|||
private bool m_twoPassQuantize = true; |
|||
private int m_desiredNumberOfColors = 256; |
|||
private bool m_enableOnePassQuantizer; |
|||
private bool m_enableExternalQuant; |
|||
private bool m_enableTwoPassQuantizer; |
|||
private int m_traceLevel; |
|||
|
|||
public int TraceLevel |
|||
{ |
|||
get |
|||
{ |
|||
return m_traceLevel; |
|||
} |
|||
set |
|||
{ |
|||
m_traceLevel = value; |
|||
} |
|||
} |
|||
|
|||
/* Decompression processing parameters --- these fields must be set before |
|||
* calling jpeg_start_decompress(). Note that jpeg_read_header() initializes |
|||
* them to default values. |
|||
*/ |
|||
|
|||
// colorspace for output
|
|||
public Colorspace OutColorspace |
|||
{ |
|||
get |
|||
{ |
|||
return m_outColorspace; |
|||
} |
|||
set |
|||
{ |
|||
m_outColorspace = value; |
|||
} |
|||
} |
|||
|
|||
// fraction by which to scale image
|
|||
public int ScaleNumerator |
|||
{ |
|||
get |
|||
{ |
|||
return m_scaleNumerator; |
|||
} |
|||
set |
|||
{ |
|||
m_scaleNumerator = value; |
|||
} |
|||
} |
|||
|
|||
public int ScaleDenominator |
|||
{ |
|||
get |
|||
{ |
|||
return m_scaleDenominator; |
|||
} |
|||
set |
|||
{ |
|||
m_scaleDenominator = value; |
|||
} |
|||
} |
|||
|
|||
// true=multiple output passes
|
|||
public bool BufferedImage |
|||
{ |
|||
get |
|||
{ |
|||
return m_bufferedImage; |
|||
} |
|||
set |
|||
{ |
|||
m_bufferedImage = value; |
|||
} |
|||
} |
|||
|
|||
// true=downsampled data wanted
|
|||
public bool RawDataOut |
|||
{ |
|||
get |
|||
{ |
|||
return m_rawDataOut; |
|||
} |
|||
set |
|||
{ |
|||
m_rawDataOut = value; |
|||
} |
|||
} |
|||
|
|||
// IDCT algorithm selector
|
|||
public DCTMethod DCTMethod |
|||
{ |
|||
get |
|||
{ |
|||
return m_dctMethod; |
|||
} |
|||
set |
|||
{ |
|||
m_dctMethod = value; |
|||
} |
|||
} |
|||
|
|||
// true=apply fancy upsampling
|
|||
public bool DoFancyUpsampling |
|||
{ |
|||
get |
|||
{ |
|||
return m_doFancyUpsampling; |
|||
} |
|||
set |
|||
{ |
|||
m_doFancyUpsampling = value; |
|||
} |
|||
} |
|||
|
|||
// true=apply interblock smoothing
|
|||
public bool DoBlockSmoothing |
|||
{ |
|||
get |
|||
{ |
|||
return m_doBlockSmoothing; |
|||
} |
|||
set |
|||
{ |
|||
m_doBlockSmoothing = value; |
|||
} |
|||
} |
|||
|
|||
// true=colormapped output wanted
|
|||
public bool QuantizeColors |
|||
{ |
|||
get |
|||
{ |
|||
return m_quantizeColors; |
|||
} |
|||
set |
|||
{ |
|||
m_quantizeColors = value; |
|||
} |
|||
} |
|||
|
|||
/* the following are ignored if not quantize_colors: */ |
|||
|
|||
// type of color dithering to use
|
|||
public DitherMode DitherMode |
|||
{ |
|||
get |
|||
{ |
|||
return m_ditherMode; |
|||
} |
|||
set |
|||
{ |
|||
m_ditherMode = value; |
|||
} |
|||
} |
|||
|
|||
// true=use two-pass color quantization
|
|||
public bool TwoPassQuantize |
|||
{ |
|||
get |
|||
{ |
|||
return m_twoPassQuantize; |
|||
} |
|||
set |
|||
{ |
|||
m_twoPassQuantize = value; |
|||
} |
|||
} |
|||
|
|||
// max # colors to use in created colormap
|
|||
public int DesiredNumberOfColors |
|||
{ |
|||
get |
|||
{ |
|||
return m_desiredNumberOfColors; |
|||
} |
|||
set |
|||
{ |
|||
m_desiredNumberOfColors = value; |
|||
} |
|||
} |
|||
|
|||
/* these are significant only in buffered-image mode: */ |
|||
|
|||
// enable future use of 1-pass quantizer
|
|||
public bool EnableOnePassQuantizer |
|||
{ |
|||
get |
|||
{ |
|||
return m_enableOnePassQuantizer; |
|||
} |
|||
set |
|||
{ |
|||
m_enableOnePassQuantizer = value; |
|||
} |
|||
} |
|||
|
|||
// enable future use of external colormap
|
|||
public bool EnableExternalQuant |
|||
{ |
|||
get |
|||
{ |
|||
return m_enableExternalQuant; |
|||
} |
|||
set |
|||
{ |
|||
m_enableExternalQuant = value; |
|||
} |
|||
} |
|||
|
|||
// enable future use of 2-pass quantizer
|
|||
public bool EnableTwoPassQuantizer |
|||
{ |
|||
get |
|||
{ |
|||
return m_enableTwoPassQuantizer; |
|||
} |
|||
set |
|||
{ |
|||
m_enableTwoPassQuantizer = value; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,57 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.IO; |
|||
using System.Text; |
|||
using Nine.Imaging; |
|||
|
|||
namespace BitMiracle.LibJpeg |
|||
{ |
|||
using ImageProcessor; |
|||
|
|||
class DecompressorToJpegImage : IDecompressDestination |
|||
{ |
|||
private JpegImage m_jpegImage; |
|||
|
|||
internal DecompressorToJpegImage(JpegImage jpegImage) |
|||
{ |
|||
m_jpegImage = jpegImage; |
|||
} |
|||
|
|||
public Stream Output |
|||
{ |
|||
get |
|||
{ |
|||
return null; |
|||
} |
|||
} |
|||
|
|||
public void SetImageAttributes(LoadedImageAttributes parameters) |
|||
{ |
|||
if (parameters.Width > ImageBase.MaxWidth || parameters.Height > ImageBase.MaxHeight) |
|||
{ |
|||
throw new ArgumentOutOfRangeException( |
|||
$"The input jpg '{ parameters.Width }x{ parameters.Height }' is bigger then the max allowed size '{ ImageBase.MaxWidth }x{ ImageBase.MaxHeight }'"); |
|||
} |
|||
|
|||
m_jpegImage.Width = parameters.Width; |
|||
m_jpegImage.Height = parameters.Height; |
|||
m_jpegImage.BitsPerComponent = 8; |
|||
m_jpegImage.ComponentsPerSample = (byte)parameters.ComponentsPerSample; |
|||
m_jpegImage.Colorspace = parameters.Colorspace; |
|||
} |
|||
|
|||
public void BeginWrite() |
|||
{ |
|||
} |
|||
|
|||
public void ProcessPixelsRow(byte[] row) |
|||
{ |
|||
SampleRow samplesRow = new SampleRow(row, m_jpegImage.Width, m_jpegImage.BitsPerComponent, m_jpegImage.ComponentsPerSample); |
|||
m_jpegImage.addSampleRow(samplesRow); |
|||
} |
|||
|
|||
public void EndWrite() |
|||
{ |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,65 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
|
|||
namespace BitMiracle.LibJpeg |
|||
{ |
|||
/// <summary>
|
|||
/// Known color spaces.
|
|||
/// </summary>
|
|||
#if EXPOSE_LIBJPEG
|
|||
public |
|||
#endif
|
|||
enum Colorspace |
|||
{ |
|||
/// <summary>
|
|||
/// Unspecified colorspace
|
|||
/// </summary>
|
|||
Unknown, |
|||
|
|||
/// <summary>
|
|||
/// Grayscale
|
|||
/// </summary>
|
|||
Grayscale, |
|||
|
|||
/// <summary>
|
|||
/// RGB
|
|||
/// </summary>
|
|||
RGB, |
|||
|
|||
/// <summary>
|
|||
/// YCbCr (also known as YUV)
|
|||
/// </summary>
|
|||
YCbCr, |
|||
|
|||
/// <summary>
|
|||
/// CMYK
|
|||
/// </summary>
|
|||
CMYK, |
|||
|
|||
/// <summary>
|
|||
/// YCbCrK
|
|||
/// </summary>
|
|||
YCCK |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// DCT/IDCT algorithm options.
|
|||
/// </summary>
|
|||
enum DCTMethod |
|||
{ |
|||
IntegerSlow, /* slow but accurate integer algorithm */ |
|||
IntegerFast, /* faster, less accurate integer method */ |
|||
Float /* floating-point: accurate, fast on fast HW */ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Dithering options for decompression.
|
|||
/// </summary>
|
|||
enum DitherMode |
|||
{ |
|||
None, /* no dithering */ |
|||
Ordered, /* simple ordered dither */ |
|||
FloydSteinberg /* Floyd-Steinberg error diffusion dither */ |
|||
} |
|||
} |
|||
@ -0,0 +1,228 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.IO; |
|||
using System.Text; |
|||
|
|||
using BitMiracle.LibJpeg.Classic; |
|||
|
|||
namespace BitMiracle.LibJpeg |
|||
{ |
|||
/// <summary>
|
|||
/// Common interface for processing of decompression.
|
|||
/// </summary>
|
|||
interface IDecompressDestination |
|||
{ |
|||
/// <summary>
|
|||
/// Strean with decompressed data
|
|||
/// </summary>
|
|||
Stream Output |
|||
{ |
|||
get; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Implementor of this interface should process image properties received from decompressor.
|
|||
/// </summary>
|
|||
/// <param name="parameters">Image properties</param>
|
|||
void SetImageAttributes(LoadedImageAttributes parameters); |
|||
|
|||
/// <summary>
|
|||
/// Called before decompression
|
|||
/// </summary>
|
|||
void BeginWrite(); |
|||
|
|||
/// <summary>
|
|||
/// It called during decompression - pass row of pixels from JPEG
|
|||
/// </summary>
|
|||
/// <param name="row"></param>
|
|||
void ProcessPixelsRow(byte[] row); |
|||
|
|||
/// <summary>
|
|||
/// Called after decompression
|
|||
/// </summary>
|
|||
void EndWrite(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Holds parameters of image for decompression (IDecomressDesination)
|
|||
/// </summary>
|
|||
class LoadedImageAttributes |
|||
{ |
|||
private Colorspace m_colorspace; |
|||
private bool m_quantizeColors; |
|||
private int m_width; |
|||
private int m_height; |
|||
private int m_componentsPerSample; |
|||
private int m_components; |
|||
private int m_actualNumberOfColors; |
|||
private byte[][] m_colormap; |
|||
private DensityUnit m_densityUnit; |
|||
private int m_densityX; |
|||
private int m_densityY; |
|||
|
|||
/* Decompression processing parameters --- these fields must be set before |
|||
* calling jpeg_start_decompress(). Note that jpeg_read_header() initializes |
|||
* them to default values. |
|||
*/ |
|||
|
|||
// colorspace for output
|
|||
public Colorspace Colorspace |
|||
{ |
|||
get |
|||
{ |
|||
return m_colorspace; |
|||
} |
|||
internal set |
|||
{ |
|||
m_colorspace = value; |
|||
} |
|||
} |
|||
|
|||
// true=colormapped output wanted
|
|||
public bool QuantizeColors |
|||
{ |
|||
get |
|||
{ |
|||
return m_quantizeColors; |
|||
} |
|||
internal set |
|||
{ |
|||
m_quantizeColors = value; |
|||
} |
|||
} |
|||
|
|||
/* Description of actual output image that will be returned to application. |
|||
* These fields are computed by jpeg_start_decompress(). |
|||
* You can also use jpeg_calc_output_dimensions() to determine these values |
|||
* in advance of calling jpeg_start_decompress(). |
|||
*/ |
|||
|
|||
// scaled image width
|
|||
public int Width |
|||
{ |
|||
get |
|||
{ |
|||
return m_width; |
|||
} |
|||
internal set |
|||
{ |
|||
m_width = value; |
|||
} |
|||
} |
|||
|
|||
// scaled image height
|
|||
public int Height |
|||
{ |
|||
get |
|||
{ |
|||
return m_height; |
|||
} |
|||
internal set |
|||
{ |
|||
m_height = value; |
|||
} |
|||
} |
|||
|
|||
// # of color components in out_color_space
|
|||
public int ComponentsPerSample |
|||
{ |
|||
get |
|||
{ |
|||
return m_componentsPerSample; |
|||
} |
|||
internal set |
|||
{ |
|||
m_componentsPerSample = value; |
|||
} |
|||
} |
|||
|
|||
// # of color components returned. it is 1 (a colormap index) when
|
|||
// quantizing colors; otherwise it equals out_color_components.
|
|||
public int Components |
|||
{ |
|||
get |
|||
{ |
|||
return m_components; |
|||
} |
|||
internal set |
|||
{ |
|||
m_components = value; |
|||
} |
|||
} |
|||
|
|||
/* When quantizing colors, the output colormap is described by these fields. |
|||
* The application can supply a colormap by setting colormap non-null before |
|||
* calling jpeg_start_decompress; otherwise a colormap is created during |
|||
* jpeg_start_decompress or jpeg_start_output. |
|||
* The map has out_color_components rows and actual_number_of_colors columns. |
|||
*/ |
|||
|
|||
// number of entries in use
|
|||
public int ActualNumberOfColors |
|||
{ |
|||
get |
|||
{ |
|||
return m_actualNumberOfColors; |
|||
} |
|||
internal set |
|||
{ |
|||
m_actualNumberOfColors = value; |
|||
} |
|||
} |
|||
|
|||
// The color map as a 2-D pixel array
|
|||
public byte[][] Colormap |
|||
{ |
|||
get |
|||
{ |
|||
return m_colormap; |
|||
} |
|||
internal set |
|||
{ |
|||
m_colormap = value; |
|||
} |
|||
} |
|||
|
|||
// These fields record data obtained from optional markers
|
|||
// recognized by the JPEG library.
|
|||
|
|||
// JFIF code for pixel size units
|
|||
public DensityUnit DensityUnit |
|||
{ |
|||
get |
|||
{ |
|||
return m_densityUnit; |
|||
} |
|||
internal set |
|||
{ |
|||
m_densityUnit = value; |
|||
} |
|||
} |
|||
|
|||
// Horizontal pixel density
|
|||
public int DensityX |
|||
{ |
|||
get |
|||
{ |
|||
return m_densityX; |
|||
} |
|||
internal set |
|||
{ |
|||
m_densityX = value; |
|||
} |
|||
} |
|||
|
|||
// Vertical pixel density
|
|||
public int DensityY |
|||
{ |
|||
get |
|||
{ |
|||
return m_densityY; |
|||
} |
|||
internal set |
|||
{ |
|||
m_densityY = value; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,21 @@ |
|||
namespace BitMiracle.LibJpeg |
|||
{ |
|||
interface IRawImage |
|||
{ |
|||
int Width |
|||
{ get; } |
|||
|
|||
int Height |
|||
{ get; } |
|||
|
|||
Colorspace Colorspace |
|||
{ get; } |
|||
|
|||
int ComponentsPerPixel |
|||
{ get; } |
|||
|
|||
void BeginRead(); |
|||
byte[] GetPixelRow(); |
|||
void EndRead(); |
|||
} |
|||
} |
|||
@ -0,0 +1,247 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Diagnostics; |
|||
using System.IO; |
|||
using System.Text; |
|||
|
|||
using BitMiracle.LibJpeg.Classic; |
|||
|
|||
namespace BitMiracle.LibJpeg |
|||
{ |
|||
/// <summary>
|
|||
/// Internal wrapper for classic jpeg compressor and decompressor
|
|||
/// </summary>
|
|||
class Jpeg |
|||
{ |
|||
private jpeg_compress_struct m_compressor = new jpeg_compress_struct(new jpeg_error_mgr()); |
|||
private jpeg_decompress_struct m_decompressor = new jpeg_decompress_struct(new jpeg_error_mgr()); |
|||
|
|||
private CompressionParameters m_compressionParameters = new CompressionParameters(); |
|||
private DecompressionParameters m_decompressionParameters = new DecompressionParameters(); |
|||
|
|||
/// <summary>
|
|||
/// Advanced users may set specific parameters of compression
|
|||
/// </summary>
|
|||
public CompressionParameters CompressionParameters |
|||
{ |
|||
get |
|||
{ |
|||
return m_compressionParameters; |
|||
} |
|||
set |
|||
{ |
|||
if (value == null) |
|||
throw new ArgumentNullException("value"); |
|||
|
|||
m_compressionParameters = value; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Advanced users may set specific parameters of decompression
|
|||
/// </summary>
|
|||
public DecompressionParameters DecompressionParameters |
|||
{ |
|||
get |
|||
{ |
|||
return m_decompressionParameters; |
|||
} |
|||
set |
|||
{ |
|||
if (value == null) |
|||
throw new ArgumentNullException("value"); |
|||
|
|||
m_decompressionParameters = value; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Compresses any image described as ICompressSource to JPEG
|
|||
/// </summary>
|
|||
/// <param name="source">Contains description of input image</param>
|
|||
/// <param name="output">Stream for output of compressed JPEG</param>
|
|||
public void Compress(IRawImage source, Stream output) |
|||
{ |
|||
if (source == null) |
|||
throw new ArgumentNullException("source"); |
|||
|
|||
if (output == null) |
|||
throw new ArgumentNullException("output"); |
|||
|
|||
m_compressor.Image_width = source.Width; |
|||
m_compressor.Image_height = source.Height; |
|||
m_compressor.In_color_space = (J_COLOR_SPACE)source.Colorspace; |
|||
m_compressor.Input_components = source.ComponentsPerPixel; |
|||
//m_compressor.Data_precision = source.DataPrecision;
|
|||
|
|||
m_compressor.jpeg_set_defaults(); |
|||
|
|||
//we need to set density parameters after setting of default jpeg parameters
|
|||
//m_compressor.Density_unit = source.DensityUnit;
|
|||
//m_compressor.X_density = (short)source.DensityX;
|
|||
//m_compressor.Y_density = (short)source.DensityY;
|
|||
|
|||
applyParameters(m_compressionParameters); |
|||
|
|||
// Specify data destination for compression
|
|||
m_compressor.jpeg_stdio_dest(output); |
|||
|
|||
// Start compression
|
|||
m_compressor.jpeg_start_compress(true); |
|||
|
|||
// Process pixels
|
|||
source.BeginRead(); |
|||
while (m_compressor.Next_scanline < m_compressor.Image_height) |
|||
{ |
|||
byte[] row = source.GetPixelRow(); |
|||
if (row == null) |
|||
{ |
|||
#if !SILVERLIGHT
|
|||
throw new InvalidDataException("Row of pixels is null"); |
|||
#else
|
|||
// System.IO.InvalidDataException is not available in Silverlight
|
|||
throw new IOException("Row of pixels is null"); |
|||
#endif
|
|||
} |
|||
|
|||
byte[][] rowForDecompressor = new byte[1][]; |
|||
rowForDecompressor[0] = row; |
|||
m_compressor.jpeg_write_scanlines(rowForDecompressor, 1); |
|||
} |
|||
source.EndRead(); |
|||
|
|||
// Finish compression and release memory
|
|||
m_compressor.jpeg_finish_compress(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Decompresses JPEG image to any image described as ICompressDestination
|
|||
/// </summary>
|
|||
/// <param name="jpeg">Stream with JPEG data</param>
|
|||
/// <param name="destination">Stream for output of compressed JPEG</param>
|
|||
public void Decompress(Stream jpeg, IDecompressDestination destination) |
|||
{ |
|||
if (jpeg == null) |
|||
throw new ArgumentNullException("jpeg"); |
|||
|
|||
if (destination == null) |
|||
throw new ArgumentNullException("destination"); |
|||
|
|||
beforeDecompress(jpeg); |
|||
|
|||
// Start decompression
|
|||
m_decompressor.jpeg_start_decompress(); |
|||
|
|||
LoadedImageAttributes parameters = getImageParametersFromDecompressor(); |
|||
destination.SetImageAttributes(parameters); |
|||
destination.BeginWrite(); |
|||
|
|||
/* Process data */ |
|||
while (m_decompressor.Output_scanline < m_decompressor.Output_height) |
|||
{ |
|||
byte[][] row = jpeg_common_struct.AllocJpegSamples(m_decompressor.Output_width * m_decompressor.Output_components, 1); |
|||
m_decompressor.jpeg_read_scanlines(row, 1); |
|||
destination.ProcessPixelsRow(row[0]); |
|||
} |
|||
|
|||
destination.EndWrite(); |
|||
|
|||
// Finish decompression and release memory.
|
|||
m_decompressor.jpeg_finish_decompress(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Tunes decompressor
|
|||
/// </summary>
|
|||
/// <param name="jpeg">Stream with input compressed JPEG data</param>
|
|||
private void beforeDecompress(Stream jpeg) |
|||
{ |
|||
m_decompressor.jpeg_stdio_src(jpeg); |
|||
/* Read file header, set default decompression parameters */ |
|||
m_decompressor.jpeg_read_header(true); |
|||
|
|||
applyParameters(m_decompressionParameters); |
|||
m_decompressor.jpeg_calc_output_dimensions(); |
|||
} |
|||
|
|||
private LoadedImageAttributes getImageParametersFromDecompressor() |
|||
{ |
|||
LoadedImageAttributes result = new LoadedImageAttributes(); |
|||
result.Colorspace = (Colorspace)m_decompressor.Out_color_space; |
|||
result.QuantizeColors = m_decompressor.Quantize_colors; |
|||
result.Width = m_decompressor.Output_width; |
|||
result.Height = m_decompressor.Output_height; |
|||
result.ComponentsPerSample = m_decompressor.Out_color_components; |
|||
result.Components = m_decompressor.Output_components; |
|||
result.ActualNumberOfColors = m_decompressor.Actual_number_of_colors; |
|||
result.Colormap = m_decompressor.Colormap; |
|||
result.DensityUnit = m_decompressor.Density_unit; |
|||
result.DensityX = m_decompressor.X_density; |
|||
result.DensityY = m_decompressor.Y_density; |
|||
return result; |
|||
} |
|||
|
|||
public jpeg_compress_struct ClassicCompressor |
|||
{ |
|||
get |
|||
{ |
|||
return m_compressor; |
|||
} |
|||
} |
|||
|
|||
public jpeg_decompress_struct ClassicDecompressor |
|||
{ |
|||
get |
|||
{ |
|||
return m_decompressor; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Delegate for application-supplied marker processing methods.
|
|||
/// Need not pass marker code since it is stored in cinfo.unread_marker.
|
|||
/// </summary>
|
|||
public delegate bool MarkerParser(Jpeg decompressor); |
|||
|
|||
/* Install a special processing method for COM or APPn markers. */ |
|||
public void SetMarkerProcessor(int markerCode, MarkerParser routine) |
|||
{ |
|||
jpeg_decompress_struct.jpeg_marker_parser_method f = delegate { return routine(this); }; |
|||
m_decompressor.jpeg_set_marker_processor(markerCode, f); |
|||
} |
|||
|
|||
private void applyParameters(DecompressionParameters parameters) |
|||
{ |
|||
Debug.Assert(parameters != null); |
|||
|
|||
if (parameters.OutColorspace != Colorspace.Unknown) |
|||
m_decompressor.Out_color_space = (J_COLOR_SPACE)parameters.OutColorspace; |
|||
|
|||
m_decompressor.Scale_num = parameters.ScaleNumerator; |
|||
m_decompressor.Scale_denom = parameters.ScaleDenominator; |
|||
m_decompressor.Buffered_image = parameters.BufferedImage; |
|||
m_decompressor.Raw_data_out = parameters.RawDataOut; |
|||
m_decompressor.Dct_method = (J_DCT_METHOD)parameters.DCTMethod; |
|||
m_decompressor.Dither_mode = (J_DITHER_MODE)parameters.DitherMode; |
|||
m_decompressor.Do_fancy_upsampling = parameters.DoFancyUpsampling; |
|||
m_decompressor.Do_block_smoothing = parameters.DoBlockSmoothing; |
|||
m_decompressor.Quantize_colors = parameters.QuantizeColors; |
|||
m_decompressor.Two_pass_quantize = parameters.TwoPassQuantize; |
|||
m_decompressor.Desired_number_of_colors = parameters.DesiredNumberOfColors; |
|||
m_decompressor.Enable_1pass_quant = parameters.EnableOnePassQuantizer; |
|||
m_decompressor.Enable_external_quant = parameters.EnableExternalQuant; |
|||
m_decompressor.Enable_2pass_quant = parameters.EnableTwoPassQuantizer; |
|||
m_decompressor.Err.Trace_level = parameters.TraceLevel; |
|||
} |
|||
|
|||
private void applyParameters(CompressionParameters parameters) |
|||
{ |
|||
Debug.Assert(parameters != null); |
|||
|
|||
m_compressor.Smoothing_factor = parameters.SmoothingFactor; |
|||
m_compressor.jpeg_set_quality(parameters.Quality, true); |
|||
if (parameters.SimpleProgressive) |
|||
m_compressor.jpeg_simple_progression(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,360 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Diagnostics; |
|||
using System.IO; |
|||
|
|||
using BitMiracle.LibJpeg.Classic; |
|||
|
|||
namespace BitMiracle.LibJpeg |
|||
{ |
|||
/// <summary>
|
|||
/// Main class for work with JPEG images.
|
|||
/// </summary>
|
|||
#if EXPOSE_LIBJPEG
|
|||
public |
|||
#endif
|
|||
sealed class JpegImage : IDisposable |
|||
{ |
|||
private bool m_alreadyDisposed; |
|||
|
|||
/// <summary>
|
|||
/// Description of image pixels (samples)
|
|||
/// </summary>
|
|||
private List<SampleRow> m_rows = new List<SampleRow>(); |
|||
|
|||
private int m_width; |
|||
private int m_height; |
|||
private byte m_bitsPerComponent; |
|||
private byte m_componentsPerSample; |
|||
private Colorspace m_colorspace; |
|||
|
|||
// Fields below (m_compressedData, m_decompressedData, m_bitmap) are not initialized in constructors necessarily.
|
|||
// Instead direct access to these field you should use corresponding properties (compressedData, decompressedData, bitmap)
|
|||
// Such agreement allows to load required data (e.g. compress image) only by request.
|
|||
|
|||
/// <summary>
|
|||
/// Bytes of jpeg image. Refreshed when m_compressionParameters changed.
|
|||
/// </summary>
|
|||
private MemoryStream m_compressedData; |
|||
|
|||
/// <summary>
|
|||
/// Current compression parameters corresponding with compressed data.
|
|||
/// </summary>
|
|||
private CompressionParameters m_compressionParameters; |
|||
|
|||
/// <summary>
|
|||
/// Bytes of decompressed image (bitmap)
|
|||
/// </summary>
|
|||
private MemoryStream m_decompressedData; |
|||
|
|||
/// <summary>
|
|||
/// Creates <see cref="JpegImage"/> from stream with an arbitrary image data
|
|||
/// </summary>
|
|||
/// <param name="imageData">Stream containing bytes of image in
|
|||
/// arbitrary format (BMP, Jpeg, GIF, PNG, TIFF, e.t.c)</param>
|
|||
public JpegImage(Stream imageData) |
|||
{ |
|||
createFromStream(imageData); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Creates <see cref="JpegImage"/> from pixels
|
|||
/// </summary>
|
|||
/// <param name="sampleData">Description of pixels.</param>
|
|||
/// <param name="colorspace">Colorspace of image.</param>
|
|||
/// <seealso cref="SampleRow"/>
|
|||
public JpegImage(SampleRow[] sampleData, Colorspace colorspace) |
|||
{ |
|||
if (sampleData == null) |
|||
throw new ArgumentNullException("sampleData"); |
|||
|
|||
if (sampleData.Length == 0) |
|||
throw new ArgumentException("sampleData must be no empty"); |
|||
|
|||
if (colorspace == Colorspace.Unknown) |
|||
throw new ArgumentException("Unknown colorspace"); |
|||
|
|||
m_rows = new List<SampleRow>(sampleData); |
|||
|
|||
SampleRow firstRow = m_rows[0]; |
|||
m_width = firstRow.Length; |
|||
m_height = m_rows.Count; |
|||
|
|||
Sample firstSample = firstRow[0]; |
|||
m_bitsPerComponent = firstSample.BitsPerComponent; |
|||
m_componentsPerSample = firstSample.ComponentCount; |
|||
m_colorspace = colorspace; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Frees and releases all resources allocated by this <see cref="JpegImage"/>
|
|||
/// </summary>
|
|||
public void Dispose() |
|||
{ |
|||
Dispose(true); |
|||
GC.SuppressFinalize(this); |
|||
} |
|||
|
|||
private void Dispose(bool disposing) |
|||
{ |
|||
if (!m_alreadyDisposed) |
|||
{ |
|||
if (disposing) |
|||
{ |
|||
// dispose managed resources
|
|||
if (m_compressedData != null) |
|||
m_compressedData.Dispose(); |
|||
|
|||
if (m_decompressedData != null) |
|||
m_decompressedData.Dispose(); |
|||
} |
|||
|
|||
// free native resources
|
|||
m_compressionParameters = null; |
|||
m_compressedData = null; |
|||
m_decompressedData = null; |
|||
m_rows = null; |
|||
m_alreadyDisposed = true; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the width of image in <see cref="Sample">samples</see>.
|
|||
/// </summary>
|
|||
/// <value>The width of image.</value>
|
|||
public int Width |
|||
{ |
|||
get |
|||
{ |
|||
return m_width; |
|||
} |
|||
internal set |
|||
{ |
|||
m_width = value; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the height of image in <see cref="Sample">samples</see>.
|
|||
/// </summary>
|
|||
/// <value>The height of image.</value>
|
|||
public int Height |
|||
{ |
|||
get |
|||
{ |
|||
return m_height; |
|||
} |
|||
internal set |
|||
{ |
|||
m_height = value; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the number of color components per <see cref="Sample">sample</see>.
|
|||
/// </summary>
|
|||
/// <value>The number of color components per sample.</value>
|
|||
public byte ComponentsPerSample |
|||
{ |
|||
get |
|||
{ |
|||
return m_componentsPerSample; |
|||
} |
|||
internal set |
|||
{ |
|||
m_componentsPerSample = value; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the number of bits per color component of <see cref="Sample">sample</see>.
|
|||
/// </summary>
|
|||
/// <value>The number of bits per color component.</value>
|
|||
public byte BitsPerComponent |
|||
{ |
|||
get |
|||
{ |
|||
return m_bitsPerComponent; |
|||
} |
|||
internal set |
|||
{ |
|||
m_bitsPerComponent = value; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the colorspace of image.
|
|||
/// </summary>
|
|||
/// <value>The colorspace of image.</value>
|
|||
public Colorspace Colorspace |
|||
{ |
|||
get |
|||
{ |
|||
return m_colorspace; |
|||
} |
|||
internal set |
|||
{ |
|||
m_colorspace = value; |
|||
} |
|||
} |
|||
|
|||
|
|||
/// <summary>
|
|||
/// Retrieves the required row of image.
|
|||
/// </summary>
|
|||
/// <param name="rowNumber">The number of row.</param>
|
|||
/// <returns>Image row of samples.</returns>
|
|||
public SampleRow GetRow(int rowNumber) |
|||
{ |
|||
return m_rows[rowNumber]; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes compressed JPEG image to stream.
|
|||
/// </summary>
|
|||
/// <param name="output">Output stream.</param>
|
|||
public void WriteJpeg(Stream output) |
|||
{ |
|||
WriteJpeg(output, new CompressionParameters()); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Compresses image to JPEG with given parameters and writes it to stream.
|
|||
/// </summary>
|
|||
/// <param name="output">Output stream.</param>
|
|||
/// <param name="parameters">The parameters of compression.</param>
|
|||
public void WriteJpeg(Stream output, CompressionParameters parameters) |
|||
{ |
|||
compress(parameters); |
|||
compressedData.WriteTo(output); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes decompressed image data as bitmap to stream.
|
|||
/// </summary>
|
|||
/// <param name="output">Output stream.</param>
|
|||
public void WriteBitmap(Stream output) |
|||
{ |
|||
decompressedData.WriteTo(output); |
|||
} |
|||
|
|||
private MemoryStream compressedData |
|||
{ |
|||
get |
|||
{ |
|||
if (m_compressedData == null) |
|||
compress(new CompressionParameters()); |
|||
|
|||
Debug.Assert(m_compressedData != null); |
|||
Debug.Assert(m_compressedData.Length != 0); |
|||
|
|||
return m_compressedData; |
|||
} |
|||
} |
|||
|
|||
private MemoryStream decompressedData |
|||
{ |
|||
get |
|||
{ |
|||
if (m_decompressedData == null) |
|||
fillDecompressedData(); |
|||
|
|||
Debug.Assert(m_decompressedData != null); |
|||
|
|||
return m_decompressedData; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Needs for DecompressorToJpegImage class
|
|||
/// </summary>
|
|||
internal void addSampleRow(SampleRow row) |
|||
{ |
|||
if (row == null) |
|||
throw new ArgumentNullException("row"); |
|||
|
|||
m_rows.Add(row); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Checks if imageData contains jpeg image
|
|||
/// </summary>
|
|||
private static bool isCompressed(Stream imageData) |
|||
{ |
|||
if (imageData == null) |
|||
return false; |
|||
|
|||
if (imageData.Length <= 2) |
|||
return false; |
|||
|
|||
imageData.Seek(0, SeekOrigin.Begin); |
|||
int first = imageData.ReadByte(); |
|||
int second = imageData.ReadByte(); |
|||
return (first == 0xFF && second == (int)JPEG_MARKER.SOI); |
|||
} |
|||
|
|||
private void createFromStream(Stream imageData) |
|||
{ |
|||
if (imageData == null) |
|||
throw new ArgumentNullException("imageData"); |
|||
|
|||
if (isCompressed(imageData)) |
|||
{ |
|||
m_compressedData = Utils.CopyStream(imageData); |
|||
decompress(); |
|||
} |
|||
else |
|||
{ |
|||
throw new NotImplementedException("JpegImage.createFromStream(Stream)"); |
|||
} |
|||
} |
|||
|
|||
private void compress(CompressionParameters parameters) |
|||
{ |
|||
Debug.Assert(m_rows != null); |
|||
Debug.Assert(m_rows.Count != 0); |
|||
|
|||
RawImage source = new RawImage(m_rows, m_colorspace); |
|||
compress(source, parameters); |
|||
} |
|||
|
|||
private void compress(IRawImage source, CompressionParameters parameters) |
|||
{ |
|||
Debug.Assert(source != null); |
|||
|
|||
if (!needCompressWith(parameters)) |
|||
return; |
|||
|
|||
m_compressedData = new MemoryStream(); |
|||
m_compressionParameters = new CompressionParameters(parameters); |
|||
|
|||
Jpeg jpeg = new Jpeg(); |
|||
jpeg.CompressionParameters = m_compressionParameters; |
|||
jpeg.Compress(source, m_compressedData); |
|||
} |
|||
|
|||
private bool needCompressWith(CompressionParameters parameters) |
|||
{ |
|||
return m_compressedData == null || |
|||
m_compressionParameters == null || |
|||
!m_compressionParameters.Equals(parameters); |
|||
} |
|||
|
|||
private void decompress() |
|||
{ |
|||
Jpeg jpeg = new Jpeg(); |
|||
jpeg.Decompress(compressedData, new DecompressorToJpegImage(this)); |
|||
} |
|||
|
|||
private void fillDecompressedData() |
|||
{ |
|||
Debug.Assert(m_decompressedData == null); |
|||
|
|||
m_decompressedData = new MemoryStream(); |
|||
BitmapDestination dest = new BitmapDestination(m_decompressedData); |
|||
|
|||
Jpeg jpeg = new Jpeg(); |
|||
jpeg.Decompress(compressedData, dest); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,80 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Diagnostics; |
|||
using System.Text; |
|||
|
|||
namespace BitMiracle.LibJpeg |
|||
{ |
|||
class RawImage : IRawImage |
|||
{ |
|||
private List<SampleRow> m_samples; |
|||
private Colorspace m_colorspace; |
|||
|
|||
private int m_currentRow = -1; |
|||
|
|||
internal RawImage(List<SampleRow> samples, Colorspace colorspace) |
|||
{ |
|||
Debug.Assert(samples != null); |
|||
Debug.Assert(samples.Count > 0); |
|||
Debug.Assert(colorspace != Colorspace.Unknown); |
|||
|
|||
m_samples = samples; |
|||
m_colorspace = colorspace; |
|||
} |
|||
|
|||
public int Width |
|||
{ |
|||
get |
|||
{ |
|||
return m_samples[0].Length; |
|||
} |
|||
} |
|||
|
|||
public int Height |
|||
{ |
|||
get |
|||
{ |
|||
return m_samples.Count; |
|||
} |
|||
} |
|||
|
|||
public Colorspace Colorspace |
|||
{ |
|||
get |
|||
{ |
|||
return m_colorspace; |
|||
} |
|||
} |
|||
|
|||
public int ComponentsPerPixel |
|||
{ |
|||
get |
|||
{ |
|||
return m_samples[0][0].ComponentCount; |
|||
} |
|||
} |
|||
|
|||
public void BeginRead() |
|||
{ |
|||
m_currentRow = 0; |
|||
} |
|||
|
|||
public byte[] GetPixelRow() |
|||
{ |
|||
SampleRow row = m_samples[m_currentRow]; |
|||
List<byte> result = new List<byte>(); |
|||
for (int i = 0; i < row.Length; ++i) |
|||
{ |
|||
Sample sample = row[i]; |
|||
for (int j = 0; j < sample.ComponentCount; ++j) |
|||
result.Add((byte)sample[j]); |
|||
} |
|||
++m_currentRow; |
|||
return result.ToArray(); |
|||
} |
|||
|
|||
public void EndRead() |
|||
{ |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,106 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
|
|||
namespace BitMiracle.LibJpeg |
|||
{ |
|||
using ImageProcessor.Formats; |
|||
|
|||
/// <summary>
|
|||
/// Represents a "sample" (you can understand it as a "pixel") of image.
|
|||
/// </summary>
|
|||
/// <remarks>It's impossible to create an instance of this class directly,
|
|||
/// but you can use existing samples through <see cref="SampleRow"/> collection.
|
|||
/// Usual scenario is to get row of samples from the <see cref="JpegImage.GetRow"/> method.
|
|||
/// </remarks>
|
|||
#if EXPOSE_LIBJPEG
|
|||
public |
|||
#endif
|
|||
class Sample |
|||
{ |
|||
private short[] m_components; |
|||
private byte m_bitsPerComponent; |
|||
|
|||
internal Sample(BitStream bitStream, byte bitsPerComponent, byte componentCount) |
|||
{ |
|||
if (bitStream == null) |
|||
throw new ArgumentNullException("bitStream"); |
|||
|
|||
if (bitsPerComponent <= 0 || bitsPerComponent > 16) |
|||
throw new ArgumentOutOfRangeException("bitsPerComponent"); |
|||
|
|||
if (componentCount <= 0 || componentCount > 5) |
|||
throw new ArgumentOutOfRangeException("componentCount"); |
|||
|
|||
m_bitsPerComponent = bitsPerComponent; |
|||
|
|||
m_components = new short[componentCount]; |
|||
for (short i = 0; i < componentCount; ++i) |
|||
m_components[i] = (short)bitStream.Read(bitsPerComponent); |
|||
} |
|||
|
|||
internal Sample(short[] components, byte bitsPerComponent) |
|||
{ |
|||
if (components == null) |
|||
throw new ArgumentNullException("components"); |
|||
|
|||
if (components.Length == 0 || components.Length > 5) |
|||
throw new ArgumentException("components must be not empty and contain less than 5 elements"); |
|||
|
|||
if (bitsPerComponent <= 0 || bitsPerComponent > 16) |
|||
throw new ArgumentOutOfRangeException("bitsPerComponent"); |
|||
|
|||
m_bitsPerComponent = bitsPerComponent; |
|||
|
|||
m_components = new short[components.Length]; |
|||
Buffer.BlockCopy(components, 0, m_components, 0, components.Length * sizeof(short)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the number of bits per color component.
|
|||
/// </summary>
|
|||
/// <value>The number of bits per color component.</value>
|
|||
public byte BitsPerComponent |
|||
{ |
|||
get |
|||
{ |
|||
return m_bitsPerComponent; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the number of color components.
|
|||
/// </summary>
|
|||
/// <value>The number of color components.</value>
|
|||
public byte ComponentCount |
|||
{ |
|||
get |
|||
{ |
|||
return (byte)m_components.Length; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the color component at the specified index.
|
|||
/// </summary>
|
|||
/// <param name="componentNumber">The number of color component.</param>
|
|||
/// <returns>Value of color component.</returns>
|
|||
public short this[int componentNumber] |
|||
{ |
|||
get |
|||
{ |
|||
return m_components[componentNumber]; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the required color component.
|
|||
/// </summary>
|
|||
/// <param name="componentNumber">The number of color component.</param>
|
|||
/// <returns>Value of color component.</returns>
|
|||
public short GetComponent(int componentNumber) |
|||
{ |
|||
return m_components[componentNumber]; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,150 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
|
|||
namespace BitMiracle.LibJpeg |
|||
{ |
|||
using ImageProcessor.Formats; |
|||
|
|||
/// <summary>
|
|||
/// Represents a row of image - collection of samples.
|
|||
/// </summary>
|
|||
#if EXPOSE_LIBJPEG
|
|||
public |
|||
#endif
|
|||
class SampleRow |
|||
{ |
|||
private byte[] m_bytes; |
|||
private Sample[] m_samples; |
|||
|
|||
/// <summary>
|
|||
/// Creates a row from raw samples data.
|
|||
/// </summary>
|
|||
/// <param name="row">Raw description of samples.<br/>
|
|||
/// You can pass collection with more than sampleCount samples - only sampleCount samples
|
|||
/// will be parsed and all remaining bytes will be ignored.</param>
|
|||
/// <param name="sampleCount">The number of samples in row.</param>
|
|||
/// <param name="bitsPerComponent">The number of bits per component.</param>
|
|||
/// <param name="componentsPerSample">The number of components per sample.</param>
|
|||
public SampleRow(byte[] row, int sampleCount, byte bitsPerComponent, byte componentsPerSample) |
|||
{ |
|||
if (row == null) |
|||
throw new ArgumentNullException("row"); |
|||
|
|||
if (row.Length == 0) |
|||
throw new ArgumentException("row is empty"); |
|||
|
|||
if (sampleCount <= 0) |
|||
throw new ArgumentOutOfRangeException("sampleCount"); |
|||
|
|||
if (bitsPerComponent <= 0 || bitsPerComponent > 16) |
|||
throw new ArgumentOutOfRangeException("bitsPerComponent"); |
|||
|
|||
if (componentsPerSample <= 0 || componentsPerSample > 5) |
|||
throw new ArgumentOutOfRangeException("componentsPerSample"); |
|||
|
|||
m_bytes = row; |
|||
|
|||
using (BitStream bitStream = new BitStream(row)) |
|||
{ |
|||
m_samples = new Sample[sampleCount]; |
|||
for (int i = 0; i < sampleCount; ++i) |
|||
m_samples[i] = new Sample(bitStream, bitsPerComponent, componentsPerSample); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Creates row from an array of components.
|
|||
/// </summary>
|
|||
/// <param name="sampleComponents">Array of color components.</param>
|
|||
/// <param name="bitsPerComponent">The number of bits per component.</param>
|
|||
/// <param name="componentsPerSample">The number of components per sample.</param>
|
|||
/// <remarks>The difference between this constructor and
|
|||
/// <see cref="M:BitMiracle.LibJpeg.SampleRow.#ctor(System.Byte[],System.Int32,System.Byte,System.Byte)">another one</see> -
|
|||
/// this constructor accept an array of prepared color components whereas
|
|||
/// another constructor accept raw bytes and parse them.
|
|||
/// </remarks>
|
|||
internal SampleRow(short[] sampleComponents, byte bitsPerComponent, byte componentsPerSample) |
|||
{ |
|||
if (sampleComponents == null) |
|||
throw new ArgumentNullException("sampleComponents"); |
|||
|
|||
if (sampleComponents.Length == 0) |
|||
throw new ArgumentException("row is empty"); |
|||
|
|||
if (bitsPerComponent <= 0 || bitsPerComponent > 16) |
|||
throw new ArgumentOutOfRangeException("bitsPerComponent"); |
|||
|
|||
if (componentsPerSample <= 0 || componentsPerSample > 5) |
|||
throw new ArgumentOutOfRangeException("componentsPerSample"); |
|||
|
|||
int sampleCount = sampleComponents.Length / componentsPerSample; |
|||
m_samples = new Sample[sampleCount]; |
|||
for (int i = 0; i < sampleCount; ++i) |
|||
{ |
|||
short[] components = new short[componentsPerSample]; |
|||
Buffer.BlockCopy(sampleComponents, i * componentsPerSample * sizeof(short), components, 0, componentsPerSample * sizeof(short)); |
|||
m_samples[i] = new Sample(components, bitsPerComponent); |
|||
} |
|||
|
|||
using (BitStream bits = new BitStream()) |
|||
{ |
|||
for (int i = 0; i < sampleCount; ++i) |
|||
{ |
|||
for (int j = 0; j < componentsPerSample; ++j) |
|||
bits.Write(sampleComponents[i * componentsPerSample + j], bitsPerComponent); |
|||
} |
|||
|
|||
m_bytes = new byte[bits.UnderlyingStream.Length]; |
|||
bits.UnderlyingStream.Seek(0, System.IO.SeekOrigin.Begin); |
|||
bits.UnderlyingStream.Read(m_bytes, 0, (int)bits.UnderlyingStream.Length); |
|||
} |
|||
} |
|||
|
|||
|
|||
/// <summary>
|
|||
/// Gets the number of samples in this row.
|
|||
/// </summary>
|
|||
/// <value>The number of samples.</value>
|
|||
public int Length |
|||
{ |
|||
get |
|||
{ |
|||
return m_samples.Length; |
|||
} |
|||
} |
|||
|
|||
|
|||
/// <summary>
|
|||
/// Gets the sample at the specified index.
|
|||
/// </summary>
|
|||
/// <param name="sampleNumber">The number of sample.</param>
|
|||
/// <returns>The required sample.</returns>
|
|||
public Sample this[int sampleNumber] |
|||
{ |
|||
get |
|||
{ |
|||
return GetAt(sampleNumber); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the sample at the specified index.
|
|||
/// </summary>
|
|||
/// <param name="sampleNumber">The number of sample.</param>
|
|||
/// <returns>The required sample.</returns>
|
|||
public Sample GetAt(int sampleNumber) |
|||
{ |
|||
return m_samples[sampleNumber]; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Serializes this row to raw bytes.
|
|||
/// </summary>
|
|||
/// <returns>The row representation as array of bytes</returns>
|
|||
public byte[] ToBytes() |
|||
{ |
|||
return m_bytes; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,81 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
using System.IO; |
|||
|
|||
namespace BitMiracle.LibJpeg |
|||
{ |
|||
class Utils |
|||
{ |
|||
public static MemoryStream CopyStream(Stream stream) |
|||
{ |
|||
if (stream == null) |
|||
throw new ArgumentNullException("stream"); |
|||
|
|||
long positionBefore = stream.Position; |
|||
stream.Seek(0, SeekOrigin.Begin); |
|||
|
|||
MemoryStream result = new MemoryStream((int)stream.Length); |
|||
|
|||
byte[] block = new byte[2048]; |
|||
for ( ; ; ) |
|||
{ |
|||
int bytesRead = stream.Read(block, 0, 2048); |
|||
result.Write(block, 0, bytesRead); |
|||
if (bytesRead < 2048) |
|||
break; |
|||
} |
|||
|
|||
stream.Seek(positionBefore, SeekOrigin.Begin); |
|||
return result; |
|||
} |
|||
|
|||
public static void CMYK2RGB(byte c, byte m, byte y, byte k, out byte red, out byte green, out byte blue) |
|||
{ |
|||
float C, M, Y, K; |
|||
C = c / 255.0f; |
|||
M = m / 255.0f; |
|||
Y = y / 255.0f; |
|||
K = k / 255.0f; |
|||
|
|||
float R, G, B; |
|||
R = C * (1.0f - K) + K; |
|||
G = M * (1.0f - K) + K; |
|||
B = Y * (1.0f - K) + K; |
|||
|
|||
R = (1.0f - R) * 255.0f + 0.5f; |
|||
G = (1.0f - G) * 255.0f + 0.5f; |
|||
B = (1.0f - B) * 255.0f + 0.5f; |
|||
|
|||
red = (byte)(R * 255); |
|||
green = (byte)(G * 255); |
|||
blue = (byte)(B * 255); |
|||
|
|||
//C = (double)c;
|
|||
//M = (double)m;
|
|||
//Y = (double)y;
|
|||
//K = (double)k;
|
|||
|
|||
//C = C / 255.0;
|
|||
//M = M / 255.0;
|
|||
//Y = Y / 255.0;
|
|||
//K = K / 255.0;
|
|||
|
|||
//R = C * (1.0 - K) + K;
|
|||
//G = M * (1.0 - K) + K;
|
|||
//B = Y * (1.0 - K) + K;
|
|||
|
|||
//R = (1.0 - R) * 255.0 + 0.5;
|
|||
//G = (1.0 - G) * 255.0 + 0.5;
|
|||
//B = (1.0 - B) * 255.0 + 0.5;
|
|||
|
|||
//r = (byte)R;
|
|||
//g = (byte)G;
|
|||
//b = (byte)B;
|
|||
|
|||
//rgb = RGB(r, g, b);
|
|||
|
|||
//return rgb;
|
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,5 @@ |
|||
Encoder/Decoder adapted from: |
|||
|
|||
https://github.com/BitMiracle/libjpeg.net/ |
|||
https://github.com/yufeih/Nine.Imaging/ |
|||
https://imagetools.codeplex.com/ |
|||
@ -0,0 +1,4 @@ |
|||
Encoder/Decoder adapted from: |
|||
|
|||
https://github.com/yufeih/Nine.Imaging/ |
|||
https://imagetools.codeplex.com/ |
|||
Loading…
Reference in new issue