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