more dos port
[retroray] / libs / imago / src / filejpeg.c
1 /*
2 libimago - a multi-format image file input/output library.
3 Copyright (C) 2010-2017 John Tsiombikas <nuclear@member.fsf.org>
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published
7 by the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License
16 along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 /* -- JPEG module -- */
20 #ifndef NO_JPEG
21
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25
26 #ifdef WIN32
27 #include <windows.h>
28 #define HAVE_BOOLEAN
29 #endif
30
31 #include <jpeglib.h>
32 #include "imago2.h"
33 #include "ftmodule.h"
34
35 #define INPUT_BUF_SIZE  512
36 #define OUTPUT_BUF_SIZE 512
37
38 /* data source manager: adapted from jdatasrc.c */
39 struct src_mgr {
40         struct jpeg_source_mgr pub;
41
42         struct img_io *io;
43         unsigned char buffer[INPUT_BUF_SIZE];
44         int start_of_file;
45 };
46
47 /* data destination manager: adapted from jdatadst.c */
48 struct dst_mgr {
49         struct jpeg_destination_mgr pub;
50
51         struct img_io *io;
52         unsigned char buffer[OUTPUT_BUF_SIZE];
53 };
54
55 static int check(struct img_io *io);
56 static int read(struct img_pixmap *img, struct img_io *io);
57 static int write(struct img_pixmap *img, struct img_io *io);
58
59 /* read source functions */
60 static void init_source(j_decompress_ptr jd);
61 static boolean fill_input_buffer(j_decompress_ptr jd);
62 static void skip_input_data(j_decompress_ptr jd, long num_bytes);
63 static void term_source(j_decompress_ptr jd);
64
65 /* write destination functions */
66 static void init_destination(j_compress_ptr jc);
67 static boolean empty_output_buffer(j_compress_ptr jc);
68 static void term_destination(j_compress_ptr jc);
69
70 int img_register_jpeg(void)
71 {
72         static struct ftype_module mod = {".jpg:.jpeg", check, read, write};
73         return img_register_module(&mod);
74 }
75
76
77 static int check(struct img_io *io)
78 {
79         unsigned char sig[10];
80
81         long pos = io->seek(0, SEEK_CUR, io->uptr);
82
83         if(io->read(sig, 10, io->uptr) < 10) {
84                 io->seek(pos, SEEK_SET, io->uptr);
85                 return -1;
86         }
87
88         if(memcmp(sig, "\xff\xd8\xff\xe0", 4) != 0 && memcmp(sig, "\xff\xd8\xff\xe1", 4) != 0
89                         && memcmp(sig, "\xff\xd8\xff\xdb", 4) != 0 && memcmp(sig + 6, "JFIF", 4) != 0) {
90                 io->seek(pos, SEEK_SET, io->uptr);
91                 return -1;
92         }
93         io->seek(pos, SEEK_SET, io->uptr);
94         return 0;
95 }
96
97 static int read(struct img_pixmap *img, struct img_io *io)
98 {
99         int i, nlines = 0;
100         struct jpeg_decompress_struct cinfo;
101         struct jpeg_error_mgr jerr;
102         struct src_mgr src;
103         unsigned char **scanlines;
104
105         io->seek(0, SEEK_CUR, io->uptr);
106
107         cinfo.err = jpeg_std_error(&jerr);      /* XXX change... */
108         jpeg_create_decompress(&cinfo);
109
110         src.pub.init_source = init_source;
111         src.pub.fill_input_buffer = fill_input_buffer;
112         src.pub.skip_input_data = skip_input_data;
113         src.pub.resync_to_restart = jpeg_resync_to_restart;
114         src.pub.term_source = term_source;
115         src.pub.next_input_byte = 0;
116         src.pub.bytes_in_buffer = 0;
117         src.io = io;
118         cinfo.src = (struct jpeg_source_mgr*)&src;
119
120         jpeg_read_header(&cinfo, 1);
121         cinfo.out_color_space = JCS_RGB;
122
123         if(img_set_pixels(img, cinfo.image_width, cinfo.image_height, IMG_FMT_RGB24, 0) == -1) {
124                 jpeg_destroy_decompress(&cinfo);
125                 return -1;
126         }
127
128         if(!(scanlines = malloc(img->height * sizeof *scanlines))) {
129                 jpeg_destroy_decompress(&cinfo);
130                 return -1;
131         }
132         scanlines[0] = img->pixels;
133         for(i=1; i<img->height; i++) {
134                 scanlines[i] = scanlines[i - 1] + img->width * img->pixelsz;
135         }
136
137         jpeg_start_decompress(&cinfo);
138         while(nlines < img->height) {
139                 int res = jpeg_read_scanlines(&cinfo, scanlines + nlines, img->height - nlines);
140                 nlines += res;
141         }
142         jpeg_finish_decompress(&cinfo);
143         jpeg_destroy_decompress(&cinfo);
144
145         free(scanlines);
146         return 0;
147 }
148
149 static int write(struct img_pixmap *img, struct img_io *io)
150 {
151         int i, nlines = 0;
152         struct jpeg_compress_struct cinfo;
153         struct jpeg_error_mgr jerr;
154         struct dst_mgr dest;
155         struct img_pixmap tmpimg;
156         unsigned char **scanlines;
157
158         img_init(&tmpimg);
159
160         if(img->fmt != IMG_FMT_RGB24) {
161                 if(img_copy(&tmpimg, img) == -1) {
162                         return -1;
163                 }
164                 if(img_convert(&tmpimg, IMG_FMT_RGB24) == -1) {
165                         img_destroy(&tmpimg);
166                         return -1;
167                 }
168                 img = &tmpimg;
169         }
170
171         if(!(scanlines = malloc(img->height * sizeof *scanlines))) {
172                 img_destroy(&tmpimg);
173                 return -1;
174         }
175         scanlines[0] = img->pixels;
176         for(i=1; i<img->height; i++) {
177                 scanlines[i] = scanlines[i - 1] + img->width * img->pixelsz;
178         }
179
180         cinfo.err = jpeg_std_error(&jerr);      /* XXX */
181         jpeg_create_compress(&cinfo);
182
183         dest.pub.init_destination = init_destination;
184         dest.pub.empty_output_buffer = empty_output_buffer;
185         dest.pub.term_destination = term_destination;
186         dest.io = io;
187         cinfo.dest = (struct jpeg_destination_mgr*)&dest;
188
189         cinfo.image_width = img->width;
190         cinfo.image_height = img->height;
191         cinfo.input_components = 3;
192         cinfo.in_color_space = JCS_RGB;
193
194         jpeg_set_defaults(&cinfo);
195         jpeg_set_quality(&cinfo, 95, 0);
196
197         jpeg_start_compress(&cinfo, 1);
198         while(nlines < img->height) {
199                 int res = jpeg_write_scanlines(&cinfo, scanlines + nlines, img->height - nlines);
200                 nlines += res;
201         }
202         jpeg_finish_compress(&cinfo);
203         jpeg_destroy_compress(&cinfo);
204
205         free(scanlines);
206         img_destroy(&tmpimg);
207         return 0;
208 }
209
210 /* -- read source functions --
211  * the following functions are adapted from jdatasrc.c in jpeglib
212  */
213 static void init_source(j_decompress_ptr jd)
214 {
215         struct src_mgr *src = (struct src_mgr*)jd->src;
216         src->start_of_file = 1;
217 }
218
219 static boolean fill_input_buffer(j_decompress_ptr jd)
220 {
221         struct src_mgr *src = (struct src_mgr*)jd->src;
222         size_t nbytes;
223
224         nbytes = src->io->read(src->buffer, INPUT_BUF_SIZE, src->io->uptr);
225
226         if(nbytes <= 0) {
227                 if(src->start_of_file) {
228                         return 0;
229                 }
230                 /* insert a fake EOI marker */
231                 src->buffer[0] = 0xff;
232                 src->buffer[1] = JPEG_EOI;
233                 nbytes = 2;
234         }
235
236         src->pub.next_input_byte = src->buffer;
237         src->pub.bytes_in_buffer = nbytes;
238         src->start_of_file = 0;
239         return 1;
240 }
241
242 static void skip_input_data(j_decompress_ptr jd, long num_bytes)
243 {
244         struct src_mgr *src = (struct src_mgr*)jd->src;
245
246         if(num_bytes > 0) {
247                 while(num_bytes > (long)src->pub.bytes_in_buffer) {
248                         num_bytes -= (long)src->pub.bytes_in_buffer;
249                         fill_input_buffer(jd);
250                 }
251                 src->pub.next_input_byte += (size_t)num_bytes;
252                 src->pub.bytes_in_buffer -= (size_t)num_bytes;
253         }
254 }
255
256 static void term_source(j_decompress_ptr jd)
257 {
258         /* nothing to see here, move along */
259 }
260
261
262 /* -- write destination functions --
263  * the following functions are adapted from jdatadst.c in jpeglib
264  */
265 static void init_destination(j_compress_ptr jc)
266 {
267         struct dst_mgr *dest = (struct dst_mgr*)jc->dest;
268
269         dest->pub.next_output_byte = dest->buffer;
270         dest->pub.free_in_buffer = OUTPUT_BUF_SIZE;
271 }
272
273 static boolean empty_output_buffer(j_compress_ptr jc)
274 {
275         struct dst_mgr *dest = (struct dst_mgr*)jc->dest;
276
277         if(dest->io->write(dest->buffer, OUTPUT_BUF_SIZE, dest->io->uptr) != OUTPUT_BUF_SIZE) {
278                 return 0;
279         }
280
281         dest->pub.next_output_byte = dest->buffer;
282         dest->pub.free_in_buffer = OUTPUT_BUF_SIZE;
283         return 1;
284 }
285
286 static void term_destination(j_compress_ptr jc)
287 {
288         struct dst_mgr *dest = (struct dst_mgr*)jc->dest;
289         size_t datacount = OUTPUT_BUF_SIZE - dest->pub.free_in_buffer;
290
291         /* write any remaining data in the buffer */
292         if(datacount > 0) {
293                 dest->io->write(dest->buffer, datacount, dest->io->uptr);
294         }
295         /* XXX flush? ... */
296 }
297
298 #else
299 /* build without JPEG support */
300 int img_register_jpeg(void)
301 {
302         return -1;
303 }
304 #endif