0ced7628f741a0dd31a2e2454d07d187215d8513
[xonotic/netradiant.git] / plugins / image / jpeg.cpp
1 /*
2 Copyright (c) 2001, Loki software, inc.
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without modification, 
6 are permitted provided that the following conditions are met:
7
8 Redistributions of source code must retain the above copyright notice, this list 
9 of conditions and the following disclaimer.
10
11 Redistributions in binary form must reproduce the above copyright notice, this
12 list of conditions and the following disclaimer in the documentation and/or
13 other materials provided with the distribution.
14
15 Neither the name of Loki software nor the names of its contributors may be used 
16 to endorse or promote products derived from this software without specific prior 
17 written permission. 
18
19 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' 
20 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
21 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
22 DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY 
23 DIRECT,INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
24 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
25 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 
26 ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
27 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
28 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
29 */
30
31 //
32 // Functions to load JPEG files from a buffer, based on jdatasrc.c
33 //
34 // Leonardo Zide (leo@lokigames.com)
35 //
36
37 #include <setjmp.h>
38 #include <stdlib.h>
39 #include <stdio.h>
40 #include <string.h>
41 #include <glib.h>
42
43 #include <jpeglib.h>
44 #include <jerror.h>
45
46 #include "image.h"
47
48 /* Expanded data source object for stdio input */
49
50 typedef struct {
51   struct jpeg_source_mgr pub;   /* public fields */
52
53   int src_size;
54   JOCTET * src_buffer;
55
56   JOCTET * buffer;              /* start of buffer */
57   boolean start_of_file;        /* have we gotten any data yet? */
58 } my_source_mgr;
59
60 typedef my_source_mgr * my_src_ptr;
61
62 #define INPUT_BUF_SIZE  4096    /* choose an efficiently fread'able size */
63
64
65 /*
66  * Initialize source --- called by jpeg_read_header
67  * before any data is actually read.
68  */
69
70 static void my_init_source (j_decompress_ptr cinfo)
71 {
72   my_src_ptr src = (my_src_ptr) cinfo->src;
73
74   /* We reset the empty-input-file flag for each image,
75    * but we don't clear the input buffer.
76    * This is correct behavior for reading a series of images from one source.
77    */
78   src->start_of_file = TRUE;
79 }
80
81
82 /*
83  * Fill the input buffer --- called whenever buffer is emptied.
84  *
85  * In typical applications, this should read fresh data into the buffer
86  * (ignoring the current state of next_input_byte & bytes_in_buffer),
87  * reset the pointer & count to the start of the buffer, and return TRUE
88  * indicating that the buffer has been reloaded.  It is not necessary to
89  * fill the buffer entirely, only to obtain at least one more byte.
90  *
91  * There is no such thing as an EOF return.  If the end of the file has been
92  * reached, the routine has a choice of ERREXIT() or inserting fake data into
93  * the buffer.  In most cases, generating a warning message and inserting a
94  * fake EOI marker is the best course of action --- this will allow the
95  * decompressor to output however much of the image is there.  However,
96  * the resulting error message is misleading if the real problem is an empty
97  * input file, so we handle that case specially.
98  *
99  * In applications that need to be able to suspend compression due to input
100  * not being available yet, a FALSE return indicates that no more data can be
101  * obtained right now, but more may be forthcoming later.  In this situation,
102  * the decompressor will return to its caller (with an indication of the
103  * number of scanlines it has read, if any).  The application should resume
104  * decompression after it has loaded more data into the input buffer.  Note
105  * that there are substantial restrictions on the use of suspension --- see
106  * the documentation.
107  *
108  * When suspending, the decompressor will back up to a convenient restart point
109  * (typically the start of the current MCU). next_input_byte & bytes_in_buffer
110  * indicate where the restart point will be if the current call returns FALSE.
111  * Data beyond this point must be rescanned after resumption, so move it to
112  * the front of the buffer rather than discarding it.
113  */
114
115 static boolean my_fill_input_buffer (j_decompress_ptr cinfo)
116 {
117   my_src_ptr src = (my_src_ptr) cinfo->src;
118   size_t nbytes;
119
120   if (src->src_size > INPUT_BUF_SIZE)
121     nbytes = INPUT_BUF_SIZE;
122   else
123     nbytes = src->src_size;
124
125   memcpy (src->buffer, src->src_buffer, nbytes);
126   src->src_buffer += nbytes;
127   src->src_size -= nbytes;
128
129   if (nbytes <= 0) {
130     if (src->start_of_file)     /* Treat empty input file as fatal error */
131       ERREXIT(cinfo, JERR_INPUT_EMPTY);
132     WARNMS(cinfo, JWRN_JPEG_EOF);
133     /* Insert a fake EOI marker */
134     src->buffer[0] = (JOCTET) 0xFF;
135     src->buffer[1] = (JOCTET) JPEG_EOI;
136     nbytes = 2;
137   }
138
139   src->pub.next_input_byte = src->buffer;
140   src->pub.bytes_in_buffer = nbytes;
141   src->start_of_file = FALSE;
142
143   return TRUE;
144 }
145
146
147 /*
148  * Skip data --- used to skip over a potentially large amount of
149  * uninteresting data (such as an APPn marker).
150  *
151  * Writers of suspendable-input applications must note that skip_input_data
152  * is not granted the right to give a suspension return.  If the skip extends
153  * beyond the data currently in the buffer, the buffer can be marked empty so
154  * that the next read will cause a fill_input_buffer call that can suspend.
155  * Arranging for additional bytes to be discarded before reloading the input
156  * buffer is the application writer's problem.
157  */
158
159 static void my_skip_input_data (j_decompress_ptr cinfo, long num_bytes)
160 {
161   my_src_ptr src = (my_src_ptr) cinfo->src;
162
163   /* Just a dumb implementation for now.  Could use fseek() except
164    * it doesn't work on pipes.  Not clear that being smart is worth
165    * any trouble anyway --- large skips are infrequent.
166    */
167   if (num_bytes > 0) {
168     while (num_bytes > (long) src->pub.bytes_in_buffer) {
169       num_bytes -= (long) src->pub.bytes_in_buffer;
170       (void) my_fill_input_buffer(cinfo);
171       /* note we assume that fill_input_buffer will never return FALSE,
172        * so suspension need not be handled.
173        */
174     }
175     src->pub.next_input_byte += (size_t) num_bytes;
176     src->pub.bytes_in_buffer -= (size_t) num_bytes;
177   }
178 }
179
180
181 /*
182  * An additional method that can be provided by data source modules is the
183  * resync_to_restart method for error recovery in the presence of RST markers.
184  * For the moment, this source module just uses the default resync method
185  * provided by the JPEG library.  That method assumes that no backtracking
186  * is possible.
187  */
188
189
190 /*
191  * Terminate source --- called by jpeg_finish_decompress
192  * after all data has been read.  Often a no-op.
193  *
194  * NB: *not* called by jpeg_abort or jpeg_destroy; surrounding
195  * application must deal with any cleanup that should happen even
196  * for error exit.
197  */
198
199 static void my_term_source (j_decompress_ptr cinfo)
200 {
201   /* no work necessary here */
202 }
203
204
205 /*
206  * Prepare for input from a stdio stream.
207  * The caller must have already opened the stream, and is responsible
208  * for closing it after finishing decompression.
209  */
210
211 static void jpeg_buffer_src (j_decompress_ptr cinfo, void* buffer, int bufsize)
212 {
213   my_src_ptr src;
214
215   /* The source object and input buffer are made permanent so that a series
216    * of JPEG images can be read from the same file by calling jpeg_stdio_src
217    * only before the first one.  (If we discarded the buffer at the end of
218    * one image, we'd likely lose the start of the next one.)
219    * This makes it unsafe to use this manager and a different source
220    * manager serially with the same JPEG object.  Caveat programmer.
221    */
222   if (cinfo->src == NULL) {     /* first time for this JPEG object? */
223     cinfo->src = (struct jpeg_source_mgr *)
224       (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
225                                   sizeof (my_source_mgr));
226     src = (my_src_ptr) cinfo->src;
227     src->buffer = (JOCTET *)
228       (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
229                                   INPUT_BUF_SIZE * sizeof (JOCTET));
230   }
231
232   src = (my_src_ptr) cinfo->src;
233   src->pub.init_source = my_init_source;
234   src->pub.fill_input_buffer = my_fill_input_buffer;
235   src->pub.skip_input_data = my_skip_input_data;
236   src->pub.resync_to_restart = jpeg_resync_to_restart; /* use default method */
237   src->pub.term_source = my_term_source;
238   src->src_buffer = (JOCTET *)buffer;
239   src->src_size = bufsize;
240   src->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */
241   src->pub.next_input_byte = NULL; /* until buffer loaded */
242 }
243
244 // =============================================================================
245
246 static char errormsg[JMSG_LENGTH_MAX];
247
248 typedef struct my_jpeg_error_mgr
249 {
250   struct jpeg_error_mgr pub;  // "public" fields
251   jmp_buf setjmp_buffer;      // for return to caller 
252 } bt_jpeg_error_mgr;
253
254 static void my_jpeg_error_exit (j_common_ptr cinfo)
255 {
256   my_jpeg_error_mgr* myerr = (bt_jpeg_error_mgr*) cinfo->err;
257
258   (*cinfo->err->format_message) (cinfo, errormsg);
259
260   longjmp (myerr->setjmp_buffer, 1);
261 }
262
263 // stash a scanline
264 static void j_putRGBScanline (unsigned char* jpegline, int widthPix, unsigned char* outBuf, int row)
265 {
266   int offset = row * widthPix * 4;
267   int count;
268
269   for (count = 0; count < widthPix; count++) 
270   {
271     unsigned char iRed, iBlu, iGrn;
272     unsigned char *oRed, *oBlu, *oGrn, *oAlp;
273
274     iRed = *(jpegline + count * 3 + 0);
275     iGrn = *(jpegline + count * 3 + 1);
276     iBlu = *(jpegline + count * 3 + 2);
277
278     oRed = outBuf + offset + count * 4 + 0;
279     oGrn = outBuf + offset + count * 4 + 1;
280     oBlu = outBuf + offset + count * 4 + 2;
281     oAlp = outBuf + offset + count * 4 + 3;
282
283     *oRed = iRed;
284     *oGrn = iGrn;
285     *oBlu = iBlu;
286     *oAlp = 255;
287   }
288 }
289
290 // stash a scanline
291 static void j_putRGBAScanline (unsigned char* jpegline, int widthPix, unsigned char* outBuf, int row)
292 {
293   int offset = row * widthPix * 4;
294   int count;
295
296   for (count = 0; count < widthPix; count++) 
297   {
298     unsigned char iRed, iBlu, iGrn, iAlp;
299     unsigned char *oRed, *oBlu, *oGrn, *oAlp;
300
301     iRed = *(jpegline + count * 4 + 0);
302     iGrn = *(jpegline + count * 4 + 1);
303     iBlu = *(jpegline + count * 4 + 2);
304                 iAlp = *(jpegline + count * 4 + 3);
305
306     oRed = outBuf + offset + count * 4 + 0;
307     oGrn = outBuf + offset + count * 4 + 1;
308     oBlu = outBuf + offset + count * 4 + 2;
309     oAlp = outBuf + offset + count * 4 + 3;
310
311     *oRed = iRed;
312     *oGrn = iGrn;
313     *oBlu = iBlu;
314         // ydnar: see bug 900
315     *oAlp = 255;        //%     iAlp;
316   }
317 }
318
319 // stash a gray scanline
320 static void j_putGrayScanlineToRGB (unsigned char* jpegline, int widthPix, unsigned char* outBuf, int row)
321 {
322   int offset = row * widthPix * 4;
323   int count;
324
325   for (count = 0; count < widthPix; count++) 
326   {
327     unsigned char iGray;
328     unsigned char *oRed, *oBlu, *oGrn, *oAlp;
329
330     // get our grayscale value
331     iGray = *(jpegline + count);
332
333     oRed = outBuf + offset + count * 4;
334     oGrn = outBuf + offset + count * 4 + 1;
335     oBlu = outBuf + offset + count * 4 + 2;
336     oAlp = outBuf + offset + count * 4 + 3;
337
338     *oRed = iGray;
339     *oGrn = iGray;
340     *oBlu = iGray;
341     *oAlp = 255;
342   }
343 }
344
345 static int _LoadJPGBuff (void *src_buffer, int src_size, unsigned char **pic, int *width, int *height) 
346 {
347   struct jpeg_decompress_struct cinfo;
348   struct my_jpeg_error_mgr jerr;
349   JSAMPARRAY buffer;
350   int row_stride, size;
351
352   cinfo.err = jpeg_std_error (&jerr.pub);
353   jerr.pub.error_exit = my_jpeg_error_exit;
354
355   if (setjmp (jerr.setjmp_buffer))
356   {
357     *pic = (unsigned char*)errormsg;
358     jpeg_destroy_decompress (&cinfo);
359     return -1;
360   }
361
362   jpeg_create_decompress (&cinfo);
363   jpeg_buffer_src (&cinfo, src_buffer, src_size);
364   jpeg_read_header (&cinfo, TRUE);
365   jpeg_start_decompress (&cinfo);
366
367   row_stride = cinfo.output_width * cinfo.output_components;
368
369   size = cinfo.output_width * cinfo.output_height * 4;
370   *width = cinfo.output_width;
371   *height = cinfo.output_height;
372   *pic = (unsigned char*) (g_malloc (size+1));
373   memset (*pic, 0, size+1);
374
375   buffer = (*cinfo.mem->alloc_sarray) ((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1);
376
377   while (cinfo.output_scanline < cinfo.output_height)
378   {
379     jpeg_read_scanlines (&cinfo, buffer, 1);
380
381     if (cinfo.out_color_components == 4)
382       j_putRGBAScanline (buffer[0], cinfo.output_width, *pic, cinfo.output_scanline-1);
383     else if (cinfo.out_color_components == 3)
384       j_putRGBScanline (buffer[0], cinfo.output_width, *pic, cinfo.output_scanline-1);
385     else if (cinfo.out_color_components == 1)
386       j_putGrayScanlineToRGB (buffer[0], cinfo.output_width, *pic, cinfo.output_scanline-1);
387   }
388
389   jpeg_finish_decompress (&cinfo);
390   jpeg_destroy_decompress (&cinfo);
391
392   return 0;
393 }
394
395 void LoadJPG (const char *filename, unsigned char **pic, int *width, int *height)
396 {
397   unsigned char *fbuffer = NULL;
398   int nLen = vfsLoadFile ((char *)filename, (void **)&fbuffer, 0 );
399   if (nLen == -1)
400     return;
401
402   if (_LoadJPGBuff (fbuffer, nLen, pic, width, height) != 0)
403   {
404     g_FuncTable.m_pfnSysPrintf( "WARNING: JPEG library failed to load %s because %s\n", filename, *pic );
405     *pic = NULL;
406   }
407
408   vfsFreeFile (fbuffer);
409 }