diff src/http/modules/ngx_http_image_filter_module.c @ 502:89dc5654117c NGINX_0_7_63

nginx 0.7.63 *) Security: now "/../" are disabled in "Destination" request header line. *) Change: minimum supported OpenSSL version is 0.9.7. *) Change: the "ask" parameter of the "ssl_verify_client" directive was changed to the "optional" parameter and now it checks a client certificate if it was offered. Thanks to Brice Figureau. *) Feature: now the "-V" switch shows TLS SNI support. *) Feature: the $ssl_client_verify variable. Thanks to Brice Figureau. *) Feature: the "ssl_crl" directive. Thanks to Brice Figureau. *) Bugfix: the $ssl_client_cert variable usage corrupted memory; the bug had appeared in 0.7.7. Thanks to Sergey Zhuravlev. *) Feature: now the start cache loader runs in a separate process; this should improve large caches handling. *) Feature: now temporary files and permanent storage area may reside at different file systems. *) Bugfix: nginx counted incorrectly disk cache size. *) Change: now directive "gzip_disable msie6" does not disable gzipping for MSIE 6.0 SV1. *) Bugfix: nginx always added "Vary: Accept-Encoding" response header line, if both "gzip_static" and "gzip_vary" were on. *) Feature: the "proxy" parameter of the "geo" directive. *) Feature: the ngx_http_geoip_module. *) Feature: the "limit_rate_after" directive. Thanks to Ivan Debnar. *) Feature: the "limit_req_log_level" and "limit_conn_log_level" directives. *) Bugfix: now "limit_req" directive conforms to the leaky bucket algorithm. Thanks to Maxim Dounin. *) Bugfix: in ngx_http_limit_req_module. Thanks to Maxim Dounin. *) Bugfix: now nginx allows underscores in a request method. *) Bugfix: "proxy_pass_header" and "fastcgi_pass_header" directives did not pass to a client the "X-Accel-Redirect", "X-Accel-Limit-Rate", "X-Accel-Buffering", and "X-Accel-Charset" lines from backend response header. Thanks to Maxim Dounin. *) Bugfix: in handling "Last-Modified" and "Accept-Ranges" backend response header lines; the bug had appeared in 0.7.44. Thanks to Maxim Dounin. *) Feature: the "image_filter_transparency" directive. *) Feature: the "image_filter" directive supports variables for setting size. *) Bugfix: in PNG alpha-channel support in the ngx_http_image_filter_module. *) Bugfix: in transparency support in the ngx_http_image_filter_module. *) Feature: now several "perl_modules" directives may be used. *) Bugfix: ngx_http_perl_module responses did not work in subrequests. *) Bugfix: nginx sent '\0' in a "Location" response header line on MKCOL request. Thanks to Xie Zhenye. *) Bugfix: an "error_page" directive did not redirect a 413 error; the bug had appeared in 0.6.10. *) Bugfix: in memory allocation error handling. Thanks to Maxim Dounin and Kirill A. Korinskiy.
author Igor Sysoev <http://sysoev.ru>
date Mon, 26 Oct 2009 00:00:00 +0300
parents 116d5de7cbb6
children 68c0ae0a4959
line wrap: on
line diff
--- a/src/http/modules/ngx_http_image_filter_module.c
+++ b/src/http/modules/ngx_http_image_filter_module.c
@@ -40,6 +40,11 @@ typedef struct {
     ngx_uint_t                   height;
     ngx_int_t                    jpeg_quality;
 
+    ngx_flag_t                   transparency;
+
+    ngx_http_complex_value_t    *wcv;
+    ngx_http_complex_value_t    *hcv;
+
     size_t                       buffer_size;
 } ngx_http_image_filter_conf_t;
 
@@ -53,6 +58,9 @@ typedef struct {
     ngx_uint_t                   width;
     ngx_uint_t                   height;
 
+    ngx_uint_t                   max_width;
+    ngx_uint_t                   max_height;
+
     ngx_uint_t                   phase;
     ngx_uint_t                   type;
 } ngx_http_image_filter_ctx_t;
@@ -80,6 +88,9 @@ static gdImagePtr ngx_http_image_new(ngx
 static u_char *ngx_http_image_out(ngx_http_request_t *r, ngx_uint_t type,
     gdImagePtr img, int *size);
 static void ngx_http_image_cleanup(void *data);
+static ngx_uint_t ngx_http_image_filter_get_value(ngx_http_request_t *r,
+    ngx_http_complex_value_t *cv, ngx_uint_t v);
+static ngx_uint_t ngx_http_image_filter_value(ngx_str_t *value);
 
 
 static void *ngx_http_image_filter_create_conf(ngx_conf_t *cf);
@@ -106,6 +117,13 @@ static ngx_command_t  ngx_http_image_fil
       offsetof(ngx_http_image_filter_conf_t, jpeg_quality),
       NULL },
 
+    { ngx_string("image_filter_transparency"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+      ngx_conf_set_flag_slot,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      offsetof(ngx_http_image_filter_conf_t, transparency),
+      NULL },
+
     { ngx_string("image_filter_buffer"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
       ngx_conf_set_size_slot,
@@ -454,7 +472,6 @@ ngx_http_image_read(ngx_http_request_t *
 static ngx_buf_t *
 ngx_http_image_process(ngx_http_request_t *r)
 {
-    ngx_buf_t                     *b;
     ngx_int_t                      rc;
     ngx_http_image_filter_ctx_t   *ctx;
     ngx_http_image_filter_conf_t  *conf;
@@ -468,20 +485,28 @@ ngx_http_image_process(ngx_http_request_
     conf = ngx_http_get_module_loc_conf(r, ngx_http_image_filter_module);
 
     if (conf->filter == NGX_HTTP_IMAGE_SIZE) {
-
-        b = ngx_http_image_json(r, rc == NGX_OK ? ctx : NULL);
+        return ngx_http_image_json(r, rc == NGX_OK ? ctx : NULL);
+    }
 
-    } else if (rc == NGX_OK
-               && ctx->width <= conf->width
-               && ctx->height <= conf->height)
-    {
-        b = ngx_http_image_asis(r, ctx);
-
-    } else {
-        b = ngx_http_image_resize(r, ctx);
+    ctx->max_width = ngx_http_image_filter_get_value(r, conf->wcv, conf->width);
+    if (ctx->max_width == 0) {
+        return NULL;
     }
 
-    return b;
+    ctx->max_height = ngx_http_image_filter_get_value(r, conf->hcv,
+                                                      conf->height);
+    if (ctx->max_height == 0) {
+        return NULL;
+    }
+
+    if (rc == NGX_OK
+        && ctx->width <= ctx->max_width
+        && ctx->height <= ctx->max_height)
+    {
+        return ngx_http_image_asis(r, ctx);
+    }
+
+    return ngx_http_image_resize(r, ctx);
 }
 
 
@@ -662,8 +687,9 @@ ngx_http_image_size(ngx_http_request_t *
 static ngx_buf_t *
 ngx_http_image_resize(ngx_http_request_t *r, ngx_http_image_filter_ctx_t *ctx)
 {
-    int                            sx, sy, dx, dy, ox, oy,
-                                   colors, transparent, size;
+    int                            sx, sy, dx, dy, ox, oy, size,
+                                   colors, palette, transparent,
+                                   red, green, blue;
     u_char                        *out;
     ngx_buf_t                     *b;
     ngx_uint_t                     resize;
@@ -682,29 +708,53 @@ ngx_http_image_resize(ngx_http_request_t
 
     conf = ngx_http_get_module_loc_conf(r, ngx_http_image_filter_module);
 
-    if ((ngx_uint_t) sx <= conf->width && (ngx_uint_t) sy <= conf->height) {
+    if ((ngx_uint_t) sx <= ctx->max_width
+        && (ngx_uint_t) sy <= ctx->max_height)
+    {
         gdImageDestroy(src);
         return ngx_http_image_asis(r, ctx);
     }
 
     colors = gdImageColorsTotal(src);
-    transparent = gdImageGetTransparent(src);
+
+    if (colors && conf->transparency) {
+        transparent = gdImageGetTransparent(src);
+
+        if (transparent != -1) {
+            palette = colors;
+            red = gdImageRed(src, transparent);
+            green = gdImageGreen(src, transparent);
+            blue = gdImageBlue(src, transparent);
+
+            goto transparent;
+        }
+    }
+
+    palette = 0;
+    transparent = -1;
+    red = 0;
+    green = 0;
+    blue = 0;
+
+transparent:
+
+    gdImageColorTransparent(src, -1);
 
     dx = sx;
     dy = sy;
 
     if (conf->filter == NGX_HTTP_IMAGE_RESIZE) {
 
-        if ((ngx_uint_t) dx > conf->width) {
-            dy = dy * conf->width / dx;
+        if ((ngx_uint_t) dx > ctx->max_width) {
+            dy = dy * ctx->max_width / dx;
             dy = dy ? dy : 1;
-            dx = conf->width;
+            dx = ctx->max_width;
         }
 
-        if ((ngx_uint_t) dy > conf->height) {
-            dx = dx * conf->height / dy;
+        if ((ngx_uint_t) dy > ctx->max_height) {
+            dx = dx * ctx->max_height / dy;
             dx = dx ? dx : 1;
-            dy = conf->height;
+            dy = ctx->max_height;
         }
 
         resize = 1;
@@ -713,34 +763,44 @@ ngx_http_image_resize(ngx_http_request_t
 
         resize = 0;
 
-        if ((ngx_uint_t) (dx * 100 / dy) < conf->width * 100 / conf->height) {
-
-            if ((ngx_uint_t) dx > conf->width) {
-                dy = dy * conf->width / dx;
+        if ((ngx_uint_t) (dx * 100 / dy)
+            < ctx->max_width * 100 / ctx->max_height)
+        {
+            if ((ngx_uint_t) dx > ctx->max_width) {
+                dy = dy * ctx->max_width / dx;
                 dy = dy ? dy : 1;
-                dx = conf->width;
+                dx = ctx->max_width;
                 resize = 1;
             }
 
         } else {
-            if ((ngx_uint_t) dy > conf->height) {
-                dx = dx * conf->height / dy;
+            if ((ngx_uint_t) dy > ctx->max_height) {
+                dx = dx * ctx->max_height / dy;
                 dx = dx ? dx : 1;
-                dy = conf->height;
+                dy = ctx->max_height;
                 resize = 1;
             }
         }
     }
 
     if (resize) {
-        dst = ngx_http_image_new(r, dx, dy, colors);
+        dst = ngx_http_image_new(r, dx, dy, palette);
         if (dst == NULL) {
             gdImageDestroy(src);
             return NULL;
         }
 
+        if (colors == 0) {
+            gdImageSaveAlpha(dst, 1);
+            gdImageAlphaBlending(dst, 0);
+        }
+
         gdImageCopyResampled(dst, src, 0, 0, 0, 0, dx, dy, sx, sy);
 
+        if (colors) {
+            gdImageTrueColorToPalette(dst, 1, 256);
+        }
+
         gdImageDestroy(src);
 
     } else {
@@ -751,15 +811,15 @@ ngx_http_image_resize(ngx_http_request_t
 
         src = dst;
 
-        if ((ngx_uint_t) dx > conf->width) {
-            ox = dx - conf->width;
+        if ((ngx_uint_t) dx > ctx->max_width) {
+            ox = dx - ctx->max_width;
 
         } else {
             ox = 0;
         }
 
-        if ((ngx_uint_t) dy > conf->height) {
-            oy = dy - conf->height;
+        if ((ngx_uint_t) dy > ctx->max_height) {
+            oy = dy - ctx->max_height;
 
         } else {
             oy = 0;
@@ -781,13 +841,24 @@ ngx_http_image_resize(ngx_http_request_t
                            "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, dy - oy);
 
+            if (colors) {
+                gdImageTrueColorToPalette(dst, 1, 256);
+            }
+
             gdImageDestroy(src);
         }
     }
 
-    gdImageColorTransparent(dst, transparent);
+    if (transparent != -1 && colors) {
+        gdImageColorTransparent(dst, gdImageColorExact(dst, red, green, blue));
+    }
 
     out = ngx_http_image_out(r, ctx->type, dst, &size);
 
@@ -941,6 +1012,43 @@ ngx_http_image_cleanup(void *data)
 }
 
 
+static ngx_uint_t
+ngx_http_image_filter_get_value(ngx_http_request_t *r,
+    ngx_http_complex_value_t *cv, ngx_uint_t v)
+{
+    ngx_str_t  val;
+
+    if (cv == NULL) {
+        return v;
+    }
+
+    if (ngx_http_complex_value(r, cv, &val) != NGX_OK) {
+        return 0;
+    }
+
+    return ngx_http_image_filter_value(&val);
+}
+
+
+static ngx_uint_t
+ngx_http_image_filter_value(ngx_str_t *value)
+{
+    ngx_int_t  n;
+
+    if (value->len == 1 && value->data[0] == '-') {
+        return (ngx_uint_t) -1;
+    }
+
+    n = ngx_atoi(value->data, value->len);
+
+    if (n > 0) {
+        return (ngx_uint_t) n;
+    }
+
+    return 0;
+}
+
+
 static void *
 ngx_http_image_filter_create_conf(ngx_conf_t *cf)
 {
@@ -948,11 +1056,12 @@ ngx_http_image_filter_create_conf(ngx_co
 
     conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_image_filter_conf_t));
     if (conf == NULL) {
-        return NGX_CONF_ERROR;
+        return NULL;
     }
 
     conf->filter = NGX_CONF_UNSET_UINT;
     conf->jpeg_quality = NGX_CONF_UNSET;
+    conf->transparency = NGX_CONF_UNSET;
     conf->buffer_size = NGX_CONF_UNSET_SIZE;
 
     return conf;
@@ -974,12 +1083,16 @@ ngx_http_image_filter_merge_conf(ngx_con
             conf->filter = prev->filter;
             conf->width = prev->width;
             conf->height = prev->height;
+            conf->wcv = prev->wcv;
+            conf->hcv = prev->hcv;
         }
     }
 
     /* 75 is libjpeg default quality */
     ngx_conf_merge_value(conf->jpeg_quality, prev->jpeg_quality, 75);
 
+    ngx_conf_merge_value(conf->transparency, prev->transparency, 1);
+
     ngx_conf_merge_size_value(conf->buffer_size, prev->buffer_size,
                               1 * 1024 * 1024);
 
@@ -992,9 +1105,11 @@ ngx_http_image_filter(ngx_conf_t *cf, ng
 {
     ngx_http_image_filter_conf_t *imcf = conf;
 
-    ngx_str_t   *value;
-    ngx_int_t    n;
-    ngx_uint_t   i;
+    ngx_str_t                         *value;
+    ngx_int_t                          n;
+    ngx_uint_t                         i;
+    ngx_http_complex_value_t           cv;
+    ngx_http_compile_complex_value_t   ccv;
 
     value = cf->args->elts;
 
@@ -1027,32 +1142,60 @@ ngx_http_image_filter(ngx_conf_t *cf, ng
         goto failed;
     }
 
-    i++;
+    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
+
+    ccv.cf = cf;
+    ccv.value = &value[++i];
+    ccv.complex_value = &cv;
 
-    if (value[i].len == 1 && value[i].data[0] == '-') {
-        imcf->width = (ngx_uint_t) -1;
+    if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
+        return NGX_CONF_ERROR;
+    }
 
-    } else {
-        n = ngx_atoi(value[i].data, value[i].len);
-        if (n == NGX_ERROR) {
+    if (cv.lengths == NULL) {
+        n = ngx_http_image_filter_value(&value[i]);
+
+        if (n == 0) {
             goto failed;
         }
 
         imcf->width = (ngx_uint_t) n;
+
+    } else {
+        imcf->wcv = ngx_palloc(cf->pool, sizeof(ngx_http_complex_value_t));
+        if (imcf->wcv == NULL) {
+            return NGX_CONF_ERROR;
+        }
+
+        *imcf->wcv = cv;
     }
 
-    i++;
+    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
+
+    ccv.cf = cf;
+    ccv.value = &value[++i];
+    ccv.complex_value = &cv;
 
-    if (value[i].len == 1 && value[i].data[0] == '-') {
-        imcf->height = (ngx_uint_t) -1;
+    if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
+        return NGX_CONF_ERROR;
+    }
 
-    } else {
-        n = ngx_atoi(value[i].data, value[i].len);
-        if (n == NGX_ERROR) {
+    if (cv.lengths == NULL) {
+        n = ngx_http_image_filter_value(&value[i]);
+
+        if (n == 0) {
             goto failed;
         }
 
         imcf->height = (ngx_uint_t) n;
+
+    } else {
+        imcf->hcv = ngx_palloc(cf->pool, sizeof(ngx_http_complex_value_t));
+        if (imcf->hcv == NULL) {
+            return NGX_CONF_ERROR;
+        }
+
+        *imcf->hcv = cv;
     }
 
     return NGX_CONF_OK;