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