Skip to content

Commit

Permalink
Implement chainable compositing filters (RFC113)
Browse files Browse the repository at this point in the history
  • Loading branch information
tbonfort committed Oct 2, 2015
1 parent 0241ceb commit 4521ccf
Show file tree
Hide file tree
Showing 8 changed files with 409 additions and 21 deletions.
3 changes: 2 additions & 1 deletion CMakeLists.txt
Expand Up @@ -262,7 +262,8 @@ mapogcfiltercommon.c maprendering.c mapwcs20.c mapogcsld.c
mapresample.c mapwfs.c mapgdal.c mapogcsos.c mapscale.c mapwfs11.c mapwfs20.c
mapgeomtransform.c mapogroutput.c mapwfslayer.c mapagg.cpp mapkml.cpp
mapgeomutil.cpp mapkmlrenderer.cpp fontcache.c textlayout.c maputfgrid.cpp
mapogr.cpp mapcontour.c mapsmoothing.c mapv8.cpp ${REGEX_SOURCES} kerneldensity.c)
mapogr.cpp mapcontour.c mapsmoothing.c mapv8.cpp ${REGEX_SOURCES} kerneldensity.c
mapcompositingfilter.c)

set(mapserver_HEADERS
cgiutil.h dejavu-sans-condensed.h dxfcolor.h fontcache.h hittest.h mapagg.h
Expand Down
5 changes: 5 additions & 0 deletions HISTORY.TXT
Expand Up @@ -10,6 +10,11 @@ the top of the list.)

For a complete change history, please see the Git log comments.

7.2 release (FUTURE)
--------------------

- Implement chainable compositing filters (RFC113)

7.0.0 release (2015/07/24)
--------------------------

Expand Down
9 changes: 2 additions & 7 deletions mapagg.cpp
Expand Up @@ -1333,15 +1333,10 @@ int aggCompositeRasterBuffer(imageObj *dest, rasterBufferObj *overlay, Compositi
#endif
}

rasterBufferObj* msApplyFilterToRasterBuffer(const rasterBufferObj *rb, CompositingFilter *filter) {
rasterBufferObj *rbret = (rasterBufferObj*)msSmallCalloc(sizeof(rasterBufferObj),1);
msCopyRasterBuffer(rbret,rb);
rendering_buffer b(rbret->data.rgba.pixels, rbret->width, rbret->height, rbret->data.rgba.row_step);
void msApplyBlurringCompositingFilter(rasterBufferObj *rb, unsigned int radius) {
rendering_buffer b(rb->data.rgba.pixels, rb->width, rb->height, rb->data.rgba.row_step);
pixel_format pf(b);
/* for now, we only support a blurring filter */
int radius = atoi(filter->filter);
mapserver::stack_blur_rgba32(pf,radius,radius);
return rbret;
}

int msPopulateRendererVTableAGG(rendererVTableObj * renderer)
Expand Down
223 changes: 223 additions & 0 deletions mapcompositingfilter.c
@@ -0,0 +1,223 @@
/******************************************************************************
*
* Project: MapServer
* Purpose: RFC 113 Layer compositing
* Author: Thomas Bonfort and the MapServer team.
*
******************************************************************************
* Copyright (c) 1996-2015 Regents of the University of Minnesota.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies of this Software or works derived from this Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*****************************************************************************/
#include "mapserver.h"
#include <regex.h>
#define pixmove(rb,srcx,srcy,dstx,dsty) \
memcpy(rb->data.rgba.pixels+dsty*rb->data.rgba.row_step+dstx*4,\
rb->data.rgba.pixels+srcy*rb->data.rgba.row_step+srcx*4,\
4)
#define pixerase(rb,x,y) memset(rb->data.rgba.pixels+y*rb->data.rgba.row_step+x*4,0,4)

void msApplyTranslationCompositingFilter(rasterBufferObj *rb, int xtrans, int ytrans) {
int src_sx,src_sy,dst_sx,dst_sy,x,y,dst_x,dst_y;
if(abs(xtrans)>=rb->width || abs(ytrans)>=rb->height) {
for(y = 0; y<rb->height; y++)
for(x = 0; x<rb->width; x++)
pixerase(rb,x,y);
}
if(xtrans == 0 && ytrans == 0)
return;
if(xtrans>=0) {
if(ytrans>=0) {
src_sx = rb->width - xtrans - 1;
src_sy = rb->height - ytrans - 1;
dst_sx = rb->width - 1;
dst_sy = rb->height -1;
for(y = src_sy,dst_y= dst_sy;y>=0;y--,dst_y--) {
for(x = src_sx,dst_x= dst_sx;x>=0;x--,dst_x--) {
pixmove(rb,x,y,dst_x,dst_y);
}
}
for(y=0;y<ytrans;y++)
for(x=0;x<rb->width;x++)
pixerase(rb,x,y);
for(y=ytrans;y<rb->height;y++)
for(x=0;x<xtrans;x++)
pixerase(rb,x,y);
} else {
src_sx = rb->width - xtrans - 1;
src_sy = - ytrans;
dst_sx = rb->width - 1;
dst_sy = 0;
for(y = src_sy,dst_y= dst_sy;y<rb->height;y++,dst_y++) {
for(x = src_sx,dst_x= dst_sx;x>=0;x--,dst_x--) {
pixmove(rb,x,y,dst_x,dst_y);
}
}
for(y=0;y<rb->height+ytrans;y++)
for(x=0;x<xtrans;x++)
pixerase(rb,x,y);
for(y=rb->height+ytrans;y<rb->height;y++)
for(x=0;x<rb->width;x++)
pixerase(rb,x,y);
}
} else {
if(ytrans>=0) {
src_sx = - xtrans;
src_sy = rb->height - ytrans - 1;
dst_sx = 0;
dst_sy = rb->height -1;
for(y = src_sy,dst_y= dst_sy;y>=0;y--,dst_y--) {
for(x = src_sx,dst_x= dst_sx;x<rb->width;x++,dst_x++) {
pixmove(rb,x,y,dst_x,dst_y);
}
}
for(y=0;y<ytrans;y++)
for(x=0;x<rb->width;x++)
pixerase(rb,x,y);
for(y=ytrans;y<rb->height;y++)
for(x=rb->width+xtrans;x<rb->width;x++)
pixerase(rb,x,y);
} else {
src_sx = - xtrans;
src_sy = - ytrans;
dst_sx = 0;
dst_sy = 0;
for(y = src_sy,dst_y= dst_sy;y<rb->height;y++,dst_y++) {
for(x = src_sx,dst_x= dst_sx;x<rb->width;x++,dst_x++) {
pixmove(rb,x,y,dst_x,dst_y);
}
}
for(y=0;y<rb->height+ytrans;y++)
for(x=rb->width+xtrans;x<rb->width;x++)
pixerase(rb,x,y);
for(y=rb->height+ytrans;y<rb->height;y++)
for(x=0;x<rb->width;x++)
pixerase(rb,x,y);
}
}
}

void msApplyBlackeningCompositingFilter(rasterBufferObj *rb) {
int row,col;
unsigned char *r,*g,*b;
for(row=0;row<rb->height;row++) {
r = rb->data.rgba.r + row*rb->data.rgba.row_step;
g = rb->data.rgba.g + row*rb->data.rgba.row_step;
b = rb->data.rgba.b + row*rb->data.rgba.row_step;
for(col=0;col<rb->width;col++) {
*r = *g = *b = 0;
r+=4;g+=4;b+=4;
}
}
}

void msApplyWhiteningCompositingFilter(rasterBufferObj *rb) {
int row,col;
unsigned char *r,*g,*b,*a;
for(row=0;row<rb->height;row++) {
r = rb->data.rgba.r + row*rb->data.rgba.row_step;
g = rb->data.rgba.g + row*rb->data.rgba.row_step;
b = rb->data.rgba.b + row*rb->data.rgba.row_step;
a = rb->data.rgba.a + row*rb->data.rgba.row_step;
for(col=0;col<rb->width;col++) {
*r = *g = *b = *a;
r+=4;g+=4;b+=4;a+=4;
}
}
}

void msApplyGrayscaleCompositingFilter(rasterBufferObj *rb) {
int row,col;
unsigned char *r,*g,*b;
for(row=0;row<rb->height;row++) {
r = rb->data.rgba.r + row*rb->data.rgba.row_step;
g = rb->data.rgba.g + row*rb->data.rgba.row_step;
b = rb->data.rgba.b + row*rb->data.rgba.row_step;
for(col=0;col<rb->width;col++) {
unsigned int mix = (unsigned int)*r + (unsigned int)*g + (unsigned int)*b;
mix /=3;
*r = *g = *b = (unsigned char)mix;
r+=4;g+=4;b+=4;
}
}
}

int msApplyCompositingFilter(mapObj *map, rasterBufferObj *rb, CompositingFilter *filter) {
int rstatus;
regex_t regex;
regmatch_t pmatch[3];

/* test for blurring filter */
regcomp(&regex, "blur\\(([0-9]+)\\)", REG_EXTENDED);
rstatus = regexec(&regex, filter->filter, 2, pmatch, 0);
regfree(&regex);
if(!rstatus) {
char *rad = malloc(pmatch[1].rm_eo - pmatch[1].rm_so + 1);
unsigned int irad;
strncpy(rad,filter->filter+pmatch[1].rm_so,pmatch[1].rm_eo-pmatch[1].rm_so);
rad[pmatch[1].rm_eo - pmatch[1].rm_so]=0;
//msDebug("got blur filter with radius %s\n",rad);
irad = atoi(rad);
free(rad);
irad = MS_NINT(irad*map->resolution/map->defresolution);
msApplyBlurringCompositingFilter(rb,irad);
return MS_SUCCESS;
}

/* test for translation filter */
regcomp(&regex, "translate\\((-?[0-9]+),(-?[0-9]+)\\)", REG_EXTENDED);
rstatus = regexec(&regex, filter->filter, 3, pmatch, 0);
regfree(&regex);
if(!rstatus) {
char *num;
int xtrans,ytrans;
num = malloc(pmatch[1].rm_eo - pmatch[1].rm_so + 1);
strncpy(num,filter->filter+pmatch[1].rm_so,pmatch[1].rm_eo-pmatch[1].rm_so);
num[pmatch[1].rm_eo - pmatch[1].rm_so]=0;
xtrans = atoi(num);
free(num);
num = malloc(pmatch[2].rm_eo - pmatch[2].rm_so + 1);
strncpy(num,filter->filter+pmatch[2].rm_so,pmatch[2].rm_eo-pmatch[2].rm_so);
num[pmatch[2].rm_eo - pmatch[2].rm_so]=0;
ytrans = atoi(num);
free(num);
//msDebug("got translation filter of radius %d,%d\n",xtrans,ytrans);
xtrans = MS_NINT(xtrans*map->resolution/map->defresolution);
ytrans = MS_NINT(ytrans*map->resolution/map->defresolution);
msApplyTranslationCompositingFilter(rb,xtrans,ytrans);
return MS_SUCCESS;
}

/* test for grayscale filter */
if(!strncmp(filter->filter,"grayscale()",strlen("grayscale()"))) {
msApplyGrayscaleCompositingFilter(rb);
return MS_SUCCESS;
}
if(!strncmp(filter->filter,"blacken()",strlen("blacken()"))) {
msApplyBlackeningCompositingFilter(rb);
return MS_SUCCESS;
}
if(!strncmp(filter->filter,"whiten()",strlen("whiten()"))) {
msApplyWhiteningCompositingFilter(rb);
return MS_SUCCESS;
}

msSetError(MS_MISCERR,"unknown compositing filter (%s)", "msApplyCompositingFilter()", filter->filter);
return MS_FAILURE;
}
22 changes: 21 additions & 1 deletion mapcopy.c
Expand Up @@ -925,6 +925,26 @@ int msCopyScaleToken(scaleTokenObj *src, scaleTokenObj *dst) {
return MS_SUCCESS;
}

int msCopyCompositingFilter(CompositingFilter **pdst, CompositingFilter *src) {
CompositingFilter *dst = NULL;
if(!src) {
*pdst = NULL;
return MS_SUCCESS;
}
while(src) {
if(!dst) {
dst = *pdst = msSmallMalloc(sizeof(CompositingFilter));
} else {
dst->next = msSmallMalloc(sizeof(CompositingFilter));
dst = dst->next;
}
dst->filter = msStrdup(src->filter);
dst->next = NULL;
src = src->next;
}
return MS_SUCCESS;
}

int msCopyCompositer(LayerCompositer **ldst, LayerCompositer *src) {
LayerCompositer *dst = NULL;
if(!src) {
Expand All @@ -942,7 +962,7 @@ int msCopyCompositer(LayerCompositer **ldst, LayerCompositer *src) {
dst->comp_op = src->comp_op;
dst->opacity = src->opacity;
dst->next = NULL;
/* TODO dst->filter */
msCopyCompositingFilter(&dst->filter, src->filter);
src = src->next;
}
return MS_SUCCESS;
Expand Down
21 changes: 15 additions & 6 deletions mapdraw.c
Expand Up @@ -179,15 +179,24 @@ imageObj *msPrepareImage(mapObj *map, int allow_nonsquare)

}

static int msCompositeRasterBuffer(imageObj *img, rasterBufferObj *rb, LayerCompositer *comp) {
static int msCompositeRasterBuffer(mapObj *map, imageObj *img, rasterBufferObj *rb, LayerCompositer *comp) {
int ret = MS_SUCCESS;
if(MS_IMAGE_RENDERER(img)->compositeRasterBuffer) {
while(comp && ret == MS_SUCCESS) {
rasterBufferObj *rb_ptr = rb;
if(comp->filter) {
rb_ptr = msApplyFilterToRasterBuffer(rb,comp->filter);
}
ret = MS_IMAGE_RENDERER(img)->compositeRasterBuffer(img,rb_ptr,comp->comp_op, comp->opacity);
CompositingFilter *filter = comp->filter;
if(filter && comp->next) {
/* if we have another compositor to apply, then we need to copy the rasterBufferObj. Otherwise
* we can work on it directly */
rb_ptr = (rasterBufferObj*)msSmallCalloc(sizeof(rasterBufferObj),1);
msCopyRasterBuffer(rb_ptr,rb);
}
while(filter && ret == MS_SUCCESS) {
ret = msApplyCompositingFilter(map,rb_ptr,filter);
filter = filter->next;
}
if(ret == MS_SUCCESS)
ret = MS_IMAGE_RENDERER(img)->compositeRasterBuffer(img,rb_ptr,comp->comp_op, comp->opacity);
if(rb_ptr != rb) {
msFreeRasterBuffer(rb_ptr);
msFree(rb_ptr);
Expand Down Expand Up @@ -870,7 +879,7 @@ int msDrawLayer(mapObj *map, layerObj *layer, imageObj *image)
/*we have a mask layer with no composition configured, do a nomral blend */
retcode = renderer->mergeRasterBuffer(image,&rb,1.0,0,0,0,0,rb.width,rb.height);
} else {
retcode = msCompositeRasterBuffer(image,&rb,layer->compositer);
retcode = msCompositeRasterBuffer(map,image,&rb,layer->compositer);
}
if(UNLIKELY(retcode == MS_FAILURE)) {
goto imagedraw_cleanup;
Expand Down

0 comments on commit 4521ccf

Please sign in to comment.