1 /*
2  *
3  *  Copyright (C) 1999-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: DVPSPresentationLUT
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/dcmdata/dcvrcs.h"
27 #include "dcmtk/dcmpstat/dvpspl.h"
28 #include "dcmtk/dcmpstat/dvpsdef.h"     /* for constants and macros */
29 #include "dcmtk/dcmnet/dimse.h"
30 
31 /* --------------- class DVPSPresentationLUT --------------- */
32 
DVPSPresentationLUT()33 DVPSPresentationLUT::DVPSPresentationLUT()
34 : presentationLUT(DVPSP_identity)
35 , presentationLUTDescriptor(DCM_LUTDescriptor)
36 , presentationLUTExplanation(DCM_LUTExplanation)
37 , presentationLUTData(DCM_LUTData)
38 , sOPInstanceUID(DCM_SOPInstanceUID)
39 {
40 }
41 
DVPSPresentationLUT(const DVPSPresentationLUT & copy)42 DVPSPresentationLUT::DVPSPresentationLUT(const DVPSPresentationLUT& copy)
43 : presentationLUT(copy.presentationLUT)
44 , presentationLUTDescriptor(copy.presentationLUTDescriptor)
45 , presentationLUTExplanation(copy.presentationLUTExplanation)
46 , presentationLUTData(copy.presentationLUTData)
47 , sOPInstanceUID(copy.sOPInstanceUID)
48 {
49 }
50 
~DVPSPresentationLUT()51 DVPSPresentationLUT::~DVPSPresentationLUT()
52 {
53 }
54 
clear()55 void DVPSPresentationLUT::clear()
56 {
57   presentationLUT = DVPSP_identity;
58   presentationLUTDescriptor.clear();
59   presentationLUTExplanation.clear();
60   presentationLUTData.clear();
61   sOPInstanceUID.clear();
62 }
63 
read(DcmItem & dset,OFBool withSOPInstance)64 OFCondition DVPSPresentationLUT::read(DcmItem &dset, OFBool withSOPInstance)
65 {
66   DcmSequenceOfItems *seq;
67   DcmItem *item;
68   OFCondition result = EC_Normal;
69   DcmStack stack;
70   OFString aString;
71 
72   DcmCodeString presentationLUTShape(DCM_PresentationLUTShape);
73 
74   READ_FROM_DATASET(DcmCodeString, EVR_CS, presentationLUTShape)
75   if (withSOPInstance) { READ_FROM_DATASET(DcmUniqueIdentifier, EVR_UI, sOPInstanceUID) }
76   else sOPInstanceUID.clear();
77 
78   /* read Presentation LUT Sequence */
79   if (result==EC_Normal)
80   {
81     stack.clear();
82     if (EC_Normal == dset.search(DCM_PresentationLUTSequence, stack, ESM_fromHere, OFFalse))
83     {
84       seq=(DcmSequenceOfItems *)stack.top();
85       if (seq->card() ==1)
86       {
87          item = seq->getItem(0);
88          stack.clear();
89          if (EC_Normal == item->search((DcmTagKey &)presentationLUTDescriptor.getTag(),
90            stack, ESM_fromHere, OFFalse))
91          {
92            presentationLUTDescriptor = *((DcmUnsignedShort *)(stack.top()));
93          }
94          stack.clear();
95          if (EC_Normal == item->search((DcmTagKey &)presentationLUTExplanation.getTag(),
96            stack, ESM_fromHere, OFFalse))
97          {
98            presentationLUTExplanation = *((DcmLongString *)(stack.top()));
99          }
100          stack.clear();
101          if (EC_Normal == item->search((DcmTagKey &)presentationLUTData.getTag(),
102            stack, ESM_fromHere, OFFalse))
103          {
104            presentationLUTData = *((DcmUnsignedShort *)(stack.top()));
105          }
106       } else {
107         result=EC_TagNotFound;
108         DCMPSTAT_WARN("found Presentation LUT SQ with number of items != 1");
109       }
110     }
111   }
112 
113 
114   /* Now perform basic sanity checks */
115 
116   if (presentationLUTShape.getLength() == 0)
117   {
118     presentationLUT = DVPSP_table;
119 
120     if (presentationLUTDescriptor.getLength() == 0)
121     {
122       result=EC_IllegalCall;
123       DCMPSTAT_WARN("presentationLUTShape and presentationLUTDescriptor absent or empty");
124     }
125     else if (presentationLUTDescriptor.getVM() != 3)
126     {
127       result=EC_IllegalCall;
128       DCMPSTAT_WARN("presentationLUTDescriptor present but VM != 3 in presentation state");
129     }
130     if (presentationLUTData.getLength() == 0)
131     {
132       result=EC_IllegalCall;
133       DCMPSTAT_WARN("presentationLUTShape and presentationLUTData absent or empty");
134     }
135   } else {
136     if (presentationLUTShape.getVM() != 1)
137     {
138       result=EC_IllegalCall;
139       DCMPSTAT_WARN("presentationLUTShape present but VM != 1");
140     } else {
141       // check presentation LUT shape
142       aString.clear();
143       presentationLUTShape.getOFString(aString,0);
144       if (aString=="IDENTITY") presentationLUT = DVPSP_identity;
145       else if (aString=="INVERSE") presentationLUT = DVPSP_inverse;
146       else if (aString=="LIN OD") presentationLUT = DVPSP_lin_od;
147       else
148       {
149         result=EC_IllegalCall;
150         DCMPSTAT_WARN("unknown presentationLUTShape keyword: " << aString);
151       }
152     }
153   }
154 
155   if (withSOPInstance)
156   {
157     if (sOPInstanceUID.getLength() == 0)
158     {
159       result=EC_IllegalCall;
160       DCMPSTAT_WARN("sOPInstanceUID absent in Presentation LUT Content Sequence");
161     }
162     else if (sOPInstanceUID.getVM() != 1)
163     {
164       result=EC_IllegalCall;
165       DCMPSTAT_WARN("sOPInstanceUID VM != 1 in Presentation LUT Content Sequence");
166     }
167   }
168 
169   return result;
170 }
171 
write(DcmItem & dset,OFBool withSOPInstance)172 OFCondition DVPSPresentationLUT::write(DcmItem &dset, OFBool withSOPInstance)
173 {
174   OFCondition result = EC_Normal;
175   DcmElement *delem=NULL;
176   DcmSequenceOfItems *dseq=NULL;
177   DcmItem *ditem=NULL;
178   DcmCodeString presentationLUTShape(DCM_PresentationLUTShape);
179 
180   if (presentationLUT==DVPSP_table)
181   {
182     if (result == EC_Normal)
183     {
184       ditem = new DcmItem();
185       if (ditem)
186       {
187         dseq = new DcmSequenceOfItems(DCM_PresentationLUTSequence);
188         if (dseq)
189         {
190           delem = new DcmUnsignedShort(presentationLUTDescriptor);
191           if (delem) ditem->insert(delem, OFTrue /*replaceOld*/); else result=EC_MemoryExhausted;
192           delem = new DcmUnsignedShort(presentationLUTData);
193           if (delem) ditem->insert(delem, OFTrue /*replaceOld*/); else result=EC_MemoryExhausted;
194           if (presentationLUTExplanation.getLength() >0)
195           {
196             delem = new DcmLongString(presentationLUTExplanation);
197             if (delem) ditem->insert(delem, OFTrue /*replaceOld*/); else result=EC_MemoryExhausted;
198           }
199           if (result==EC_Normal)
200           {
201             dseq->insert(ditem);
202             dset.insert(dseq, OFTrue /*replaceOld*/);
203           } else {
204             // out of memory during creation of sequence contents.
205             delete dseq;
206             delete ditem;
207             result = EC_MemoryExhausted;
208           }
209         } else {
210           // could allocate item but not sequence. Bail out.
211           delete ditem;
212           result = EC_MemoryExhausted;
213         }
214       }
215       else result = EC_MemoryExhausted;
216     }
217   } else {
218     if (presentationLUT==DVPSP_inverse) presentationLUTShape.putString("INVERSE");
219     else if (presentationLUT==DVPSP_lin_od) presentationLUTShape.putString("LIN OD");
220     else presentationLUTShape.putString("IDENTITY");
221     ADD_TO_DATASET(DcmCodeString, presentationLUTShape)
222   }
223   if (withSOPInstance) { ADD_TO_DATASET(DcmUniqueIdentifier, sOPInstanceUID) }
224 
225   return result;
226 }
227 
228 
haveTable()229 OFBool DVPSPresentationLUT::haveTable()
230 {
231   if ((presentationLUTDescriptor.getVM()==3)&&(presentationLUTData.getLength() > 0)) return OFTrue;
232   else return OFFalse;
233 }
234 
getSOPInstanceUID()235 const char *DVPSPresentationLUT::getSOPInstanceUID()
236 {
237   char *c = NULL;
238   if (EC_Normal == sOPInstanceUID.getString(c)) return c; else return NULL;
239 }
240 
getCurrentExplanation()241 const char *DVPSPresentationLUT::getCurrentExplanation()
242 {
243   const char *value = NULL;
244   switch (presentationLUT)
245   {
246     case DVPSP_identity:
247       value = "Identity Presentation LUT Shape";
248       break;
249     case DVPSP_inverse:
250       value = "Inverse Presentation LUT Shape";
251       break;
252     case DVPSP_lin_od:
253       value = "Linear Optical Density Presentation LUT Shape";
254       break;
255     case DVPSP_table:
256       value = getLUTExplanation();
257       if (value==NULL) value = "Unnamed Presentation LUT";
258       break;
259   }
260   return value;
261 }
262 
getLUTExplanation()263 const char *DVPSPresentationLUT::getLUTExplanation()
264 {
265   char *value = NULL;
266   if (EC_Normal != presentationLUTExplanation.getString(value)) return NULL;
267   return value;
268 }
269 
setLUT(DcmUnsignedShort & lutDescriptor,DcmUnsignedShort & lutData,DcmLongString & lutExplanation)270 OFCondition DVPSPresentationLUT::setLUT(
271     DcmUnsignedShort& lutDescriptor,
272     DcmUnsignedShort& lutData,
273     DcmLongString& lutExplanation)
274 {
275   if ((lutDescriptor.getVM()==3)&&(lutData.getLength() > 0))
276   {
277     presentationLUTDescriptor = lutDescriptor;
278     presentationLUTData = lutData;
279     presentationLUTExplanation = lutExplanation;
280     presentationLUT = DVPSP_table;
281   } else return EC_IllegalCall;
282   return EC_Normal;
283 }
284 
setType(DVPSPresentationLUTType newType)285 OFCondition DVPSPresentationLUT::setType(DVPSPresentationLUTType newType)
286 {
287   if ((newType == DVPSP_table)&&(! haveTable())) return EC_IllegalCall;
288   presentationLUT = newType;
289   return EC_Normal;
290 }
291 
292 
setSOPInstanceUID(const char * value)293 OFCondition DVPSPresentationLUT::setSOPInstanceUID(const char *value)
294 {
295   if ((value==NULL)||(strlen(value)==0)) return EC_IllegalCall;
296   return sOPInstanceUID.putString(value);
297 }
298 
299 
isLegalPrintPresentationLUT()300 OFBool DVPSPresentationLUT::isLegalPrintPresentationLUT()
301 {
302   OFBool result = OFFalse;
303   Uint16 val=0;
304   switch (presentationLUT)
305   {
306     case DVPSP_table:
307       if (EC_Normal == presentationLUTDescriptor.getUint16(val,2))
308       {
309         if ((val>=10)&&(val<=16)) result = OFTrue;
310       }
311       break;
312     case DVPSP_inverse:
313       break;
314     case DVPSP_identity:
315     case DVPSP_lin_od:
316       result = OFTrue;
317       break;
318   }
319   return result;
320 }
321 
matchesImageDepth(OFBool is12bit)322 OFBool DVPSPresentationLUT::matchesImageDepth(OFBool is12bit)
323 {
324   Uint16 numEntries=0;
325   Uint16 firstMapped=0;
326   OFBool result = OFFalse;
327   switch (presentationLUT)
328   {
329     case DVPSP_table:
330 
331       if ((EC_Normal == presentationLUTDescriptor.getUint16(numEntries,0)) &&
332          (EC_Normal == presentationLUTDescriptor.getUint16(firstMapped,1)))
333       {
334       	if ((firstMapped == 0)&&((is12bit && (numEntries == 4096))||((!is12bit) && (numEntries == 256)))) result = OFTrue;
335       }
336       break;
337     case DVPSP_inverse:
338       break;
339     case DVPSP_identity:
340     case DVPSP_lin_od:
341       result = OFTrue;
342       break;
343   }
344   return result;
345 }
346 
getAlignment()347 DVPSPrintPresentationLUTAlignment DVPSPresentationLUT::getAlignment()
348 {
349   if (presentationLUT == DVPSP_table)
350   {
351     Uint16 numberOfEntries = 0;
352     Uint16 firstEntryMapped = 0xFFFF;
353     if (EC_Normal != presentationLUTDescriptor.getUint16(numberOfEntries, 0)) numberOfEntries = 0;
354     if (EC_Normal != presentationLUTDescriptor.getUint16(firstEntryMapped, 1)) firstEntryMapped = 0xFFFF;
355     if ((numberOfEntries == 256)&&(firstEntryMapped == 0)) return DVPSK_table8;
356     if ((numberOfEntries == 4096)&&(firstEntryMapped == 0)) return DVPSK_table12;
357     return DVPSK_other;
358   }
359   return DVPSK_shape;
360 }
361 
362 
printSCPCreate(DcmDataset * rqDataset,T_DIMSE_Message & rsp,DcmDataset * & rspDataset,OFBool matchRequired,OFBool supports12Bit)363 OFBool DVPSPresentationLUT::printSCPCreate(
364   DcmDataset *rqDataset,
365   T_DIMSE_Message& rsp,
366   DcmDataset *& rspDataset,
367   OFBool matchRequired,
368   OFBool supports12Bit)
369 {
370   OFBool result = OFTrue;
371   DcmStack stack;
372 
373   if ((rqDataset==NULL)||(EC_Normal != read(*rqDataset, OFFalse)))
374   {
375     DCMPSTAT_WARN("cannot create Presentation LUT: attribute list error.");
376     rsp.msg.NCreateRSP.DimseStatus = STATUS_N_NoSuchAttribute;
377     result = OFFalse;
378   }
379 
380   // read() has cleared sOPInstanceUID; assign UID now.
381   if (EC_Normal != setSOPInstanceUID(rsp.msg.NCreateRSP.AffectedSOPInstanceUID))
382   {
383     rsp.msg.NCreateRSP.DimseStatus = STATUS_N_ProcessingFailure;
384     result = OFFalse;
385   }
386 
387   // browse through rqDataset and check for unsupported attributes
388   if (result && rqDataset)
389   {
390     OFBool intoSub = OFTrue;
391     stack.clear();
392     while (EC_Normal == rqDataset->nextObject(stack, intoSub))
393     {
394       intoSub = OFFalse;
395       const DcmTagKey& currentTag = (stack.top())->getTag();
396       if (currentTag.getElement() == 0x0000) /* group length */ ;
397       else if (currentTag == DCM_PresentationLUTShape) /* OK */ ;
398       else if (currentTag == DCM_PresentationLUTSequence) /* OK */ ;
399       else
400       {
401         DCMPSTAT_WARN("cannot create Presentation LUT: unsupported attribute received:" << OFendl
402             << DcmObject::PrintHelper(*stack.top(), DCMTypes::PF_shortenLongTagValues));
403         rsp.msg.NCreateRSP.DimseStatus = STATUS_N_NoSuchAttribute;
404         result = OFFalse;
405       }
406     }
407   }
408 
409   // if match between LUT and pixel data required, enforce rule
410   if (result && matchRequired)
411   {
412     OFBool matches = OFTrue;
413     switch (getAlignment())
414     {
415       case DVPSK_shape:
416       case DVPSK_table8:
417         break; // always OK
418       case DVPSK_table12:
419         // is OK if printer supports 12 bit
420         matches = supports12Bit;
421         break;
422       case DVPSK_other: // never fits
423         matches = OFFalse;
424         break;
425     }
426     if (!matches)
427     {
428       DCMPSTAT_WARN("cannot create Presentation LUT: Mismatch between LUT entries and image pixel depth.");
429       rsp.msg.NCreateRSP.DimseStatus = STATUS_N_NoSuchAttribute;
430       result = OFFalse;
431     }
432   }
433 
434   // if n-create was successful, create response dataset
435   if (result)
436   {
437     rspDataset = new DcmDataset;
438     if (rspDataset)
439     {
440       if (EC_Normal == write(*rspDataset, OFFalse))
441       {
442         rsp.msg.NCreateRSP.DataSetType = DIMSE_DATASET_PRESENT;
443       } else {
444       	delete rspDataset;
445       	rspDataset = NULL;
446         rsp.msg.NCreateRSP.DimseStatus = STATUS_N_ProcessingFailure;
447         result = OFFalse;
448       }
449     } else {
450       rsp.msg.NCreateRSP.DimseStatus = STATUS_N_ProcessingFailure;
451       result = OFFalse;
452     }
453   }
454   return result;
455 }
456