2cf357a9410c5fc382464039a39b978de4755f37
[oweals/cde.git] / cde / lib / DtHelp / JpegUtils.c
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these libraries and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 /* $XConsortium: JpegUtils.c /main/3 1996/10/06 19:38:48 rws $ */
24 /*
25 ** JpegUtils.c
26 **
27 ** This module provides utilities for converting jpeg data associated with
28 ** a _DtGrStream into an XImage.  The module is based on code from several 
29 ** of the files from the Independent JPEG library, version 6a (copyright 
30 ** attached below).
31 */
32
33 /*
34  * Copyright (C) 1991-1996, Thomas G. Lane.
35  * This file is part of the Independent JPEG Group's software.
36  * For conditions of distribution and use, see the accompanying README file.
37  *
38  */
39
40 /*
41 ** Include files
42 */
43
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include <math.h>
48 #include <setjmp.h>
49 #include "GraphicsP.h"
50 #include "jpeglib.h"
51 #include "cdjpeg.h"
52 #include "JpegUtilsI.h"
53
54 /*
55 ** Macro definitions
56 */
57
58 #define BYTESPERSAMPLE 1
59 #define MAX_COLORS 64        /* Not allowed to be higher than 256 */
60 #define BYTE_MAXVAL 0xFF
61 #define XCOL_MAXVAL 0xFFFF
62 #define INTERP_TO_XCOLORSPACE(val) ((XCOL_MAXVAL/BYTE_MAXVAL) * val)
63 #define INPUT_BUF_SIZE  4096    /* choose an efficiently fread'able size */
64
65 /*
66 ** Type definitions
67 */
68
69 /* 
70 ** Custom destination manager structure 
71 */
72 typedef struct {
73   struct djpeg_dest_struct pub; /* public fields */
74   JSAMPROW pixrow;              /* decompressor output buffer */
75   XImage *ximage;               /* XImage to put pixel info into */
76 } ximg_dest_struct;
77
78 typedef ximg_dest_struct * ximg_dest_ptr;
79
80 /* 
81 ** Custom source manager structure 
82 */
83 typedef struct {
84   struct jpeg_source_mgr pub;   /* public fields */
85
86   _DtGrStream *stream;          /* source stream */
87   unsigned long input_buf_size; /* size of input buffer */
88   JOCTET * buffer;              /* start of buffer */
89   boolean start_of_file;        /* have we gotten any data yet? */
90 } stream_source_mgr;
91
92 typedef stream_source_mgr * stream_src_ptr;
93
94 /* 
95 ** Custom error handler structure
96 */
97
98 struct my_error_mgr {
99   struct jpeg_error_mgr pub;    /* "public" fields */
100
101   jmp_buf setjmp_buffer;        /* for return to caller */
102 };
103
104 typedef struct my_error_mgr * my_error_ptr;
105
106 /*
107 ** Routines
108 */
109
110 /******************************************************************************
111  *
112  * Function my_error_exit
113  *
114  * This routine replaces the standard fatal error handling routine.  Instead
115  * of exiting the program, the routine longjmps back to jpeg_to_ximage,
116  * which cleans up and returns a _DtGrCONVERT_FAILURE.
117  *
118  *****************************************************************************/
119 static void my_error_exit (j_common_ptr cinfo)
120 {
121     my_error_ptr myerr = (my_error_ptr) cinfo->err;
122
123     (*cinfo->err->output_message) (cinfo);
124
125     /* Return control to the setjmp point */
126     longjmp(myerr->setjmp_buffer, 1);
127 }
128
129 /******************************************************************************
130  *
131  * Function my_output_message
132  *
133  * This routine replaces the standard error message outputter.  Instead
134  * of outputting JPEG library error and warning messages to stderr, it
135  * suppresses them.
136  *
137  *****************************************************************************/
138 static void my_output_message (j_common_ptr cinfo)
139 {
140     /* 
141     ** Uncommenting the lines below will cause error and warning messages
142     ** from the JPEG library to be displayed to stderr instead of suppressed
143     */
144
145     /*
146     ** char buffer[JMSG_LENGTH_MAX];
147     */
148
149     /* 
150     ** Create the message 
151     **
152     ** (*cinfo->err->format_message) (cinfo, buffer); 
153     */
154
155     /* 
156     ** Send it to stderr, adding a newline 
157     **
158     ** fprintf(stderr, "%s\n", buffer);  
159     */
160 }
161
162 /******************************************************************************
163  *
164  * Function init_source
165  *
166  * This is the custom source manager's initialization routine, called by 
167  * jpeg_read_header before any data is actually read.  It currently does
168  * nothing.
169  *
170  *****************************************************************************/
171 static void init_source (
172     j_decompress_ptr cinfo)
173 {
174 /*    stream_src_ptr src = (stream_src_ptr) cinfo->src; */
175 }
176
177 /******************************************************************************
178  *
179  * Function fill_input_buffer
180  *
181  * This is the custom source manager's fill input buffer routine, called by
182  * the JPEG library whenever it has finished processing the data in the 
183  * source buffer and needs it to be refilled with the next chunk of data.
184  *
185  *****************************************************************************/
186 static boolean fill_input_buffer (
187     j_decompress_ptr cinfo)
188 {
189     stream_src_ptr src = (stream_src_ptr) cinfo->src;
190     size_t nbytes;
191
192     /*
193     ** Since we always process buffer-based streams in a single chunk, the
194     ** only reason read_input_buffer should be called for one is if the data
195     ** in the buffer was truncated or otherwise bogus.  If this is the case
196     ** we set nbytes to zero, allocate a new buffer to hold a fake EOI marker
197     ** so the stream buffer isn't overwritten, and let the error-handling 
198     ** code below take care of things.
199     */
200     if (src->stream->type == _DtGrBUFFER)
201     {
202         nbytes = 0;
203         src->buffer = (JOCTET *) (*cinfo->mem->alloc_small) (
204                                           (j_common_ptr) cinfo, JPOOL_IMAGE,
205                                           2 * SIZEOF(JOCTET));
206     }
207     else /* _DtGrFILE, read the next chunk of data */
208         nbytes = _DtGrRead (src->buffer, 1, src->input_buf_size, src->stream);
209  
210     if (nbytes <= 0) 
211     {
212         if (src->start_of_file) /* Treat empty input file as fatal error */
213             ERREXIT(cinfo, JERR_INPUT_EMPTY);
214         WARNMS(cinfo, JWRN_JPEG_EOF);
215         /* Insert a fake EOI marker */
216         src->buffer[0] = (JOCTET) 0xFF;
217         src->buffer[1] = (JOCTET) JPEG_EOI;
218         nbytes = 2;
219     }
220
221     src->pub.next_input_byte = src->buffer;
222     src->pub.bytes_in_buffer = nbytes;
223     src->start_of_file = FALSE;
224
225     return TRUE;
226 }
227
228 /******************************************************************************
229  *
230  * Function skip_input_data
231  *
232  * This is the custom source manager's skip input data function, called
233  * by the JPEG library when it wants to skip over a potentially large
234  * amount of uninteresting data (such as an APPn marker).
235  *
236  *****************************************************************************/
237 static void skip_input_data (
238     j_decompress_ptr cinfo, 
239     long num_bytes)
240 {
241     stream_src_ptr src = (stream_src_ptr) cinfo->src;
242
243     /* Just a dumb implementation for now.  Could use fseek() except
244     ** it doesn't work on pipes.  Not clear that being smart is worth
245     ** any trouble anyway --- large skips are infrequent.
246     */
247     if (num_bytes > 0) 
248     {
249         while (num_bytes > (long) src->pub.bytes_in_buffer) 
250         {
251             num_bytes -= (long) src->pub.bytes_in_buffer;
252             (void) fill_input_buffer(cinfo);
253             /* note we assume that fill_input_buffer will never return FALSE,
254             ** so suspension need not be handled.
255             */
256         }
257         src->pub.next_input_byte += (size_t) num_bytes;
258         src->pub.bytes_in_buffer -= (size_t) num_bytes;
259     }
260 }
261
262 /******************************************************************************
263  *
264  * Function term_source
265  *
266  * This is the custom source manager's termination routine, it currently
267  * does nothing.
268  *
269  *****************************************************************************/
270 static void term_source (
271     j_decompress_ptr cinfo)
272 {
273   /* no work necessary here */
274   return;
275 }
276
277 /******************************************************************************
278  *
279  * Function jpeg_stream_src
280  *
281  * This is the custom source manager's creation routine.  Most of the custom
282  * source manager code is based on the JPEG library's stdio source manager
283  * code in the jdatasrc.c file included with the JPEG library distribution.
284  *
285  *****************************************************************************/
286 static void jpeg_stream_src (
287     j_decompress_ptr cinfo, 
288     _DtGrStream *stream)
289 {
290     stream_src_ptr src;
291
292     /*
293     ** Create the custom source manager structure
294     */
295     cinfo->src = (struct jpeg_source_mgr *) (*cinfo->mem->alloc_small) (
296                                             (j_common_ptr) cinfo, JPOOL_IMAGE,
297                                             SIZEOF(stream_source_mgr));
298     src = (stream_src_ptr) cinfo->src;
299
300     src->stream = stream;
301
302     /*
303     ** If this is a file-based stream, we need to allocate a buffer to
304     ** read data into.  If this is a buffer-based stream, we just use the
305     ** buffer already attached to the stream.  Note that this implies that
306     ** we always process buffer-based streams in a single chunk, if there
307     ** is ever a reason to do otherwise, this routine and fill_input_buffer 
308     ** will need to be modified appropriately.
309     */
310     if (stream->type == _DtGrFILE)
311     {
312         src->buffer = (JOCTET *) (*cinfo->mem->alloc_small) (
313                                         (j_common_ptr) cinfo, JPOOL_IMAGE,
314                                         INPUT_BUF_SIZE * SIZEOF(JOCTET));
315         src->input_buf_size = INPUT_BUF_SIZE;
316         src->pub.bytes_in_buffer = 0;    /* forces fill_input_buffer call */
317         src->pub.next_input_byte = NULL; /* on first read                 */
318         src->start_of_file = TRUE;
319     }
320     else /* _DtGrBUFFER */
321     {
322         src->buffer = (unsigned char *) stream->source.buffer.base;
323         src->input_buf_size = stream->source.buffer.size;
324         src->pub.bytes_in_buffer = src->input_buf_size;
325         src->pub.next_input_byte = src->buffer;
326         src->start_of_file = FALSE;
327     }
328
329     /*
330     ** Initialize the method procedures
331     */
332     src->pub.init_source = init_source;
333     src->pub.fill_input_buffer = fill_input_buffer;
334     src->pub.skip_input_data = skip_input_data;
335     src->pub.resync_to_restart = jpeg_resync_to_restart; /* default method */
336     src->pub.term_source = term_source;
337 }
338
339 /******************************************************************************
340  *
341  * Function copy_pixels
342  *
343  * This routine loops through a scanline of decompressed, quantized
344  * JPEG data and uses XPutPixel to copy the pixel values into the 
345  * XImage associated with the destination manager.
346  *
347  *****************************************************************************/
348 static void copy_pixels(
349     j_decompress_ptr cinfo, 
350     djpeg_dest_ptr   dinfo,
351     JDIMENSION       rows_supplied)
352 {
353     int pixval;
354     JSAMPROW ptr;
355     JDIMENSION col;
356     ximg_dest_ptr dest = (ximg_dest_ptr) dinfo;
357
358     ptr = dest->pub.buffer[0];
359     for (col=0; col < cinfo->output_width; col++) 
360     {
361         pixval = GETJSAMPLE(*ptr++);
362         XPutPixel(dest->ximage,col,cinfo->output_scanline-1, pixval);
363     }
364 }
365
366
367 /******************************************************************************
368  *
369  * Function start_output_ximg
370  *
371  * This is the data destination manager startup routine, it currently does
372  * nothing.
373  *
374  *****************************************************************************/
375 static void start_output_ximg (
376     j_decompress_ptr cinfo, 
377     djpeg_dest_ptr dinfo)
378 {
379     return;
380 }
381
382 /******************************************************************************
383  *
384  * Function finish_output_ximg
385  *
386  * This is the data destination manager shutdown routine, it currently does 
387  * nothing.
388  *
389  *****************************************************************************/
390 static void finish_output_ximg (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo)
391 {
392 }
393
394 /******************************************************************************
395  *
396  * Function init_jpeg_dest_mgr
397  *
398  * This routine allocates and initializes a data destination manager
399  * that the JPEG library will use as a sink for JPEG data after the
400  * data has been decompressed.
401  *
402  *****************************************************************************/
403 djpeg_dest_ptr init_jpeg_dest_mgr (
404     j_decompress_ptr cinfo)
405 {
406     ximg_dest_ptr dest;
407
408     /* 
409     ** Create module interface object, fill in the method pointers 
410     */
411     dest = (ximg_dest_ptr) (*cinfo->mem->alloc_small) (
412                                             (j_common_ptr) cinfo, JPOOL_IMAGE, 
413                                             SIZEOF(ximg_dest_struct));
414     dest->pub.start_output = start_output_ximg;
415     dest->pub.finish_output = finish_output_ximg;
416     dest->pub.put_pixel_rows = copy_pixels;
417
418     /*
419     ** Calculate the output image dimensions so we can allocate the
420     ** right amount of space 
421     */
422     jpeg_calc_output_dimensions(cinfo);
423
424     /*
425     ** Create a buffer for the JPEG library to write decompressed
426     ** scanline data into.
427     */
428     dest->pixrow = (JSAMPROW) (*cinfo->mem->alloc_small) (
429                           (j_common_ptr) cinfo, JPOOL_IMAGE,
430                           cinfo->output_width * cinfo->out_color_components );
431     dest->pub.buffer = &dest->pixrow;
432     dest->pub.buffer_height = 1;
433
434     /*
435     ** Return the initialized destination manager 
436     */
437     return (djpeg_dest_ptr) dest;
438 }
439
440 /******************************************************************************
441  *
442  * Function jpeg_to_ximage
443  *
444  * This routine converts compressed jpeg data associated with a _DtGrStream 
445  * into an XImage.  
446  *
447  * No X color allocation is done.  The image is quantized down to MAX_COLORS 
448  * during decompression, and an array of XColor structures with the red, 
449  * green, and blue fields initialized to the colors used in the image is
450  * returned to the caller.  Each pixel value in the XImage data is an index
451  * into this array.  The caller must use this information to allocate X
452  * color cells and substitute the appropriate pixel values into the XImage
453  * data array before using the XImage.
454  *
455  * The routine makes use of a custom source data manager to allow the JPEG
456  * data source to be a _DtGrStream, a custom data destination manager to 
457  * allow the decompressed and post-processed data to be written to an XImage,
458  * and a custom error handler to allow standard _DtGr error codes to be
459  * returned to the caller in the event of a JPEG library error.
460  *
461  *****************************************************************************/
462 enum _DtGrLoadStatus jpeg_to_ximage (
463     _DtGrStream           *stream,
464     Screen                *screen,
465     Visual                *visual,
466     Dimension             *in_out_width,
467     Dimension             *in_out_height,
468     XImage                **ximage,
469     XColor                **xcolors,
470     int                    *ncolors,
471     int                    *xres)
472 {
473     struct jpeg_decompress_struct cinfo;
474     struct my_error_mgr jerr;
475     djpeg_dest_ptr dest_mgr = NULL;
476     ximg_dest_ptr dest;
477     int i, num_scanlines, nullCount, ximWidth;
478     unsigned char *ximData = NULL;
479     Display *display = DisplayOfScreen(screen);
480     int nplanes = DisplayPlanes(display,XScreenNumberOfScreen(screen));
481     XColor *colors = NULL;
482
483     /*
484     ** Initialize the return values 
485     */
486     *ximage = NULL;
487     *xres = *ncolors = *in_out_width = *in_out_height = 0;
488
489     /*
490     ** Initialize the jpeg library error handler with our custom routines
491     */
492
493     cinfo.err = jpeg_std_error(&jerr.pub);
494     jerr.pub.error_exit = my_error_exit;
495     jerr.pub.output_message = my_output_message;
496
497     /* 
498     ** Establish the setjmp return context for my_error_exit to use
499     */
500     if (setjmp(jerr.setjmp_buffer)) 
501     {
502         /* If we get here, the JPEG code has signaled an error.  We need to
503         ** free memory, clean up the JPEG object, and return a failure code.
504         */
505         if (*ximage != NULL)
506             XDestroyImage (*ximage);
507         if (colors != NULL)
508             free (colors);
509         jpeg_destroy_decompress(&cinfo);
510         return (_DtGrCONVERT_FAILURE);
511     }
512
513     /*
514     ** Create a jpeg decompression object
515     */
516     jpeg_create_decompress(&cinfo);
517
518     /*
519     ** Create a custom source data manager
520     */
521     jpeg_stream_src(&cinfo, stream);
522
523     /*
524     ** Read the jpeg header 
525     */
526     jpeg_read_header(&cinfo, TRUE);
527     if (cinfo.X_density > 0 &&
528         (cinfo.density_unit == 1 || cinfo.density_unit == 2)) {
529         if (cinfo.density_unit == 1)
530             *xres = cinfo.X_density;
531         else
532             *xres = cinfo.X_density * 2.54 + 0.5;
533     }
534
535     /*
536     ** Initialize our desired post-processing attributes
537     */
538     cinfo.quantize_colors = TRUE;
539     cinfo.desired_number_of_colors = MAX_COLORS;
540
541     /*
542     ** Create a custom data destination manager to allow our processed data
543     ** to be channeled into an XImage.
544     */
545     dest_mgr = init_jpeg_dest_mgr(&cinfo);
546
547     /*
548     ** Initialize the decompression state
549     */
550     jpeg_start_decompress(&cinfo);
551     (*dest_mgr->start_output) (&cinfo, dest_mgr);
552
553     /*
554     ** Create an XImage to hold the processed data
555     */
556         
557     nullCount = (4 - (cinfo.output_width % 4)) & 0x03;
558     ximWidth = cinfo.output_width + nullCount;
559
560     if (nplanes > 8 )
561         ximData = (unsigned char *) malloc(ximWidth * 
562                                            cinfo.output_height * 4 );
563     else
564         ximData = (unsigned char *) malloc(ximWidth * cinfo.output_height );
565
566     if (!ximData) 
567     {
568         jpeg_destroy_decompress(&cinfo);
569         return (_DtGrNO_MEMORY);
570     }
571
572     *ximage = XCreateImage(display, visual, nplanes,
573                            (nplanes == 1) ? XYPixmap : ZPixmap, 
574                            0, (char *)ximData, cinfo.output_width, 
575                            cinfo.output_height, 32, 0);
576
577     if (!*ximage) 
578     {
579         free (ximData);
580         jpeg_destroy_decompress(&cinfo);
581         return (_DtGrCONVERT_FAILURE);
582     }
583
584     /*
585     ** Store the XImage in the custom destination manager
586     */
587     dest = (ximg_dest_ptr) dest_mgr;
588     dest->ximage = *ximage;
589
590     /*
591     ** Process scanlines until there are none left
592     */
593
594     while (cinfo.output_scanline < cinfo.output_height) 
595     {
596         num_scanlines = jpeg_read_scanlines(&cinfo, 
597                                             (JSAMPARRAY)dest_mgr->buffer, 
598                                             dest_mgr->buffer_height);
599         (*dest_mgr->put_pixel_rows) (&cinfo, dest_mgr, num_scanlines);
600     }
601
602     /*
603     ** Return the colormap info as an array of XColors which can be
604     ** used later for X color allocation purposes.
605     */
606
607     if (cinfo.actual_number_of_colors) 
608     {
609         colors = (XColor *) malloc((unsigned) sizeof(XColor) * 
610                                    cinfo.actual_number_of_colors);
611         if (!colors)
612         {
613             XDestroyImage (*ximage);
614             jpeg_destroy_decompress(&cinfo);
615             return (_DtGrNO_MEMORY);
616         }
617         for (i=0; i<cinfo.actual_number_of_colors; i++)
618         {
619             if (cinfo.out_color_space == JCS_GRAYSCALE)
620             {
621                 colors[i].red = colors[i].green = colors[i].blue = 
622                     INTERP_TO_XCOLORSPACE(cinfo.colormap[0][i]);
623             }
624             else /* JCS_RGB */
625             {
626                 colors[i].red   = INTERP_TO_XCOLORSPACE(cinfo.colormap[0][i]);
627                 colors[i].green = INTERP_TO_XCOLORSPACE(cinfo.colormap[1][i]);
628                 colors[i].blue  = INTERP_TO_XCOLORSPACE(cinfo.colormap[2][i]);
629             }
630         }
631
632         *xcolors = colors;            
633         *ncolors = cinfo.actual_number_of_colors;
634     }
635
636     /*
637     ** Set the other return parameters
638     */
639     *in_out_width = cinfo.output_width;
640     *in_out_height = cinfo.output_height;
641  
642     /*
643     ** Shut down the decompression engine and free the allocated memory
644     */
645     (*dest_mgr->finish_output) (&cinfo, dest_mgr);
646     jpeg_finish_decompress(&cinfo);
647     jpeg_destroy_decompress(&cinfo);
648
649     /*
650     ** Return success
651     */
652     return (_DtGrSUCCESS);
653 }