1 /******************************************************************************
2 * $Id$
3 *
4 * Project: MapServer
5 * Purpose: MapCache tile caching support file: pixel manipulation operations
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 #ifdef USE_PIXMAN
32 #include <pixman.h>
33 #else
34 #include <math.h>
35 #endif
36
mapcache_image_create(mapcache_context * ctx)37 mapcache_image* mapcache_image_create(mapcache_context *ctx)
38 {
39 mapcache_image *img = (mapcache_image*)apr_pcalloc(ctx->pool,sizeof(mapcache_image));
40 img->w= img->h= 0;
41 img->data=NULL;
42 img->has_alpha = MC_ALPHA_UNKNOWN;
43 img->is_blank = MC_EMPTY_UNKNOWN;
44 return img;
45 }
46
mapcache_image_create_with_data(mapcache_context * ctx,int width,int height)47 mapcache_image* mapcache_image_create_with_data(mapcache_context *ctx, int width, int height) {
48 mapcache_image *img = (mapcache_image*)apr_pcalloc(ctx->pool,sizeof(mapcache_image));
49 img->w = width;
50 img->h = height;
51 img->data = calloc(1, width*height*4*sizeof(unsigned char));
52 apr_pool_cleanup_register(ctx->pool, img->data, (void*)free, apr_pool_cleanup_null) ;
53 img->stride = 4 * width;
54 img->has_alpha = MC_ALPHA_UNKNOWN;
55 img->is_blank = MC_EMPTY_UNKNOWN;
56 return img;
57 }
58
mapcache_image_has_alpha(mapcache_image * img,unsigned int cutoff)59 int mapcache_image_has_alpha(mapcache_image *img, unsigned int cutoff)
60 {
61 size_t i,j;
62 if(img->has_alpha == MC_ALPHA_UNKNOWN) {
63 unsigned char *ptr, *rptr = img->data;
64 for(i=0; i<img->h; i++) {
65 ptr = rptr;
66 for(j=0; j<img->w; j++) {
67 if(ptr[3]<(unsigned char)cutoff) {
68 img->has_alpha = MC_ALPHA_YES;
69 return 1;
70 }
71 ptr += 4;
72 }
73 rptr += img->stride;
74 }
75 img->has_alpha = MC_ALPHA_NO;
76 }
77 assert(img->has_alpha != MC_ALPHA_UNKNOWN);
78 if(img->has_alpha == MC_ALPHA_YES) {
79 return 1;
80 } else {
81 return 0;
82 }
83 }
84
mapcache_image_merge(mapcache_context * ctx,mapcache_image * base,mapcache_image * overlay)85 void mapcache_image_merge(mapcache_context *ctx, mapcache_image *base, mapcache_image *overlay)
86 {
87 int starti,startj;
88 #ifdef USE_PIXMAN
89 pixman_image_t *si;
90 pixman_image_t *bi;
91 pixman_transform_t transform;
92 #else
93 int i,j;
94 unsigned char *browptr, *orowptr, *bptr, *optr;
95 #endif
96
97 if(base->w < overlay->w || base->h < overlay->h) {
98 ctx->set_error(ctx, 500, "attempting to merge an larger image onto another");
99 return;
100 }
101
102 starti = (base->h - overlay->h)/2;
103 startj = (base->w - overlay->w)/2;
104 #ifdef USE_PIXMAN
105 si = pixman_image_create_bits(PIXMAN_a8r8g8b8,overlay->w,overlay->h,
106 (uint32_t*)overlay->data,overlay->stride);
107 bi = pixman_image_create_bits(PIXMAN_a8r8g8b8,base->w,base->h,
108 (uint32_t*)base->data,base->stride);
109 pixman_image_set_filter(si,PIXMAN_FILTER_NEAREST, NULL, 0);
110 if(starti || startj) {
111 pixman_transform_init_translate(&transform,
112 pixman_int_to_fixed(-startj),
113 pixman_int_to_fixed(-starti));
114 pixman_image_set_transform (si, &transform);
115 }
116 pixman_image_composite (PIXMAN_OP_OVER, si, NULL, bi,
117 0, 0, 0, 0, 0, 0, base->w,base->h);
118 pixman_image_unref(si);
119 pixman_image_unref(bi);
120 #else
121
122
123 browptr = base->data + starti * base->stride + startj*4;
124 orowptr = overlay->data;
125 for(i=0; i<overlay->h; i++) {
126 bptr = browptr;
127 optr = orowptr;
128 for(j=0; j<overlay->w; j++) {
129 if(optr[3]) { /* if overlay is not completely transparent */
130 if(optr[3] == 255) {
131 bptr[0]=optr[0];
132 bptr[1]=optr[1];
133 bptr[2]=optr[2];
134 bptr[3]=optr[3];
135 } else if(optr[3] != 0) {
136 unsigned int br = bptr[0];
137 unsigned int bg = bptr[1];
138 unsigned int bb = bptr[2];
139 unsigned int ba = bptr[3];
140 unsigned int or = optr[0];
141 unsigned int og = optr[1];
142 unsigned int ob = optr[2];
143 unsigned int oa = optr[3];
144 bptr[0] = (unsigned char)(or + (((255-oa)*br)>>8));
145 bptr[1] = (unsigned char)(og + (((255-oa)*bg)>>8));
146 bptr[2] = (unsigned char)(ob + (((255-oa)*bb)>>8));
147
148 bptr[3] = oa+((ba*(255-oa))>>8);
149 }
150 }
151 bptr+=4;
152 optr+=4;
153 }
154 browptr += base->stride;
155 orowptr += overlay->stride;
156 }
157 #endif
158 }
159
160 #ifndef USE_PIXMAN
161 #ifndef _WIN32
bilinear_pixel(mapcache_image * img,double x,double y,unsigned char * dst)162 static inline void bilinear_pixel(mapcache_image *img, double x, double y, unsigned char *dst)
163 {
164 #else
165 static __inline void bilinear_pixel(mapcache_image *img, double x, double y, unsigned char *dst)
166 {
167 #endif
168
169 int px,py;
170 int px1, py1;
171 unsigned char *p1, *p2, *p3, *p4;
172 float fx, fy, fx1, fy1;
173 int w1, w2, w3, w4;
174 px = (int)x;
175 py = (int)y;
176
177 px1 = (px==(img->w-1))?(px):(px+1);
178 py1 = (py==(img->h-1))?(py):(py+1);
179
180 p1 = &img->data[py*img->stride+px*4];
181 p2 = &img->data[py*img->stride+px1*4];
182 p3 = &img->data[py1*img->stride+px*4];
183 p4 = &img->data[py1*img->stride+px1*4];
184
185 // Calculate the weights for each pixel
186 fx = x - px;
187 fy = y - py;
188 fx1 = 1.0f - fx;
189 fy1 = 1.0f - fy;
190
191 w1 = fx1 * fy1 * 256.0f;
192 w2 = fx * fy1 * 256.0f;
193 w3 = fx1 * fy * 256.0f;
194 w4 = fx * fy * 256.0f;
195
196 // Calculate the weighted sum of pixels (for each color channel)
197 dst[0] = (p1[0] * w1 + p2[0] * w2 + p3[0] * w3 + p4[0] * w4) >> 8;
198 dst[1] = (p1[1] * w1 + p2[1] * w2 + p3[1] * w3 + p4[1] * w4) >> 8;
199 dst[2] = (p1[2] * w1 + p2[2] * w2 + p3[2] * w3 + p4[2] * w4) >> 8;
200 dst[3] = (p1[3] * w1 + p2[3] * w2 + p3[3] * w3 + p4[3] * w4) >> 8;
201 }
202 #endif
203
204 void mapcache_image_copy_resampled_nearest(mapcache_context *ctx, mapcache_image *src, mapcache_image *dst,
205 double off_x, double off_y, double scale_x, double scale_y)
206 {
207 #ifdef USE_PIXMAN
208 pixman_image_t *si = pixman_image_create_bits(PIXMAN_a8r8g8b8,src->w,src->h,
209 (uint32_t*)src->data,src->stride);
210 pixman_image_t *bi = pixman_image_create_bits(PIXMAN_a8r8g8b8,dst->w,dst->h,
211 (uint32_t*)dst->data,dst->stride);
212 pixman_transform_t transform;
213 pixman_transform_init_translate(&transform,pixman_double_to_fixed(-off_x),pixman_double_to_fixed(-off_y));
214 pixman_transform_scale(&transform,NULL,pixman_double_to_fixed(1.0/scale_x),pixman_double_to_fixed(1.0/scale_y));
215 pixman_image_set_transform (si, &transform);
216 pixman_image_set_filter(si,PIXMAN_FILTER_NEAREST, NULL, 0);
217 pixman_image_composite (PIXMAN_OP_SRC, si, NULL, bi,
218 0, 0, 0, 0, 0, 0, dst->w,dst->h);
219 pixman_image_unref(si);
220 pixman_image_unref(bi);
221 #else
222 int dstx,dsty;
223 unsigned char *dstrowptr = dst->data;
224 for(dsty=0; dsty<dst->h; dsty++) {
225 int *dstptr = (int*)dstrowptr;
226 int srcy = (int)(((dsty-off_y)/scale_y)+0.5);
227 if(srcy >= 0 && srcy < src->h) {
228 for(dstx=0; dstx<dst->w; dstx++) {
229 int srcx = (int)(((dstx-off_x)/scale_x)+0.5);
230 if(srcx >= 0 && srcx < src->w) {
231 *dstptr = *((int*)&(src->data[srcy*src->stride+srcx*4]));
232 }
233 dstptr ++;
234 }
235 }
236 dstrowptr += dst->stride;
237 }
238 #endif
239 }
240
241 void mapcache_image_copy_resampled_bilinear(mapcache_context *ctx, mapcache_image *src, mapcache_image *dst,
242 double off_x, double off_y, double scale_x, double scale_y, int reflect_edges)
243 {
244 #ifdef USE_PIXMAN
245 pixman_image_t *si = pixman_image_create_bits(PIXMAN_a8r8g8b8,src->w,src->h,
246 (uint32_t*)src->data,src->stride);
247 pixman_image_t *bi = pixman_image_create_bits(PIXMAN_a8r8g8b8,dst->w,dst->h,
248 (uint32_t*)dst->data,dst->stride);
249 pixman_transform_t transform;
250 pixman_transform_init_translate(&transform,pixman_double_to_fixed(-off_x),pixman_double_to_fixed(-off_y));
251 pixman_transform_scale(&transform,NULL,pixman_double_to_fixed(1.0/scale_x),pixman_double_to_fixed(1.0/scale_y));
252 pixman_image_set_transform (si, &transform);
253 if(reflect_edges) {
254 pixman_image_set_repeat (si, PIXMAN_REPEAT_REFLECT);
255 }
256 pixman_image_set_filter(si,PIXMAN_FILTER_BILINEAR, NULL, 0);
257 pixman_image_composite (PIXMAN_OP_OVER, si, NULL, bi,
258 0, 0, 0, 0, 0, 0, dst->w,dst->h);
259 pixman_image_unref(si);
260 pixman_image_unref(bi);
261 #else
262 int dstx,dsty;
263 unsigned char *dstrowptr = dst->data;
264 for(dsty=0; dsty<dst->h; dsty++) {
265 unsigned char *dstptr = dstrowptr;
266 double srcy = (dsty-off_y)/scale_y;
267 if(srcy >= 0 && srcy < src->h) {
268 for(dstx=0; dstx<dst->w; dstx++) {
269 double srcx = (dstx-off_x)/scale_x;
270 if(srcx >= 0 && srcx < src->w) {
271 bilinear_pixel(src,srcx,srcy,dstptr);
272 }
273 dstptr += 4;
274 }
275 }
276 dstrowptr += dst->stride;
277 }
278 #endif
279 }
280
281 void mapcache_image_metatile_split(mapcache_context *ctx, mapcache_metatile *mt)
282 {
283
284 if(mt->map.tileset->format) {
285 /* the tileset has a format defined, we will use it to encode the data */
286 mapcache_image *tileimg;
287 mapcache_image *metatile;
288 int i,j;
289 int sx,sy;
290
291 /*
292 ** No metatile support for raw format types...
293 */
294 if(mt->map.tileset->format->type == GC_RAW) {
295 mt->tiles[0].encoded_data = mt->map.encoded_data;
296 return;
297 }
298
299 if(mt->map.raw_image) {
300 metatile = mt->map.raw_image;
301 } else {
302 metatile = mapcache_imageio_decode(ctx, mt->map.encoded_data);
303 }
304 if(!metatile) {
305 ctx->set_error(ctx, 500, "failed to load image data from metatile");
306 return;
307 }
308 if(metatile->w != mt->map.width ||
309 metatile->h != mt->map.height) {
310 ctx->set_error(ctx, 500, "image size does not correspond to metatile size");
311 return;
312 }
313
314 for(i=0; i<mt->metasize_x; i++) {
315 for(j=0; j<mt->metasize_y; j++) {
316 tileimg = mapcache_image_create(ctx);
317 tileimg->w = mt->map.grid_link->grid->tile_sx;
318 tileimg->h = mt->map.grid_link->grid->tile_sy;
319 tileimg->stride = metatile->stride;
320 switch(mt->map.grid_link->grid->origin) {
321 case MAPCACHE_GRID_ORIGIN_BOTTOM_LEFT:
322 sx = mt->map.tileset->metabuffer + i * tileimg->w;
323 sy = mt->map.height - (mt->map.tileset->metabuffer + (j+1) * tileimg->h);
324 break;
325 case MAPCACHE_GRID_ORIGIN_TOP_LEFT:
326 sx = mt->map.tileset->metabuffer + i * tileimg->w;
327 sy = mt->map.tileset->metabuffer + j * tileimg->h;
328 break;
329 case MAPCACHE_GRID_ORIGIN_BOTTOM_RIGHT: /* FIXME not implemented */
330 sx = mt->map.tileset->metabuffer + i * tileimg->w;
331 sy = mt->map.height - (mt->map.tileset->metabuffer + (j+1) * tileimg->h);
332 break;
333 case MAPCACHE_GRID_ORIGIN_TOP_RIGHT: /* FIXME not implemented */
334 sx = mt->map.tileset->metabuffer + i * tileimg->w;
335 sy = mt->map.height - (mt->map.tileset->metabuffer + (j+1) * tileimg->h);
336 break;
337 default:
338 ctx->set_error(ctx,500,"BUG: unknown grid origin");
339 return;
340 }
341 tileimg->data = &(metatile->data[sy*metatile->stride + 4 * sx]);
342 if(mt->map.tileset->watermark) {
343 mapcache_image_merge(ctx,tileimg,mt->map.tileset->watermark);
344 GC_CHECK_ERROR(ctx);
345 }
346 mt->tiles[i*mt->metasize_y+j].raw_image = tileimg;
347 GC_CHECK_ERROR(ctx);
348 }
349 }
350 } else {
351 #ifdef DEBUG
352 if(mt->map.tileset->metasize_x != 1 ||
353 mt->map.tileset->metasize_y != 1 ||
354 mt->map.tileset->metabuffer != 0 ||
355 !mt->map.encoded_data) {
356 ctx->set_error(ctx, 500, "##### BUG ##### using a metatile with no format");
357 return;
358 }
359 #endif
360 mt->tiles[0].encoded_data = mt->map.encoded_data;
361 }
362 }
363
364 int mapcache_image_blank_color(mapcache_image* image)
365 {
366 if(image->is_blank == MC_EMPTY_UNKNOWN) {
367 int* pixptr;
368 int r,c;
369 for(r=0; r<image->h; r++) {
370 pixptr = (int*)(image->data + r * image->stride);
371 for(c=0; c<image->w; c++) {
372 if(*(pixptr++) != *((int*)image->data)) {
373 image->is_blank = MC_EMPTY_NO;
374 return MAPCACHE_FALSE;
375 }
376 }
377 }
378 image->is_blank = MC_EMPTY_YES;
379 }
380 assert(image->is_blank != MC_EMPTY_UNKNOWN);
381 if(image->is_blank == MC_EMPTY_YES)
382 return MAPCACHE_TRUE;
383 else
384 return MAPCACHE_FALSE;
385 }
386
387
388 void mapcache_image_fill(mapcache_context *ctx, mapcache_image *image, const unsigned char *fill_color) {
389 #if 0 && defined(USE_PIXMAN)
390 pixman_fill((uint32_t*)image->data, image->stride, 32, 0, 0, image->w, image->h, *((int*)fill_color) );
391 #else
392 int r,c;
393 unsigned char *pixptr;
394 for(r=0;r<image->h;r++) {
395 pixptr = image->data + image->stride * r;
396 for(c=0;c<image->w;c++) {
397 pixptr[0]=fill_color[0];
398 pixptr[1]=fill_color[1];
399 pixptr[2]=fill_color[2];
400 pixptr[3]=fill_color[3];
401 pixptr+=4;
402 }
403 }
404 #endif
405 }
406 /* vim: ts=2 sts=2 et sw=2
407 */
408