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