1 /*
2  *
3  *  Copyright (C) 2003-2019, 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:  dcmimage
15  *
16  *  Author:  Alexander Haderer
17  *
18  *  Purpose: Implements PNG interface for plugable image formats
19  *
20  */
21 
22 
23 #include "dcmtk/config/osconfig.h"
24 
25 #ifdef WITH_LIBPNG
26 
27 #include "dcmtk/dcmdata/dctypes.h"
28 #include "dcmtk/dcmimgle/diimage.h"
29 #include "dcmtk/dcmimage/dipipng.h"
30 #include "dcmtk/dcmdata/dcuid.h"      /* for dcmtk version */
31 
32 BEGIN_EXTERN_C
33 #ifdef HAVE_LIBPNG_PNG_H
34 #include <libpng/png.h>
35 #else
36 #include <png.h>
37 #endif
38 END_EXTERN_C
39 
40 
DiPNGPlugin()41 DiPNGPlugin::DiPNGPlugin()
42 : DiPluginFormat()
43 , interlaceType(E_pngInterlaceAdam7)
44 , metainfoType(E_pngFileMetainfo)
45 , bitsPerSample(8)
46 {
47 }
48 
49 
~DiPNGPlugin()50 DiPNGPlugin::~DiPNGPlugin()
51 {
52 }
53 
54 
write(DiImage * image,FILE * stream,const unsigned long frame) const55 int DiPNGPlugin::write(
56   DiImage *image,
57   FILE *stream,
58   const unsigned long frame) const
59 {
60   volatile int result = 0;  // gcc -W requires volatile here because of longjmp
61   if ((image != NULL) && (stream != NULL))
62   {
63     /* create bitmap with 8 or 16 bits per sample */
64     const int bit_depth = bitsPerSample;
65     const void *data = image->getOutputData(frame, bit_depth /*bits*/, 0 /*planar*/);
66     if (data != NULL)
67     {
68       png_struct *png_ptr = NULL;
69       png_info *info_ptr = NULL;
70       png_byte *pix_ptr = NULL;
71 
72       png_byte ** volatile row_ptr = NULL;
73       volatile png_textp  text_ptr = NULL;
74       png_time ptime;
75 
76       const int width  = image->getColumns();
77       const int height = image->getRows();
78       int color_type;
79       int bpp;            // bytesperpixel
80 
81       int row;
82 
83       // create png write struct
84       png_ptr = png_create_write_struct( PNG_LIBPNG_VER_STRING, NULL, NULL, NULL );
85       if( png_ptr == NULL ) {
86         return 0;
87       }
88 
89       // create png info struct
90       info_ptr = png_create_info_struct( png_ptr );
91       if( info_ptr == NULL ) {
92         png_destroy_write_struct( &png_ptr, NULL );
93         return 0;
94       }
95 
96       // setjmp stuff for png lib
97       if( setjmp(png_jmpbuf(png_ptr) ) ) {
98         png_destroy_write_struct( &png_ptr, NULL );
99         if( row_ptr )  delete[] row_ptr;
100         if( text_ptr ) delete[] text_ptr;
101         return 0;
102       }
103 
104       if( (image->getInternalColorModel() == EPI_Monochrome1) ||
105           (image->getInternalColorModel() == EPI_Monochrome2) )
106       {
107         color_type = PNG_COLOR_TYPE_GRAY;
108         bpp = bit_depth / 8;
109       } else {
110         color_type = PNG_COLOR_TYPE_RGB;
111         bpp = 3 * bit_depth / 8;
112       }
113 
114       int opt_interlace = 0;
115       switch (interlaceType) {
116         case E_pngInterlaceAdam7:
117           opt_interlace = PNG_INTERLACE_ADAM7;
118           break;
119         case E_pngInterlaceNone:
120           opt_interlace = PNG_INTERLACE_NONE;
121           break;
122       }
123 
124       // init png io structure
125       png_init_io( png_ptr, stream );
126 
127       // set write mode
128       png_set_IHDR( png_ptr, info_ptr, width, height, bit_depth, color_type,
129                     opt_interlace, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
130 
131       // set text & time
132       if( metainfoType == E_pngFileMetainfo ) {
133         text_ptr = new png_text[3];
134         if( text_ptr == NULL ) {
135           png_destroy_write_struct( &png_ptr, NULL );
136           return result;
137         }
138         text_ptr[0].key         = OFconst_cast(char *, "Title");
139         text_ptr[0].text        = OFconst_cast(char *, "Converted DICOM Image");
140         text_ptr[0].compression = PNG_TEXT_COMPRESSION_NONE;
141         text_ptr[1].key         = OFconst_cast(char *, "Software");
142         text_ptr[1].text        = OFconst_cast(char *, "OFFIS DCMTK");
143         text_ptr[1].compression = PNG_TEXT_COMPRESSION_NONE;
144         text_ptr[2].key         = OFconst_cast(char *, "Version");
145         text_ptr[2].text        = OFconst_cast(char *, OFFIS_DCMTK_VERSION);
146         text_ptr[2].compression = PNG_TEXT_COMPRESSION_NONE;
147 #ifdef PNG_iTXt_SUPPORTED
148         text_ptr[0].lang = NULL;
149         text_ptr[1].lang = NULL;
150         text_ptr[2].lang = NULL;
151 #endif
152         png_set_text( png_ptr, info_ptr, text_ptr, 3 );
153 
154         png_convert_from_time_t( &ptime, time(NULL) );
155         png_set_tIME( png_ptr, info_ptr, &ptime );
156       }
157 
158       // write header
159       png_write_info( png_ptr, info_ptr );
160       row_ptr = new png_bytep[height];
161       if( row_ptr == NULL ) {
162         png_destroy_write_struct( &png_ptr, NULL );
163         if( text_ptr ) delete[] text_ptr;
164         return result;
165       }
166       for( row=0, pix_ptr=OFstatic_cast(png_byte*, OFconst_cast(void*, data));
167         row<height;
168         row++, pix_ptr+=width*bpp )
169       {
170         row_ptr[row] = pix_ptr;
171       }
172 
173       // swap bytes (if needed)
174       if ( (bit_depth == 16) && (gLocalByteOrder != EBO_BigEndian) )
175         png_set_swap( png_ptr );
176 
177       // write image
178       png_write_image( png_ptr, row_ptr );
179 
180       // write additional chunks
181       png_write_end( png_ptr, info_ptr );
182 
183       // finish
184       png_destroy_write_struct( &png_ptr, &info_ptr );
185       delete[] row_ptr;
186       if( text_ptr ) delete[] text_ptr;
187       result = 1;
188     }
189   }
190 
191   return result;
192 }
193 
194 
setInterlaceType(DiPNGInterlace itype)195 void DiPNGPlugin::setInterlaceType(DiPNGInterlace itype)
196 {
197   interlaceType = itype;
198 }
199 
200 
setMetainfoType(DiPNGMetainfo minfo)201 void DiPNGPlugin::setMetainfoType(DiPNGMetainfo minfo)
202 {
203   metainfoType = minfo;
204 }
205 
206 
setBitsPerSample(const int bpp)207 void DiPNGPlugin::setBitsPerSample(const int bpp)
208 {
209   if( (bpp == 8) || (bpp == 16) )
210     bitsPerSample = bpp;
211 }
212 
213 
getLibraryVersionString()214 OFString DiPNGPlugin::getLibraryVersionString()
215 {
216   OFString versionStr = "LIBPNG, Version ";
217   char cver[10];
218   png_uint_32 ver = png_access_version_number();
219   if( ver < 999999 ) {
220     sprintf( cver, "%li.%li.%li",
221                    OFstatic_cast(long int, (ver/10000)%100),
222                    OFstatic_cast(long int, (ver/100)%100),
223                    OFstatic_cast(long int, ver%100) );
224   }else{
225     sprintf( cver, "unknown" );
226   }
227   versionStr.append( cver );
228   return versionStr;
229 }
230 
231 #else /* WITH_LIBPNG */
232 
233 int dipipng_cc_dummy_to_keep_linker_from_moaning = 0;
234 
235 #endif
236