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  * WARNING: the code in this file and its header will not work properly
22  * on big endian systems.
23  */
24 
25 #include <fcntl.h>
26 #include <stdio.h>
27 #include <string.h>
28 #include <math.h>
29 #ifdef _WIN32
30 #include <io.h>
31 #endif
32 #include "core/siril.h"
33 #include "core/proto.h"
34 #include "core/siril_date.h"
35 #include "gui/utils.h"
36 #include "gui/progress_and_log.h"
37 #include "algos/demosaicing.h"
38 #include "io/conversion.h"
39 #include "io/image_format_fits.h"
40 #include "ser.h"
41 
42 static gboolean user_warned = FALSE;
43 
44 static int ser_write_header(struct ser_struct *ser_file);
45 static int ser_write_image_for_writer(struct seqwriter_data *writer, fits *image, int index);
46 static int ser_write_frame_from_fit_internal(struct ser_struct *ser_file, fits *fit, int frame_no);
47 
48 
49 /* Output SER timestamp */
display_date(guint64 timestamp,char * txt)50 static int display_date(guint64 timestamp, char *txt) {
51 	if (timestamp == 0)
52 		return -1;
53 
54 	GDateTime *date = ser_timestamp_to_date_time(timestamp);
55 	if (date) {
56 		gchar *str = date_time_to_FITS_date(date);
57 		fprintf(stdout, "%s%s\n", txt, str);
58 		free(str);
59 		g_date_time_unref(date);
60 	}
61 	return 0;
62 }
63 
convert_color_id_to_char(ser_color color_id)64 static char *convert_color_id_to_char(ser_color color_id) {
65 	switch (color_id) {
66 	case SER_MONO:
67 		return "MONO";
68 	case SER_BAYER_RGGB:
69 		return "RGGB";
70 	case SER_BAYER_BGGR:
71 		return "BGGR";
72 	case SER_BAYER_GBRG:
73 		return "GBRG";
74 	case SER_BAYER_GRBG:
75 		return "GRBG";
76 	case SER_BAYER_CYYM:
77 		return "CYYM";
78 	case SER_BAYER_YCMY:
79 		return "YCMY";
80 	case SER_BAYER_YMCY:
81 		return "YMCY";
82 	case SER_BAYER_MYYC:
83 		return "MYYC";
84 	case SER_RGB:
85 		return "RGB";
86 	case SER_BGR:
87 		return "BGR";
88 	default:
89 		return "";
90 	}
91 }
92 
93 /* reads timestamps from the trailer of the file and stores them in ser_file->ts */
ser_read_timestamp(struct ser_struct * ser_file)94 static int ser_read_timestamp(struct ser_struct *ser_file) {
95 	int i;
96 	gboolean timestamps_in_order = TRUE;
97 	guint64 previous_ts = 0L;
98 	gint64 frame_size;
99 
100 	ser_file->fps = -1.0;	// will be calculated from the timestamps
101 
102 	if (!ser_file->frame_count || ser_file->image_width <= 0 ||
103 			ser_file->image_height <= 0 || ser_file->byte_pixel_depth <= 0 ||
104 			!ser_file->number_of_planes)
105 		return 0;
106 
107 	frame_size = ser_file->image_width *
108 		ser_file->image_height * ser_file->number_of_planes;
109 	gint64 offset = SER_HEADER_LEN + frame_size *
110 		(gint64)ser_file->byte_pixel_depth * (gint64)ser_file->frame_count;
111 	/* Check if file is large enough to have timestamps */
112 	if (ser_file->filesize >= offset + (8 * ser_file->frame_count)) {
113 		ser_file->ts = calloc(8, ser_file->frame_count);
114 		if (!ser_file->ts) {
115 			PRINT_ALLOC_ERR;
116 			return 0;
117 		}
118 		ser_file->ts_alloc = ser_file->frame_count;
119 
120 		// Seek to start of timestamps
121 		for (i = 0; i < ser_file->frame_count; i++) {
122 			if ((gint64) -1 == fseek64(ser_file->file, offset + (i * 8), SEEK_SET))
123 				return -1;
124 
125 			if (8 != fread(&ser_file->ts[i], 1, 8, ser_file->file))
126 				return 0;
127 
128 			ser_file->ts[i] = le64_to_cpu(ser_file->ts[i]);
129 		}
130 
131 		/* Check order of Timestamps */
132 		guint64 *ts_ptr = ser_file->ts;
133 		guint64 min_ts = *ts_ptr;
134 		guint64 max_ts = *ts_ptr;
135 
136 		for (i = 0; i < ser_file->frame_count; i++) {
137 			if (*ts_ptr < previous_ts) {
138 				// Timestamps are not in order
139 				timestamps_in_order = FALSE;
140 			}
141 			previous_ts = *ts_ptr;
142 			// Keep track of maximum timestamp value
143 			if (*ts_ptr > max_ts) {
144 				max_ts = *ts_ptr;
145 			}
146 			// Keep track of minimum timestamp value
147 			if (*ts_ptr < min_ts) {
148 				min_ts = *ts_ptr;
149 			}
150 			ts_ptr++;
151 		}
152 
153 		if (timestamps_in_order) {
154 			if (min_ts == max_ts)
155 				fprintf(stdout, _("Warning: timestamps in the SER sequence are all identical.\n"));
156 			else fprintf(stdout, _("Timestamps in the SER sequence are correctly ordered.\n"));
157 		} else {
158 			fprintf(stdout, _("Warning: timestamps in the SER sequence are not in the correct order.\n"));
159 		}
160 
161 		ser_file->ts_min = min_ts;
162 		ser_file->ts_max = max_ts;
163 		double diff_ts = (ser_file->ts_max - ser_file->ts_min) / 1000.0;
164 		// diff_ts now in units of 100 us or ten thousandths of a second
165 		if (diff_ts > 0.0) {
166 			// There is a positive time difference between first and last
167 			// timestamps, we can calculate a frames per second value
168 			ser_file->fps = (ser_file->frame_count - 1) * 10000.0 / diff_ts;
169 		}
170 	} else {
171 		fprintf(stdout, _("Warning: no timestamps stored in the SER sequence.\n"));
172 	}
173 	return 0;
174 }
175 
ser_recompute_frame_count(struct ser_struct * ser_file)176 static int ser_recompute_frame_count(struct ser_struct *ser_file) {
177 	int frame_count_calculated;
178 	gint64 filesize = ser_file->filesize;
179 
180 	siril_log_message(_("Trying to fix broken SER file...\n"));
181 	gint64 frame_size = ser_file->image_width * ser_file->image_height;
182 	if (frame_size == 0)
183 		return 0;
184 
185 	if (ser_file->color_id == SER_RGB || ser_file->color_id == SER_BGR) {
186 		frame_size *= 3;  // Color images have twice as many samples
187 	}
188 
189 	if (ser_file->bit_pixel_depth > 8) {
190 		frame_size *= 2;  // Greater than 8-bit data has 2 bytes per pixel rather than one
191 	}
192 
193 	filesize -= SER_HEADER_LEN;  // Remove header size from file size
194 	frame_count_calculated = filesize / frame_size;
195 
196 	return frame_count_calculated;
197 }
198 
ser_read_header(struct ser_struct * ser_file)199 static int ser_read_header(struct ser_struct *ser_file) {
200 	char header[SER_HEADER_LEN];
201 
202 	if (!ser_file || ser_file->file == NULL)
203 		return -1;
204 
205 	/* Get file size */
206 	fseek64(ser_file->file, 0, SEEK_END);
207 	ser_file->filesize = ftell64(ser_file->file);
208 	fseek64(ser_file->file, 0, SEEK_SET);
209 	if (ser_file->filesize == -1) {
210 		perror("seek");
211 		return -1;
212 	}
213 
214 	/* Read header (size of 178) */
215 	if (SER_HEADER_LEN != fread(header, 1, sizeof header, ser_file->file)) {
216 		perror("fread");
217 		return -1;
218 	}
219 
220 	// modify this to support big endian
221 	memcpy(&ser_file->lu_id, header + 14, 28);	// read all integers
222 
223 	ser_file->lu_id = le32_to_cpu(ser_file->lu_id);
224 	ser_file->color_id = le32_to_cpu(ser_file->color_id);
225 	ser_file->little_endian = le32_to_cpu(ser_file->little_endian);
226 	ser_file->image_width = le32_to_cpu(ser_file->image_width);
227 	ser_file->image_height = le32_to_cpu(ser_file->image_height);
228 	ser_file->bit_pixel_depth = le32_to_cpu(ser_file->bit_pixel_depth);
229 	ser_file->frame_count = le32_to_cpu(ser_file->frame_count);
230 
231 	memcpy(&ser_file->date, header + 162, 8);
232 	memcpy(&ser_file->date_utc, header + 170, 8);
233 
234 	ser_file->date = le64_to_cpu(ser_file->date);
235 	ser_file->date_utc = le64_to_cpu(ser_file->date_utc);
236 
237 	// strings
238 	ser_file->file_id = g_strndup(header, 14);
239 
240 	memcpy(ser_file->observer, header + 42, 40);
241 	memcpy(ser_file->instrument, header + 82, 40);
242 	memcpy(ser_file->telescope, header + 122, 40);
243 
244 	/* internal representations of header data */
245 	if (ser_file->bit_pixel_depth <= 8)
246 		ser_file->byte_pixel_depth = SER_PIXEL_DEPTH_8;
247 	else ser_file->byte_pixel_depth = SER_PIXEL_DEPTH_16;
248 
249 	if (ser_file->color_id == SER_RGB || ser_file->color_id == SER_BGR)
250 		ser_file->number_of_planes = 3;
251 	else
252 		ser_file->number_of_planes = 1;
253 
254 	/* In some cases, oacapture, firecapture, ... crash before writing
255 	 * frame_count data. Here we try to get the calculated frame count
256 	 * which has not been written in the header. Then we fix the SER file
257 	 */
258 	if (ser_file->frame_count == 0) {
259 		ser_file->frame_count = ser_recompute_frame_count(ser_file);
260 
261 		if (ser_file->frame_count > 0) {
262 			if (ser_write_header(ser_file) == 0)
263 				siril_log_message(_("SER file has been fixed...\n"));
264 		}
265 	}
266 
267 	ser_read_timestamp(ser_file);
268 
269 	return 0;
270 }
271 
ser_write_timestamps(struct ser_struct * ser_file)272 static int ser_write_timestamps(struct ser_struct *ser_file) {
273 	int i;
274 	gint64 frame_size;
275 
276 	if (!ser_file->frame_count || ser_file->image_width <= 0 ||
277 			ser_file->image_height <= 0 || ser_file->byte_pixel_depth <= 0 ||
278 			!ser_file->number_of_planes)
279 		return -1;
280 
281 	if (ser_file->ts) {
282 		// Seek to start of timestamps
283 		frame_size = ser_file->image_width * ser_file->image_height
284 			* ser_file->number_of_planes;
285 		gint64 offset = SER_HEADER_LEN + frame_size *
286 			(gint64)ser_file->byte_pixel_depth * (gint64)ser_file->frame_count;
287 
288 		for (i = 0; i < ser_file->frame_count; i++) {
289 			guint64 ts;
290 
291 			if (i >= ser_file->ts_alloc)
292 				break;
293 			if ((gint64)-1 == fseek64(ser_file->file, offset+(i*8), SEEK_SET)) {
294 				return -1;
295 			}
296 
297 			ts = cpu_to_le64(ser_file->ts[i]);
298 
299 			if (8 != fwrite(&ts, 1, 8, ser_file->file)) {
300 				perror("write timestamps:");
301 				return -1;
302 			}
303 		}
304 	}
305 	return 0;
306 }
307 
308 /* (over)write the header of the opened file on the disk */
ser_write_header(struct ser_struct * ser_file)309 static int ser_write_header(struct ser_struct *ser_file) {
310 	char header[SER_HEADER_LEN];
311 	struct ser_struct ser_file_le;
312 
313 	if (!ser_file || ser_file->file == NULL)
314 		return -1;
315 	if ((gint64) -1 == fseek64(ser_file->file, 0, SEEK_SET)) {
316 		perror("seek");
317 		return -1;
318 	}
319 
320 	memcpy(&ser_file_le, ser_file, sizeof(struct ser_struct));
321 
322 	ser_file_le.lu_id = cpu_to_le32(ser_file_le.lu_id);
323 	ser_file_le.color_id = cpu_to_le32(ser_file_le.color_id);
324 	ser_file_le.little_endian = cpu_to_le32(ser_file_le.little_endian);
325 	ser_file_le.image_width = cpu_to_le32(ser_file_le.image_width);
326 	ser_file_le.image_height = cpu_to_le32(ser_file_le.image_height);
327 	ser_file_le.bit_pixel_depth = cpu_to_le32(ser_file_le.bit_pixel_depth);
328 	ser_file_le.frame_count = cpu_to_le32(ser_file_le.frame_count);
329 	ser_file_le.date = cpu_to_le64(ser_file_le.date);
330 	ser_file_le.date_utc = cpu_to_le64(ser_file_le.date_utc);
331 
332 	memset(header, 0, sizeof(header));
333 	memcpy(header, ser_file_le.file_id, 14);
334 	memcpy(header + 14, &ser_file_le.lu_id, 28);
335 	memcpy(header + 42, ser_file_le.observer, 40);
336 	memcpy(header + 82, ser_file_le.instrument, 40);
337 	memcpy(header + 122, ser_file_le.telescope, 40);
338 	memcpy(header + 162, &ser_file_le.date, 8);
339 	memcpy(header + 170, &ser_file_le.date_utc, 8);
340 
341 	if (sizeof(header) != fwrite(header, 1, sizeof(header), ser_file->file)) {
342 		perror("write");
343 		return 1;
344 	}
345 	return 0;
346 }
347 
348 /* populate fields that are not already set in ser_create_file */
ser_write_header_from_fit(struct ser_struct * ser_file,fits * fit)349 static int ser_write_header_from_fit(struct ser_struct *ser_file, fits *fit) {
350 	ser_file->image_width = fit->rx;
351 	ser_file->image_height = fit->ry;
352 	fprintf(stdout, "setting SER image size as %dx%d\n", fit->rx, fit->ry);
353 	// already managed during creation for monochrome formats
354 	if (fit->naxes[2] == 3) {
355 		ser_file->color_id = SER_RGB;
356 	}
357 	if (ser_file->color_id == SER_RGB)
358 		ser_file->number_of_planes = 3;
359 	else {
360 		if (!g_strcmp0(fit->bayer_pattern, "RGGB")) {
361 			ser_file->color_id = SER_BAYER_RGGB;
362 		} else if (!g_strcmp0(fit->bayer_pattern, "BGGR")) {
363 			ser_file->color_id = SER_BAYER_BGGR;
364 		} else if (!g_strcmp0(fit->bayer_pattern, "GBRG")) {
365 			ser_file->color_id = SER_BAYER_GBRG;
366 		} else if (!g_strcmp0(fit->bayer_pattern, "GRBG")) {
367 			ser_file->color_id = SER_BAYER_GRBG;
368 		}
369 		ser_file->number_of_planes = 1;
370 	}
371 
372 	if (fit->bitpix == BYTE_IMG) {
373 		ser_file->byte_pixel_depth = SER_PIXEL_DEPTH_8;
374 		ser_file->bit_pixel_depth = 8;
375 	} else if (fit->bitpix == USHORT_IMG || fit->bitpix == SHORT_IMG) {
376 		ser_file->byte_pixel_depth = SER_PIXEL_DEPTH_16;
377 		ser_file->bit_pixel_depth = 16;
378 	} else {
379 		siril_log_message(_("Writing a 32-bit image to SER files is not supported.\n"));
380 		return 1;
381 	}
382 	if (fit->instrume[0] != 0) {
383 		memset(ser_file->instrument, 0, 40);
384 		memcpy(ser_file->instrument, fit->instrume, 40);
385 	}
386 	if (fit->observer[0] != 0) {
387 		memset(ser_file->observer, 0, 40);
388 		memcpy(ser_file->observer, fit->observer, 40);
389 	}
390 	if (fit->instrume[0] != 0) {
391 		memset(ser_file->telescope, 0, 40);
392 		memcpy(ser_file->telescope, fit->telescop, 40);
393 	}
394 
395 	if (fit->date_obs)
396 		ser_file->date = date_time_to_ser_timestamp(fit->date_obs);
397 	return 0;
398 }
399 
get_SER_Bayer_Pattern(ser_color pattern)400 static int get_SER_Bayer_Pattern(ser_color pattern) {
401 	switch (pattern) {
402 	case SER_BAYER_RGGB:
403 		return BAYER_FILTER_RGGB;
404 	case SER_BAYER_BGGR:
405 		return BAYER_FILTER_BGGR;
406 	case SER_BAYER_GBRG:
407 		return BAYER_FILTER_GBRG;
408 	case SER_BAYER_GRBG:
409 		return BAYER_FILTER_GRBG;
410 	default:
411 		return BAYER_FILTER_NONE;
412 	}
413 }
414 
415 /* once a buffer (data) has been acquired from the file, with frame_size pixels
416  * read in it, depending on ser_file's endianess and pixel depth, data is
417  * reorganized to match Siril's data format . */
ser_manage_endianess_and_depth(struct ser_struct * ser_file,WORD * data,gint64 frame_size)418 static void ser_manage_endianess_and_depth(struct ser_struct *ser_file,
419 		WORD *data, gint64 frame_size) {
420 	int i;
421 	if (ser_file->byte_pixel_depth == SER_PIXEL_DEPTH_8) {
422 		// inline conversion to 16 bit
423 		for (i = frame_size - 1; i >= 0; i--)
424 			data[i] = (WORD) (((BYTE*)data)[i]);
425 	} else if (ser_file->little_endian == SER_BIG_ENDIAN) {
426 		// inline conversion
427 		for (i = frame_size - 1; i >= 0; i--) {
428 			data[i] = be16_to_cpu(data[i]);
429 		}
430 	} else if (ser_file->little_endian == SER_LITTLE_ENDIAN) {
431 		// inline conversion
432 		for (i = frame_size - 1; i >= 0; i--) {
433 			data[i] = le16_to_cpu(data[i]);
434 		}
435 	}
436 }
437 
ser_alloc_ts(struct ser_struct * ser_file,int frame_no)438 static int ser_alloc_ts(struct ser_struct *ser_file, int frame_no) {
439 	int retval = 0;
440 #ifdef _OPENMP
441 	omp_set_lock(&ser_file->ts_lock);
442 #endif
443 	if (ser_file->ts_alloc <= frame_no) {
444 		guint64 *new = realloc(ser_file->ts, (frame_no + 1) * 2 * sizeof(guint64));
445 		if (!new) {
446 			PRINT_ALLOC_ERR;
447 			retval = 1;
448 		} else {
449 			ser_file->ts = new;
450 			ser_file->ts_alloc = (frame_no + 1) * 2;
451 		}
452 	}
453 #ifdef _OPENMP
454 	omp_unset_lock(&ser_file->ts_lock);
455 #endif
456 	return retval;
457 }
458 
459 /*
460  * Public functions
461  */
462 
ser_is_cfa(struct ser_struct * ser_file)463 gboolean ser_is_cfa(struct ser_struct *ser_file) {
464 	return ser_file && (ser_file->color_id == SER_BAYER_RGGB ||
465 			ser_file->color_id == SER_BAYER_GRBG ||
466 			ser_file->color_id == SER_BAYER_GBRG ||
467 			ser_file->color_id == SER_BAYER_BGGR);
468 	// SER_BAYER_CYYM SER_BAYER_YCMY SER_BAYER_YMCY SER_BAYER_MYYC are not
469 	// supported yet so returning false for them here is good
470 }
471 
472 /* set the timestamps of the ser_file using a list of timestamps in string form */
ser_convertTimeStamp(struct ser_struct * ser_file,GSList * timestamp)473 void ser_convertTimeStamp(struct ser_struct *ser_file, GSList *timestamp) {
474 	int i = 0;
475 	if (ser_file->ts)
476 		free(ser_file->ts);
477 	ser_file->ts = calloc(sizeof(guint64), ser_file->frame_count);
478 	if (!ser_file->ts) {
479 		PRINT_ALLOC_ERR;
480 		return;
481 	}
482 	ser_file->ts_alloc = ser_file->frame_count;
483 
484 	GSList *t = timestamp;
485 	while (t && i < ser_file->frame_count) {
486 		guint64 utc = date_time_to_ser_timestamp((GDateTime *)t->data);
487 		t = t->next;
488 		memcpy(&ser_file->ts[i], &utc, sizeof(guint64));
489 		i++;
490 	}
491 }
492 
ser_display_info(struct ser_struct * ser_file)493 void ser_display_info(struct ser_struct *ser_file) {
494 	char *color = convert_color_id_to_char(ser_file->color_id);
495 
496 	fprintf(stdout, "=========== SER file info ==============\n");
497 	fprintf(stdout, "file id: %s\n", ser_file->file_id);
498 	fprintf(stdout, "lu id: %d\n", ser_file->lu_id);
499 	fprintf(stdout, "little endian: %d\n", ser_file->little_endian);
500 	fprintf(stdout, "sensor type: %s\n", color);
501 	fprintf(stdout, "image size: %d x %d (%d bits)\n", ser_file->image_width,
502 			ser_file->image_height, ser_file->bit_pixel_depth);
503 	fprintf(stdout, "frame count: %u\n", ser_file->frame_count);
504 	fprintf(stdout, "observer: %.40s\n", ser_file->observer);
505 	fprintf(stdout, "instrument: %.40s\n", ser_file->instrument);
506 	fprintf(stdout, "telescope: %.40s\n", ser_file->telescope);
507 	display_date(ser_file->date, "local time: ");
508 	display_date(ser_file->date_utc, "UTC time: ");
509 	fprintf(stdout, "fps: %.3lf\n", ser_file->fps);
510 	fprintf(stdout, "========================================\n");
511 }
512 
ser_end_write(struct ser_struct * ser_file,gboolean abort)513 static int ser_end_write(struct ser_struct *ser_file, gboolean abort) {
514 	int retval = 0;
515 	if (ser_file->writer) {
516 		retval = stop_writer(ser_file->writer, abort);
517 		ser_file->frame_count = ser_file->writer->frame_count;
518 		free(ser_file->writer);
519 		ser_file->writer = NULL;
520 	}
521 	return retval;
522 }
523 
ser_close_and_delete_file(struct ser_struct * ser_file)524 int ser_close_and_delete_file(struct ser_struct *ser_file) {
525 	if (ser_file == NULL) return -1;
526 	int retval = ser_end_write(ser_file, TRUE);
527 	char *filename = ser_file->filename;
528 	ser_file->filename = NULL;
529 	ser_close_file(ser_file); // closes, frees and zeroes
530 	siril_log_message(_("Removing failed SER file: %s\n"), filename);
531 	g_unlink(filename);
532 	free(filename);
533 	return retval;
534 }
535 
ser_write_and_close(struct ser_struct * ser_file)536 int ser_write_and_close(struct ser_struct *ser_file) {
537 	if (ser_file == NULL) return -1;
538 	int retval = ser_end_write(ser_file, FALSE);
539 	if (!ser_file->frame_count) {
540 		siril_log_color_message(_("The SER sequence is being created with no image in it.\n"), "red");
541 		ser_close_and_delete_file(ser_file);
542 		return -1;
543 	}
544 	ser_write_header(ser_file);	// writes the header
545 	ser_write_timestamps(ser_file);	// writes the trailer
546 	ser_close_file(ser_file);// closes, frees and zeroes
547 	return retval;
548 }
549 
550 /* ser_file must be allocated and initialised with ser_init_struct()
551  * the file is created with no image size, the first image added will set it. */
ser_create_file(const char * filename,struct ser_struct * ser_file,gboolean overwrite,struct ser_struct * copy_from)552 int ser_create_file(const char *filename, struct ser_struct *ser_file,
553 		gboolean overwrite, struct ser_struct *copy_from) {
554 	if (overwrite)
555 		g_unlink(filename);
556 	if ((ser_file->file = g_fopen(filename, "w+b")) == NULL) {
557 		perror("open SER file for creation");
558 		return 1;
559 	}
560 
561 	ser_file->filename = strdup(filename);
562 	ser_file->ts = NULL;
563 	ser_file->ts_alloc = 0;
564 	ser_file->fps = -1.0;
565 	ser_file->frame_count = 0;	// incremented on image add
566 
567 	if (copy_from) {
568 		memcpy(&ser_file->lu_id, &copy_from->lu_id, 12);
569 		memset(&ser_file->image_width, 0, 16);
570 		memcpy(&ser_file->date, &copy_from->date, 8);
571 		memcpy(&ser_file->date_utc, &copy_from->date_utc, 8);
572 		ser_file->file_id = strdup(copy_from->file_id);
573 		memcpy(ser_file->observer, copy_from->observer, 40);
574 		memcpy(ser_file->instrument, copy_from->instrument, 40);
575 		memcpy(ser_file->telescope, copy_from->telescope, 40);
576 		ser_file->byte_pixel_depth = copy_from->byte_pixel_depth;
577 		ser_file->number_of_planes = 0;	// used as an indicator of new SER
578 
579 		if (copy_from->ts && copy_from->frame_count > 0) {
580 			ser_file->ts = calloc(8, copy_from->frame_count);
581 			if (!ser_file->ts) {
582 				PRINT_ALLOC_ERR;
583 			}
584 			else ser_file->ts_alloc = copy_from->frame_count;
585 		}
586 		/* we write the header now, but it should be written again
587 		 * before closing in case the number of the image in the new
588 		 * SER changes from the copied SER */
589 		ser_write_header(ser_file);
590 	} else {	// new SER
591 		ser_file->file_id = strdup("LUCAM-RECORDER");
592 		ser_file->lu_id = 0;
593 		ser_file->color_id = SER_MONO;	// this is 0
594 		ser_file->little_endian = SER_LITTLE_ENDIAN; // what will it do on big endian machine?
595 		memset(ser_file->observer, 0, 40);
596 		memset(ser_file->instrument, 0, 40);
597 		memset(ser_file->telescope, 0, 40);
598 		memset(&ser_file->date, 0, 8);
599 		memset(&ser_file->date_utc, 0, 8);
600 		ser_file->number_of_planes = 0;	// used as an indicator of new SER
601 	}
602 #ifdef _OPENMP
603 	omp_init_lock(&ser_file->fd_lock);
604 	omp_init_lock(&ser_file->ts_lock);
605 #endif
606 	ser_file->writer = malloc(sizeof(struct seqwriter_data));
607 	ser_file->writer->write_image_hook = ser_write_image_for_writer;
608 	ser_file->writer->sequence = ser_file;
609 
610 	siril_log_message(_("Created SER file %s\n"), filename);
611 	start_writer(ser_file->writer, ser_file->frame_count);
612 	return 0;
613 }
614 
ser_write_image_for_writer(struct seqwriter_data * writer,fits * image,int index)615 static int ser_write_image_for_writer(struct seqwriter_data *writer, fits *image, int index) {
616 	struct ser_struct *ser_file = (struct ser_struct *)writer->sequence;
617 
618 	return ser_write_frame_from_fit_internal(ser_file, image, index);
619 }
620 
ser_open_file(const char * filename,struct ser_struct * ser_file)621 int ser_open_file(const char *filename, struct ser_struct *ser_file) {
622 	if (ser_file->file) {
623 		fprintf(stderr, "SER: file already opened, or badly closed\n");
624 		return -1;
625 	}
626 	ser_file->file = g_fopen(filename, "r+b"); // now we can fix broken file, so not O_RDONLY anymore
627 	if (ser_file->file == NULL) {
628 		perror("SER file open");
629 		return -1;
630 	}
631 	if (ser_read_header(ser_file)) {
632 		fprintf(stderr, "SER: reading header failed, closing file %s\n",
633 				filename);
634 		ser_close_file(ser_file);
635 		return -1;
636 	}
637 	ser_file->filename = strdup(filename);
638 
639 #ifdef _OPENMP
640 	omp_init_lock(&ser_file->fd_lock);
641 	omp_init_lock(&ser_file->ts_lock);
642 #endif
643 	return 0;
644 }
645 
ser_close_file(struct ser_struct * ser_file)646 int ser_close_file(struct ser_struct *ser_file) {
647 	int retval = 0;
648 	if (!ser_file)
649 		return -1;
650 	if (ser_file->file) {
651 		retval = fclose(ser_file->file);
652 		ser_file->file = NULL;
653 	}
654 	if (ser_file->file_id)
655 		free(ser_file->file_id);
656 	if (ser_file->ts)
657 		free(ser_file->ts);
658 	if (ser_file->filename)
659 		free(ser_file->filename);
660 #ifdef _OPENMP
661 	omp_destroy_lock(&ser_file->fd_lock);
662 	omp_destroy_lock(&ser_file->ts_lock);
663 #endif
664 	ser_init_struct(ser_file);
665 	return retval;
666 }
667 
ser_init_struct(struct ser_struct * ser_file)668 void ser_init_struct(struct ser_struct *ser_file) {
669 	g_assert(ser_file);
670 	memset(ser_file, 0, sizeof(struct ser_struct));
671 }
672 
ser_metadata_as_fits(struct ser_struct * ser_file,fits * fit)673 int ser_metadata_as_fits(struct ser_struct *ser_file, fits *fit) {
674 	ser_color type_ser = ser_file->color_id;
675 	if (!com.pref.debayer.open_debayer && type_ser != SER_RGB && type_ser != SER_BGR) {
676 		type_ser = SER_MONO;
677 	}
678 	switch (type_ser) {
679 	case SER_MONO:
680 		fit->naxis = 2;
681 		fit->naxes[2] = 1;
682 		break;
683 	case SER_BAYER_RGGB:
684 	case SER_BAYER_BGGR:
685 	case SER_BAYER_GBRG:
686 	case SER_BAYER_GRBG:
687 	case SER_BGR:
688 	case SER_RGB:
689 		fit->naxis = 3;
690 		fit->naxes[2] = 3;
691 		break;
692 	default:
693 		return 1;
694 	}
695 	fit->naxes[0] = fit->rx = ser_file->image_width;
696 	fit->naxes[1] = fit->ry = ser_file->image_height;
697 	fit->bitpix = (ser_file->byte_pixel_depth == SER_PIXEL_DEPTH_8) ? BYTE_IMG : USHORT_IMG;
698 	fit->orig_bitpix = fit->bitpix;
699 	fit->binning_x = fit->binning_y = 1;
700 	return 0;
701 }
702 
703 /* reads a frame on an already opened SER sequence.
704  * frame number starts at 0 */
ser_read_frame(struct ser_struct * ser_file,int frame_no,fits * fit,gboolean force_float,gboolean open_debayer)705 int ser_read_frame(struct ser_struct *ser_file, int frame_no, fits *fit, gboolean force_float, gboolean open_debayer) {
706 	int retval = 0, i, j, swap = 0;
707 	gint64 offset, frame_size;
708 	size_t read_size;
709 	WORD *olddata, *tmp;
710 	if (!ser_file || ser_file->file == NULL || !ser_file->number_of_planes ||
711 			!fit || frame_no < 0 || frame_no >= ser_file->frame_count)
712 		return -1;
713 
714 	frame_size = ser_file->image_width * ser_file->image_height *
715 			ser_file->number_of_planes;
716 	read_size = frame_size * ser_file->byte_pixel_depth;
717 
718 	olddata = fit->data;
719 	if ((fit->data = realloc(fit->data, frame_size * sizeof(WORD))) == NULL) {
720 		PRINT_ALLOC_ERR;
721 		if (olddata)
722 			free(olddata);
723 		return -1;
724 	}
725 
726 	offset = SER_HEADER_LEN	+ frame_size *
727 		(gint64)ser_file->byte_pixel_depth * (gint64)frame_no;
728 	/*fprintf(stdout, "offset is %lu (frame %d, %d pixels, %d-byte)\n", offset,
729 	 frame_no, frame_size, ser_file->pixel_bytedepth);*/
730 #ifdef _OPENMP
731 	omp_set_lock(&ser_file->fd_lock);
732 #endif
733 	if ((gint64)-1 == fseek64(ser_file->file, offset, SEEK_SET)) {
734 		perror("fseek in SER");
735 		retval = -1;
736 	} else {
737 		if (fread(fit->data, 1, read_size, ser_file->file) != read_size)
738 			retval = -1;
739 	}
740 #ifdef _OPENMP
741 	omp_unset_lock(&ser_file->fd_lock);
742 #endif
743 	if (retval)
744 		return -1;
745 
746 	ser_manage_endianess_and_depth(ser_file, fit->data, frame_size);
747 
748 	fit->bitpix = (ser_file->byte_pixel_depth == SER_PIXEL_DEPTH_8) ? BYTE_IMG : USHORT_IMG;
749 	fit->orig_bitpix = fit->bitpix;
750 	fit->binning_x = fit->binning_y = 1;
751 
752 	/* If opening images debayered is not activated, read the image as CFA monochrome */
753 	ser_color type_ser = ser_file->color_id;
754 	if (!open_debayer && type_ser != SER_RGB && type_ser != SER_BGR) {
755 		const gchar *pattern = NULL;
756 		type_ser = SER_MONO;
757 
758 		if (com.pref.debayer.use_bayer_header) {
759 			if (ser_file->color_id == SER_BAYER_RGGB)
760 				pattern = "RGGB";
761 			else if (ser_file->color_id == SER_BAYER_BGGR)
762 				pattern = "BGGR";
763 			else if (ser_file->color_id == SER_BAYER_GBRG)
764 				pattern = "GBRG";
765 			else if (ser_file->color_id == SER_BAYER_GRBG)
766 				pattern = "GRBG";
767 		} else {
768 			pattern = filter_pattern[com.pref.debayer.bayer_pattern];
769 		}
770 		if (pattern)
771 			strcpy(fit->bayer_pattern, pattern);
772 		g_snprintf(fit->row_order, FLEN_VALUE, "%s", "BOTTOM-UP");
773 	}
774 
775 	switch (type_ser) {
776 	case SER_MONO:
777 		fit->naxis = 2;
778 		fit->naxes[0] = fit->rx = ser_file->image_width;
779 		fit->naxes[1] = fit->ry = ser_file->image_height;
780 		fit->naxes[2] = 1;
781 		fit->pdata[RLAYER] = fit->data;
782 		fit->pdata[GLAYER] = fit->data;
783 		fit->pdata[BLAYER] = fit->data;
784 		break;
785 	case SER_BAYER_RGGB:
786 	case SER_BAYER_BGGR:
787 	case SER_BAYER_GBRG:
788 	case SER_BAYER_GRBG:
789 		fit->naxes[0] = fit->rx = ser_file->image_width;
790 		fit->naxes[1] = fit->ry = ser_file->image_height;
791 		fit->naxes[2] = 3;
792 		/* Get Bayer informations from header if available */
793 		sensor_pattern sensortmp;
794 		sensortmp = com.pref.debayer.bayer_pattern;
795 		if (com.pref.debayer.use_bayer_header) {
796 			sensor_pattern bayer;
797 			bayer = get_SER_Bayer_Pattern(type_ser);
798 			if (bayer != com.pref.debayer.bayer_pattern) {
799 				if (bayer == BAYER_FILTER_NONE  && !user_warned) {
800 					siril_log_color_message(_("No Bayer pattern found in the header file.\n"), "red");
801 				}
802 				else {
803 					if (!user_warned) {
804 						siril_log_color_message(_("Bayer pattern found in header (%s) is different"
805 								" from Bayer pattern in settings (%s). Overriding settings.\n"),
806 								"salmon", filter_pattern[bayer], filter_pattern[com.pref.debayer.bayer_pattern]);
807 					}
808 					com.pref.debayer.bayer_pattern = bayer;
809 				}
810 				user_warned = TRUE;
811 			}
812 		}
813 		/* for performance consideration (and many others) we force the interpolation algorithm
814 		 * to be BAYER_BILINEAR
815 		 */
816 		debayer(fit, BAYER_RCD, com.pref.debayer.bayer_pattern);
817 		com.pref.debayer.bayer_pattern = sensortmp;
818 		break;
819 	case SER_BGR:
820 		swap = 2;
821 		/* no break */
822 	case SER_RGB:
823 		tmp = malloc(frame_size * sizeof(WORD));
824 		if (!tmp) {
825 			PRINT_ALLOC_ERR;
826 			return -1;
827 		}
828 		memcpy(tmp, fit->data, sizeof(WORD) * frame_size);
829 		fit->naxes[0] = fit->rx = ser_file->image_width;
830 		fit->naxes[1] = fit->ry = ser_file->image_height;
831 		fit->naxes[2] = 3;
832 		fit->naxis = 3;
833 		fit->pdata[RLAYER] = fit->data;
834 		fit->pdata[GLAYER] = fit->data + fit->rx * fit->ry;
835 		fit->pdata[BLAYER] = fit->data + fit->rx * fit->ry * 2;
836 		for (i = 0, j = 0; j < fit->rx * fit->ry; i += 3, j++) {
837 			fit->pdata[0 + swap][j] = tmp[i + RLAYER];
838 			fit->pdata[1][j] = tmp[i + GLAYER];
839 			fit->pdata[2 - swap][j] = tmp[i + BLAYER];
840 		}
841 		free(tmp);
842 		break;
843 	case SER_BAYER_CYYM:
844 	case SER_BAYER_YCMY:
845 	case SER_BAYER_YMCY:
846 	case SER_BAYER_MYYC:
847 	default:
848 		siril_log_message(_("This type of Bayer pattern is not handled yet.\n"));
849 		return -1;
850 	}
851 
852 	/* copy the SER timestamp to the fits */
853 	if (ser_file->ts) {
854 		GDateTime *timestamp = ser_timestamp_to_date_time(ser_file->ts[frame_no]);
855 		if (timestamp) {
856 			if (fit->date_obs) {
857 				g_date_time_unref(fit->date_obs);
858 			}
859 			fit->date_obs = timestamp;
860 		}
861 	}
862 
863 	if (force_float) {
864 		float *newbuf;
865 		size_t pixel_count = fit->naxes[0] * fit->naxes[1] * fit->naxes[2];
866 		if (fit->bitpix == BYTE_IMG)
867 			newbuf = ushort8_buffer_to_float(fit->data, pixel_count);
868 		else newbuf = ushort_buffer_to_float(fit->data, pixel_count);
869 		fit_replace_buffer(fit, newbuf, DATA_FLOAT);
870 	}
871 
872 	fits_flip_top_to_bottom(fit);
873 	fit->top_down = FALSE;
874 
875 	return 0;
876 }
877 
878 /* multi-type cropping, works in constant space if needed */
879 #define crop_area_from_lines(BUFFER_TYPE) { \
880 	int x, y, src, dst = 0; \
881 	BUFFER_TYPE *inbuf = (BUFFER_TYPE *)read_buffer; \
882 	BUFFER_TYPE *out = (BUFFER_TYPE *)outbuf; \
883 	for (y = 0; y < area->h; y++) { \
884 		src = y * ser_file->image_width + area->x; \
885 		for (x = 0; x < area->w; x++) \
886 			out[dst++] = inbuf[src++]; \
887 	} \
888 }
889 
890 /* multi-type RGB reordering, works in constant space if needed */
891 #define crop_area_from_color_lines(BUFFER_TYPE) { \
892 	int x, y, src, dst = 0; \
893 	BUFFER_TYPE *inbuf = (BUFFER_TYPE *)read_buffer; \
894 	BUFFER_TYPE *out = (BUFFER_TYPE *)outbuf; \
895 	int color_offset; \
896 	if (ser_file->color_id == SER_BGR) { \
897 		color_offset = 2 - layer; \
898 	} else { \
899 		color_offset = layer; \
900 	} \
901 	for (y = 0; y < area->h; y++) { \
902 		src = (y * ser_file->image_width + area->x) * 3 + color_offset; \
903 		for (x = 0; x < area->w; x++) { \
904 			out[dst++] = inbuf[src]; \
905 			src += 3; \
906 		} \
907 	} \
908 }
909 
910 /* reading an area from a SER frame, for one layer only, either layer == -1 for
911  * monochrome and debayer, or 0-2 for color.
912  * the area is read in one read(2) call to limit the number of syscalls, for a
913  * full-width area of same height as requested, then cropped horizontally to
914  * get the requested area.
915  * This function is the first one of siril to handle two different data types
916  * (BYTE and WORD) for the same algorithm! This uses VIPS-style macros.
917  * */
read_area_from_image(struct ser_struct * ser_file,const int frame_no,WORD * outbuf,const rectangle * area,const int layer)918 static int read_area_from_image(struct ser_struct *ser_file, const int frame_no,
919 		WORD *outbuf, const rectangle *area, const int layer) {
920 	gint64 offset, frame_size;
921 	int retval = 0;
922 	WORD *read_buffer;
923 	size_t read_size = ser_file->image_width * area->h * ser_file->byte_pixel_depth;
924 	if (layer != -1) read_size *= 3;
925 	if (layer != -1 || area->w != ser_file->image_width) {
926 		// allocated space is probably not enough to
927 		// store whole lines or RGB data
928 		read_buffer = malloc(read_size);
929 		if (!read_buffer) {
930 			PRINT_ALLOC_ERR;
931 			return -1;
932 		}
933 	}
934 	else read_buffer = outbuf;
935 
936 	frame_size = ser_file->image_width * ser_file->image_height *
937 		ser_file->number_of_planes * ser_file->byte_pixel_depth;
938 
939 #ifdef _OPENMP
940 	omp_set_lock(&ser_file->fd_lock);
941 #endif
942 	// we read the full-stride rectangle that contains the requested area
943 	offset = SER_HEADER_LEN + frame_size * frame_no +	// requested frame
944 		area->y * ser_file->image_width *
945 		ser_file->byte_pixel_depth * (layer != -1 ? 3 : 1);	// requested area
946 
947 	if ((gint64)-1 == fseek64(ser_file->file, offset, SEEK_SET)) {
948 		perror("fseek in SER");
949 		retval = -1;
950 	} else {
951 		if (fread(read_buffer, 1, read_size, ser_file->file) != read_size) {
952 			retval = -1;
953 		}
954 	}
955 #ifdef _OPENMP
956 	omp_unset_lock(&ser_file->fd_lock);
957 #endif
958 	if (!retval) {
959 		if (area->w != ser_file->image_width) {
960 			// here we crop x-wise our area
961 			if (layer != -1) {
962 				/* reorder the RGBRGB to RRGGBB and crop */
963 				if (ser_file->byte_pixel_depth == SER_PIXEL_DEPTH_8) {
964 					crop_area_from_color_lines(BYTE);
965 				} else if (ser_file->byte_pixel_depth == SER_PIXEL_DEPTH_16) {
966 					crop_area_from_color_lines(WORD);
967 				}
968 			} else {
969 				/* just crop */
970 				if (ser_file->byte_pixel_depth == SER_PIXEL_DEPTH_8) {
971 					crop_area_from_lines(BYTE);
972 				} else if (ser_file->byte_pixel_depth == SER_PIXEL_DEPTH_16) {
973 					crop_area_from_lines(WORD);
974 				}
975 			}
976 		} else if (layer != -1) {
977 			/* just reorder RGB data, the crop function works too */
978 			if (ser_file->byte_pixel_depth == SER_PIXEL_DEPTH_8) {
979 				crop_area_from_color_lines(BYTE);
980 			} else if (ser_file->byte_pixel_depth == SER_PIXEL_DEPTH_16) {
981 				crop_area_from_color_lines(WORD);
982 			}
983 		}
984 	}
985 	if (layer != -1 || area->w != ser_file->image_width)
986 		free(read_buffer);
987 	return retval;
988 }
989 
990 /* read an area of an image in an opened SER sequence */
ser_read_opened_partial(struct ser_struct * ser_file,int layer,int frame_no,WORD * buffer,const rectangle * area)991 int ser_read_opened_partial(struct ser_struct *ser_file, int layer,
992 		int frame_no, WORD *buffer, const rectangle *area) {
993 	int xoffset, yoffset, x, y;
994 	ser_color type_ser;
995 	WORD *rawbuf, *demosaiced_buf;
996 	rectangle debayer_area, image_area;
997 	sensor_pattern sensortmp;
998 
999 	if (!ser_file || ser_file->file == NULL || frame_no < 0
1000 			|| frame_no >= ser_file->frame_count)
1001 		return -1;
1002 
1003 	type_ser = ser_file->color_id;
1004 	if (!com.pref.debayer.open_debayer &&
1005 			type_ser != SER_RGB && type_ser != SER_BGR)
1006 		type_ser = SER_MONO;
1007 
1008 	switch (type_ser) {
1009 	case SER_MONO:
1010 		if (read_area_from_image(ser_file, frame_no, buffer, area, -1))
1011 			return -1;
1012 		ser_manage_endianess_and_depth(ser_file, buffer, area->w * area->h);
1013 		break;
1014 
1015 	case SER_BAYER_RGGB:
1016 	case SER_BAYER_BGGR:
1017 	case SER_BAYER_GBRG:
1018 	case SER_BAYER_GRBG:
1019 		/* SER v2: RGB images obtained from demosaicing.
1020 		 * Original is monochrome, we demosaic it in an area slightly larger than the
1021 		 * requested area, giving 3 channels in form of RGBRGBRGB buffers, and finally
1022 		 * we extract one of the three channels and crop it to the requested area. */
1023 
1024 		/* Get Bayer informations from header if available */
1025 		sensortmp = com.pref.debayer.bayer_pattern;
1026 		if (com.pref.debayer.use_bayer_header) {
1027 			sensor_pattern bayer;
1028 			bayer = get_SER_Bayer_Pattern(type_ser);
1029 			if (bayer != com.pref.debayer.bayer_pattern) {
1030 				if (bayer == BAYER_FILTER_NONE && !user_warned) {
1031 					siril_log_color_message(_("No Bayer pattern found in the header file.\n"), "red");
1032 				}
1033 				else {
1034 					if (!user_warned) {
1035 						siril_log_color_message(_("Bayer pattern found in header (%s) is different"
1036 								" from Bayer pattern in settings (%s). Overriding settings.\n"),
1037 								"salmon", filter_pattern[bayer], filter_pattern[com.pref.debayer.bayer_pattern]);
1038 					}
1039 					com.pref.debayer.bayer_pattern = bayer;
1040 				}
1041 				user_warned = TRUE;
1042 			}
1043 		}
1044 		if (layer < 0 || layer >= 3) {
1045 			siril_log_message(_("For a demosaiced image, layer has to be R, G or B (0 to 2).\n"));
1046 			return -1;
1047 		}
1048 
1049 		image_area = (rectangle) { .x = 0, .y = 0,
1050 			.w = ser_file->image_width, .h = ser_file->image_height };
1051 		get_debayer_area(area, &debayer_area, &image_area, &xoffset, &yoffset);
1052 
1053 		// allocating a buffer for WORD because it's going to be converted in-place
1054 		rawbuf = malloc(debayer_area.w * debayer_area.h * sizeof(WORD));
1055 		if (!rawbuf) {
1056 			PRINT_ALLOC_ERR;
1057 			return -1;
1058 		}
1059 		if (read_area_from_image(ser_file, frame_no, rawbuf, &debayer_area, -1)) {
1060 			free(rawbuf);
1061 			return -1;
1062 		}
1063 		ser_manage_endianess_and_depth(ser_file, rawbuf, debayer_area.w * debayer_area.h);
1064 
1065 		/* for performance consideration (and many others) we force the interpolation algorithm
1066 		 * to be BAYER_BILINEAR
1067 		 */
1068 		demosaiced_buf = debayer_buffer(rawbuf, &debayer_area.w,
1069 				&debayer_area.h, BAYER_BILINEAR, com.pref.debayer.bayer_pattern, ser_file->bit_pixel_depth);
1070 		free(rawbuf);
1071 		if (!demosaiced_buf)
1072 			return -1;
1073 
1074 		/* area is the destination area.
1075 		 * debayer_area is the demosaiced buf area.
1076 		 * xoffset and yoffset are the x,y offsets of area in the debayer area.
1077 		 */
1078         const int nbpixels = debayer_area.w * debayer_area.h;
1079 		for (y = 0; y < area->h; y++) {
1080 			for (x = 0; x < area->w; x++) {
1081 				buffer[y*area->w + x] = demosaiced_buf[layer * nbpixels + (yoffset+y)*debayer_area.w + xoffset+x];
1082 			}
1083 		}
1084 
1085 		free(demosaiced_buf);
1086 		com.pref.debayer.bayer_pattern = sensortmp;
1087 		break;
1088 
1089 	case SER_BGR:
1090 	case SER_RGB:
1091 		g_assert(ser_file->number_of_planes == 3);
1092 		if (read_area_from_image(ser_file, frame_no, buffer, area, layer))
1093 			return -1;
1094 		ser_manage_endianess_and_depth(ser_file, buffer, area->w * area->h);
1095 		break;
1096 	default:
1097 		siril_log_message(_("This type of Bayer pattern is not handled yet.\n"));
1098 		return -1;
1099 	}
1100 
1101 	return 0;
1102 }
1103 
ser_read_opened_partial_fits(struct ser_struct * ser_file,int layer,int frame_no,fits * fit,const rectangle * area)1104 int ser_read_opened_partial_fits(struct ser_struct *ser_file, int layer,
1105 		int frame_no, fits *fit, const rectangle *area) {
1106 	if (new_fit_image(&fit, area->w, area->h, 1, DATA_USHORT))
1107 		return -1;
1108 	fit->top_down = TRUE;
1109 	if (ser_file->ts) {
1110 		GDateTime *timestamp = ser_timestamp_to_date_time(ser_file->ts[frame_no]);
1111 		if (timestamp) {
1112 			if (fit->date_obs) {
1113 				g_date_time_unref(fit->date_obs);
1114 			}
1115 			fit->date_obs = timestamp;
1116 		}
1117 	}
1118 	return ser_read_opened_partial(ser_file, layer, frame_no, fit->pdata[0], area);
1119 }
1120 
1121 // public function for writing an image to the file, calls the writer
ser_write_frame_from_fit(struct ser_struct * ser_file,fits * fit,int frame_no)1122 int ser_write_frame_from_fit(struct ser_struct *ser_file, fits *fit, int frame_no) {
1123 	return seqwriter_append_write(ser_file->writer, fit, frame_no);
1124 }
1125 
1126 // internal function, called by the writer hook ser_write_image_for_writer()
1127 // frame_no should always be the next image, or frame_count
ser_write_frame_from_fit_internal(struct ser_struct * ser_file,fits * fit,int frame_no)1128 static int ser_write_frame_from_fit_internal(struct ser_struct *ser_file, fits *fit, int frame_no) {
1129 	int pixel, plane, dest;
1130 	int ret, retval = 0;
1131 	gint64 offset, frame_size;
1132 	BYTE *data8 = NULL;			// for 8-bit files
1133 	WORD *data16 = NULL;		// for 16-bit files
1134 
1135 	if (!ser_file || ser_file->file == NULL || !fit)
1136 		return -1;
1137 	if (ser_file->number_of_planes == 0) {
1138 		// adding first frame of a new sequence, use it to populate the header
1139 		if (ser_write_header_from_fit(ser_file, fit)) {
1140 			return 1;
1141 		}
1142 	}
1143 	if (fit->rx != ser_file->image_width || fit->ry != ser_file->image_height) {
1144 		siril_log_message(_("Trying to add an image of different size in a SER\n"));
1145 		return 1;
1146 	}
1147 
1148 	fits_flip_top_to_bottom(fit);
1149 	frame_size = ser_file->image_width * ser_file->image_height *
1150 		ser_file->number_of_planes;
1151 
1152 	offset = SER_HEADER_LEN	+ frame_size *
1153 			(gint64)ser_file->byte_pixel_depth * (gint64)frame_no;
1154 
1155 	if (ser_file->byte_pixel_depth == SER_PIXEL_DEPTH_8) {
1156 		data8 = malloc(frame_size * ser_file->byte_pixel_depth);
1157 		if (!data8) {
1158 			PRINT_ALLOC_ERR;
1159 			return -1;
1160 		}
1161 	} else {
1162 		data16 = malloc(frame_size * ser_file->byte_pixel_depth);
1163 		if (!data16) {
1164 			PRINT_ALLOC_ERR;
1165 			return -1;
1166 		}
1167 	}
1168 
1169 	for (plane = 0; plane < ser_file->number_of_planes; plane++) {
1170 		dest = plane;
1171 		for (pixel = 0; pixel < ser_file->image_width * ser_file->image_height;
1172 				pixel++) {
1173 			if (ser_file->byte_pixel_depth == SER_PIXEL_DEPTH_8)
1174 				data8[dest] = round_to_BYTE(fit->pdata[plane][pixel]);
1175 			else {
1176 				if (ser_file->little_endian == SER_BIG_ENDIAN)
1177 					data16[dest] = (fit->pdata[plane][pixel] >> 8 | fit->pdata[plane][pixel] << 8);
1178 				else
1179 					data16[dest] = fit->pdata[plane][pixel];
1180 			}
1181 			dest += ser_file->number_of_planes;
1182 		}
1183 	}
1184 
1185 #ifdef _OPENMP
1186 	omp_set_lock(&ser_file->fd_lock);
1187 #endif
1188 	if ((gint64)-1 == fseek64(ser_file->file, offset, SEEK_SET)) {
1189 #ifdef _OPENMP
1190 		omp_unset_lock(&ser_file->fd_lock);
1191 #endif
1192 		perror("seek");
1193 		retval = -1;
1194 		goto free_and_quit;
1195 	}
1196 
1197 	if (ser_file->byte_pixel_depth == SER_PIXEL_DEPTH_8) {
1198 		ret = fwrite(data8, 1, frame_size * ser_file->byte_pixel_depth, ser_file->file);
1199 #ifdef _OPENMP
1200 		omp_unset_lock(&ser_file->fd_lock);
1201 #endif
1202 		if (ret != frame_size * ser_file->byte_pixel_depth) {
1203 			perror("write image in SER");
1204 			retval = 1;
1205 			goto free_and_quit;
1206 		}
1207 	} else {
1208 		ret = fwrite(data16, 1, frame_size * ser_file->byte_pixel_depth, ser_file->file);
1209 #ifdef _OPENMP
1210 		omp_unset_lock(&ser_file->fd_lock);
1211 #endif
1212 		if (ret != frame_size * ser_file->byte_pixel_depth) {
1213 			perror("write image in SER");
1214 			retval = 1;
1215 			goto free_and_quit;
1216 		}
1217 	}
1218 
1219 #ifdef _OPENMP
1220 #pragma omp atomic
1221 #endif
1222 	ser_file->frame_count++;
1223 
1224 	if (fit->date_obs && !ser_alloc_ts(ser_file, frame_no)) {
1225 		guint64 utc;
1226 		utc = date_time_to_ser_timestamp(fit->date_obs);
1227 		ser_file->ts[frame_no] = utc;
1228 	}
1229 
1230 free_and_quit:
1231 	if (data8) free(data8);
1232 	if (data16) free(data16);
1233 	return retval;
1234 }
1235 
ser_compute_file_size(struct ser_struct * ser_file,int nb_frames)1236 gint64 ser_compute_file_size(struct ser_struct *ser_file, int nb_frames) {
1237 	gint64 frame_size, size = ser_file->filesize;
1238 
1239 	if (nb_frames != ser_file->frame_count) {
1240 		frame_size = (size - SER_HEADER_LEN) / ser_file->frame_count;
1241 		size = SER_HEADER_LEN + frame_size * nb_frames;
1242 	}
1243 	return size;
1244 }
1245 
import_metadata_from_serfile(struct ser_struct * ser_file,fits * to)1246 int import_metadata_from_serfile(struct ser_struct *ser_file, fits *to) {
1247 	strncpy(to->instrume, ser_file->instrument, FLEN_VALUE);
1248 	strncpy(to->observer, ser_file->observer, FLEN_VALUE);
1249 	strncpy(to->telescop, ser_file->telescope, FLEN_VALUE);
1250 	return 0;
1251 }
1252 
free_preview_data(guchar * pixels,gpointer data)1253 static GdkPixbufDestroyNotify free_preview_data(guchar *pixels, gpointer data) {
1254 	free(pixels);
1255 	free(data);
1256 	return FALSE;
1257 }
1258 
1259 /**
1260  * Create a monochrome preview (only the first channel is displayed) of a SER file in a GdkPixbuf.
1261  * @param filename
1262  * @return a GdkPixbuf containing the preview or NULL
1263  */
get_thumbnail_from_ser(char * filename,gchar ** descr)1264 GdkPixbuf* get_thumbnail_from_ser(char *filename, gchar **descr) {
1265 	GdkPixbuf *pixbuf = NULL;
1266 	int MAX_SIZE = com.pref.thumbnail_size;
1267 	gchar *description = NULL;
1268 	int i, j, k, l, N, M;
1269 	int w, h, pixScale, Ws, Hs, n_channels, n_frames, bit;
1270 	int sz;
1271 	struct ser_struct ser;
1272 	fits fit = { 0 };
1273 
1274 	ser_init_struct(&ser);
1275 	if (ser_open_file(filename, &ser)) {
1276 		return NULL;
1277 	}
1278 	float *pix = malloc(MAX_SIZE * sizeof(float));
1279 	float *ima_data = NULL, *ptr, byte, n, m, max, min, wd, avr;
1280 	guchar *pixbuf_data = NULL, *pptr;
1281 
1282 	w = ser.image_width;
1283 	h = ser.image_height;
1284 	sz = w * h;
1285 	ima_data = malloc(sz * sizeof(float));
1286 	pixbuf_data = malloc(3 * MAX_SIZE * MAX_SIZE * sizeof(guchar));
1287 
1288 	/* here no need to debayer, for performance purposes
1289 	 * we just display monochrome display */
1290 	ser_read_frame(&ser, 0, &fit, FALSE, FALSE);
1291 
1292 	for (i = 0; i < sz; i++) {
1293 		ima_data[i + 0] = (float)fit.pdata[RLAYER][i];
1294 	}
1295 
1296 	i = (int) ceil((float) w / MAX_SIZE);
1297 	j = (int) ceil((float) h / MAX_SIZE);
1298 	pixScale = (i > j) ? i : j;	// picture scale factor
1299 	if (pixScale == 0) return NULL;
1300 	Ws = w / pixScale; 			// picture width in pixScale blocks
1301 	Hs = h / pixScale; 			// -//- height pixScale
1302 
1303 	n_frames = ser.frame_count;
1304 	bit = ser.bit_pixel_depth;
1305 
1306 	switch (ser.color_id) {
1307 	case SER_MONO:
1308 		n_channels = 1;
1309 		break;
1310 	default:
1311 		n_channels = 3;
1312 	}
1313 
1314 	description = g_strdup_printf("%d x %d %s\n%d %s (%d bits)\n%d %s\n%s", w,
1315 			h, ngettext("pixel", "pixels", h), n_channels,
1316 			ngettext("channel", "channels", n_channels), bit, n_frames,
1317 			ngettext("frame", "frames", n_frames), _("(Monochrome Preview)"));
1318 
1319 	M = 0; // line number
1320 	for (i = 0; i < Hs; i++) { // cycle through a blocks by lines
1321 		//pptr = &pixbuf_data[i * Ws * 3];
1322 		for (j = 0; j < MAX_SIZE; j++)
1323 			pix[j] = 0;
1324 		m = 0.f; // amount of strings read in block
1325 		for (l = 0; l < pixScale; l++, m++) { // cycle through a block lines
1326 			ptr = &ima_data[M * w];
1327 			N = 0; // number of column
1328 			for (j = 0; j < Ws; j++) { // cycle through a blocks by columns
1329 				n = 0.;	// amount of columns read in block
1330 				byte = 0.; // average intensity in block
1331 				for (k = 0; k < pixScale; k++, n++) { // cycle through block pixels
1332 					if (N++ < w) // row didn't end
1333 						byte += *ptr++; // sum[(pix-min)/wd]/n = [sum(pix)/n-min]/wd
1334 					else
1335 						break;
1336 				}
1337 				pix[j] += byte / n; //(byte / n - min)/wd;
1338 			}
1339 			if (++M >= h)
1340 				break;
1341 		}
1342 		// fill unused picture pixels
1343 		ptr = &ima_data[i * Ws];
1344 		for (l = 0; l < Ws; l++)
1345 			*ptr++ = pix[l] / m;
1346 	}
1347 	ptr = ima_data;
1348 	sz = Ws * Hs;
1349 	max = min = *ptr;
1350 	avr = 0;
1351 	for (i = 0; i < sz; i++, ptr++) {
1352 		float tmp = *ptr;
1353 		if (tmp > max)
1354 			max = tmp;
1355 		else if (tmp < min)
1356 			min = tmp;
1357 		avr += tmp;
1358 	}
1359 	avr /= (float) sz;
1360 	wd = max - min;
1361 	avr = (avr - min) / wd;	// normal average by preview
1362 	if (avr > 1.)
1363 		wd /= avr;
1364 	ptr = ima_data;
1365 	for (i = Hs - 1; i > -1; i--) {	// fill pixbuf mirroring image by vertical
1366 		pptr = &pixbuf_data[Ws * i * 3];
1367 		for (j = 0; j < Ws; j++) {
1368 			*pptr++ = (guchar) round_to_BYTE(255.f * (*ptr - min) / wd);
1369 			*pptr++ = (guchar) round_to_BYTE(255.f * (*ptr - min) / wd);
1370 			*pptr++ = (guchar) round_to_BYTE(255.f * (*ptr - min) / wd);
1371 			ptr++;
1372 		}
1373 	}
1374 
1375 	ser_close_file(&ser);
1376 	pixbuf = gdk_pixbuf_new_from_data(pixbuf_data,		// guchar* data
1377 			GDK_COLORSPACE_RGB,	// only this supported
1378 			FALSE,				// no alpha
1379 			8,				// number of bits
1380 			Ws, Hs,				// size
1381 			Ws * 3,				// line length in bytes
1382 			(GdkPixbufDestroyNotify) free_preview_data, // function (*GdkPixbufDestroyNotify) (guchar *pixels, gpointer data);
1383 			NULL
1384 			);
1385 	free(ima_data);
1386 	free(pix);
1387 	*descr = description;
1388 	return pixbuf;
1389 }
1390 
ser_read_frame_date(struct ser_struct * ser_file,int frame_no)1391 GDateTime *ser_read_frame_date(struct ser_struct *ser_file, int frame_no) {
1392 	if (ser_file->ts)
1393 		return ser_timestamp_to_date_time(ser_file->ts[frame_no]);
1394 	return NULL;
1395 }
1396 
1397