Mercurial > hg > nginx-mail
diff src/http/modules/ngx_http_image_filter_module.c @ 665:0b460e61bdcd default tip
Merge with nginx 1.0.0.
author | Maxim Dounin <mdounin@mdounin.ru> |
---|---|
date | Mon, 25 Apr 2011 04:22:17 +0400 |
parents | b4dcae568a2a |
children |
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 @@ -16,6 +16,7 @@ #define NGX_HTTP_IMAGE_SIZE 2 #define NGX_HTTP_IMAGE_RESIZE 3 #define NGX_HTTP_IMAGE_CROP 4 +#define NGX_HTTP_IMAGE_ROTATE 5 #define NGX_HTTP_IMAGE_START 0 @@ -38,12 +39,15 @@ typedef struct { ngx_uint_t filter; ngx_uint_t width; ngx_uint_t height; - ngx_int_t jpeg_quality; + ngx_uint_t angle; + ngx_uint_t jpeg_quality; ngx_flag_t transparency; ngx_http_complex_value_t *wcv; ngx_http_complex_value_t *hcv; + ngx_http_complex_value_t *acv; + ngx_http_complex_value_t *jqcv; size_t buffer_size; } ngx_http_image_filter_conf_t; @@ -57,12 +61,13 @@ typedef struct { ngx_uint_t width; ngx_uint_t height; - ngx_uint_t max_width; ngx_uint_t max_height; + ngx_uint_t angle; ngx_uint_t phase; ngx_uint_t type; + ngx_uint_t force; } ngx_http_image_filter_ctx_t; @@ -98,13 +103,15 @@ static char *ngx_http_image_filter_merge void *child); static char *ngx_http_image_filter(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +static char *ngx_http_image_filter_jpeg_quality(ngx_conf_t *cf, + ngx_command_t *cmd, void *conf); static ngx_int_t ngx_http_image_filter_init(ngx_conf_t *cf); static ngx_command_t ngx_http_image_filter_commands[] = { { ngx_string("image_filter"), - NGX_HTTP_LOC_CONF|NGX_CONF_TAKE13, + NGX_HTTP_LOC_CONF|NGX_CONF_TAKE13|NGX_CONF_TAKE2, ngx_http_image_filter, NGX_HTTP_LOC_CONF_OFFSET, 0, @@ -112,9 +119,9 @@ static ngx_command_t ngx_http_image_fil { ngx_string("image_filter_jpeg_quality"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, - ngx_conf_set_num_slot, + ngx_http_image_filter_jpeg_quality, NGX_HTTP_LOC_CONF_OFFSET, - offsetof(ngx_http_image_filter_conf_t, jpeg_quality), + 0, NULL }, { ngx_string("image_filter_transparency"), @@ -227,7 +234,7 @@ ngx_http_image_header_filter(ngx_http_re ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "image filter: too big response: %O", len); - return NGX_ERROR; + return NGX_HTTP_UNSUPPORTED_MEDIA_TYPE; } if (len == -1) { @@ -488,6 +495,17 @@ ngx_http_image_process(ngx_http_request_ return ngx_http_image_json(r, rc == NGX_OK ? ctx : NULL); } + ctx->angle = ngx_http_image_filter_get_value(r, conf->acv, conf->angle); + + if (conf->filter == NGX_HTTP_IMAGE_ROTATE) { + + if (ctx->angle != 90 && ctx->angle != 180 && ctx->angle != 270) { + return NULL; + } + + return 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; @@ -501,7 +519,9 @@ 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->angle == 0 + && !ctx->force) { return ngx_http_image_asis(r, ctx); } @@ -527,8 +547,7 @@ ngx_http_image_json(ngx_http_request_t * ngx_http_clean_header(r); r->headers_out.status = NGX_HTTP_OK; - r->headers_out.content_type.len = sizeof("text/plain") - 1; - r->headers_out.content_type.data = (u_char *) "text/plain"; + ngx_str_set(&r->headers_out.content_type, "text/plain"); r->headers_out.content_type_lowcase = NULL; if (ctx == NULL) { @@ -601,6 +620,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; @@ -611,26 +631,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; } @@ -638,12 +670,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; @@ -689,7 +725,7 @@ ngx_http_image_resize(ngx_http_request_t { int sx, sy, dx, dy, ox, oy, size, colors, palette, transparent, - red, green, blue; + red, green, blue, t; u_char *out; ngx_buf_t *b; ngx_uint_t resize; @@ -708,7 +744,9 @@ 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 + && ctx->angle == 0 + && (ngx_uint_t) sx <= ctx->max_width && (ngx_uint_t) sy <= ctx->max_height) { gdImageDestroy(src); @@ -759,6 +797,10 @@ transparent: resize = 1; + } else if (conf->filter == NGX_HTTP_IMAGE_ROTATE) { + + resize = 0; + } else { /* NGX_HTTP_IMAGE_CROP */ resize = 0; @@ -807,6 +849,38 @@ transparent: dst = src; } + if (ctx->angle) { + src = dst; + + switch (ctx->angle) { + + case 90: + case 270: + dst = ngx_http_image_new(r, dy, dx, palette); + if (dst == NULL) { + gdImageDestroy(src); + return NULL; + } + gdImageCopyRotated(dst, src, dy/2, dx/2, 0, 0, dx, dy, ctx->angle); + gdImageDestroy(src); + + t = dx; + dx = dy; + dy = t; + break; + + case 180: + dst = ngx_http_image_new(r, dx, dy, palette); + if (dst == NULL) { + gdImageDestroy(src); + return NULL; + } + gdImageCopyRotated(dst, src, dx/2, dy/2, 0, 0, dx, dy, ctx->angle); + gdImageDestroy(src); + break; + } + } + if (conf->filter == NGX_HTTP_IMAGE_CROP) { src = dst; @@ -970,6 +1044,7 @@ ngx_http_image_out(ngx_http_request_t *r { char *failed; u_char *out; + ngx_int_t jq; ngx_http_image_filter_conf_t *conf; out = NULL; @@ -978,7 +1053,13 @@ ngx_http_image_out(ngx_http_request_t *r case NGX_HTTP_IMAGE_JPEG: conf = ngx_http_get_module_loc_conf(r, ngx_http_image_filter_module); - out = gdImageJpegPtr(img, size, conf->jpeg_quality); + + jq = ngx_http_image_filter_get_value(r, conf->jqcv, conf->jpeg_quality); + if (jq <= 0) { + return NULL; + } + + out = gdImageJpegPtr(img, size, jq); failed = "gdImageJpegPtr() failed"; break; @@ -1060,7 +1141,8 @@ ngx_http_image_filter_create_conf(ngx_co } conf->filter = NGX_CONF_UNSET_UINT; - conf->jpeg_quality = NGX_CONF_UNSET; + conf->jpeg_quality = NGX_CONF_UNSET_UINT; + conf->angle = NGX_CONF_UNSET_UINT; conf->transparency = NGX_CONF_UNSET; conf->buffer_size = NGX_CONF_UNSET_SIZE; @@ -1089,7 +1171,16 @@ 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_uint_value(conf->jpeg_quality, prev->jpeg_quality, 75); + + if (conf->jqcv == NULL) { + conf->jqcv = prev->jqcv; + } + + ngx_conf_merge_uint_value(conf->angle, prev->angle, 0); + if (conf->acv == NULL) { + conf->acv = prev->acv; + } ngx_conf_merge_value(conf->transparency, prev->transparency, 1); @@ -1130,6 +1221,46 @@ ngx_http_image_filter(ngx_conf_t *cf, ng } return NGX_CONF_OK; + + } else if (cf->args->nelts == 3) { + + if (ngx_strcmp(value[i].data, "rotate") == 0) { + imcf->filter = NGX_HTTP_IMAGE_ROTATE; + + ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); + + ccv.cf = cf; + ccv.value = &value[++i]; + ccv.complex_value = &cv; + + if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { + return NGX_CONF_ERROR; + } + + if (cv.lengths == NULL) { + n = ngx_http_image_filter_value(&value[i]); + + if (n != 90 && n != 180 && n != 270) { + goto failed; + } + + imcf->angle = (ngx_uint_t) n; + + } else { + imcf->acv = ngx_palloc(cf->pool, + sizeof(ngx_http_complex_value_t)); + if (imcf->acv == NULL) { + return NGX_CONF_ERROR; + } + + *imcf->acv = cv; + } + + return NGX_CONF_OK; + + } else { + goto failed; + } } if (ngx_strcmp(value[i].data, "resize") == 0) { @@ -1209,6 +1340,53 @@ failed: } +static char * +ngx_http_image_filter_jpeg_quality(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf) +{ + ngx_http_image_filter_conf_t *imcf = conf; + + ngx_str_t *value; + ngx_int_t n; + ngx_http_complex_value_t cv; + ngx_http_compile_complex_value_t ccv; + + value = cf->args->elts; + + ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); + + ccv.cf = cf; + ccv.value = &value[1]; + ccv.complex_value = &cv; + + if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { + return NGX_CONF_ERROR; + } + + if (cv.lengths == NULL) { + n = ngx_http_image_filter_value(&value[1]); + + if (n <= 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid parameter \"%V\"", &value[1]); + return NGX_CONF_ERROR; + } + + imcf->jpeg_quality = (ngx_uint_t) n; + + } else { + imcf->jqcv = ngx_palloc(cf->pool, sizeof(ngx_http_complex_value_t)); + if (imcf->jqcv == NULL) { + return NGX_CONF_ERROR; + } + + *imcf->jqcv = cv; + } + + return NGX_CONF_OK; +} + + static ngx_int_t ngx_http_image_filter_init(ngx_conf_t *cf) {