git-svn-id: http://libopenmetaverse.googlecode.com/svn/trunk@1316 52acb1d6-8a22-11de-b505-999d5b087335
378 lines
14 KiB
C#
378 lines
14 KiB
C#
using System;
|
|
using System.IO;
|
|
using System.Drawing;
|
|
using System.Drawing.Imaging;
|
|
using System.Runtime.InteropServices;
|
|
|
|
namespace OpenJPEGNet
|
|
{
|
|
#if NO_UNSAFE
|
|
#else
|
|
|
|
public class OpenJPEG
|
|
{
|
|
// This structure is used to pass images back and forth for both encoding and decoding
|
|
[StructLayout(LayoutKind.Sequential, Pack = 4)]
|
|
private struct LibslImage
|
|
{
|
|
public IntPtr encoded; // encoded image data
|
|
public int length; // encoded image length
|
|
|
|
public IntPtr decoded; // decoded image data (8 bits per component, RGBA order)
|
|
public int width; // width of decoded image
|
|
public int height; // height of decoded image
|
|
public int components; // number of decoded components
|
|
}
|
|
|
|
// allocate encoded buffer based on length field
|
|
[DllImport("openjpeg-libsl.dll", CallingConvention = CallingConvention.Cdecl)]
|
|
private static extern bool LibslAllocEncoded(ref LibslImage image);
|
|
|
|
// allocate decoded buffer based on width and height fields
|
|
[DllImport("openjpeg-libsl.dll", CallingConvention = CallingConvention.Cdecl)]
|
|
private static extern bool LibslAllocDecoded(ref LibslImage image);
|
|
|
|
// free buffers
|
|
[DllImport("openjpeg-libsl.dll", CallingConvention = CallingConvention.Cdecl)]
|
|
private static extern bool LibslFree(ref LibslImage image);
|
|
|
|
// encode raw to jpeg2000
|
|
[DllImport("openjpeg-libsl.dll", CallingConvention = CallingConvention.Cdecl)]
|
|
private static extern bool LibslEncode(ref LibslImage image, bool lossless);
|
|
|
|
// decode jpeg2000 to raw
|
|
[DllImport("openjpeg-libsl.dll", CallingConvention = CallingConvention.Cdecl)]
|
|
private static extern bool LibslDecode(ref LibslImage image);
|
|
|
|
// encode
|
|
public static byte[] Encode(byte[] decoded, int width, int height, int components, bool lossless)
|
|
{
|
|
if (decoded.Length != width * height * components)
|
|
throw new ArgumentException("Length of decoded buffer does not match parameters");
|
|
|
|
LibslImage image = new LibslImage();
|
|
|
|
// allocate and copy to input buffer
|
|
image.width = width;
|
|
image.height = height;
|
|
image.components = components;
|
|
LibslAllocDecoded(ref image);
|
|
Marshal.Copy(decoded, 0, image.decoded, width * height * components);
|
|
|
|
// codec will allocate output buffer
|
|
LibslEncode(ref image, lossless);
|
|
|
|
// copy output buffer
|
|
byte[] encoded = new byte[image.length];
|
|
Marshal.Copy(image.encoded, encoded, 0, image.length);
|
|
|
|
// free buffers
|
|
LibslFree(ref image);
|
|
|
|
return encoded;
|
|
}
|
|
|
|
public static byte[] Encode(byte[] decoded, int width, int height, int components)
|
|
{
|
|
return Encode(decoded, width, height, components, false);
|
|
}
|
|
|
|
public static byte[] Decode(byte[] encoded, out int width, out int height, out int components)
|
|
{
|
|
LibslImage image = new LibslImage();
|
|
|
|
// allocate and copy to input buffer
|
|
image.length = encoded.Length;
|
|
LibslAllocEncoded(ref image);
|
|
Marshal.Copy(encoded, 0, image.encoded, encoded.Length);
|
|
|
|
// codec will allocate output buffer
|
|
LibslDecode(ref image);
|
|
|
|
// copy output buffer
|
|
byte[] decoded = new byte[image.width * image.height * image.components];
|
|
Marshal.Copy(image.decoded, decoded, 0, image.width * image.height * image.components);
|
|
|
|
// copy image dimensions
|
|
width = image.width;
|
|
height = image.height;
|
|
components = image.components;
|
|
|
|
// free buffers
|
|
LibslFree(ref image);
|
|
|
|
return decoded;
|
|
}
|
|
|
|
public const int TGA_HEADER_SIZE = 32;
|
|
|
|
public static byte[] DecodeToTGA(byte[] encoded)
|
|
{
|
|
int width, height, components;
|
|
byte[] decoded = Decode(encoded, out width, out height, out components);
|
|
|
|
byte[] tga = new byte[width * height * 4 + TGA_HEADER_SIZE];
|
|
int di = 0;
|
|
tga[di++] = 0; // idlength
|
|
tga[di++] = 0; // colormaptype = 0: no colormap
|
|
tga[di++] = 2; // image type = 2: uncompressed RGB
|
|
tga[di++] = 0; // color map spec is five zeroes for no color map
|
|
tga[di++] = 0; // color map spec is five zeroes for no color map
|
|
tga[di++] = 0; // color map spec is five zeroes for no color map
|
|
tga[di++] = 0; // color map spec is five zeroes for no color map
|
|
tga[di++] = 0; // color map spec is five zeroes for no color map
|
|
tga[di++] = 0; // x origin = two bytes
|
|
tga[di++] = 0; // x origin = two bytes
|
|
tga[di++] = 0; // y origin = two bytes
|
|
tga[di++] = 0; // y origin = two bytes
|
|
tga[di++] = (byte)(width & 0xFF); // width - low byte
|
|
tga[di++] = (byte)(width >> 8); // width - hi byte
|
|
tga[di++] = (byte)(height & 0xFF); // height - low byte
|
|
tga[di++] = (byte)(height >> 8); // height - hi byte
|
|
tga[di++] = 32; // 32 bits per pixel
|
|
tga[di++] = 40; // image descriptor byte
|
|
|
|
int si = 0;
|
|
|
|
switch (components)
|
|
{
|
|
case 5:
|
|
for (int i = 0; i < (width * height); i++)
|
|
{
|
|
tga[di++] = decoded[si + 2]; // blue
|
|
tga[di++] = decoded[si + 1]; // green
|
|
tga[di++] = decoded[si + 0]; // red
|
|
tga[di++] = decoded[si + 4]; // alpha
|
|
si += 5;
|
|
}
|
|
break;
|
|
case 4:
|
|
for (int i = 0; i < (width * height); i++)
|
|
{
|
|
tga[di++] = decoded[si + 2]; // blue
|
|
tga[di++] = decoded[si + 1]; // green
|
|
tga[di++] = decoded[si + 0]; // red
|
|
tga[di++] = decoded[si + 3]; // alpha
|
|
si += 4;
|
|
}
|
|
break;
|
|
case 3:
|
|
for (int i = 0; i < (width * height); i++)
|
|
{
|
|
tga[di++] = decoded[si + 2]; // blue
|
|
tga[di++] = decoded[si + 1]; // green
|
|
tga[di++] = decoded[si + 0]; // red
|
|
tga[di++] = 0xFF; // alpha
|
|
si += 3;
|
|
}
|
|
break;
|
|
case 2:
|
|
for (int i = 0; i < (width * height); i++)
|
|
{
|
|
tga[di++] = decoded[si + 0]; // blue
|
|
tga[di++] = decoded[si + 0]; // green
|
|
tga[di++] = decoded[si + 0]; // red
|
|
tga[di++] = decoded[si + 1]; // alpha
|
|
si += 2;
|
|
}
|
|
break;
|
|
case 1:
|
|
for (int i = 0; i < (width * height); i++)
|
|
{
|
|
tga[di++] = decoded[si]; // blue
|
|
tga[di++] = decoded[si]; // green
|
|
tga[di++] = decoded[si]; // red
|
|
tga[di++] = 0xFF; // alpha
|
|
si++;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
throw new Exception("Invalid number of components: " + components);
|
|
}
|
|
|
|
return tga;
|
|
}
|
|
|
|
public static Image DecodeToImage(byte[] encoded)
|
|
{
|
|
return LoadTGAClass.LoadTGA(new MemoryStream(DecodeToTGA(encoded)));
|
|
}
|
|
|
|
public unsafe static byte[] EncodeFromImage(Bitmap bitmap, bool lossless)
|
|
{
|
|
int numcomps;
|
|
BitmapData bd;
|
|
byte[] decoded;
|
|
int i = 0;
|
|
|
|
int bitmapWidth = bitmap.Width;
|
|
int bitmapHeight = bitmap.Height;
|
|
|
|
if ((bitmap.PixelFormat & PixelFormat.Alpha) != 0 || (bitmap.PixelFormat & PixelFormat.PAlpha) != 0)
|
|
{
|
|
// four layers, RGBA
|
|
numcomps = 4;
|
|
decoded = new byte[bitmapWidth * bitmapHeight * numcomps];
|
|
bd = bitmap.LockBits(new Rectangle(0, 0, bitmapWidth, bitmapHeight), ImageLockMode.ReadOnly,
|
|
PixelFormat.Format32bppArgb);
|
|
|
|
for (int y = 0; y < bitmapHeight; y++)
|
|
{
|
|
for (int x = 0; x < bitmapWidth; x++)
|
|
{
|
|
byte* pixel = (byte*)bd.Scan0;
|
|
pixel += (y * bitmapWidth + x) * numcomps;
|
|
|
|
// GDI+ gives us BGRA and we need to turn that in to RGBA
|
|
decoded[i++] = *(pixel + 2);
|
|
decoded[i++] = *(pixel + 1);
|
|
decoded[i++] = *(pixel);
|
|
decoded[i++] = *(pixel + 3);
|
|
pixel += 4;
|
|
}
|
|
}
|
|
}
|
|
else if (bitmap.PixelFormat == PixelFormat.Format16bppGrayScale)
|
|
{
|
|
// one layer
|
|
numcomps = 1;
|
|
decoded = new byte[bitmapWidth * bitmapHeight * numcomps];
|
|
bd = bitmap.LockBits(new Rectangle(0, 0, bitmapWidth, bitmapHeight), ImageLockMode.ReadOnly,
|
|
PixelFormat.Format16bppGrayScale);
|
|
|
|
for (int y = 0; y < bitmapHeight; y++)
|
|
{
|
|
for (int x = 0; x < bitmapWidth; x++)
|
|
{
|
|
byte* pixel = (byte*)bd.Scan0;
|
|
pixel += (y * bitmapWidth + x) * numcomps;
|
|
|
|
// turn 16 bit data in to 8 bit data (TODO: Does this work?)
|
|
decoded[i++] = *(pixel);
|
|
pixel += 2;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// three layers, RGB
|
|
numcomps = 3;
|
|
decoded = new byte[bitmapWidth * bitmapHeight * numcomps];
|
|
bd = bitmap.LockBits(new Rectangle(0, 0, bitmapWidth, bitmapHeight), ImageLockMode.ReadOnly,
|
|
PixelFormat.Format24bppRgb);
|
|
|
|
for (int y = 0; y < bitmapHeight; y++)
|
|
{
|
|
for (int x = 0; x < bitmapWidth; x++)
|
|
{
|
|
byte* pixel = (byte*)bd.Scan0;
|
|
pixel += (y * bitmapWidth + x) * numcomps;
|
|
|
|
decoded[i++] = *(pixel + 2);
|
|
decoded[i++] = *(pixel + 1);
|
|
decoded[i++] = *(pixel + 0);
|
|
pixel += 3;
|
|
}
|
|
}
|
|
}
|
|
|
|
bitmap.UnlockBits(bd);
|
|
byte[] encoded = Encode(decoded, bitmap.Width, bitmap.Height, numcomps, lossless);
|
|
return encoded;
|
|
}
|
|
|
|
public static byte[] ConvertComponents(byte[] source, int width, int height, int sourceComponents, int destComponents)
|
|
{
|
|
if (sourceComponents == destComponents)
|
|
return source;
|
|
|
|
int x, y, si = 0, di = 0;
|
|
byte r, g, b, alpha, bump;
|
|
byte[] dest = new byte[width*height*destComponents];
|
|
|
|
for (y = 0; y < height; y++)
|
|
{
|
|
for (x = 0; x < width; x++)
|
|
{
|
|
si = (y * width + x) * sourceComponents;
|
|
di = (y * width + x) * destComponents;
|
|
|
|
switch (sourceComponents)
|
|
{
|
|
case 1:
|
|
r = source[si];
|
|
g = source[si];
|
|
b = source[si];
|
|
bump = 0;
|
|
alpha = 255;
|
|
break;
|
|
|
|
case 3:
|
|
r = source[si];
|
|
g = source[si + 1];
|
|
b = source[si + 2];
|
|
bump = 0;
|
|
alpha = 255;
|
|
break;
|
|
|
|
case 4:
|
|
r = source[si];
|
|
g = source[si + 1];
|
|
b = source[si + 2];
|
|
bump = 0;
|
|
alpha = source[si + 3];
|
|
break;
|
|
|
|
case 5:
|
|
r = source[si];
|
|
g = source[si + 1];
|
|
b = source[si + 2];
|
|
bump = source[si + 3];
|
|
alpha = source[si + 4];
|
|
break;
|
|
|
|
default:
|
|
throw new ArgumentException("Invalid number of source components " + sourceComponents);
|
|
}
|
|
|
|
switch (destComponents)
|
|
{
|
|
case 1:
|
|
dest[di] = (byte)(((int)r + g + b) / 3);
|
|
break;
|
|
|
|
case 3:
|
|
dest[di] = r;
|
|
dest[di + 1] = g;
|
|
dest[di + 2] = b;
|
|
break;
|
|
|
|
case 4:
|
|
dest[di] = r;
|
|
dest[di + 1] = g;
|
|
dest[di + 2] = b;
|
|
dest[di + 3] = alpha;
|
|
break;
|
|
|
|
case 5:
|
|
dest[di] = r;
|
|
dest[di + 1] = g;
|
|
dest[di + 2] = b;
|
|
dest[di + 3] = bump;
|
|
dest[di + 4] = alpha;
|
|
break;
|
|
|
|
default:
|
|
throw new ArgumentException("Invalid number of dest components " + destComponents);
|
|
}
|
|
}
|
|
}
|
|
|
|
return dest;
|
|
}
|
|
}
|
|
|
|
#endif
|
|
}
|