fdebd4fa4c8198ca7fdfeb41098a3384aedacbca
[libopusenc.git] / src / picture.c
1 /* Copyright (C)2007-2013 Xiph.Org Foundation
2    File: picture.c
3
4    Redistribution and use in source and binary forms, with or without
5    modification, are permitted provided that the following conditions
6    are met:
7
8    - Redistributions of source code must retain the above copyright
9    notice, this list of conditions and the following disclaimer.
10
11    - Redistributions in binary form must reproduce the above copyright
12    notice, this list of conditions and the following disclaimer in the
13    documentation and/or other materials provided with the distribution.
14
15    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16    ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
18    A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
19    CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20    EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21    PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22    PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
23    LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
24    NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
31
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include "picture.h"
36
37 static const char BASE64_TABLE[64]={
38   'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
39   'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
40   'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
41   'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/'
42 };
43
44 /*Utility function for base64 encoding METADATA_BLOCK_PICTURE tags.
45   Stores BASE64_LENGTH(len)+1 bytes in dst (including a terminating NUL).*/
46 void base64_encode(char *dst, const char *src, int len){
47   unsigned s0;
48   unsigned s1;
49   unsigned s2;
50   int      ngroups;
51   int      i;
52   ngroups=len/3;
53   for(i=0;i<ngroups;i++){
54     s0=(unsigned char)src[3*i+0];
55     s1=(unsigned char)src[3*i+1];
56     s2=(unsigned char)src[3*i+2];
57     dst[4*i+0]=BASE64_TABLE[s0>>2];
58     dst[4*i+1]=BASE64_TABLE[(s0&3)<<4|s1>>4];
59     dst[4*i+2]=BASE64_TABLE[(s1&15)<<2|s2>>6];
60     dst[4*i+3]=BASE64_TABLE[s2&63];
61   }
62   len-=3*i;
63   if(len==1){
64     s0=(unsigned char)src[3*i+0];
65     dst[4*i+0]=BASE64_TABLE[s0>>2];
66     dst[4*i+1]=BASE64_TABLE[(s0&3)<<4];
67     dst[4*i+2]='=';
68     dst[4*i+3]='=';
69     i++;
70   }
71   else if(len==2){
72     s0=(unsigned char)src[3*i+0];
73     s1=(unsigned char)src[3*i+1];
74     dst[4*i+0]=BASE64_TABLE[s0>>2];
75     dst[4*i+1]=BASE64_TABLE[(s0&3)<<4|s1>>4];
76     dst[4*i+2]=BASE64_TABLE[(s1&15)<<2];
77     dst[4*i+3]='=';
78     i++;
79   }
80   dst[4*i]='\0';
81 }
82
83 /*A version of strncasecmp() that is guaranteed to only ignore the case of
84    ASCII characters.*/
85 int oi_strncasecmp(const char *a, const char *b, int n){
86   int i;
87   for(i=0;i<n;i++){
88     int aval;
89     int bval;
90     int diff;
91     aval=a[i];
92     bval=b[i];
93     if(aval>='a'&&aval<='z') {
94       aval-='a'-'A';
95     }
96     if(bval>='a'&&bval<='z'){
97       bval-='a'-'A';
98     }
99     diff=aval-bval;
100     if(diff){
101       return diff;
102     }
103   }
104   return 0;
105 }
106
107 int is_jpeg(const unsigned char *buf, size_t length){
108   return length>=11&&memcmp(buf,"\xFF\xD8\xFF\xE0",4)==0
109    &&(buf[4]<<8|buf[5])>=16&&memcmp(buf+6,"JFIF",5)==0;
110 }
111
112 int is_png(const unsigned char *buf, size_t length){
113   return length>=8&&memcmp(buf,"\x89PNG\x0D\x0A\x1A\x0A",8)==0;
114 }
115
116 int is_gif(const unsigned char *buf, size_t length){
117   return length>=6
118    &&(memcmp(buf,"GIF87a",6)==0||memcmp(buf,"GIF89a",6)==0);
119 }
120
121 #define READ_U32_BE(buf) \
122     (((buf)[0]<<24)|((buf)[1]<<16)|((buf)[2]<<8)|((buf)[3]&0xff))
123
124 /*Tries to extract the width, height, bits per pixel, and palette size of a
125    PNG.
126   On failure, simply leaves its outputs unmodified.*/
127 void extract_png_params(const unsigned char *data, size_t data_length,
128                         opus_uint32 *width, opus_uint32 *height,
129                         opus_uint32 *depth, opus_uint32 *colors,
130                         int *has_palette){
131   if(is_png(data,data_length)){
132     size_t offs;
133     offs=8;
134     while(data_length-offs>=12){
135       opus_uint32 chunk_len;
136       chunk_len=READ_U32_BE(data+offs);
137       if(chunk_len>data_length-(offs+12))break;
138       else if(chunk_len==13&&memcmp(data+offs+4,"IHDR",4)==0){
139         int color_type;
140         *width=READ_U32_BE(data+offs+8);
141         *height=READ_U32_BE(data+offs+12);
142         color_type=data[offs+17];
143         if(color_type==3){
144           *depth=24;
145           *has_palette=1;
146         }
147         else{
148           int sample_depth;
149           sample_depth=data[offs+16];
150           if(color_type==0)*depth=sample_depth;
151           else if(color_type==2)*depth=sample_depth*3;
152           else if(color_type==4)*depth=sample_depth*2;
153           else if(color_type==6)*depth=sample_depth*4;
154           *colors=0;
155           *has_palette=0;
156           break;
157         }
158       }
159       else if(*has_palette>0&&memcmp(data+offs+4,"PLTE",4)==0){
160         *colors=chunk_len/3;
161         break;
162       }
163       offs+=12+chunk_len;
164     }
165   }
166 }
167
168 /*Tries to extract the width, height, bits per pixel, and palette size of a
169    GIF.
170   On failure, simply leaves its outputs unmodified.*/
171 void extract_gif_params(const unsigned char *data, size_t data_length,
172                         opus_uint32 *width, opus_uint32 *height,
173                         opus_uint32 *depth, opus_uint32 *colors,
174                         int *has_palette){
175   if(is_gif(data,data_length)&&data_length>=14){
176     *width=data[6]|data[7]<<8;
177     *height=data[8]|data[9]<<8;
178     /*libFLAC hard-codes the depth to 24.*/
179     *depth=24;
180     *colors=1<<((data[10]&7)+1);
181     *has_palette=1;
182   }
183 }
184
185
186 /*Tries to extract the width, height, bits per pixel, and palette size of a
187    JPEG.
188   On failure, simply leaves its outputs unmodified.*/
189 void extract_jpeg_params(const unsigned char *data, size_t data_length,
190                          opus_uint32 *width, opus_uint32 *height,
191                          opus_uint32 *depth, opus_uint32 *colors,
192                          int *has_palette){
193   if(is_jpeg(data,data_length)){
194     size_t offs;
195     offs=2;
196     for(;;){
197       size_t segment_len;
198       int    marker;
199       while(offs<data_length&&data[offs]!=0xFF)offs++;
200       while(offs<data_length&&data[offs]==0xFF)offs++;
201       marker=data[offs];
202       offs++;
203       /*If we hit EOI* (end of image), or another SOI* (start of image),
204          or SOS (start of scan), then stop now.*/
205       if(offs>=data_length||(marker>=0xD8&&marker<=0xDA))break;
206       /*RST* (restart markers): skip (no segment length).*/
207       else if(marker>=0xD0&&marker<=0xD7)continue;
208       /*Read the length of the marker segment.*/
209       if(data_length-offs<2)break;
210       segment_len=data[offs]<<8|data[offs+1];
211       if(segment_len<2||data_length-offs<segment_len)break;
212       if(marker==0xC0||(marker>0xC0&&marker<0xD0&&(marker&3)!=0)){
213         /*Found a SOFn (start of frame) marker segment:*/
214         if(segment_len>=8){
215           *height=data[offs+3]<<8|data[offs+4];
216           *width=data[offs+5]<<8|data[offs+6];
217           *depth=data[offs+2]*data[offs+7];
218           *colors=0;
219           *has_palette=0;
220         }
221         break;
222       }
223       /*Other markers: skip the whole marker segment.*/
224       offs+=segment_len;
225     }
226   }
227 }
228
229 #define IMAX(a,b) ((a) > (b) ? (a) : (b))
230
231 /*Parse a picture SPECIFICATION as given on the command-line.
232   spec: The specification.
233   error_message: Returns an error message on error.
234   seen_file_icons: Bit flags used to track if any pictures of type 1 or type 2
235    have already been added, to ensure only one is allowed.
236   Return: A Base64-encoded string suitable for use in a METADATA_BLOCK_PICTURE
237    tag.*/
238 char *parse_picture_specification(const char *filename, int picture_type, const char *description,
239                                   int *error, int *seen_file_icons){
240   FILE          *picture_file;
241   opus_uint32  width;
242   opus_uint32  height;
243   opus_uint32  depth;
244   opus_uint32  colors;
245   unsigned char *buf;
246   const char    *mime_type;
247   char          *out;
248   size_t         cbuf;
249   size_t         nbuf;
250   size_t         data_offset;
251   size_t         data_length;
252   size_t         b64_length;
253   *error = OPE_OK;
254   /*If a filename has a '|' in it, there's no way we can distinguish it from a
255      full specification just from the spec string.
256     Instead, try to open the file.
257     If it exists, the user probably meant the file.*/
258   if (picture_type < 0) picture_type=3;
259   if (description == NULL) description = "";
260   picture_file=fopen(filename,"rb");
261   /*Buffer size: 8 static 4-byte fields plus 2 dynamic fields, plus the
262      file/URL data.
263     We reserve at least 10 bytes for the media type, in case we still need to
264      extract it from the file.*/
265   data_offset=32+strlen(description)+10;
266   buf=NULL;
267   {
268     int          has_palette;
269     /*Complicated case: we have a real file.
270       Read it in, attempt to parse the media type and image dimensions if
271        necessary, and validate what the user passed in.*/
272     if(picture_file==NULL){
273       *error = OPE_CANNOT_OPEN;
274       return NULL;
275     }
276     nbuf=data_offset;
277     /*Add a reasonable starting image file size.*/
278     cbuf=data_offset+65536;
279     for(;;){
280       unsigned char *new_buf;
281       size_t         nread;
282       new_buf=realloc(buf,cbuf);
283       if(new_buf==NULL){
284         fclose(picture_file);
285         free(buf);
286         *error = OPE_ALLOC_FAIL;
287         return NULL;
288       }
289       buf=new_buf;
290       nread=fread(buf+nbuf,1,cbuf-nbuf,picture_file);
291       nbuf+=nread;
292       if(nbuf<cbuf){
293         int file_error;
294         file_error=ferror(picture_file);
295         fclose(picture_file);
296         if(file_error){
297           free(buf);
298           *error = OPE_INVALID_PICTURE;
299           return NULL;
300         }
301         break;
302       }
303       if(cbuf==0xFFFFFFFF){
304         fclose(picture_file);
305         free(buf);
306         *error = OPE_INVALID_PICTURE;
307         return NULL;
308       }
309       else if(cbuf>0x7FFFFFFFU)cbuf=0xFFFFFFFFU;
310       else cbuf=cbuf<<1|1;
311     }
312     data_length=nbuf-data_offset;
313     /*Try to extract the image dimensions/color information from the file.*/
314     width=height=depth=colors=0;
315     has_palette=-1;
316     {
317       if(is_jpeg(buf+data_offset,data_length)){
318         mime_type="image/jpeg";
319         extract_jpeg_params(buf+data_offset,data_length,
320          &width,&height,&depth,&colors,&has_palette);
321       }
322       else if(is_png(buf+data_offset,data_length)){
323         mime_type="image/png";
324         extract_png_params(buf+data_offset,data_length,
325          &width,&height,&depth,&colors,&has_palette);
326       }
327       else if(is_gif(buf+data_offset,data_length)){
328         mime_type="image/gif";
329         extract_gif_params(buf+data_offset,data_length,
330          &width,&height,&depth,&colors,&has_palette);
331       }
332       else{
333         free(buf);
334         *error = OPE_INVALID_PICTURE;
335         return NULL;
336       }
337     }
338   }
339   /*These fields MUST be set correctly OR all set to zero.
340     So if any of them (except colors, for which 0 is a valid value) are still
341      zero, clear the rest to zero.*/
342   if(width==0||height==0||depth==0)width=height=depth=colors=0;
343   if(picture_type==1&&(width!=32||height!=32
344    ||strlen(mime_type)!=9
345    ||oi_strncasecmp("image/png",mime_type,9)!=0)){
346     free(buf);
347     *error = OPE_INVALID_ICON;
348     return NULL;
349   }
350   /*Build the METADATA_BLOCK_PICTURE buffer.
351     We do this backwards from data_offset, because we didn't necessarily know
352      how big the media type string was before we read the data in.*/
353   data_offset-=4;
354   WRITE_U32_BE(buf+data_offset,(unsigned long)data_length);
355   data_offset-=4;
356   WRITE_U32_BE(buf+data_offset,colors);
357   data_offset-=4;
358   WRITE_U32_BE(buf+data_offset,depth);
359   data_offset-=4;
360   WRITE_U32_BE(buf+data_offset,height);
361   data_offset-=4;
362   WRITE_U32_BE(buf+data_offset,width);
363   data_offset-=strlen(description);
364   memcpy(buf+data_offset,description,strlen(description));
365   data_offset-=4;
366   WRITE_U32_BE(buf+data_offset,strlen(description));
367   data_offset-=strlen(mime_type);
368   memcpy(buf+data_offset,mime_type,strlen(mime_type));
369   data_offset-=4;
370   WRITE_U32_BE(buf+data_offset,strlen(mime_type));
371   data_offset-=4;
372   WRITE_U32_BE(buf+data_offset,picture_type);
373   data_length=nbuf-data_offset;
374   b64_length=BASE64_LENGTH(data_length);
375   out=(char *)malloc(b64_length+1);
376   if(out!=NULL){
377     base64_encode(out,(char *)buf+data_offset,data_length);
378     if(picture_type>=1&&picture_type<=2)*seen_file_icons|=picture_type;
379   } else {
380     *error = OPE_ALLOC_FAIL;
381   }
382   free(buf);
383   return out;
384 }