mirror of
https://github.com/crskycode/GARbro.git
synced 2024-11-24 22:25:36 +08:00
1600 lines
57 KiB
C#
1600 lines
57 KiB
C#
//! \file ImageERI.cs
|
|
//! \date Tue May 26 12:04:30 2015
|
|
//! \brief Entis rasterized image format.
|
|
//
|
|
// Copyright (C) 2015 by morkt
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
// of this software and associated documentation files (the "Software"), to
|
|
// deal in the Software without restriction, including without limitation the
|
|
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
|
// sell copies of the Software, and to permit persons to whom the Software is
|
|
// furnished to do so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in
|
|
// all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
// IN THE SOFTWARE.
|
|
//
|
|
|
|
using System;
|
|
using System.ComponentModel.Composition;
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
using System.Windows.Media;
|
|
using System.Windows.Media.Imaging;
|
|
using GameRes.Utility;
|
|
|
|
namespace GameRes.Formats.Entis
|
|
{
|
|
internal class EriMetaData : ImageMetaData
|
|
{
|
|
public int StreamPos;
|
|
public int Version;
|
|
public CvType Transformation;
|
|
public EriCode Architecture;
|
|
public int FormatType;
|
|
public bool VerticalFlip;
|
|
public int ClippedPixel;
|
|
public int SamplingFlags;
|
|
public ulong QuantumizedBits;
|
|
public ulong AllottedBits;
|
|
public int BlockingDegree;
|
|
public int LappedBlock;
|
|
public int FrameTransform;
|
|
public int FrameDegree;
|
|
}
|
|
|
|
public enum CvType
|
|
{
|
|
Lossless_ERI = 0x03020000,
|
|
DCT_ERI = 0x00000001,
|
|
LOT_ERI = 0x00000005,
|
|
LOT_ERI_MSS = 0x00000105,
|
|
}
|
|
|
|
public enum EriCode
|
|
{
|
|
RunlengthGamma = -1,
|
|
RunlengthHuffman = -4,
|
|
Nemesis = -16,
|
|
}
|
|
|
|
public enum EriImage
|
|
{
|
|
RGB = 0x00000001,
|
|
RGBA = 0x04000001,
|
|
Gray = 0x00000002,
|
|
TypeMask = 0x00FFFFFF,
|
|
WithPalette = 0x01000000,
|
|
UseClipping = 0x02000000,
|
|
WithAlpha = 0x04000000,
|
|
SideBySide = 0x10000000,
|
|
}
|
|
|
|
internal class EriFile : BinaryReader
|
|
{
|
|
internal struct Section
|
|
{
|
|
public AsciiString Id;
|
|
public long Length;
|
|
}
|
|
|
|
public EriFile (Stream stream) : base (stream, System.Text.Encoding.ASCII, true)
|
|
{
|
|
}
|
|
|
|
public Section ReadSection ()
|
|
{
|
|
var section = new Section();
|
|
section.Id = new AsciiString (8);
|
|
if (8 != this.Read (section.Id.Value, 0, 8))
|
|
throw new EndOfStreamException();
|
|
section.Length = this.ReadInt64();
|
|
return section;
|
|
}
|
|
|
|
public long FindSection (string name)
|
|
{
|
|
var id = new AsciiString (8);
|
|
for (;;)
|
|
{
|
|
if (8 != this.Read (id.Value, 0, 8))
|
|
throw new EndOfStreamException();
|
|
var length = this.ReadInt64();
|
|
if (length < 0)
|
|
throw new EndOfStreamException();
|
|
if (id == name)
|
|
return length;
|
|
this.BaseStream.Seek (length, SeekOrigin.Current);
|
|
}
|
|
}
|
|
}
|
|
|
|
[Export(typeof(ImageFormat))]
|
|
public class EriFormat : ImageFormat
|
|
{
|
|
public override string Tag { get { return "ERI"; } }
|
|
public override string Description { get { return "Entis rasterized image format"; } }
|
|
public override uint Signature { get { return 0x69746e45u; } } // 'Enti'
|
|
|
|
public override ImageMetaData ReadMetaData (Stream stream)
|
|
{
|
|
byte[] header = new byte[0x40];
|
|
if (header.Length != stream.Read (header, 0, header.Length))
|
|
return null;
|
|
if (0x03000100 != LittleEndian.ToUInt32 (header, 8))
|
|
return null;
|
|
if (!Binary.AsciiEqual (header, 0x10, "Entis Rasterized Image"))
|
|
return null;
|
|
using (var reader = new EriFile (stream))
|
|
{
|
|
var section = reader.ReadSection();
|
|
if (section.Id != "Header " || section.Length <= 0)
|
|
return null;
|
|
int header_size = (int)section.Length;
|
|
int stream_pos = 0x50 + header_size;
|
|
EriMetaData info = null;
|
|
while (header_size > 8)
|
|
{
|
|
section = reader.ReadSection();
|
|
header_size -= 8;
|
|
if (section.Length <= 0 || section.Length > header_size)
|
|
break;
|
|
if ("ImageInf" == section.Id)
|
|
{
|
|
int version = reader.ReadInt32();
|
|
if (version != 0x00020100 && version != 0x00020200)
|
|
return null;
|
|
info = new EriMetaData { StreamPos = stream_pos, Version = version };
|
|
info.Transformation = (CvType)reader.ReadInt32();
|
|
info.Architecture = (EriCode)reader.ReadInt32();
|
|
info.FormatType = reader.ReadInt32();
|
|
int w = reader.ReadInt32();
|
|
int h = reader.ReadInt32();
|
|
info.Width = (uint)Math.Abs (w);
|
|
info.Height = (uint)Math.Abs (h);
|
|
info.VerticalFlip = h < 0;
|
|
info.BPP = reader.ReadInt32();
|
|
info.ClippedPixel = reader.ReadInt32();
|
|
info.SamplingFlags = reader.ReadInt32();
|
|
info.QuantumizedBits = reader.ReadUInt64();
|
|
info.AllottedBits = reader.ReadUInt64();
|
|
info.BlockingDegree = reader.ReadInt32();
|
|
info.LappedBlock = reader.ReadInt32();
|
|
info.FrameTransform = reader.ReadInt32();
|
|
info.FrameDegree = reader.ReadInt32();
|
|
break;
|
|
}
|
|
header_size -= (int)section.Length;
|
|
reader.BaseStream.Seek (section.Length, SeekOrigin.Current);
|
|
}
|
|
return info;
|
|
}
|
|
}
|
|
|
|
public override ImageData Read (Stream stream, ImageMetaData info)
|
|
{
|
|
var meta = info as EriMetaData;
|
|
if (null == meta)
|
|
throw new ArgumentException ("EriFormat.Read should be supplied with EriMetaData", "info");
|
|
stream.Position = meta.StreamPos;
|
|
using (var input = new EriFile (stream))
|
|
{
|
|
Color[] palette = null;
|
|
for (;;) // ReadSection throws an exception in case of EOF
|
|
{
|
|
var section = input.ReadSection();
|
|
if ("Stream " == section.Id)
|
|
continue;
|
|
if ("ImageFrm" == section.Id)
|
|
break;
|
|
if ("Palette " == section.Id && info.BPP <= 8 && section.Length <= 0x400)
|
|
{
|
|
palette = ReadPalette (stream, (int)section.Length);
|
|
continue;
|
|
}
|
|
input.BaseStream.Seek (section.Length, SeekOrigin.Current);
|
|
}
|
|
var reader = new EriReader (stream, meta, palette);
|
|
reader.DecodeImage();
|
|
var bitmap = BitmapSource.Create ((int)info.Width, (int)info.Height,
|
|
ImageData.DefaultDpiX, ImageData.DefaultDpiY,
|
|
reader.Format, reader.Palette, reader.Data, reader.Stride);
|
|
bitmap.Freeze();
|
|
return new ImageData (bitmap, info);
|
|
}
|
|
}
|
|
|
|
private Color[] ReadPalette (Stream input, int palette_length)
|
|
{
|
|
var palette_data = new byte[0x400];
|
|
if (palette_length > palette_data.Length)
|
|
throw new InvalidFormatException();
|
|
if (palette_length != input.Read (palette_data, 0, palette_length))
|
|
throw new InvalidFormatException();
|
|
var colors = new Color[256];
|
|
for (int i = 0; i < 256; ++i)
|
|
{
|
|
colors[i] = Color.FromRgb (palette_data[i*4+2], palette_data[i*4+1], palette_data[i*4]);
|
|
}
|
|
return colors;
|
|
}
|
|
|
|
public override void Write (Stream file, ImageData image)
|
|
{
|
|
throw new NotImplementedException ("EriFormat.Write not implemented");
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
E R I S A - L i b r a r y
|
|
-----------------------------------------------------------------------------
|
|
Copyright (C) 2002-2004 Leshade Entis, Entis-soft. All rights reserved.
|
|
*****************************************************************************/
|
|
|
|
internal class EriReader
|
|
{
|
|
EriMetaData m_info;
|
|
byte[] m_output;
|
|
ERISADecodeContext m_context;
|
|
int m_dst;
|
|
|
|
uint m_nBlockSize;
|
|
uint m_nBlockArea;
|
|
uint m_nBlockSamples;
|
|
uint m_nChannelCount;
|
|
uint m_nWidthBlocks;
|
|
uint m_nHeightBlocks;
|
|
|
|
int m_dwBytesPerLine;
|
|
uint m_dwClippedPixel;
|
|
|
|
int m_ptrDstBlock;
|
|
int m_nDstLineBytes;
|
|
int m_nDstPixelBytes;
|
|
uint m_nDstWidth;
|
|
uint m_nDstHeight;
|
|
uint m_fdwDecFlags;
|
|
|
|
// buffers for lossless encoding
|
|
byte[] m_ptrOperations;
|
|
sbyte[] m_ptrColumnBuf;
|
|
sbyte[] m_ptrLineBuf;
|
|
sbyte[] m_ptrDecodeBuf;
|
|
sbyte[] m_ptrArrangeBuf;
|
|
int[] m_pArrangeTable = new int[4];
|
|
|
|
HuffmanTree m_pHuffmanTree;
|
|
|
|
PtrProcedure[] m_pfnColorOperation;
|
|
|
|
public byte[] Data { get { return m_output; } }
|
|
public PixelFormat Format { get; private set; }
|
|
public int Stride { get { return Math.Abs (m_dwBytesPerLine); } }
|
|
public BitmapPalette Palette { get; private set; }
|
|
|
|
public EriReader (Stream stream, EriMetaData info, Color[] palette)
|
|
{
|
|
m_info = info;
|
|
if (CvType.Lossless_ERI == m_info.Transformation)
|
|
InitializeLossless();
|
|
else if (CvType.LOT_ERI == m_info.Transformation
|
|
|| CvType.DCT_ERI == m_info.Transformation)
|
|
InitializeLossy();
|
|
else
|
|
throw new NotSupportedException ("Not supported ERI compression");
|
|
if (null != palette)
|
|
Palette = new BitmapPalette (palette);
|
|
CreateImageBuffer();
|
|
m_context.AttachInputFile (stream);
|
|
|
|
m_pfnColorOperation = new PtrProcedure[0x10]
|
|
{
|
|
ColorOperation0000,
|
|
ColorOperation0000,
|
|
ColorOperation0000,
|
|
ColorOperation0000,
|
|
ColorOperation0000,
|
|
ColorOperation0101,
|
|
ColorOperation0110,
|
|
ColorOperation0111,
|
|
ColorOperation0000,
|
|
ColorOperation1001,
|
|
ColorOperation1010,
|
|
ColorOperation1011,
|
|
ColorOperation0000,
|
|
ColorOperation1101,
|
|
ColorOperation1110,
|
|
ColorOperation1111
|
|
};
|
|
}
|
|
|
|
private void InitializeLossless ()
|
|
{
|
|
switch (m_info.Architecture)
|
|
{
|
|
case EriCode.Nemesis:
|
|
throw new NotSupportedException ("Not supported ERI compression");
|
|
case EriCode.RunlengthHuffman:
|
|
case EriCode.RunlengthGamma:
|
|
break;
|
|
default:
|
|
throw new InvalidFormatException();
|
|
}
|
|
if (0 == m_info.BlockingDegree)
|
|
throw new InvalidFormatException();
|
|
|
|
switch (m_info.FormatType & (int)EriImage.TypeMask)
|
|
{
|
|
case (int)EriImage.RGB:
|
|
if (m_info.BPP <= 8)
|
|
m_nChannelCount = 1;
|
|
else if (0 == (m_info.FormatType & (int)EriImage.WithAlpha))
|
|
m_nChannelCount = 3;
|
|
else
|
|
m_nChannelCount = 4;
|
|
break;
|
|
|
|
case (int)EriImage.Gray:
|
|
m_nChannelCount = 1;
|
|
break;
|
|
|
|
default:
|
|
throw new InvalidFormatException();
|
|
}
|
|
|
|
m_nBlockSize = (uint) (1 << m_info.BlockingDegree);
|
|
m_nBlockArea = (uint) (1 << (m_info.BlockingDegree * 2));
|
|
m_nBlockSamples = m_nBlockArea * m_nChannelCount;
|
|
m_nWidthBlocks = (uint)((m_info.Width + m_nBlockSize - 1) >> m_info.BlockingDegree);
|
|
m_nHeightBlocks = (uint)((m_info.Height + m_nBlockSize - 1) >> m_info.BlockingDegree);
|
|
|
|
m_ptrOperations = new byte[m_nWidthBlocks * m_nHeightBlocks];
|
|
m_ptrColumnBuf = new sbyte[m_nBlockSize * m_nChannelCount];
|
|
m_ptrLineBuf = new sbyte[m_nChannelCount * (m_nWidthBlocks << m_info.BlockingDegree)];
|
|
m_ptrDecodeBuf = new sbyte[m_nBlockSamples];
|
|
m_ptrArrangeBuf = new sbyte[m_nBlockSamples];
|
|
|
|
InitializeArrangeTable();
|
|
if (0x00020200 == m_info.Version)
|
|
{
|
|
if (EriCode.RunlengthHuffman == m_info.Architecture)
|
|
{
|
|
m_pHuffmanTree = new HuffmanTree();
|
|
}
|
|
/*
|
|
else if (EriCode.Nemesis == m_info.Architecture)
|
|
{
|
|
m_pProbERISA = new ERISA_PROB_MODEL ;
|
|
}
|
|
*/
|
|
}
|
|
if (EriCode.RunlengthHuffman == m_info.Architecture)
|
|
m_context = new HuffmanDecodeContext (0x10000);
|
|
else
|
|
m_context = new RLEDecodeContext (0x10000);
|
|
}
|
|
|
|
private void InitializeLossy ()
|
|
{
|
|
throw new NotImplementedException ("Lossy ERI compression not implemented");
|
|
}
|
|
|
|
int[] m_ptrTable;
|
|
|
|
void InitializeArrangeTable ()
|
|
{
|
|
uint i, j, k, l, m;
|
|
|
|
m_ptrTable = new int[m_nBlockSamples * 4];
|
|
m_pArrangeTable[0] = 0;
|
|
m_pArrangeTable[1] = (int)m_nBlockSamples;
|
|
m_pArrangeTable[2] = (int)m_nBlockSamples * 2;
|
|
m_pArrangeTable[3] = (int)m_nBlockSamples * 3;
|
|
|
|
int ptrNext = m_pArrangeTable[0];
|
|
for (i = 0; i < m_nBlockSamples; ++i)
|
|
{
|
|
m_ptrTable[ptrNext+i] = (int)i;
|
|
}
|
|
|
|
ptrNext = m_pArrangeTable[1];
|
|
l = 0;
|
|
for (i = 0; i < m_nChannelCount; i++)
|
|
{
|
|
for (j = 0; j < m_nBlockSize; j++)
|
|
{
|
|
m = l + j;
|
|
for (k = 0; k < m_nBlockSize; k++)
|
|
{
|
|
m_ptrTable[ptrNext++] = (int)m;
|
|
m += m_nBlockSize;
|
|
}
|
|
}
|
|
l += m_nBlockArea;
|
|
}
|
|
ptrNext = m_pArrangeTable[2];
|
|
for (i = 0; i < m_nBlockArea; i++)
|
|
{
|
|
k = i;
|
|
for (j = 0; j < m_nChannelCount; j++)
|
|
{
|
|
m_ptrTable[ptrNext++] = (int)k;
|
|
k += m_nBlockArea;
|
|
}
|
|
}
|
|
ptrNext = m_pArrangeTable[3];
|
|
for (i = 0; i < m_nBlockSize; i++)
|
|
{
|
|
l = i;
|
|
for (j = 0; j < m_nBlockSize; j++)
|
|
{
|
|
m = l;
|
|
l += m_nBlockSize;
|
|
for (k = 0; k < m_nChannelCount; k++)
|
|
{
|
|
m_ptrTable[ptrNext++] = (int)m;
|
|
m += m_nBlockArea;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void CreateImageBuffer ()
|
|
{
|
|
uint stride = ((m_info.Width * (uint)m_info.BPP / 8u) + 0x03u) & ~0x03u;
|
|
uint image_bytes = stride * m_info.Height;
|
|
m_output = new byte[image_bytes];
|
|
m_dwBytesPerLine = (int)stride;
|
|
m_dwClippedPixel = 0;
|
|
if (!m_info.VerticalFlip)
|
|
{
|
|
m_dst = ((int)m_info.Height - 1) * m_dwBytesPerLine;
|
|
m_dwBytesPerLine = -m_dwBytesPerLine;
|
|
}
|
|
else
|
|
{
|
|
m_dst = 0;
|
|
}
|
|
}
|
|
|
|
public void DecodeImage ()
|
|
{
|
|
if (CvType.Lossless_ERI == m_info.Transformation)
|
|
DecodeLosslessImage (m_context as RLEDecodeContext);
|
|
else
|
|
DecodeLossyImage (m_context as HuffmanDecodeContext);
|
|
}
|
|
|
|
private delegate void PtrProcedure ();
|
|
|
|
private void DecodeLosslessImage (RLEDecodeContext context)
|
|
{
|
|
context.FlushBuffer();
|
|
|
|
uint nERIVersion = context.GetNBits (8);
|
|
uint fOpTable = context.GetNBits (8);
|
|
uint fEncodeType = context.GetNBits (8);
|
|
uint nBitCount = context.GetNBits (8);
|
|
|
|
if (0 != fOpTable || 0 != (fEncodeType & 0xFE))
|
|
{
|
|
throw new InvalidFormatException();
|
|
}
|
|
switch (nERIVersion)
|
|
{
|
|
case 1:
|
|
if (nBitCount != 0)
|
|
throw new InvalidFormatException();
|
|
break;
|
|
case 8:
|
|
if (nBitCount != 8)
|
|
throw new InvalidFormatException();
|
|
break;
|
|
case 16:
|
|
if ((nBitCount != 8) || (fEncodeType != 0))
|
|
throw new InvalidFormatException();
|
|
break;
|
|
default:
|
|
throw new InvalidFormatException();
|
|
}
|
|
m_nDstPixelBytes = m_info.BPP >> 3;
|
|
m_nDstLineBytes = m_dwBytesPerLine;
|
|
var pfnRestoreFunc = GetLLRestoreFunc (m_info.FormatType, m_info.BPP);
|
|
if (null == pfnRestoreFunc)
|
|
throw new InvalidFormatException();
|
|
/*
|
|
if (EriCode.Nemesis == m_info.Architecture)
|
|
{
|
|
Debug.Assert (m_pProbERISA != null);
|
|
m_pProbERISA.Initialize();
|
|
}
|
|
*/
|
|
int i;
|
|
int ptrNextOperation = 0; // index within m_ptrOperations
|
|
if ((0 != (fEncodeType & 0x01)) && (m_nChannelCount >= 3))
|
|
{
|
|
Debug.Assert (m_info.Architecture != EriCode.Nemesis);
|
|
int nAllBlockCount = (int)(m_nWidthBlocks * m_nHeightBlocks);
|
|
for (i = 0; i < nAllBlockCount; i++)
|
|
{
|
|
if (EriCode.RunlengthGamma == m_info.Architecture)
|
|
{
|
|
m_ptrOperations[i] = (byte)(context.GetNBits(4) | 0xC0);
|
|
}
|
|
else
|
|
{
|
|
Debug.Assert (EriCode.RunlengthHuffman == m_info.Architecture);
|
|
m_ptrOperations[i] = (byte)(context as HuffmanDecodeContext).GetHuffmanCode (m_pHuffmanTree);
|
|
}
|
|
}
|
|
}
|
|
if (context.GetABit() != 0)
|
|
throw new InvalidFormatException();
|
|
|
|
if (EriCode.RunlengthGamma == m_info.Architecture)
|
|
{
|
|
if (0 != (fEncodeType & 0x01))
|
|
{
|
|
context.InitGammaContext();
|
|
}
|
|
}
|
|
else if (EriCode.RunlengthHuffman == m_info.Architecture)
|
|
{
|
|
(context as HuffmanDecodeContext).PrepareToDecodeERINACode();
|
|
}
|
|
/*
|
|
else
|
|
{
|
|
Debug.Assert (EriCode.Nemesis == m_info.Architecture);
|
|
context.PrepareToDecodeERISACode();
|
|
}
|
|
*/
|
|
int nWidthSamples = (int)(m_nChannelCount * m_nWidthBlocks * m_nBlockSize);
|
|
for (i = 0; i < nWidthSamples; ++i)
|
|
m_ptrLineBuf[i] = 0;
|
|
|
|
int nAllBlockLines = (int)(m_nBlockSize * m_nChannelCount);
|
|
int nLeftHeight = (int)m_info.Height;
|
|
|
|
for (int nPosY = 0; nPosY < (int) m_nHeightBlocks; ++nPosY)
|
|
{
|
|
int nColumnBufSamples = (int)(m_nBlockSize * m_nChannelCount);
|
|
for (i = 0; i < nColumnBufSamples; ++i)
|
|
m_ptrColumnBuf[i] = 0;
|
|
|
|
m_ptrDstBlock = m_dst + nPosY * m_dwBytesPerLine * (int)m_nBlockSize;
|
|
m_nDstHeight = m_nBlockSize;
|
|
if ((int)m_nDstHeight > nLeftHeight)
|
|
{
|
|
m_nDstHeight = (uint)nLeftHeight;
|
|
}
|
|
int nLeftWidth = (int)m_info.Width;
|
|
int ptrNextLineBuf = 0; // m_ptrLineBuf;
|
|
|
|
for (int nPosX = 0; nPosX < (int)m_nWidthBlocks; ++nPosX)
|
|
{
|
|
m_nDstWidth = Math.Min (m_nBlockSize, (uint)nLeftWidth);
|
|
|
|
uint dwOperationCode;
|
|
if (m_nChannelCount >= 3)
|
|
{
|
|
if (0 != (fEncodeType & 1))
|
|
{
|
|
dwOperationCode = m_ptrOperations[ptrNextOperation++];
|
|
}
|
|
else if (m_info.Architecture == EriCode.RunlengthHuffman)
|
|
{
|
|
dwOperationCode = (uint)(context as HuffmanDecodeContext).GetHuffmanCode (m_pHuffmanTree);
|
|
}
|
|
/*
|
|
else if (m_info.Architecture == EriCode.Nemesis)
|
|
{
|
|
dwOperationCode = context.DecodeERISACode (m_pProbERISA);
|
|
}
|
|
*/
|
|
else
|
|
{
|
|
Debug.Assert (EriCode.RunlengthGamma == m_info.Architecture);
|
|
dwOperationCode = context.GetNBits (4) | 0xC0;
|
|
context.InitGammaContext();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ((int)EriImage.Gray == m_info.FormatType)
|
|
{
|
|
dwOperationCode = 0xC0;
|
|
}
|
|
else
|
|
{
|
|
dwOperationCode = 0x00;
|
|
}
|
|
if (0 == (fEncodeType & 0x01) && m_info.Architecture == EriCode.RunlengthGamma)
|
|
{
|
|
context.InitGammaContext();
|
|
}
|
|
}
|
|
if (context.DecodeBytes (m_ptrArrangeBuf, m_nBlockSamples) < m_nBlockSamples)
|
|
{
|
|
throw new InvalidFormatException();
|
|
}
|
|
PerformOperation (dwOperationCode, nAllBlockLines, m_ptrLineBuf, ptrNextLineBuf);
|
|
ptrNextLineBuf += nColumnBufSamples;
|
|
|
|
pfnRestoreFunc();
|
|
|
|
m_ptrDstBlock += (int)(m_nDstPixelBytes * m_nBlockSize);
|
|
nLeftWidth -= (int) m_nBlockSize;
|
|
}
|
|
nLeftHeight -= (int)m_nBlockSize;
|
|
}
|
|
}
|
|
|
|
private void DecodeLossyImage (HuffmanDecodeContext context)
|
|
{
|
|
throw new NotImplementedException ("Lossy ERI compression not implemented");
|
|
}
|
|
|
|
void PerformOperation (uint dwOpCode, int nAllBlockLines, sbyte[] pNextLineBuf, int iNextLineIdx )
|
|
{
|
|
int i, j, k;
|
|
uint nArrangeCode, nColorOperation, nDiffOperation;
|
|
nColorOperation = dwOpCode & 0x0F;
|
|
nArrangeCode = (dwOpCode >> 4) & 0x03;
|
|
nDiffOperation = (dwOpCode >> 6) & 0x03;
|
|
|
|
if (0 == nArrangeCode)
|
|
{
|
|
Buffer.BlockCopy (m_ptrArrangeBuf, 0, m_ptrDecodeBuf, 0, (int)m_nBlockSamples);
|
|
if (0 == dwOpCode)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int pArrange = m_pArrangeTable[nArrangeCode];
|
|
for (i = 0; i < (int)m_nBlockSamples; i++)
|
|
{
|
|
m_ptrDecodeBuf[m_ptrTable[pArrange + i]] = m_ptrArrangeBuf[i];
|
|
}
|
|
}
|
|
m_pfnColorOperation[nColorOperation]();
|
|
|
|
int ptrNextBuf = 0; // m_ptrDecodeBuf
|
|
int ptrNextColBuf = 0; // m_ptrColumnBuf
|
|
if (0 != (nDiffOperation & 0x01))
|
|
{
|
|
for (i = 0; i < nAllBlockLines; i++)
|
|
{
|
|
sbyte nLastVal = m_ptrColumnBuf[ptrNextColBuf];
|
|
for (j = 0; j < (int)m_nBlockSize; j++)
|
|
{
|
|
nLastVal += m_ptrDecodeBuf[ptrNextBuf];
|
|
m_ptrDecodeBuf[ptrNextBuf++] = nLastVal;
|
|
}
|
|
m_ptrColumnBuf[ptrNextColBuf++] = nLastVal;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (i = 0; i < nAllBlockLines; i ++)
|
|
{
|
|
m_ptrColumnBuf[ptrNextColBuf++] = m_ptrDecodeBuf[ptrNextBuf + m_nBlockSize - 1];
|
|
ptrNextBuf += (int)m_nBlockSize;
|
|
}
|
|
}
|
|
int iNextDst = 0;
|
|
for (k = 0; k < (int)m_nChannelCount; k++)
|
|
{
|
|
sbyte[] ptrLastLine = pNextLineBuf;
|
|
int idxLastLine = iNextLineIdx;
|
|
for (i = 0; i < (int)m_nBlockSize; i++)
|
|
{
|
|
for (j = 0; j < (int)m_nBlockSize; j++)
|
|
{
|
|
m_ptrDecodeBuf[iNextDst+j] += ptrLastLine[idxLastLine+j];
|
|
}
|
|
ptrLastLine = m_ptrDecodeBuf;
|
|
idxLastLine = iNextDst;
|
|
iNextDst += (int)m_nBlockSize;
|
|
}
|
|
Buffer.BlockCopy (ptrLastLine, idxLastLine, pNextLineBuf, iNextLineIdx, (int)m_nBlockSize);
|
|
iNextLineIdx += (int)m_nBlockSize;
|
|
}
|
|
}
|
|
|
|
PtrProcedure GetLLRestoreFunc (int fdwFormatType, int dwBitsPerPixel)
|
|
{
|
|
switch (dwBitsPerPixel)
|
|
{
|
|
case 32:
|
|
if ((int)EriImage.RGBA == fdwFormatType)
|
|
{
|
|
Format = PixelFormats.Bgra32;
|
|
return RestoreRGBA32;
|
|
}
|
|
Format = PixelFormats.Bgr32;
|
|
return RestoreRGB24;
|
|
case 24:
|
|
Format = PixelFormats.Bgr24;
|
|
return RestoreRGB24;
|
|
case 16:
|
|
Format = PixelFormats.Bgr555;
|
|
return RestoreRGB16;
|
|
case 8:
|
|
if (null == Palette)
|
|
Format = PixelFormats.Gray8;
|
|
else
|
|
Format = PixelFormats.Indexed8;
|
|
return RestoreGray8;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
void RestoreRGBA32 ()
|
|
{
|
|
int ptrDstLine = m_ptrDstBlock;
|
|
int ptrSrcLine = 0; //m_ptrDecodeBuf;
|
|
int nBlockSamples = (int)m_nBlockArea;
|
|
int nBlockSamplesX3 = nBlockSamples * 3;
|
|
|
|
for (uint y = 0; y < m_nDstHeight; y++)
|
|
{
|
|
int ptrDstNext = ptrDstLine;
|
|
int ptrSrcNext = ptrSrcLine;
|
|
|
|
for (uint x = 0; x < m_nDstWidth; x++)
|
|
{
|
|
m_output[ptrDstNext++] = (byte)m_ptrDecodeBuf[ptrSrcNext];
|
|
m_output[ptrDstNext++] = (byte)m_ptrDecodeBuf[ptrSrcNext + nBlockSamples];
|
|
m_output[ptrDstNext++] = (byte)m_ptrDecodeBuf[ptrSrcNext + nBlockSamples * 2];
|
|
m_output[ptrDstNext++] = (byte)m_ptrDecodeBuf[ptrSrcNext + nBlockSamplesX3];
|
|
ptrSrcNext ++;
|
|
}
|
|
ptrSrcLine += (int)m_nBlockSize;
|
|
ptrDstLine += m_nDstLineBytes;
|
|
}
|
|
}
|
|
|
|
void RestoreRGB24()
|
|
{
|
|
int ptrDstLine = m_ptrDstBlock;
|
|
int ptrSrcLine = 0; //m_ptrDecodeBuf;
|
|
int nBytesPerPixel = m_nDstPixelBytes;
|
|
int nBlockSamples = (int)m_nBlockArea;
|
|
|
|
for (uint y = 0; y < m_nDstHeight; y++)
|
|
{
|
|
int ptrDstNext = ptrDstLine;
|
|
int ptrSrcNext = ptrSrcLine;
|
|
|
|
for (uint x = 0; x < m_nDstWidth; x++)
|
|
{
|
|
m_output[ptrDstNext] = (byte)m_ptrDecodeBuf[ptrSrcNext];
|
|
m_output[ptrDstNext+1] = (byte)m_ptrDecodeBuf[ptrSrcNext + nBlockSamples];
|
|
m_output[ptrDstNext+2] = (byte)m_ptrDecodeBuf[ptrSrcNext + nBlockSamples * 2];
|
|
ptrSrcNext ++;
|
|
ptrDstNext += nBytesPerPixel;
|
|
}
|
|
ptrSrcLine += (int)m_nBlockSize;
|
|
ptrDstLine += m_nDstLineBytes;
|
|
}
|
|
}
|
|
|
|
void RestoreRGB16()
|
|
{
|
|
int ptrDstLine = m_ptrDstBlock;
|
|
int ptrSrcLine = 0; //m_ptrDecodeBuf;
|
|
int nBlockSamples = (int)m_nBlockArea;
|
|
|
|
for (uint y = 0; y < m_nDstHeight; y++)
|
|
{
|
|
int ptrDstNext = ptrDstLine;
|
|
int ptrSrcNext = ptrSrcLine;
|
|
|
|
for (uint x = 0; x < m_nDstWidth; x++)
|
|
{
|
|
int word = (m_ptrDecodeBuf[ptrSrcNext] & 0x1F) |
|
|
((m_ptrDecodeBuf[ptrSrcNext + nBlockSamples] & 0x1F) << 5) |
|
|
((m_ptrDecodeBuf[ptrSrcNext + nBlockSamples * 2] & 0x1F) << 10);
|
|
m_output[ptrDstNext++] = (byte)word;
|
|
m_output[ptrDstNext++] = (byte)(word >> 8);
|
|
ptrSrcNext ++;
|
|
}
|
|
ptrSrcLine += (int)m_nBlockSize;
|
|
ptrDstLine += m_nDstLineBytes;
|
|
}
|
|
}
|
|
|
|
void RestoreGray8()
|
|
{
|
|
int ptrDstLine = m_ptrDstBlock;
|
|
int ptrSrcLine = 0; //m_ptrDecodeBuf;
|
|
|
|
for (uint y = 0; y < m_nDstHeight; y++)
|
|
{
|
|
Buffer.BlockCopy (m_ptrDecodeBuf, ptrSrcLine, m_output, ptrDstLine, (int)m_nDstWidth);
|
|
ptrSrcLine += (int)m_nBlockSize;
|
|
ptrDstLine += m_nDstLineBytes;
|
|
}
|
|
}
|
|
|
|
void ColorOperation0000 ()
|
|
{
|
|
}
|
|
|
|
void ColorOperation0101 ()
|
|
{
|
|
int ptrNext = 0; // m_ptrDecodeBuf;
|
|
uint nChSamples = m_nBlockArea;
|
|
uint nRepCount = m_nBlockArea;
|
|
do
|
|
{
|
|
sbyte nBase = m_ptrDecodeBuf[ptrNext];
|
|
m_ptrDecodeBuf[ptrNext++ + nChSamples] += nBase;
|
|
}
|
|
while (0 != --nRepCount);
|
|
}
|
|
|
|
void ColorOperation0110 ()
|
|
{
|
|
int ptrNext = 0; // m_ptrDecodeBuf;
|
|
uint nChSamples = m_nBlockArea * 2;
|
|
uint nRepCount = m_nBlockArea;
|
|
do
|
|
{
|
|
sbyte nBase = m_ptrDecodeBuf[ptrNext];
|
|
m_ptrDecodeBuf[ptrNext++ + nChSamples] += nBase;
|
|
}
|
|
while (0 != --nRepCount);
|
|
}
|
|
|
|
void ColorOperation0111 ()
|
|
{
|
|
int ptrNext = 0; // m_ptrDecodeBuf;
|
|
uint nChSamples = m_nBlockArea;
|
|
uint nRepCount = m_nBlockArea;
|
|
do
|
|
{
|
|
sbyte nBase = m_ptrDecodeBuf[ptrNext];
|
|
m_ptrDecodeBuf[ptrNext + nChSamples] += nBase;
|
|
m_ptrDecodeBuf[ptrNext + nChSamples * 2] += nBase;
|
|
ptrNext ++;
|
|
}
|
|
while (0 != --nRepCount);
|
|
}
|
|
|
|
void ColorOperation1001 ()
|
|
{
|
|
int ptrNext = 0; //m_ptrDecodeBuf;
|
|
uint nChSamples = m_nBlockArea;
|
|
uint nRepCount = m_nBlockArea;
|
|
do
|
|
{
|
|
sbyte nBase = m_ptrDecodeBuf[ptrNext + nChSamples];
|
|
m_ptrDecodeBuf[ptrNext++] += nBase;
|
|
}
|
|
while (0 != --nRepCount);
|
|
}
|
|
|
|
void ColorOperation1010 ()
|
|
{
|
|
int ptrNext = 0; // m_ptrDecodeBuf;
|
|
uint nChSamples = m_nBlockArea;
|
|
uint nRepCount = m_nBlockArea;
|
|
do
|
|
{
|
|
sbyte nBase = m_ptrDecodeBuf[ptrNext + nChSamples];
|
|
m_ptrDecodeBuf[ptrNext++ + nChSamples * 2] += nBase;
|
|
}
|
|
while (0 != --nRepCount);
|
|
}
|
|
|
|
void ColorOperation1011 ()
|
|
{
|
|
int ptrNext = 0; //m_ptrDecodeBuf;
|
|
uint nChSamples = m_nBlockArea;
|
|
uint nRepCount = m_nBlockArea;
|
|
do
|
|
{
|
|
sbyte nBase = m_ptrDecodeBuf[ptrNext + nChSamples];
|
|
m_ptrDecodeBuf[ptrNext] += nBase;
|
|
m_ptrDecodeBuf[ptrNext + nChSamples * 2] += nBase;
|
|
ptrNext ++;
|
|
}
|
|
while (0 != --nRepCount);
|
|
}
|
|
|
|
void ColorOperation1101 ()
|
|
{
|
|
int ptrNext = 0; //m_ptrDecodeBuf;
|
|
uint nChSamples = m_nBlockArea * 2;
|
|
uint nRepCount = m_nBlockArea;
|
|
do
|
|
{
|
|
sbyte nBase = m_ptrDecodeBuf[ptrNext + nChSamples];
|
|
m_ptrDecodeBuf[ptrNext++] += nBase;
|
|
}
|
|
while (0 != --nRepCount);
|
|
}
|
|
|
|
void ColorOperation1110 ()
|
|
{
|
|
int ptrNext = 0; // m_ptrDecodeBuf;
|
|
uint nChSamples = m_nBlockArea;
|
|
uint nRepCount = m_nBlockArea;
|
|
do
|
|
{
|
|
sbyte nBase = m_ptrDecodeBuf[ptrNext + nChSamples * 2];
|
|
m_ptrDecodeBuf[ptrNext++ + nChSamples] += nBase;
|
|
}
|
|
while (0 != --nRepCount);
|
|
}
|
|
|
|
void ColorOperation1111 ()
|
|
{
|
|
int ptrNext = 0; // m_ptrDecodeBuf;
|
|
uint nChSamples = m_nBlockArea;
|
|
uint nRepCount = m_nBlockArea;
|
|
do
|
|
{
|
|
sbyte nBase = m_ptrDecodeBuf[ptrNext + nChSamples * 2];
|
|
m_ptrDecodeBuf[ptrNext] += nBase;
|
|
m_ptrDecodeBuf[ptrNext + nChSamples] += nBase;
|
|
ptrNext ++;
|
|
}
|
|
while (0 != --nRepCount);
|
|
}
|
|
}
|
|
|
|
internal static class Erina
|
|
{
|
|
public const int CodeFlag = int.MinValue;
|
|
public const int HuffmanEscape = 0x7FFFFFFF;
|
|
public const int HuffmanNull = 0x8000;
|
|
public const int HuffmanMax = 0x4000;
|
|
public const int HuffmanRoot = 0x200;
|
|
};
|
|
|
|
internal class HuffmanNode
|
|
{
|
|
public ushort Weight;
|
|
public ushort Parent;
|
|
public int ChildCode;
|
|
|
|
public void CopyFrom (HuffmanNode other)
|
|
{
|
|
this.Weight = other.Weight;
|
|
this.Parent = other.Parent;
|
|
this.ChildCode = other.ChildCode;
|
|
}
|
|
}
|
|
|
|
internal class HuffmanTree
|
|
{
|
|
public HuffmanNode[] m_hnTree = new HuffmanNode[0x201];
|
|
public int[] m_iSymLookup = new int[0x100];
|
|
public int m_iEscape;
|
|
public int m_iTreePointer;
|
|
|
|
public HuffmanTree ()
|
|
{
|
|
Initialize();
|
|
}
|
|
|
|
public void Initialize ()
|
|
{
|
|
for (int i = 0; i < 0x201; i++)
|
|
{
|
|
m_hnTree[i] = new HuffmanNode();
|
|
}
|
|
for (int i = 0; i < 0x100; i++)
|
|
{
|
|
m_iSymLookup[i] = (int)Erina.HuffmanNull;
|
|
}
|
|
m_iEscape = (int)Erina.HuffmanNull;
|
|
m_iTreePointer = (int)Erina.HuffmanRoot;
|
|
m_hnTree[Erina.HuffmanRoot].Weight = 0;
|
|
m_hnTree[Erina.HuffmanRoot].Parent = Erina.HuffmanNull;
|
|
m_hnTree[Erina.HuffmanRoot].ChildCode = Erina.HuffmanNull;
|
|
}
|
|
|
|
public void IncreaseOccuredCount (int iEntry)
|
|
{
|
|
m_hnTree[iEntry].Weight++;
|
|
Normalize (iEntry);
|
|
if (m_hnTree[Erina.HuffmanRoot].Weight >= Erina.HuffmanMax)
|
|
{
|
|
HalfAndRebuild();
|
|
}
|
|
}
|
|
|
|
private void RecountOccuredCount (int iParent)
|
|
{
|
|
int iChild = m_hnTree[iParent].ChildCode;
|
|
m_hnTree[iParent].Weight = (ushort)(m_hnTree[iChild].Weight + m_hnTree[iChild + 1].Weight);
|
|
}
|
|
|
|
private void Normalize (int iEntry)
|
|
{
|
|
while (iEntry < Erina.HuffmanRoot)
|
|
{
|
|
int iSwap = iEntry + 1;
|
|
ushort weight = m_hnTree[iEntry].Weight;
|
|
while (iSwap < Erina.HuffmanRoot)
|
|
{
|
|
if (m_hnTree[iSwap].Weight >= weight)
|
|
break;
|
|
++iSwap;
|
|
}
|
|
if (iEntry == --iSwap)
|
|
{
|
|
iEntry = m_hnTree[iEntry].Parent;
|
|
RecountOccuredCount (iEntry);
|
|
continue;
|
|
}
|
|
int iChild, nCode;
|
|
if (0 == (m_hnTree[iEntry].ChildCode & Erina.CodeFlag))
|
|
{
|
|
iChild = m_hnTree[iEntry].ChildCode;
|
|
m_hnTree[iChild].Parent = (ushort)iSwap;
|
|
m_hnTree[iChild + 1].Parent = (ushort)iSwap;
|
|
}
|
|
else
|
|
{
|
|
nCode = m_hnTree[iEntry].ChildCode & ~Erina.CodeFlag;
|
|
if (nCode != Erina.HuffmanEscape)
|
|
m_iSymLookup[nCode & 0xFF] = iSwap;
|
|
else
|
|
m_iEscape = iSwap;
|
|
}
|
|
if (0 == (m_hnTree[iSwap].ChildCode & Erina.CodeFlag))
|
|
{
|
|
iChild = m_hnTree[iSwap].ChildCode;
|
|
m_hnTree[iChild].Parent = (ushort)iEntry;
|
|
m_hnTree[iChild+1].Parent = (ushort)iEntry;
|
|
}
|
|
else
|
|
{
|
|
nCode = m_hnTree[iSwap].ChildCode & ~Erina.CodeFlag;
|
|
if (nCode != Erina.HuffmanEscape)
|
|
m_iSymLookup[nCode & 0xFF] = iEntry;
|
|
else
|
|
m_iEscape = iEntry;
|
|
}
|
|
var node = m_hnTree[iSwap]; // XXX
|
|
ushort iEntryParent = m_hnTree[iEntry].Parent;
|
|
ushort iSwapParent = m_hnTree[iSwap].Parent;
|
|
|
|
m_hnTree[iSwap] = m_hnTree[iEntry];
|
|
m_hnTree[iEntry] = node;
|
|
m_hnTree[iSwap].Parent = iSwapParent;
|
|
m_hnTree[iEntry].Parent = iEntryParent;
|
|
|
|
RecountOccuredCount (iSwapParent);
|
|
iEntry = iSwapParent;
|
|
}
|
|
}
|
|
|
|
public void AddNewEntry (int nNewCode)
|
|
{
|
|
if (m_iTreePointer > 0)
|
|
{
|
|
m_iTreePointer -= 2;
|
|
int i = m_iTreePointer;
|
|
var phnNew = m_hnTree[i];
|
|
phnNew.Weight = 1;
|
|
phnNew.ChildCode = Erina.CodeFlag | nNewCode;
|
|
m_iSymLookup[nNewCode & 0xFF] = i ;
|
|
|
|
var phnRoot = m_hnTree[Erina.HuffmanRoot];
|
|
if (phnRoot.ChildCode != Erina.HuffmanNull)
|
|
{
|
|
var phnParent = m_hnTree[i + 2];
|
|
var phnChild = m_hnTree[i + 1];
|
|
phnChild.CopyFrom (phnParent); // m_hnTree[i + 1] = m_hnTree[i + 2];
|
|
|
|
if (0 != (phnChild.ChildCode & Erina.CodeFlag))
|
|
{
|
|
int nCode = phnChild.ChildCode & ~Erina.CodeFlag;
|
|
if (nCode != Erina.HuffmanEscape)
|
|
m_iSymLookup[nCode & 0xFF] = i + 1;
|
|
else
|
|
m_iEscape = i + 1;
|
|
}
|
|
phnParent.Weight = (ushort)(phnNew.Weight + phnChild.Weight);
|
|
phnParent.Parent = phnChild.Parent;
|
|
phnParent.ChildCode = i;
|
|
|
|
phnNew.Parent = phnChild.Parent = (ushort)(i + 2);
|
|
Normalize (i + 2);
|
|
}
|
|
else
|
|
{
|
|
phnNew.Parent = Erina.HuffmanRoot;
|
|
m_iEscape = i + 1;
|
|
var phnEscape = m_hnTree[m_iEscape];
|
|
phnEscape.Weight = 1;
|
|
phnEscape.Parent = Erina.HuffmanRoot;
|
|
phnEscape.ChildCode = Erina.CodeFlag | Erina.HuffmanEscape;
|
|
|
|
phnRoot.Weight = 2;
|
|
phnRoot.ChildCode = i;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int i = m_iTreePointer;
|
|
var phnEntry = m_hnTree[i];
|
|
if (phnEntry.ChildCode == (Erina.CodeFlag | Erina.HuffmanEscape))
|
|
{
|
|
phnEntry = m_hnTree[i + 1];
|
|
}
|
|
phnEntry.ChildCode = Erina.CodeFlag | nNewCode;
|
|
}
|
|
}
|
|
|
|
private void HalfAndRebuild ()
|
|
{
|
|
int i;
|
|
int iNextEntry = Erina.HuffmanRoot;
|
|
for (i = Erina.HuffmanRoot - 1; i >= m_iTreePointer; i--)
|
|
{
|
|
if (0 != (m_hnTree[i].ChildCode & Erina.CodeFlag))
|
|
{
|
|
m_hnTree[i].Weight = (ushort)((m_hnTree[i].Weight + 1) >> 1);
|
|
m_hnTree[iNextEntry--].CopyFrom (m_hnTree[i]);
|
|
}
|
|
}
|
|
++iNextEntry;
|
|
|
|
int iChild, nCode;
|
|
i = m_iTreePointer;
|
|
for (;;)
|
|
{
|
|
m_hnTree[i].CopyFrom (m_hnTree[iNextEntry]);
|
|
m_hnTree[i + 1].CopyFrom (m_hnTree[iNextEntry + 1]);
|
|
iNextEntry += 2;
|
|
var phnChild1 = m_hnTree[i];
|
|
var phnChild2 = m_hnTree[i + 1];
|
|
|
|
if (0 == (phnChild1.ChildCode & Erina.CodeFlag))
|
|
{
|
|
iChild = phnChild1.ChildCode;
|
|
m_hnTree[iChild].Parent = (ushort)i;
|
|
m_hnTree[iChild + 1].Parent = (ushort)i;
|
|
}
|
|
else
|
|
{
|
|
nCode = phnChild1.ChildCode & ~Erina.CodeFlag;
|
|
if (Erina.HuffmanEscape == nCode)
|
|
m_iEscape = i;
|
|
else
|
|
m_iSymLookup[nCode & 0xFF] = i;
|
|
}
|
|
if (0 == (phnChild2.ChildCode & Erina.CodeFlag))
|
|
{
|
|
iChild = phnChild2.ChildCode;
|
|
m_hnTree[iChild].Parent = (ushort)(i + 1);
|
|
m_hnTree[iChild + 1].Parent = (ushort)(i + 1);
|
|
}
|
|
else
|
|
{
|
|
nCode = phnChild2.ChildCode & ~Erina.CodeFlag;
|
|
if (Erina.HuffmanEscape == nCode)
|
|
m_iEscape = i + 1;
|
|
else
|
|
m_iSymLookup[nCode & 0xFF] = i + 1;
|
|
}
|
|
ushort weight = (ushort)(phnChild1.Weight + phnChild2.Weight);
|
|
|
|
if (iNextEntry <= Erina.HuffmanRoot)
|
|
{
|
|
int j = iNextEntry;
|
|
for (;;)
|
|
{
|
|
if (weight <= m_hnTree[j].Weight)
|
|
{
|
|
m_hnTree[j - 1].Weight = weight;
|
|
m_hnTree[j - 1].ChildCode = i;
|
|
break;
|
|
}
|
|
m_hnTree[j - 1].CopyFrom (m_hnTree[j]);
|
|
if (++j > Erina.HuffmanRoot)
|
|
{
|
|
m_hnTree[Erina.HuffmanRoot].Weight = weight;
|
|
m_hnTree[Erina.HuffmanRoot].ChildCode = i;
|
|
break;
|
|
}
|
|
}
|
|
--iNextEntry;
|
|
}
|
|
else
|
|
{
|
|
m_hnTree[Erina.HuffmanRoot].Weight = weight;
|
|
m_hnTree[Erina.HuffmanRoot].Parent = Erina.HuffmanNull;
|
|
m_hnTree[Erina.HuffmanRoot].ChildCode = i;
|
|
phnChild1.Parent = Erina.HuffmanRoot;
|
|
phnChild2.Parent = Erina.HuffmanRoot;
|
|
break;
|
|
}
|
|
i += 2;
|
|
}
|
|
}
|
|
}
|
|
|
|
internal class RLEDecodeContext : ERISADecodeContext
|
|
{
|
|
protected int m_flgZero;
|
|
protected uint m_nLength;
|
|
|
|
public RLEDecodeContext (uint nBufferingSize) : base (nBufferingSize)
|
|
{
|
|
}
|
|
|
|
public void InitGammaContext ()
|
|
{
|
|
m_flgZero = GetABit();
|
|
m_nLength = 0;
|
|
}
|
|
|
|
public override uint DecodeBytes (Array ptrDst, uint nCount)
|
|
{
|
|
return DecodeGammaCodeBytes (ptrDst as sbyte[], nCount);
|
|
}
|
|
|
|
public uint DecodeGammaCodeBytes (sbyte[] ptrDst, uint nCount)
|
|
{
|
|
int dst = 0;
|
|
uint nDecoded = 0;
|
|
|
|
if (m_nLength == 0)
|
|
{
|
|
m_nLength = (uint)GetGammaCode();
|
|
if (0 == m_nLength)
|
|
{
|
|
return nDecoded;
|
|
}
|
|
}
|
|
for (;;)
|
|
{
|
|
uint nRepeat = Math.Min (m_nLength, nCount);
|
|
Debug.Assert (nRepeat > 0);
|
|
m_nLength -= nRepeat;
|
|
nCount -= nRepeat;
|
|
|
|
if (0 == m_flgZero)
|
|
{
|
|
nDecoded += nRepeat;
|
|
do
|
|
{
|
|
ptrDst[dst++] = 0;
|
|
}
|
|
while (0 != --nRepeat);
|
|
}
|
|
else
|
|
{
|
|
do
|
|
{
|
|
sbyte nSign = (sbyte)GetABit();
|
|
sbyte nCode = (sbyte)GetGammaCode();
|
|
if (0 == nCode)
|
|
{
|
|
return nDecoded;
|
|
}
|
|
nDecoded ++;
|
|
ptrDst[dst++] = (sbyte)((nCode ^ nSign) - nSign);
|
|
}
|
|
while (0 != --nRepeat);
|
|
}
|
|
if (0 == nCount)
|
|
{
|
|
if (0 == m_nLength)
|
|
{
|
|
m_flgZero = ~m_flgZero;
|
|
}
|
|
return nDecoded;
|
|
}
|
|
m_flgZero = ~m_flgZero;
|
|
m_nLength = (uint) GetGammaCode();
|
|
if (0 == m_nLength)
|
|
{
|
|
return nDecoded;
|
|
}
|
|
}
|
|
}
|
|
|
|
protected int GetGammaCode()
|
|
{
|
|
if (!PrefetchBuffer())
|
|
{
|
|
return 0;
|
|
}
|
|
m_nIntBufCount--;
|
|
uint dwIntBuf = m_dwIntBuffer;
|
|
m_dwIntBuffer <<= 1;
|
|
if (0 == (dwIntBuf & 0x80000000))
|
|
{
|
|
return 1;
|
|
}
|
|
if (!PrefetchBuffer())
|
|
{
|
|
return 0;
|
|
}
|
|
int nCode = 0;
|
|
if ((0 != (~m_dwIntBuffer & 0x55000000)) && (m_nIntBufCount >= 8))
|
|
{
|
|
uint i = (m_dwIntBuffer >> 24) << 1;
|
|
nCode = nGammaCodeLookup[i];
|
|
int nBitCount = nGammaCodeLookup[i + 1];
|
|
Debug.Assert (nBitCount <= m_nIntBufCount);
|
|
Debug.Assert (nCode > 0);
|
|
m_nIntBufCount -= nBitCount;
|
|
m_dwIntBuffer <<= nBitCount;
|
|
return nCode;
|
|
}
|
|
int nBase = 2;
|
|
for (;;)
|
|
{
|
|
if (m_nIntBufCount >= 2)
|
|
{
|
|
dwIntBuf = m_dwIntBuffer;
|
|
m_dwIntBuffer <<= 2;
|
|
nCode = (int)(((uint)nCode << 1) | (dwIntBuf >> 31));
|
|
m_nIntBufCount -= 2;
|
|
if (0 == (dwIntBuf & 0x40000000))
|
|
{
|
|
return nCode + nBase;
|
|
}
|
|
nBase <<= 1;
|
|
}
|
|
else
|
|
{
|
|
if (!PrefetchBuffer())
|
|
{
|
|
return 0;
|
|
}
|
|
nCode = (int)(((uint)nCode << 1) | (m_dwIntBuffer >> 31));
|
|
m_nIntBufCount --;
|
|
m_dwIntBuffer <<= 1;
|
|
|
|
if (!PrefetchBuffer())
|
|
{
|
|
return 0;
|
|
}
|
|
dwIntBuf = m_dwIntBuffer;
|
|
m_nIntBufCount --;
|
|
m_dwIntBuffer <<= 1;
|
|
if (0 == (dwIntBuf & 0x80000000))
|
|
{
|
|
return nCode + nBase;
|
|
}
|
|
nBase <<= 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
static readonly byte[] nGammaCodeLookup = new byte[0x200]
|
|
{
|
|
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
|
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
|
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
|
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
|
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
|
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
|
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
|
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
|
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
|
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
|
8, 6, 8, 6, 8, 6, 8, 6, 16, 8, 0xff, 0xff, 17, 8, 0xff, 0xff,
|
|
9, 6, 9, 6, 9, 6, 9, 6, 18, 8, 0xff, 0xff, 19, 8, 0xff, 0xff,
|
|
5, 4, 5, 4, 5, 4, 5, 4, 5, 4, 5, 4, 5, 4, 5, 4,
|
|
5, 4, 5, 4, 5, 4, 5, 4, 5, 4, 5, 4, 5, 4, 5, 4,
|
|
10, 6, 10, 6, 10, 6, 10, 6, 20, 8, 0xff, 0xff, 21, 8, 0xff, 0xff,
|
|
11, 6, 11, 6, 11, 6, 11, 6, 22, 8, 0xff, 0xff, 23, 8, 0xff, 0xff,
|
|
3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2,
|
|
3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2,
|
|
3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2,
|
|
3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2,
|
|
3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2,
|
|
3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2,
|
|
3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2,
|
|
3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2,
|
|
6, 4, 6, 4, 6, 4, 6, 4, 6, 4, 6, 4, 6, 4, 6, 4,
|
|
6, 4, 6, 4, 6, 4, 6, 4, 6, 4, 6, 4, 6, 4, 6, 4,
|
|
12, 6, 12, 6, 12, 6, 12, 6, 24, 8, 0xff, 0xff, 25, 8, 0xff, 0xff,
|
|
13, 6, 13, 6, 13, 6, 13, 6, 26, 8, 0xff, 0xff, 27, 8, 0xff, 0xff,
|
|
7, 4, 7, 4, 7, 4, 7, 4, 7, 4, 7, 4, 7, 4, 7, 4,
|
|
7, 4, 7, 4, 7, 4, 7, 4, 7, 4, 7, 4, 7, 4, 7, 4,
|
|
14, 6, 14, 6, 14, 6, 14, 6, 28, 8, 0xff, 0xff, 29, 8, 0xff, 0xff,
|
|
15, 6, 15, 6, 15, 6, 15, 6, 30, 8, 0xff, 0xff, 31, 8, 0xff, 0xff
|
|
};
|
|
}
|
|
|
|
internal class HuffmanDecodeContext : RLEDecodeContext
|
|
{
|
|
int m_dwERINAFlags;
|
|
HuffmanTree m_pLastHuffmanTree;
|
|
HuffmanTree[] m_ppHuffmanTree;
|
|
|
|
// ERINAEncodingFlag
|
|
public const int efERINAOrder0 = 0x0000;
|
|
public const int efERINAOrder1 = 0x0001;
|
|
|
|
public HuffmanDecodeContext (uint nBufferingSize) : base (nBufferingSize)
|
|
{
|
|
}
|
|
|
|
public void PrepareToDecodeERINACode (int flags = efERINAOrder1)
|
|
{
|
|
int i;
|
|
if (null == m_ppHuffmanTree)
|
|
{
|
|
m_ppHuffmanTree = new HuffmanTree[0x101];
|
|
}
|
|
m_dwERINAFlags = flags;
|
|
m_nLength = 0;
|
|
if (efERINAOrder0 == flags)
|
|
{
|
|
m_ppHuffmanTree[0] = new HuffmanTree();
|
|
m_ppHuffmanTree[0x100] = new HuffmanTree();
|
|
for (i = 1; i < 0x100; i++)
|
|
{
|
|
m_ppHuffmanTree[i] = m_ppHuffmanTree[0];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (i = 0; i < 0x101; i++)
|
|
{
|
|
m_ppHuffmanTree[i] = new HuffmanTree();
|
|
}
|
|
}
|
|
m_pLastHuffmanTree = m_ppHuffmanTree[0];
|
|
}
|
|
|
|
public override uint DecodeBytes (Array ptrDst, uint nCount)
|
|
{
|
|
return DecodeErinaCodeBytes (ptrDst as sbyte[], nCount);
|
|
}
|
|
|
|
public uint DecodeErinaCodeBytes (sbyte[] ptrDst, uint nCount)
|
|
{
|
|
var tree = m_pLastHuffmanTree;
|
|
int symbol, length;
|
|
uint i = 0;
|
|
if (m_nLength > 0)
|
|
{
|
|
length = (int)Math.Min (m_nLength, nCount);
|
|
m_nLength -= (uint)length;
|
|
do
|
|
{
|
|
ptrDst[i++] = 0;
|
|
}
|
|
while (0 != --length);
|
|
}
|
|
while (i < nCount)
|
|
{
|
|
symbol = GetHuffmanCode (tree);
|
|
if (Erina.HuffmanEscape == symbol)
|
|
{
|
|
break;
|
|
}
|
|
ptrDst[i++] = (sbyte)symbol;
|
|
|
|
if (0 == symbol)
|
|
{
|
|
length = GetLengthHuffman (m_ppHuffmanTree[0x100]);
|
|
if (Erina.HuffmanEscape == length)
|
|
{
|
|
break;
|
|
}
|
|
if (0 != --length)
|
|
{
|
|
m_nLength = (uint)length;
|
|
if (i + length > nCount)
|
|
{
|
|
length = (int)(nCount - i);
|
|
}
|
|
m_nLength -= (uint)length;
|
|
while (length > 0)
|
|
{
|
|
ptrDst[i++] = 0;
|
|
--length;
|
|
}
|
|
}
|
|
}
|
|
tree = m_ppHuffmanTree[symbol & 0xFF];
|
|
}
|
|
m_pLastHuffmanTree = tree;
|
|
return i;
|
|
}
|
|
|
|
private int GetLengthHuffman (HuffmanTree tree)
|
|
{
|
|
int nCode;
|
|
if (tree.m_iEscape != Erina.HuffmanNull)
|
|
{
|
|
int iEntry = Erina.HuffmanRoot;
|
|
int iChild = tree.m_hnTree[Erina.HuffmanRoot].ChildCode;
|
|
do
|
|
{
|
|
if (!PrefetchBuffer())
|
|
{
|
|
return Erina.HuffmanEscape;
|
|
}
|
|
iEntry = iChild + (int)(m_dwIntBuffer >> 31);
|
|
iChild = tree.m_hnTree[iEntry].ChildCode;
|
|
m_dwIntBuffer <<= 1;
|
|
--m_nIntBufCount;
|
|
}
|
|
while (0 == (iChild & Erina.CodeFlag));
|
|
|
|
if ((m_dwERINAFlags != efERINAOrder0) ||
|
|
(tree.m_hnTree[Erina.HuffmanRoot].Weight < Erina.HuffmanMax-1))
|
|
{
|
|
tree.IncreaseOccuredCount (iEntry);
|
|
}
|
|
nCode = iChild & ~Erina.CodeFlag;
|
|
if (nCode != Erina.HuffmanEscape)
|
|
{
|
|
return nCode ;
|
|
}
|
|
}
|
|
nCode = GetGammaCode();
|
|
if (-1 == nCode)
|
|
{
|
|
return Erina.HuffmanEscape;
|
|
}
|
|
tree.AddNewEntry (nCode);
|
|
return nCode;
|
|
}
|
|
|
|
public int GetHuffmanCode (HuffmanTree tree)
|
|
{
|
|
int nCode;
|
|
if (tree.m_iEscape != Erina.HuffmanNull)
|
|
{
|
|
int iEntry = Erina.HuffmanRoot;
|
|
int iChild = tree.m_hnTree[Erina.HuffmanRoot].ChildCode;
|
|
do
|
|
{
|
|
if (!PrefetchBuffer())
|
|
{
|
|
return Erina.HuffmanEscape;
|
|
}
|
|
iEntry = iChild + (int)(m_dwIntBuffer >> 31);
|
|
iChild = tree.m_hnTree[iEntry].ChildCode;
|
|
m_dwIntBuffer <<= 1;
|
|
--m_nIntBufCount;
|
|
}
|
|
while (0 == (iChild & Erina.CodeFlag));
|
|
|
|
if ((m_dwERINAFlags != efERINAOrder0) ||
|
|
(tree.m_hnTree[Erina.HuffmanRoot].Weight < Erina.HuffmanMax-1))
|
|
{
|
|
tree.IncreaseOccuredCount (iEntry);
|
|
}
|
|
nCode = iChild & ~Erina.CodeFlag;
|
|
if (nCode != Erina.HuffmanEscape)
|
|
{
|
|
return nCode;
|
|
}
|
|
}
|
|
nCode = (int)GetNBits (8);
|
|
tree.AddNewEntry (nCode);
|
|
|
|
return nCode;
|
|
}
|
|
}
|
|
}
|