Coverage Report

Created: 2026-08-14 06:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/dcmtk/dcmimgle/libsrc/dcmimage.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: DicomImage-Interface (Source)
19
 *
20
 */
21
22
23
#include "dcmtk/config/osconfig.h"
24
#include "dcmtk/dcmimgle/dcmimage.h"
25
26
#include "dcmtk/dcmdata/dctypes.h"
27
#include "dcmtk/dcmdata/dcdeftag.h"
28
#include "dcmtk/dcmdata/dcobject.h"
29
#include "dcmtk/dcmdata/dcuid.h"
30
#include "dcmtk/dcmdata/dcdict.h"
31
#include "dcmtk/ofstd/ofstd.h"
32
33
#include "dcmtk/dcmimgle/diovlimg.h"
34
#include "dcmtk/dcmimgle/dimo1img.h"
35
#include "dcmtk/dcmimgle/dimo2img.h"
36
#include "dcmtk/dcmimgle/didocu.h"
37
#include "dcmtk/dcmimgle/diregbas.h"
38
#include "dcmtk/dcmimgle/diplugin.h"
39
40
#ifndef FILENAME_MAX
41
#define FILENAME_MAX 255
42
#endif
43
44
45
/*------------------*
46
 *  initialization  *
47
 *------------------*/
48
49
DiRegisterBase *DiRegisterBase::Pointer = NULL;
50
51
52
/*----------------*
53
 *  constructors  *
54
 *----------------*/
55
56
// --- create 'DicomImage' from 'filename', for valid 'flags' see 'diutils.h'
57
58
DicomImage::DicomImage(const OFFilename &filename,
59
                       const unsigned long flags,
60
                       const unsigned long fstart,
61
                       const unsigned long fcount)
62
0
  : ImageStatus(EIS_Normal),
63
0
    PhotometricInterpretation(EPI_Unknown),
64
0
    Document(NULL),
65
0
    Image(NULL)
66
0
{
67
0
    if (checkDataDictionary())                  // valid 'dicom.dic' found ?
68
0
    {
69
0
        Document = new DiDocument(filename, flags | CIF_MayDetachPixelData, fstart, fcount);
70
0
        Init();
71
0
    }
72
0
}
73
74
75
// --- create 'DicomImage' from valid 'DicomObject' with transfer syntax 'xfer'
76
77
DicomImage::DicomImage(DcmObject *object,
78
                       const E_TransferSyntax xfer,
79
                       const unsigned long flags,
80
                       const unsigned long fstart,
81
                       const unsigned long fcount)
82
1.04k
  : ImageStatus(EIS_Normal),
83
1.04k
    PhotometricInterpretation(EPI_Unknown),
84
1.04k
    Document(NULL),
85
1.04k
    Image(NULL)
86
1.04k
{
87
1.04k
    if (checkDataDictionary())                  // valid 'dicom.dic' found ?
88
0
    {
89
0
        Document = new DiDocument(object, xfer, flags, fstart, fcount);
90
0
        Init();
91
0
    }
92
1.04k
}
93
94
95
// --- create 'DicomImage' from valid 'DicomObject' with given rescale 'slope' and 'intercept'
96
97
DicomImage::DicomImage(DcmObject *object,
98
                       const E_TransferSyntax xfer,
99
                       const double slope,
100
                       const double intercept,
101
                       const unsigned long flags,
102
                       const unsigned long fstart,
103
                       const unsigned long fcount)
104
0
  : ImageStatus(EIS_Normal),
105
0
    PhotometricInterpretation(EPI_Unknown),
106
0
    Document(NULL),
107
0
    Image(NULL)
108
0
{
109
0
    if (checkDataDictionary())                  // valid 'dicom.dic' found ?
110
0
    {
111
0
        Document = new DiDocument(object, xfer, flags, fstart, fcount);
112
0
        if ((Document != NULL) && (Document->good()))
113
0
        {
114
0
            PhotometricInterpretation = EPI_Monochrome2;            // default for presentation states
115
0
            Image = new DiMono2Image(Document, ImageStatus, slope, intercept);
116
0
        }
117
0
    }
118
0
}
119
120
121
// --- create 'DicomImage' from valid 'DicomObject' with given modality LUT (specified by 'data' and 'descriptor')
122
123
DicomImage::DicomImage(DcmObject *object,
124
                       E_TransferSyntax xfer,
125
                       const DcmUnsignedShort &data,
126
                       const DcmUnsignedShort &descriptor,
127
                       const DcmLongString *explanation,
128
                       const unsigned long flags,
129
                       const unsigned long fstart,
130
                       const unsigned long fcount)
131
0
  : ImageStatus(EIS_Normal),
132
0
    PhotometricInterpretation(EPI_Unknown),
133
0
    Document(NULL),
134
0
    Image(NULL)
135
0
{
136
0
    if (checkDataDictionary())                  // valid 'dicom.dic' found ?
137
0
    {
138
0
        Document = new DiDocument(object, xfer, flags, fstart, fcount);
139
0
        if ((Document != NULL) && (Document->good()))
140
0
        {
141
0
            PhotometricInterpretation = EPI_Monochrome2;            // default for presentation states
142
0
            Image = new DiMono2Image(Document, ImageStatus, data, descriptor, explanation);
143
0
        }
144
0
    }
145
0
}
146
147
148
// --- protected: create 'DicomImage' from source with different image data and photometric interpretation
149
150
DicomImage::DicomImage(const DicomImage *dicom,
151
                       DiImage *image,
152
                       const EP_Interpretation interpret)
153
0
  : ImageStatus(dicom->ImageStatus),
154
0
    PhotometricInterpretation(dicom->PhotometricInterpretation),
155
0
    Document(dicom->Document),                   // necessary, ever used ??
156
0
    Image(image)
157
0
{
158
0
    if (interpret != EPI_Unknown)
159
0
        PhotometricInterpretation = interpret;
160
0
    if (Document != NULL)
161
0
        Document->addReference();               // 'Document' is only referenced not copied !
162
0
}
163
164
165
/*--------------*
166
 *  destructor  *
167
 *--------------*/
168
169
DicomImage::~DicomImage()
170
1.04k
{
171
1.04k
    delete Image;
172
1.04k
    if (Document != NULL)
173
0
        Document->removeReference();            // only delete if object is no longer referenced
174
1.04k
}
175
176
177
/*********************************************************************/
178
179
// --- initialize 'DicomImage' object (same procedure for every 'real' constructor)
180
181
void DicomImage::Init()
182
0
{
183
0
    if ((Document != NULL) && (Document->good()))
184
0
    {
185
0
        const char *str;
186
0
        if (hasSOPclassUID(UID_RETIRED_StandaloneOverlayStorage))
187
0
        {
188
0
            PhotometricInterpretation = EPI_Monochrome2;            // standalone overlays are handled like monochrome
189
0
            Image = new DiOverlayImage(Document, ImageStatus);      // images without pixel data
190
0
        }
191
0
        else if (Document->getFlags() & CIF_UsePresentationState)
192
0
        {
193
0
            PhotometricInterpretation = EPI_Monochrome2;            // default for presentation states
194
0
            Image = new DiMono2Image(Document, ImageStatus);
195
0
        }
196
0
        else if (strlen(str = Document->getPhotometricInterpretation()) > 0)
197
0
        {
198
0
            const SP_Interpretation *pin = PhotometricInterpretationNames;
199
0
            char *cstr = new char[strlen(str) + 1];
200
0
            if (cstr != NULL)
201
0
            {
202
0
                char *q = cstr;
203
0
                char c;
204
0
                for (const char *p = str; *p != 0; p++)    // remove invalid chars
205
0
                {
206
0
                    c = *p;
207
0
                    if (isalpha(c))
208
0
                        *(q++) = OFstatic_cast(char, toupper(c));
209
0
                    else if (isdigit(c))
210
0
                        *(q++) = c;
211
0
                }
212
0
                *q = '\0';                                          // end of C string
213
0
                DCMIMGLE_DEBUG("filtered version of 'PhotometricInterpretation' = " << OFSTRING_GUARD(cstr));
214
0
                while ((pin->Name != NULL) && (strcmp(pin->Name, cstr) != 0))
215
0
                    ++pin;
216
0
                delete[] cstr;
217
0
            } else {
218
0
                DCMIMGLE_WARN("can't create filtered version of 'PhotometricInterpretation' (" << str << ")");
219
0
                cstr = OFconst_cast(char *, str);                   // just reference the original string
220
0
                while ((pin->DefinedTerm != NULL) && (strcmp(pin->DefinedTerm, cstr) != 0))
221
0
                    ++pin;
222
0
            }
223
0
            PhotometricInterpretation = pin->Type;                  // unknown if last entry
224
0
            switch (PhotometricInterpretation)
225
0
            {
226
0
                case EPI_Monochrome1:                               // create 'Image' depending on color model
227
0
                    Image = new DiMono1Image(Document, ImageStatus);
228
0
                    break;
229
0
                case EPI_Monochrome2:
230
0
                    Image = new DiMono2Image(Document, ImageStatus);
231
0
                    break;
232
0
                default:                                            // unknown or unsupported color model
233
0
                    if (DiRegisterBase::Pointer != NULL)
234
0
                        Image = DiRegisterBase::Pointer->createImage(Document, ImageStatus, PhotometricInterpretation);
235
0
                    else
236
0
                        DCMIMGLE_DEBUG("Support for color images not registered, need to include \"dcmtk/dcmimage/diregist.h\"");
237
0
                    if (Image == NULL)
238
0
                    {
239
0
                        if (PhotometricInterpretation == EPI_Unknown)
240
0
                        {
241
0
                            ImageStatus = EIS_InvalidValue;
242
0
                            DCMIMGLE_ERROR("invalid value for 'PhotometricInterpretation' (" << str << ")");
243
0
                        } else {
244
0
                            ImageStatus = EIS_NotSupportedValue;
245
0
                            DCMIMGLE_ERROR("unsupported value for 'PhotometricInterpretation' (" << str << ")");
246
0
                        }
247
0
                    }
248
0
            }
249
0
        }
250
0
        else if (Document->getFlags() & CIF_AcrNemaCompatibility)   // ACR-NEMA has no 'photometric interpretation'
251
0
        {
252
0
            PhotometricInterpretation = EPI_Monochrome2;
253
0
            Image = new DiMono2Image(Document, ImageStatus);
254
0
        } else {
255
0
            ImageStatus = EIS_MissingAttribute;
256
0
            PhotometricInterpretation = EPI_Missing;
257
0
            if (Document->getPixelData() != NULL)
258
0
                DCMIMGLE_ERROR("mandatory attribute 'PhotometricInterpretation' is missing or can't be determined");
259
0
        }
260
0
    }
261
0
    else
262
0
        ImageStatus = EIS_InvalidDocument;
263
0
}
264
265
266
// --- check whether the loadable 'DataDictionary' is present/loaded
267
268
int DicomImage::checkDataDictionary()
269
1.04k
{
270
1.04k
    if (!dcmDataDict.isDictionaryLoaded())
271
1.04k
    {
272
1.04k
        ImageStatus = EIS_NoDataDictionary;
273
1.04k
        DCMIMGLE_ERROR("can't load data dictionary");
274
1.04k
    }
275
1.04k
    return ImageStatus == EIS_Normal;
276
1.04k
}
277
278
279
/*********************************************************************/
280
281
282
const char *DicomImage::getString(const EI_Status status)
283
0
{
284
0
    switch (status)
285
0
    {
286
0
      case EIS_Normal:
287
0
        return "Status OK";
288
0
      case EIS_NoDataDictionary:
289
0
        return "No data dictionary";
290
0
      case EIS_InvalidDocument:
291
0
        return "Invalid DICOM document";
292
0
      case EIS_MissingAttribute:
293
0
        return "Missing attribute";
294
0
      case EIS_InvalidValue:
295
0
        return "Invalid element value";
296
0
      case EIS_NotSupportedValue:
297
0
        return "Unsupported element value";
298
0
      case EIS_MemoryFailure:
299
0
        return "Out of memory";
300
0
      case EIS_InvalidImage:
301
0
        return "Invalid DICOM image";
302
0
      case EIS_OtherError:
303
0
      default:
304
0
        return "Unspecified";
305
0
    }
306
0
}
307
308
309
const char *DicomImage::getString(const EP_Interpretation interpret)
310
0
{
311
0
    const SP_Interpretation *pin = PhotometricInterpretationNames;
312
0
    while ((pin->DefinedTerm != NULL) && (pin->Type != interpret))
313
0
        ++pin;
314
0
    return pin->DefinedTerm;
315
0
}
316
317
318
// --- return unique 'SOPclassUID' string
319
320
const char *DicomImage::getSOPclassUID() const
321
0
{
322
0
    if (Document != NULL)
323
0
    {
324
0
        const char *str;
325
0
        if (Document->getValue(DCM_SOPClassUID, str))
326
0
            return str;
327
0
    }
328
0
    return NULL;
329
0
}
330
331
332
// --- return 'true' (1) if 'Document' has the same 'SOPclassUID' as given in parameter 'uid'
333
334
int DicomImage::hasSOPclassUID(const char *uid) const
335
0
{
336
0
    const char *str = getSOPclassUID();
337
0
    return (str != NULL) && (strcmp(str, uid) == 0);
338
0
}
339
340
341
// --- create new 'DicomImage' with 'fcount' frames starting with frame 'fstart'
342
343
DicomImage *DicomImage::createDicomImage(unsigned long fstart,
344
                                         unsigned long fcount) const
345
0
{
346
0
    if ((Image != NULL) && (fstart < getFrameCount()))
347
0
    {
348
0
        if ((fcount == 0) || (fstart + fcount > getFrameCount()))
349
0
            fcount = getFrameCount() - fstart;
350
0
        DiImage *image = Image->createImage(fstart, fcount);
351
0
        if (image != NULL)
352
0
        {
353
0
            DicomImage *dicom = new DicomImage(this, image);
354
0
            return dicom;
355
0
        }
356
0
    }
357
0
    return NULL;
358
0
}
359
360
361
// --- create scaled to given size ('width' and 'height') image, memory isn't handled internally !
362
// --- if one dimension is '0' the other is automatically adjusted (with respect to pixel aspect ratio)
363
364
DicomImage *DicomImage::createScaledImage(const unsigned long width,
365
                                          const unsigned long height,
366
                                          const int interpolate,
367
                                          int aspect) const
368
0
{
369
0
    return createScaledImage(0, 0, getWidth(), getHeight(), width, height, interpolate, aspect);
370
0
}
371
372
373
// --- create scaled with given factors ('xfactor' and 'yfactor') image, memory isn't handled internally !
374
375
DicomImage *DicomImage::createScaledImage(const double xfactor,
376
                                          const double yfactor,
377
                                          const int interpolate,
378
                                          const int aspect) const
379
0
{
380
0
    return createScaledImage(0, 0, getWidth(), getHeight(), OFstatic_cast(unsigned long, xfactor * getWidth()),
381
0
        OFstatic_cast(unsigned long, yfactor * getHeight()), interpolate, aspect);
382
0
}
383
384
385
// --- clip & scale
386
387
DicomImage *DicomImage::createScaledImage(const signed long left_pos,
388
                                          const signed long top_pos,
389
                                          unsigned long clip_width,
390
                                          unsigned long clip_height,
391
                                          unsigned long scale_width,
392
                                          unsigned long scale_height,
393
                                          const int interpolate,
394
                                          int aspect,
395
                                          const Uint16 pvalue) const
396
0
{
397
0
    const unsigned long gw = getWidth();
398
0
    const unsigned long gh = getHeight();
399
0
    if ((Image != NULL) && (gw > 0) && (gh > 0))
400
0
    {
401
0
        if ((clip_width == 0) && (left_pos < OFstatic_cast(signed long, gw)))  // set 'width' if parameter is missing
402
0
            clip_width = gw - left_pos;
403
0
        if ((clip_height == 0) && (top_pos < OFstatic_cast(signed long, gh)))  // same for 'height'
404
0
            clip_height = gh - top_pos;
405
0
        if ((scale_width == 0) && (scale_height == 0))
406
0
        {
407
0
            scale_width = clip_width;                                // auto-set width/height
408
0
            scale_height = clip_height;
409
0
        }
410
0
        else if ((clip_width > 0) && (clip_height > 0))
411
0
        {
412
0
            if (aspect)                                              // maintain pixel aspect ratio
413
0
            {
414
0
                if (scale_width == 0)
415
0
                    scale_width = OFstatic_cast(unsigned long, getWidthHeightRatio() * OFstatic_cast(double, scale_height * clip_width) / clip_height);
416
0
                else if (scale_height == 0)
417
0
                    scale_height = OFstatic_cast(unsigned long, getHeightWidthRatio() * OFstatic_cast(double, scale_width * clip_height) / clip_width);
418
0
                else
419
0
                    aspect = 0;                                      // ignore pixel aspect ratio
420
0
            }
421
0
            else                                                     // ignore pixel aspect ratio
422
0
            {
423
0
                if (scale_width == 0)
424
0
                    scale_width = OFstatic_cast(unsigned long, OFstatic_cast(double, scale_height * clip_width) / clip_height);
425
0
                else if (scale_height == 0)
426
0
                    scale_height = OFstatic_cast(unsigned long, OFstatic_cast(double, scale_width * clip_height) / clip_width);
427
0
            }
428
0
        }
429
0
        const unsigned long maxvalue = DicomImageClass::maxval(bitsof(Uint16));
430
0
        if (scale_width > maxvalue)
431
0
            scale_width = maxvalue;                                  // limit 'width' to maximum value (65535)
432
0
        if (scale_height > maxvalue)
433
0
            scale_height = maxvalue;                                 // same for 'height'
434
435
        /* need to limit clipping region ... !? */
436
437
0
        if (((left_pos < 0) || (OFstatic_cast(unsigned long, left_pos + clip_width) > gw) ||
438
0
            (top_pos < 0) || (OFstatic_cast(unsigned long, top_pos + clip_height) > gh)) &&
439
0
            ((clip_width != scale_width) || (clip_height != scale_height)))
440
0
        {
441
0
            DCMIMGLE_ERROR("combined clipping & scaling outside image boundaries not yet supported");
442
0
        }
443
0
        else if ((scale_width > 0) && (scale_height > 0))
444
0
        {
445
0
            DiImage *image = Image->createScale(left_pos, top_pos, clip_width, clip_height, scale_width, scale_height,
446
0
                interpolate, aspect, pvalue);
447
0
            if (image != NULL)
448
0
            {
449
0
                DicomImage *dicom = new DicomImage(this, image);
450
0
                return dicom;
451
0
            }
452
0
        }
453
0
    }
454
0
    return NULL;
455
0
}
456
457
458
// --- clip & scale
459
460
DicomImage *DicomImage::createScaledImage(const signed long left_pos,
461
                                          const signed long top_pos,
462
                                          unsigned long width,
463
                                          unsigned long height,
464
                                          const double xfactor,
465
                                          const double yfactor,
466
                                          const int interpolate,
467
                                          const int aspect,
468
                                          const Uint16 pvalue) const
469
0
{
470
0
    if ((xfactor >= 0) && (yfactor >= 0))
471
0
    {
472
0
        const unsigned long gw = getWidth();
473
0
        const unsigned long gh = getHeight();
474
0
        if ((width == 0) && (left_pos < OFstatic_cast(signed long, gw)))  // set 'width' if parameter is missing (0)
475
0
            width = gw - left_pos;
476
0
        if ((height == 0) && (top_pos < OFstatic_cast(signed long, gh)))  // same for 'height'
477
0
            height = gh - top_pos;
478
0
        return createScaledImage(left_pos, top_pos, width, height, OFstatic_cast(unsigned long, xfactor * width),
479
0
            OFstatic_cast(unsigned long, yfactor * height), interpolate, aspect, pvalue);
480
0
    }
481
0
    return NULL;
482
0
}
483
484
485
// --- create clipped to given box ('left_pos', 'top_pos' and 'width', 'height') image,
486
// ---- memory isn't handled internally! 'width' and 'height' are optional
487
488
DicomImage *DicomImage::createClippedImage(const signed long left_pos,
489
                                           const signed long top_pos,
490
                                           unsigned long width,
491
                                           unsigned long height,
492
                                           const Uint16 pvalue) const
493
0
{
494
0
    return createScaledImage(left_pos, top_pos, width, height, OFstatic_cast(unsigned long, 0),
495
0
        OFstatic_cast(unsigned long, 0), 0, 0, pvalue);
496
0
}
497
498
499
// --- flip image (horizontal: x > 1 and/or vertical y > 1)
500
501
int DicomImage::flipImage(int horz,
502
                          int vert) const
503
0
{
504
0
    if ((Image != NULL) && (horz || vert))
505
0
    {
506
0
        if (getWidth() <= 1)
507
0
            horz = 0;
508
0
        if (getHeight() <= 1)
509
0
            vert = 0;
510
0
        if (horz || vert)
511
0
            return Image->flip(horz, vert);
512
0
        else
513
0
            return 2;
514
0
    }
515
0
    return 0;
516
0
}
517
518
519
// --- create flipped image (horizontal: x > 1 and/or vertical y > 1), memory isn't handled internally !
520
521
DicomImage *DicomImage::createFlippedImage(int horz,
522
                                           int vert) const
523
0
{
524
0
    if ((Image != NULL) && (horz || vert))
525
0
    {
526
0
        if (getWidth() <= 1)                                        // can't flip horizontally
527
0
            horz = 0;
528
0
        if (getHeight() <= 1)                                       // can't flip vertically
529
0
            vert = 0;
530
0
        DiImage *image;
531
0
        if (horz || vert)                                           // flip at least one axis
532
0
            image = Image->createFlip(horz, vert);
533
0
        else                                                        // copy image
534
0
            image = Image->createImage(0, getFrameCount());
535
0
        if (image != NULL)
536
0
        {
537
0
            DicomImage *dicom = new DicomImage(this, image);
538
0
            return dicom;
539
0
        }
540
0
    }
541
0
    return NULL;
542
0
}
543
544
545
// -- normalize given 'degree' value to 0, 90, 180, 270
546
547
int DicomImage::normalizeDegreeValue(signed int &degree) const
548
0
{
549
0
    switch (degree)
550
0
    {
551
0
        case 0:
552
0
        case 360:
553
0
        case -360:
554
0
            degree = 0;
555
0
            return 1;
556
0
        case 90:
557
0
        case -270:
558
0
            degree = 90;
559
0
            return 1;
560
0
        case 180:
561
0
        case -180:
562
0
            degree = 180;
563
0
            return 1;
564
0
        case 270:
565
0
        case -90:
566
0
            degree = 270;
567
0
            return 1;
568
0
        default:
569
0
            return 0;
570
0
    }
571
0
}
572
573
574
// --- rotate image by given 'degree'
575
576
int DicomImage::rotateImage(signed int degree) const
577
0
{
578
0
    if ((Image != NULL) && normalizeDegreeValue(degree))
579
0
    {
580
0
        if ((degree == 0) || (getWidth() * getHeight() <= 1))       // nothing to do
581
0
            return 2;
582
0
        else
583
0
            return Image->rotate(OFstatic_cast(int, degree));
584
0
    }
585
0
    return 0;
586
0
}
587
588
589
// --- create by given 'degree' rotated image, memory isn't handled internally !
590
591
DicomImage *DicomImage::createRotatedImage(signed int degree) const
592
0
{
593
0
    if ((Image != NULL) && normalizeDegreeValue(degree))
594
0
    {
595
0
        DiImage *image = Image->createRotate(OFstatic_cast(int, degree));
596
0
        if (image != NULL)
597
0
        {
598
0
            DicomImage *dicom = new DicomImage(this, image);
599
0
            return dicom;
600
0
        }
601
0
    }
602
0
    return NULL;
603
0
}
604
605
606
// --- create color-image to mono-image with given 'red', 'green' and 'blue' coefficients converted image, memory ... !
607
608
DicomImage *DicomImage::createMonochromeImage(const double red,
609
                                              const double green,
610
                                              const double blue) const
611
0
{
612
0
    if (Image != NULL)
613
0
    {
614
0
        DiImage *image = Image->createMono(red, green, blue);       // create monochrome image data
615
0
        if (image != NULL)
616
0
        {
617
0
            DicomImage *dicom = new DicomImage(this, image, EPI_Monochrome2);
618
0
            return dicom;
619
0
        }
620
0
    }
621
0
    return NULL;
622
0
}
623
624
625
// --- create monochrome output image of specified frame (incl. windowing)
626
627
DicomImage *DicomImage::createMonoOutputImage(const unsigned long frame,
628
                                              const int bits)
629
0
{
630
0
    if ((Image != NULL) && (Image->getMonoImagePtr() != NULL))
631
0
    {
632
0
        DiImage *image = Image->getMonoImagePtr()->createOutputImage(frame, bits);
633
0
        if (image != NULL)
634
0
        {
635
0
            DicomImage *dicom = new DicomImage(this, image, EPI_Monochrome2);
636
0
            return dicom;
637
0
        }
638
0
    }
639
0
    return NULL;
640
0
}
641
642
643
/*********************************************************************/
644
645
646
// --- write 'frame' of image data to 'filename' with 'bits' depth
647
648
int DicomImage::writePPM(const char *filename,
649
                         const int bits,
650
                         const unsigned long frame)
651
0
{
652
0
    if ((filename != NULL) && (Image != NULL))
653
0
    {
654
0
        char fname[FILENAME_MAX + 1];
655
0
        if (OFStandard::snprintf(fname, sizeof(fname), filename, frame) >= 0)           // replace '%d' etc. with frame number
656
0
            filename = fname;
657
0
        FILE *stream = fopen(filename, "w");                // open text file for writing
658
0
        int ok = writePPM(stream, bits, frame);
659
0
        if (fclose(stream)) ok = 0;
660
0
        return ok;
661
0
    }
662
0
    return 0;
663
0
}
664
665
666
// --- same for C++ 'ostream'
667
668
int DicomImage::writePPM(STD_NAMESPACE ostream& stream,
669
                         const int bits,
670
                         const unsigned long frame)
671
0
{
672
0
    if ((stream.good()) && (Image != NULL))
673
0
        return Image->writePPM(stream, frame, Image->getBits(bits));
674
0
    return 0;
675
0
}
676
677
678
// --- same for C 'FILE'
679
680
int DicomImage::writePPM(FILE *stream,
681
                         const int bits,
682
                         const unsigned long frame)
683
0
{
684
0
    if ((stream != NULL) && (Image != NULL))
685
0
        return Image->writePPM(stream, frame, Image->getBits(bits));
686
0
    return 0;
687
0
}
688
689
690
// --- same for RAW PPM (binary form of PPM with a maximum of 8 bits depth)
691
692
int DicomImage::writeRawPPM(const char *filename,
693
                            const int bits,
694
                            const unsigned long frame)
695
0
{
696
0
    if ((filename != NULL) && (Image != NULL) && (Image->getBits(bits) <= MAX_RAWPPM_BITS))
697
0
    {
698
0
        char fname[FILENAME_MAX + 1];
699
0
        if (OFStandard::snprintf(fname, sizeof(fname), filename, frame) >= 0)           // replace '%d' etc. with frame number
700
0
            filename = fname;
701
0
        FILE *stream = fopen(filename, "wb");               // open binary file for writing
702
0
        if (stream != NULL)
703
0
        {
704
0
            int ok = Image->writeRawPPM(stream, frame, Image->getBits(bits));
705
0
            if (fclose(stream)) ok = 0;
706
0
            return ok;
707
0
        }
708
0
    }
709
0
    return 0;
710
0
}
711
712
// --- same for C 'FILE'
713
714
int DicomImage::writeRawPPM(FILE *stream,
715
                            const int bits,
716
                            const unsigned long frame)
717
0
{
718
0
    if ((stream != NULL) && (Image != NULL))
719
0
        return Image->writeRawPPM(stream, frame, Image->getBits(bits));
720
0
    return 0;
721
0
}
722
723
724
// --- write 'frame' of image data to 'filename' with 'bits' depth in BMP format
725
726
int DicomImage::writeBMP(const char *filename,
727
                         const int bits,
728
                         const unsigned long frame)
729
0
{
730
0
    if ((filename != NULL) && (Image != NULL) &&
731
0
        ((bits == 0) || ((bits == 8) && isMonochrome()) || (bits == 24) || (bits == 32)))
732
0
    {
733
0
        char fname[FILENAME_MAX + 1];
734
0
        if (OFStandard::snprintf(fname, sizeof(fname), filename, frame) >= 0)           // replace '%d' etc. with frame number
735
0
            filename = fname;
736
0
        FILE *stream = fopen(filename, "wb");               // open binary file for writing
737
0
        if (stream != NULL)
738
0
        {
739
0
            int ok = Image->writeBMP(stream, frame, bits);
740
0
            if (fclose(stream)) ok = 0;
741
0
            return ok;
742
0
        }
743
0
    }
744
0
    return 0;
745
0
}
746
747
748
// --- same for open C 'FILE' in BMP format
749
750
int DicomImage::writeBMP(FILE *stream,
751
                         const int bits,
752
                         const unsigned long frame)
753
0
{
754
0
    if ((stream != NULL) && (Image != NULL) &&
755
0
        ((bits == 0) || ((bits == 8) && isMonochrome()) || (bits == 24) || (bits == 32)))
756
0
    {
757
0
        return Image->writeBMP(stream, frame, bits);
758
0
    }
759
0
    return 0;
760
0
}
761
762
763
// --- write 'frame' of image data to 'filename' pluggable image format
764
765
int DicomImage::writePluginFormat(const DiPluginFormat *plugin,
766
                                  const char *filename,
767
                                  const unsigned long frame)
768
0
{
769
0
    if ((plugin != NULL) && (filename != NULL) && (Image != NULL))
770
0
    {
771
0
        char fname[FILENAME_MAX + 1];
772
0
        if (OFStandard::snprintf(fname, sizeof(fname), filename, frame) >= 0)           // replace '%d' etc. with frame number
773
0
            filename = fname;
774
0
        FILE *stream = fopen(filename, "wb");               // open binary file for writing
775
0
        if (stream != NULL)
776
0
        {
777
0
            int ok = plugin->write(Image, stream, frame);
778
0
            if (fclose(stream)) ok = 0;
779
0
            return ok;
780
0
        }
781
0
    }
782
0
    return 0;
783
0
}
784
785
786
// --- same for open C 'FILE' in pluggable image format
787
788
int DicomImage::writePluginFormat(const DiPluginFormat *plugin,
789
                                  FILE *stream,
790
                                  const unsigned long frame)
791
0
{
792
0
    if ((plugin != NULL) && (stream != NULL) && (Image != NULL))
793
0
        return plugin->write(Image, stream, frame);
794
0
    return 0;
795
0
}