]> Creatis software - gdcm.git/blob - src/gdcmDataEntry.cxx
ENH: Adding IsValueCountValid. Should save a lot of time for dev, since we you really...
[gdcm.git] / src / gdcmDataEntry.cxx
1 /*=========================================================================
2                                                                                 
3   Program:   gdcm
4   Module:    $RCSfile: gdcmDataEntry.cxx,v $
5   Language:  C++
6   Date:      $Date: 2005/10/21 14:09:41 $
7   Version:   $Revision: 1.5 $
8                                                                                 
9   Copyright (c) CREATIS (Centre de Recherche et d'Applications en Traitement de
10   l'Image). All rights reserved. See Doc/License.txt or
11   http://www.creatis.insa-lyon.fr/Public/Gdcm/License.html for details.
12                                                                                 
13      This software is distributed WITHOUT ANY WARRANTY; without even
14      the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15      PURPOSE.  See the above copyright notices for more information.
16                                                                                 
17 =========================================================================*/
18
19 #include "gdcmDataEntry.h"
20 #include "gdcmVR.h"
21 #include "gdcmTS.h"
22 #include "gdcmGlobal.h"
23 #include "gdcmUtil.h"
24 #include "gdcmDebug.h"
25
26 #include <fstream>
27
28 namespace gdcm 
29 {
30 //-----------------------------------------------------------------------------
31 #define MAX_SIZE_PRINT_ELEMENT_VALUE 0x7fffffff
32 uint32_t DataEntry::MaxSizePrintEntry = MAX_SIZE_PRINT_ELEMENT_VALUE;
33
34 //-----------------------------------------------------------------------------
35 // Constructor / Destructor
36 /**
37  * \brief   Constructor for a given DictEntry
38  * @param   e Pointer to existing dictionary entry
39  */
40 DataEntry::DataEntry(DictEntry *e) 
41             : DocEntry(e)
42 {
43    State = STATE_LOADED;
44    Flag = FLAG_NONE;
45
46    BinArea = 0;
47    SelfArea = true;
48 }
49
50 /**
51  * \brief   Constructor for a given DocEntry
52  * @param   e Pointer to existing Doc entry
53  */
54 DataEntry::DataEntry(DocEntry *e)
55             : DocEntry(e->GetDictEntry())
56 {
57    Flag = 0;
58    BinArea = 0;
59    SelfArea = true;
60
61    Copy(e);
62 }
63
64 /**
65  * \brief   Canonical destructor.
66  */
67 DataEntry::~DataEntry ()
68 {
69    DeleteBinArea();
70 }
71
72 //-----------------------------------------------------------------------------
73 // Print
74
75 //-----------------------------------------------------------------------------
76 // Public
77 /**
78  * \brief Sets the value (non string) of the current Dicom Header Entry
79  */
80 void DataEntry::SetBinArea( uint8_t *area, bool self )  
81
82    DeleteBinArea();
83
84    BinArea = area;
85    SelfArea = self;
86
87    State = STATE_LOADED;
88 }
89
90 void DataEntry::CopyBinArea( uint8_t *area, uint32_t length )
91 {
92    DeleteBinArea();
93
94    uint32_t lgh = length + length%2;
95    SetLength(lgh);
96
97    if( area && length > 0 )
98    {
99       NewBinArea();
100       memcpy(BinArea,area,length);
101       if( length!=lgh )
102          BinArea[length]=0;
103
104       State = STATE_LOADED;
105    }
106 }
107
108 void DataEntry::SetValue(const uint32_t &id,const double &val)
109 {
110    if( !BinArea )
111       NewBinArea();
112    State = STATE_LOADED;
113
114    if( id > GetValueCount() )
115    {
116       gdcmErrorMacro("Index (" << id << ")is greater than the data size");
117       return;
118    }
119
120    const VRKey &vr = GetVR();
121    if( vr == "US" || vr == "SS" )
122    {
123       uint16_t *data = (uint16_t *)BinArea;
124       data[id] = (uint16_t)val;
125    }
126    else if( vr == "UL" || vr == "SL" )
127    {
128       uint32_t *data = (uint32_t *)BinArea;
129       data[id] = (uint32_t)val;
130    }
131    else if( vr == "FL" )
132    {
133       float *data = (float *)BinArea;
134       data[id] = (float)val;
135    }
136    else if( vr == "FD" )
137    {
138       double *data = (double *)BinArea;
139       data[id] = (double)val;
140    }
141    else if( Global::GetVR()->IsVROfStringRepresentable(vr) )
142    {
143       gdcmErrorMacro("SetValue on String representable not implemented yet");
144    }
145    else
146    {
147       BinArea[id] = (uint8_t)val;
148    }
149 }
150
151 double DataEntry::GetValue(const uint32_t &id) const
152 {
153    if( !BinArea )
154    {
155       gdcmErrorMacro("BinArea not set. Can't get the value");
156       return 0;
157    }
158
159    uint32_t count = GetValueCount();
160    if( id > count )
161    {
162       gdcmErrorMacro("Index (" << id << ")is greater than the data size");
163       return 0;
164    }
165
166    const VRKey &vr = GetVR();
167    if( vr == "US" || vr == "SS" )
168       return ((uint16_t *)BinArea)[id];
169    else if( vr == "UL" || vr == "SL" )
170       return ((uint32_t *)BinArea)[id];
171    else if( vr == "FL" )
172       return ((float *)BinArea)[id];
173    else if( vr == "FD" )
174       return ((double *)BinArea)[id];
175    else if( Global::GetVR()->IsVROfStringRepresentable(vr) )
176    {
177       if( GetLength() )
178       {
179          // Don't use std::string to accelerate processing
180          double val = 0.0;
181          char *tmp = new char[GetLength()+1];
182          memcpy(tmp,BinArea,GetLength());
183          tmp[GetLength()]=0;
184
185          if( count == 0 )
186          {
187             val = atof(tmp);
188          }
189          else
190          {
191             count = id;
192             char *beg = tmp;
193             for(uint32_t i=0;i<GetLength();i++)
194             {
195                if( tmp[i] == '\\' )
196                {
197                   if( count == 0 )
198                   {
199                      tmp[i] = 0;
200                      break;
201                   }
202                   else
203                   {
204                      count--;
205                      beg = &(tmp[i+1]);
206                   }
207                }
208             }
209             val = atof(beg);
210          }
211
212          delete[] tmp;
213          return val;
214       }
215       else 
216          return 0.0;
217    }
218    else
219       return BinArea[id];
220 }
221
222 bool DataEntry::IsValueCountValid() const
223 {
224   uint32_t vm;
225   ostringstream os;
226   os.str( GetVM() );
227   os >> vm;
228   return vm == GetValueCount();
229 }
230
231 uint32_t DataEntry::GetValueCount(void) const
232 {
233    const VRKey &vr = GetVR();
234    if( vr == "US" || vr == "SS" )
235       return GetLength()/sizeof(uint16_t);
236    else if( vr == "UL" || vr == "SL" )
237       return GetLength()/sizeof(uint32_t);
238    else if( vr == "FL" )
239       return GetLength()/sizeof(float);
240    else if( vr == "FD" )
241       return GetLength()/sizeof(double);
242    else if( Global::GetVR()->IsVROfStringRepresentable(vr) )
243    {
244       // Don't use std::string to accelerate processing
245       uint32_t count = 1;
246       for(uint32_t i=0;i<GetLength();i++)
247       {
248          if( BinArea[i] == '\\')
249             count++;
250       }
251       return count;
252    }
253
254    return GetLength();
255 }
256
257 void DataEntry::SetString(std::string const &value)
258 {
259    DeleteBinArea();
260
261    const VRKey &vr = GetVR();
262    if ( vr == "US" || vr == "SS" )
263    {
264       std::vector<std::string> tokens;
265       Util::Tokenize (value, tokens, "\\");
266       SetLength(tokens.size()*sizeof(uint16_t));
267       NewBinArea();
268
269       uint16_t *data = (uint16_t *)BinArea;
270       for (unsigned int i=0; i<tokens.size();i++)
271          data[i] = atoi(tokens[i].c_str());
272       tokens.clear();
273    }
274    else if ( vr == "UL" || vr == "SL" )
275    {
276       std::vector<std::string> tokens;
277       Util::Tokenize (value, tokens, "\\");
278       SetLength(tokens.size()*sizeof(uint32_t));
279       NewBinArea();
280
281       uint32_t *data = (uint32_t *)BinArea;
282       for (unsigned int i=0; i<tokens.size();i++)
283          data[i] = atoi(tokens[i].c_str());
284       tokens.clear();
285    }
286    else if ( vr == "FL" )
287    {
288       std::vector<std::string> tokens;
289       Util::Tokenize (value, tokens, "\\");
290       SetLength(tokens.size()*sizeof(float));
291       NewBinArea();
292
293       float *data = (float *)BinArea;
294       for (unsigned int i=0; i<tokens.size();i++)
295          data[i] = (float)atof(tokens[i].c_str());
296       tokens.clear();
297    }
298    else if ( vr == "FD" )
299    {
300       std::vector<std::string> tokens;
301       Util::Tokenize (value, tokens, "\\");
302       SetLength(tokens.size()*sizeof(double));
303       NewBinArea();
304
305       double *data = (double *)BinArea;
306       for (unsigned int i=0; i<tokens.size();i++)
307          data[i] = atof(tokens[i].c_str());
308       tokens.clear();
309    }
310    else
311    {
312       if( value.size() > 0 )
313       {
314          std::string finalVal = Util::DicomString( value.c_str() );
315          SetLength(finalVal.size());
316          NewBinArea();
317
318          memcpy(BinArea, &(finalVal[0]), finalVal.size());
319       }
320    }
321    State = STATE_LOADED;
322 }
323
324 std::string const &DataEntry::GetString() const
325 {
326    static std::ostringstream s;
327    const VRKey &vr = GetVR();
328
329    s.str("");
330    StrArea="";
331
332    if( !BinArea )
333       return StrArea;
334
335    if( vr == "US" || vr == "SS" )
336    {
337       uint16_t *data=(uint16_t *)BinArea;
338
339       for (unsigned int i=0; i < GetValueCount(); i++)
340       {
341          if( i!=0 )
342             s << '\\';
343          s << data[i];
344       }
345       StrArea=s.str();
346    }
347    // See above comment on multiple integers (mutatis mutandis).
348    else if( vr == "UL" || vr == "SL" )
349    {
350       uint32_t *data=(uint32_t *)BinArea;
351
352       for (unsigned int i=0; i < GetValueCount(); i++)
353       {
354          if( i!=0 )
355             s << '\\';
356          s << data[i];
357       }
358       StrArea=s.str();
359    }
360    else if( vr == "FL" )
361    {
362       float *data=(float *)BinArea;
363
364       for (unsigned int i=0; i < GetValueCount(); i++)
365       {
366          if( i!=0 )
367             s << '\\';
368          s << data[i];
369       }
370       StrArea=s.str();
371    }
372    else if( vr == "FD" )
373    {
374       double *data=(double *)BinArea;
375
376       for (unsigned int i=0; i < GetValueCount(); i++)
377       {
378          if( i!=0 )
379             s << '\\';
380          s << data[i];
381       }
382       StrArea=s.str();
383    }
384    else
385       StrArea.append((const char *)BinArea,GetLength());
386
387    return StrArea;
388 }
389
390 void DataEntry::Copy(DocEntry *doc)
391 {
392    DocEntry::Copy(doc);
393
394    DataEntry *entry = dynamic_cast<DataEntry *>(doc);
395    if ( entry )
396    {
397       State = entry->State;
398       Flag = entry->Flag;
399       CopyBinArea(entry->BinArea,entry->GetLength());
400    }
401 }
402
403 void DataEntry::WriteContent(std::ofstream *fp, FileType filetype)
404
405    DocEntry::WriteContent(fp, filetype);
406
407    if ( GetGroup() == 0xfffe )
408    {
409       return; //delimitors have NO value
410    }
411
412    uint8_t *binArea8 = BinArea; //safe notation
413    size_t lgr = GetLength();
414    if (BinArea) // the binArea was *actually* loaded
415    {
416
417    //  The same operation should be done if we wanted 
418    //  to write image with Big Endian Transfer Syntax, 
419    //  while working on Little Endian Processor
420    // --> forget Big Endian Transfer Syntax writting!
421    //     Next DICOM version will give it up ...
422
423    // --> FIXME 
424    //    The stuff looks nice, but it's probably bugged,
425    //    since troubles occur on big endian processors (SunSparc, Motorola)
426    //    while reading the pixels of a 
427    //    gdcm-written Little-Endian 16 bits per pixel image
428
429 #if defined(GDCM_WORDS_BIGENDIAN) || defined(GDCM_FORCE_BIGENDIAN_EMULATION)
430
431       /// \todo FIXME : Right now, we only care of Pixels element
432       ///       we should deal with *all* the BinEntries
433       ///       Well, not really since we are not interpreting values read...
434
435       // 8 Bits Pixels *are* OB, 16 Bits Pixels *are* OW
436       // -value forced while Reading process-
437       
438       // -->  WARNING
439       // -->        the following lines *looked* very clever, 
440       // -->        but they don't work on big endian processors.
441       // -->        since I've no access for the moment to a big endian proc :-(
442       // -->        I comment them out, to see the result on the dash board 
443       // -->     
444       
445       // --> Revert to initial code : TestWriteSimple hangs on Darwin :-(     
446       if (GetGroup() == 0x7fe0 && GetVR() == "OW")
447       {  
448          uint16_t *binArea16 = (uint16_t*)binArea8;
449          binary_write (*fp, binArea16, lgr );
450       }
451       else
452       { 
453          // For any other VR, DataEntry is re-written as-is
454          binary_write (*fp, binArea8, lgr );
455       }
456
457       // -->  WARNING      
458       // -->         remove the following line, an uncomment the previous ones, 
459       // -->         if it doesn't work better
460       // -->     
461       /*binary_write ( *fp, binArea8, lgr ); // Elem value*/
462       
463 #else
464       binary_write ( *fp, binArea8, lgr ); // Elem value
465 #endif //GDCM_WORDS_BIGENDIAN
466    }
467    else
468    {
469       // nothing was loaded, but we need to skip space on disc
470       
471       //  --> WARNING : nothing is written; 
472       //  --> the initial data (on the the source image) is lost
473       //  --> user is *not* informed !
474       
475       fp->seekp(lgr, std::ios::cur);
476    }
477 }
478
479 //-----------------------------------------------------------------------------
480 // Protected
481 void DataEntry::NewBinArea(void)
482 {
483    DeleteBinArea();
484    if( GetLength() > 0 )
485       BinArea = new uint8_t[GetLength()];
486    SelfArea = true;
487 }
488
489 void DataEntry::DeleteBinArea(void)
490 {
491    if (BinArea && SelfArea)
492    {
493       delete[] BinArea;
494       BinArea = NULL;
495    }
496 }
497
498 //-----------------------------------------------------------------------------
499 // Private
500
501 //-----------------------------------------------------------------------------
502 // Print
503 /**
504  * \brief   Prints a DataEntry (Dicom entry)
505  * @param   os ostream we want to print in
506  * @param indent Indentation string to be prepended during printing
507  */
508 void DataEntry::Print(std::ostream &os, std::string const & )
509 {
510    os << "D ";
511    DocEntry::Print(os);
512
513    uint16_t g = GetGroup();
514    if (g == 0xfffe) // delimiters have NO value
515    {          
516       return; // just to avoid identing all the remaining code 
517    }
518
519    std::ostringstream s;
520    TSAtr v;
521
522    if( BinArea )
523    {
524       v = GetString();
525       const VRKey &vr = GetVR();
526
527       if( vr == "US" || vr == "SS" )
528          s << " [" << GetString() << "]";
529       else if( vr == "UL" || vr == "SL" )
530          s << " [" << GetString() << "]";
531       else if ( vr == "FL" )
532          s << " [" << GetString() << "]";
533       else if ( vr == "FD" )
534          s << " [" << GetString() << "]";
535       else
536       { 
537          if(Global::GetVR()->IsVROfStringRepresentable(vr))
538          {
539             std::string cleanString = Util::CreateCleanString(v);  // replace non printable characters by '.'
540             if ( cleanString.length() <= GetMaxSizePrintEntry()
541             || PrintLevel >= 3
542             || IsNotLoaded() )
543             {
544                s << " [" << cleanString << "]";
545             }
546             else
547             {
548                s << " [gdcm::too long for print (" << cleanString.length() << ") ]";
549             }
550          }
551          else
552          {
553             if ( Util::IsCleanArea( GetBinArea(),GetLength()  ) )
554             {
555                std::string cleanString = 
556                      Util::CreateCleanString( BinArea,GetLength()  );
557                s << " [" << cleanString << "]";
558             }
559             else
560             {
561                s << " [" << GDCM_BINLOADED << ";"
562                << "length = " << GetLength() << "]";
563             }
564          }
565       }
566    }
567    else
568    {
569       if( IsNotLoaded() )
570          s << " [" << GDCM_NOTLOADED << "]";
571       else if( IsUnfound() )
572          s << " [" << GDCM_UNFOUND << "]";
573       else if( IsUnread() )
574          s << " [" << GDCM_UNREAD << "]";
575       else if ( GetLength() == 0 )
576          s << " []";
577    }
578
579    if( IsPixelData() )
580       s << " (" << GDCM_PIXELDATA << ")";
581
582    // Display the UID value (instead of displaying only the rough code)
583    // First 'clean' trailing character (space or zero) 
584    if(BinArea)
585    {
586       const uint16_t &gr = GetGroup();
587       const uint16_t &elt = GetElement();
588       TS *ts = Global::GetTS();
589
590       if (gr == 0x0002)
591       {
592          // Any more to be displayed ?
593          if ( elt == 0x0010 || elt == 0x0002 )
594          {
595             if ( v.length() != 0 )  // for brain damaged headers
596             {
597                if ( ! isdigit((unsigned char)v[v.length()-1]) )
598                {
599                   v.erase(v.length()-1, 1);
600                }
601             }
602             s << "  ==>\t[" << ts->GetValue(v) << "]";
603          }
604       }
605       else if (gr == 0x0008)
606       {
607          if ( elt == 0x0016 || elt == 0x1150 )
608          {
609             if ( v.length() != 0 )  // for brain damaged headers
610             {
611                if ( ! isdigit((unsigned char)v[v.length()-1]) )
612                {
613                   v.erase(v.length()-1, 1);
614                }
615             }
616             s << "  ==>\t[" << ts->GetValue(v) << "]";
617          }
618       }
619       else if (gr == 0x0004)
620       {
621          if ( elt == 0x1510 || elt == 0x1512  )
622          {
623             if ( v.length() != 0 )  // for brain damaged headers  
624             {
625                if ( ! isdigit((unsigned char)v[v.length()-1]) )
626                {
627                   v.erase(v.length()-1, 1);  
628                }
629             }
630             s << "  ==>\t[" << ts->GetValue(v) << "]";
631          }
632       }
633    }
634
635    os << s.str();
636 }
637
638 //-----------------------------------------------------------------------------
639 } // end namespace gdcm
640