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