+//This is needed when compiling in debug mode
+#ifdef _MSC_VER
+// 'type' : forcing value to bool 'true' or 'false' (performance warning)
+//#pragma warning ( disable : 4800 )
+// 'identifier' : class 'type' needs to have dll-interface to be used by
+// clients of class 'type2'
+#pragma warning ( disable : 4251 )
+// 'identifier' : identifier was truncated to 'number' characters in the
+// debug information
+#pragma warning ( disable : 4786 )
+#endif //_MSC_VER
+
+#include "gdcmFile.h"
+#include "gdcmUtil.h"
+#include "iddcmjpeg.h" // for the 'LibIDO' Jpeg LossLess
+
+#define str2num(str, typeNum) *((typeNum *)(str))
+
+/////////////////////////////////////////////////////////////////
+/**
+ * \ingroup gdcmFile
+ * \brief Constructor dedicated to writing a new DICOMV3 part10 compliant
+ * file (see SetFileName, SetDcmTag and Write)
+ * Opens (in read only and when possible) an existing file and checks
+ * for DICOM compliance. Returns NULL on failure.
+ * \Note the in-memory representation of all available tags found in
+ * the DICOM header is post-poned to first header information access.
+ * This avoid a double parsing of public part of the header when
+ * one sets an a posteriori shadow dictionary (efficiency can be
+ * seen as a side effect).
+ *
+ * @param filename file to be opened for parsing
+ *
+ * @return
+ */
+
+gdcmFile::gdcmFile(std::string & filename)
+ :gdcmHeader(filename.c_str())
+{
+ SetPixelDataSizeFromHeader();
+}
+
+gdcmFile::gdcmFile(const char * filename)
+ :gdcmHeader(filename)
+{
+ SetPixelDataSizeFromHeader();
+}
+
+/**
+ * \ingroup gdcmFile
+ * \brief calcule la longueur (in bytes) A ALLOUER pour recevoir les
+ * pixels de l'image
+ * ou DES images dans le cas d'un multiframe
+ * ATTENTION : il ne s'agit PAS de la longueur du groupe des Pixels
+ * (dans le cas d'images compressees, elle n'a pas de sens).
+ *
+ * @return longueur a allouer
+ */
+void gdcmFile::SetPixelDataSizeFromHeader(void) {
+ int nb;
+ std::string str_nb;
+
+ str_nb=gdcmHeader::GetPubElValByNumber(0x0028,0x0100);
+ if (str_nb == GDCM_UNFOUND ) {
+ nb = 16;
+ } else {
+ nb = atoi(str_nb.c_str() );
+ if (nb == 12) nb =16;
+ }
+ lgrTotale = GetXSize() * GetYSize() * GetZSize() * (nb/8)* GetSamplesPerPixel();
+
+ std::string str_PhotometricInterpretation = gdcmHeader::GetPubElValByNumber(0x0028,0x0004);
+ if ( str_PhotometricInterpretation == "PALETTE COLOR "
+ || str_PhotometricInterpretation == "YBR_FULL") { // --> some more to be added !!
+ lgrTotale*=3;
+ }
+
+ // remaining to check :
+ // str_PhotometricInterpretation == "YBR_FULL"
+ // str_PhotometricInterpretation == "YBR_FULL_422" (no LUT, no Palette)
+ // -->and some more !!
+}
+
+/////////////////////////////////////////////////////////////////
+/**
+ * \ingroup gdcmFile
+ * \brief Returns the size (in bytes) of required memory to hold
+ * the pixel data represented in this file.
+ * @return The size of pixel data in bytes.
+ */
+size_t gdcmFile::GetImageDataSize(void) {
+ return (lgrTotale);
+}
+
+
+/////////////////////////////////////////////////////////////////
+/**
+ * \ingroup gdcmFile
+ * \brief Parse pixel data from disk and *prints* the result
+ * \ For multi-fragment Jpeg/Rle files checking purpose *only*
+ * \ Allows to 'see' if the file *does* conform
+ * \ (some of them do not)
+ * \ with Dicom Part 3, Annex A (PS 3.5-2003, page 58, page 85)
+ *
+ */
+bool gdcmFile::ParsePixelData(void) {
+
+ if ( !OpenFile())
+ return false;
+
+ if ( fseek(fp, GetPixelOffset(), SEEK_SET) == -1 ) {
+ CloseFile();
+ return false;
+ }
+
+ if ( !IsDicomV3() ||
+ IsImplicitVRLittleEndianTransferSyntax() ||
+ IsExplicitVRLittleEndianTransferSyntax() ||
+ IsExplicitVRBigEndianTransferSyntax() ||
+ IsDeflatedExplicitVRLittleEndianTransferSyntax() ) {
+
+ printf ("gdcmFile::ParsePixelData : non JPEG/RLE File\n");
+ return 0;
+ }
+
+ int nb;
+ std::string str_nb=gdcmHeader::GetPubElValByNumber(0x0028,0x0100);
+ if (str_nb == GDCM_UNFOUND ) {
+ nb = 16;
+ } else {
+ nb = atoi(str_nb.c_str() );
+ if (nb == 12) nb =16;
+ }
+ int nBytes= nb/8;
+
+ //int taille = GetXSize() * GetYSize() * GetZSize() * GetSamplesPerPixel();
+ int taille = GetXSize() * GetYSize() * GetSamplesPerPixel();
+
+ printf ("Checking the Dicom-Jpeg/RLE Pixels\n");
+
+ guint16 ItemTagGr,ItemTagEl;
+ int ln;
+ long ftellRes;
+ char * destination = NULL;
+
+ // ------------------------------- for Parsing : Position on begining of Jpeg/RLE Pixels
+
+ if( !IsRLELossLessTransferSyntax()) {
+
+ // JPEG Image
+
+ std::cout << "JPEG image" << std::endl;
+ ftellRes=ftell(fp);
+ fread(&ItemTagGr,2,1,fp); // Reading (fffe) : Basic Offset Table Item Tag Gr
+ fread(&ItemTagEl,2,1,fp); // Reading (e000) : Basic Offset Table Item Tag El
+ if(GetSwapCode()) {
+ ItemTagGr=SwapShort(ItemTagGr);
+ ItemTagEl=SwapShort(ItemTagEl);
+ }
+ printf ("at %x : ItemTag (should be fffe,e000): %04x,%04x\n",
+ ftellRes,ItemTagGr,ItemTagEl );
+ ftellRes=ftell(fp);
+ fread(&ln,4,1,fp);
+ if(GetSwapCode())
+ ln=SwapLong(ln); // Basic Offset Table Item Lentgh
+ printf("at %x : Basic Offset Table Item Lentgh (??) %d x(%08x)\n",
+ ftellRes,ln,ln);
+ if (ln != 0) {
+ // What is it used for ??
+ char * BasicOffsetTableItemValue= (char *)malloc(ln+1);
+ fread(BasicOffsetTableItemValue,ln,1,fp);
+ guint32 a;
+ for (int i=0;i<ln;i+=4){
+ a=str2num(&BasicOffsetTableItemValue[i],guint32);
+ printf(" x(%08x) %d\n",a,a);
+ }
+ }
+
+ ftellRes=ftell(fp);
+ fread(&ItemTagGr,2,1,fp); // Reading (fffe) : Item Tag Gr
+ fread(&ItemTagEl,2,1,fp); // Reading (e000) : Item Tag El
+ if(GetSwapCode()) {
+ ItemTagGr=SwapShort(ItemTagGr);
+ ItemTagEl=SwapShort(ItemTagEl);
+ }
+ printf ("at %x : ItemTag (should be fffe,e000 or e0dd): %04x,%04x\n",
+ ftellRes,ItemTagGr,ItemTagEl );
+
+ while ( ( ItemTagGr == 0xfffe) && (ItemTagEl != 0xe0dd) ) { // Parse fragments
+
+ ftellRes=ftell(fp);
+ fread(&ln,4,1,fp);
+ if(GetSwapCode())
+ ln=SwapLong(ln); // length
+ printf(" at %x : fragment length %d x(%08x)\n",
+ ftellRes, ln,ln);
+
+ // destination += taille * nBytes; // location in user's memory
+ //printf (" Destination will be x(%x) = %d \n",
+ // destination,destination );
+
+ // ------------------------
+ fseek(fp,ln,SEEK_CUR); // skipping (not reading) fragment pixels
+ // ------------------------
+
+ ftellRes=ftell(fp);
+ fread(&ItemTagGr,2,1,fp); // Reading (fffe) : Item Tag Gr
+ fread(&ItemTagEl,2,1,fp); // Reading (e000) : Item Tag El
+ if(GetSwapCode()) {
+ ItemTagGr=SwapShort(ItemTagGr);
+ ItemTagEl=SwapShort(ItemTagEl);
+ }
+ printf ("at %x : ItemTag (should be fffe,e000 or e0dd): %04x,%04x\n",
+ ftellRes,ItemTagGr,ItemTagEl );
+ }
+
+ } else {
+
+ // RLE Image
+
+ std::cout << "RLE image" << std::endl;
+ long RleSegmentLength[15],fragmentLength;
+ guint32 nbRleSegments;
+ guint32 RleSegmentOffsetTable[15];
+ ftellRes=ftell(fp);
+ // Basic Offset Table with Item Value
+ // Item Tag
+ fread(&ItemTagGr,2,1,fp); // Reading (fffe):Basic Offset Table Item Tag Gr
+ fread(&ItemTagEl,2,1,fp); // Reading (e000):Basic Offset Table Item Tag El
+ if(GetSwapCode()) {
+ ItemTagGr=SwapShort(ItemTagGr);
+ ItemTagEl=SwapShort(ItemTagEl);
+ }
+ printf ("at %x : ItemTag (should be fffe,e000): %04x,%04x\n",
+ ftellRes,ItemTagGr,ItemTagEl );
+ // Item Length
+ ftellRes=ftell(fp);
+ fread(&ln,4,1,fp);
+ if(GetSwapCode())
+ ln=SwapLong(ln); // Basic Offset Table Item Lentgh
+ printf("at %x : Basic Offset Table Item Lentgh (??) %d x(%08x)\n",
+ ftellRes,ln,ln);
+ if (ln != 0) {
+ // What is it used for ??
+ char * BasicOffsetTableItemValue= (char *)malloc(ln+1);
+ fread(BasicOffsetTableItemValue,ln,1,fp);
+ guint32 a;
+ for (int i=0;i<ln;i+=4){
+ a=str2num(&BasicOffsetTableItemValue[i],guint32);
+ printf(" x(%08x) %d\n",a,a);
+ }
+ }
+
+ ftellRes=ftell(fp);
+ fread(&ItemTagGr,2,1,fp); // Reading (fffe) : Item Tag Gr
+ fread(&ItemTagEl,2,1,fp); // Reading (e000) : Item Tag El
+ if(GetSwapCode()) {
+ ItemTagGr=SwapShort(ItemTagGr);
+ ItemTagEl=SwapShort(ItemTagEl);
+ }
+ printf ("at %x : ItemTag (should be fffe,e000 or e0dd): %04x,%04x\n",
+ ftellRes,ItemTagGr,ItemTagEl );
+
+ // while 'Sequence Delimiter Item' (fffe,e0dd) not found
+ while ( ( ItemTagGr == 0xfffe) && (ItemTagEl != 0xe0dd) ) {
+ // Parse fragments of the current Fragment (Frame)
+ ftellRes=ftell(fp);
+ fread(&fragmentLength,4,1,fp);
+ if(GetSwapCode())
+ fragmentLength=SwapLong(fragmentLength); // length
+ printf(" at %x : 'fragment' length %d x(%08x)\n",
+ ftellRes, fragmentLength,fragmentLength);
+
+// destination += taille * nBytes; // location in user's memory
+// printf (" Destination will be x(%x) = %d \n",
+// destination,destination );
+
+ //------------------ scanning (not reading) fragment pixels
+
+ fread(&nbRleSegments,4,1,fp); // Reading : Number of RLE Segments
+ if(GetSwapCode())
+ nbRleSegments=SwapLong(nbRleSegments);
+ printf(" Nb of RLE Segments : %d\n",nbRleSegments);
+
+ for(int k=1; k<=15; k++) { // Reading RLE Segments Offset Table
+ ftellRes=ftell(fp);
+ fread(&RleSegmentOffsetTable[k],4,1,fp);
+ if(GetSwapCode())
+ RleSegmentOffsetTable[k]=SwapLong(RleSegmentOffsetTable[k]);
+ printf(" at : %x Offset Segment %d : %d (%x)\n",
+ ftellRes,k,RleSegmentOffsetTable[k],RleSegmentOffsetTable[k]);
+ }
+ if (nbRleSegments>1) {
+ for(int k=1; k<=nbRleSegments-1; k++) { // skipping (not reading) RLE Segments
+ RleSegmentLength[k]=RleSegmentOffsetTable[k+1]-RleSegmentOffsetTable[k];
+ ftellRes=ftell(fp);
+ printf (" Segment %d : Length = %d Start at %x\n",
+ k,RleSegmentLength[k], ftellRes);
+ fseek(fp,RleSegmentLength[k],SEEK_CUR);
+ }
+ }
+ RleSegmentLength[nbRleSegments] = fragmentLength - RleSegmentOffsetTable[nbRleSegments] ;
+ // TODO : Check the value
+ ftellRes=ftell(fp);
+ printf (" Segment %d : Length = %d Start at %x\n",
+ nbRleSegments,RleSegmentLength[nbRleSegments],ftellRes);
+
+ fseek(fp,RleSegmentLength[nbRleSegments],SEEK_CUR);
+
+ // ------------------ end of scanning fragment pixels
+
+ ftellRes=ftell(fp);
+ fread(&ItemTagGr,2,1,fp); // Reading (fffe) : Item Tag Gr
+ fread(&ItemTagEl,2,1,fp); // Reading (e000) : Item Tag El
+ if(GetSwapCode()) {
+ ItemTagGr=SwapShort(ItemTagGr);
+ ItemTagEl=SwapShort(ItemTagEl);
+ }
+ printf ("at %x : ItemTag (should be fffe,e000 or e0dd): %04x,%04x\n",
+ ftellRes,ItemTagGr,ItemTagEl );
+ }
+ }
+ return 1;
+}
+
+/////////////////////////////////////////////////////////////////
+/**
+ * \ingroup gdcmFile
+ * \brief Read pixel data from disk (optionaly decompressing) into the
+ * caller specified memory location.
+ * @param destination where the pixel data should be stored.
+ *
+ */
+bool gdcmFile::ReadPixelData(void* destination) {
+
+ if ( !OpenFile())
+ return false;
+
+ if ( fseek(fp, GetPixelOffset(), SEEK_SET) == -1 ) {
+ CloseFile();
+ return false;
+ }
+
+// ------------------------- Uncompressed File
+
+ if ( !IsDicomV3() ||
+ IsImplicitVRLittleEndianTransferSyntax() ||
+ IsExplicitVRLittleEndianTransferSyntax() ||
+ IsExplicitVRBigEndianTransferSyntax() ||
+ IsDeflatedExplicitVRLittleEndianTransferSyntax() ) {
+
+ size_t ItemRead = fread(destination, lgrTotale, 1, fp);
+ if ( ItemRead != 1 ) {
+ CloseFile();
+ return false;
+ } else {
+ CloseFile();
+ return true;
+ }
+ }
+
+ // ------------------------ Compressed File .
+
+ int nb;
+ std::string str_nb=gdcmHeader::GetPubElValByNumber(0x0028,0x0100);
+ if (str_nb == GDCM_UNFOUND ) {
+ nb = 16;
+ } else {
+ nb = atoi(str_nb.c_str() );
+ if (nb == 12) nb =16;
+ }
+ int nBytes= nb/8;
+
+ //int taille = GetXSize() * GetYSize() * GetZSize() * GetSamplesPerPixel();
+ int taille = GetXSize() * GetYSize() * GetSamplesPerPixel();
+
+
+ // ------------------------------- JPEG LossLess : call to Jpeg Libido
+
+ if (IsJPEGLossless() && GetZSize() == 1) {
+
+ int ln; // Position on begining of Jpeg Pixels
+ fseek(fp,4,SEEK_CUR); // skipping (fffe,e000) : Basic Offset Table Item
+ fread(&ln,4,1,fp);
+ if(GetSwapCode())
+ ln=SwapLong(ln); // Item length
+ fseek(fp,ln,SEEK_CUR); // skipping Basic Offset Table ('ln' bytes)
+ fseek(fp,4,SEEK_CUR); // skipping (fffe,e000) : First fragment Item Tag
+ fread(&ln,4,1,fp); // First fragment length (just to know)
+ if(GetSwapCode())
+ ln=SwapLong(ln);
+
+ ClbJpeg* jpg = _IdDcmJpegRead(fp); // TODO : find a 'full' one.
+ // (We use the LibIDO one :-(
+ if(jpg == NULL) {
+ CloseFile();
+ return false;
+ }
+ int * dataJpg = jpg->DataImg;
+
+ switch (nBytes) {
+ case 1:
+ {
+ unsigned short *dest = (unsigned short *)destination;
+ for (int i=0; i<taille; i++) {
+ *((unsigned char *)dest+i) = *(dataJpg +i);
+ }
+ }
+ break;
+
+ case 2:
+ {
+ unsigned short *dest = (unsigned short *)destination;
+ for (int i=0; i<taille; i++) {
+ *((unsigned short *)dest+i) = *(dataJpg +i);
+ }
+ }
+ break;
+ }
+ _IdDcmJpegFree (jpg);
+ return true;
+ }
+
+ // ------------------------------- RLE
+
+ if (gdcmHeader::IsRLELossLessTransferSyntax()) {
+ int res = (bool)gdcm_read_RLE_file (destination);
+ return res;
+ }
+
+ // ------------------------------- JPEG Lossy : call to IJG 6b
+
+ long fragmentBegining; // for ftell, fseek
+ bool b = gdcmHeader::IsJPEG2000();
+
+ bool res;
+ guint16 ItemTagGr,ItemTagEl;
+ int ln; // Position on begining of Jpeg Pixels
+
+ fread(&ItemTagGr,2,1,fp); // Reading (fffe) : Item Tag Gr
+ fread(&ItemTagEl,2,1,fp); // Reading (e000) : Item Tag El
+ if(GetSwapCode()) {
+ ItemTagGr=SwapShort(ItemTagGr);
+ ItemTagEl=SwapShort(ItemTagEl);
+ }
+ fread(&ln,4,1,fp);
+ if(GetSwapCode())
+ ln=SwapLong(ln); // Basic Offset Table Item length
+
+ if (ln != 0) {
+ // What is it used for ?!?
+ char *BasicOffsetTableItemValue = (char *)malloc(ln+1);
+ fread(BasicOffsetTableItemValue,ln,1,fp);
+ }
+
+ // first Fragment initialisation
+ fread(&ItemTagGr,2,1,fp); // Reading (fffe) : Item Tag Gr
+ fread(&ItemTagEl,2,1,fp); // Reading (e000) : Item Tag El
+ if(GetSwapCode()) {
+ ItemTagGr=SwapShort(ItemTagGr);
+ ItemTagEl=SwapShort(ItemTagEl);
+ }
+
+ // parsing fragments until Sequence Delim. Tag found
+ //unsigned short *dest = (unsigned short *)destination;
+
+ while ( ( ItemTagGr == 0xfffe) && (ItemTagEl != 0xe0dd) ) {
+ fread(&ln,4,1,fp);
+ if(GetSwapCode())
+ ln=SwapLong(ln); // Fragment Item length
+
+ // FIXME : multi fragments
+ fragmentBegining=ftell(fp);
+
+
+ if (b)
+ res = (bool)gdcm_read_JPEG2000_file (destination); // Reading Fragment pixels
+
+ else if (IsJPEGLossless()) { // ------------- call to LibIDO Jpeg for each Frame/fragment
+
+ // Warning : Works only if there is one fragment per frame
+ // (Or a single fragment for the multiframe file)
+ ClbJpeg* jpg = _IdDcmJpegRead(fp); // TODO : find a 'full' one.
+ // (We use the LibIDO one :-(
+ if(jpg == NULL) {
+ CloseFile();
+ return false;
+ }
+ int * dataJpg = jpg->DataImg;
+ unsigned short *dest = (unsigned short *)destination;
+ switch (nBytes) {
+ case 1:
+ {
+ for (int i=0; i<taille; i++) {
+ *((unsigned char *)dest+i) = *(dataJpg +i);
+ }
+ break;
+ }
+
+ case 2:
+ {
+ for (int i=0; i<taille; i++) {
+ *((unsigned short *)dest+i) = *(dataJpg +i);
+ }
+ break;
+ }
+ }
+ _IdDcmJpegFree (jpg);
+
+ } // ------------------------------------- endif (IsJPEGLossless())
+
+ else
+ if (GetBitsStored() == 8) {
+ res = (bool)gdcm_read_JPEG_file (destination); // Reading Fragment pixels
+ } else {
+ res = (bool)gdcm_read_JPEG_file12 (destination);// Reading Fragment pixels
+ }
+
+ if (!res) break;
+
+ // FIXME : will work only when each fragment corresponds to a Frame :-(
+
+ destination = (char *)destination + taille * nBytes; // location in user's memory
+ // for next fragment (if any)
+ // TODO : find a suitable file (multifragment/single Frame Jpeg file) to check
+
+ fseek(fp,fragmentBegining,SEEK_SET); // To be sure we start
+ fseek(fp,ln,SEEK_CUR); // at the begining of next fragment
+
+ ItemTagGr = ItemTagEl =0;
+ fread(&ItemTagGr,2,1,fp); // Reading (fffe) : Item Tag Gr
+ fread(&ItemTagEl,2,1,fp); // Reading (e000) : Item Tag El
+ if(GetSwapCode()) {
+ ItemTagGr=SwapShort(ItemTagGr);
+ ItemTagEl=SwapShort(ItemTagEl);
+ }
+
+ //(char *) destination += taille * nBytes;
+ //std::cout << "destination" << destination << std::endl;
+ }
+
+ return res;
+}
+
+/**
+ * \ingroup gdcmFile
+ * \brief Allocates necessary memory, copies the pixel data
+ * (image[s]/volume[s]) to newly allocated zone.
+ * @return Pointer to newly allocated pixel data.
+ * \ NULL if alloc fails
+ */
+void * gdcmFile::GetImageData (void) {
+ PixelData = (void *) malloc(lgrTotale);
+ if (PixelData)
+ GetImageDataIntoVector(PixelData, lgrTotale);
+ return(PixelData);
+}
+
+/**
+ * \ingroup gdcmFile
+ * \brief Copies at most MaxSize bytes of pixel data to caller's
+ * memory space.
+ * @param destination Address (in caller's memory space) at which the
+ * pixel data should be copied
+ * @param MaxSize Maximum number of bytes to be copied. When MaxSize
+ * is not sufficient to hold the pixel data the copy is not
+ * executed (i.e. no partial copy).
+ * @return On success, the number of bytes actually copied. Zero on
+ * failure e.g. MaxSize is lower than necessary.
+ */
+
+size_t gdcmFile::GetImageDataIntoVector (void* destination, size_t MaxSize) {
+
+ int nb, nbu, highBit, signe;
+ std::string str_nbFrames, str_nb, str_nbu, str_highBit, str_signe;
+
+ if ( lgrTotale > MaxSize ) {
+ dbg.Verbose(0, "gdcmFile::GetImageDataIntoVector: pixel data bigger"
+ "than caller's expected MaxSize");
+ return (size_t)0;
+ }
+
+ (void)ReadPixelData(destination);
+
+ // Nombre de Bits Alloues pour le stockage d'un Pixel
+ str_nb = GetPubElValByNumber(0x0028,0x0100);
+ if (str_nb == GDCM_UNFOUND ) {
+ nb = 16;
+ } else {
+ nb = atoi(str_nb.c_str() );
+ }