1 /******************************************************************************
2  * $Id$
3  *
4  * Project:  MapServer
5  * Purpose:  MapCache tile caching support file: JPEG format
6  * Author:   Thomas Bonfort and the MapServer team.
7  *
8  ******************************************************************************
9  * Copyright (c) 1996-2011 Regents of the University of Minnesota.
10  *
11  * Permission is hereby granted, free of charge, to any person obtaining a
12  * copy of this software and associated documentation files (the "Software"),
13  * to deal in the Software without restriction, including without limitation
14  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15  * and/or sell copies of the Software, and to permit persons to whom the
16  * Software is furnished to do so, subject to the following conditions:
17  *
18  * The above copyright notice and this permission notice shall be included in
19  * all copies of this Software or works derived from this Software.
20  *
21  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27  * DEALINGS IN THE SOFTWARE.
28  *****************************************************************************/
29 
30 #include "mapcache.h"
31 #include <apr_strings.h>
32 #include <jpeglib.h>
33 
34 /**\addtogroup imageio_jpg */
35 /** @{ */
36 
37 typedef struct {
38   struct jpeg_destination_mgr pub;
39   unsigned char *data;
40   mapcache_buffer *buffer;
41 } mapcache_jpeg_destination_mgr;
42 
43 
44 #define OUTPUT_BUF_SIZE 4096
45 
_mapcache_imageio_jpeg_init_source(j_decompress_ptr cinfo)46 static void _mapcache_imageio_jpeg_init_source(j_decompress_ptr cinfo)
47 {
48   /* nothing to do */
49 }
50 
_mapcache_imageio_jpeg_fill_input_buffer(j_decompress_ptr cinfo)51 static int _mapcache_imageio_jpeg_fill_input_buffer(j_decompress_ptr cinfo)
52 {
53   static JOCTET mybuffer[4];
54 
55   /* The whole JPEG data is expected to reside in the supplied memory
56    * buffer, so any request for more data beyond the given buffer size
57    * is treated as an error.
58    */
59   /* Insert a fake EOI marker */
60   mybuffer[0] = (JOCTET) 0xFF;
61   mybuffer[1] = (JOCTET) JPEG_EOI;
62 
63   cinfo->src->next_input_byte = mybuffer;
64   cinfo->src->bytes_in_buffer = 2;
65 
66   return TRUE;
67 }
68 
_mapcache_imageio_jpeg_skip_input_data(j_decompress_ptr cinfo,long num_bytes)69 static void _mapcache_imageio_jpeg_skip_input_data(j_decompress_ptr cinfo, long num_bytes)
70 {
71   struct jpeg_source_mgr * src = cinfo->src;
72 
73   /* Just a dumb implementation for now.  Could use fseek() except
74    * it doesn't work on pipes.  Not clear that being smart is worth
75    * any trouble anyway --- large skips are infrequent.
76    */
77   if (num_bytes > 0) {
78     while (num_bytes > (long) src->bytes_in_buffer) {
79       num_bytes -= (long) src->bytes_in_buffer;
80       (void) (*src->fill_input_buffer) (cinfo);
81       /* note we assume that fill_input_buffer will never return FALSE,
82        * so suspension need not be handled.
83        */
84     }
85     src->next_input_byte += (size_t) num_bytes;
86     src->bytes_in_buffer -= (size_t) num_bytes;
87   }
88 }
89 
_mapcache_imageio_jpeg_term_source(j_decompress_ptr cinfo)90 static void _mapcache_imageio_jpeg_term_source(j_decompress_ptr cinfo)
91 {
92 }
93 
94 
95 
_mapcache_imageio_jpeg_mem_src(j_decompress_ptr cinfo,unsigned char * inbuffer,unsigned long insize)96 int _mapcache_imageio_jpeg_mem_src (j_decompress_ptr cinfo, unsigned char * inbuffer, unsigned long insize)
97 {
98   struct jpeg_source_mgr * src;
99 
100   if (inbuffer == NULL || insize == 0) /* Treat empty input as fatal error */
101     return MAPCACHE_FAILURE;
102 
103   /* The source object is made permanent so that a series of JPEG images
104    * can be read from the same buffer by calling jpeg_mem_src only before
105    * the first one.
106    */
107   if (cinfo->src == NULL) {   /* first time for this JPEG object? */
108     cinfo->src = (struct jpeg_source_mgr *)
109                  (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
110                      sizeof(struct jpeg_source_mgr));
111   }
112 
113   src = cinfo->src;
114   src->init_source = _mapcache_imageio_jpeg_init_source;
115   src->fill_input_buffer = _mapcache_imageio_jpeg_fill_input_buffer;
116   src->skip_input_data = _mapcache_imageio_jpeg_skip_input_data;
117   src->resync_to_restart = jpeg_resync_to_restart; /* use default method */
118   src->term_source = _mapcache_imageio_jpeg_term_source;
119   src->bytes_in_buffer = (size_t) insize;
120   src->next_input_byte = (JOCTET *) inbuffer;
121   return MAPCACHE_SUCCESS;
122 }
123 
124 
125 
_mapcache_imageio_jpeg_init_destination(j_compress_ptr cinfo)126 void _mapcache_imageio_jpeg_init_destination (j_compress_ptr cinfo)
127 {
128   mapcache_jpeg_destination_mgr *dest = (mapcache_jpeg_destination_mgr*) cinfo->dest;
129 
130   /* Allocate the output buffer --- it will be released when done with image */
131   dest->data = (unsigned char *)(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo,
132                JPOOL_IMAGE,OUTPUT_BUF_SIZE * sizeof (unsigned char));
133 
134   dest->pub.next_output_byte = dest->data;
135   dest->pub.free_in_buffer = OUTPUT_BUF_SIZE;
136 }
137 
_mapcache_imageio_jpeg_buffer_term_destination(j_compress_ptr cinfo)138 void _mapcache_imageio_jpeg_buffer_term_destination (j_compress_ptr cinfo)
139 {
140   mapcache_jpeg_destination_mgr *dest = (mapcache_jpeg_destination_mgr*) cinfo->dest;
141   mapcache_buffer_append(dest->buffer,OUTPUT_BUF_SIZE-dest->pub.free_in_buffer, dest->data);
142   dest->pub.next_output_byte = dest->data;
143   dest->pub.free_in_buffer = OUTPUT_BUF_SIZE;
144 }
145 
146 
_mapcache_imageio_jpeg_buffer_empty_output_buffer(j_compress_ptr cinfo)147 int _mapcache_imageio_jpeg_buffer_empty_output_buffer (j_compress_ptr cinfo)
148 {
149   mapcache_jpeg_destination_mgr *dest = (mapcache_jpeg_destination_mgr*) cinfo->dest;
150   mapcache_buffer_append(dest->buffer,OUTPUT_BUF_SIZE, dest->data);
151   dest->pub.next_output_byte = dest->data;
152   dest->pub.free_in_buffer = OUTPUT_BUF_SIZE;
153   return TRUE;
154 }
155 
_mapcache_imageio_jpeg_encode(mapcache_context * ctx,mapcache_image * img,mapcache_image_format * format)156 mapcache_buffer* _mapcache_imageio_jpeg_encode(mapcache_context *ctx, mapcache_image *img, mapcache_image_format *format)
157 {
158   struct jpeg_compress_struct cinfo;
159   struct jpeg_error_mgr jerr;
160   mapcache_jpeg_destination_mgr *dest;
161   JSAMPLE *rowdata;
162   unsigned int row;
163   mapcache_buffer *buffer = mapcache_buffer_create(5000, ctx->pool);
164   cinfo.err = jpeg_std_error(&jerr);
165   jpeg_create_compress(&cinfo);
166 
167   cinfo.dest = (struct jpeg_destination_mgr *)(*cinfo.mem->alloc_small) (
168                  (j_common_ptr) &cinfo, JPOOL_PERMANENT,
169                  sizeof (mapcache_jpeg_destination_mgr));
170   ((mapcache_jpeg_destination_mgr*)cinfo.dest)->pub.empty_output_buffer = _mapcache_imageio_jpeg_buffer_empty_output_buffer;
171   ((mapcache_jpeg_destination_mgr*)cinfo.dest)->pub.term_destination = _mapcache_imageio_jpeg_buffer_term_destination;
172   ((mapcache_jpeg_destination_mgr*)cinfo.dest)->buffer = buffer;
173 
174   dest = (mapcache_jpeg_destination_mgr*) cinfo.dest;
175   dest->pub.init_destination = _mapcache_imageio_jpeg_init_destination;
176 
177   cinfo.image_width = img->w;
178   cinfo.image_height = img->h;
179   cinfo.input_components = 3;
180   cinfo.in_color_space = JCS_RGB;
181   jpeg_set_defaults(&cinfo);
182   jpeg_set_quality(&cinfo, ((mapcache_image_format_jpeg*)format)->quality, TRUE);
183   switch(((mapcache_image_format_jpeg*)format)->photometric) {
184     case MAPCACHE_PHOTOMETRIC_RGB:
185       jpeg_set_colorspace(&cinfo, JCS_RGB);
186       break;
187     case MAPCACHE_PHOTOMETRIC_YCBCR:
188     default:
189       jpeg_set_colorspace(&cinfo, JCS_YCbCr);
190   }
191   switch(((mapcache_image_format_jpeg*)format)->optimize) {
192     case MAPCACHE_OPTIMIZE_NO:
193       cinfo.optimize_coding = FALSE;
194       break;
195     case MAPCACHE_OPTIMIZE_ARITHMETIC:
196       cinfo.optimize_coding = FALSE;
197       cinfo.arith_code = TRUE;
198       break;
199     case MAPCACHE_OPTIMIZE_YES:
200     default:
201       cinfo.optimize_coding = TRUE;
202   }
203   jpeg_start_compress(&cinfo, TRUE);
204 
205   rowdata = (JSAMPLE*)malloc(img->w*cinfo.input_components*sizeof(JSAMPLE));
206   for(row=0; row<img->h; row++) {
207     JSAMPLE *pixptr = rowdata;
208     int col;
209     unsigned char *r,*g,*b;
210     r=&(img->data[2])+row*img->stride;
211     g=&(img->data[1])+row*img->stride;
212     b=&(img->data[0])+row*img->stride;
213     for(col=0; col<img->w; col++) {
214       *(pixptr++) = *r;
215       *(pixptr++) = *g;
216       *(pixptr++) = *b;
217       r+=4;
218       g+=4;
219       b+=4;
220     }
221     (void) jpeg_write_scanlines(&cinfo, &rowdata, 1);
222   }
223 
224   /* Step 6: Finish compression */
225 
226   jpeg_finish_compress(&cinfo);
227   jpeg_destroy_compress(&cinfo);
228   free(rowdata);
229   return buffer;
230 }
231 
_mapcache_imageio_jpeg_decode_to_image(mapcache_context * r,mapcache_buffer * buffer,mapcache_image * img)232 void _mapcache_imageio_jpeg_decode_to_image(mapcache_context *r, mapcache_buffer *buffer,
233     mapcache_image *img)
234 {
235   int s;
236   struct jpeg_decompress_struct cinfo = {NULL};
237   struct jpeg_error_mgr jerr;
238   unsigned char *temp;
239   jpeg_create_decompress(&cinfo);
240   cinfo.err = jpeg_std_error(&jerr);
241   if (_mapcache_imageio_jpeg_mem_src(&cinfo,buffer->buf, buffer->size) != MAPCACHE_SUCCESS) {
242     r->set_error(r,500,"failed to allocate jpeg decoding struct");
243     return;
244   }
245 
246   img->has_alpha = MC_ALPHA_NO;
247   jpeg_read_header(&cinfo, TRUE);
248   jpeg_start_decompress(&cinfo);
249   img->w = cinfo.output_width;
250   img->h = cinfo.output_height;
251   s = cinfo.output_components;
252   if(!img->data) {
253     img->data = calloc(1,img->w*img->h*4*sizeof(unsigned char));
254     apr_pool_cleanup_register(r->pool, img->data, (void*)free, apr_pool_cleanup_null) ;
255     img->stride = img->w * 4;
256   }
257 
258   temp = malloc(img->w*s);
259   apr_pool_cleanup_register(r->pool, temp, (void*)free, apr_pool_cleanup_null) ;
260   while ((int)cinfo.output_scanline < img->h) {
261     int i;
262     unsigned char *rowptr = &img->data[cinfo.output_scanline * img->stride];
263     unsigned char *tempptr = temp;
264     jpeg_read_scanlines(&cinfo, &tempptr, 1);
265     if (s == 1) {
266       for (i = 0; i < img->w; i++) {
267         *rowptr++ = *tempptr;
268         *rowptr++ = *tempptr;
269         *rowptr++ = *tempptr;
270         *rowptr++ = 255;
271         tempptr++;
272       }
273     } else if (s == 3) {
274       for (i = 0; i < img->w; i++) {
275         rowptr[0] = tempptr[2];
276         rowptr[1] = tempptr[1];
277         rowptr[2] = tempptr[0];
278         rowptr[3] = 255;
279         rowptr+=4;
280         tempptr+=3;
281       }
282     } else {
283       r->set_error(r, 500, "unsupported jpeg format");
284       jpeg_destroy_decompress(&cinfo);
285       return;
286     }
287   }
288   jpeg_finish_decompress(&cinfo);
289   jpeg_destroy_decompress(&cinfo);
290 }
291 
_mapcache_imageio_jpeg_decode(mapcache_context * r,mapcache_buffer * buffer)292 mapcache_image* _mapcache_imageio_jpeg_decode(mapcache_context *r, mapcache_buffer *buffer)
293 {
294   mapcache_image *img = mapcache_image_create(r);
295   _mapcache_imageio_jpeg_decode_to_image(r, buffer,img);
296   if(GC_HAS_ERROR(r)) {
297     return NULL;
298   }
299   return img;
300 
301 }
302 
_mapcache_imageio_jpg_create_empty(mapcache_context * ctx,mapcache_image_format * format,size_t width,size_t height,unsigned int color)303 static mapcache_buffer* _mapcache_imageio_jpg_create_empty(mapcache_context *ctx, mapcache_image_format *format,
304     size_t width, size_t height, unsigned int color)
305 {
306   mapcache_image *empty;
307   mapcache_buffer *buf;
308   int i;
309   apr_pool_t *pool = NULL;
310   if(apr_pool_create(&pool,ctx->pool) != APR_SUCCESS) {
311     ctx->set_error(ctx,500,"png create empty: failed to create temp memory pool");
312     return NULL;
313   }
314   empty = mapcache_image_create(ctx);
315   if(GC_HAS_ERROR(ctx)) {
316     return NULL;
317   }
318   empty->data = malloc(width*height*4*sizeof(unsigned char));
319   for(i=0; i<width*height; i++) {
320     ((unsigned int*)empty->data)[i] = color;
321   }
322   empty->w = width;
323   empty->h = height;
324   empty->stride = width * 4;
325 
326   buf = format->write(ctx,empty,format);
327   apr_pool_destroy(pool);
328   free(empty->data);
329   return buf;
330 }
331 
mapcache_imageio_create_jpeg_format(apr_pool_t * pool,char * name,int quality,mapcache_photometric photometric,mapcache_optimization optimize)332 mapcache_image_format* mapcache_imageio_create_jpeg_format(apr_pool_t *pool, char *name, int quality,
333     mapcache_photometric photometric, mapcache_optimization optimize)
334 {
335   mapcache_image_format_jpeg *format = apr_pcalloc(pool, sizeof(mapcache_image_format_jpeg));
336   format->format.name = name;
337   format->format.extension = apr_pstrdup(pool,"jpg");
338   format->format.mime_type = apr_pstrdup(pool,"image/jpeg");
339   format->format.metadata = apr_table_make(pool,3);
340   format->format.create_empty_image = _mapcache_imageio_jpg_create_empty;
341   format->format.write = _mapcache_imageio_jpeg_encode;
342   format->quality = quality;
343   format->optimize = optimize;
344   format->photometric = photometric;
345   format->format.type = GC_JPEG;
346   return (mapcache_image_format*)format;
347 }
348 
349 /** @} */
350 
351 
352 
353 /* vim: ts=2 sts=2 et sw=2
354 */
355