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