1 /*
2  *
3  *  Copyright (C) 2000-2020, 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: dcmsr
15  *
16  *  Author: Joerg Riesmeier
17  *
18  *  Purpose:
19  *    classes: DSRImageReferenceValue
20  *
21  */
22 
23 
24 #include "dcmtk/config/osconfig.h"    /* make sure OS specific configuration is included first */
25 
26 #include "dcmtk/dcmsr/dsrimgvl.h"
27 #include "dcmtk/dcmsr/dsrxmld.h"
28 
29 #include "dcmtk/dcmimgle/dcmimage.h"
30 #include "dcmtk/dcmimgle/diutils.h"
31 #include "dcmtk/dcmimage/diregist.h"  /* add support for color images */
32 #include "dcmtk/dcmimage/diquant.h"   /* for DcmQuant */
33 
34 #include "dcmtk/dcmdata/dcdeftag.h"
35 #include "dcmtk/dcmdata/dcuid.h"
36 
37 
DSRImageReferenceValue()38 DSRImageReferenceValue::DSRImageReferenceValue()
39   : DSRCompositeReferenceValue(),
40     FrameList(),
41     SegmentList(),
42     PresentationState(),
43     RealWorldValueMapping(),
44     IconImage(NULL)
45 {
46 }
47 
48 
DSRImageReferenceValue(const OFString & sopClassUID,const OFString & sopInstanceUID,const OFBool check)49 DSRImageReferenceValue::DSRImageReferenceValue(const OFString &sopClassUID,
50                                                const OFString &sopInstanceUID,
51                                                const OFBool check)
52   : DSRCompositeReferenceValue(),
53     FrameList(),
54     SegmentList(),
55     PresentationState(),
56     RealWorldValueMapping(),
57     IconImage(NULL)
58 {
59     /* use the set method for checking purposes */
60     setReference(sopClassUID, sopInstanceUID, check);
61 }
62 
63 
DSRImageReferenceValue(const OFString & imageSOPClassUID,const OFString & imageSOPInstanceUID,const OFString & pstateSOPClassUID,const OFString & pstateSOPInstanceUID,const OFBool check)64 DSRImageReferenceValue::DSRImageReferenceValue(const OFString &imageSOPClassUID,
65                                                const OFString &imageSOPInstanceUID,
66                                                const OFString &pstateSOPClassUID,
67                                                const OFString &pstateSOPInstanceUID,
68                                                const OFBool check)
69   : DSRCompositeReferenceValue(),
70     FrameList(),
71     SegmentList(),
72     PresentationState(),
73     RealWorldValueMapping(),
74     IconImage(NULL)
75 {
76     /* use the set methods for checking purposes */
77     setReference(imageSOPClassUID, imageSOPInstanceUID, check);
78     setPresentationState(DSRCompositeReferenceValue(pstateSOPClassUID, pstateSOPInstanceUID, OFFalse /*check*/), check);
79 }
80 
81 
DSRImageReferenceValue(const DSRImageReferenceValue & referenceValue)82 DSRImageReferenceValue::DSRImageReferenceValue(const DSRImageReferenceValue &referenceValue)
83   : DSRCompositeReferenceValue(referenceValue),
84     FrameList(referenceValue.FrameList),
85     SegmentList(referenceValue.SegmentList),
86     PresentationState(referenceValue.PresentationState),
87     RealWorldValueMapping(referenceValue.RealWorldValueMapping),
88     IconImage(NULL)
89 {
90     /* do not check values since this would be unexpected to the user */
91 
92     /* create copy of icon image (if any), first frame only */
93     if (referenceValue.IconImage != NULL)
94         IconImage = referenceValue.IconImage->createDicomImage(0 /*fstart*/, 1 /*fcount*/);
95 }
96 
97 
DSRImageReferenceValue(const DSRCompositeReferenceValue & imageReferenceValue,const DSRCompositeReferenceValue & pstateReferenceValue)98 DSRImageReferenceValue::DSRImageReferenceValue(const DSRCompositeReferenceValue &imageReferenceValue,
99                                                const DSRCompositeReferenceValue &pstateReferenceValue)
100   : DSRCompositeReferenceValue(imageReferenceValue),
101     FrameList(),
102     SegmentList(),
103     PresentationState(pstateReferenceValue),
104     RealWorldValueMapping(),
105     IconImage(NULL)
106 {
107 }
108 
109 
~DSRImageReferenceValue()110 DSRImageReferenceValue::~DSRImageReferenceValue()
111 {
112     deleteIconImage();
113 }
114 
115 
operator =(const DSRImageReferenceValue & referenceValue)116 DSRImageReferenceValue &DSRImageReferenceValue::operator=(const DSRImageReferenceValue &referenceValue)
117 {
118     /* check for self-assignment, which would create a memory leak */
119     if (this != &referenceValue)
120     {
121         DSRCompositeReferenceValue::operator=(referenceValue);
122         /* do not check since this would be unexpected to the user */
123         FrameList = referenceValue.FrameList;
124         SegmentList = referenceValue.SegmentList;
125         PresentationState = referenceValue.PresentationState;
126         RealWorldValueMapping = referenceValue.RealWorldValueMapping;
127         /* create copy of icon image (if any), first frame only */
128         IconImage = (referenceValue.IconImage != NULL) ? referenceValue.IconImage->createDicomImage(0 /*fstart*/, 1 /*fcount*/) : NULL;
129     }
130     return *this;
131 }
132 
133 
operator ==(const DSRImageReferenceValue & referenceValue) const134 OFBool DSRImageReferenceValue::operator==(const DSRImageReferenceValue &referenceValue) const
135 {
136     /* the optional icon image is not used for comparison */
137     return DSRCompositeReferenceValue::operator==(referenceValue) &&
138            (FrameList == referenceValue.FrameList) &&
139            (SegmentList == referenceValue.SegmentList) &&
140            (PresentationState == referenceValue.PresentationState) &&
141            (RealWorldValueMapping == referenceValue.RealWorldValueMapping);
142 }
143 
144 
operator !=(const DSRImageReferenceValue & referenceValue) const145 OFBool DSRImageReferenceValue::operator!=(const DSRImageReferenceValue &referenceValue) const
146 {
147     /* the optional icon image is not used for comparison */
148     return DSRCompositeReferenceValue::operator!=(referenceValue) ||
149            (FrameList != referenceValue.FrameList) ||
150            (SegmentList != referenceValue.SegmentList) ||
151            (PresentationState != referenceValue.PresentationState) ||
152            (RealWorldValueMapping != referenceValue.RealWorldValueMapping);
153 }
154 
155 
clear()156 void DSRImageReferenceValue::clear()
157 {
158     DSRCompositeReferenceValue::clear();
159     FrameList.clear();
160     SegmentList.clear();
161     PresentationState.clear();
162     RealWorldValueMapping.clear();
163     deleteIconImage();
164 }
165 
166 
isValid() const167 OFBool DSRImageReferenceValue::isValid() const
168 {
169     return DSRCompositeReferenceValue::isValid() && checkCurrentValue().good();
170 }
171 
172 
isShort(const size_t flags) const173 OFBool DSRImageReferenceValue::isShort(const size_t flags) const
174 {
175     return (FrameList.isEmpty() && SegmentList.isEmpty()) || !(flags & DSRTypes::HF_renderFullData);
176 }
177 
178 
isSegmentation() const179 OFBool DSRImageReferenceValue::isSegmentation() const
180 {
181     return isSegmentationObject(SOPClassUID);
182 }
183 
184 
print(STD_NAMESPACE ostream & stream,const size_t flags) const185 OFCondition DSRImageReferenceValue::print(STD_NAMESPACE ostream &stream,
186                                           const size_t flags) const
187 {
188     /* first, determine SOP class component */
189     OFString sopClassString = "\"" + SOPClassUID + "\"";
190     if (!(flags & DSRTypes::PF_printSOPClassUID))
191     {
192         if (flags & DSRTypes::PF_printLongSOPClassName)
193         {
194             /* look up name of known SOP classes */
195             const char *className = dcmFindNameOfUID(SOPClassUID.c_str());
196             if (className != NULL)
197                 sopClassString = className;
198         } else {
199             /* create short name for SOP class, e.g. "CT image" */
200             const char *modality = dcmSOPClassUIDToModality(SOPClassUID.c_str());
201             if (modality != NULL)
202                 sopClassString = OFString(modality) + " image";
203         }
204     }
205     /* and then, print it */
206     stream << "(" << sopClassString << ",";
207     /* print SOP instance component (if desired) */
208     if (flags & DSRTypes::PF_printSOPInstanceUID)
209         stream << "\"" << SOPInstanceUID << "\"";
210     /* print list of frame or segment numbers (if present) */
211     if (!FrameList.isEmpty())
212     {
213         stream << ",";
214         FrameList.print(stream, flags);
215     }
216     else if (!SegmentList.isEmpty())
217     {
218         stream << ",";
219         SegmentList.print(stream, flags);
220     }
221     stream << ")";
222     /* print information on presentation state (if present) */
223     if (PresentationState.isValid())
224     {
225         /* first, determine SOP class component */
226         OFString pstateClassString = "\"" + PresentationState.getSOPClassUID() + "\"";
227         if (!(flags & DSRTypes::PF_printSOPClassUID))
228         {
229             if (flags & DSRTypes::PF_printLongSOPClassName)
230             {
231                 /* look up name of known SOP classes */
232                 const char *className = dcmFindNameOfUID(PresentationState.getSOPClassUID().c_str());
233                 if (className != NULL)
234                     pstateClassString = className;
235             } else {
236                 /* create short name for presentation state, e.g. "GSPS" */
237                 const DSRTypes::E_PresentationStateType pstateType = DSRTypes::sopClassUIDToPresentationStateType(PresentationState.getSOPClassUID());
238                 if (pstateType != DSRTypes::PT_invalid)
239                     pstateClassString = DSRTypes::presentationStateTypeToShortName(pstateType);
240             }
241         }
242         /* and, then print it */
243         stream << ",(" << pstateClassString << ",";
244         /* also print SOP instance component (if desired) */
245         if (flags & DSRTypes::PF_printSOPInstanceUID)
246             stream << "\"" << PresentationState.getSOPInstanceUID() << "\"";
247         stream << ")";
248     }
249     return EC_Normal;
250 }
251 
252 
readXML(const DSRXMLDocument & doc,DSRXMLCursor cursor,const size_t flags)253 OFCondition DSRImageReferenceValue::readXML(const DSRXMLDocument &doc,
254                                             DSRXMLCursor cursor,
255                                             const size_t flags)
256 {
257     /* first read general composite reference information */
258     OFCondition result = DSRCompositeReferenceValue::readXML(doc, cursor, flags);
259     /* then read image related XML tags */
260     if (result.good())
261     {
262         cursor.gotoChild();
263         /* either frame or segment list (conditional) */
264         DSRXMLCursor childCursor = doc.getNamedNode(cursor, "frames", OFFalse /*required*/);
265         if (childCursor.valid())
266         {
267             OFString tmpString;
268             /* put element content to the frame list */
269             result = FrameList.putString(doc.getStringFromNodeContent(childCursor, tmpString).c_str());
270         } else {
271             childCursor = doc.getNamedNode(cursor, "segments", OFFalse /*required*/);
272             if (childCursor.valid())
273             {
274                 OFString tmpString;
275                 /* put element content to the segment list */
276                 result = SegmentList.putString(doc.getStringFromNodeContent(childCursor, tmpString).c_str());
277             }
278         }
279         if (result.good())
280         {
281             /* presentation state object (optional) */
282             childCursor = doc.getNamedNode(cursor, "pstate", OFFalse /*required*/);
283             if (childCursor.valid())
284                 result = PresentationState.readXML(doc, childCursor, flags);
285         }
286         if (result.good())
287         {
288             /* real world value mapping object (optional) */
289             childCursor = doc.getNamedNode(cursor, "mapping", OFFalse /*required*/);
290             if (childCursor.valid())
291                 result = RealWorldValueMapping.readXML(doc, childCursor, flags);
292         }
293     }
294     return result;
295 }
296 
297 
writeXML(STD_NAMESPACE ostream & stream,const size_t flags) const298 OFCondition DSRImageReferenceValue::writeXML(STD_NAMESPACE ostream &stream,
299                                              const size_t flags) const
300 {
301     OFCondition result = DSRCompositeReferenceValue::writeXML(stream, flags);
302     /* either frame or segment list (conditional) */
303     if (((flags & DSRTypes::XF_writeEmptyTags) && SegmentList.isEmpty()) || !FrameList.isEmpty())
304     {
305         stream << "<frames>";
306         FrameList.print(stream);
307         stream << "</frames>" << OFendl;
308     }
309     else if ((flags & DSRTypes::XF_writeEmptyTags) || !SegmentList.isEmpty())
310     {
311         stream << "<segments>";
312         SegmentList.print(stream);
313         stream << "</segments>" << OFendl;
314     }
315     /* presentation state object (optional) */
316     if ((flags & DSRTypes::XF_writeEmptyTags) || PresentationState.isValid())
317     {
318         stream << "<pstate>" << OFendl;
319         if (PresentationState.isValid())
320             PresentationState.writeXML(stream, flags);
321         stream << "</pstate>" << OFendl;
322     }
323     /* real world value mapping object (optional) */
324     if ((flags & DSRTypes::XF_writeEmptyTags) || RealWorldValueMapping.isValid())
325     {
326         stream << "<mapping>" << OFendl;
327         if (RealWorldValueMapping.isValid())
328             RealWorldValueMapping.writeXML(stream, flags);
329         stream << "</mapping>" << OFendl;
330     }
331     return result;
332 }
333 
334 
readItem(DcmItem & dataset,const size_t flags)335 OFCondition DSRImageReferenceValue::readItem(DcmItem &dataset,
336                                              const size_t flags)
337 {
338     /* be very careful, delete any previously created icon image (should never apply) */
339     deleteIconImage();
340     /* read ReferencedSOPClassUID and ReferencedSOPInstanceUID */
341     OFCondition result = DSRCompositeReferenceValue::readItem(dataset, flags);
342     if (result.good())
343     {
344         /* read ReferencedFrameNumber (conditional) */
345         FrameList.read(dataset, flags);
346         /* read ReferencedSegmentNumber (conditional) */
347         SegmentList.read(dataset, flags);
348         /* read ReferencedSOPSequence (Presentation State, optional) */
349         PresentationState.readSequence(dataset, DCM_ReferencedSOPSequence, "3" /*type*/, flags);
350         /* read ReferencedRealWorldValueMappingInstanceSequence (optional) */
351         RealWorldValueMapping.readSequence(dataset, DCM_ReferencedRealWorldValueMappingInstanceSequence, "3" /*type*/, flags);
352         /* read IconImageSequence (optional) */
353         DcmSequenceOfItems *dseq = NULL;
354         /* use local status variable since the sequence is optional */
355         const OFCondition seqStatus = dataset.findAndGetSequence(DCM_IconImageSequence, dseq);
356         DSRTypes::checkElementValue(dseq, DCM_IconImageSequence, "1", "3", seqStatus, "IMAGE content item");
357         if (seqStatus.good())
358         {
359             /* check for empty sequence (allowed!) */
360             if (!dseq->isEmpty())
361             {
362                 /* read first item */
363                 DcmItem *ditem = dseq->getItem(0);
364                 if ((ditem != NULL) && !ditem->isEmpty())
365                 {
366                     /* try to load/process the icon image */
367                     IconImage = new DicomImage(ditem, EXS_LittleEndianExplicit);
368                     if (IconImage != NULL)
369                     {
370                         if (IconImage->getStatus() != EIS_Normal)
371                             result = SR_EC_CannotCreateIconImage;
372                     } else
373                         result = EC_MemoryExhausted;
374                 } else
375                     result = SR_EC_InvalidDocumentTree;
376             }
377         }
378         /* check data and report warnings if any */
379         checkListData(SOPClassUID, FrameList, SegmentList, OFTrue /*reportWarnings*/);
380     }
381     return result;
382 }
383 
384 
writeItem(DcmItem & dataset) const385 OFCondition DSRImageReferenceValue::writeItem(DcmItem &dataset) const
386 {
387     /* write ReferencedSOPClassUID and ReferencedSOPInstanceUID */
388     OFCondition result = DSRCompositeReferenceValue::writeItem(dataset);
389     /* write ReferencedFrameNumber or ReferencedSegmentNumber (conditional) */
390     if (result.good())
391     {
392         if (!FrameList.isEmpty())
393             result = FrameList.write(dataset);
394         else if (!SegmentList.isEmpty())
395             result = SegmentList.write(dataset);
396     }
397     /* write ReferencedSOPSequence (Presentation State, optional) */
398     if (result.good())
399     {
400         if (PresentationState.isValid())
401             result = PresentationState.writeSequence(dataset, DCM_ReferencedSOPSequence);
402     }
403     /* write ReferencedRealWorldValueMappingInstanceSequence (optional) */
404     if (result.good())
405     {
406         if (RealWorldValueMapping.isValid())
407             result = RealWorldValueMapping.writeSequence(dataset, DCM_ReferencedRealWorldValueMappingInstanceSequence);
408     }
409     /* write IconImageSequence (optional) */
410     if (result.good() && (IconImage != NULL))
411     {
412         DcmItem *ditem = NULL;
413         /* create sequence with a single item */
414         result = dataset.findOrCreateSequenceItem(DCM_IconImageSequence, ditem, 0 /*position*/);
415         if (result.good())
416         {
417             /* monochrome images can be written directly */
418             if (IconImage->isMonochrome())
419             {
420                 /* write icon image to dataset */
421                 if (IconImage->writeFrameToDataset(*ditem))
422                 {
423                     /* delete unwanted element NumberOfFrames (0028,0008) */
424                     ditem->findAndDeleteElement(DCM_NumberOfFrames);
425                 } else
426                     result = EC_CorruptedData;
427             } else {
428                 OFString tmpString;
429                 /* color images need to be converted to "PALETTE COLOR" */
430                 result = DcmQuant::createPaletteColorImage(*IconImage, *ditem, OFTrue /*writeAsOW*/, OFFalse /*write16BitEntries*/,
431                     OFFalse /*floydSteinberg*/, 256 /*numberOfColors*/, tmpString /*description*/);
432             }
433         }
434     }
435     /* check data and report warnings if any */
436     checkListData(SOPClassUID, FrameList, SegmentList, OFTrue /*reportWarnings*/);
437     return result;
438 }
439 
440 
renderHTML(STD_NAMESPACE ostream & docStream,STD_NAMESPACE ostream & annexStream,size_t & annexNumber,const size_t flags) const441 OFCondition DSRImageReferenceValue::renderHTML(STD_NAMESPACE ostream &docStream,
442                                                STD_NAMESPACE ostream &annexStream,
443                                                size_t &annexNumber,
444                                                const size_t flags) const
445 {
446     /* reference: image */
447     docStream << "<a href=\"" << HTML_HYPERLINK_PREFIX_FOR_CGI;
448     docStream << "?image=" << SOPClassUID << "+" << SOPInstanceUID;
449     /* reference: pstate */
450     if (PresentationState.isValid())
451     {
452         docStream << "&amp;pstate=" << PresentationState.getSOPClassUID();
453         docStream << "+" << PresentationState.getSOPInstanceUID();
454     }
455     /* reference: frames or segments */
456     if (!FrameList.isEmpty())
457     {
458         docStream << "&amp;frames=";
459         FrameList.print(docStream, 0 /*flags*/, '+');
460     }
461     else if (!SegmentList.isEmpty())
462     {
463         docStream << "&amp;segments=";
464         SegmentList.print(docStream, 0 /*flags*/, '+');
465     }
466     docStream << "\">";
467     /* text: image */
468     const char *modality = dcmSOPClassUIDToModality(SOPClassUID.c_str());
469     if (modality != NULL)
470         docStream << modality;
471     else
472         docStream << "unknown";
473     docStream << " image";
474     /* text: presentation state */
475     if (PresentationState.isValid())
476         docStream << " with presentation state";
477     docStream << "</a>";
478     if (!isShort(flags))
479     {
480         const char *lineBreak = (flags & DSRTypes::HF_renderSectionTitlesInline) ? " " :
481                                 (flags & DSRTypes::HF_XHTML11Compatibility) ? "<br />" : "<br>";
482         if (flags & DSRTypes::HF_currentlyInsideAnnex)
483         {
484             docStream << OFendl << "<p>" << OFendl;
485             /* render frame list (= print)*/
486             docStream << "<b>Referenced Frame Number:</b>" << lineBreak;
487             FrameList.print(docStream);
488             docStream << "</p>";
489         } else {
490             docStream << " ";
491             DSRTypes::createHTMLAnnexEntry(docStream, annexStream, "for more details see", annexNumber, flags);
492             annexStream << "<p>" << OFendl;
493             /* render frame list (= print)*/
494             annexStream << "<b>Referenced Frame Number:</b>" << lineBreak;
495             FrameList.print(annexStream);
496             annexStream << "</p>" << OFendl;
497         }
498     }
499     return EC_Normal;
500 }
501 
502 
createIconImage(const OFString & filename,const unsigned long frame,const unsigned long width,const unsigned long height)503 OFCondition DSRImageReferenceValue::createIconImage(const OFString &filename,
504                                                     const unsigned long frame,
505                                                     const unsigned long width,
506                                                     const unsigned long height)
507 {
508     /* delete old icon image (if any) */
509     deleteIconImage();
510     OFCondition result = EC_IllegalParameter;
511     if (!filename.empty())
512     {
513         /* try to load specified DICOM image */
514         const unsigned long flags = CIF_UsePartialAccessToPixelData | CIF_NeverAccessEmbeddedOverlays;
515         DicomImage *image = new DicomImage(filename.c_str(), flags, frame, 1 /*fcount*/);
516         if (image != NULL)
517         {
518             /* set VOI window (for monochrome images) */
519             if (image->isMonochrome() && !image->setWindow(0))
520                 image->setMinMaxWindow();
521             /* do the real work: create a down-scaled version of the DICOM image */
522             result = createIconImage(image, width, height);
523             delete image;
524         } else
525             result = EC_MemoryExhausted;
526     }
527     return result;
528 }
529 
530 
createIconImage(DcmObject * object,const E_TransferSyntax xfer,const unsigned long frame,const unsigned long width,const unsigned long height)531 OFCondition DSRImageReferenceValue::createIconImage(DcmObject *object,
532                                                     const E_TransferSyntax xfer,
533                                                     const unsigned long frame,
534                                                     const unsigned long width,
535                                                     const unsigned long height)
536 {
537     /* delete old icon image (if any) */
538     deleteIconImage();
539     OFCondition result = EC_IllegalParameter;
540     if (object != NULL)
541     {
542         /* try to load specified DICOM image */
543         const unsigned long flags = CIF_UsePartialAccessToPixelData | CIF_NeverAccessEmbeddedOverlays;
544         DicomImage *image = new DicomImage(object, xfer, flags, frame, 1 /*fcount*/);
545         if (image != NULL)
546         {
547             /* set VOI window (for monochrome images) */
548             if (image->isMonochrome() && !image->setWindow(0))
549                 image->setMinMaxWindow();
550             /* do the real work: create a down-scaled version of the DICOM image */
551             result = createIconImage(image, width, height);
552             delete image;
553         } else
554             result = EC_MemoryExhausted;
555     }
556     return result;
557 }
558 
559 
createIconImage(const DicomImage * image,const unsigned long width,const unsigned long height)560 OFCondition DSRImageReferenceValue::createIconImage(const DicomImage *image,
561                                                     const unsigned long width,
562                                                     const unsigned long height)
563 {
564     /* delete old icon image (if any) */
565     deleteIconImage();
566     OFCondition result = EC_IllegalParameter;
567     if (image != NULL)
568     {
569         const EI_Status imageStatus = image->getStatus();
570         /* check whether image loading/processing was successful */
571         switch (imageStatus)
572         {
573             case EIS_Normal:
574             {
575                 if (image->getFrameCount() > 1)
576                     DCMSR_DEBUG("DICOM image passed for creating an icon image contains multiple frames");
577                 /* create a down-scaled version of the DICOM image */
578                 const int aspect = (width == 0) || (height == 0);
579                 IconImage = image->createScaledImage(width, height, 1 /*interpolate*/, aspect);
580                 result = (IconImage != NULL) ? EC_Normal : SR_EC_CannotCreateIconImage;
581                 break;
582             }
583             case EIS_InvalidDocument:
584             case EIS_InvalidImage:
585                 result = SR_EC_InvalidDocument;
586                 break;
587             case EIS_MissingAttribute:
588                 result = SR_EC_MandatoryAttributeMissing;
589                 break;
590             case EIS_InvalidValue:
591                 result = SR_EC_InvalidValue;
592                 break;
593             case EIS_NotSupportedValue:
594                 result = SR_EC_UnsupportedValue;
595                 break;
596             case EIS_MemoryFailure:
597                 result = EC_MemoryExhausted;
598                 break;
599             default:
600                 /* this is the fallback for all other kind of errors */
601                 result = SR_EC_CannotCreateIconImage;
602                 break;
603         }
604     }
605     return result;
606 }
607 
608 
deleteIconImage()609 void DSRImageReferenceValue::deleteIconImage()
610 {
611     delete IconImage;
612     IconImage = NULL;
613 }
614 
615 
getValue(DSRImageReferenceValue & referenceValue) const616 OFCondition DSRImageReferenceValue::getValue(DSRImageReferenceValue &referenceValue) const
617 {
618     referenceValue = *this;
619     return EC_Normal;
620 }
621 
622 
setValue(const DSRImageReferenceValue & referenceValue,const OFBool check)623 OFCondition DSRImageReferenceValue::setValue(const DSRImageReferenceValue &referenceValue,
624                                              const OFBool check)
625 {
626     OFCondition result = DSRCompositeReferenceValue::setValue(referenceValue, check);
627     if (result.good())
628     {
629         FrameList = referenceValue.FrameList;
630         SegmentList = referenceValue.SegmentList;
631         /* ignore status (return value) since the references are optional */
632         setPresentationState(referenceValue.PresentationState, check);
633         setRealWorldValueMapping(referenceValue.RealWorldValueMapping, check);
634     }
635     return result;
636 }
637 
638 
setPresentationState(const DSRCompositeReferenceValue & pstateValue,const OFBool check)639 OFCondition DSRImageReferenceValue::setPresentationState(const DSRCompositeReferenceValue &pstateValue,
640                                                          const OFBool check)
641 {
642     OFCondition result = EC_Normal;
643     /* check whether the passed value is valid */
644     if (check)
645         result = checkPresentationState(pstateValue);
646     /* both UID values need to be empty or non-empty (optional) */
647     else if (pstateValue.getSOPClassUID().empty() != pstateValue.getSOPInstanceUID().empty())
648         result = SR_EC_InvalidValue;
649     if (result.good())
650         PresentationState = pstateValue;
651     return result;
652 }
653 
654 
setRealWorldValueMapping(const DSRCompositeReferenceValue & mappingValue,const OFBool check)655 OFCondition DSRImageReferenceValue::setRealWorldValueMapping(const DSRCompositeReferenceValue &mappingValue,
656                                                              const OFBool check)
657 {
658     OFCondition result = EC_Normal;
659     /* check whether the passed value is valid */
660     if (check)
661         result = checkRealWorldValueMapping(mappingValue);
662     /* both UID values need to be empty or non-empty (optional) */
663     else if (mappingValue.getSOPClassUID().empty() != mappingValue.getSOPInstanceUID().empty())
664         result = SR_EC_InvalidValue;
665     if (result.good())
666         RealWorldValueMapping = mappingValue;
667     return result;
668 }
669 
670 
appliesToFrame(const Sint32 frameNumber) const671 OFBool DSRImageReferenceValue::appliesToFrame(const Sint32 frameNumber) const
672 {
673     OFBool result = OFTrue;
674     if (!FrameList.isEmpty())
675         result = FrameList.isElement(frameNumber);
676     return result;
677 }
678 
679 
appliesToSegment(const Uint16 segmentNumber) const680 OFBool DSRImageReferenceValue::appliesToSegment(const Uint16 segmentNumber) const
681 {
682     OFBool result = OFTrue;
683     if (!SegmentList.isEmpty())
684         result = SegmentList.isElement(segmentNumber);
685     return result;
686 }
687 
688 
isSegmentationObject(const OFString & sopClassUID) const689 OFBool DSRImageReferenceValue::isSegmentationObject(const OFString &sopClassUID) const
690 {
691     /* check for all segmentation SOP classes (according to DICOM PS 3.6-2020c) */
692     return (sopClassUID == UID_SegmentationStorage) || (sopClassUID == UID_SurfaceSegmentationStorage);
693 }
694 
695 
checkSOPClassUID(const OFString & sopClassUID) const696 OFCondition DSRImageReferenceValue::checkSOPClassUID(const OFString &sopClassUID) const
697 {
698     OFCondition result = DSRCompositeReferenceValue::checkSOPClassUID(sopClassUID);
699     if (result.good())
700     {
701         /* check for all valid/known SOP classes (according to DICOM PS 3.6) */
702         if (!dcmIsImageStorageSOPClassUID(sopClassUID.c_str()) && !isSegmentationObject(sopClassUID))
703         {
704             result = SR_EC_InvalidValue;
705         }
706     }
707     return result;
708 }
709 
710 
checkPresentationState(const DSRCompositeReferenceValue & referenceValue) const711 OFCondition DSRImageReferenceValue::checkPresentationState(const DSRCompositeReferenceValue &referenceValue) const
712 {
713     OFCondition result = EC_Normal;
714     /* the reference to a presentation state object is optional, so an empty value is also valid */
715     if (!referenceValue.isEmpty())
716     {
717         if (DSRTypes::sopClassUIDToPresentationStateType(referenceValue.getSOPClassUID()) == DSRTypes::PT_invalid)
718             result = SR_EC_InvalidValue;
719     }
720     return result;
721 }
722 
723 
checkRealWorldValueMapping(const DSRCompositeReferenceValue & referenceValue) const724 OFCondition DSRImageReferenceValue::checkRealWorldValueMapping(const DSRCompositeReferenceValue &referenceValue) const
725 {
726     OFCondition result = EC_Normal;
727     /* the reference to a real world value mapping object is optional, so an empty value is also valid */
728     if (!referenceValue.isEmpty())
729     {
730         if (referenceValue.getSOPClassUID() != UID_RealWorldValueMappingStorage)
731             result = SR_EC_InvalidValue;
732     }
733     return result;
734 }
735 
736 
737 // helper macro to avoid annoying check of boolean flag
738 #define REPORT_WARNING(msg) { if (reportWarnings) DCMSR_WARN(msg); }
739 
checkListData(const OFString & sopClassUID,const DSRImageFrameList & frameList,const DSRImageSegmentList & segmentList,const OFBool reportWarnings) const740 OFCondition DSRImageReferenceValue::checkListData(const OFString &sopClassUID,
741                                                   const DSRImageFrameList &frameList,
742                                                   const DSRImageSegmentList &segmentList,
743                                                   const OFBool reportWarnings) const
744 {
745     OFCondition result = EC_Normal;
746     /* check whether both lists of referenced frame and segment numbers are non-empty */
747     if (!frameList.isEmpty() && !segmentList.isEmpty())
748     {
749         /* this is just a warning since only one list will ever be written */
750         REPORT_WARNING("Both Referenced Frame Number and Referenced Segment Number present in IMAGE content item")
751     }
752     /* check whether referenced image is a segmentation object (see "type 1C" condition) */
753     if (!segmentList.isEmpty() && !isSegmentationObject(sopClassUID))
754     {
755         REPORT_WARNING("Referenced Segment Number present in IMAGE content item for non-segmentation object")
756         result = SR_EC_InvalidValue;
757     }
758     /* tbd: check whether referenced image is a multi-frame image? (see "type 1C" condition) */
759     return result;
760 }
761 
762 
checkCurrentValue() const763 OFCondition DSRImageReferenceValue::checkCurrentValue() const
764 {
765     OFCondition result = DSRCompositeReferenceValue::checkCurrentValue();
766     if (result.good())
767         result = checkPresentationState(PresentationState);
768     if (result.good())
769         result = checkRealWorldValueMapping(RealWorldValueMapping);
770     if (result.good())
771         result = checkListData(SOPClassUID, FrameList, SegmentList);
772     return result;
773 }
774