OSDN Git Service

wwww
[proj16/16.git] / src / lib / doslib / ext / flac / picture.c
1 /* grabbag - Convenience lib for various routines common to several tools
2  * Copyright (C) 2006,2007  Josh Coalson
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
17  */
18
19 #if HAVE_CONFIG_H
20 #  include "config.h"
21 #endif
22
23 #include "share/alloc.h"
24 #include "share/grabbag.h"
25 #include "flac/assert.h"
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29
30 /* slightly different that strndup(): this always copies 'size' bytes starting from s into a NUL-terminated string. */
31 static char *local__strndup_(const char *s, size_t size)
32 {
33         char *x = (char*)safe_malloc_add_2op_(size, /*+*/1);
34         if(x) {
35                 memcpy(x, s, size);
36                 x[size] = '\0';
37         }
38         return x;
39 }
40
41 static FLAC__bool local__parse_type_(const char *s, size_t len, FLAC__StreamMetadata_Picture *picture)
42 {
43         size_t i;
44         FLAC__uint32 val = 0;
45
46         picture->type = FLAC__STREAM_METADATA_PICTURE_TYPE_FRONT_COVER;
47
48         if(len == 0)
49                 return true; /* empty string implies default to 'front cover' */
50
51         for(i = 0; i < len; i++) {
52                 if(s[i] >= '0' && s[i] <= '9')
53                         val = 10*val + (FLAC__uint32)(s[i] - '0');
54                 else
55                         return false;
56         }
57
58         if(i == len)
59                 picture->type = val;
60         else
61                 return false;
62
63         return true;
64 }
65
66 static FLAC__bool local__parse_resolution_(const char *s, size_t len, FLAC__StreamMetadata_Picture *picture)
67 {
68         int state = 0;
69         size_t i;
70         FLAC__uint32 val = 0;
71
72         picture->width = picture->height = picture->depth = picture->colors = 0;
73
74         if(len == 0)
75                 return true; /* empty string implies client wants to get info from the file itself */
76
77         for(i = 0; i < len; i++) {
78                 if(s[i] == 'x') {
79                         if(state == 0)
80                                 picture->width = val;
81                         else if(state == 1)
82                                 picture->height = val;
83                         else
84                                 return false;
85                         state++;
86                         val = 0;
87                 }
88                 else if(s[i] == '/') {
89                         if(state == 2)
90                                 picture->depth = val;
91                         else
92                                 return false;
93                         state++;
94                         val = 0;
95                 }
96                 else if(s[i] >= '0' && s[i] <= '9')
97                         val = 10*val + (FLAC__uint32)(s[i] - '0');
98                 else
99                         return false;
100         }
101
102         if(state < 2)
103                 return false;
104         else if(state == 2)
105                 picture->depth = val;
106         else if(state == 3)
107                 picture->colors = val;
108         else
109                 return false;
110         if(picture->depth < 32 && 1u<<picture->depth < picture->colors)
111                 return false;
112
113         return true;
114 }
115
116 static FLAC__bool local__extract_mime_type_(FLAC__StreamMetadata *obj)
117 {
118         if(obj->data.picture.data_length >= 8 && 0 == memcmp(obj->data.picture.data, "\x89PNG\x0d\x0a\x1a\x0a", 8))
119                 return FLAC__metadata_object_picture_set_mime_type(obj, "image/png", /*copy=*/true);
120         else if(obj->data.picture.data_length >= 6 && (0 == memcmp(obj->data.picture.data, "GIF87a", 6) || 0 == memcmp(obj->data.picture.data, "GIF89a", 6)))
121                 return FLAC__metadata_object_picture_set_mime_type(obj, "image/gif", /*copy=*/true);
122         else if(obj->data.picture.data_length >= 2 && 0 == memcmp(obj->data.picture.data, "\xff\xd8", 2))
123                 return FLAC__metadata_object_picture_set_mime_type(obj, "image/jpeg", /*copy=*/true);
124         return false;
125 }
126
127 static FLAC__bool local__extract_resolution_color_info_(FLAC__StreamMetadata_Picture *picture)
128 {
129         const FLAC__byte *data = picture->data;
130         FLAC__uint32 len = picture->data_length;
131
132         if(0 == strcmp(picture->mime_type, "image/png")) {
133                 /* c.f. http://www.w3.org/TR/PNG/ */
134                 FLAC__bool need_palette = false; /* if IHDR has color_type=3, we need to also read the PLTE chunk to get the #colors */
135                 if(len < 8 || memcmp(data, "\x89PNG\x0d\x0a\x1a\x0a", 8))
136                         return false;
137                 /* try to find IHDR chunk */
138                 data += 8;
139                 len -= 8;
140                 while(len > 12) { /* every PNG chunk must be at least 12 bytes long */
141                         const FLAC__uint32 clen = (FLAC__uint32)data[0] << 24 | (FLAC__uint32)data[1] << 16 | (FLAC__uint32)data[2] << 8 | (FLAC__uint32)data[3];
142                         if(0 == memcmp(data+4, "IHDR", 4) && clen == 13) {
143                                 unsigned color_type = data[17];
144                                 picture->width = (FLAC__uint32)data[8] << 24 | (FLAC__uint32)data[9] << 16 | (FLAC__uint32)data[10] << 8 | (FLAC__uint32)data[11];
145                                 picture->height = (FLAC__uint32)data[12] << 24 | (FLAC__uint32)data[13] << 16 | (FLAC__uint32)data[14] << 8 | (FLAC__uint32)data[15];
146                                 if(color_type == 3) {
147                                         /* even though the bit depth for color_type==3 can be 1,2,4,or 8,
148                                          * the spec in 11.2.2 of http://www.w3.org/TR/PNG/ says that the
149                                          * sample depth is always 8
150                                          */
151                                         picture->depth = 8 * 3u;
152                                         need_palette = true;
153                                         data += 12 + clen;
154                                         len -= 12 + clen;
155                                 }
156                                 else {
157                                         if(color_type == 0) /* greyscale, 1 sample per pixel */
158                                                 picture->depth = (FLAC__uint32)data[16];
159                                         if(color_type == 2) /* truecolor, 3 samples per pixel */
160                                                 picture->depth = (FLAC__uint32)data[16] * 3u;
161                                         if(color_type == 4) /* greyscale+alpha, 2 samples per pixel */
162                                                 picture->depth = (FLAC__uint32)data[16] * 2u;
163                                         if(color_type == 6) /* truecolor+alpha, 4 samples per pixel */
164                                                 picture->depth = (FLAC__uint32)data[16] * 4u;
165                                         picture->colors = 0;
166                                         return true;
167                                 }
168                         }
169                         else if(need_palette && 0 == memcmp(data+4, "PLTE", 4)) {
170                                 picture->colors = clen / 3u;
171                                 return true;
172                         }
173                         else if(clen + 12 > len)
174                                 return false;
175                         else {
176                                 data += 12 + clen;
177                                 len -= 12 + clen;
178                         }
179                 }
180         }
181         else if(0 == strcmp(picture->mime_type, "image/jpeg")) {
182                 /* c.f. http://www.w3.org/Graphics/JPEG/itu-t81.pdf and Q22 of http://www.faqs.org/faqs/jpeg-faq/part1/ */
183                 if(len < 2 || memcmp(data, "\xff\xd8", 2))
184                         return false;
185                 data += 2;
186                 len -= 2;
187                 while(1) {
188                         /* look for sync FF byte */
189                         for( ; len > 0; data++, len--) {
190                                 if(*data == 0xff)
191                                         break;
192                         }
193                         if(len == 0)
194                                 return false;
195                         /* eat any extra pad FF bytes before marker */
196                         for( ; len > 0; data++, len--) {
197                                 if(*data != 0xff)
198                                         break;
199                         }
200                         if(len == 0)
201                                 return false;
202                         /* if we hit SOS or EOI, bail */
203                         if(*data == 0xda || *data == 0xd9)
204                                 return false;
205                         /* looking for some SOFn */
206                         else if(memchr("\xc0\xc1\xc2\xc3\xc5\xc6\xc7\xc9\xca\xcb\xcd\xce\xcf", *data, 13)) {
207                                 data++; len--; /* skip marker byte */
208                                 if(len < 2)
209                                         return false;
210                                 else {
211                                         const FLAC__uint32 clen = (FLAC__uint32)data[0] << 8 | (FLAC__uint32)data[1];
212                                         if(clen < 8 || len < clen)
213                                                 return false;
214                                         picture->width = (FLAC__uint32)data[5] << 8 | (FLAC__uint32)data[6];
215                                         picture->height = (FLAC__uint32)data[3] << 8 | (FLAC__uint32)data[4];
216                                         picture->depth = (FLAC__uint32)data[2] * (FLAC__uint32)data[7];
217                                         picture->colors = 0;
218                                         return true;
219                                 }
220                         }
221                         /* else skip it */
222                         else {
223                                 data++; len--; /* skip marker byte */
224                                 if(len < 2)
225                                         return false;
226                                 else {
227                                         const FLAC__uint32 clen = (FLAC__uint32)data[0] << 8 | (FLAC__uint32)data[1];
228                                         if(clen < 2 || len < clen)
229                                                 return false;
230                                         data += clen;
231                                         len -= clen;
232                                 }
233                         }
234                 }
235         }
236         else if(0 == strcmp(picture->mime_type, "image/gif")) {
237                 /* c.f. http://www.w3.org/Graphics/GIF/spec-gif89a.txt */
238                 if(len < 14)
239                         return false;
240                 if(memcmp(data, "GIF87a", 6) && memcmp(data, "GIF89a", 6))
241                         return false;
242 #if 0
243                 /* according to the GIF spec, even if the GCTF is 0, the low 3 bits should still tell the total # colors used */
244                 if(data[10] & 0x80 == 0)
245                         return false;
246 #endif
247                 picture->width = (FLAC__uint32)data[6] | ((FLAC__uint32)data[7] << 8);
248                 picture->height = (FLAC__uint32)data[8] | ((FLAC__uint32)data[9] << 8);
249 #if 0
250                 /* this value doesn't seem to be reliable... */
251                 picture->depth = (((FLAC__uint32)(data[10] & 0x70) >> 4) + 1) * 3u;
252 #else
253                 /* ...just pessimistically assume it's 24-bit color without scanning all the color tables */
254                 picture->depth = 8u * 3u;
255 #endif
256                 picture->colors = 1u << ((FLAC__uint32)(data[10] & 0x07) + 1u);
257                 return true;
258         }
259         return false;
260 }
261
262 FLAC__StreamMetadata *grabbag__picture_parse_specification(const char *spec, const char **error_message)
263 {
264         FLAC__StreamMetadata *obj;
265         int state = 0;
266         static const char *error_messages[] = {
267                 "memory allocation error",
268                 "invalid picture specification",
269                 "invalid picture specification: can't parse resolution/color part",
270                 "unable to extract resolution and color info from URL, user must set explicitly",
271                 "unable to extract resolution and color info from file, user must set explicitly",
272                 "error opening picture file",
273                 "error reading picture file",
274                 "invalid picture type",
275                 "unable to guess MIME type from file, user must set explicitly",
276                 "type 1 icon must be a 32x32 pixel PNG"
277         };
278
279         FLAC__ASSERT(0 != spec);
280         FLAC__ASSERT(0 != error_message);
281
282         /* double protection */
283         if(0 == spec)
284                 return 0;
285         if(0 == error_message)
286                 return 0;
287
288         *error_message = 0;
289
290         if(0 == (obj = FLAC__metadata_object_new(FLAC__METADATA_TYPE_PICTURE)))
291                 *error_message = error_messages[0];
292
293         if(strchr(spec, '|')) { /* full format */
294                 const char *p;
295                 char *q;
296                 for(p = spec; *error_message==0 && *p; ) {
297                         if(*p == '|') {
298                                 switch(state) {
299                                         case 0: /* type */
300                                                 if(!local__parse_type_(spec, p-spec, &obj->data.picture))
301                                                         *error_message = error_messages[7];
302                                                 break;
303                                         case 1: /* mime type */
304                                                 if(p-spec) { /* if blank, we'll try to guess later from the picture data */
305                                                         if(0 == (q = local__strndup_(spec, p-spec)))
306                                                                 *error_message = error_messages[0];
307                                                         else if(!FLAC__metadata_object_picture_set_mime_type(obj, q, /*copy=*/false))
308                                                                 *error_message = error_messages[0];
309                                                 }
310                                                 break;
311                                         case 2: /* description */
312                                                 if(0 == (q = local__strndup_(spec, p-spec)))
313                                                         *error_message = error_messages[0];
314                                                 else if(!FLAC__metadata_object_picture_set_description(obj, (FLAC__byte*)q, /*copy=*/false))
315                                                         *error_message = error_messages[0];
316                                                 break;
317                                         case 3: /* resolution/color (e.g. [300x300x16[/1234]] */
318                                                 if(!local__parse_resolution_(spec, p-spec, &obj->data.picture))
319                                                         *error_message = error_messages[2];
320                                                 break;
321                                         default:
322                                                 *error_message = error_messages[1];
323                                                 break;
324                                 }
325                                 p++;
326                                 spec = p;
327                                 state++;
328                         }
329                         else
330                                 p++;
331                 }
332         }
333         else { /* simple format, filename only, everything else guessed */
334                 if(!local__parse_type_("", 0, &obj->data.picture)) /* use default picture type */
335                         *error_message = error_messages[7];
336                 /* leave MIME type to be filled in later */
337                 /* leave description empty */
338                 /* leave the rest to be filled in later: */
339                 else if(!local__parse_resolution_("", 0, &obj->data.picture))
340                         *error_message = error_messages[2];
341                 else
342                         state = 4;
343         }
344
345         /* parse filename, read file, try to extract resolution/color info if needed */
346         if(*error_message == 0) {
347                 if(state != 4)
348                         *error_message = error_messages[1];
349                 else { /* 'spec' points to filename/URL */
350                         if(0 == strcmp(obj->data.picture.mime_type, "-->")) { /* magic MIME type means URL */
351                                 if(!FLAC__metadata_object_picture_set_data(obj, (FLAC__byte*)spec, strlen(spec), /*copy=*/true))
352                                         *error_message = error_messages[0];
353                                 else if(obj->data.picture.width == 0 || obj->data.picture.height == 0 || obj->data.picture.depth == 0)
354                                         *error_message = error_messages[3];
355                         }
356                         else { /* regular picture file */
357                                 const off_t size = grabbag__file_get_filesize(spec);
358                                 if(size < 0)
359                                         *error_message = error_messages[5];
360                                 else {
361                                         FLAC__byte *buffer = (FLAC__byte*)safe_malloc_(size);
362                                         if(0 == buffer)
363                                                 *error_message = error_messages[0];
364                                         else {
365                                                 FILE *f = fopen(spec, "rb");
366                                                 if(0 == f)
367                                                         *error_message = error_messages[5];
368                                                 else {
369                                                         if(fread(buffer, 1, size, f) != (size_t)size)
370                                                                 *error_message = error_messages[6];
371                                                         fclose(f);
372                                                         if(0 == *error_message) {
373                                                                 if(!FLAC__metadata_object_picture_set_data(obj, buffer, size, /*copy=*/false))
374                                                                         *error_message = error_messages[6];
375                                                                 /* try to extract MIME type if user left it blank */
376                                                                 else if(*obj->data.picture.mime_type == '\0' && !local__extract_mime_type_(obj))
377                                                                         *error_message = error_messages[8];
378                                                                 /* try to extract resolution/color info if user left it blank */
379                                                                 else if((obj->data.picture.width == 0 || obj->data.picture.height == 0 || obj->data.picture.depth == 0) && !local__extract_resolution_color_info_(&obj->data.picture))
380                                                                         *error_message = error_messages[4];
381                                                         }
382                                                 }
383                                         }
384                                 }
385                         }
386                 }
387         }
388
389         if(*error_message == 0) {
390                 if(
391                         obj->data.picture.type == FLAC__STREAM_METADATA_PICTURE_TYPE_FILE_ICON_STANDARD && 
392                         (
393                                 (strcmp(obj->data.picture.mime_type, "image/png") && strcmp(obj->data.picture.mime_type, "-->")) ||
394                                 obj->data.picture.width != 32 ||
395                                 obj->data.picture.height != 32
396                         )
397                 )
398                         *error_message = error_messages[9];
399         }
400
401         if(*error_message && obj) {
402                 FLAC__metadata_object_delete(obj);
403                 obj = 0;
404         }
405
406         return obj;
407 }