6d54d83eb5627a8225950ec03c5a5f8e33505c73
[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 #include "unicode_support.h"
37
38 static const char BASE64_TABLE[64]={
39   'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
40   'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
41   'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
42   'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/'
43 };
44
45 /*Utility function for base64 encoding METADATA_BLOCK_PICTURE tags.
46   Stores BASE64_LENGTH(len)+1 bytes in dst (including a terminating NUL).*/
47 static void base64_encode(char *dst, const char *src, int len){
48   unsigned s0;
49   unsigned s1;
50   unsigned s2;
51   int      ngroups;
52   int      i;
53   ngroups=len/3;
54   for(i=0;i<ngroups;i++){
55     s0=(unsigned char)src[3*i+0];
56     s1=(unsigned char)src[3*i+1];
57     s2=(unsigned char)src[3*i+2];
58     dst[4*i+0]=BASE64_TABLE[s0>>2];
59     dst[4*i+1]=BASE64_TABLE[(s0&3)<<4|s1>>4];
60     dst[4*i+2]=BASE64_TABLE[(s1&15)<<2|s2>>6];
61     dst[4*i+3]=BASE64_TABLE[s2&63];
62   }
63   len-=3*i;
64   if(len==1){
65     s0=(unsigned char)src[3*i+0];
66     dst[4*i+0]=BASE64_TABLE[s0>>2];
67     dst[4*i+1]=BASE64_TABLE[(s0&3)<<4];
68     dst[4*i+2]='=';
69     dst[4*i+3]='=';
70     i++;
71   }
72   else if(len==2){
73     s0=(unsigned char)src[3*i+0];
74     s1=(unsigned char)src[3*i+1];
75     dst[4*i+0]=BASE64_TABLE[s0>>2];
76     dst[4*i+1]=BASE64_TABLE[(s0&3)<<4|s1>>4];
77     dst[4*i+2]=BASE64_TABLE[(s1&15)<<2];
78     dst[4*i+3]='=';
79     i++;
80   }
81   dst[4*i]='\0';
82 }
83
84 /*A version of strncasecmp() that is guaranteed to only ignore the case of
85    ASCII characters.*/
86 static int oi_strncasecmp(const char *a, const char *b, int n){
87   int i;
88   for(i=0;i<n;i++){
89     int aval;
90     int bval;
91     int diff;
92     aval=a[i];
93     bval=b[i];
94     if(aval>='a'&&aval<='z') {
95       aval-='a'-'A';
96     }
97     if(bval>='a'&&bval<='z'){
98       bval-='a'-'A';
99     }
100     diff=aval-bval;
101     if(diff){
102       return diff;
103     }
104   }
105   return 0;
106 }
107
108 static int is_jpeg(const unsigned char *buf, size_t length){
109   return length>=3&&memcmp(buf,"\xFF\xD8\xFF",3)==0;
110 }
111
112 static 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 static 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 static 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 static 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 static 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 *_ope_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 (picture_type < 0) picture_type=3;
255   if (picture_type > 20) {
256     *error=OPE_INVALID_PICTURE;
257     return NULL;
258   }
259   if(picture_type>=1&&picture_type<=2&&(*seen_file_icons&picture_type)){
260     *error=OPE_INVALID_PICTURE;
261     return NULL;
262   }
263
264   if (description == NULL) description = "";
265   picture_file=_ope_fopen(filename,"rb");
266   /*Buffer size: 8 static 4-byte fields plus 2 dynamic fields, plus the
267      file/URL data.
268     We reserve at least 10 bytes for the media type, in case we still need to
269      extract it from the file.*/
270   data_offset=32+strlen(description)+10;
271   buf=NULL;
272   {
273     int          has_palette;
274     /*Complicated case: we have a real file.
275       Read it in, attempt to parse the media type and image dimensions if
276        necessary, and validate what the user passed in.*/
277     if(picture_file==NULL){
278       *error = OPE_CANNOT_OPEN;
279       return NULL;
280     }
281     nbuf=data_offset;
282     /*Add a reasonable starting image file size.*/
283     cbuf=data_offset+65536;
284     for(;;){
285       unsigned char *new_buf;
286       size_t         nread;
287       new_buf=realloc(buf,cbuf);
288       if(new_buf==NULL){
289         fclose(picture_file);
290         free(buf);
291         *error = OPE_ALLOC_FAIL;
292         return NULL;
293       }
294       buf=new_buf;
295       nread=fread(buf+nbuf,1,cbuf-nbuf,picture_file);
296       nbuf+=nread;
297       if(nbuf<cbuf){
298         int file_error;
299         file_error=ferror(picture_file);
300         fclose(picture_file);
301         if(file_error){
302           free(buf);
303           *error = OPE_INVALID_PICTURE;
304           return NULL;
305         }
306         break;
307       }
308       if(cbuf==0xFFFFFFFF){
309         fclose(picture_file);
310         free(buf);
311         *error = OPE_INVALID_PICTURE;
312         return NULL;
313       }
314       else if(cbuf>0x7FFFFFFFU)cbuf=0xFFFFFFFFU;
315       else cbuf=cbuf<<1|1;
316     }
317     data_length=nbuf-data_offset;
318     /*Try to extract the image dimensions/color information from the file.*/
319     width=height=depth=colors=0;
320     has_palette=-1;
321     {
322       if(is_jpeg(buf+data_offset,data_length)){
323         mime_type="image/jpeg";
324         extract_jpeg_params(buf+data_offset,data_length,
325          &width,&height,&depth,&colors,&has_palette);
326       }
327       else if(is_png(buf+data_offset,data_length)){
328         mime_type="image/png";
329         extract_png_params(buf+data_offset,data_length,
330          &width,&height,&depth,&colors,&has_palette);
331       }
332       else if(is_gif(buf+data_offset,data_length)){
333         mime_type="image/gif";
334         extract_gif_params(buf+data_offset,data_length,
335          &width,&height,&depth,&colors,&has_palette);
336       }
337       else{
338         free(buf);
339         *error = OPE_INVALID_PICTURE;
340         return NULL;
341       }
342     }
343   }
344   /*These fields MUST be set correctly OR all set to zero.
345     So if any of them (except colors, for which 0 is a valid value) are still
346      zero, clear the rest to zero.*/
347   if(width==0||height==0||depth==0)width=height=depth=colors=0;
348   if(picture_type==1&&(width!=32||height!=32
349    ||strlen(mime_type)!=9
350    ||oi_strncasecmp("image/png",mime_type,9)!=0)){
351     free(buf);
352     *error = OPE_INVALID_ICON;
353     return NULL;
354   }
355   /*Build the METADATA_BLOCK_PICTURE buffer.
356     We do this backwards from data_offset, because we didn't necessarily know
357      how big the media type string was before we read the data in.*/
358   data_offset-=4;
359   WRITE_U32_BE(buf+data_offset,(unsigned long)data_length);
360   data_offset-=4;
361   WRITE_U32_BE(buf+data_offset,colors);
362   data_offset-=4;
363   WRITE_U32_BE(buf+data_offset,depth);
364   data_offset-=4;
365   WRITE_U32_BE(buf+data_offset,height);
366   data_offset-=4;
367   WRITE_U32_BE(buf+data_offset,width);
368   data_offset-=strlen(description);
369   memcpy(buf+data_offset,description,strlen(description));
370   data_offset-=4;
371   WRITE_U32_BE(buf+data_offset,strlen(description));
372   data_offset-=strlen(mime_type);
373   memcpy(buf+data_offset,mime_type,strlen(mime_type));
374   data_offset-=4;
375   WRITE_U32_BE(buf+data_offset,strlen(mime_type));
376   data_offset-=4;
377   WRITE_U32_BE(buf+data_offset,picture_type);
378   data_length=nbuf-data_offset;
379   b64_length=BASE64_LENGTH(data_length);
380   out=(char *)malloc(b64_length+1);
381   if(out!=NULL){
382     base64_encode(out,(char *)buf+data_offset,data_length);
383     if(picture_type>=1&&picture_type<=2)*seen_file_icons|=picture_type;
384   } else {
385     *error = OPE_ALLOC_FAIL;
386   }
387   free(buf);
388   return out;
389 }