Coverage Report

Created: 2026-09-28 07:04

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/dcmtk/dcmimgle/libsrc/didocu.cc
Line
Count
Source
1
/*
2
 *
3
 *  Copyright (C) 1996-2025, 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:  dcmimgle
15
 *
16
 *  Author:  Joerg Riesmeier
17
 *
18
 *  Purpose: DicomDocument (Source)
19
 *
20
 */
21
22
23
#include "dcmtk/config/osconfig.h"
24
25
#include "dcmtk/dcmdata/dctk.h"
26
27
#include "dcmtk/dcmimgle/didocu.h"
28
#include "dcmtk/dcmimgle/diutils.h"
29
30
31
/*----------------*
32
 *  constructors  *
33
 *----------------*/
34
35
DiDocument::DiDocument(const OFFilename &filename,
36
                       const unsigned long flags,
37
                       const unsigned long fstart,
38
                       const unsigned long fcount)
39
0
  : Object(NULL),
40
0
    FileFormat(new DcmFileFormat()),
41
0
    PixelData(NULL),
42
0
    Xfer(EXS_Unknown),
43
0
    FrameStart(fstart),
44
0
    FrameCount(fcount),
45
0
    Flags(flags),
46
0
    PhotometricInterpretation()
47
0
{
48
0
    if (FileFormat)
49
0
    {
50
0
        if (FileFormat->loadFile(filename).bad())
51
0
        {
52
0
            DCMIMGLE_ERROR("can't read file '" << filename << "'");
53
0
            delete FileFormat;
54
0
            FileFormat = NULL;
55
0
        } else {
56
0
            Object = FileFormat->getDataset();
57
0
            if (Object != NULL)
58
0
            {
59
0
                Xfer = OFstatic_cast(DcmDataset *, Object)->getOriginalXfer();
60
0
                convertPixelData();
61
0
            }
62
0
        }
63
0
    }
64
0
}
65
66
67
DiDocument::DiDocument(DcmObject *object,
68
                       const E_TransferSyntax xfer,
69
                       const unsigned long flags,
70
                       const unsigned long fstart,
71
                       const unsigned long fcount)
72
0
  : Object(NULL),
73
0
    FileFormat(NULL),
74
0
    PixelData(NULL),
75
0
    Xfer(xfer),
76
0
    FrameStart(fstart),
77
0
    FrameCount(fcount),
78
0
    Flags(flags),
79
0
    PhotometricInterpretation()
80
0
{
81
0
    if (object != NULL)
82
0
    {
83
0
        const DcmEVR classType = object->ident();
84
        // check whether given DICOM object has a valid type
85
0
        if (classType == EVR_fileFormat)
86
0
        {
87
            // store reference to DICOM file format to be deleted on object destruction
88
0
            if (Flags & CIF_TakeOverExternalDataset)
89
0
                FileFormat = OFstatic_cast(DcmFileFormat *, object);
90
0
            Object = OFstatic_cast(DcmFileFormat *, object)->getDataset();
91
0
        }
92
0
        else if ((classType == EVR_dataset) || (classType == EVR_item))
93
0
            Object = object;
94
0
        else
95
0
            DCMIMGLE_ERROR("invalid DICOM object passed to constructor (wrong class)");
96
0
        if (Object != NULL)
97
0
        {
98
            // try to determine the transfer syntax from the given object
99
0
            if (Xfer == EXS_Unknown)
100
0
            {
101
                // check type before casting the object
102
0
                if (Object->ident() == EVR_dataset)
103
0
                    Xfer = OFstatic_cast(DcmDataset *, Object)->getOriginalXfer();
104
0
                else // could only be an item
105
0
                    DCMIMGLE_WARN("can't determine original transfer syntax from given DICOM object");
106
0
            }
107
0
            convertPixelData();
108
0
        }
109
0
    }
110
0
}
111
112
113
void DiDocument::convertPixelData()
114
0
{
115
0
    DcmStack pstack;
116
0
    OFCondition status;
117
0
    DcmXfer xfer(Xfer);
118
0
    DCMIMGLE_DEBUG("transfer syntax of DICOM dataset: " << xfer.getXferName() << " (" << xfer.getXferID() << ")");
119
    // only search on main dataset level
120
0
    if (search(DCM_PixelData, pstack))
121
0
    {
122
0
        DcmObject *pobject = pstack.top();
123
0
        if (pobject != NULL)
124
0
        {
125
            // check for correct class before type casting
126
0
            if (pobject->ident() == EVR_PixelData)
127
0
            {
128
0
                PixelData = OFstatic_cast(DcmPixelData *, pobject);
129
                // check for a special (faulty) case where the original pixel data is uncompressed in
130
                // native format and the transfer syntax of the dataset refers to encapsulated format
131
0
                if (Object->ident() == EVR_dataset)
132
0
                {
133
0
                    E_TransferSyntax repType = EXS_Unknown;
134
0
                    const DcmRepresentationParameter *repParam = NULL;
135
0
                    PixelData->getOriginalRepresentationKey(repType, repParam);
136
0
                    if (xfer.usesEncapsulatedFormat() && DcmXfer(repType).usesNativeFormat())
137
0
                    {
138
0
                        DCMIMGLE_WARN("pixel data is stored in native format, although "
139
0
                            << "the transfer syntax of the dataset refers to encapsulated format");
140
0
                    }
141
0
                }
142
                // convert pixel data to uncompressed format (if required)
143
0
                if ((Flags & CIF_DecompressCompletePixelData) || !(Flags & CIF_UsePartialAccessToPixelData))
144
0
                {
145
0
                    if (Object->ident() == EVR_dataset)
146
0
                    {
147
                        // Call DcmDataset::chooseRepresentation() to enable an update
148
                        // of the transfer syntax attributes of the dataset instance
149
0
                        status = OFstatic_cast(DcmDataset *, Object)->chooseRepresentation(EXS_LittleEndianExplicit, NULL);
150
0
                    }
151
0
                    else
152
0
                    {
153
                        // Object is a DcmItem instance. Directly call DcmPixelData::chooseRepresentation().
154
0
                        pstack.clear();
155
                        // push reference to DICOM dataset on the stack (required for decompression process)
156
0
                        pstack.push(Object);
157
                        // dummy stack entry
158
0
                        pstack.push(PixelData);
159
0
                        status = PixelData->chooseRepresentation(EXS_LittleEndianExplicit, NULL, pstack);
160
0
                    }
161
0
                    if (status.good())
162
0
                    {
163
                        // set transfer syntax to native format (uncompressed pixel data)
164
0
                        if (xfer.usesEncapsulatedFormat())
165
0
                        {
166
0
                            Xfer = EXS_LittleEndianExplicit;
167
0
                            DCMIMGLE_DEBUG("decompressed complete pixel data in memory: " << PixelData->getLength(Xfer) << " bytes");
168
0
                        }
169
0
                    } else {
170
0
                        DCMIMGLE_ERROR("can't change to unencapsulated representation for pixel data");
171
0
                        DCMIMGLE_DEBUG("DcmPixelData::chooseRepresentation() returned: " << status.text());
172
0
                    }
173
0
                }
174
                // determine color model of the decompressed image
175
0
                status = PixelData->getDecompressedColorModel(OFstatic_cast(DcmItem *, Object), PhotometricInterpretation);
176
0
                if (status.bad())
177
0
                {
178
0
                    if (Flags & CIF_AcrNemaCompatibility)
179
0
                    {
180
0
                        DCMIMGLE_WARN("can't determine 'PhotometricInterpretation' of decompressed image "
181
0
                            << "... assuming MONOCHROME2 (ACR-NEMA compatibility)");
182
0
                    } else
183
0
                        DCMIMGLE_ERROR("can't determine 'PhotometricInterpretation' of decompressed image");
184
0
                    DCMIMGLE_DEBUG("DcmPixelData::getDecompressedColorModel() returned: " << status.text());
185
0
                }
186
0
            } else {
187
0
                DCMIMGLE_ERROR("invalid pixel data in DICOM dataset (wrong class)");
188
0
                DCMIMGLE_DEBUG("found PixelData " << DCM_PixelData << " as an instance of the class for VR '"
189
0
                    << OFSTRING_GUARD(DcmVR(pobject->ident()).getVRName()) << "' instead of '"
190
0
                    << OFSTRING_GUARD(DcmVR(EVR_PixelData).getVRName()) << "'");
191
0
            }
192
0
        } else
193
0
            DCMIMGLE_ERROR("invalid pixel data in DICOM dataset");
194
0
    } else
195
0
        DCMIMGLE_ERROR("no pixel data found in DICOM dataset");
196
0
}
197
198
199
/*--------------*
200
 *  destructor  *
201
 *--------------*/
202
203
DiDocument::~DiDocument()
204
0
{
205
    // DICOM image loaded from file: delete file format (and data set)
206
0
    if (FileFormat != NULL)
207
0
        delete FileFormat;
208
    // DICOM image loaded from external data set: only delete if flag is set
209
0
    else if (Flags & CIF_TakeOverExternalDataset)
210
0
        delete Object;
211
0
}
212
213
214
/********************************************************************/
215
216
217
DcmElement *DiDocument::search(const DcmTagKey &tag,
218
                               DcmObject *obj) const
219
0
{
220
0
    DcmStack stack;
221
0
    if (obj == NULL)
222
0
        obj = Object;
223
    // only search on main dataset level
224
0
    if ((obj != NULL) && (obj->search(tag, stack, ESM_fromHere, OFFalse /* searchIntoSub */) == EC_Normal) &&
225
0
        (stack.top()->getLength(Xfer) > 0) && stack.top()->isElement())
226
0
    {
227
0
        return OFstatic_cast(DcmElement *, stack.top());
228
0
    }
229
0
    return NULL;
230
0
}
231
232
233
/********************************************************************/
234
235
236
int DiDocument::search(const DcmTagKey &tag,
237
                       DcmStack &pstack) const
238
0
{
239
0
    if (pstack.empty())
240
0
        pstack.push(Object);
241
0
    DcmObject *obj = pstack.top();
242
0
    if ((obj != NULL) && (obj->search(tag, pstack, ESM_fromHere, OFFalse /* searchIntoSub */) == EC_Normal) &&
243
0
        (pstack.top()->getLength(Xfer) > 0))
244
0
            return 1;
245
0
    return 0;
246
0
}
247
248
249
/********************************************************************/
250
251
252
unsigned long DiDocument::getVM(const DcmTagKey &tag) const
253
0
{
254
0
    DcmElement *elem = search(tag);
255
0
    if (elem != NULL)
256
0
        return elem->getVM();
257
0
    return 0;
258
0
}
259
260
261
unsigned long DiDocument::getValue(const DcmTagKey &tag,
262
                                   Uint16 &returnVal,
263
                                   const unsigned long pos,
264
                                   DcmItem *item,
265
                                   const OFBool allowSigned) const
266
0
{
267
0
    return getElemValue(search(tag, item), returnVal, pos, allowSigned);
268
0
}
269
270
271
unsigned long DiDocument::getValue(const DcmTagKey &tag,
272
                                   Sint16 &returnVal,
273
                                   const unsigned long pos,
274
                                   DcmItem *item) const
275
0
{
276
0
    DcmElement *elem = search(tag, item);
277
0
    if (elem != NULL)
278
0
    {
279
0
        if (elem->getSint16(returnVal, pos).good())
280
0
            return elem->getVM();
281
0
    }
282
0
    return 0;
283
0
}
284
285
286
unsigned long DiDocument::getValue(const DcmTagKey &tag,
287
                                   Uint32 &returnVal,
288
                                   const unsigned long pos,
289
                                   DcmItem *item) const
290
0
{
291
0
    DcmElement *elem = search(tag, item);
292
0
    if (elem != NULL)
293
0
    {
294
0
        if (elem->getUint32(returnVal, pos).good())
295
0
            return elem->getVM();
296
0
    }
297
0
    return 0;
298
0
}
299
300
301
unsigned long DiDocument::getValue(const DcmTagKey &tag,
302
                                   Sint32 &returnVal,
303
                                   const unsigned long pos,
304
                                   DcmItem *item) const
305
0
{
306
0
    DcmElement *elem = search(tag, item);
307
0
    if (elem != NULL)
308
0
    {
309
0
        if (elem->getSint32(returnVal, pos).good())
310
0
            return elem->getVM();
311
0
    }
312
0
    return 0;
313
0
}
314
315
316
unsigned long DiDocument::getValue(const DcmTagKey &tag,
317
                                   double &returnVal,
318
                                   const unsigned long pos,
319
                                   DcmItem *item) const
320
0
{
321
0
    DcmElement *elem = search(tag, item);
322
0
    if (elem != NULL)
323
0
    {
324
0
        if (elem->getFloat64(returnVal, pos).good())
325
0
            return elem->getVM();
326
0
    }
327
0
    return 0;
328
0
}
329
330
331
unsigned long DiDocument::getValue(const DcmTagKey &tag,
332
                                   const Uint16 *&returnVal,
333
                                   DcmItem *item) const
334
0
{
335
0
    DcmElement *elem = search(tag, item);
336
0
    if (elem != NULL)
337
0
    {
338
0
        Uint16 *val;
339
0
        if (elem->getUint16Array(val).good())
340
0
        {
341
0
            returnVal = val;
342
0
            const DcmEVR vr = elem->getVR();
343
0
            if ((vr == EVR_OB) || (vr == EVR_OW) || (vr == EVR_lt))
344
0
                return elem->getLength(Xfer) / sizeof(Uint16);
345
0
            return elem->getVM();
346
0
        }
347
0
    }
348
0
    return 0;
349
0
}
350
351
352
unsigned long DiDocument::getValue(const DcmTagKey &tag,
353
                                   const char *&returnVal,
354
                                   DcmItem *item) const
355
0
{
356
0
    return getElemValue(search(tag, item), returnVal);
357
0
}
358
359
360
unsigned long DiDocument::getValue(const DcmTagKey &tag,
361
                                   OFString &returnVal,
362
                                   const unsigned long pos,
363
                                   DcmItem *item) const
364
0
{
365
0
    return getElemValue(search(tag, item), returnVal, pos);
366
0
}
367
368
369
unsigned long DiDocument::getSequence(const DcmTagKey &tag,
370
                                      DcmSequenceOfItems *&seq,
371
                                      DcmItem *item) const
372
0
{
373
0
    DcmElement *elem = search(tag, item);
374
0
    if ((elem != NULL) && (elem->ident() == EVR_SQ))
375
0
        return (seq = OFstatic_cast(DcmSequenceOfItems *, elem))->card();
376
0
    return 0;
377
0
}
378
379
380
unsigned long DiDocument::getElemValue(const DcmElement *elem,
381
                                       Uint16 &returnVal,
382
                                       const unsigned long pos,
383
                                       const OFBool allowSigned)
384
0
{
385
0
    if (elem != NULL)
386
0
    {
387
        // remove 'const' to use non-const methods
388
0
        if (OFconst_cast(DcmElement *, elem)->getUint16(returnVal, pos).good())
389
0
            return OFconst_cast(DcmElement *, elem)->getVM();
390
0
        else if (allowSigned)
391
0
        {
392
            // try to retrieve signed value ...
393
0
            Sint16 value = 0;
394
0
            if (OFconst_cast(DcmElement *, elem)->getSint16(value, pos).good())
395
0
            {
396
                // ... and cast to unsigned type
397
0
                returnVal = OFstatic_cast(Uint16, value);
398
0
                DCMIMGLE_TRACE("retrieved signed value (" << value << ") at position " << pos
399
0
                    << " from element " << OFconst_cast(DcmElement *, elem)->getTag()
400
0
                    << ", VR=" << DcmVR(OFconst_cast(DcmElement *, elem)->getVR()).getVRName()
401
0
                    << ", VM=" << OFconst_cast(DcmElement *, elem)->getVM());
402
0
                return OFconst_cast(DcmElement *, elem)->getVM();
403
0
            }
404
0
        }
405
0
    }
406
0
    return 0;
407
0
}
408
409
410
unsigned long DiDocument::getElemValue(const DcmElement *elem,
411
                                       const Uint16 *&returnVal)
412
0
{
413
0
    if (elem != NULL)
414
0
    {
415
0
        Uint16 *val;
416
        // remove 'const' to use non-const methods
417
0
        if (OFconst_cast(DcmElement *, elem)->getUint16Array(val).good())
418
0
        {
419
0
            returnVal = val;
420
0
            const DcmEVR vr = OFconst_cast(DcmElement *, elem)->getVR();
421
0
            if ((vr == EVR_OW) || (vr == EVR_lt))
422
0
                return OFconst_cast(DcmElement *, elem)->getLength(/*Xfer*/) / sizeof(Uint16);
423
0
            return OFconst_cast(DcmElement *, elem)->getVM();
424
0
        }
425
0
    }
426
0
    return 0;
427
0
}
428
429
430
unsigned long DiDocument::getElemValue(const DcmElement *elem,
431
                                       const char *&returnVal)
432
0
{
433
0
    if (elem != NULL)
434
0
    {
435
0
        char *val;
436
        // remove 'const' to use non-const methods
437
0
        if (OFconst_cast(DcmElement *, elem)->getString(val).good())
438
0
        {
439
0
            returnVal = val;
440
0
            return OFconst_cast(DcmElement *, elem)->getVM();
441
0
        }
442
0
    }
443
0
    return 0;
444
0
}
445
446
447
unsigned long DiDocument::getElemValue(const DcmElement *elem,
448
                                       OFString &returnVal,
449
                                       const unsigned long pos)
450
0
{
451
0
    if (elem != NULL)
452
0
    {
453
        // remove 'const' to use non-const methods
454
0
        if (OFconst_cast(DcmElement *, elem)->getOFString(returnVal, pos).good())
455
0
            return OFconst_cast(DcmElement *, elem)->getVM();
456
0
    }
457
0
    return 0;
458
0
}