1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <stddef.h>
4 #include <string.h>
5 #include <errno.h>
6 #include <jpeglib.h>
7 #include <setjmp.h>
8 
9 #include <libexif/exif-data.h>
10 
11 #include "readers.h"
12 #include "misc.h"
13 
14 /* ---------------------------------------------------------------------- */
15 /* load                                                                   */
16 
17 struct jpeg_state {
18     FILE * infile;                /* source file */
19 
20     struct jpeg_decompress_struct cinfo;
21     struct jpeg_error_mgr jerr;
22     jmp_buf errjump;
23     JSAMPARRAY buffer;            /* Output row buffer */
24     int row_stride,linelength;    /* physical row width in output buffer */
25     unsigned char *image,*ptr;
26 
27     /* thumbnail */
28     unsigned char  *thumbnail;
29     unsigned int   tpos, tsize;
30 };
31 
32 /* ---------------------------------------------------------------------- */
33 /* data source manager for thumbnail images                               */
34 
thumbnail_src_init(struct jpeg_decompress_struct * cinfo)35 static void thumbnail_src_init(struct jpeg_decompress_struct *cinfo)
36 {
37     struct jpeg_state *h  = container_of(cinfo, struct jpeg_state, cinfo);
38     cinfo->src->next_input_byte = h->thumbnail;
39     cinfo->src->bytes_in_buffer = h->tsize;
40 }
41 
thumbnail_src_fill(struct jpeg_decompress_struct * cinfo)42 static int thumbnail_src_fill(struct jpeg_decompress_struct *cinfo)
43 {
44     fprintf(stderr,"jpeg: panic: no more thumbnail input data\n");
45     exit(1);
46 }
47 
thumbnail_src_skip(struct jpeg_decompress_struct * cinfo,long num_bytes)48 static void thumbnail_src_skip(struct jpeg_decompress_struct *cinfo,
49 			       long num_bytes)
50 {
51     cinfo->src->next_input_byte += num_bytes;
52 }
53 
thumbnail_src_term(struct jpeg_decompress_struct * cinfo)54 static void thumbnail_src_term(struct jpeg_decompress_struct *cinfo)
55 {
56     /* nothing */
57 }
58 
59 static struct jpeg_source_mgr thumbnail_mgr = {
60     .init_source         = thumbnail_src_init,
61     .fill_input_buffer   = thumbnail_src_fill,
62     .skip_input_data     = thumbnail_src_skip,
63     .resync_to_restart   = jpeg_resync_to_restart,
64     .term_source         = thumbnail_src_term,
65 };
66 
67 /* ---------------------------------------------------------------------- */
68 /* jpeg loader                                                            */
69 
jerror_exit(j_common_ptr info)70 static void jerror_exit(j_common_ptr info)
71 {
72     struct jpeg_decompress_struct *cinfo = (struct jpeg_decompress_struct *)info;
73     struct jpeg_state *h  = container_of(cinfo, struct jpeg_state, cinfo);
74     cinfo->err->output_message(info);
75     longjmp(h->errjump, 1);
76     jpeg_destroy_decompress(cinfo);
77     exit(1);
78 }
79 
80 static void*
jpeg_init(FILE * fp,char * filename,unsigned int page,struct ida_image_info * i,int thumbnail)81 jpeg_init(FILE *fp, char *filename, unsigned int page,
82 	  struct ida_image_info *i, int thumbnail)
83 {
84     struct jpeg_state *h;
85     jpeg_saved_marker_ptr mark;
86 
87     h = malloc(sizeof(*h));
88     memset(h,0,sizeof(*h));
89     h->infile = fp;
90 
91     h->cinfo.err = jpeg_std_error(&h->jerr);
92     h->cinfo.err->error_exit = jerror_exit;
93     if(setjmp(h->errjump))
94 	return 0;
95 
96     jpeg_create_decompress(&h->cinfo);
97     jpeg_save_markers(&h->cinfo, JPEG_COM,    0xffff); /* comment */
98     jpeg_save_markers(&h->cinfo, JPEG_APP0+1, 0xffff); /* EXIF */
99     jpeg_stdio_src(&h->cinfo, h->infile);
100     jpeg_read_header(&h->cinfo, TRUE);
101 
102     for (mark = h->cinfo.marker_list; NULL != mark; mark = mark->next) {
103 	switch (mark->marker) {
104 	case JPEG_COM:
105 	    if (debug)
106 		fprintf(stderr,"jpeg: comment found (COM marker) [%.*s]\n",
107 			(int)mark->data_length, mark->data);
108 	    load_add_extra(i,EXTRA_COMMENT,mark->data,mark->data_length);
109 	    break;
110 	case JPEG_APP0 +1:
111 	    if (debug)
112 		fprintf(stderr,"jpeg: exif data found (APP1 marker)\n");
113 	    load_add_extra(i,EXTRA_COMMENT,mark->data,mark->data_length);
114 
115 	    if (thumbnail) {
116 		ExifData *ed;
117 
118 		ed = exif_data_new_from_data(mark->data,mark->data_length);
119 		if (ed->data &&
120 		    ed->data[0] == 0xff &&
121 		    ed->data[1] == 0xd8) {
122 		    if (debug)
123 			fprintf(stderr,"jpeg: exif thumbnail found\n");
124 
125 		    /* save away thumbnail data */
126 		    h->thumbnail = malloc(ed->size);
127 		    h->tsize = ed->size;
128 		    memcpy(h->thumbnail,ed->data,ed->size);
129 		}
130 		exif_data_unref(ed);
131 	    }
132 	    break;
133 	}
134     }
135 
136     if (h->thumbnail) {
137 	/* save image size */
138 	i->thumbnail   = 1;
139 	i->real_width  = h->cinfo.image_width;
140 	i->real_height = h->cinfo.image_height;
141 
142 	/* re-setup jpeg */
143 	jpeg_destroy_decompress(&h->cinfo);
144 	fclose(h->infile);
145 	h->infile = NULL;
146 	jpeg_create_decompress(&h->cinfo);
147 	h->cinfo.src = &thumbnail_mgr;
148 	jpeg_read_header(&h->cinfo, TRUE);
149     }
150 
151     h->cinfo.out_color_space = JCS_RGB;
152     jpeg_start_decompress(&h->cinfo);
153     i->width  = h->cinfo.image_width;
154     i->height = h->cinfo.image_height;
155     i->npages = 1;
156     switch (h->cinfo.density_unit) {
157     case 0: /* unknown */
158 	break;
159     case 1: /* dot per inch */
160 	i->dpi = h->cinfo.X_density;
161 	break;
162     case 2: /* dot per cm */
163 	i->dpi = res_cm_to_inch(h->cinfo.X_density);
164 	break;
165     }
166 
167     return h;
168 }
169 
170 static void
jpeg_read(unsigned char * dst,unsigned int line,void * data)171 jpeg_read(unsigned char *dst, unsigned int line, void *data)
172 {
173     struct jpeg_state *h = data;
174     JSAMPROW row = dst;
175 
176     if(setjmp(h->errjump))
177 	return;
178     jpeg_read_scanlines(&h->cinfo, &row, 1);
179 }
180 
181 static void
jpeg_done(void * data)182 jpeg_done(void *data)
183 {
184     struct jpeg_state *h = data;
185 
186     if (setjmp(h->errjump))
187 	return;
188     jpeg_destroy_decompress(&h->cinfo);
189     if (h->infile)
190 	fclose(h->infile);
191     if (h->thumbnail)
192 	free(h->thumbnail);
193     free(h);
194 }
195 
196 struct ida_loader jpeg_loader = {
197     magic: "\xff\xd8",
198     moff:  0,
199     mlen:  2,
200     name:  "libjpeg",
201     init:  jpeg_init,
202     read:  jpeg_read,
203     done:  jpeg_done,
204 };
205 
init_rd(void)206 static void __init init_rd(void)
207 {
208     load_register(&jpeg_loader);
209 }
210