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