Files
libremetaverse/openjpegnet/OpenJPEG.cs
John Hurliman 3a94792761 * Reworked the Login functions, added documentation and (untested) support for login redirects
* Converted _VisualParam_ back to a static array after further analysis
* Rewrote AppearanceManager to work with the VisualParam rewrite
* Cleaned up ImageTool
* Minor cleanup and FIXME note in name2key.exe
* Cleanup and FIXME note in Teleport.exe
* Reorganized the function naming in GridManager to make more sense (not completely done here)
* Added an enum for requesting the object or terrain layer for the map blocks, and more sun properties
* Made some of the MainAvatar properties read-only (eventually they will all be read-only)
* Added try/catch safeties in openjpegnet
* Finally killed the evil SecondLife.Tick()

git-svn-id: http://libopenmetaverse.googlecode.com/svn/trunk@905 52acb1d6-8a22-11de-b505-999d5b087335
2007-01-26 22:01:56 +00:00

567 lines
21 KiB
C#

using System;
using System.IO;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
namespace OpenJPEGNet
{
public class OpenJPEG
{
public const string OPENJPEG_VERSION = "1.0.0";
public const int TRUE = 1;
public const int FALSE = 0;
public const int MAX_PATH = 260;
public const int J2K_MAXRLVLS = 33;
public const int J2K_MAXBANDS = 3 * J2K_MAXRLVLS - 2;
public enum OPJ_PROG_ORDER
{
PROG_UNKNOWN = -1,
LRCP = 0,
RLCP = 1,
RPCL = 2,
PCRL = 3,
CPRL = 4
};
public enum OPJ_COLOR_SPACE
{
CLRSPC_UNKNOWN = -1,
CLRSPC_SRGB = 1,
CLRSPC_GRAY = 2,
CLRSPC_SYCC = 3
};
public enum OPJ_CODEC_FORMAT
{
CODEC_UNKNOWN = -1,
CODEC_J2K = 0,
CODEC_JPT = 1,
CODEC_JP2 = 2
};
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate void opj_msg_callback(string msg, IntPtr client_data);
[StructLayout(LayoutKind.Sequential,Pack=4)]
public unsafe struct opj_event_mgr_t
{
public opj_msg_callback error_handler;
public opj_msg_callback warning_handler;
public opj_msg_callback info_handler;
};
[StructLayout(LayoutKind.Sequential,Pack=4)]
public unsafe struct opj_poc_t
{
public int resno0;
public int compno0;
public int layno1;
public int resno1;
public int compno1;
public OPJ_PROG_ORDER prg;
public int tile;
public fixed char progorder[4];
};
[StructLayout(LayoutKind.Sequential,Pack=4)]
public unsafe struct opj_cparameters_t
{
public bool tile_size_on;
public int cp_tx0;
public int cp_ty0;
public int cp_tdx;
public int cp_tdy;
public int cp_disto_alloc;
public int cp_fixed_alloc;
public int cp_fixed_quality;
public int* cp_matrice;
public string cp_comment;
public int csty;
public OPJ_PROG_ORDER prog_order;
//[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
//public opj_poc_t[] POC;
public fixed int POC[256];
public int numpocs;
public int tcp_numlayers;
public fixed int tcp_rates[100];
public fixed float tcp_distoratio[100];
public int numresolution;
public int cblockw_init;
public int cblockh_init;
public int mode;
public int irreversible;
public int roi_compno;
public int roi_shift;
public int res_spec;
public fixed int prcw_init[J2K_MAXRLVLS];
public fixed int prch_init[J2K_MAXRLVLS];
public fixed char infile[MAX_PATH];
public fixed char outfile[MAX_PATH];
public int index_on;
public fixed char index[MAX_PATH];
public int image_offset_x0;
public int image_offset_y0;
public int subsampling_dx;
public int subsampling_dy;
public int decod_format;
public int cod_format;
};
[StructLayout(LayoutKind.Sequential,Pack=4)]
public unsafe struct opj_dparameters_t
{
public int cp_reduce;
public int cp_layer;
public fixed char infile[MAX_PATH];
public fixed char outfile[MAX_PATH];
public int decod_format;
public int cod_format;
};
[StructLayout(LayoutKind.Sequential,Pack=4)]
public unsafe struct opj_common_struct_t
{
void* event_mgr; //opj_event_mgr_t*
void* client_data;
bool is_decompressor;
OPJ_CODEC_FORMAT codec_format;
void* j2k_handle;
void* jp2_handle;
};
[StructLayout(LayoutKind.Sequential,Pack=4)]
public unsafe struct opj_cinfo_t
{
void* event_mgr; //opj_event_mgr_t*
void* client_data;
int is_decompressor;
int codec_format;
void* j2k_handle;
void* jp2_handle;
};
[StructLayout(LayoutKind.Sequential,Pack=4)]
public unsafe struct opj_dinfo_t
{
void* event_mgr; //opj_event_mgr_t*
void* client_data;
bool is_decompressor;
OPJ_CODEC_FORMAT codec_format;
void* j2k_handle;
void* jp2_handle;
};
public const int OPJ_STREAM_READ = 0x0001;
public const int OPJ_STREAM_WRITE = 0x0002;
[StructLayout(LayoutKind.Sequential,Pack=4)]
public unsafe struct opj_cio_t
{
public IntPtr cinfo;
public int openmode;
public byte* buffer;
public int length;
public byte* start;
public byte* end;
public byte* bp;
};
[StructLayout(LayoutKind.Sequential,Pack=4)]
public unsafe struct opj_image_comp_t
{
public int dx;
public int dy;
public int w;
public int h;
public int x0;
public int y0;
public int prec;
public int bpp;
public int sgnd;
public int resno_decoded;
public int factor;
public int* data;
};
[StructLayout(LayoutKind.Sequential,Pack=4)]
public unsafe struct opj_image_t
{
public int x0;
public int y0;
public int x1;
public int y1;
public int numcomps;
public OPJ_COLOR_SPACE color_space;
public opj_image_comp_t* comps;
};
[StructLayout(LayoutKind.Sequential,Pack=4)]
public struct opj_image_cmptparm_t
{
public int dx;
public int dy;
public int w;
public int h;
public int x0;
public int y0;
public int prec;
public int bpp;
public int sgnd;
}
[DllImport("openjpeg.dll", CharSet=CharSet.Ansi)]
public static extern string opj_version();
[DllImport("openjpeg.dll")]
public unsafe static extern opj_image_t* opj_image_create(int numcmpts, opj_image_cmptparm_t[] cmptparms, OPJ_COLOR_SPACE clrspc);
[DllImport("openjpeg.dll")]
public unsafe static extern void opj_image_destroy(opj_image_t* image);
[DllImport("openjpeg.dll")]
// TODO: Can we get rid of this MarshalAs?
public unsafe static extern opj_cio_t* opj_cio_open(void* cio, [MarshalAs(UnmanagedType.LPArray)]byte[] buffer, int length);
[DllImport("openjpeg.dll")]
public unsafe static extern void opj_cio_close(opj_cio_t* cio);
[DllImport("openjpeg.dll")]
public unsafe static extern int cio_tell(opj_cio_t* cio);
[DllImport("openjpeg.dll")]
public unsafe static extern void cio_seek(opj_cio_t* cio, int pos);
//[DllImport("openjpeg.dll")]
// opj_event_mgr_t*
//public unsafe static extern opj_event_mgr_t* opj_set_event_mgr(opj_common_struct_t* cinfo, opj_event_mgr_t* event_mgr, void* context);
[DllImport("openjpeg.dll", EntryPoint="opj_create_decompress")]
// opj_dinfo_t*
public static extern IntPtr opj_create_decompress(OPJ_CODEC_FORMAT format);
[DllImport("openjpeg.dll")]
public static extern void opj_destroy_decompress(IntPtr dinfo);
[DllImport("openjpeg.dll")]
public static extern void opj_set_default_decoder_parameters(ref opj_dparameters_t parameters);
[DllImport("openjpeg.dll")]
public static extern void opj_setup_decoder(ref opj_dinfo_t dinfo, ref opj_dparameters_t parameters);
[DllImport("openjpeg.dll")]
// opj_image_t*
public static extern IntPtr opj_decode(ref opj_dinfo_t dinfo, ref opj_cio_t cio);
[DllImport("openjpeg.dll")]
// opj_cinfo_t*
public unsafe static extern opj_cinfo_t* opj_create_compress(OPJ_CODEC_FORMAT format);
[DllImport("openjpeg.dll")]
public unsafe static extern void opj_destroy_compress(opj_cinfo_t* cinfo);
[DllImport("openjpeg.dll")]
public static extern void opj_set_default_encoder_parameters(IntPtr parameters);
[DllImport("openjpeg.dll")]
public unsafe static extern void opj_setup_encoder(opj_cinfo_t* cinfo, IntPtr parameters, opj_image_t* image);
[DllImport("openjpeg.dll")]
public unsafe static extern bool opj_encode(opj_cinfo_t* cinfo, opj_cio_t* cio, opj_image_t* image, char* index);
///////////////////////////////////
/// <summary>
/// Decodes a byte array containing JPEG2000 data using the J2K codec
/// in to a byte array containing a Targa file
/// </summary>
/// <param name="j2cdata">Byte array containing JPEG2000 data using the
/// J2K codec</param>
/// <returns>Byte array containing a Targa file</returns>
public unsafe static byte[] DecodeToTGA(byte[] j2kdata)
{
byte[] output;
try
{
const int TGA_HEADER_SIZE = 32;
opj_dparameters_t parameters = new opj_dparameters_t();
opj_image_t image;
opj_dinfo_t dinfo;
opj_cio_t cio;
// TODO: configure the event callbacks
// setup the decoding parameters
opj_set_default_decoder_parameters(ref parameters);
// get a decoder handle
IntPtr dinfo_ptr = opj_create_decompress(OPJ_CODEC_FORMAT.CODEC_J2K);
dinfo = (opj_dinfo_t)Marshal.PtrToStructure(dinfo_ptr, typeof(opj_dinfo_t));
// TODO: setup the callbacks
// setup the decoder
opj_setup_decoder(ref dinfo, ref parameters);
// open a byte stream
IntPtr cio_ptr = (IntPtr)opj_cio_open((opj_common_struct_t*)dinfo_ptr, j2kdata, j2kdata.Length);
cio = (opj_cio_t)Marshal.PtrToStructure(cio_ptr, typeof(opj_cio_t));
// decode
IntPtr image_ptr = opj_decode(ref dinfo, ref cio);
image = (opj_image_t)Marshal.PtrToStructure(image_ptr, typeof(opj_image_t));
int width = image.x1 - image.x0;
int height = image.y1 - image.y0;
int components = image.numcomps;
// create the targa file in memory
output = new byte[width * height * 4 + TGA_HEADER_SIZE];
int offset = 0;
output[offset++] = 0; // idlength
output[offset++] = 0; // colormaptype = 0: no colormap
output[offset++] = 2; // image type = 2: uncompressed RGB
output[offset++] = 0; // color map spec is five zeroes for no color map
output[offset++] = 0; // color map spec is five zeroes for no color map
output[offset++] = 0; // color map spec is five zeroes for no color map
output[offset++] = 0; // color map spec is five zeroes for no color map
output[offset++] = 0; // color map spec is five zeroes for no color map
output[offset++] = 0; // x origin = two bytes
output[offset++] = 0; // x origin = two bytes
output[offset++] = 0; // y origin = two bytes
output[offset++] = 0; // y origin = two bytes
output[offset++] = (byte)(width & 0xFF); // width - low byte
output[offset++] = (byte)(width >> 8); // width - hi byte
output[offset++] = (byte)(height & 0xFF); // height - low byte
output[offset++] = (byte)(height >> 8); // height - hi byte
output[offset++] = 32; // 32 bits per pixel
output[offset++] = 40; // image descriptor byte
switch (image.numcomps)
{
case 5:
// We can't represent the fifth
case 4:
for (int i = 0; i < (width * height); i++)
{
output[offset++] = (byte)image.comps[2].data[i]; // red
output[offset++] = (byte)image.comps[1].data[i]; // green
output[offset++] = (byte)image.comps[0].data[i]; // blue
output[offset++] = (byte)image.comps[3].data[i]; // alpha
}
break;
case 3:
for (int i = 0; i < (width * height); i++)
{
output[offset++] = (byte)image.comps[2].data[i]; // red
output[offset++] = (byte)image.comps[1].data[i]; // green
output[offset++] = (byte)image.comps[0].data[i]; // blue
output[offset++] = 0xFF; // alpha
}
break;
default:
Console.WriteLine("Unhandled numcomps: " + image.numcomps);
return null;
}
File.WriteAllBytes("out.tga", output);
opj_cio_close((opj_cio_t*)cio_ptr);
opj_destroy_decompress(dinfo_ptr);
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
return null;
}
return output;
}
/// <summary>
/// Decodes a byte array containing JPEG2000 data using the J2K codec
/// in to a GDI+ Image object
/// </summary>
/// <param name="j2cdata"></param>
/// <returns></returns>
public static Image DecodeToImage(byte[] j2kdata)
{
return LoadTGAClass.LoadTGA(new MemoryStream(DecodeToTGA(j2kdata)));
}
public unsafe static byte[] EncodeFromImage(Bitmap bitmap, string comment)
{
byte[] output = null;
try
{
const int MAX_COMPS = 4;
// setup the parameters
IntPtr parameters_ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(opj_cparameters_t)));
opj_set_default_encoder_parameters(parameters_ptr);
opj_cparameters_t parameters = (opj_cparameters_t)Marshal.PtrToStructure(parameters_ptr, typeof(opj_cparameters_t));
parameters.tcp_rates[0] = 0;
parameters.tcp_numlayers++;
parameters.cp_disto_alloc = 1;
parameters.cod_format = 0;
parameters.subsampling_dx = 1;
parameters.subsampling_dy = 1;
parameters.cp_comment = comment;
OPJ_COLOR_SPACE color_space = OPJ_COLOR_SPACE.CLRSPC_SRGB;
opj_image_cmptparm_t[] cmptparm = new opj_image_cmptparm_t[MAX_COMPS];
int width = bitmap.Width;
int height = bitmap.Height;
for (int c = 0; c < MAX_COMPS; c++)
{
cmptparm[c] = new opj_image_cmptparm_t();
cmptparm[c].prec = 8;
cmptparm[c].bpp = 8;
cmptparm[c].sgnd = 0;
cmptparm[c].dx = parameters.subsampling_dx;
cmptparm[c].dy = parameters.subsampling_dy;
cmptparm[c].w = width;
cmptparm[c].h = height;
}
// create the image
int i = 0;
int numcomps;
PixelFormat format = bitmap.PixelFormat;
if ((format & PixelFormat.Alpha) != 0 || (format & PixelFormat.PAlpha) != 0)
numcomps = 4;
else if (format == PixelFormat.Format16bppGrayScale)
numcomps = 1;
else
numcomps = 3;
opj_image_t* image_ptr = opj_image_create(numcomps, cmptparm, color_space);
image_ptr->x1 = width;
image_ptr->y1 = height;
BitmapData data;
// Build the raw image buffer for openjpeg to read
if ((format & PixelFormat.Alpha) != 0 || (format & PixelFormat.PAlpha) != 0)
{
// four layers, RGBA
data = bitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly,
PixelFormat.Format32bppArgb);
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
byte* pixel = (byte*)data.Scan0;
pixel += (y * width + x) * numcomps;
// GDI+ gives us BGRA and we need to turn that in to RGBA
(*image_ptr).comps[0].data[i] = *(pixel + 2);
(*image_ptr).comps[1].data[i] = *(pixel + 1);
(*image_ptr).comps[2].data[i] = *(pixel);
(*image_ptr).comps[3].data[i] = *(pixel + 3);
pixel += 4;
i++;
}
}
}
else if (format == PixelFormat.Format16bppGrayScale)
{
// one layer
data = bitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly,
PixelFormat.Format16bppGrayScale);
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
byte* pixel = (byte*)data.Scan0;
pixel += (y * width + x) * numcomps;
// turn 16 bit data in to 8 bit data (TODO: Does this work?)
(*image_ptr).comps[0].data[i] = *(pixel);
pixel += 2;
i++;
}
}
}
else
{
// three layers, RGB
data = bitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly,
PixelFormat.Format24bppRgb);
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
byte* pixel = (byte*)data.Scan0;
pixel += (y * width + x) * numcomps;
for (int c = numcomps - 1; c >= 0; c--)
{
(*image_ptr).comps[c].data[i] = *pixel;
pixel++;
}
i++;
}
}
}
bitmap.UnlockBits(data);
// get a J2K compressor handle
opj_cinfo_t* cinfo_ptr = opj_create_compress(OPJ_CODEC_FORMAT.CODEC_J2K);
// TODO: setup the callbacks
// setup the encoder parameters
Marshal.StructureToPtr(parameters, parameters_ptr, true);
opj_setup_encoder(cinfo_ptr, parameters_ptr, image_ptr);
// open a byte stream for writing
opj_cio_t* cio_ptr = opj_cio_open((void*)cinfo_ptr, null, 0);
// encode the image
bool success = opj_encode(cinfo_ptr, cio_ptr, image_ptr, null);
if (!success)
{
opj_cio_close(cio_ptr);
return null;
}
int codestream_length = cio_tell(cio_ptr);
output = new byte[codestream_length];
Marshal.Copy((IntPtr)(*cio_ptr).buffer, output, 0, codestream_length);
opj_cio_close(cio_ptr);
opj_destroy_compress(cinfo_ptr);
opj_image_destroy(image_ptr);
Marshal.FreeHGlobal(parameters_ptr);
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
return null;
}
return output;
}
}
}