1 /* GNUPLOT - breaders.c */
2 
3 /*[
4  * Copyright 2004  Petr Mikulik
5  *
6  * As part of the program Gnuplot, which is
7  *
8  * Copyright 1986 - 1993, 1998, 2004   Thomas Williams, Colin Kelley
9  *
10  * Permission to use, copy, and distribute this software and its
11  * documentation for any purpose with or without fee is hereby granted,
12  * provided that the above copyright notice appear in all copies and
13  * that both that copyright notice and this permission notice appear
14  * in supporting documentation.
15  *
16  * Permission to modify the software is granted, but not the right to
17  * distribute the complete modified source code.  Modifications are to
18  * be distributed as patches to the released version.  Permission to
19  * distribute binaries produced by compiling modified sources is granted,
20  * provided you
21  *   1. distribute the corresponding source modifications from the
22  *    released version in the form of a patch file along with the binaries,
23  *   2. add special version identification to distinguish your version
24  *    in addition to the base release version number,
25  *   3. provide your name and address as the primary contact for the
26  *    support of your modified version, and
27  *   4. retain our contact information in regard to use of the base
28  *    software.
29  * Permission to distribute the released version of the source code along
30  * with corresponding source modifications in the form of a patch file is
31  * granted with same provisions 2 through 4 for binary distributions.
32  *
33  * This software is provided "as is" without express or implied warranty
34  * to the extent permitted by applicable law.
35 ]*/
36 
37 /* AUTHOR : Petr Mikulik */
38 
39 /*
40  * Readers to set up binary data file information for particular formats.
41  */
42 
43 #include "breaders.h"
44 #include "datafile.h"
45 #include "alloc.h"
46 #include "misc.h"
47 
48 /*
49  * Reader for the ESRF Header File format files (EDF / EHF).
50  */
51 
52 /* Inside datafile.c, but kept hidden. */
53 extern int df_no_bin_cols;	/* cols to read */
54 extern df_endianess_type df_bin_file_endianess;
55 extern TBOOLEAN df_matrix_file, df_binary_file;
56 extern void *df_pixeldata;
57 
58 /* Reader for the ESRF Header File format files (EDF / EHF).
59  */
60 
61 /* gen_table4 */
62 struct gen_table4 {
63     const char *key;
64     int value;
65     short signum; /* 0..unsigned, 1..signed, 2..float or double */
66     short sajzof; /* sizeof on 32bit architecture */
67 };
68 
69 /* Exactly like lookup_table_nth from tables.c, but for gen_table4 instead
70  * of gen_table.
71  */
72 static int
lookup_table4_nth(const struct gen_table4 * tbl,const char * search_str)73 lookup_table4_nth(const struct gen_table4 *tbl, const char *search_str)
74 {
75     int k = -1;
76     while (tbl[++k].key)
77 	if (tbl[k].key && !strncmp(search_str, tbl[k].key, strlen(tbl[k].key)))
78 	    return k;
79     return -1; /* not found */
80 }
81 
82 static const struct gen_table4 edf_datatype_table[] =
83 {
84     { "UnsignedByte",	DF_UCHAR,   0, 1 },
85     { "SignedByte",	DF_CHAR,    1, 1 },
86     { "UnsignedShort",	DF_USHORT,  0, 2 },
87     { "SignedShort",	DF_SHORT,   1, 2 },
88     { "UnsignedInteger",DF_UINT,    0, 4 },
89     { "SignedInteger",	DF_INT,	    1, 4 },
90     { "UnsignedLong",	DF_ULONG,   0, 8 },
91     { "SignedLong",	DF_LONG,    1, 8 },
92     { "FloatValue",	DF_FLOAT,   2, 4 },
93     { "DoubleValue",	DF_DOUBLE,  2, 8 },
94     { "Float",		DF_FLOAT,   2, 4 }, /* Float and FloatValue are synonyms */
95     { "Double",		DF_DOUBLE,  2, 8 }, /* Double and DoubleValue are synonyms */
96     { NULL, -1, -1, -1 }
97 };
98 
99 static const struct gen_table edf_byteorder_table[] =
100 {
101     { "LowByteFirst",	DF_LITTLE_ENDIAN }, /* little endian */
102     { "HighByteFirst",	DF_BIG_ENDIAN },    /* big endian */
103     { NULL, -1 }
104 };
105 
106 /* Orientation of axes of the raster, as the binary matrix is saved in
107  * the file.
108  */
109 enum EdfRasterAxes {
110     EDF_RASTER_AXES_XrightYdown,	/* matricial format: rows, columns */
111     EDF_RASTER_AXES_XrightYup		/* cartesian coordinate system */
112     /* other 6 combinations not available (not needed until now) */
113 };
114 
115 static const struct gen_table edf_rasteraxes_table[] =
116 {
117     { "XrightYdown",	EDF_RASTER_AXES_XrightYdown },
118     { "XrightYup",	EDF_RASTER_AXES_XrightYup },
119     { NULL, -1 }
120 };
121 
122 
123 /* Find value_ptr as pointer to the parameter of the given key in the header.
124  * Returns NULL on success.
125  */
126 static char*
edf_findInHeader(const char * header,const char * key)127 edf_findInHeader ( const char* header, const char* key )
128 {
129     char *value_ptr = strstr( header, key );
130 
131     if (!value_ptr)
132 	return NULL;
133     /* an edf line is "key     = value ;" */
134     value_ptr = 1 + strchr(value_ptr + strlen(key), '=');
135     while (isspace((unsigned char)*value_ptr))
136 	value_ptr++;
137     return value_ptr;
138 }
139 
140 void
edf_filetype_function(void)141 edf_filetype_function(void)
142 {
143     FILE *fp;
144     char *header = NULL;
145     int header_size = 0;
146     char *p;
147     int k;
148     /* open (header) file */
149     fp = loadpath_fopen(df_filename, "rb");
150     if (!fp)
151 	os_error(NO_CARET, "Can't open data file \"%s\"", df_filename);
152     /* read header: it is a multiple of 512 B ending by "}\n" */
153     while (header_size == 0 || strncmp(&header[header_size-2],"}\n",2)) {
154 	int header_size_prev = header_size;
155 	header_size += 512;
156 	if (!header)
157 	    header = gp_alloc(header_size+1, "EDF header");
158 	else
159 	    header = gp_realloc(header, header_size+1, "EDF header");
160 	header[header_size_prev] = 0; /* protection against empty file */
161 	k = fread(header+header_size_prev, 512, 1, fp);
162 	if (k == 0) { /* protection against indefinite loop */
163 	    free(header);
164 	    os_error(NO_CARET, "Damaged EDF header of %s: not multiple of 512 B.\n", df_filename);
165 	}
166 	header[header_size] = 0; /* end of string: protection against strstr later on */
167     }
168     fclose(fp);
169     /* make sure there is a binary record structure for each image */
170     if (df_num_bin_records < 1)
171 	df_add_binary_records(1-df_num_bin_records, DF_CURRENT_RECORDS); /* otherwise put here: number of images (records) from this file */
172     if ((p = edf_findInHeader(header, "EDF_BinaryFileName"))) {
173 	int plen = strcspn(p, " ;\n");
174 	df_filename = gp_realloc(df_filename, plen+1, "datafile name");
175 	strncpy(df_filename, p, plen);
176 	df_filename[plen] = '\0';
177 	if ((p = edf_findInHeader(header, "EDF_BinaryFilePosition")))
178 	    df_bin_record[0].scan_skip[0] = atoi(p);
179 	else
180 	    df_bin_record[0].scan_skip[0] = 0;
181     } else
182 	df_bin_record[0].scan_skip[0] = header_size; /* skip header */
183     /* set default values */
184     df_bin_record[0].scan_dir[0] = 1;
185     df_bin_record[0].scan_dir[1] = -1;
186     df_bin_record[0].scan_generate_coord = TRUE;
187     df_bin_record[0].cart_scan[0] = DF_SCAN_POINT;
188     df_bin_record[0].cart_scan[1] = DF_SCAN_LINE;
189     df_extend_binary_columns(1);
190     df_set_skip_before(1,0);
191     df_set_skip_after(1,0);
192     df_no_use_specs = 1;
193     use_spec[0].column = 1;
194     /* now parse the header */
195     if ((p = edf_findInHeader(header, "Dim_1")))
196 	df_bin_record[0].scan_dim[0] = atoi(p);
197     if ((p = edf_findInHeader(header, "Dim_2")))
198 	df_bin_record[0].scan_dim[1] = atoi(p);
199     if ((p = edf_findInHeader(header, "DataType"))) {
200 	k = lookup_table4_nth(edf_datatype_table, p);
201 	if (k >= 0) { /* known EDF DataType */
202 	    int s = edf_datatype_table[k].sajzof;
203 	    switch (edf_datatype_table[k].signum) {
204 		case 0: df_set_read_type(1,SIGNED_TEST(s)); break;
205 		case 1: df_set_read_type(1,UNSIGNED_TEST(s)); break;
206 		case 2: df_set_read_type(1,FLOAT_TEST(s)); break;
207 	    }
208 	}
209     }
210     if ((p = edf_findInHeader(header, "ByteOrder"))) {
211 	k = lookup_table_nth(edf_byteorder_table, p);
212 	if (k >= 0)
213 	    df_bin_file_endianess = edf_byteorder_table[k].value;
214     }
215     /* Origin vs center: EDF specs allows only Center, but it does not hurt if
216        Origin is supported as well; however, Center rules if both specified.
217     */
218     if ((p = edf_findInHeader(header, "Origin_1"))) {
219 	df_bin_record[0].scan_cen_or_ori[0] = atof(p);
220 	df_bin_record[0].scan_trans = DF_TRANSLATE_VIA_ORIGIN;
221     }
222     if ((p = edf_findInHeader(header, "Origin_2"))) {
223 	df_bin_record[0].scan_cen_or_ori[1] = atof(p);
224 	df_bin_record[0].scan_trans = DF_TRANSLATE_VIA_ORIGIN;
225     }
226     if ((p = edf_findInHeader(header, "Center_1"))) {
227 	df_bin_record[0].scan_cen_or_ori[0] = atof(p);
228 	df_bin_record[0].scan_trans = DF_TRANSLATE_VIA_CENTER;
229     }
230     if ((p = edf_findInHeader(header, "Center_2"))) {
231 	df_bin_record[0].scan_cen_or_ori[1] = atof(p);
232 	df_bin_record[0].scan_trans = DF_TRANSLATE_VIA_CENTER;
233     }
234     /* now pixel sizes and raster orientation */
235     if ((p = edf_findInHeader(header, "PSize_1")))
236 	df_bin_record[0].scan_delta[0] = atof(p);
237     if ((p = edf_findInHeader(header, "PSize_2")))
238 	df_bin_record[0].scan_delta[1] = atof(p);
239     if ((p = edf_findInHeader(header, "RasterAxes"))) {
240 	k = lookup_table_nth(edf_rasteraxes_table, p);
241 	switch (k) {
242 	    case EDF_RASTER_AXES_XrightYup:
243 		df_bin_record[0].scan_dir[0] = 1;
244 		df_bin_record[0].scan_dir[1] = 1;
245 		df_bin_record[0].cart_scan[0] = DF_SCAN_POINT;
246 		df_bin_record[0].cart_scan[1] = DF_SCAN_LINE;
247 		break;
248 	    default: /* also EDF_RASTER_AXES_XrightYdown */
249 		df_bin_record[0].scan_dir[0] = 1;
250 		df_bin_record[0].scan_dir[1] = -1;
251 		df_bin_record[0].cart_scan[0] = DF_SCAN_POINT;
252 		df_bin_record[0].cart_scan[1] = DF_SCAN_LINE;
253 	}
254     }
255 
256     free(header);
257 
258 }
259 
260 /*
261  *	Use libgd for input of binary images in PNG GIF JPEG formats
262  *	Ethan A Merritt - August 2009
263  */
264 
265 #define GD_PNG 1
266 #define GD_GIF 2
267 #define GD_JPEG 3
268 void gd_filetype_function(int filetype);
269 
270 void
png_filetype_function(void)271 png_filetype_function(void)
272 {
273     gd_filetype_function(GD_PNG);
274 }
275 
276 void
gif_filetype_function(void)277 gif_filetype_function(void)
278 {
279     gd_filetype_function(GD_GIF);
280 }
281 
282 void
jpeg_filetype_function(void)283 jpeg_filetype_function(void)
284 {
285     gd_filetype_function(GD_JPEG);
286 }
287 
288 #ifndef HAVE_GD_PNG
289 
290 void
gd_filetype_function(int type)291 gd_filetype_function(int type)
292 {
293     int_error(NO_CARET, "This copy of gnuplot cannot read png/gif/jpeg images");
294 }
295 
296 int
df_libgd_get_pixel(int i,int j,int component)297 df_libgd_get_pixel(int i, int j, int component) { return 0; }
298 
299 TBOOLEAN
df_read_pixmap(t_pixmap * pixmap)300 df_read_pixmap( t_pixmap *pixmap )
301 {
302     int_warn(NO_CARET, "This copy of gnuplot cannot read png/gif/jpeg images");
303     return FALSE;
304 }
305 
306 #else
307 
308 #include <gd.h>
309 static gdImagePtr im = NULL;
310 
311 void
gd_filetype_function(int filetype)312 gd_filetype_function(int filetype)
313 {
314     FILE *fp;
315     unsigned int M, N;
316 
317     /* free previous image, if any */
318     if (im) {
319 	gdImageDestroy(im);
320 	im = NULL;
321     }
322 
323     /* read image into memory */
324     fp = loadpath_fopen(df_filename, "rb");
325     if (!fp)
326 	int_error(NO_CARET, "Can't open data file \"%s\"", df_filename);
327 
328     switch(filetype) {
329 	case GD_PNG:	im = gdImageCreateFromPng(fp); break;
330 	case GD_GIF:
331 #ifdef HAVE_GD_GIF
332 			im = gdImageCreateFromGif(fp);
333 #endif
334 			break;
335 	case GD_JPEG:
336 #ifdef HAVE_GD_JPEG
337 			im = gdImageCreateFromJpeg(fp);
338 #endif
339 	default:	break;
340     }
341     fclose(fp);
342 
343     if (!im)
344 	int_error(NO_CARET, "libgd doesn't recognize the format of \"%s\"", df_filename);
345 
346     /* check on image properties and complain if we can't handle them */
347     M = im->sx;
348     N = im->sy;
349     FPRINTF((stderr,"This is a %u X %u %s image\n",M,N,
350 		im->trueColor ? "TrueColor" : "palette"));
351 
352     df_pixeldata = im->trueColor ? (void *)im->tpixels : (void *)im->pixels;
353     df_matrix_file = FALSE;
354     df_binary_file = TRUE;
355 
356     df_bin_record[0].scan_skip[0] = 0;
357     df_bin_record[0].scan_dim[0] = M;
358     df_bin_record[0].scan_dim[1] = N;
359 
360     df_bin_record[0].scan_dir[0] = 1;
361     df_bin_record[0].scan_dir[1] = -1;
362     df_bin_record[0].scan_generate_coord = TRUE;
363     df_bin_record[0].cart_scan[0] = DF_SCAN_POINT;
364     df_bin_record[0].cart_scan[1] = DF_SCAN_LINE;
365 
366     df_extend_binary_columns(4);
367     df_set_read_type(1, DF_UCHAR);
368     df_set_read_type(2, DF_UCHAR);
369     df_set_read_type(3, DF_UCHAR);
370     df_set_read_type(4, DF_UCHAR);
371     df_set_skip_before(1,0);
372 
373     df_no_use_specs = 4;
374     use_spec[0].column = 1;
375     use_spec[1].column = 2;
376     use_spec[2].column = 3;
377     use_spec[3].column = 4;
378 }
379 
380 int
df_libgd_get_pixel(int i,int j,int component)381 df_libgd_get_pixel(int i, int j, int component)
382 {
383     static int pixel;
384     int alpha;
385 
386     switch(component) {
387     case 0:	pixel = gdImageGetTrueColorPixel(im, i,j);
388     		return gdTrueColorGetRed(pixel);
389     case 1:	return gdTrueColorGetGreen(pixel);
390     case 2:	return gdTrueColorGetBlue(pixel);
391     case 3:	/* runs from 0-127 rather than 0-255 */
392 		alpha = 2 * gdTrueColorGetAlpha(pixel);
393 		return (255-alpha);
394     default:	return 0; /* shouldn't happen */
395     }
396 }
397 
398 TBOOLEAN
df_read_pixmap(t_pixmap * pixmap)399 df_read_pixmap( t_pixmap *pixmap )
400 {
401     int filetype;
402     int i, j;
403     coordval *pixel;
404     char *file_ext = strrchr(pixmap->filename, '.');
405 
406     /* Parse file name */
407     if (!file_ext++)
408 	return FALSE;
409     if (!strcasecmp(file_ext, "png"))
410 	filetype = GD_PNG;
411     else if (!strcasecmp(file_ext, "gif"))
412 	filetype = GD_GIF;
413     else if (!strcasecmp(file_ext, "jpeg") || !strcasecmp(file_ext, "jpg"))
414 	filetype = GD_JPEG;
415     else {
416 	/* Clear anything that was there before */
417 	pixmap->nrows = pixmap->ncols = 0;
418 	int_warn(NO_CARET, "unrecognized pixmap type: %s", pixmap->filename);
419 	return FALSE;
420     }
421 
422     /* Create a blank record that gd_filetype_function can write into */
423     df_add_binary_records(1, DF_CURRENT_RECORDS);
424 
425     /* Open file and allocate space for image data */
426     df_filename = (char *) pixmap->filename;
427     gd_filetype_function(filetype);
428     df_filename = NULL;
429     pixmap->ncols = df_bin_record[0].scan_dim[0];
430     pixmap->nrows = df_bin_record[0].scan_dim[1];
431     pixmap->image_data = gp_realloc( pixmap->image_data,
432 			4. * sizeof(coordval) * pixmap->ncols * pixmap->nrows, "pixmap");
433 
434     /* Fill in image data */
435     pixel = pixmap->image_data;
436     for (i=0; i<pixmap->nrows; i++)
437     for (j=0; j<pixmap->ncols; j++)
438     {
439 	*pixel++ = (coordval)df_libgd_get_pixel(j,i,0) / 255.;
440 	*pixel++ = (coordval)df_libgd_get_pixel(j,i,1) / 255.;
441 	*pixel++ = (coordval)df_libgd_get_pixel(j,i,2) / 255.;
442 	*pixel++ = (coordval)df_libgd_get_pixel(j,i,3);
443     }
444 
445     return TRUE;
446 }
447 
448 #endif
449