added page flipping/scrolling VBE calls
[dosrtxon] / libs / imago / src / filejpeg.c
1 /*
2 libimago - a multi-format image file input/output library.
3 Copyright (C) 2010 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
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24
25 #ifdef WIN32
26 #include <windows.h>
27 #define HAVE_BOOLEAN
28 #endif
29
30 #include <jpeglib.h>
31 #include "imago2.h"
32 #include "ftmodule.h"
33
34 #define INPUT_BUF_SIZE  512
35 #define OUTPUT_BUF_SIZE 512
36
37 /* data source manager: adapted from jdatasrc.c */
38 struct src_mgr {
39         struct jpeg_source_mgr pub;
40
41         struct img_io *io;
42         unsigned char buffer[INPUT_BUF_SIZE];
43         int start_of_file;
44 };
45
46 /* data destination manager: adapted from jdatadst.c */
47 struct dst_mgr {
48         struct jpeg_destination_mgr pub;
49
50         struct img_io *io;
51         unsigned char buffer[OUTPUT_BUF_SIZE];
52 };
53
54 static int check(struct img_io *io);
55 static int read(struct img_pixmap *img, struct img_io *io);
56 static int write(struct img_pixmap *img, struct img_io *io);
57
58 /* read source functions */
59 static void init_source(j_decompress_ptr jd);
60 static boolean fill_input_buffer(j_decompress_ptr jd);
61 static void skip_input_data(j_decompress_ptr jd, long num_bytes);
62 static void term_source(j_decompress_ptr jd);
63
64 /* write destination functions */
65 static void init_destination(j_compress_ptr jc);
66 static boolean empty_output_buffer(j_compress_ptr jc);
67 static void term_destination(j_compress_ptr jc);
68
69 int img_register_jpeg(void)
70 {
71         static struct ftype_module mod = {".jpg", check, read, write};
72         return img_register_module(&mod);
73 }
74
75
76 static int check(struct img_io *io)
77 {
78         unsigned char sig[10];
79
80         long pos = io->seek(0, SEEK_CUR, io->uptr);
81
82         if(io->read(sig, 10, io->uptr) < 10) {
83                 io->seek(pos, SEEK_SET, io->uptr);
84                 return -1;
85         }
86
87         if(memcmp(sig, "\xff\xd8\xff\xe0", 4) != 0 && memcmp(sig, "\xff\xd8\xff\xe1", 4) != 0
88                         && memcmp(sig, "\xff\xd8\xff\xdb", 4) != 0 && memcmp(sig + 6, "JFIF", 4) != 0) {
89                 io->seek(pos, SEEK_SET, io->uptr);
90                 return -1;
91         }
92         io->seek(pos, SEEK_SET, io->uptr);
93         return 0;
94 }
95
96 static int read(struct img_pixmap *img, struct img_io *io)
97 {
98         int i, nlines = 0;
99         struct jpeg_decompress_struct cinfo;
100         struct jpeg_error_mgr jerr;
101         struct src_mgr src;
102         unsigned char **scanlines;
103
104         io->seek(0, SEEK_CUR, io->uptr);
105
106         cinfo.err = jpeg_std_error(&jerr);      /* XXX change... */
107         jpeg_create_decompress(&cinfo);
108
109         src.pub.init_source = init_source;
110         src.pub.fill_input_buffer = fill_input_buffer;
111         src.pub.skip_input_data = skip_input_data;
112         src.pub.resync_to_restart = jpeg_resync_to_restart;
113         src.pub.term_source = term_source;
114         src.pub.next_input_byte = 0;
115         src.pub.bytes_in_buffer = 0;
116         src.io = io;
117         cinfo.src = (struct jpeg_source_mgr*)&src;
118
119         jpeg_read_header(&cinfo, 1);
120         cinfo.out_color_space = JCS_RGB;
121
122         if(img_set_pixels(img, cinfo.image_width, cinfo.image_height, IMG_FMT_RGB24, 0) == -1) {
123                 jpeg_destroy_decompress(&cinfo);
124                 return -1;
125         }
126
127         if(!(scanlines = malloc(img->height * sizeof *scanlines))) {
128                 jpeg_destroy_decompress(&cinfo);
129                 return -1;
130         }
131         scanlines[0] = img->pixels;
132         for(i=1; i<img->height; i++) {
133                 scanlines[i] = scanlines[i - 1] + img->width * img->pixelsz;
134         }
135
136         jpeg_start_decompress(&cinfo);
137         while(nlines < img->height) {
138                 int res = jpeg_read_scanlines(&cinfo, scanlines + nlines, img->height - nlines);
139                 nlines += res;
140         }
141         jpeg_finish_decompress(&cinfo);
142         jpeg_destroy_decompress(&cinfo);
143
144         free(scanlines);
145         return 0;
146 }
147
148 static int write(struct img_pixmap *img, struct img_io *io)
149 {
150         int i, nlines = 0;
151         struct jpeg_compress_struct cinfo;
152         struct jpeg_error_mgr jerr;
153         struct dst_mgr dest;
154         struct img_pixmap tmpimg;
155         unsigned char **scanlines;
156
157         img_init(&tmpimg);
158
159         if(img->fmt != IMG_FMT_RGB24) {
160                 if(img_copy(&tmpimg, img) == -1) {
161                         return -1;
162                 }
163                 if(img_convert(&tmpimg, IMG_FMT_RGB24) == -1) {
164                         img_destroy(&tmpimg);
165                         return -1;
166                 }
167                 img = &tmpimg;
168         }
169
170         if(!(scanlines = malloc(img->height * sizeof *scanlines))) {
171                 img_destroy(&tmpimg);
172                 return -1;
173         }
174         scanlines[0] = img->pixels;
175         for(i=1; i<img->height; i++) {
176                 scanlines[i] = scanlines[i - 1] + img->width * img->pixelsz;
177         }
178
179         cinfo.err = jpeg_std_error(&jerr);      /* XXX */
180         jpeg_create_compress(&cinfo);
181
182         dest.pub.init_destination = init_destination;
183         dest.pub.empty_output_buffer = empty_output_buffer;
184         dest.pub.term_destination = term_destination;
185         dest.io = io;
186         cinfo.dest = (struct jpeg_destination_mgr*)&dest;
187
188         cinfo.image_width = img->width;
189         cinfo.image_height = img->height;
190         cinfo.input_components = 3;
191         cinfo.in_color_space = JCS_RGB;
192
193         jpeg_set_defaults(&cinfo);
194
195         jpeg_start_compress(&cinfo, 1);
196         while(nlines < img->height) {
197                 int res = jpeg_write_scanlines(&cinfo, scanlines + nlines, img->height - nlines);
198                 nlines += res;
199         }
200         jpeg_finish_compress(&cinfo);
201         jpeg_destroy_compress(&cinfo);
202
203         free(scanlines);
204         img_destroy(&tmpimg);
205         return 0;
206 }
207
208 /* -- read source functions --
209  * the following functions are adapted from jdatasrc.c in jpeglib
210  */
211 static void init_source(j_decompress_ptr jd)
212 {
213         struct src_mgr *src = (struct src_mgr*)jd->src;
214         src->start_of_file = 1;
215 }
216
217 static boolean fill_input_buffer(j_decompress_ptr jd)
218 {
219         struct src_mgr *src = (struct src_mgr*)jd->src;
220         size_t nbytes;
221
222         nbytes = src->io->read(src->buffer, INPUT_BUF_SIZE, src->io->uptr);
223
224         if(nbytes <= 0) {
225                 if(src->start_of_file) {
226                         return 0;
227                 }
228                 /* insert a fake EOI marker */
229                 src->buffer[0] = 0xff;
230                 src->buffer[1] = JPEG_EOI;
231                 nbytes = 2;
232         }
233
234         src->pub.next_input_byte = src->buffer;
235         src->pub.bytes_in_buffer = nbytes;
236         src->start_of_file = 0;
237         return 1;
238 }
239
240 static void skip_input_data(j_decompress_ptr jd, long num_bytes)
241 {
242         struct src_mgr *src = (struct src_mgr*)jd->src;
243
244         if(num_bytes > 0) {
245                 while(num_bytes > (long)src->pub.bytes_in_buffer) {
246                         num_bytes -= (long)src->pub.bytes_in_buffer;
247                         fill_input_buffer(jd);
248                 }
249                 src->pub.next_input_byte += (size_t)num_bytes;
250                 src->pub.bytes_in_buffer -= (size_t)num_bytes;
251         }
252 }
253
254 static void term_source(j_decompress_ptr jd)
255 {
256         /* nothing to see here, move along */
257 }
258
259
260 /* -- write destination functions --
261  * the following functions are adapted from jdatadst.c in jpeglib
262  */
263 static void init_destination(j_compress_ptr jc)
264 {
265         struct dst_mgr *dest = (struct dst_mgr*)jc->dest;
266
267         dest->pub.next_output_byte = dest->buffer;
268         dest->pub.free_in_buffer = OUTPUT_BUF_SIZE;
269 }
270
271 static boolean empty_output_buffer(j_compress_ptr jc)
272 {
273         struct dst_mgr *dest = (struct dst_mgr*)jc->dest;
274
275         if(dest->io->write(dest->buffer, OUTPUT_BUF_SIZE, dest->io->uptr) != OUTPUT_BUF_SIZE) {
276                 return 0;
277         }
278
279         dest->pub.next_output_byte = dest->buffer;
280         dest->pub.free_in_buffer = OUTPUT_BUF_SIZE;
281         return 1;
282 }
283
284 static void term_destination(j_compress_ptr jc)
285 {
286         struct dst_mgr *dest = (struct dst_mgr*)jc->dest;
287         size_t datacount = OUTPUT_BUF_SIZE - dest->pub.free_in_buffer;
288
289         /* write any remaining data in the buffer */
290         if(datacount > 0) {
291                 dest->io->write(dest->buffer, datacount, dest->io->uptr);
292         }
293         /* XXX flush? ... */
294 }