Coverage Report

Created: 2026-05-16 06:37

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/dcmtk/dcmdata/libsrc/dcpxitem.cc
Line
Count
Source
1
/*
2
 *
3
 *  Copyright (C) 1994-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:  dcmdata
15
 *
16
 *  Author:  Gerd Ehlers, Andreas Barth
17
 *
18
 *  Purpose: Implementation of class DcmPixelItem
19
 *
20
 */
21
22
23
#include "dcmtk/config/osconfig.h"    /* make sure OS specific configuration is included first */
24
25
#include "dcmtk/ofstd/ofstream.h"
26
#include "dcmtk/dcmdata/dcpxitem.h"
27
#include "dcmtk/dcmdata/dcswap.h"
28
#include "dcmtk/ofstd/ofstring.h"
29
#include "dcmtk/ofstd/ofstd.h"
30
#include "dcmtk/dcmdata/dcistrma.h"    /* for class DcmInputStream */
31
#include "dcmtk/dcmdata/dcostrma.h"    /* for class DcmOutputStream */
32
#include "dcmtk/dcmdata/dcwcache.h"    /* for class DcmWriteCache */
33
34
35
// ********************************
36
37
38
DcmPixelItem::DcmPixelItem(const DcmTag &tag,
39
                           const Uint32 len)
40
0
  : DcmOtherByteOtherWord(tag, len)
41
0
{
42
0
    setTagVR(EVR_pixelItem);
43
0
}
44
45
46
DcmPixelItem::DcmPixelItem(const DcmPixelItem &old)
47
0
  : DcmOtherByteOtherWord(old)
48
0
{
49
0
}
50
51
52
DcmPixelItem &DcmPixelItem::operator=(const DcmPixelItem &obj)
53
0
{
54
0
    DcmOtherByteOtherWord::operator=(obj);
55
0
    return *this;
56
0
}
57
58
59
OFCondition DcmPixelItem::copyFrom(const DcmObject &rhs)
60
0
{
61
0
    if (this != &rhs)
62
0
    {
63
0
        if (rhs.ident() != ident()) return EC_IllegalCall;
64
0
        *this = OFstatic_cast(const DcmPixelItem &, rhs);
65
0
    }
66
0
    return EC_Normal;
67
0
}
68
69
70
DcmPixelItem::~DcmPixelItem()
71
0
{
72
0
}
73
74
75
// ********************************
76
77
78
OFBool DcmPixelItem::isNested() const
79
0
{
80
0
    OFBool nested = OFFalse;
81
0
    if (getParent() != NULL)
82
0
    {
83
        // check for surrounding structure of a pixel sequence
84
0
        if ((getParent()->ident() == EVR_pixelSQ))
85
0
            nested = OFTrue;
86
0
    }
87
0
    return nested;
88
0
}
89
90
91
DcmItem *DcmPixelItem::getParentItem()
92
0
{
93
0
    DcmItem *parentItem = NULL;
94
0
    if (getParent() != NULL)
95
0
    {
96
        // make sure that the direct parent has the correct type
97
0
        if (getParent()->ident() == EVR_pixelSQ)
98
0
        {
99
0
            DcmObject *parent = getParent()->getParent();
100
0
            if (parent != NULL)
101
0
            {
102
                // make sure that it is really a class derived from DcmItem
103
0
                switch (parent->ident())
104
0
                {
105
0
                    case EVR_metainfo:
106
0
                    case EVR_dataset:
107
0
                    case EVR_item:
108
0
                    case EVR_dirRecord:
109
0
                        parentItem = OFreinterpret_cast(DcmItem *, parent);
110
0
                        break;
111
0
                    default:
112
0
                        DCMDATA_DEBUG("DcmPixelItem::getParentItem() Parent object has wrong class identifier: "
113
0
                            << OFstatic_cast(int, parent->ident())
114
0
                            << " (" << DcmVR(parent->ident()).getVRName() << ")");
115
0
                        break;
116
0
                }
117
0
            }
118
0
        } else {
119
0
            DCMDATA_DEBUG("DcmPixelItem::getParentItem() Direct parent object is not a pixel sequence");
120
0
        }
121
0
    }
122
0
    return parentItem;
123
0
}
124
125
126
// ********************************
127
128
129
Uint32 DcmPixelItem::calcElementLength(const E_TransferSyntax xfer,
130
                                       const E_EncodingType enctype)
131
0
{
132
    /* silence unused arguments warnings */
133
0
    OFstatic_cast(void, xfer);
134
0
    OFstatic_cast(void, enctype);
135
    /* get length of the pixel data */
136
0
    Uint32 valueLength = getLengthField();
137
    /* make sure the value did not overflow, clamp it otherwise. */
138
0
    if (OFStandard::check32BitAddOverflow(valueLength, 8))
139
0
      return OFnumeric_limits<Uint32>::max();
140
0
    return valueLength + 8;
141
0
}
142
143
144
// ********************************
145
146
147
OFCondition DcmPixelItem::writeTagAndLength(DcmOutputStream &outStream,
148
                                            const E_TransferSyntax oxfer,
149
                                            Uint32 &writtenBytes) const
150
0
{
151
0
    OFCondition l_error = outStream.status();
152
0
    if (l_error.good())
153
0
    {
154
        /* write tag information */
155
0
        l_error = writeTag(outStream, getTag(), oxfer);
156
0
        writtenBytes = 4;
157
        /* prepare to write the value field */
158
0
        Uint32 valueLength = getLengthField();
159
0
        DcmXfer outXfer(oxfer);
160
        /* check byte-ordering */
161
0
        const E_ByteOrder oByteOrder = outXfer.getByteOrder();
162
0
        if (oByteOrder == EBO_unknown)
163
0
        {
164
0
            return EC_IllegalCall;
165
0
        }
166
0
        swapIfNecessary(oByteOrder, gLocalByteOrder, &valueLength, 4, 4);
167
        // availability of four bytes space in output buffer
168
        // has been checked by caller.
169
0
        writtenBytes += OFstatic_cast(Uint32, outStream.write(&valueLength, 4));
170
0
    } else
171
0
        writtenBytes = 0;
172
0
    return l_error;
173
0
}
174
175
176
void DcmPixelItem::print(STD_NAMESPACE ostream &out,
177
                         const size_t flags,
178
                         const int level,
179
                         const char *pixelFileName,
180
                         size_t *pixelCounter)
181
0
{
182
    /* call inherited method */
183
0
    printPixel(out, flags, level, pixelFileName, pixelCounter);
184
0
}
185
186
187
OFCondition DcmPixelItem::createOffsetTable(const DcmOffsetList &offsetList)
188
0
{
189
0
    OFCondition result = EC_Normal;
190
191
0
    size_t numEntries = offsetList.size();
192
0
    if (numEntries > 0)
193
0
    {
194
0
        Uint32 current = 0;
195
0
        Uint32 *array = new Uint32[numEntries];
196
0
        if (array)
197
0
        {
198
0
            DCMDATA_DEBUG("DcmPixelItem: creating offset table with " << numEntries << " entries");
199
0
            OFListConstIterator(Uint32) first = offsetList.begin();
200
0
            OFListConstIterator(Uint32) last = offsetList.end();
201
0
            unsigned long idx = 0;
202
0
            OFBool overflow = OFFalse;
203
0
            while ((first != last) && result.good())
204
0
            {
205
                // check for 32-bit unsigned integer overflow (during previous iteration) and report on this
206
0
                if (overflow)
207
0
                {
208
0
                    DCMDATA_WARN("DcmPixelItem: offset value exceeds maximum (32-bit unsigned integer) for frame #"
209
0
                        << (idx + 1) << ", cannot create offset table");
210
0
                    result = EC_InvalidBasicOffsetTable;
211
0
                }
212
                // check for odd offset values, should never happen at this point (if list was filled by an encoder)
213
0
                else if (current & 1)
214
0
                {
215
0
                    DCMDATA_WARN("DcmPixelItem: odd offset value (" << current << ") for frame #"
216
0
                        << (idx + 1) << ", cannot create offset table");
217
0
                    result = EC_InvalidBasicOffsetTable;
218
0
                } else {
219
                    // value "current" is proven to be valid
220
0
                    array[idx++] = current;
221
                    // check for 32-bit unsigned integer overflow (but report only during next iteration)
222
0
                    overflow = !OFStandard::safeAdd(current, *first, current);
223
0
                    ++first;
224
0
                }
225
0
            }
226
0
            if (result.good())
227
0
            {
228
0
                result = swapIfNecessary(EBO_LittleEndian, gLocalByteOrder, array, OFstatic_cast(Uint32, numEntries * sizeof(Uint32)), sizeof(Uint32));
229
0
                if (result.good())
230
0
                    result = putUint8Array(OFreinterpret_cast(Uint8 *, array), OFstatic_cast(unsigned long, numEntries * sizeof(Uint32)));
231
0
            }
232
0
            delete[] array;
233
0
        } else
234
0
            result = EC_MemoryExhausted;
235
0
    }
236
0
    return result;
237
0
}
238
239
240
OFCondition DcmPixelItem::writeXML(STD_NAMESPACE ostream &out,
241
                                   const size_t flags)
242
0
{
243
0
    if (flags & DCMTypes::XF_useNativeModel)
244
0
    {
245
        /* in Native DICOM Model, there is no concept of a "pixel item" */
246
0
        return makeOFCondition(OFM_dcmdata, EC_CODE_CannotConvertToXML, OF_error,
247
0
            "Cannot convert Pixel Item to Native DICOM Model");
248
0
    } else {
249
        /* XML start tag for "item" */
250
0
        out << "<pixel-item";
251
        /* value length in bytes = 0..max */
252
0
        out << " len=\"" << getLengthField() << "\"";
253
        /* value loaded = no (or absent)*/
254
0
        if (!valueLoaded())
255
0
            out << " loaded=\"no\"";
256
        /* pixel item contains binary data */
257
0
        if (!(flags & DCMTypes::XF_writeBinaryData))
258
0
            out << " binary=\"hidden\"";
259
0
        else if (flags & DCMTypes::XF_encodeBase64)
260
0
            out << " binary=\"base64\"";
261
0
        else
262
0
            out << " binary=\"yes\"";
263
0
        out << ">";
264
        /* write element value (if loaded) */
265
0
        if (valueLoaded() && (flags & DCMTypes::XF_writeBinaryData))
266
0
        {
267
            /* encode binary data as Base64 */
268
0
            if (flags & DCMTypes::XF_encodeBase64)
269
0
            {
270
                /* pixel items always contain 8 bit data, therefore, byte swapping not required */
271
0
                OFStandard::encodeBase64(out, OFstatic_cast(Uint8 *, getValue()), OFstatic_cast(size_t, getLengthField()));
272
0
            } else {
273
                /* get and check 8 bit data */
274
0
                Uint8 *byteValues = NULL;
275
0
                if (getUint8Array(byteValues).good() && (byteValues != NULL))
276
0
                {
277
0
                    const unsigned long count = getLengthField();
278
0
                    out << STD_NAMESPACE hex << STD_NAMESPACE setfill('0');
279
                    /* print byte values in hex mode */
280
0
                    out << STD_NAMESPACE setw(2) << OFstatic_cast(int, *(byteValues++));
281
0
                    for (unsigned long i = 1; i < count; i++)
282
0
                        out << "\\" << STD_NAMESPACE setw(2) << OFstatic_cast(int, *(byteValues++));
283
                    /* reset i/o manipulators */
284
0
                    out << STD_NAMESPACE dec << STD_NAMESPACE setfill(' ');
285
0
                }
286
0
            }
287
0
        }
288
        /* XML end tag for "item" */
289
0
        out << "</pixel-item>" << OFendl;
290
        /* always report success */
291
0
        return EC_Normal;
292
0
    }
293
0
}
294
295
296
OFCondition DcmPixelItem::writeSignatureFormat(
297
    DcmOutputStream &outStream,
298
    const E_TransferSyntax oxfer,
299
    const E_EncodingType enctype,
300
    DcmWriteCache *wcache)
301
0
{
302
0
  if (dcmEnableOldSignatureFormat.get())
303
0
  {
304
      /* Old signature format as created by DCMTK releases previous to 3.5.4.
305
       * This is non-conformant because it includes the item length in pixel items.
306
       */
307
0
      return DcmOtherByteOtherWord::writeSignatureFormat(outStream, oxfer, enctype, wcache);
308
0
  }
309
0
  else
310
0
  {
311
0
      DcmWriteCache wcache2;
312
313
      /* In case the transfer state is not initialized, this is an illegal call */
314
0
      if (getTransferState() == ERW_notInitialized)
315
0
          errorFlag = EC_IllegalCall;
316
0
      else
317
0
      {
318
          /* if this is not an illegal call, we need to do something. First */
319
          /* of all, check the error state of the stream that was passed */
320
          /* only do something if the error state of the stream is ok */
321
0
          errorFlag = outStream.status();
322
0
          if (errorFlag.good())
323
0
          {
324
              /* create an object that represents the transfer syntax */
325
0
              DcmXfer outXfer(oxfer);
326
327
              /* pointer to element value if value resides in memory or old-style
328
               * write behaviour is active (i.e. everything loaded into memory upon write
329
               */
330
0
              Uint8 *value = NULL;
331
0
              OFBool accessPossible = OFFalse;
332
333
              /* check that we actually do have access to the element's value.
334
               * If the element is unaccessible (which would mean that the value resides
335
               * in file and access to the file fails), write an empty element with
336
               * zero length.
337
               */
338
0
              if (getLengthField() > 0)
339
0
              {
340
0
                if (valueLoaded())
341
0
                {
342
                  /* get this element's value. Mind the byte ordering (little */
343
                  /* or big endian) of the transfer syntax which shall be used */
344
0
                  value = OFstatic_cast(Uint8 *, getValue(outXfer.getByteOrder()));
345
0
                  if (value) accessPossible = OFTrue;
346
0
                }
347
0
                else
348
0
                {
349
                  /* Use local cache object if needed. This may cause those bytes
350
                   * that are read but not written because the output stream stalls to
351
                   * be read again, and the file from being re-opened next time.
352
                   * Therefore, this case should be avoided.
353
                   */
354
0
                  if (!wcache) wcache = &wcache2;
355
356
                  /* initialize cache object. This is safe even if the object was already initialized */
357
0
                  wcache->init(this, getLengthField(), getTransferredBytes(), outXfer.getByteOrder());
358
359
                  /* access first block of element content */
360
0
                  errorFlag = wcache->fillBuffer(*this);
361
362
                  /* check that everything worked and the buffer is non-empty now */
363
0
                  accessPossible = errorFlag.good() && (! wcache->bufferIsEmpty());
364
0
                }
365
0
              }
366
367
              /* if this element's transfer state is ERW_init (i.e. it has not yet been written to */
368
              /* the stream) and if the outstream provides enough space for tag and length information */
369
              /* write tag and length information to it, do something */
370
0
              if (getTransferState() == ERW_init)
371
0
              {
372
                  // Force a compression filter (if any) to process the input buffer, by calling outStream.write().
373
                  // This ensures that we cannot get stuck if there are just a few bytes available in the buffer
374
0
                  outStream.write(NULL, 0);
375
376
                  /* first compare with DCM_TagInfoLength (12). If there is not enough space
377
                   * in the buffer, check if the buffer is still sufficient for the requirements
378
                   * of this element, which may need only 8 instead of 12 bytes.
379
                   */
380
0
                  if (outStream.avail() >= 4)
381
0
                  {
382
                      /* if there is no value, Length (member variable) shall be set to 0 */
383
0
                      if (! accessPossible) setLengthField(0);
384
385
                      /* write tag and length information (and possibly also data type information) to the stream, */
386
                      /* mind the transfer syntax and remember the amount of bytes that have been written */
387
0
                      errorFlag = writeTag(outStream, getTag(), oxfer);
388
389
                      /* if the writing was successful, set this element's transfer */
390
                      /* state to ERW_inWork and the amount of transferred bytes to 0 */
391
0
                      if (errorFlag.good())
392
0
                      {
393
0
                          setTransferState(ERW_inWork);
394
0
                          setTransferredBytes(0);
395
0
                      }
396
0
                  } else
397
0
                      errorFlag = EC_StreamNotifyClient;
398
0
              }
399
              /* if there is a value that has to be written to the stream */
400
              /* and if this element's transfer state is ERW_inWork */
401
0
              if (accessPossible && getTransferState() == ERW_inWork)
402
0
              {
403
404
0
                  Uint32 len = 0;
405
0
                  if (valueLoaded())
406
0
                  {
407
                      /* write as many bytes as possible to the stream starting at value[getTransferredBytes()] */
408
                      /* (note that the bytes value[0] to value[getTransferredBytes()-1] have already been */
409
                      /* written to the stream) */
410
0
                      len = OFstatic_cast(Uint32, outStream.write(&value[getTransferredBytes()], getLengthField() - getTransferredBytes()));
411
412
                      /* increase the amount of bytes which have been transferred correspondingly */
413
0
                      incTransferredBytes(len);
414
415
                      /* see if there is something fishy with the stream */
416
0
                      errorFlag = outStream.status();
417
0
                  }
418
0
                  else
419
0
                  {
420
0
                      Uint32 buflen = 0;
421
0
                      OFBool done = getTransferredBytes() == getLengthField();
422
0
                      while (! done)
423
0
                      {
424
                        // re-fill buffer from file if empty
425
0
                        errorFlag = wcache->fillBuffer(*this);
426
0
                        buflen = wcache->contentLength();
427
428
0
                        if (errorFlag.good())
429
0
                        {
430
                          // write as many bytes from cache buffer to stream as possible
431
0
                          len = wcache->writeBuffer(outStream);
432
433
                          /* increase the amount of bytes which have been transferred correspondingly */
434
0
                          incTransferredBytes(len);
435
436
                          /* see if there is something fishy with the stream */
437
0
                          errorFlag = outStream.status();
438
0
                        }
439
440
                        // stop writing if something went wrong, we were unable to send all of the buffer content
441
                        // (which indicates that the output stream needs to be flushed, or everything was sent out.
442
0
                        done = errorFlag.bad() || (len < buflen) || (getTransferredBytes() == getLengthField());
443
0
                      }
444
0
                  }
445
446
                  /* if the amount of transferred bytes equals the length of the element's value, the */
447
                  /* entire value has been written to the stream. Thus, this element's transfer state */
448
                  /* has to be set to ERW_ready. If this is not the case but the error flag still shows */
449
                  /* an ok value, there was no more space in the stream and a corresponding return value */
450
                  /* has to be set. (Isn't the "else if" part superfluous?!?) */
451
0
                  if (getLengthField() == getTransferredBytes()) setTransferState(ERW_ready);
452
0
                  else if (errorFlag.good()) errorFlag = EC_StreamNotifyClient;
453
0
              }
454
0
          }
455
0
      }
456
0
  }
457
458
  /* return result value */
459
0
  return errorFlag;
460
0
}