1 /*
2  * jpeg.c:
3  * JPEG file support. Based on example.c in the IJG jpeg-6b distribution.
4  *
5  * Copyright (c) 2001 Chris Lightfoot.
6  * Email: chris@ex-parrot.com; WWW: http://www.ex-parrot.com/~chris/
7  *
8  */
9 
10 #ifdef HAVE_CONFIG_H
11     #include <config.h>
12 #endif
13 
14 #include "compat/compat.h"
15 
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <string.h>
19 #include <setjmp.h>
20 
21 #include <jpeglib.h>
22 
23 #include "common/util.h"
24 #include "img.h"
25 
26 /* struct my_error_mgr:
27  * Error handling struct for JPEG library interaction. */
28 struct my_error_mgr {
29     struct jpeg_error_mgr pub;
30     jmp_buf jb;
31 };
32 
33 /* my_error_exit:
34  * Error handler method for JPEG library. */
my_error_exit(j_common_ptr cinfo)35 static void my_error_exit(j_common_ptr cinfo) {
36     struct my_error_mgr *e = (struct my_error_mgr*)cinfo->err;
37     (*cinfo->err->output_message)(cinfo);
38     longjmp(e->jb, 1);
39 }
40 
41 /* jpeg_load_hdr:
42  * Load the header of a JPEG file. */
jpeg_load_hdr(img I)43 int jpeg_load_hdr(img I) {
44     struct jpeg_decompress_struct *cinfo;
45     struct my_error_mgr *jerr;
46     alloc_struct(jpeg_decompress_struct, cinfo);
47     I->us = cinfo;
48     alloc_struct(my_error_mgr, jerr);
49     cinfo->err = jpeg_std_error(&jerr->pub);
50     jerr->pub.error_exit = my_error_exit;
51     if (setjmp(jerr->jb)) {
52         /* Oops, something went wrong. */
53         I->err = IE_HDRFORMAT;
54         jpeg_destroy_decompress(cinfo);
55         return 0;
56     }
57 
58     jpeg_create_decompress(cinfo);
59     jpeg_stdio_src(cinfo, I->fp);
60 
61     /* Read the header of the image. */
62     jpeg_read_header(cinfo, TRUE);
63 
64     jpeg_start_decompress(cinfo);
65 
66     I->width = cinfo->output_width;
67     I->height = cinfo->output_height;
68 
69     return 1;
70 }
71 
72 /* jpeg_abort_load:
73  * Abort loading a JPEG after the header is done. */
jpeg_abort_load(img I)74 int jpeg_abort_load(img I) {
75     jpeg_finish_decompress((struct jpeg_decompress_struct*)I->us);
76     jpeg_destroy_decompress((struct jpeg_decompress_struct*)I->us);
77     return 1;
78 }
79 
80 /* jpeg_load_img:
81  * Read a JPEG file into an image. */
jpeg_load_img(img I)82 int jpeg_load_img(img I) {
83     struct jpeg_decompress_struct *cinfo = I->us;
84     struct my_error_mgr *jerr;
85     JSAMPARRAY buffer;
86     img_alloc(I);
87     jerr = (struct my_error_mgr*)cinfo->err;
88     if (setjmp(jerr->jb)) {
89         /* Oops, something went wrong. */
90         I->err = IE_IMGFORMAT;
91         jpeg_destroy_decompress(cinfo);
92         return 0;
93     }
94 
95     cinfo->out_color_space = JCS_RGB;
96     cinfo->out_color_components = cinfo->output_components = 3;
97 
98     /* Start decompression. */
99     buffer = cinfo->mem->alloc_sarray((j_common_ptr)cinfo, JPOOL_IMAGE, cinfo->output_width * cinfo->output_components, 1);
100 
101     while (cinfo->output_scanline < cinfo->output_height) {
102         pel *p, *end;
103         unsigned char *q;
104         jpeg_read_scanlines(cinfo, buffer, 1);
105 
106         /* Now we have a buffer in RGB format. */
107         for (p = I->data[cinfo->output_scanline - 1], end = p + I->width, q = (unsigned char*)buffer[0]; p < end; ++p, q += 3)
108             *p = PEL(*q, *(q + 1), *(q + 2));
109     }
110 
111     jpeg_finish_decompress(cinfo);
112     jpeg_destroy_decompress(cinfo);
113 
114     return 1;
115 }
116 
117 /* jpeg_save_img:
118  * Write an image out into a JPEG file. */
jpeg_save_img(const img I,FILE * fp)119 int jpeg_save_img(const img I, FILE *fp) {
120     struct jpeg_compress_struct cinfo;
121     struct my_error_mgr jerr;
122     JSAMPROW *buffer;
123 
124     cinfo.err = jpeg_std_error(&jerr.pub);
125     jerr.pub.error_exit = my_error_exit;
126     if (setjmp(jerr.jb)) {
127         /* Oops, something went wrong. */
128         I->err = IE_SYSERROR;
129         jpeg_destroy_compress(&cinfo);
130         return 0;
131     }
132 
133     jpeg_create_compress(&cinfo);
134 
135     /* Compressor will write to fp. */
136     jpeg_stdio_dest(&cinfo, fp);
137 
138     /* Image parameters. */
139     cinfo.image_width = I->width;
140     cinfo.image_height = I->height;
141     cinfo.input_components = 3;
142     cinfo.in_color_space = JCS_RGB;
143 
144     /* Default parameters. */
145     jpeg_set_defaults(&cinfo);
146 
147     /* XXX compression quality? */
148 
149     jpeg_start_compress(&cinfo, TRUE);
150 
151     buffer = cinfo.mem->alloc_sarray((j_common_ptr)&cinfo, JPOOL_IMAGE, I->width * 3, 1);
152 
153     while (cinfo.next_scanline < cinfo.image_height) {
154         pel *p, *end;
155         unsigned char *q;
156         /* Copy image data into correct format. */
157         for (p = I->data[cinfo.next_scanline], end = p + I->width, q = (unsigned char*)buffer[0]; p < end; ++p) {
158             *q++ = (unsigned char)GETR(*p);
159             *q++ = (unsigned char)GETG(*p);
160             *q++ = (unsigned char)GETB(*p);
161         }
162 
163         /* Write scanline. */
164         jpeg_write_scanlines(&cinfo, buffer, 1);
165     }
166 
167     jpeg_finish_compress(&cinfo);
168     jpeg_destroy_compress(&cinfo);
169 
170     return 1;
171 }
172