1 /*
2  *
3  *  Copyright (C) 1998-2018, OFFIS e.V.
4  *  All rights reserved.  See COPYRIGHT file for details.
5  *
6  *  This software and supporting documentation were developed by
7  *
8  *    OFFIS e.V.
9  *    R&D Division Health
10  *    Escherweg 2
11  *    D-26121 Oldenburg, Germany
12  *
13  *
14  *  Module: dcmpstat
15  *
16  *  Author: Marco Eichelberg
17  *
18  *  Purpose:
19  *    classes: DVPSSoftcopyVOI
20  *
21  */
22 
23 #include "dcmtk/config/osconfig.h"    /* make sure OS specific configuration is included first */
24 #include "dcmtk/dcmdata/dcdeftag.h"
25 #include "dcmtk/dcmdata/dcsequen.h"
26 #include "dcmtk/dcmpstat/dvpssv.h"
27 #include "dcmtk/dcmpstat/dvpsri.h"      /* for DVPSReferencedImage */
28 #include "dcmtk/dcmpstat/dvpsrsl.h"     /* DVPSReferencedSeries_PList */
29 #include "dcmtk/dcmpstat/dvpsdef.h"     /* for constants and macros */
30 #include "dcmtk/ofstd/ofstd.h"
31 #include "dcmtk/dcmpstat/dvpsrs.h"      /* for DVPSReferencedSeries, needed by MSVC5 with STL */
32 
33 /* --------------- class DVPSSoftcopyVOI --------------- */
34 
DVPSSoftcopyVOI()35 DVPSSoftcopyVOI::DVPSSoftcopyVOI()
36 : referencedImageList()
37 , useLUT(OFFalse)
38 , voiLUTDescriptor(DCM_LUTDescriptor)
39 , voiLUTExplanation(DCM_LUTExplanation)
40 , voiLUTData(DCM_LUTData)
41 , windowCenter(DCM_WindowCenter)
42 , windowWidth(DCM_WindowWidth)
43 , windowCenterWidthExplanation(DCM_WindowCenterWidthExplanation)
44 {
45 }
46 
DVPSSoftcopyVOI(const DVPSSoftcopyVOI & copy)47 DVPSSoftcopyVOI::DVPSSoftcopyVOI(const DVPSSoftcopyVOI& copy)
48 : referencedImageList(copy.referencedImageList)
49 , useLUT(copy.useLUT)
50 , voiLUTDescriptor(copy.voiLUTDescriptor)
51 , voiLUTExplanation(copy.voiLUTExplanation)
52 , voiLUTData(copy.voiLUTData)
53 , windowCenter(copy.windowCenter)
54 , windowWidth(copy.windowWidth)
55 , windowCenterWidthExplanation(copy.windowCenterWidthExplanation)
56 {
57 }
58 
~DVPSSoftcopyVOI()59 DVPSSoftcopyVOI::~DVPSSoftcopyVOI()
60 {
61 }
62 
read(DcmItem & dset)63 OFCondition DVPSSoftcopyVOI::read(DcmItem &dset)
64 {
65   OFCondition result = EC_Normal;
66   DcmStack stack;
67   DcmSequenceOfItems *seq;
68   DcmItem *item;
69 
70   READ_FROM_DATASET(DcmDecimalString, EVR_DS, windowCenter)
71   READ_FROM_DATASET(DcmDecimalString, EVR_DS, windowWidth)
72   READ_FROM_DATASET(DcmLongString, EVR_LO, windowCenterWidthExplanation)
73 
74   /* read VOI LUT Sequence */
75   if (result==EC_Normal)
76   {
77     stack.clear();
78     if (EC_Normal == dset.search(DCM_VOILUTSequence, stack, ESM_fromHere, OFFalse))
79     {
80       seq=(DcmSequenceOfItems *)stack.top();
81       if (seq->card() ==1)
82       {
83          item = seq->getItem(0);
84          stack.clear();
85          if (EC_Normal == item->search((DcmTagKey &)voiLUTDescriptor.getTag(),
86            stack, ESM_fromHere, OFFalse))
87          {
88            voiLUTDescriptor = *((DcmUnsignedShort *)(stack.top()));
89          }
90          stack.clear();
91          if (EC_Normal == item->search((DcmTagKey &)voiLUTExplanation.getTag(),
92            stack, ESM_fromHere, OFFalse))
93          {
94            voiLUTExplanation = *((DcmLongString *)(stack.top()));
95          }
96          stack.clear();
97          if (EC_Normal == item->search((DcmTagKey &)voiLUTData.getTag(),
98            stack, ESM_fromHere, OFFalse))
99          {
100            voiLUTData = *((DcmUnsignedShort *)(stack.top()));
101          }
102       } else {
103         result=EC_TagNotFound;
104         DCMPSTAT_WARN("VOI LUT SQ does not have exactly one item in presentation state");
105       }
106     }
107   }
108 
109   if (result==EC_Normal) result = referencedImageList.read(dset);
110 
111   /* Now perform basic sanity checks */
112 
113   if (result==EC_Normal)
114   {
115     if (windowCenter.getLength() > 0)
116     {
117       useLUT = OFFalse;
118 
119       if (windowWidth.getLength() == 0)
120       {
121         result=EC_IllegalCall;
122         DCMPSTAT_WARN("windowCenter present but windowWidth absent or empty in presentation state");
123       }
124       else if (windowWidth.getVM() != 1)
125       {
126         result=EC_IllegalCall;
127         DCMPSTAT_WARN("windowCenter present but windowWidth VM != 1 in presentation state");
128       }
129       if (windowCenter.getVM() != 1)
130       {
131         result=EC_IllegalCall;
132         DCMPSTAT_WARN("windowCenter present but VM != 1 in presentation state");
133       }
134     } else useLUT = OFTrue;
135 
136     if (voiLUTData.getLength() > 0)
137     {
138 
139       if (! useLUT)
140       {
141         result=EC_IllegalCall;
142         DCMPSTAT_WARN("both VOI window and LUT present in presentation state");
143       }
144 
145       if (voiLUTDescriptor.getLength() == 0)
146       {
147         result=EC_IllegalCall;
148         DCMPSTAT_WARN("voiLUTData present but voiLUTDescriptor absent or empty in presentation state");
149       }
150       else if (voiLUTDescriptor.getVM() != 3)
151       {
152         result=EC_IllegalCall;
153         DCMPSTAT_WARN("voiLUTData present but voiLUTDescriptor VM != 3 in presentation state");
154       }
155     }
156     else if (useLUT)
157     {
158         result=EC_IllegalCall;
159         DCMPSTAT_WARN("neither VOI window nor LUT present in presentation state");
160     }
161   }
162   return result;
163 }
164 
write(DcmItem & dset)165 OFCondition DVPSSoftcopyVOI::write(DcmItem &dset)
166 {
167   OFCondition result = EC_Normal;
168   DcmElement *delem=NULL;
169   DcmSequenceOfItems *dseq=NULL;
170   DcmItem *ditem=NULL;
171 
172   if (useLUT)
173   {
174     ditem = new DcmItem();
175     if (ditem)
176     {
177       dseq = new DcmSequenceOfItems(DCM_VOILUTSequence);
178       if (dseq)
179       {
180         delem = new DcmUnsignedShort(voiLUTDescriptor);
181         if (delem) ditem->insert(delem, OFTrue /*replaceOld*/); else result=EC_MemoryExhausted;
182         delem = new DcmUnsignedShort(voiLUTData);
183         if (delem) ditem->insert(delem, OFTrue /*replaceOld*/); else result=EC_MemoryExhausted;
184         if (voiLUTExplanation.getLength() >0)
185         {
186           delem = new DcmLongString(voiLUTExplanation);
187           if (delem) ditem->insert(delem, OFTrue /*replaceOld*/); else result=EC_MemoryExhausted;
188         }
189         if (result==EC_Normal)
190         {
191           dseq->insert(ditem);
192           dset.insert(dseq, OFTrue /*replaceOld*/);
193         } else {
194           // out of memory during creation of sequence contents.
195           delete dseq;
196           delete ditem;
197           result = EC_MemoryExhausted;
198         }
199       } else {
200         // could allocate item but not sequence. Bail out.
201         delete ditem;
202         result = EC_MemoryExhausted;
203       }
204     }
205     else result = EC_MemoryExhausted;
206   }
207   else
208   {
209     ADD_TO_DATASET(DcmDecimalString, windowCenter)
210     ADD_TO_DATASET(DcmDecimalString, windowWidth)
211     if (windowCenterWidthExplanation.getLength() > 0) { ADD_TO_DATASET(DcmLongString, windowCenterWidthExplanation) }
212   }
213 
214   if ((result == EC_Normal)&&(referencedImageList.size() >0)) result = referencedImageList.write(dset);
215   return result;
216 }
217 
isApplicable(const char * instanceUID,unsigned long frame)218 OFBool DVPSSoftcopyVOI::isApplicable(const char *instanceUID, unsigned long frame)
219 {
220   return referencedImageList.isApplicable(instanceUID, frame);
221 }
222 
matchesApplicability(const char * instanceUID,unsigned long frame,DVPSObjectApplicability applicability)223 OFBool DVPSSoftcopyVOI::matchesApplicability(const char *instanceUID, unsigned long frame, DVPSObjectApplicability applicability)
224 {
225   return referencedImageList.matchesApplicability(instanceUID, frame, applicability);
226 }
227 
removeImageReference(DVPSReferencedSeries_PList & allReferences,const char * instanceUID,unsigned long frame,unsigned long numberOfFrames,DVPSObjectApplicability applicability)228 void DVPSSoftcopyVOI::removeImageReference(
229     DVPSReferencedSeries_PList& allReferences,
230     const char *instanceUID,
231     unsigned long frame,
232     unsigned long numberOfFrames,
233     DVPSObjectApplicability applicability)
234 {
235   referencedImageList.removeImageReference(allReferences, instanceUID, frame, numberOfFrames, applicability);
236   return;
237 }
238 
addImageReference(const char * sopclassUID,const char * instanceUID,unsigned long frame,DVPSObjectApplicability applicability)239 OFCondition DVPSSoftcopyVOI::addImageReference(
240     const char *sopclassUID,
241     const char *instanceUID,
242     unsigned long frame,
243     DVPSObjectApplicability applicability)
244 {
245   return referencedImageList.addImageReference(sopclassUID, instanceUID, frame, applicability);
246 }
247 
getCurrentVOIDescription()248 const char *DVPSSoftcopyVOI::getCurrentVOIDescription()
249 {
250   char *c=NULL;
251   if (useLUT)
252   {
253     if (EC_Normal == voiLUTExplanation.getString(c)) return c;
254   }
255   else
256   {
257     if (EC_Normal == windowCenterWidthExplanation.getString(c)) return c;
258   }
259   return NULL;
260 }
261 
getCurrentWindowWidth(double & w)262 OFCondition DVPSSoftcopyVOI::getCurrentWindowWidth(double &w)
263 {
264   OFCondition result = EC_IllegalCall;
265   if (!useLUT)
266   {
267     Float64 temp=0.0;
268     result = windowWidth.getFloat64(temp,0);
269     if (EC_Normal==result) w = (double)temp;
270   }
271   return result;
272 }
273 
getCurrentWindowCenter(double & c)274 OFCondition DVPSSoftcopyVOI::getCurrentWindowCenter(double &c)
275 {
276   OFCondition result = EC_IllegalCall;
277   if (!useLUT)
278   {
279     Float64 temp=0.0;
280     result = windowCenter.getFloat64(temp,0);
281     if (EC_Normal==result) c = (double)temp;
282   }
283   return result;
284 }
285 
setVOIWindow(double wCenter,double wWidth,const char * description)286 OFCondition DVPSSoftcopyVOI::setVOIWindow(double wCenter, double wWidth, const char *description)
287 {
288   if (wWidth < 1.0)
289   {
290     DCMPSTAT_WARN("Window Width < 1 not allowed.");
291     return EC_IllegalCall;
292   }
293   DcmDecimalString wc(DCM_WindowCenter);
294   DcmDecimalString ww(DCM_WindowWidth);
295   DcmLongString expl(DCM_WindowCenterWidthExplanation);
296   char buf[80];
297 
298   OFStandard::ftoa(buf, sizeof(buf), wCenter, OFStandard::ftoa_uppercase);
299   OFCondition result = wc.putString(buf);
300   OFStandard::ftoa(buf, sizeof(buf), wWidth, OFStandard::ftoa_uppercase);
301   if (EC_Normal == result) result = ww.putString(buf);
302   if ((EC_Normal == result)&&(description)) result = expl.putString(description);
303   if (EC_Normal == result)
304   {
305     // everything worked fine, now copy.
306     windowCenter = wc;
307     windowWidth = ww;
308     windowCenterWidthExplanation = expl;
309     voiLUTDescriptor.clear();
310     voiLUTData.clear();
311     voiLUTExplanation.clear();
312     useLUT = OFFalse;
313   }
314   return result;
315 }
316 
setVOILUT(DcmUnsignedShort & lutDescriptor,DcmUnsignedShort & lutData,DcmLongString & lutExplanation)317 OFCondition DVPSSoftcopyVOI::setVOILUT(
318     DcmUnsignedShort& lutDescriptor,
319     DcmUnsignedShort& lutData,
320     DcmLongString& lutExplanation)
321 {
322   if (lutData.getLength() == 0) return EC_IllegalCall;
323   if (lutDescriptor.getVM() != 3) return EC_IllegalCall;
324   voiLUTDescriptor = lutDescriptor;
325   voiLUTData = lutData;
326   voiLUTExplanation = lutExplanation;
327   windowCenter.clear();
328   windowWidth.clear();
329   windowCenterWidthExplanation.clear();
330   useLUT = OFTrue;
331   return EC_Normal;
332 }
333