Add FITS Decoder
[ffmpeg.git] / libavcodec / fits.c
1 /*
2  * FITS implementation of common functions
3  * Copyright (c) 2017 Paras Chadha
4  *
5  * This file is part of FFmpeg.
6  *
7  * FFmpeg is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * FFmpeg 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 GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with FFmpeg; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20  */
21
22 #include "avcodec.h"
23 #include "libavutil/dict.h"
24 #include "fits.h"
25
26 int avpriv_fits_header_init(FITSHeader *header, FITSHeaderState state)
27 {
28     header->state = state;
29     header->naxis_index = 0;
30     header->blank_found = 0;
31     header->pcount = 0;
32     header->gcount = 1;
33     header->groups = 0;
34     header->rgb = 0;
35     header->image_extension = 0;
36     header->bscale = 1.0;
37     header->bzero = 0;
38     header->data_min_found = 0;
39     header->data_max_found = 0;
40     return 0;
41 }
42
43 static int dict_set_if_not_null(AVDictionary ***metadata, char *keyword, char *value)
44 {
45     if (metadata)
46         av_dict_set(*metadata, keyword, value, 0);
47     return 0;
48 }
49
50 /**
51  * Extract keyword and value from a header line (80 bytes) and store them in keyword and value strings respectively
52  * @param ptr8 pointer to the data
53  * @param keyword pointer to the char array in which keyword is to be stored
54  * @param value pointer to the char array in which value is to be stored
55  * @return 0 if calculated successfully otherwise AVERROR_INVALIDDATA
56  */
57 static int read_keyword_value(const uint8_t *ptr8, char *keyword, char *value)
58 {
59     int i;
60
61     for (i = 0; i < 8 && ptr8[i] != ' '; i++) {
62         keyword[i] = ptr8[i];
63     }
64     keyword[i] = '\0';
65
66     if (ptr8[8] == '=') {
67         i = 10;
68         while (i < 80 && ptr8[i] == ' ') {
69             i++;
70         }
71
72         if (i < 80) {
73             *value++ = ptr8[i];
74             i++;
75             if (ptr8[i-1] == '\'') {
76                 for (; i < 80 && ptr8[i] != '\''; i++) {
77                     *value++ = ptr8[i];
78                 }
79                 *value++ = '\'';
80             } else if (ptr8[i-1] == '(') {
81                 for (; i < 80 && ptr8[i] != ')'; i++) {
82                     *value++ = ptr8[i];
83                 }
84                 *value++ = ')';
85             } else {
86                 for (; i < 80 && ptr8[i] != ' ' && ptr8[i] != '/'; i++) {
87                     *value++ = ptr8[i];
88                 }
89             }
90         }
91     }
92     *value = '\0';
93     return 0;
94 }
95
96 #define CHECK_KEYWORD(key) \
97     if (strcmp(keyword, key)) { \
98         av_log(avcl, AV_LOG_ERROR, "expected %s keyword, found %s = %s\n", key, keyword, value); \
99         return AVERROR_INVALIDDATA; \
100     }
101
102 #define CHECK_VALUE(key, val) \
103     if (sscanf(value, "%d", &header->val) != 1) { \
104         av_log(avcl, AV_LOG_ERROR, "invalid value of %s keyword, %s = %s\n", key, keyword, value); \
105         return AVERROR_INVALIDDATA; \
106     }
107
108 int avpriv_fits_header_parse_line(void *avcl, FITSHeader *header, const uint8_t line[80], AVDictionary ***metadata)
109 {
110     int dim_no, ret;
111     int64_t t;
112     double d;
113     char keyword[10], value[72], c;
114
115     read_keyword_value(line, keyword, value);
116     switch (header->state) {
117     case STATE_SIMPLE:
118         CHECK_KEYWORD("SIMPLE");
119
120         if (value[0] == 'F') {
121             av_log(avcl, AV_LOG_WARNING, "not a standard FITS file\n");
122         } else if (value[0] != 'T') {
123             av_log(avcl, AV_LOG_ERROR, "invalid value of SIMPLE keyword, SIMPLE = %c\n", value[0]);
124             return AVERROR_INVALIDDATA;
125         }
126
127         header->state = STATE_BITPIX;
128         break;
129     case STATE_XTENSION:
130         CHECK_KEYWORD("XTENSION");
131
132         if (!strcmp(value, "'IMAGE   '")) {
133             header->image_extension = 1;
134         }
135
136         header->state = STATE_BITPIX;
137         break;
138     case STATE_BITPIX:
139         CHECK_KEYWORD("BITPIX");
140         CHECK_VALUE("BITPIX", bitpix);
141         dict_set_if_not_null(metadata, keyword, value);
142
143         header->state = STATE_NAXIS;
144         break;
145     case STATE_NAXIS:
146         CHECK_KEYWORD("NAXIS");
147         CHECK_VALUE("NAXIS", naxis);
148         dict_set_if_not_null(metadata, keyword, value);
149
150         if (header->naxis) {
151             header->state = STATE_NAXIS_N;
152         } else {
153             header->state = STATE_REST;
154         }
155         break;
156     case STATE_NAXIS_N:
157         ret = sscanf(keyword, "NAXIS%d", &dim_no);
158         if (ret != 1 || dim_no != header->naxis_index + 1) {
159             av_log(avcl, AV_LOG_ERROR, "expected NAXIS%d keyword, found %s = %s\n", header->naxis_index + 1, keyword, value);
160             return AVERROR_INVALIDDATA;
161         }
162
163         if (sscanf(value, "%d", &header->naxisn[header->naxis_index]) != 1) {
164             av_log(avcl, AV_LOG_ERROR, "invalid value of NAXIS%d keyword, %s = %s\n", header->naxis_index + 1, keyword, value);
165             return AVERROR_INVALIDDATA;
166         }
167
168         dict_set_if_not_null(metadata, keyword, value);
169         header->naxis_index++;
170         if (header->naxis_index == header->naxis) {
171             header->state = STATE_REST;
172         }
173         break;
174     case STATE_REST:
175         if (!strcmp(keyword, "BLANK") && sscanf(value, "%"SCNd64"", &t) == 1) {
176             header->blank = t;
177             header->blank_found = 1;
178         } else if (!strcmp(keyword, "BSCALE") && sscanf(value, "%lf", &d) == 1) {
179             header->bscale = d;
180         } else if (!strcmp(keyword, "BZERO") && sscanf(value, "%lf", &d) == 1) {
181             header->bzero = d;
182         } else if (!strcmp(keyword, "CTYPE3") && !strncmp(value, "'RGB", 4)) {
183             header->rgb = 1;
184         } else if (!strcmp(keyword, "DATAMAX") && sscanf(value, "%lf", &d) == 1) {
185             header->data_max_found = 1;
186             header->data_max = d;
187         } else if (!strcmp(keyword, "DATAMIN") && sscanf(value, "%lf", &d) == 1) {
188             header->data_min_found = 1;
189             header->data_min = d;
190         } else if (!strcmp(keyword, "END")) {
191             return 1;
192         } else if (!strcmp(keyword, "GROUPS") && sscanf(value, "%c", &c) == 1) {
193             header->groups = (c == 'T');
194         } else if (!strcmp(keyword, "GCOUNT") && sscanf(value, "%"SCNd64"", &t) == 1) {
195             header->gcount = t;
196         } else if (!strcmp(keyword, "PCOUNT") && sscanf(value, "%"SCNd64"", &t) == 1) {
197             header->pcount = t;
198         }
199         dict_set_if_not_null(metadata, keyword, value);
200         break;
201     }
202     return 0;
203 }