修改Nginx的image_filter模块源码实现图片裁剪

Nginx的http_image_filter插件可以方便地对图片进行缩放、裁剪、转换格式等等。官方文档:http://nginx.org/en/docs/http/ngx_http_image_filter_module.html

官方的filer包含以下几种:

image_filter off;
image_filter test;
image_filter size;
image_filter rotate 90 | 180 | 270;
image_filter resize width height;
image_filter crop width height;

其中crop为裁剪,只保留图片居中的部分。例如原图:http://127.0.0.1/uploads/36.jpg

裁剪后:http://127.0.0.1/crop/a/200_80/uploads/36.jpg

而我现在想要的效果是保留图片的上面部分,从上往下裁剪。而nginx不支持这种,所以修改了一下插件的源码,添加一种裁剪方法:croptop。

我用的是v1.5.3版本的nginx源码。修改代码 src/http/modules/ngx_http_image_filter_module.c:

$ diff ngx_http_image_filter_module.c ngx_http_image_filter_module.c.bak

21d20
< #define NGX_HTTP_IMAGE_CROP_TOP  6
825,835d823
<     } else if (conf->filter == NGX_HTTP_IMAGE_CROP_TOP) {
<
<         resize = 0;
<
<         if ((ngx_uint_t) dx > ctx->max_width) {
<             dy = dy * ctx->max_width / dx;
<             dy = dy ? dy : 1;
<             dx = ctx->max_width;
<             resize = 1;
<         }
<
927c915
<
---
>
977,1012d964
<     if (conf->filter == NGX_HTTP_IMAGE_CROP_TOP) {
<
<         src = dst;
<
<         if ((ngx_uint_t) dy > ctx->max_height) {
<             oy = dy - ctx->max_height;
<             ox = 0;
<             dst = ngx_http_image_new(r, dx - ox, dy - oy, colors);
<
<             if (dst == NULL) {
<                 gdImageDestroy(src);
<                 return NULL;
<             }
<
<             ox /= 2;
<             oy = 0;
<
<             ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
<                            "image crop: %d x %d @ %d x %d",
<                            dx, dy, ox, oy);
<
<             if (colors == 0) {
<                 gdImageSaveAlpha(dst, 1);
<                 gdImageAlphaBlending(dst, 0);
<             }
<
<             gdImageCopy(dst, src, 0, 0, ox, oy, dx - ox, ctx->max_height);
<
<             if (colors) {
<                 gdImageTrueColorToPalette(dst, 1, 256);
<             }
<
<             gdImageDestroy(src);
<         }
<     }
<
1383,1385d1334
<
<     } else if (ngx_strcmp(value[i].data, "croptop") == 0) {
<         imcf->filter = NGX_HTTP_IMAGE_CROP_TOP;

可根据此更改制作path包:

$ diff ngx_http_image_filter_module.c ngx_http_image_filter_module.c.bak > image_filter.patch
$ patch ngx_http_image_filter_module.c image_filter.patch

nginx 配置:

    location ~ /croptop/([^/]+)/(\d+)_(\d+)/(.*)$ {
         set $md5 $1;
         set $width $2;
         set $height $3;
         set $img_path $4;
         set $imguri http://127.0.0.1/$img_path;

         proxy_pass $imguri;
         image_filter croptop $width $height;
         image_filter_jpeg_quality 85;
    }

编译nginx:

sudo apt-get install openssl-dev
sudo apt-get install openssl
sudo apt-get install libssl0.9.8
sudo apt-get install libssl-dev
sudo apt-get install libpcre3 libpcre3-dev
sudo apt-get install libgd2-xpm-dev
sudo apt-get install libgeoip-dev

./configure --prefix=/usr/local/nginx --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log \
--http-client-body-temp-path=/var/lib/nginx/body --http-fastcgi-temp-path=/var/lib/nginx/fastcgi \
--http-log-path=/var/log/nginx/access.log --http-proxy-temp-path=/var/lib/nginx/proxy \
--http-scgi-temp-path=/var/lib/nginx/scgi --http-uwsgi-temp-path=/var/lib/nginx/uwsgi \
--lock-path=/var/lock/nginx.lock --pid-path=/run/nginx.pid --with-pcre-jit --with-debug \
--with-http_addition_module --with-http_dav_module --with-http_geoip_module --with-http_gzip_static_module \
--with-http_image_filter_module  --with-http_stub_status_module --with-http_ssl_module --with-http_sub_module \
--with-http_xslt_module --with-ipv6 --with-sha1=/usr/include/openssl --with-md5=/usr/include/openssl \
--with-mail --with-mail_ssl_module

访问新方法来裁剪图片 http://127.0.0.1/croptop/a/200_80/uploads/36.jpg,效果如下图:

原crop裁剪方法有个问题,就是当图片原宽度比裁剪宽度小的时候,比如原 232 x 350,要裁剪成 250 x 250,那么裁剪后变成 165 x 250,也就是它会把高350压缩成 250,宽度等比缩放到 165,而165比我们要的250要小,所以就不再裁剪了。

我想要的效果是,宽度小于250,就不缩放,直接按 232 x 232来裁剪出一个正方形。于是添加了一个自定义裁剪类型square,代码如下:

$ diff ngx_http_image_filter_module.c ngx_http_image_filter_module.c.bak

21,22d20
< #define NGX_HTTP_IMAGE_CROP_TOP  6
< #define NGX_HTTP_IMAGE_SQUARE    7
826,850d823
<     } else if (conf->filter == NGX_HTTP_IMAGE_CROP_TOP) {
< 
<         resize = 0;
< 
<         if ((ngx_uint_t) dx > ctx->max_width) {
<             dy = dy * ctx->max_width / dx;
<             dy = dy ? dy : 1;
<             dx = ctx->max_width;
<             resize = 1;
<         }
< 
<     } else if (conf->filter == NGX_HTTP_IMAGE_SQUARE) {
< 
<         resize = 0;
< 
<         if ((ngx_uint_t) dx > ctx->max_width) {
<             dy = dy * ctx->max_width / dx;
<             dy = dy ? dy : 1;
<             dx = ctx->max_width;
<             resize = 1;
<         } else {
<             ctx->max_width = dx;
<             ctx->max_height = dx;
<         }
< 
942,950d914
<    
<     if (conf->filter == NGX_HTTP_IMAGE_CROP || conf->filter == NGX_HTTP_IMAGE_SQUARE) {
< 
<        if (conf->filter == NGX_HTTP_IMAGE_SQUARE) {
<            if ((ngx_uint_t)dy < ctx->max_width){
<                 ctx->max_width = dy;
<                 ctx->max_height = dy;
<             }
<         }
951a916
>     if (conf->filter == NGX_HTTP_IMAGE_CROP) {
1000,1035d964
<     if (conf->filter == NGX_HTTP_IMAGE_CROP_TOP) {
< 
<         src = dst;
< 	
<         if ((ngx_uint_t) dy > ctx->max_height) {
<             oy = dy - ctx->max_height;
<             ox = 0;
<             dst = ngx_http_image_new(r, dx - ox, dy - oy, colors);
< 
<             if (dst == NULL) {
<                 gdImageDestroy(src);
<                 return NULL;
<             }
< 
<             ox /= 2;
<             oy = 0;
< 
<             ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
<                            "image crop: %d x %d @ %d x %d",
<                            dx, dy, ox, oy);
< 
<             if (colors == 0) {
<                 gdImageSaveAlpha(dst, 1);
<                 gdImageAlphaBlending(dst, 0);
<             }
< 
<             gdImageCopy(dst, src, 0, 0, ox, oy, dx - ox, ctx->max_height);
< 
<             if (colors) {
<                 gdImageTrueColorToPalette(dst, 1, 256);
<             }
< 
<             gdImageDestroy(src);
<         }
<     }
< 
1407,1409d1335
<     } else if (ngx_strcmp(value[i].data, "croptop") == 0) {
<         imcf->filter = NGX_HTTP_IMAGE_CROP_TOP;
< 
1412,1414d1337
< 
<     } else if (ngx_strcmp(value[i].data, "square") == 0) {
<         imcf->filter = NGX_HTTP_IMAGE_SQUARE;