1 /*
2  * This file is part of Siril, an astronomy image processor.
3  * Copyright (C) 2005-2011 Francois Meyer (dulle at free.fr)
4  * Copyright (C) 2012-2021 team free-astro (see more in AUTHORS file)
5  * Reference site is https://free-astro.org/index.php/Siril
6  *
7  * Siril is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation, either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * Siril is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with Siril. If not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 /* Internal image formats import and export: BMP and PPM */
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <sys/types.h>
25 #include <sys/stat.h>
26 #include <fcntl.h>
27 #include <unistd.h>
28 #include <string.h>
29 #include <assert.h>
30 
31 #include "core/siril.h"
32 #include "core/proto.h"
33 #include "gui/utils.h"
34 #include "gui/progress_and_log.h"
35 #include "io/image_format_fits.h"
36 
37 #ifndef O_BINARY
38 #define O_BINARY 0
39 #endif
40 
bmp32tofits48(unsigned char * rvb,unsigned long rx,unsigned long ry,fits * fit)41 static int bmp32tofits48(unsigned char *rvb, unsigned long rx, unsigned long ry, fits *fit) {
42 	size_t datasize, i;
43 	WORD *rdata, *gdata, *bdata, *olddata;
44 
45 	datasize = rx * ry;
46 
47 	olddata = fit->data;
48 	if ((fit->data = realloc(fit->data, 3 * datasize * sizeof(WORD))) == NULL) {
49 		PRINT_ALLOC_ERR;
50 		if (olddata)
51 			free(fit->data);
52 		return 1;
53 	}
54 
55 	rdata = fit->pdata[RLAYER] = fit->data;
56 	gdata = fit->pdata[GLAYER] = fit->data + datasize;
57 	bdata = fit->pdata[BLAYER] = fit->data + 2 * datasize;
58 	for (i = 0; i < datasize; i++) {
59 		*bdata++ = (WORD) *rvb++;
60 		*gdata++ = (WORD) *rvb++;
61 		*rdata++ = (WORD) *rvb++;
62 		rvb++;
63 	}
64 	fit->bitpix = fit->orig_bitpix = BYTE_IMG;
65 	fit->naxis = 3;
66 	fit->rx = rx;
67 	fit->ry = ry;
68 	fit->naxes[0] = rx;
69 	fit->naxes[1] = ry;
70 	fit->naxes[2] = 3;
71 	fit->binning_x = fit->binning_y = 1;
72 	return 0;
73 }
74 
bmp24tofits48(unsigned char * rvb,unsigned long rx,unsigned long ry,fits * fit)75 static int bmp24tofits48(unsigned char *rvb, unsigned long rx, unsigned long ry, fits *fit) {
76 	int i, j;
77 	WORD *rdata, *gdata, *bdata, *olddata;
78 
79 	int padsize = (4 - (rx * 3) % 4) % 4;
80 	size_t newdatasize = ry * rx;
81 
82 	olddata = fit->data;
83 	if ((fit->data = realloc(fit->data, 3 * newdatasize * sizeof(WORD))) == NULL) {
84 		PRINT_ALLOC_ERR;
85 		if (olddata)
86 			free(fit->data);
87 		return 1;
88 	}
89 	rdata = fit->pdata[RLAYER] = fit->data;
90 	gdata = fit->pdata[GLAYER] = fit->data + newdatasize;
91 	bdata = fit->pdata[BLAYER] = fit->data + 2 * newdatasize;
92 	for (i = 0; i < ry; i++) {
93 		for (j = 0; j < rx; j++) {
94 			*bdata++ = *rvb++;
95 			*gdata++ = *rvb++;
96 			*rdata++ = *rvb++;
97 		}
98 		rvb += padsize;
99 	}
100 	fit->bitpix = fit->orig_bitpix = BYTE_IMG;
101 	fit->naxis = 3;
102 	fit->rx = rx;
103 	fit->ry = ry;
104 	fit->naxes[0] = rx;
105 	fit->naxes[1] = ry;
106 	fit->naxes[2] = 3;
107 	fit->binning_x = fit->binning_y = 1;
108 	return 0;
109 }
110 
bmp16tofits48(unsigned char * rvb,unsigned long rx,unsigned long ry,fits * fit)111 static int bmp16tofits48(unsigned char *rvb, unsigned long rx, unsigned long ry, fits *fit) {
112 	WORD *rdata, *gdata, *bdata, *olddata;
113 	size_t newdatasize = ry * rx;
114 
115 	olddata = fit->data;
116 	if ((fit->data = realloc(fit->data, 3 * newdatasize * sizeof(WORD))) == NULL) {
117 		PRINT_ALLOC_ERR;
118 		if (olddata)
119 			free(fit->data);
120 		return 1;
121 	}
122 	rdata = fit->pdata[RLAYER] = fit->data;
123 	gdata = fit->pdata[GLAYER] = fit->data + newdatasize;
124 	bdata = fit->pdata[BLAYER] = fit->data + 2 * newdatasize;
125 	for (size_t i = 0; i < newdatasize; i++) {
126 		unsigned char buf0 = *rvb++;
127 		unsigned char buf1 = *rvb++;
128 		unsigned pixel_data = buf0 | buf1 << 8;
129 
130 		*rdata++ = ((pixel_data & 0x7c00) >> 10) * 255.0 / 31.0 + 0.5;
131 		*gdata++ = ((pixel_data & 0x03e0) >> 5) * 255.0 / 31.0 + 0.5;
132 		*bdata++ = ((pixel_data & 0x001f) >> 0) * 255.0 / 31.0 + 0.5;
133 	}
134 	fit->bitpix = fit->orig_bitpix = BYTE_IMG;
135 	fit->naxis = 3;
136 	fit->rx = rx;
137 	fit->ry = ry;
138 	fit->naxes[0] = rx;
139 	fit->naxes[1] = ry;
140 	fit->naxes[2] = 3;
141 	fit->binning_x = fit->binning_y = 1;
142 	return 0;
143 }
144 
bmp8tofits(unsigned char * rgb,unsigned long rx,unsigned long ry,fits * fit)145 static int bmp8tofits(unsigned char *rgb, unsigned long rx, unsigned long ry, fits *fit) {
146 	size_t nbdata, padsize;
147 	int i, j;
148 	WORD *data, *olddata;
149 
150 	padsize = (4 - (rx % 4)) % 4;
151 	nbdata = rx * ry;
152 
153 	olddata = fit->data;
154 	if ((fit->data = realloc(fit->data, nbdata * sizeof(WORD))) == NULL) {
155 		PRINT_ALLOC_ERR;
156 		if (olddata)
157 			free(fit->data);
158 		return 1;
159 	}
160 	data = fit->pdata[BW_LAYER] = fit->data;
161 	for (i = 0; i < ry; i++) {
162 		for (j = 0; j < rx; j++) {
163 			*data++ = (WORD) *rgb++;
164 		}
165 		rgb += padsize;
166 	}
167 	fit->bitpix = fit->orig_bitpix = BYTE_IMG;
168 	fit->rx = rx;
169 	fit->ry = ry;
170 	fit->naxes[0] = rx;
171 	fit->naxes[1] = ry;
172 	fit->naxes[2] = 1;
173 	fit->naxis = 2;
174 	fit->binning_x = fit->binning_y = 1;
175 	return 0;
176 }
177 
get_image_size(BYTE * header,unsigned long * width,unsigned long * height)178 static void get_image_size(BYTE *header, unsigned long *width,
179 		unsigned long *height) {
180 	unsigned long bitmapinfoheader = 0;
181 	unsigned long lx = 0, ly = 0;
182 	unsigned short sx = 0, sy = 0;
183 
184 	memcpy(&bitmapinfoheader, header + 14, 4);
185 	if (bitmapinfoheader == 12) {
186 		memcpy(&sx, header + 18, 2);
187 		memcpy(&sy, header + 20, 2);
188 		*width = (unsigned long) sx;
189 		*height = (unsigned long) sy;
190 	} else {
191 		memcpy(&lx, header + 18, 4);
192 		memcpy(&ly, header + 22, 4);
193 		*width = lx;
194 		*height = ly;
195 	}
196 }
197 
198 /* reads a BMP image at filename `name', and stores it into the fit argument */
readbmp(const char * name,fits * fit)199 int readbmp(const char *name, fits *fit) {
200 	BYTE header[256];
201 	FILE *file;
202 	long int count;
203 	unsigned char *buf;
204 	unsigned long data_offset = 0;
205 	unsigned long width = 0, height = 0;
206 	unsigned short nbplane = 0;
207 
208 	if ((file = g_fopen(name, "rb")) == NULL) {
209 		siril_log_message(_("Error opening BMP.\n"));
210 		return -1;
211 	}
212 
213 	if ((count = fread(header, 1, 54, file)) != 54) {
214 		fprintf(stderr, "readbmp: %ld header bytes read instead of 54\n", count);
215 		perror("readbmp");
216 		fclose(file);
217 		return -1;
218 	}
219 
220 	/*	memcpy(&compression, header + 30, 4);*/
221 
222 	get_image_size(header, &width, &height);
223 	memcpy(&nbplane, header + 28, 2);
224 	nbplane = nbplane / 8;
225 	memcpy(&data_offset, header + 10, 4);
226 
227 	unsigned int padsize = (4 - (width * nbplane) % 4) % 4;
228 	size_t nbdata = width * height * nbplane + height * padsize;
229 
230 	if (fseek(file, data_offset, SEEK_SET) == -1) {
231 		perror("BMP fseek for data");
232 		fclose(file);
233 		return -1;
234 	}
235 
236 	buf = malloc(nbdata);
237 	if (!buf) {
238 		PRINT_ALLOC_ERR;
239 		fclose(file);
240 		return -1;
241 	}
242 	unsigned long f;
243 	if (nbdata != (f = fread(buf, 1, nbdata, file))) {
244 		fprintf(stderr, "readbmp: could not read all data: (%zu, %lu)\n", nbdata, f);
245 		free(buf);
246 		fclose(file);
247 		return -1;
248 	}
249 	fclose(file);
250 
251 	switch (nbplane) {
252 		case 1:
253 			bmp8tofits(buf, width, height, fit);
254 			break;
255 		case 2:
256 			bmp16tofits48(buf, width, height, fit);
257 			break;
258 		case 3:
259 			bmp24tofits48(buf, width, height, fit);
260 			break;
261 		case 4:
262 			bmp32tofits48(buf, width, height, fit);
263 			break;
264 		default:
265 			siril_log_message(_("Sorry but Siril cannot "
266 						"open this kind of BMP. Try to convert it before.\n"));
267 	}
268 	fit->type = DATA_USHORT;
269 	free(buf);
270 	char *basename = g_path_get_basename(name);
271 	siril_log_message(_("Reading BMP: file %s, %ld layer(s), %ux%u pixels\n"),
272 			basename, fit->naxes[2], fit->rx, fit->ry);
273 	g_free(basename);
274 	return (int) nbplane;
275 }
276 
savebmp(const char * name,fits * fit)277 int savebmp(const char *name, fits *fit) {
278 	unsigned char bmpfileheader[14] = { 'B', 'M', 	//Magic Number
279 		0, 0, 0, 0, 	//Size in bytes, see below
280 		0, 0, 0, 0, 54, 0, 0, 0	//offset
281 	};
282 	unsigned char bmpinfoheader[40] = { 40, 0, 0, 0, //info of the header size
283 		0, 0, 0, 0, 	//width, see below
284 		0, 0, 0, 0, 	//height, see below
285 		1, 0, 		//number color planes
286 		24, 0,		//bits per pixel
287 		0, 0, 0, 0, 	//no compression
288 		0, 0, 0, 0, 	//image bits size
289 		0, 0, 0, 0, 	//horizontal resolution, we don't care
290 		0, 0, 0, 0, 	//vertical resolution, we don't care neither
291 		0, 0, 0, 0, 	//colors in pallete
292 		0, 0, 0, 0, 	//important colors
293 	};
294 	unsigned int width = fit->rx, height = fit->ry;
295 	double norm;
296 
297 	FILE *f;
298 
299 	WORD *gbuf[3] = { fit->pdata[RLAYER], fit->pdata[GLAYER], fit->pdata[BLAYER] };
300 	float *gbuff[3] = { fit->fpdata[RLAYER], fit->fpdata[GLAYER], fit->fpdata[BLAYER] };
301 
302 	unsigned int padsize = (4 - (width * 3) % 4) % 4;
303 	size_t datasize = width * height * 3 + padsize * height;
304 	size_t filesize = datasize + sizeof(bmpfileheader) + sizeof(bmpinfoheader);
305 	int i, j;
306 	WORD red, blue, green;
307 	float redf, bluef, greenf;
308 	unsigned char pixel[3];
309 
310 	bmpfileheader[2] = (unsigned char) (filesize);
311 	bmpfileheader[3] = (unsigned char) (filesize >> 8);
312 	bmpfileheader[4] = (unsigned char) (filesize >> 16);
313 	bmpfileheader[5] = (unsigned char) (filesize >> 24);
314 
315 	bmpinfoheader[4] = (unsigned char) (width);
316 	bmpinfoheader[5] = (unsigned char) (width >> 8);
317 	bmpinfoheader[6] = (unsigned char) (width >> 16);
318 	bmpinfoheader[7] = (unsigned char) (width >> 24);
319 
320 	bmpinfoheader[8] = (unsigned char) (height);
321 	bmpinfoheader[9] = (unsigned char) (height >> 8);
322 	bmpinfoheader[10] = (unsigned char) (height >> 16);
323 	bmpinfoheader[11] = (unsigned char) (height >> 24);
324 
325 	bmpinfoheader[24] = (unsigned char) (datasize);
326 	bmpinfoheader[25] = (unsigned char) (datasize >> 8);
327 	bmpinfoheader[26] = (unsigned char) (datasize >> 16);
328 	bmpinfoheader[27] = (unsigned char) (datasize >> 24);
329 
330 	char *filename = strdup(name);
331 	if (!g_str_has_suffix(filename, ".bmp")) {
332 		filename = str_append(&filename, ".bmp");
333 	}
334 
335 	f = g_fopen(filename, "wb");
336 	if (f == NULL) {
337 		siril_log_message(_("Can't create BMP file.\n"));
338 		free(filename);
339 		return 1;
340 	}
341 
342 	norm = (fit->orig_bitpix != BYTE_IMG ?
343 			UCHAR_MAX_DOUBLE / USHRT_MAX_DOUBLE : 1.0);
344 
345 	fwrite(bmpfileheader, sizeof(bmpfileheader), 1, f);
346 	fwrite(bmpinfoheader, sizeof(bmpinfoheader), 1, f);
347 
348 	if (fit->type == DATA_USHORT) {
349 		for (i = 0; i < height; i++) {
350 			for (j = 0; j < width; j++) {
351 				red = *gbuf[RLAYER]++;
352 				if (fit->naxes[2] == 3) {
353 					green = *gbuf[GLAYER]++;
354 					blue = *gbuf[BLAYER]++;
355 				} else {
356 					green = red;
357 					blue = red;
358 				}
359 
360 				pixel[0] = round_to_BYTE(blue * norm); /* swap Blue and Red */
361 				pixel[1] = round_to_BYTE(green * norm);
362 				pixel[2] = round_to_BYTE(red * norm);
363 
364 				fwrite(pixel, sizeof(pixel), 1, f);
365 			}
366 			if (padsize != 0)
367 				fwrite("0", 1, padsize, f);		//We fill the end of width with 0
368 		}
369 	} else {
370 		for (i = 0; i < height; i++) {
371 			for (j = 0; j < width; j++) {
372 				redf = *gbuff[RLAYER]++;
373 				if (fit->naxes[2] == 3) {
374 					greenf = *gbuff[GLAYER]++;
375 					bluef = *gbuff[BLAYER]++;
376 				} else {
377 					greenf = redf;
378 					bluef = redf;
379 				}
380 
381 				pixel[0] = float_to_uchar_range(bluef); /* swap Blue and Red */
382 				pixel[1] = float_to_uchar_range(greenf);
383 				pixel[2] = float_to_uchar_range(redf);
384 
385 				fwrite(pixel, sizeof(pixel), 1, f);
386 			}
387 			if (padsize != 0)
388 				fwrite("0", 1, padsize, f);	//We fill the end of width with 0
389 		}
390 	}
391 	fclose(f);
392 	siril_log_message(_("Saving BMP: file %s, %ld layer(s), %ux%u pixels\n"), filename,
393 			fit->naxes[2], fit->rx, fit->ry);
394 	free(filename);
395 	return 0;
396 }
397 
398 /********************* NetPBM IMAGE LOADING **********************/
399 /* P1	Portable bitmap	ASCII
400  * P2	Portable graymap	ASCII
401  * P3	Portable pixmap	ASCII
402  * P4	Portable bitmap	Binary
403  * P5	Portable graymap	Binary
404  * P6	Portable pixmap	Binary
405  */
406 /* This method loads a pnm or pgm binary file into the fits image passed as argument. */
import_pnm_to_fits(const char * filename,fits * fit)407 int import_pnm_to_fits(const char *filename, fits *fit) {
408 	FILE *file;
409 	char buf[256];
410 	size_t i, j;
411 	int max_val;
412 	size_t stride;
413 
414 	if ((file = g_fopen(filename, "rb")) == NULL) {
415 		siril_log_message(_("Sorry but Siril cannot open this file.\n"));
416 		return -1;
417 	}
418 	if (fgets(buf, 256, file) == NULL) {
419 		perror("reading pnm file");
420 		fclose(file);
421 		return -1;
422 	}
423 	if (buf[0] != 'P' || buf[1] < '5' || buf[1] > '6' || buf[2] != '\n') {
424 		siril_log_message(
425 				_("Wrong magic cookie in PNM file, ASCII types and"
426 					" b&w bitmaps are not supported.\n"));
427 		fclose(file);
428 		return -1;
429 	}
430 	if (buf[1] == '6') {
431 		fit->naxis = 3;
432 		fit->naxes[2] = 3;
433 	} else {
434 		fit->naxes[2] = 1;
435 		fit->naxis = 2;
436 	}
437 
438 	do {
439 		if (fgets(buf, 256, file) == NULL) {
440 			fclose(file);
441 			return -1;
442 		}
443 	} while (buf[0] == '#');
444 	i = 0;
445 	while (buf[i] >= '0' && buf[i] <= '9')
446 		i++;
447 	if (i == 0) {
448 		fclose(file);
449 		return -1;
450 	}
451 	buf[i] = '\0';
452 	fit->rx = g_ascii_strtoull(buf, NULL, 10);
453 	j = ++i;
454 	while (buf[j] >= '0' && buf[j] <= '9')
455 		j++;
456 	if (j == i) {
457 		fclose(file);
458 		return -1;
459 	}
460 	if (buf[j] != '\n') {
461 		fclose(file);
462 		return -1;
463 	}
464 	buf[j] = '\0';
465 	fit->ry = g_ascii_strtoull(buf + i, NULL, 10);
466 
467 	do {
468 		if (fgets(buf, 256, file) == NULL) {
469 			fclose(file);
470 			return -1;
471 		}
472 	} while (buf[0] == '#');
473 	i = 0;
474 	while (buf[i] >= '0' && buf[i] <= '9')
475 		i++;
476 	if (buf[i] != '\n') {
477 		fclose(file);
478 		return -1;
479 	}
480 	buf[i] = '\0';
481 	max_val = g_ascii_strtoll(buf, NULL, 10);
482 	if (max_val < UCHAR_MAX) {
483 		fclose(file);
484 		return -1;
485 	}
486 	if (max_val == UCHAR_MAX) {
487 		/* 8-bit file */
488 		unsigned char *tmpbuf = NULL;
489 		WORD *olddata;
490 		if (fit->naxes[2] == 1)
491 			stride = fit->rx;
492 		else
493 			stride = fit->rx * 3;
494 		tmpbuf = malloc(stride * fit->ry);
495 		olddata = fit->data;
496 		fit->data = realloc(fit->data, stride * fit->ry * sizeof(WORD));
497 		if (fit->data == NULL || tmpbuf == NULL) {
498 			PRINT_ALLOC_ERR;
499 			fclose(file);
500 			if (olddata && !fit->data)
501 				free(olddata);
502 			if (tmpbuf)
503 				free(tmpbuf);
504 			fit->data = NULL;
505 			return -1;
506 		}
507 		if (fread(tmpbuf, stride, fit->ry, file) < fit->ry) {
508 			siril_log_message(_("Error reading 8-bit PPM image data.\n"));
509 			fclose(file);
510 			free(tmpbuf);
511 			free(fit->data);
512 			fit->data = NULL;
513 			return -1;
514 		}
515 		if (fit->naxes[2] == 3)
516 			rgb24bit_to_fits48bit(tmpbuf, fit, FALSE);
517 		else
518 			rgb8bit_to_fits16bit(tmpbuf, fit);
519 		free(tmpbuf);
520 		fit->bitpix = BYTE_IMG;
521 		fit->binning_x = fit->binning_y = 1;
522 		fits_flip_top_to_bottom(fit);
523 	} else if (max_val == USHRT_MAX || max_val == SHRT_MAX) {
524 		/* 16-bit file */
525 		if (fit->naxes[2] == 1) {
526 			WORD *olddata = fit->data;
527 			stride = fit->rx * sizeof(WORD);
528 			fit->data = realloc(fit->data, stride * fit->ry * sizeof(WORD));
529 			if (fit->data == NULL) {
530 				PRINT_ALLOC_ERR;
531 				fclose(file);
532 				if (olddata)
533 					free(olddata);
534 				return -1;
535 			}
536 			if (fread(fit->data, stride, fit->ry, file) < fit->ry) {
537 				siril_log_message(
538 						_("Error reading 16-bit gray PPM image data.\n"));
539 				fclose(file);
540 				free(fit->data);
541 				fit->data = NULL;
542 				return -1;
543 			}
544 			/* change endianness in place */
545 			size_t nbdata = fit->rx * fit->ry;
546 			for (i = 0; i < nbdata; i++)
547 				fit->data[i] = change_endianness16(fit->data[i]);
548 			fit->pdata[0] = fit->data;
549 			fit->pdata[1] = fit->data;
550 			fit->pdata[2] = fit->data;
551 
552 		} else {
553 			/* RGB 16-bit image */
554 			WORD *tmpbuf, *olddata = fit->data;
555 			stride = fit->rx * 3 * sizeof(WORD);
556 			tmpbuf = malloc(stride * fit->ry);
557 			fit->data = realloc(fit->data, stride * fit->ry * sizeof(WORD));
558 			if (fit->data == NULL || tmpbuf == NULL) {
559 				PRINT_ALLOC_ERR;
560 				fclose(file);
561 				if (olddata && !fit->data)
562 					free(olddata);
563 				if (tmpbuf)
564 					free(tmpbuf);
565 				fit->data = NULL;
566 				return -1;
567 			}
568 			if (fread(tmpbuf, stride, fit->ry, file) < fit->ry) {
569 				siril_log_message(
570 						_("Error reading 16-bit color PPM image data.\n"));
571 				fclose(file);
572 				free(tmpbuf);
573 				free(fit->data);
574 				fit->data = NULL;
575 				return -1;
576 			}
577 			rgb48bit_to_fits48bit(tmpbuf, fit, FALSE, TRUE);
578 			free(tmpbuf);
579 		}
580 		fit->bitpix = USHORT_IMG;
581 		fit->binning_x = fit->binning_y = 1;
582 		fits_flip_top_to_bottom(fit);
583 	} else {
584 		siril_log_message(_("Not handled max value for PNM: %d.\n"),
585 				max_val);
586 		fclose(file);
587 		return -1;
588 	}
589 	fit->type = DATA_USHORT;
590 	g_snprintf(fit->row_order, FLEN_VALUE, "%s", "TOP-DOWN");
591 
592 	fclose(file);
593 	char *basename = g_path_get_basename(filename);
594 	siril_log_message(_("Reading NetPBM: file %s, %ld layer(s), %ux%u pixels\n"),
595 			basename, fit->naxes[2], fit->rx, fit->ry);
596 	g_free(basename);
597 	return fit->naxes[2];
598 }
599 
saveppm(const char * name,fits * fit)600 static int saveppm(const char *name, fits *fit) {
601 	FILE *fp = g_fopen(name, "wb");
602 	size_t i, ndata = fit->rx * fit->ry;
603 	double norm;
604 	const char *comment = "# CREATOR : SIRIL";
605 
606 	fprintf(fp, "P6\n%s\n%u %u\n%u\n", comment, fit->rx, fit->ry, USHRT_MAX);
607 	WORD *gbuf[3] = { fit->pdata[RLAYER], fit->pdata[GLAYER], fit->pdata[BLAYER] };
608 	float *gbuff[3] = { fit->fpdata[RLAYER], fit->fpdata[GLAYER], fit->fpdata[BLAYER] };
609 	fits_flip_top_to_bottom(fit);
610 	norm = (fit->orig_bitpix != BYTE_IMG) ? 1.0 : USHRT_MAX_DOUBLE / UCHAR_MAX_DOUBLE;
611 	if (fit->type == DATA_USHORT) {
612 		for (i = 0; i < ndata; i++) {
613 			WORD color[3];
614 			color[0] = *gbuf[RLAYER]++ * norm;
615 			color[1] = *gbuf[GLAYER]++ * norm;
616 			color[2] = *gbuf[BLAYER]++ * norm;
617 
618 			color[0] = change_endianness16(color[0]);
619 			color[1] = change_endianness16(color[1]);
620 			color[2] = change_endianness16(color[2]);
621 			fwrite(color, sizeof(WORD), 3, fp);
622 		}
623 	} else {
624 		for (i = 0; i < ndata; i++) {
625 			WORD color[3];
626 			color[0] = float_to_ushort_range(*gbuff[RLAYER]++);
627 			color[1] = float_to_ushort_range(*gbuff[GLAYER]++);
628 			color[2] = float_to_ushort_range(*gbuff[BLAYER]++);
629 
630 			color[0] = change_endianness16(color[0]);
631 			color[1] = change_endianness16(color[1]);
632 			color[2] = change_endianness16(color[2]);
633 			fwrite(color, sizeof(WORD), 3, fp);
634 		}
635 	}
636 	fclose(fp);
637 	fits_flip_top_to_bottom(fit);
638 	siril_log_message(_("Saving NetPBM: file %s, %ld layer(s), %ux%u pixels\n"),
639 			name, fit->naxes[2], fit->rx, fit->ry);
640 	return 0;
641 }
642 
savepgm(const char * name,fits * fit)643 static int savepgm(const char *name, fits *fit) {
644 	FILE *fp;
645 	size_t i, ndata = fit->rx * fit->ry;
646 	double norm;
647 	WORD *gbuf = fit->pdata[RLAYER];
648 	float *gbuff = fit->fpdata[RLAYER];
649 	const char *comment = "# CREATOR : SIRIL";
650 
651 	fp = g_fopen(name, "wb");
652 	if (!fp)
653 		return -1;
654 	fprintf(fp, "P5\n%s\n%u %u\n%u\n", comment, fit->rx, fit->ry, USHRT_MAX);
655 
656 	fits_flip_top_to_bottom(fit);
657 	norm = (fit->orig_bitpix != BYTE_IMG) ? 1.0 : USHRT_MAX_DOUBLE / UCHAR_MAX_DOUBLE;
658 	if (fit->type == DATA_USHORT) {
659 		for (i = 0; i < ndata; i++) {
660 			WORD tmp = *gbuf++ * norm;
661 			/* change endianness in place */
662 			WORD data[1];
663 			data[0] = (tmp >> 8) | (tmp << 8);
664 			fwrite(data, sizeof(data), 1, fp);
665 		}
666 	} else {
667 		for (i = 0; i < ndata; i++) {
668 			WORD tmp = float_to_ushort_range(*gbuff++);
669 			/* change endianness in place */
670 			WORD data[1];
671 			data[0] = (tmp >> 8) | (tmp << 8);
672 			fwrite(data, sizeof(data), 1, fp);
673 		}
674 	}
675 	fclose(fp);
676 	fits_flip_top_to_bottom(fit);
677 	siril_log_message(_("Saving NetPBM: file %s, %ld layer(s), %ux%u pixels\n"),
678 			name, fit->naxes[2], fit->rx, fit->ry);
679 	return 0;
680 }
681 
saveNetPBM(const char * name,fits * fit)682 int saveNetPBM(const char *name, fits *fit) {
683 	int retval;
684 	char *filename = strdup(name);
685 
686 	if (fit->naxes[2] == 1) {
687 		if (!g_str_has_suffix(filename, ".pgm")) {
688 			filename = str_append(&filename, ".pgm");
689 		}
690 		retval = savepgm(filename, fit);
691 	} else {
692 		if (!g_str_has_suffix(filename, ".ppm") && !g_str_has_suffix(filename, ".pnm")) {
693 			filename = str_append(&filename, ".ppm");
694 		}
695 		retval = saveppm(filename, fit);
696 	}
697 	free(filename);
698 	return retval;
699 }
700 
701 
pictofit(WORD * buf,fits * fit)702 static int pictofit(WORD *buf, fits *fit) {
703 	WORD *data, *olddata = fit->data;
704 
705 	size_t i, nbdata = fit->rx * fit->ry;
706 	if ((fit->data = realloc(fit->data, nbdata * sizeof(WORD))) == NULL) {
707 		PRINT_ALLOC_ERR;
708 		if (olddata)
709 			free(olddata);
710 		return -1;
711 	}
712 	data = fit->pdata[BW_LAYER] = fit->data;
713 	fit->pdata[GLAYER] = fit->data;
714 	fit->pdata[BLAYER] = fit->data;
715 #ifdef _OPENMP
716 #pragma omp parallel for num_threads(com.max_thread) private(i) schedule(static)
717 #endif
718 	for (i = 0; i < nbdata; i++)
719 		data[i] = buf[i];
720 	fit->bitpix = fit->orig_bitpix = SHORT_IMG;
721 	fit->naxes[0] = fit->rx;
722 	fit->naxes[1] = fit->ry;
723 	fit->naxes[2] = 1;
724 	fit->naxis = 2;
725 	return 0;
726 }
727 
pictofitrgb(WORD * buf,fits * fit)728 static int pictofitrgb(WORD *buf, fits *fit) {
729 	WORD *data[3], *olddata = fit->data;
730 
731 	size_t i, nbdata = fit->rx * fit->ry;
732 	if ((fit->data = realloc(fit->data, nbdata * 3 * sizeof(WORD))) == NULL) {
733 		PRINT_ALLOC_ERR;
734 		if (olddata)
735 			free(olddata);
736 		return -1;
737 	}
738 	data[RLAYER] = fit->pdata[RLAYER] = fit->data;
739 	data[GLAYER] = fit->pdata[GLAYER] = fit->data + nbdata;
740 	data[BLAYER] = fit->pdata[BLAYER] = fit->data + 2 * nbdata;
741 #ifdef _OPENMP
742 #pragma omp parallel for num_threads(com.max_thread) private(i) schedule(static)
743 #endif
744 	for (i = 0; i < nbdata; i++)
745 		data[RLAYER][i] = buf[i + (nbdata * RLAYER)];
746 
747 #ifdef _OPENMP
748 #pragma omp parallel for num_threads(com.max_thread) private(i) schedule(static)
749 #endif
750 	for (i = 0; i < nbdata; i++)
751 		data[GLAYER][i] = buf[i + (nbdata * GLAYER)];
752 
753 #ifdef _OPENMP
754 #pragma omp parallel for num_threads(com.max_thread) private(i) schedule(static)
755 #endif
756 	for (i = 0; i < nbdata; i++)
757 		data[BLAYER][i] = buf[i + (nbdata * BLAYER)];
758 
759 	fit->bitpix = fit->orig_bitpix = SHORT_IMG;
760 	fit->naxis = 3;
761 	fit->naxes[0] = fit->rx;
762 	fit->naxes[1] = fit->ry;
763 	fit->naxes[2] = 3;
764 	return 0;
765 }
766 
_pic_read_header(struct pic_struct * pic_file)767 static int _pic_read_header(struct pic_struct *pic_file) {
768 	char header[290];
769 	if (!pic_file || !pic_file->file)
770 		return -1;
771 	if (sizeof(header) != fread(header, 1, sizeof(header), pic_file->file)) {
772 		perror("read");
773 		return -1;
774 	}
775 
776 	memcpy(&pic_file->magic, header, 4);
777 
778 	if (pic_file->magic != 0x12231fc) {
779 		siril_log_message(_("Wrong magic cookie in PIC file. "
780 					"This image is not supported.\n"));
781 		return -1;
782 	}
783 
784 	memcpy(&pic_file->width, header + 68, 2);
785 	memcpy(&pic_file->height, header + 70, 2);
786 	assert(pic_file->width > 0 && pic_file->height > 0);
787 	memcpy(pic_file->bin, header + 80, 12);
788 	memcpy(&pic_file->nbplane, header + 92, 2);
789 	assert(pic_file->nbplane != 0);
790 	memcpy(&pic_file->hi, header + 118, 2);
791 	memcpy(&pic_file->lo, header + 120, 2);
792 	pic_file->date = g_strndup(header + 94, 10);
793 	pic_file->time = g_strndup(header + 104, 12);
794 	return 0;
795 }
796 
_pic_close_file(struct pic_struct * pic_file)797 static int _pic_close_file(struct pic_struct *pic_file) {
798 	int retval = 0;
799 	if (!pic_file)
800 		return retval;
801 	if (pic_file->file) {
802 		retval = fclose(pic_file->file);
803 		pic_file->file = NULL;
804 	}
805 	g_free(pic_file->date);
806 	g_free(pic_file->time);
807 	free(pic_file);
808 	return retval;
809 }
810 
readpic(const char * name,fits * fit)811 int readpic(const char *name, fits *fit) {
812 	struct pic_struct *pic_file;
813 	WORD *buf;
814 	int retval = 0;
815 
816 	pic_file = calloc(1, sizeof(struct pic_struct));
817 
818 	if ((pic_file->file = g_fopen(name, "rb")) == NULL) {
819 		siril_log_message(
820 				_("Sorry but Siril cannot open the PIC file: %s.\n"), name);
821 		free(pic_file);
822 		return -1;
823 	}
824 
825 	if (_pic_read_header(pic_file)) {
826 		_pic_close_file(pic_file);
827 		return -1;
828 	}
829 
830 	fit->rx = (unsigned int) pic_file->width;
831 	fit->ry = (unsigned int) pic_file->height;
832 	fit->binning_x = (unsigned int) pic_file->bin[4];
833 	fit->binning_y = (unsigned int) pic_file->bin[5];
834 	fit->hi = pic_file->hi;
835 	fit->lo = pic_file->lo;
836 	fit->type = DATA_USHORT;
837 
838 	size_t nbdata = fit->rx * fit->ry;
839 
840 	fseek(pic_file->file, 290, SEEK_SET);
841 	buf = malloc(nbdata * pic_file->nbplane * sizeof(WORD));
842 
843 	if ((fread(buf, 1, nbdata * pic_file->nbplane * sizeof(WORD), pic_file->file))
844 			!= nbdata * pic_file->nbplane * sizeof(WORD)) {
845 		siril_log_message(_("Cannot Read the data\n"));
846 		free(buf);
847 		_pic_close_file(pic_file);
848 		return -1;
849 	}
850 
851 	switch (pic_file->nbplane) {
852 		case 1:
853 			retval = pictofit(buf, fit);
854 			break;
855 		case 3:
856 			retval = pictofitrgb(buf, fit);
857 			break;
858 		default:
859 			retval = -1;
860 			siril_log_message(_("Sorry but Siril cannot open this file.\n"));
861 	}
862 	free(buf);
863 
864 	if (retval) {
865 		_pic_close_file(pic_file);
866 		return -1;
867 	}
868 
869 	char *basename = g_path_get_basename(name);
870 	siril_log_message(_("Reading PIC: file %s, %ld layer(s), %ux%u pixels\n"),
871 			basename, fit->naxes[2], fit->rx, fit->ry);
872 	siril_log_message("(%d,%d)-(%d,%d) - Binning %dx%d\n", pic_file->bin[0],
873 			pic_file->bin[1], pic_file->bin[2], pic_file->bin[3],
874 			fit->binning_x, fit->binning_y);
875 
876 	if (pic_file->date[0] != 0x00) {
877 		g_strchug(pic_file->date);	// removing left white spaces if exist
878 		siril_log_message(_("Date (of observation): %s\n"), pic_file->date);
879 	}
880 	if (pic_file->time[0] != 0x00) {
881 		g_strchug(pic_file->time);	// removing left white spaces if exist
882 		siril_log_message(_("Time (of observation): %s\n"), pic_file->time);
883 	}
884 
885 	_pic_close_file(pic_file);
886 	g_free(basename);
887 	return retval;
888 }
889