/* * The copyright in this software is being made available under the 2-clauses * BSD License, included below. This software may be subject to other third * party and contributor rights, including patent rights, and no such rights * are granted under this license. * * Copyright (c) 2007, Digital Signal Processing Laboratory, Università degli studi di Perugia (UPG), Italy * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS' * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ ///////////////////////////////////////////////////////////////////////////// // Name: imagjpeg2000.cpp // Purpose: wxImage JPEG 2000 family file format handler // Author: Giuseppe Baruffa - based on imagjpeg.cpp, Vaclav Slavik // RCS-ID: $Id: imagjpeg2000.cpp,v 0.00 2008/01/31 10:58:00 MW Exp $ // Copyright: (c) Giuseppe Baruffa // Licence: wxWindows licence ///////////////////////////////////////////////////////////////////////////// // For compilers that support precompilation, includes "wx.h". #include "wx/wxprec.h" #ifdef __BORLANDC__ #pragma hdrstop #endif #if wxUSE_IMAGE && wxUSE_LIBOPENJPEG #include "imagjpeg2000.h" #ifndef WX_PRECOMP #include "wx/log.h" #include "wx/app.h" #include "wx/intl.h" #include "wx/bitmap.h" #include "wx/module.h" #endif #include "openjp2/openjpeg.h" #include "wx/filefn.h" #include "wx/wfstream.h" // ---------------------------------------------------------------------------- // types // ---------------------------------------------------------------------------- //----------------------------------------------------------------------------- // wxJPEG2000Handler //----------------------------------------------------------------------------- IMPLEMENT_DYNAMIC_CLASS(wxJPEG2000Handler,wxImageHandler) #if wxUSE_STREAMS //------------- JPEG 2000 Data Source Manager #define J2K_CFMT 0 #define JP2_CFMT 1 #define JPT_CFMT 2 #define MJ2_CFMT 3 #define PXM_DFMT 0 #define PGX_DFMT 1 #define BMP_DFMT 2 #define YUV_DFMT 3 #define MAX_MESSAGE_LEN 200 /* check file type */ int jpeg2000familytype(unsigned char *hdr, int hdr_len) { // check length if (hdr_len < 24) return -1; // check format if (hdr[0] == 0x00 && hdr[1] == 0x00 && hdr[2] == 0x00 && hdr[3] == 0x0C && hdr[4] == 0x6A && hdr[5] == 0x50 && hdr[6] == 0x20 && hdr[7] == 0x20 && hdr[20] == 0x6A && hdr[21] == 0x70 && hdr[22] == 0x32) // JP2 file format return JP2_CFMT; else if (hdr[0] == 0x00 && hdr[1] == 0x00 && hdr[2] == 0x00 && hdr[3] == 0x0C && hdr[4] == 0x6A && hdr[5] == 0x50 && hdr[6] == 0x20 && hdr[7] == 0x20 && hdr[20] == 0x6D && hdr[21] == 0x6A && hdr[22] == 0x70 && hdr[23] == 0x32) // MJ2 file format return MJ2_CFMT; else if (hdr[0] == 0xFF && hdr[1] == 0x4F) // J2K file format return J2K_CFMT; else // unknown format return -1; } /* we have to use this to avoid GUI-noGUI threads crashing */ void printevent(const char *msg) { #ifndef __WXGTK__ wxMutexGuiEnter(); #endif /* __WXGTK__ */ wxLogMessage(wxT("%s"), msg); #ifndef __WXGTK__ wxMutexGuiLeave(); #endif /* __WXGTK__ */ } /* sample error callback expecting a FILE* client object */ void jpeg2000_error_callback(const char *msg, void *client_data) { char mess[MAX_MESSAGE_LEN + 20]; int message_len = strlen(msg); if (message_len > MAX_MESSAGE_LEN) message_len = MAX_MESSAGE_LEN; if (msg[message_len - 1] == '\n') message_len--; sprintf(mess, "[ERROR] %.*s", message_len, msg); printevent(mess); } /* sample warning callback expecting a FILE* client object */ void jpeg2000_warning_callback(const char *msg, void *client_data) { char mess[MAX_MESSAGE_LEN + 20]; int message_len = strlen(msg); if (message_len > MAX_MESSAGE_LEN) message_len = MAX_MESSAGE_LEN; if (msg[message_len - 1] == '\n') message_len--; sprintf(mess, "[WARNING] %.*s", message_len, msg); printevent(mess); } /* sample debug callback expecting no client object */ void jpeg2000_info_callback(const char *msg, void *client_data) { char mess[MAX_MESSAGE_LEN + 20]; int message_len = strlen(msg); if (message_len > MAX_MESSAGE_LEN) message_len = MAX_MESSAGE_LEN; if (msg[message_len - 1] == '\n') message_len--; sprintf(mess, "[INFO] %.*s", message_len, msg); printevent(mess); } /* macro functions */ /* From little endian to big endian, 2 and 4 bytes */ #define BYTE_SWAP2(X) ((X & 0x00FF) << 8) | ((X & 0xFF00) >> 8) #define BYTE_SWAP4(X) ((X & 0x000000FF) << 24) | ((X & 0x0000FF00) << 8) | ((X & 0x00FF0000) >> 8) | ((X & 0xFF000000) >> 24) #ifdef __WXGTK__ #define BYTE_SWAP8(X) ((X & 0x00000000000000FFULL) << 56) | ((X & 0x000000000000FF00ULL) << 40) | \ ((X & 0x0000000000FF0000ULL) << 24) | ((X & 0x00000000FF000000ULL) << 8) | \ ((X & 0x000000FF00000000ULL) >> 8) | ((X & 0x0000FF0000000000ULL) >> 24) | \ ((X & 0x00FF000000000000ULL) >> 40) | ((X & 0xFF00000000000000ULL) >> 56) #else #define BYTE_SWAP8(X) ((X & 0x00000000000000FF) << 56) | ((X & 0x000000000000FF00) << 40) | \ ((X & 0x0000000000FF0000) << 24) | ((X & 0x00000000FF000000) << 8) | \ ((X & 0x000000FF00000000) >> 8) | ((X & 0x0000FF0000000000) >> 24) | \ ((X & 0x00FF000000000000) >> 40) | ((X & 0xFF00000000000000) >> 56) #endif /* From codestream to int values */ #define STREAM_TO_UINT32(C, P) (((unsigned long int) (C)[(P) + 0] << 24) + \ ((unsigned long int) (C)[(P) + 1] << 16) + \ ((unsigned long int) (C)[(P) + 2] << 8) + \ ((unsigned long int) (C)[(P) + 3] << 0)) #define STREAM_TO_UINT16(C, P) (((unsigned long int) (C)[(P) + 0] << 8) + \ ((unsigned long int) (C)[(P) + 1] << 0)) /* defines */ #define SHORT_DESCR_LEN 32 #define LONG_DESCR_LEN 256 /* enumeration for file formats */ #define JPEG2000FILENUM 4 typedef enum { JP2_FILE, J2K_FILE, MJ2_FILE, UNK_FILE } jpeg2000filetype; /* enumeration for the box types */ #define JPEG2000BOXNUM 23 typedef enum { FILE_BOX, JP_BOX, FTYP_BOX, JP2H_BOX, IHDR_BOX, COLR_BOX, JP2C_BOX, JP2I_BOX, XML_BOX, UUID_BOX, UINF_BOX, MOOV_BOX, MVHD_BOX, TRAK_BOX, TKHD_BOX, MDIA_BOX, MINF_BOX, STBL_BOX, STSD_BOX, MJP2_BOX, MDAT_BOX, ANY_BOX, UNK_BOX } jpeg2000boxtype; /* jpeg2000 family box signatures */ #define FILE_SIGN "" #define JP_SIGN "jP\040\040" #define FTYP_SIGN "ftyp" #define JP2H_SIGN "jp2h" #define IHDR_SIGN "ihdr" #define COLR_SIGN "colr" #define JP2C_SIGN "jp2c" #define JP2I_SIGN "jp2i" #define XML_SIGN "xml\040" #define UUID_SIGN "uuid" #define UINF_SIGN "uinf" #define MOOV_SIGN "moov" #define MVHD_SIGN "mvhd" #define TRAK_SIGN "trak" #define TKHD_SIGN "tkhd" #define MDIA_SIGN "mdia" #define MINF_SIGN "minf" #define VMHD_SIGN "vmhd" #define STBL_SIGN "stbl" #define STSD_SIGN "stsd" #define MJP2_SIGN "mjp2" #define MDAT_SIGN "mdat" #define ANY_SIGN "" #define UNK_SIGN "" /* the box structure itself */ struct jpeg2000boxdef { char value[5]; /* hexadecimal value/string*/ char name[SHORT_DESCR_LEN]; /* short description */ char descr[LONG_DESCR_LEN]; /* long description */ int sbox; /* is it a superbox? */ int req[JPEG2000FILENUM]; /* mandatory box */ jpeg2000boxtype ins; /* contained in box... */ }; /* the possible boxes */ struct jpeg2000boxdef jpeg2000box[] = { /* sign */ {FILE_SIGN, /* short */ "placeholder for nothing", /* long */ "Nothing to say", /* sbox */ 0, /* req */ {1, 1, 1}, /* ins */ FILE_BOX}, /* sign */ {JP_SIGN, /* short */ "JPEG 2000 Signature box", /* long */ "This box uniquely identifies the file as being part of the JPEG 2000 family of files", /* sbox */ 0, /* req */ {1, 1, 1}, /* ins */ FILE_BOX}, /* sign */ {FTYP_SIGN, /* short */ "File Type box", /* long */ "This box specifies file type, version and compatibility information, including specifying if this file " "is a conforming JP2 file or if it can be read by a conforming JP2 reader", /* sbox */ 0, /* req */ {1, 1, 1}, /* ins */ FILE_BOX}, /* sign */ {JP2H_SIGN, /* short */ "JP2 Header box", /* long */ "This box contains a series of boxes that contain header-type information about the file", /* sbox */ 1, /* req */ {1, 1, 1}, /* ins */ FILE_BOX}, /* sign */ {IHDR_SIGN, /* short */ "Image Header box", /* long */ "This box specifies the size of the image and other related fields", /* sbox */ 0, /* req */ {1, 1, 1}, /* ins */ JP2H_BOX}, /* sign */ {COLR_SIGN, /* short */ "Colour Specification box", /* long */ "This box specifies the colourspace of the image", /* sbox */ 0, /* req */ {1, 1, 1}, /* ins */ JP2H_BOX}, /* sign */ {JP2C_SIGN, /* short */ "Contiguous Codestream box", /* long */ "This box contains the codestream as defined by Annex A", /* sbox */ 0, /* req */ {1, 1, 1}, /* ins */ FILE_BOX}, /* sign */ {JP2I_SIGN, /* short */ "Intellectual Property box", /* long */ "This box contains intellectual property information about the image", /* sbox */ 0, /* req */ {0, 0, 0}, /* ins */ FILE_BOX}, /* sign */ {XML_SIGN, /* short */ "XML box", /* long */ "This box provides a tool by which vendors can add XML formatted information to a JP2 file", /* sbox */ 0, /* req */ {0, 0, 0}, /* ins */ FILE_BOX}, /* sign */ {UUID_SIGN, /* short */ "UUID box", /* long */ "This box provides a tool by which vendors can add additional information to a file " "without risking conflict with other vendors", /* sbox */ 0, /* req */ {0, 0, 0}, /* ins */ FILE_BOX}, /* sign */ {UINF_SIGN, /* short */ "UUID Info box", /* long */ "This box provides a tool by which a vendor may provide access to additional information associated with a UUID", /* sbox */ 0, /* req */ {0, 0, 0}, /* ins */ FILE_BOX}, /* sign */ {MOOV_SIGN, /* short */ "Movie box", /* long */ "This box contains the media data. In video tracks, this box would contain JPEG2000 video frames", /* sbox */ 1, /* req */ {1, 1, 1}, /* ins */ FILE_BOX}, /* sign */ {MVHD_SIGN, /* short */ "Movie Header box", /* long */ "This box defines overall information which is media-independent, and relevant to the entire presentation " "considered as a whole", /* sbox */ 0, /* req */ {1, 1, 1}, /* ins */ MOOV_BOX}, /* sign */ {TRAK_SIGN, /* short */ "Track box", /* long */ "This is a container box for a single track of a presentation. A presentation may consist of one or more tracks", /* sbox */ 1, /* req */ {1, 1, 1}, /* ins */ MOOV_BOX}, /* sign */ {TKHD_SIGN, /* short */ "Track Header box", /* long */ "This box specifies the characteristics of a single track. Exactly one Track Header Box is contained in a track", /* sbox */ 0, /* req */ {1, 1, 1}, /* ins */ TRAK_BOX}, /* sign */ {MDIA_SIGN, /* short */ "Media box", /* long */ "The media declaration container contains all the objects which declare information about the media data " "within a track", /* sbox */ 1, /* req */ {1, 1, 1}, /* ins */ TRAK_BOX}, /* sign */ {MINF_SIGN, /* short */ "Media Information box", /* long */ "This box contains all the objects which declare characteristic information of the media in the track", /* sbox */ 1, /* req */ {1, 1, 1}, /* ins */ MDIA_BOX}, /* sign */ {STBL_SIGN, /* short */ "Sample Table box", /* long */ "The sample table contains all the time and data indexing of the media samples in a track", /* sbox */ 1, /* req */ {1, 1, 1}, /* ins */ MINF_BOX}, /* sign */ {STSD_SIGN, /* short */ "Sample Description box", /* long */ "The sample description table gives detailed information about the coding type used, and any initialization " "information needed for that coding", /* sbox */ 0, /* req */ {1, 1, 1}, /* ins */ MINF_BOX}, /* sign */ {MJP2_SIGN, /* short */ "MJP2 Sample Description box", /* long */ "The MJP2 sample description table gives detailed information about the coding type used, and any initialization " "information needed for that coding", /* sbox */ 0, /* req */ {1, 1, 1}, /* ins */ MINF_BOX}, /* sign */ {MDAT_SIGN, /* short */ "Media Data box", /* long */ "The meta-data for a presentation is stored in the single Movie Box which occurs at the top-level of a file", /* sbox */ 1, /* req */ {1, 1, 1}, /* ins */ FILE_BOX}, /* sign */ {ANY_SIGN, /* short */ "Any box", /* long */ "All the existing boxes", /* sbox */ 0, /* req */ {0, 0, 0}, /* ins */ FILE_BOX}, /* sign */ {UNK_SIGN, /* short */ "Unknown Type box", /* long */ "The signature is not recognised to be that of an existing box", /* sbox */ 0, /* req */ {0, 0, 0}, /* ins */ ANY_BOX} }; /* declaration */ int jpeg2000_box_handler_function(jpeg2000boxtype boxtype, wxInputStream& stream, unsigned long int filepoint, unsigned long int filelimit, int level, char *scansign, unsigned long int *scanpoint); #ifdef __WXMSW__ typedef unsigned __int64 int8byte; #endif // __WXMSW__ #ifdef __WXGTK__ typedef unsigned long long int8byte; #endif // __WXGTK__ /* internal mini-search for a box signature */ int jpeg2000_file_parse(wxInputStream& stream, unsigned long int filepoint, unsigned long int filelimit, int level, char *scansign, unsigned long int *scanpoint) { unsigned long int LBox = 0x00000000; char TBox[5] = "\0\0\0\0"; int8byte XLBox = 0x0000000000000000; unsigned long int box_length = 0; int last_box = 0, box_num = 0; int box_type = ANY_BOX; unsigned char fourbytes[4]; int box_number = 0; /* cycle all over the file */ box_num = 0; last_box = 0; while (!last_box) { /* do not exceed file limit */ if (filepoint >= filelimit) return (0); /* seek on file */ if (stream.SeekI(filepoint, wxFromStart) == wxInvalidOffset) return (-1); /* read the mandatory LBox, 4 bytes */ if (!stream.Read(fourbytes, 4)) { wxLogError(wxT("Problem reading LBox from the file (file ended?)")); return -1; }; LBox = STREAM_TO_UINT32(fourbytes, 0); /* read the mandatory TBox, 4 bytes */ if (!stream.Read(TBox, 4)) { wxLogError(wxT("Problem reading TBox from the file (file ended?)")); return -1; }; /* look if scansign is got */ if ((scansign != NULL) && (memcmp(TBox, scansign, 4) == 0)) { /* hack/exploit */ // stop as soon as you find the level-th codebox if (box_number == level) { memcpy(scansign, " ", 4); *scanpoint = filepoint; return (0); } else box_number++; }; /* determine the box type */ for (box_type = JP_BOX; box_type < UNK_BOX; box_type++) if (memcmp(TBox, jpeg2000box[box_type].value, 4) == 0) break; /* read the optional XLBox, 8 bytes */ if (LBox == 1) { if (!stream.Read(&XLBox, 8)) { wxLogError(wxT("Problem reading XLBox from the file (file ended?)")); return -1; }; box_length = (unsigned long int) BYTE_SWAP8(XLBox); } else if (LBox == 0x00000000) { /* last box in file */ last_box = 1; box_length = filelimit - filepoint; } else box_length = LBox; /* go deep in the box */ jpeg2000_box_handler_function((jpeg2000boxtype) box_type, stream, (LBox == 1) ? (filepoint + 16) : (filepoint + 8), filepoint + box_length, level, scansign, scanpoint); /* if it's a superbox go inside it */ if (jpeg2000box[box_type].sbox) jpeg2000_file_parse(stream, (LBox == 1) ? (filepoint + 16) : (filepoint + 8), filepoint + box_length, level, scansign, scanpoint); /* increment box number and filepoint*/ box_num++; filepoint += box_length; }; /* all good */ return (0); } // search first contiguos codestream box in an mj2 file unsigned long int searchjpeg2000c(wxInputStream& stream, unsigned long int fsize, int number) { char scansign[] = "jp2c"; unsigned long int scanpoint = 0L; wxLogMessage(wxT("Searching jp2c box... ")); /* do the parsing */ if (jpeg2000_file_parse(stream, 0, fsize, number, scansign, &scanpoint) < 0) wxLogMessage(wxT("Unrecoverable error during JPEG 2000 box parsing: stopping")); if (strcmp(scansign, " ")) wxLogMessage(wxT("Box not found")); else { wxLogMessage(wxString::Format(wxT("Box found at byte %d"), scanpoint)); }; return (scanpoint); } // search the jp2h box in the file unsigned long int searchjpeg2000headerbox(wxInputStream& stream, unsigned long int fsize) { char scansign[] = "jp2h"; unsigned long int scanpoint = 0L; wxLogMessage(wxT("Searching jp2h box... ")); /* do the parsing */ if (jpeg2000_file_parse(stream, 0, fsize, 0, scansign, &scanpoint) < 0) wxLogMessage(wxT("Unrecoverable error during JPEG 2000 box parsing: stopping")); if (strcmp(scansign, " ")) wxLogMessage(wxT("Box not found")); else wxLogMessage(wxString::Format(wxT("Box found at byte %d"), scanpoint)); return (scanpoint); } /* handling functions */ #define ITEM_PER_ROW 10 /* Box handler function */ int jpeg2000_box_handler_function(jpeg2000boxtype boxtype, wxInputStream& stream, unsigned long int filepoint, unsigned long int filelimit, int level, char *scansign, unsigned long int *scanpoint) { switch (boxtype) { /* Sample Description box */ case (STSD_BOX): jpeg2000_file_parse(stream, filepoint + 8, filelimit, level, scansign, scanpoint); break; /* MJP2 Sample Description box */ case (MJP2_BOX): jpeg2000_file_parse(stream, filepoint + 78, filelimit, level, scansign, scanpoint); break; /* not yet implemented */ default: break; }; return (0); } // the jP and ftyp parts of the header #define jpeg2000headSIZE 32 unsigned char jpeg2000head[jpeg2000headSIZE] = { 0x00, 0x00, 0x00, 0x0C, 'j', 'P', ' ', ' ', 0x0D, 0x0A, 0x87, 0x0A, 0x00, 0x00, 0x00, 0x14, 'f', 't', 'y', 'p', 'j', 'p', '2', ' ', 0x00, 0x00, 0x00, 0x00, 'j', 'p', '2', ' ' }; ///////////////////////////////////////////////// ///////////////////////////////////////////////// // load the jpeg2000 file format bool wxJPEG2000Handler::LoadFile(wxImage *image, wxInputStream& stream, bool verbose, int index) { opj_dparameters_t parameters; /* decompression parameters */ opj_event_mgr_t event_mgr; /* event manager */ opj_image_t *opjimage = NULL; unsigned char *src = NULL; unsigned char *ptr; int file_length, jp2c_point, jp2h_point; unsigned long int jp2hboxlen, jp2cboxlen; opj_codestream_info_t cstr_info; /* Codestream information structure */ unsigned char hdr[24]; int jpfamform; // destroy the image image->Destroy(); /* read the beginning of the file to check the type */ if (!stream.Read(hdr, WXSIZEOF(hdr))) return false; if ((jpfamform = jpeg2000familytype(hdr, WXSIZEOF(hdr))) < 0) return false; stream.SeekI(0, wxFromStart); /* handle to a decompressor */ opj_dinfo_t* dinfo = NULL; opj_cio_t *cio = NULL; /* configure the event callbacks */ memset(&event_mgr, 0, sizeof(opj_event_mgr_t)); event_mgr.error_handler = jpeg2000_error_callback; event_mgr.warning_handler = jpeg2000_warning_callback; event_mgr.info_handler = jpeg2000_info_callback; /* set decoding parameters to default values */ opj_set_default_decoder_parameters(¶meters); /* prepare parameters */ strncpy(parameters.infile, "", sizeof(parameters.infile) - 1); strncpy(parameters.outfile, "", sizeof(parameters.outfile) - 1); parameters.decod_format = jpfamform; parameters.cod_format = BMP_DFMT; if (m_reducefactor) parameters.cp_reduce = m_reducefactor; if (m_qualitylayers) parameters.cp_layer = m_qualitylayers; /*if (n_components) parameters. = n_components;*/ /* JPWL only */ #ifdef USE_JPWL parameters.jpwl_exp_comps = m_expcomps; parameters.jpwl_max_tiles = m_maxtiles; parameters.jpwl_correct = m_enablejpwl; #endif /* USE_JPWL */ /* get a decoder handle */ if (jpfamform == JP2_CFMT || jpfamform == MJ2_CFMT) dinfo = opj_create_decompress(CODEC_JP2); else if (jpfamform == J2K_CFMT) dinfo = opj_create_decompress(CODEC_J2K); else return false; /* find length of the stream */ stream.SeekI(0, wxFromEnd); file_length = (int) stream.TellI(); /* it's a movie */ if (jpfamform == MJ2_CFMT) { /* search for the first codestream box and the movie header box */ jp2c_point = searchjpeg2000c(stream, file_length, m_framenum); jp2h_point = searchjpeg2000headerbox(stream, file_length); // read the jp2h box and store it stream.SeekI(jp2h_point, wxFromStart); stream.Read(&jp2hboxlen, sizeof(unsigned long int)); jp2hboxlen = BYTE_SWAP4(jp2hboxlen); // read the jp2c box and store it stream.SeekI(jp2c_point, wxFromStart); stream.Read(&jp2cboxlen, sizeof(unsigned long int)); jp2cboxlen = BYTE_SWAP4(jp2cboxlen); // malloc memory source src = (unsigned char *) malloc(jpeg2000headSIZE + jp2hboxlen + jp2cboxlen); // copy the jP and ftyp memcpy(src, jpeg2000head, jpeg2000headSIZE); // copy the jp2h stream.SeekI(jp2h_point, wxFromStart); stream.Read(&src[jpeg2000headSIZE], jp2hboxlen); // copy the jp2c stream.SeekI(jp2c_point, wxFromStart); stream.Read(&src[jpeg2000headSIZE + jp2hboxlen], jp2cboxlen); } else if (jpfamform == JP2_CFMT || jpfamform == J2K_CFMT) { /* It's a plain image */ /* get data */ stream.SeekI(0, wxFromStart); src = (unsigned char *) malloc(file_length); stream.Read(src, file_length); } else return false; /* catch events using our callbacks and give a local context */ opj_set_event_mgr((opj_common_ptr)dinfo, &event_mgr, stderr); /* setup the decoder decoding parameters using user parameters */ opj_setup_decoder(dinfo, ¶meters); /* open a byte stream */ if (jpfamform == MJ2_CFMT) cio = opj_cio_open((opj_common_ptr)dinfo, src, jpeg2000headSIZE + jp2hboxlen + jp2cboxlen); else if (jpfamform == JP2_CFMT || jpfamform == J2K_CFMT) cio = opj_cio_open((opj_common_ptr)dinfo, src, file_length); else { free(src); return false; } /* decode the stream and fill the image structure */ opjimage = opj_decode_with_info(dinfo, cio, &cstr_info); if (!opjimage) { wxMutexGuiEnter(); wxLogError(wxT("JPEG 2000 failed to decode image!")); wxMutexGuiLeave(); opj_destroy_decompress(dinfo); opj_cio_close(cio); free(src); return false; } /* close the byte stream */ opj_cio_close(cio); /* - At this point, we have the structure "opjimage" that is filled with decompressed data, as processed by the OpenJPEG decompression engine - We need to fill the class "image" with the proper pixel sample values */ { int shiftbpp; int c, tempcomps; // check components number if (m_components > opjimage->numcomps) m_components = opjimage->numcomps; // check image depth (only on the first one, for now) if (m_components) shiftbpp = opjimage->comps[m_components - 1].prec - 8; else shiftbpp = opjimage->comps[0].prec - 8; // prepare image size if (m_components) image->Create(opjimage->comps[m_components - 1].w, opjimage->comps[m_components - 1].h, true); else image->Create(opjimage->comps[0].w, opjimage->comps[0].h, true); // access image raw data image->SetMask(false); ptr = image->GetData(); // workaround for components different from 1 or 3 if ((opjimage->numcomps != 1) && (opjimage->numcomps != 3)) { #ifndef __WXGTK__ wxMutexGuiEnter(); #endif /* __WXGTK__ */ wxLogMessage(wxT("JPEG2000: weird number of components")); #ifndef __WXGTK__ wxMutexGuiLeave(); #endif /* __WXGTK__ */ tempcomps = 1; } else tempcomps = opjimage->numcomps; // workaround for subsampled components for (c = 1; c < tempcomps; c++) { if ((opjimage->comps[c].w != opjimage->comps[c - 1].w) || (opjimage->comps[c].h != opjimage->comps[c - 1].h)) { tempcomps = 1; break; } } // workaround for different precision components for (c = 1; c < tempcomps; c++) { if (opjimage->comps[c].bpp != opjimage->comps[c - 1].bpp) { tempcomps = 1; break; } } // only one component selected if (m_components) tempcomps = 1; // RGB color picture if (tempcomps == 3) { int row, col; int *r = opjimage->comps[0].data; int *g = opjimage->comps[1].data; int *b = opjimage->comps[2].data; if (shiftbpp > 0) { for (row = 0; row < opjimage->comps[0].h; row++) { for (col = 0; col < opjimage->comps[0].w; col++) { *(ptr++) = (*(r++)) >> shiftbpp; *(ptr++) = (*(g++)) >> shiftbpp; *(ptr++) = (*(b++)) >> shiftbpp; } } } else if (shiftbpp < 0) { for (row = 0; row < opjimage->comps[0].h; row++) { for (col = 0; col < opjimage->comps[0].w; col++) { *(ptr++) = (*(r++)) << -shiftbpp; *(ptr++) = (*(g++)) << -shiftbpp; *(ptr++) = (*(b++)) << -shiftbpp; } } } else { for (row = 0; row < opjimage->comps[0].h; row++) { for (col = 0; col < opjimage->comps[0].w; col++) { *(ptr++) = *(r++); *(ptr++) = *(g++); *(ptr++) = *(b++); } } } } // B/W picture if (tempcomps == 1) { int row, col; int selcomp; if (m_components) selcomp = m_components - 1; else selcomp = 0; int *y = opjimage->comps[selcomp].data; if (shiftbpp > 0) { for (row = 0; row < opjimage->comps[selcomp].h; row++) { for (col = 0; col < opjimage->comps[selcomp].w; col++) { *(ptr++) = (*(y)) >> shiftbpp; *(ptr++) = (*(y)) >> shiftbpp; *(ptr++) = (*(y++)) >> shiftbpp; } } } else if (shiftbpp < 0) { for (row = 0; row < opjimage->comps[selcomp].h; row++) { for (col = 0; col < opjimage->comps[selcomp].w; col++) { *(ptr++) = (*(y)) << -shiftbpp; *(ptr++) = (*(y)) << -shiftbpp; *(ptr++) = (*(y++)) << -shiftbpp; } } } else { for (row = 0; row < opjimage->comps[selcomp].h; row++) { for (col = 0; col < opjimage->comps[selcomp].w; col++) { *(ptr++) = *(y); *(ptr++) = *(y); *(ptr++) = *(y++); } } } } } wxMutexGuiEnter(); wxLogMessage(wxT("JPEG 2000 image loaded.")); wxMutexGuiLeave(); /* close openjpeg structs */ opj_destroy_decompress(dinfo); opj_image_destroy(opjimage); free(src); if (!image->Ok()) return false; else return true; } #define CINEMA_24_CS 1302083 /* Codestream length for 24fps */ #define CINEMA_48_CS 651041 /* Codestream length for 48fps */ #define COMP_24_CS 1041666 /* Maximum size per color component for 2K & 4K @ 24fps */ #define COMP_48_CS 520833 /* Maximum size per color component for 2K @ 48fps */ // save the j2k codestream bool wxJPEG2000Handler::SaveFile( wxImage *wimage, wxOutputStream& stream, bool verbose ) { opj_cparameters_t parameters; /* compression parameters */ opj_event_mgr_t event_mgr; /* event manager */ opj_image_t *oimage = NULL; opj_image_cmptparm_t *cmptparm; opj_cio_t *cio = NULL; opj_codestream_info_t cstr_info; int codestream_length; bool bSuccess; int i; char indexfilename[OPJ_PATH_LEN] = ""; /* index file name */ /* configure the event callbacks (not required) setting of each callback is optionnal */ memset(&event_mgr, 0, sizeof(opj_event_mgr_t)); event_mgr.error_handler = jpeg2000_error_callback; event_mgr.warning_handler = jpeg2000_warning_callback; event_mgr.info_handler = jpeg2000_info_callback; /* set encoding parameters to default values */ opj_set_default_encoder_parameters(¶meters); /* load parameters */ parameters.cp_cinema = OFF; /* subsampling */ if (sscanf(m_subsampling.ToAscii(), "%d,%d", &(parameters.subsampling_dx), &(parameters.subsampling_dy)) != 2) { wxLogError(wxT("Wrong sub-sampling encoder setting: dx,dy")); return false; } /* compression rates */ if ((m_rates != wxT("")) && (!m_enablequality)) { const char *s1 = m_rates.ToAscii(); wxLogMessage(wxT("rates %s"), s1); while (sscanf(s1, "%f", &(parameters.tcp_rates[parameters.tcp_numlayers])) == 1) { parameters.tcp_numlayers++; while (*s1 && *s1 != ',') { s1++; } if (!*s1) break; s1++; } wxLogMessage(wxT("%d layers"), parameters.tcp_numlayers); parameters.cp_disto_alloc = 1; } /* image quality, dB */ if ((m_quality != wxT("")) && (m_enablequality)) { const char *s2 = m_quality.ToAscii(); wxLogMessage(wxT("qualities %s"), s2); while (sscanf(s2, "%f", ¶meters.tcp_distoratio[parameters.tcp_numlayers]) == 1) { parameters.tcp_numlayers++; while (*s2 && *s2 != ',') { s2++; } if (!*s2) break; s2++; } wxLogMessage(wxT("%d layers"), parameters.tcp_numlayers); parameters.cp_fixed_quality = 1; } /* image origin */ if (sscanf(m_origin.ToAscii(), "%d,%d", ¶meters.image_offset_x0, ¶meters.image_offset_y0) != 2) { wxLogError(wxT("bad coordinate of the image origin: x0,y0")); return false; } /* Create comment for codestream */ if(m_enablecomm) { parameters.cp_comment = (char *) malloc(strlen(m_comment.ToAscii()) + 1); if(parameters.cp_comment) { strcpy(parameters.cp_comment, m_comment.ToAscii()); } } else { parameters.cp_comment = NULL; } /* indexing file */ if (m_enableidx) { strncpy(indexfilename, m_index.ToAscii(), OPJ_PATH_LEN); wxLogMessage(wxT("index file is %s"), indexfilename); } /* if no rate entered, lossless by default */ if (parameters.tcp_numlayers == 0) { parameters.tcp_rates[0] = 0; /* MOD antonin : losslessbug */ parameters.tcp_numlayers++; parameters.cp_disto_alloc = 1; } /* irreversible transform */ parameters.irreversible = (m_irreversible == true) ? 1 : 0; /* resolutions */ parameters.numresolution = m_resolutions; /* codeblocks size */ if (m_cbsize != wxT("")) { int cblockw_init = 0, cblockh_init = 0; sscanf(m_cbsize.ToAscii(), "%d,%d", &cblockw_init, &cblockh_init); if (cblockw_init * cblockh_init > 4096 || cblockw_init > 1024 || cblockw_init < 4 || cblockh_init > 1024 || cblockh_init < 4) { wxLogError(wxT("!! Size of code_block error !! Restrictions:\n width*height<=4096\n 4<=width,height<= 1024")); return false; } parameters.cblockw_init = cblockw_init; parameters.cblockh_init = cblockh_init; } /* precincts size */ if (m_prsize != wxT("")) { char sep; int res_spec = 0; char *s = (char *) m_prsize.c_str(); do { sep = 0; sscanf(s, "[%d,%d]%c", ¶meters.prcw_init[res_spec], ¶meters.prch_init[res_spec], &sep); parameters.csty |= 0x01; res_spec++; s = strpbrk(s, "]") + 2; } while (sep == ','); parameters.res_spec = res_spec; } /* tiles */ if (m_tsize != wxT("")) { sscanf(m_tsize.ToAscii(), "%d,%d", ¶meters.cp_tdx, ¶meters.cp_tdy); parameters.tile_size_on = true; } /* tile origin */ if (sscanf(m_torigin.ToAscii(), "%d,%d", ¶meters.cp_tx0, ¶meters.cp_ty0) != 2) { wxLogError(wxT("tile offset setting error: X0,Y0")); return false; } /* use SOP */ if (m_enablesop) parameters.csty |= 0x02; /* use EPH */ if (m_enableeph) parameters.csty |= 0x04; /* multiple component transform */ if (m_multicomp) parameters.tcp_mct = 1; else parameters.tcp_mct = 0; /* mode switch */ parameters.mode = (m_enablebypass ? 1 : 0) + (m_enablereset ? 2 : 0) + (m_enablerestart ? 4 : 0) + (m_enablevsc ? 8 : 0) + (m_enableerterm ? 16 : 0) + (m_enablesegmark ? 32 : 0); /* progression order */ switch (m_progression) { /* LRCP */ case 0: parameters.prog_order = LRCP; break; /* RLCP */ case 1: parameters.prog_order = RLCP; break; /* RPCL */ case 2: parameters.prog_order = RPCL; break; /* PCRL */ case 3: parameters.prog_order = PCRL; break; /* CPRL */ case 4: parameters.prog_order = CPRL; break; /* DCI2K24 */ case 5: parameters.cp_cinema = CINEMA2K_24; parameters.cp_rsiz = CINEMA2K; break; /* DCI2K48 */ case 6: parameters.cp_cinema = CINEMA2K_48; parameters.cp_rsiz = CINEMA2K; break; /* DCI4K */ case 7: parameters.cp_cinema = CINEMA4K_24; parameters.cp_rsiz = CINEMA4K; break; default: break; } /* check cinema */ if (parameters.cp_cinema) { /* set up */ parameters.tile_size_on = false; parameters.cp_tdx=1; parameters.cp_tdy=1; /*Tile part*/ parameters.tp_flag = 'C'; parameters.tp_on = 1; /*Tile and Image shall be at (0,0)*/ parameters.cp_tx0 = 0; parameters.cp_ty0 = 0; parameters.image_offset_x0 = 0; parameters.image_offset_y0 = 0; /*Codeblock size= 32*32*/ parameters.cblockw_init = 32; parameters.cblockh_init = 32; parameters.csty |= 0x01; /*The progression order shall be CPRL*/ parameters.prog_order = CPRL; /* No ROI */ parameters.roi_compno = -1; parameters.subsampling_dx = 1; parameters.subsampling_dy = 1; /* 9-7 transform */ parameters.irreversible = 1; } /* convert wx image into opj image */ cmptparm = (opj_image_cmptparm_t*) malloc(3 * sizeof(opj_image_cmptparm_t)); /* initialize opj image components */ memset(&cmptparm[0], 0, 3 * sizeof(opj_image_cmptparm_t)); for(i = 0; i < 3; i++) { cmptparm[i].prec = 8; cmptparm[i].bpp = 8; cmptparm[i].sgnd = false; cmptparm[i].dx = parameters.subsampling_dx; cmptparm[i].dy = parameters.subsampling_dy; cmptparm[i].w = wimage->GetWidth(); cmptparm[i].h = wimage->GetHeight(); } /* create the image */ oimage = opj_image_create(3, &cmptparm[0], CLRSPC_SRGB); if(!oimage) { if (cmptparm) free(cmptparm); return false; } /* set image offset and reference grid */ oimage->x0 = parameters.image_offset_x0; oimage->y0 = parameters.image_offset_y0; oimage->x1 = parameters.image_offset_x0 + (wimage->GetWidth() - 1) * 1 + 1; oimage->y1 = parameters.image_offset_y0 + (wimage->GetHeight() - 1) * 1 + 1; /* load image data */ unsigned char *value = wimage->GetData(); int area = wimage->GetWidth() * wimage->GetHeight(); for (i = 0; i < area; i++) { oimage->comps[0].data[i] = *(value++); oimage->comps[1].data[i] = *(value++); oimage->comps[2].data[i] = *(value++); } /* check cinema again */ if (parameters.cp_cinema) { int i; float temp_rate; opj_poc_t *POC = NULL; switch (parameters.cp_cinema) { case CINEMA2K_24: case CINEMA2K_48: if (parameters.numresolution > 6) { parameters.numresolution = 6; } if (!((oimage->comps[0].w == 2048) | (oimage->comps[0].h == 1080))) { wxLogWarning(wxT("Image coordinates %d x %d is not 2K compliant. JPEG Digital Cinema Profile-3 " "(2K profile) compliance requires that at least one of coordinates match 2048 x 1080"), oimage->comps[0].w, oimage->comps[0].h); parameters.cp_rsiz = STD_RSIZ; } break; case CINEMA4K_24: if (parameters.numresolution < 1) { parameters.numresolution = 1; } else if (parameters.numresolution > 7) { parameters.numresolution = 7; } if (!((oimage->comps[0].w == 4096) | (oimage->comps[0].h == 2160))) { wxLogWarning(wxT("Image coordinates %d x %d is not 4K compliant. JPEG Digital Cinema Profile-4" "(4K profile) compliance requires that at least one of coordinates match 4096 x 2160"), oimage->comps[0].w, oimage->comps[0].h); parameters.cp_rsiz = STD_RSIZ; } parameters.POC[0].tile = 1; parameters.POC[0].resno0 = 0; parameters.POC[0].compno0 = 0; parameters.POC[0].layno1 = 1; parameters.POC[0].resno1 = parameters.numresolution - 1; parameters.POC[0].compno1 = 3; parameters.POC[0].prg1 = CPRL; parameters.POC[1].tile = 1; parameters.POC[1].resno0 = parameters.numresolution - 1; parameters.POC[1].compno0 = 0; parameters.POC[1].layno1 = 1; parameters.POC[1].resno1 = parameters.numresolution; parameters.POC[1].compno1 = 3; parameters.POC[1].prg1 = CPRL; parameters.numpocs = 2; break; } switch (parameters.cp_cinema) { case CINEMA2K_24: case CINEMA4K_24: for (i = 0 ; i < parameters.tcp_numlayers; i++) { temp_rate = 0; if (parameters.tcp_rates[i] == 0) { parameters.tcp_rates[0] = ((float) (oimage->numcomps * oimage->comps[0].w * oimage->comps[0].h * oimage->comps[0].prec)) / (CINEMA_24_CS * 8 * oimage->comps[0].dx * oimage->comps[0].dy); }else{ temp_rate = ((float) (oimage->numcomps * oimage->comps[0].w * oimage->comps[0].h * oimage->comps[0].prec)) / (parameters.tcp_rates[i] * 8 * oimage->comps[0].dx * oimage->comps[0].dy); if (temp_rate > CINEMA_24_CS ) { parameters.tcp_rates[i]= ((float) (oimage->numcomps * oimage->comps[0].w * oimage->comps[0].h * oimage->comps[0].prec)) / (CINEMA_24_CS * 8 * oimage->comps[0].dx * oimage->comps[0].dy); } else { /* do nothing */ } } } parameters.max_comp_size = COMP_24_CS; break; case CINEMA2K_48: for (i = 0; i < parameters.tcp_numlayers; i++) { temp_rate = 0 ; if (parameters.tcp_rates[i] == 0) { parameters.tcp_rates[0] = ((float) (oimage->numcomps * oimage->comps[0].w * oimage->comps[0].h * oimage->comps[0].prec)) / (CINEMA_48_CS * 8 * oimage->comps[0].dx * oimage->comps[0].dy); }else{ temp_rate =((float) (oimage->numcomps * oimage->comps[0].w * oimage->comps[0].h * oimage->comps[0].prec)) / (parameters.tcp_rates[i] * 8 * oimage->comps[0].dx * oimage->comps[0].dy); if (temp_rate > CINEMA_48_CS ){ parameters.tcp_rates[0]= ((float) (oimage->numcomps * oimage->comps[0].w * oimage->comps[0].h * oimage->comps[0].prec)) / (CINEMA_48_CS * 8 * oimage->comps[0].dx * oimage->comps[0].dy); }else{ /* do nothing */ } } } parameters.max_comp_size = COMP_48_CS; break; } parameters.cp_disto_alloc = 1; } /* get a J2K compressor handle */ opj_cinfo_t* cinfo = opj_create_compress(CODEC_J2K); /* catch events using our callbacks and give a local context */ opj_set_event_mgr((opj_common_ptr)cinfo, &event_mgr, stderr); /* setup the encoder parameters using the current image and user parameters */ opj_setup_encoder(cinfo, ¶meters, oimage); /* open a byte stream for writing */ /* allocate memory for all tiles */ cio = opj_cio_open((opj_common_ptr)cinfo, NULL, 0); /* encode the image */ bSuccess = opj_encode_with_info(cinfo, cio, oimage, &cstr_info); if (!bSuccess) { opj_cio_close(cio); opj_destroy_compress(cinfo); opj_image_destroy(oimage); if (cmptparm) free(cmptparm); if(parameters.cp_comment) free(parameters.cp_comment); if(parameters.cp_matrice) free(parameters.cp_matrice); #ifndef __WXGTK__ wxMutexGuiEnter(); #endif /* __WXGTK__ */ wxLogError(wxT("failed to encode image")); #ifndef __WXGTK__ wxMutexGuiLeave(); #endif /* __WXGTK__ */ return false; } codestream_length = cio_tell(cio); wxLogMessage(wxT("Codestream: %d bytes"), codestream_length); /* write the buffer to stream */ stream.Write(cio->buffer, codestream_length); /* close and free the byte stream */ opj_cio_close(cio); /* Write the index to disk */ if (*indexfilename) { if (write_index_file(&cstr_info, indexfilename)) { wxLogError(wxT("Failed to output index file")); } } /* free remaining compression structures */ opj_destroy_compress(cinfo); /* free image data */ opj_image_destroy(oimage); if (cmptparm) free(cmptparm); if(parameters.cp_comment) free(parameters.cp_comment); if(parameters.cp_matrice) free(parameters.cp_matrice); #ifndef __WXGTK__ wxMutexGuiEnter(); #endif /* __WXGTK__ */ wxLogMessage(wxT("J2K: Image encoded!")); #ifndef __WXGTK__ wxMutexGuiLeave(); #endif /* __WXGTK__ */ return true; } #ifdef __VISUALC__ #pragma warning(default:4611) #endif /* VC++ */ // recognize the JPEG 2000 family starting box or the 0xFF4F JPEG 2000 SOC marker bool wxJPEG2000Handler::DoCanRead(wxInputStream& stream) { unsigned char hdr[24]; int jpfamform; if ( !stream.Read(hdr, WXSIZEOF(hdr)) ) return false; jpfamform = jpeg2000familytype(hdr, WXSIZEOF(hdr)); return ((jpfamform == JP2_CFMT) || (jpfamform == MJ2_CFMT) || (jpfamform == J2K_CFMT)); } #endif // wxUSE_STREAMS #endif // wxUSE_LIBOPENJPEG