diff src/http/modules/ngx_http_image_filter_module.c @ 578:f3a9e57d2e17

Merge with current.
author Maxim Dounin <mdounin@mdounin.ru>
date Thu, 11 Mar 2010 21:27:17 +0300
parents 2da4537168f8
children 8246d8a2c2be
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,8 @@ 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;
 
@@ -61,6 +63,7 @@ typedef struct {
 
     ngx_uint_t                   phase;
     ngx_uint_t                   type;
+    ngx_uint_t                   force;
 } ngx_http_image_filter_ctx_t;
 
 
@@ -115,6 +118,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,
@@ -492,7 +502,8 @@ ngx_http_image_process(ngx_http_request_
 
     if (rc == NGX_OK
         && ctx->width <= ctx->max_width
-        && ctx->height <= ctx->max_height)
+        && ctx->height <= ctx->max_height
+        && !ctx->force)
     {
         return ngx_http_image_asis(r, ctx);
     }
@@ -592,6 +603,7 @@ static ngx_int_t
 ngx_http_image_size(ngx_http_request_t *r, ngx_http_image_filter_ctx_t *ctx)
 {
     u_char      *p, *last;
+    size_t       len, app;
     ngx_uint_t   width, height;
 
     p = ctx->image;
@@ -602,26 +614,38 @@ ngx_http_image_size(ngx_http_request_t *
 
         p += 2;
         last = ctx->image + ctx->length - 10;
+        width = 0;
+        height = 0;
+        app = 0;
 
         while (p < last) {
 
             if (p[0] == 0xff && p[1] != 0xff) {
 
                 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                               "JPEG: %02xd %02xd", *p, *(p + 1));
+                               "JPEG: %02xd %02xd", p[0], p[1]);
 
                 p++;
 
-                if (*p == 0xc0 || *p == 0xc1 || *p == 0xc2 || *p == 0xc3
-                    || *p == 0xc9 || *p == 0xca || *p == 0xcb)
+                if ((*p == 0xc0 || *p == 0xc1 || *p == 0xc2 || *p == 0xc3
+                     || *p == 0xc9 || *p == 0xca || *p == 0xcb)
+                    && (width == 0 || height == 0))
                 {
-                    goto found;
+                    width = p[6] * 256 + p[7];
+                    height = p[4] * 256 + p[5];
                 }
 
                 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                                "JPEG: %02xd %02xd", p[1], p[2]);
 
-                p += p[1] * 256 + p[2];
+                len = p[1] * 256 + p[2];
+
+                if (*p >= 0xe1 && *p <= 0xef) {
+                    /* application data, e.g., EXIF, Adobe XMP, etc. */
+                    app += len;
+                }
+
+                p += len;
 
                 continue;
             }
@@ -629,12 +653,16 @@ ngx_http_image_size(ngx_http_request_t *
             p++;
         }
 
-        return NGX_DECLINED;
+        if (width == 0 || height == 0) {
+            return NGX_DECLINED;
+        }
 
-    found:
-
-        width = p[6] * 256 + p[7];
-        height = p[4] * 256 + p[5];
+        if (ctx->length / 20 < app) {
+            /* force conversion if application data consume more than 5% */
+            ctx->force = 1;
+            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                           "app data size: %uz", app);
+        }
 
         break;
 
@@ -678,8 +706,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, red, green, blue, 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;
@@ -698,7 +727,8 @@ 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 <= ctx->max_width
+    if (!ctx->force
+        && (ngx_uint_t) sx <= ctx->max_width
         && (ngx_uint_t) sy <= ctx->max_height)
     {
         gdImageDestroy(src);
@@ -706,17 +736,29 @@ ngx_http_image_resize(ngx_http_request_t
     }
 
     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);
 
-    if (transparent != -1 && colors) {
-        red = gdImageRed(src, transparent);
-        green = gdImageGreen(src, transparent);
-        blue = gdImageBlue(src, transparent);
-        gdImageColorTransparent(src, -1);
+            goto transparent;
+        }
+    }
 
-    } else {
-        red = 0; green = 0; blue = 0;
-    }
+    palette = 0;
+    transparent = -1;
+    red = 0;
+    green = 0;
+    blue = 0;
+
+transparent:
+
+    gdImageColorTransparent(src, -1);
 
     dx = sx;
     dy = sy;
@@ -762,14 +804,23 @@ ngx_http_image_resize(ngx_http_request_t
     }
 
     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 {
@@ -810,8 +861,17 @@ 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);
         }
     }
@@ -1021,6 +1081,7 @@ ngx_http_image_filter_create_conf(ngx_co
 
     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;
@@ -1050,6 +1111,8 @@ ngx_http_image_filter_merge_conf(ngx_con
     /* 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);