diff options
Diffstat (limited to 'app/nginx/src/http/modules/ngx_http_image_filter_module.c')
-rw-r--r-- | app/nginx/src/http/modules/ngx_http_image_filter_module.c | 1675 |
1 files changed, 0 insertions, 1675 deletions
diff --git a/app/nginx/src/http/modules/ngx_http_image_filter_module.c b/app/nginx/src/http/modules/ngx_http_image_filter_module.c deleted file mode 100644 index dbec5d8..0000000 --- a/app/nginx/src/http/modules/ngx_http_image_filter_module.c +++ /dev/null @@ -1,1675 +0,0 @@ - -/* - * Copyright (C) Igor Sysoev - * Copyright (C) Nginx, Inc. - */ - - -#include <ngx_config.h> -#include <ngx_core.h> -#include <ngx_http.h> - -#include <gd.h> - - -#define NGX_HTTP_IMAGE_OFF 0 -#define NGX_HTTP_IMAGE_TEST 1 -#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 -#define NGX_HTTP_IMAGE_READ 1 -#define NGX_HTTP_IMAGE_PROCESS 2 -#define NGX_HTTP_IMAGE_PASS 3 -#define NGX_HTTP_IMAGE_DONE 4 - - -#define NGX_HTTP_IMAGE_NONE 0 -#define NGX_HTTP_IMAGE_JPEG 1 -#define NGX_HTTP_IMAGE_GIF 2 -#define NGX_HTTP_IMAGE_PNG 3 -#define NGX_HTTP_IMAGE_WEBP 4 - - -#define NGX_HTTP_IMAGE_BUFFERED 0x08 - - -typedef struct { - ngx_uint_t filter; - ngx_uint_t width; - ngx_uint_t height; - ngx_uint_t angle; - ngx_uint_t jpeg_quality; - ngx_uint_t webp_quality; - ngx_uint_t sharpen; - - ngx_flag_t transparency; - ngx_flag_t interlace; - - ngx_http_complex_value_t *wcv; - ngx_http_complex_value_t *hcv; - ngx_http_complex_value_t *acv; - ngx_http_complex_value_t *jqcv; - ngx_http_complex_value_t *wqcv; - ngx_http_complex_value_t *shcv; - - size_t buffer_size; -} ngx_http_image_filter_conf_t; - - -typedef struct { - u_char *image; - u_char *last; - - size_t length; - - 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; - - -static ngx_int_t ngx_http_image_send(ngx_http_request_t *r, - ngx_http_image_filter_ctx_t *ctx, ngx_chain_t *in); -static ngx_uint_t ngx_http_image_test(ngx_http_request_t *r, ngx_chain_t *in); -static ngx_int_t ngx_http_image_read(ngx_http_request_t *r, ngx_chain_t *in); -static ngx_buf_t *ngx_http_image_process(ngx_http_request_t *r); -static ngx_buf_t *ngx_http_image_json(ngx_http_request_t *r, - ngx_http_image_filter_ctx_t *ctx); -static ngx_buf_t *ngx_http_image_asis(ngx_http_request_t *r, - ngx_http_image_filter_ctx_t *ctx); -static void ngx_http_image_length(ngx_http_request_t *r, ngx_buf_t *b); -static ngx_int_t ngx_http_image_size(ngx_http_request_t *r, - ngx_http_image_filter_ctx_t *ctx); - -static ngx_buf_t *ngx_http_image_resize(ngx_http_request_t *r, - ngx_http_image_filter_ctx_t *ctx); -static gdImagePtr ngx_http_image_source(ngx_http_request_t *r, - ngx_http_image_filter_ctx_t *ctx); -static gdImagePtr ngx_http_image_new(ngx_http_request_t *r, int w, int h, - int colors); -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); -static char *ngx_http_image_filter_merge_conf(ngx_conf_t *cf, void *parent, - 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 char *ngx_http_image_filter_webp_quality(ngx_conf_t *cf, - ngx_command_t *cmd, void *conf); -static char *ngx_http_image_filter_sharpen(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_TAKE123, - ngx_http_image_filter, - NGX_HTTP_LOC_CONF_OFFSET, - 0, - NULL }, - - { ngx_string("image_filter_jpeg_quality"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, - ngx_http_image_filter_jpeg_quality, - NGX_HTTP_LOC_CONF_OFFSET, - 0, - NULL }, - - { ngx_string("image_filter_webp_quality"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, - ngx_http_image_filter_webp_quality, - NGX_HTTP_LOC_CONF_OFFSET, - 0, - NULL }, - - { ngx_string("image_filter_sharpen"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, - ngx_http_image_filter_sharpen, - NGX_HTTP_LOC_CONF_OFFSET, - 0, - 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_interlace"), - 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, interlace), - 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, - NGX_HTTP_LOC_CONF_OFFSET, - offsetof(ngx_http_image_filter_conf_t, buffer_size), - NULL }, - - ngx_null_command -}; - - -static ngx_http_module_t ngx_http_image_filter_module_ctx = { - NULL, /* preconfiguration */ - ngx_http_image_filter_init, /* postconfiguration */ - - NULL, /* create main configuration */ - NULL, /* init main configuration */ - - NULL, /* create server configuration */ - NULL, /* merge server configuration */ - - ngx_http_image_filter_create_conf, /* create location configuration */ - ngx_http_image_filter_merge_conf /* merge location configuration */ -}; - - -ngx_module_t ngx_http_image_filter_module = { - NGX_MODULE_V1, - &ngx_http_image_filter_module_ctx, /* module context */ - ngx_http_image_filter_commands, /* module directives */ - NGX_HTTP_MODULE, /* module type */ - NULL, /* init master */ - NULL, /* init module */ - NULL, /* init process */ - NULL, /* init thread */ - NULL, /* exit thread */ - NULL, /* exit process */ - NULL, /* exit master */ - NGX_MODULE_V1_PADDING -}; - - -static ngx_http_output_header_filter_pt ngx_http_next_header_filter; -static ngx_http_output_body_filter_pt ngx_http_next_body_filter; - - -static ngx_str_t ngx_http_image_types[] = { - ngx_string("image/jpeg"), - ngx_string("image/gif"), - ngx_string("image/png"), - ngx_string("image/webp") -}; - - -static ngx_int_t -ngx_http_image_header_filter(ngx_http_request_t *r) -{ - off_t len; - ngx_http_image_filter_ctx_t *ctx; - ngx_http_image_filter_conf_t *conf; - - if (r->headers_out.status == NGX_HTTP_NOT_MODIFIED) { - return ngx_http_next_header_filter(r); - } - - ctx = ngx_http_get_module_ctx(r, ngx_http_image_filter_module); - - if (ctx) { - ngx_http_set_ctx(r, NULL, ngx_http_image_filter_module); - return ngx_http_next_header_filter(r); - } - - conf = ngx_http_get_module_loc_conf(r, ngx_http_image_filter_module); - - if (conf->filter == NGX_HTTP_IMAGE_OFF) { - return ngx_http_next_header_filter(r); - } - - if (r->headers_out.content_type.len - >= sizeof("multipart/x-mixed-replace") - 1 - && ngx_strncasecmp(r->headers_out.content_type.data, - (u_char *) "multipart/x-mixed-replace", - sizeof("multipart/x-mixed-replace") - 1) - == 0) - { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "image filter: multipart/x-mixed-replace response"); - - return NGX_ERROR; - } - - ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_image_filter_ctx_t)); - if (ctx == NULL) { - return NGX_ERROR; - } - - ngx_http_set_ctx(r, ctx, ngx_http_image_filter_module); - - len = r->headers_out.content_length_n; - - if (len != -1 && len > (off_t) conf->buffer_size) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "image filter: too big response: %O", len); - - return NGX_HTTP_UNSUPPORTED_MEDIA_TYPE; - } - - if (len == -1) { - ctx->length = conf->buffer_size; - - } else { - ctx->length = (size_t) len; - } - - if (r->headers_out.refresh) { - r->headers_out.refresh->hash = 0; - } - - r->main_filter_need_in_memory = 1; - r->allow_ranges = 0; - - return NGX_OK; -} - - -static ngx_int_t -ngx_http_image_body_filter(ngx_http_request_t *r, ngx_chain_t *in) -{ - ngx_int_t rc; - ngx_str_t *ct; - ngx_chain_t out; - ngx_http_image_filter_ctx_t *ctx; - ngx_http_image_filter_conf_t *conf; - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "image filter"); - - if (in == NULL) { - return ngx_http_next_body_filter(r, in); - } - - ctx = ngx_http_get_module_ctx(r, ngx_http_image_filter_module); - - if (ctx == NULL) { - return ngx_http_next_body_filter(r, in); - } - - switch (ctx->phase) { - - case NGX_HTTP_IMAGE_START: - - ctx->type = ngx_http_image_test(r, in); - - conf = ngx_http_get_module_loc_conf(r, ngx_http_image_filter_module); - - if (ctx->type == NGX_HTTP_IMAGE_NONE) { - - if (conf->filter == NGX_HTTP_IMAGE_SIZE) { - out.buf = ngx_http_image_json(r, NULL); - - if (out.buf) { - out.next = NULL; - ctx->phase = NGX_HTTP_IMAGE_DONE; - - return ngx_http_image_send(r, ctx, &out); - } - } - - return ngx_http_filter_finalize_request(r, - &ngx_http_image_filter_module, - NGX_HTTP_UNSUPPORTED_MEDIA_TYPE); - } - - /* override content type */ - - ct = &ngx_http_image_types[ctx->type - 1]; - r->headers_out.content_type_len = ct->len; - r->headers_out.content_type = *ct; - r->headers_out.content_type_lowcase = NULL; - - if (conf->filter == NGX_HTTP_IMAGE_TEST) { - ctx->phase = NGX_HTTP_IMAGE_PASS; - - return ngx_http_image_send(r, ctx, in); - } - - ctx->phase = NGX_HTTP_IMAGE_READ; - - /* fall through */ - - case NGX_HTTP_IMAGE_READ: - - rc = ngx_http_image_read(r, in); - - if (rc == NGX_AGAIN) { - return NGX_OK; - } - - if (rc == NGX_ERROR) { - return ngx_http_filter_finalize_request(r, - &ngx_http_image_filter_module, - NGX_HTTP_UNSUPPORTED_MEDIA_TYPE); - } - - /* fall through */ - - case NGX_HTTP_IMAGE_PROCESS: - - out.buf = ngx_http_image_process(r); - - if (out.buf == NULL) { - return ngx_http_filter_finalize_request(r, - &ngx_http_image_filter_module, - NGX_HTTP_UNSUPPORTED_MEDIA_TYPE); - } - - out.next = NULL; - ctx->phase = NGX_HTTP_IMAGE_PASS; - - return ngx_http_image_send(r, ctx, &out); - - case NGX_HTTP_IMAGE_PASS: - - return ngx_http_next_body_filter(r, in); - - default: /* NGX_HTTP_IMAGE_DONE */ - - rc = ngx_http_next_body_filter(r, NULL); - - /* NGX_ERROR resets any pending data */ - return (rc == NGX_OK) ? NGX_ERROR : rc; - } -} - - -static ngx_int_t -ngx_http_image_send(ngx_http_request_t *r, ngx_http_image_filter_ctx_t *ctx, - ngx_chain_t *in) -{ - ngx_int_t rc; - - rc = ngx_http_next_header_filter(r); - - if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) { - return NGX_ERROR; - } - - rc = ngx_http_next_body_filter(r, in); - - if (ctx->phase == NGX_HTTP_IMAGE_DONE) { - /* NGX_ERROR resets any pending data */ - return (rc == NGX_OK) ? NGX_ERROR : rc; - } - - return rc; -} - - -static ngx_uint_t -ngx_http_image_test(ngx_http_request_t *r, ngx_chain_t *in) -{ - u_char *p; - - p = in->buf->pos; - - if (in->buf->last - p < 16) { - return NGX_HTTP_IMAGE_NONE; - } - - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "image filter: \"%c%c\"", p[0], p[1]); - - if (p[0] == 0xff && p[1] == 0xd8) { - - /* JPEG */ - - return NGX_HTTP_IMAGE_JPEG; - - } else if (p[0] == 'G' && p[1] == 'I' && p[2] == 'F' && p[3] == '8' - && p[5] == 'a') - { - if (p[4] == '9' || p[4] == '7') { - /* GIF */ - return NGX_HTTP_IMAGE_GIF; - } - - } else if (p[0] == 0x89 && p[1] == 'P' && p[2] == 'N' && p[3] == 'G' - && p[4] == 0x0d && p[5] == 0x0a && p[6] == 0x1a && p[7] == 0x0a) - { - /* PNG */ - - return NGX_HTTP_IMAGE_PNG; - - } else if (p[0] == 'R' && p[1] == 'I' && p[2] == 'F' && p[3] == 'F' - && p[8] == 'W' && p[9] == 'E' && p[10] == 'B' && p[11] == 'P') - { - /* WebP */ - - return NGX_HTTP_IMAGE_WEBP; - } - - return NGX_HTTP_IMAGE_NONE; -} - - -static ngx_int_t -ngx_http_image_read(ngx_http_request_t *r, ngx_chain_t *in) -{ - u_char *p; - size_t size, rest; - ngx_buf_t *b; - ngx_chain_t *cl; - ngx_http_image_filter_ctx_t *ctx; - - ctx = ngx_http_get_module_ctx(r, ngx_http_image_filter_module); - - if (ctx->image == NULL) { - ctx->image = ngx_palloc(r->pool, ctx->length); - if (ctx->image == NULL) { - return NGX_ERROR; - } - - ctx->last = ctx->image; - } - - p = ctx->last; - - for (cl = in; cl; cl = cl->next) { - - b = cl->buf; - size = b->last - b->pos; - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "image buf: %uz", size); - - rest = ctx->image + ctx->length - p; - - if (size > rest) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "image filter: too big response"); - return NGX_ERROR; - } - - p = ngx_cpymem(p, b->pos, size); - b->pos += size; - - if (b->last_buf) { - ctx->last = p; - return NGX_OK; - } - } - - ctx->last = p; - r->connection->buffered |= NGX_HTTP_IMAGE_BUFFERED; - - return NGX_AGAIN; -} - - -static ngx_buf_t * -ngx_http_image_process(ngx_http_request_t *r) -{ - ngx_int_t rc; - ngx_http_image_filter_ctx_t *ctx; - ngx_http_image_filter_conf_t *conf; - - r->connection->buffered &= ~NGX_HTTP_IMAGE_BUFFERED; - - ctx = ngx_http_get_module_ctx(r, ngx_http_image_filter_module); - - rc = ngx_http_image_size(r, ctx); - - conf = ngx_http_get_module_loc_conf(r, ngx_http_image_filter_module); - - if (conf->filter == NGX_HTTP_IMAGE_SIZE) { - 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; - } - - 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 - && ctx->angle == 0 - && !ctx->force) - { - return ngx_http_image_asis(r, ctx); - } - - return ngx_http_image_resize(r, ctx); -} - - -static ngx_buf_t * -ngx_http_image_json(ngx_http_request_t *r, ngx_http_image_filter_ctx_t *ctx) -{ - size_t len; - ngx_buf_t *b; - - b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t)); - if (b == NULL) { - return NULL; - } - - b->memory = 1; - b->last_buf = 1; - - ngx_http_clean_header(r); - - r->headers_out.status = NGX_HTTP_OK; - r->headers_out.content_type_len = sizeof("application/json") - 1; - ngx_str_set(&r->headers_out.content_type, "application/json"); - r->headers_out.content_type_lowcase = NULL; - - if (ctx == NULL) { - b->pos = (u_char *) "{}" CRLF; - b->last = b->pos + sizeof("{}" CRLF) - 1; - - ngx_http_image_length(r, b); - - return b; - } - - len = sizeof("{ \"img\" : " - "{ \"width\": , \"height\": , \"type\": \"jpeg\" } }" CRLF) - 1 - + 2 * NGX_SIZE_T_LEN; - - b->pos = ngx_pnalloc(r->pool, len); - if (b->pos == NULL) { - return NULL; - } - - b->last = ngx_sprintf(b->pos, - "{ \"img\" : " - "{ \"width\": %uz," - " \"height\": %uz," - " \"type\": \"%s\" } }" CRLF, - ctx->width, ctx->height, - ngx_http_image_types[ctx->type - 1].data + 6); - - ngx_http_image_length(r, b); - - return b; -} - - -static ngx_buf_t * -ngx_http_image_asis(ngx_http_request_t *r, ngx_http_image_filter_ctx_t *ctx) -{ - ngx_buf_t *b; - - b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t)); - if (b == NULL) { - return NULL; - } - - b->pos = ctx->image; - b->last = ctx->last; - b->memory = 1; - b->last_buf = 1; - - ngx_http_image_length(r, b); - - return b; -} - - -static void -ngx_http_image_length(ngx_http_request_t *r, ngx_buf_t *b) -{ - r->headers_out.content_length_n = b->last - b->pos; - - if (r->headers_out.content_length) { - r->headers_out.content_length->hash = 0; - } - - r->headers_out.content_length = NULL; -} - - -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; - - switch (ctx->type) { - - case NGX_HTTP_IMAGE_JPEG: - - 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[0], p[1]); - - p++; - - if ((*p == 0xc0 || *p == 0xc1 || *p == 0xc2 || *p == 0xc3 - || *p == 0xc9 || *p == 0xca || *p == 0xcb) - && (width == 0 || height == 0)) - { - 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]); - - len = p[1] * 256 + p[2]; - - if (*p >= 0xe1 && *p <= 0xef) { - /* application data, e.g., EXIF, Adobe XMP, etc. */ - app += len; - } - - p += len; - - continue; - } - - p++; - } - - if (width == 0 || height == 0) { - return NGX_DECLINED; - } - - 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; - - case NGX_HTTP_IMAGE_GIF: - - if (ctx->length < 10) { - return NGX_DECLINED; - } - - width = p[7] * 256 + p[6]; - height = p[9] * 256 + p[8]; - - break; - - case NGX_HTTP_IMAGE_PNG: - - if (ctx->length < 24) { - return NGX_DECLINED; - } - - width = p[18] * 256 + p[19]; - height = p[22] * 256 + p[23]; - - break; - - case NGX_HTTP_IMAGE_WEBP: - - if (ctx->length < 30) { - return NGX_DECLINED; - } - - if (p[12] != 'V' || p[13] != 'P' || p[14] != '8') { - return NGX_DECLINED; - } - - switch (p[15]) { - - case ' ': - if (p[20] & 1) { - /* not a key frame */ - return NGX_DECLINED; - } - - if (p[23] != 0x9d || p[24] != 0x01 || p[25] != 0x2a) { - /* invalid start code */ - return NGX_DECLINED; - } - - width = (p[26] | p[27] << 8) & 0x3fff; - height = (p[28] | p[29] << 8) & 0x3fff; - - break; - - case 'L': - if (p[20] != 0x2f) { - /* invalid signature */ - return NGX_DECLINED; - } - - width = ((p[21] | p[22] << 8) & 0x3fff) + 1; - height = ((p[22] >> 6 | p[23] << 2 | p[24] << 10) & 0x3fff) + 1; - - break; - - case 'X': - width = (p[24] | p[25] << 8 | p[26] << 16) + 1; - height = (p[27] | p[28] << 8 | p[29] << 16) + 1; - break; - - default: - return NGX_DECLINED; - } - - break; - - default: - - return NGX_DECLINED; - } - - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "image size: %d x %d", (int) width, (int) height); - - ctx->width = width; - ctx->height = height; - - return NGX_OK; -} - - -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, ax, ay, size, - colors, palette, transparent, sharpen, - red, green, blue, t; - u_char *out; - ngx_buf_t *b; - ngx_uint_t resize; - gdImagePtr src, dst; - ngx_pool_cleanup_t *cln; - ngx_http_image_filter_conf_t *conf; - - src = ngx_http_image_source(r, ctx); - - if (src == NULL) { - return NULL; - } - - sx = gdImageSX(src); - sy = gdImageSY(src); - - conf = ngx_http_get_module_loc_conf(r, ngx_http_image_filter_module); - - if (!ctx->force - && ctx->angle == 0 - && (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); - - 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 > ctx->max_width) { - dy = dy * ctx->max_width / dx; - dy = dy ? dy : 1; - dx = ctx->max_width; - } - - if ((ngx_uint_t) dy > ctx->max_height) { - dx = dx * ctx->max_height / dy; - dx = dx ? dx : 1; - dy = ctx->max_height; - } - - resize = 1; - - } else if (conf->filter == NGX_HTTP_IMAGE_ROTATE) { - - resize = 0; - - } else { /* NGX_HTTP_IMAGE_CROP */ - - resize = 0; - - if ((double) dx / dy < (double) ctx->max_width / ctx->max_height) { - 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 ((ngx_uint_t) dy > ctx->max_height) { - dx = dx * ctx->max_height / dy; - dx = dx ? dx : 1; - dy = ctx->max_height; - resize = 1; - } - } - } - - if (resize) { - 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 { - dst = src; - } - - if (ctx->angle) { - src = dst; - - ax = (dx % 2 == 0) ? 1 : 0; - ay = (dy % 2 == 0) ? 1 : 0; - - switch (ctx->angle) { - - case 90: - case 270: - dst = ngx_http_image_new(r, dy, dx, palette); - if (dst == NULL) { - gdImageDestroy(src); - return NULL; - } - if (ctx->angle == 90) { - ox = dy / 2 + ay; - oy = dx / 2 - ax; - - } else { - ox = dy / 2 - ay; - oy = dx / 2 + ax; - } - - gdImageCopyRotated(dst, src, ox, oy, 0, 0, - dx + ax, dy + ay, 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 - ax, dy / 2 - ay, 0, 0, - dx + ax, dy + ay, ctx->angle); - gdImageDestroy(src); - break; - } - } - - if (conf->filter == NGX_HTTP_IMAGE_CROP) { - - src = dst; - - if ((ngx_uint_t) dx > ctx->max_width) { - ox = dx - ctx->max_width; - - } else { - ox = 0; - } - - if ((ngx_uint_t) dy > ctx->max_height) { - oy = dy - ctx->max_height; - - } else { - oy = 0; - } - - if (ox || oy) { - - dst = ngx_http_image_new(r, dx - ox, dy - oy, colors); - - if (dst == NULL) { - gdImageDestroy(src); - return NULL; - } - - ox /= 2; - oy /= 2; - - 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, dy - oy); - - if (colors) { - gdImageTrueColorToPalette(dst, 1, 256); - } - - gdImageDestroy(src); - } - } - - if (transparent != -1 && colors) { - gdImageColorTransparent(dst, gdImageColorExact(dst, red, green, blue)); - } - - sharpen = ngx_http_image_filter_get_value(r, conf->shcv, conf->sharpen); - if (sharpen > 0) { - gdImageSharpen(dst, sharpen); - } - - gdImageInterlace(dst, (int) conf->interlace); - - out = ngx_http_image_out(r, ctx->type, dst, &size); - - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "image: %d x %d %d", sx, sy, colors); - - gdImageDestroy(dst); - ngx_pfree(r->pool, ctx->image); - - if (out == NULL) { - return NULL; - } - - cln = ngx_pool_cleanup_add(r->pool, 0); - if (cln == NULL) { - gdFree(out); - return NULL; - } - - b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t)); - if (b == NULL) { - gdFree(out); - return NULL; - } - - cln->handler = ngx_http_image_cleanup; - cln->data = out; - - b->pos = out; - b->last = out + size; - b->memory = 1; - b->last_buf = 1; - - ngx_http_image_length(r, b); - ngx_http_weak_etag(r); - - return b; -} - - -static gdImagePtr -ngx_http_image_source(ngx_http_request_t *r, ngx_http_image_filter_ctx_t *ctx) -{ - char *failed; - gdImagePtr img; - - img = NULL; - - switch (ctx->type) { - - case NGX_HTTP_IMAGE_JPEG: - img = gdImageCreateFromJpegPtr(ctx->length, ctx->image); - failed = "gdImageCreateFromJpegPtr() failed"; - break; - - case NGX_HTTP_IMAGE_GIF: - img = gdImageCreateFromGifPtr(ctx->length, ctx->image); - failed = "gdImageCreateFromGifPtr() failed"; - break; - - case NGX_HTTP_IMAGE_PNG: - img = gdImageCreateFromPngPtr(ctx->length, ctx->image); - failed = "gdImageCreateFromPngPtr() failed"; - break; - - case NGX_HTTP_IMAGE_WEBP: -#if (NGX_HAVE_GD_WEBP) - img = gdImageCreateFromWebpPtr(ctx->length, ctx->image); - failed = "gdImageCreateFromWebpPtr() failed"; -#else - failed = "nginx was built without GD WebP support"; -#endif - break; - - default: - failed = "unknown image type"; - break; - } - - if (img == NULL) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, failed); - } - - return img; -} - - -static gdImagePtr -ngx_http_image_new(ngx_http_request_t *r, int w, int h, int colors) -{ - gdImagePtr img; - - if (colors == 0) { - img = gdImageCreateTrueColor(w, h); - - if (img == NULL) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "gdImageCreateTrueColor() failed"); - return NULL; - } - - } else { - img = gdImageCreate(w, h); - - if (img == NULL) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "gdImageCreate() failed"); - return NULL; - } - } - - return img; -} - - -static u_char * -ngx_http_image_out(ngx_http_request_t *r, ngx_uint_t type, gdImagePtr img, - int *size) -{ - char *failed; - u_char *out; - ngx_int_t q; - ngx_http_image_filter_conf_t *conf; - - out = NULL; - - switch (type) { - - case NGX_HTTP_IMAGE_JPEG: - conf = ngx_http_get_module_loc_conf(r, ngx_http_image_filter_module); - - q = ngx_http_image_filter_get_value(r, conf->jqcv, conf->jpeg_quality); - if (q <= 0) { - return NULL; - } - - out = gdImageJpegPtr(img, size, q); - failed = "gdImageJpegPtr() failed"; - break; - - case NGX_HTTP_IMAGE_GIF: - out = gdImageGifPtr(img, size); - failed = "gdImageGifPtr() failed"; - break; - - case NGX_HTTP_IMAGE_PNG: - out = gdImagePngPtr(img, size); - failed = "gdImagePngPtr() failed"; - break; - - case NGX_HTTP_IMAGE_WEBP: -#if (NGX_HAVE_GD_WEBP) - conf = ngx_http_get_module_loc_conf(r, ngx_http_image_filter_module); - - q = ngx_http_image_filter_get_value(r, conf->wqcv, conf->webp_quality); - if (q <= 0) { - return NULL; - } - - out = gdImageWebpPtrEx(img, size, q); - failed = "gdImageWebpPtrEx() failed"; -#else - failed = "nginx was built without GD WebP support"; -#endif - break; - - default: - failed = "unknown image type"; - break; - } - - if (out == NULL) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, failed); - } - - return out; -} - - -static void -ngx_http_image_cleanup(void *data) -{ - gdFree(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) -{ - ngx_http_image_filter_conf_t *conf; - - conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_image_filter_conf_t)); - if (conf == NULL) { - return NULL; - } - - /* - * set by ngx_pcalloc(): - * - * conf->width = 0; - * conf->height = 0; - * conf->angle = 0; - * conf->wcv = NULL; - * conf->hcv = NULL; - * conf->acv = NULL; - * conf->jqcv = NULL; - * conf->wqcv = NULL; - * conf->shcv = NULL; - */ - - conf->filter = NGX_CONF_UNSET_UINT; - conf->jpeg_quality = NGX_CONF_UNSET_UINT; - conf->webp_quality = NGX_CONF_UNSET_UINT; - conf->sharpen = NGX_CONF_UNSET_UINT; - conf->transparency = NGX_CONF_UNSET; - conf->interlace = NGX_CONF_UNSET; - conf->buffer_size = NGX_CONF_UNSET_SIZE; - - return conf; -} - - -static char * -ngx_http_image_filter_merge_conf(ngx_conf_t *cf, void *parent, void *child) -{ - ngx_http_image_filter_conf_t *prev = parent; - ngx_http_image_filter_conf_t *conf = child; - - if (conf->filter == NGX_CONF_UNSET_UINT) { - - if (prev->filter == NGX_CONF_UNSET_UINT) { - conf->filter = NGX_HTTP_IMAGE_OFF; - - } else { - conf->filter = prev->filter; - conf->width = prev->width; - conf->height = prev->height; - conf->angle = prev->angle; - conf->wcv = prev->wcv; - conf->hcv = prev->hcv; - conf->acv = prev->acv; - } - } - - if (conf->jpeg_quality == NGX_CONF_UNSET_UINT) { - - /* 75 is libjpeg default quality */ - ngx_conf_merge_uint_value(conf->jpeg_quality, prev->jpeg_quality, 75); - - if (conf->jqcv == NULL) { - conf->jqcv = prev->jqcv; - } - } - - if (conf->webp_quality == NGX_CONF_UNSET_UINT) { - - /* 80 is libwebp default quality */ - ngx_conf_merge_uint_value(conf->webp_quality, prev->webp_quality, 80); - - if (conf->wqcv == NULL) { - conf->wqcv = prev->wqcv; - } - } - - if (conf->sharpen == NGX_CONF_UNSET_UINT) { - ngx_conf_merge_uint_value(conf->sharpen, prev->sharpen, 0); - - if (conf->shcv == NULL) { - conf->shcv = prev->shcv; - } - } - - ngx_conf_merge_value(conf->transparency, prev->transparency, 1); - - ngx_conf_merge_value(conf->interlace, prev->interlace, 0); - - ngx_conf_merge_size_value(conf->buffer_size, prev->buffer_size, - 1 * 1024 * 1024); - - return NGX_CONF_OK; -} - - -static char * -ngx_http_image_filter(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_uint_t i; - ngx_http_complex_value_t cv; - ngx_http_compile_complex_value_t ccv; - - value = cf->args->elts; - - i = 1; - - if (cf->args->nelts == 2) { - if (ngx_strcmp(value[i].data, "off") == 0) { - imcf->filter = NGX_HTTP_IMAGE_OFF; - - } else if (ngx_strcmp(value[i].data, "test") == 0) { - imcf->filter = NGX_HTTP_IMAGE_TEST; - - } else if (ngx_strcmp(value[i].data, "size") == 0) { - imcf->filter = NGX_HTTP_IMAGE_SIZE; - - } else { - goto failed; - } - - return NGX_CONF_OK; - - } else if (cf->args->nelts == 3) { - - if (ngx_strcmp(value[i].data, "rotate") == 0) { - if (imcf->filter != NGX_HTTP_IMAGE_RESIZE - && imcf->filter != NGX_HTTP_IMAGE_CROP) - { - 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) { - imcf->filter = NGX_HTTP_IMAGE_RESIZE; - - } else if (ngx_strcmp(value[i].data, "crop") == 0) { - imcf->filter = NGX_HTTP_IMAGE_CROP; - - } else { - goto failed; - } - - 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 == 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; - } - - 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 == 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; - -failed: - - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid parameter \"%V\"", - &value[i]); - - return NGX_CONF_ERROR; -} - - -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 value \"%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 char * -ngx_http_image_filter_webp_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 value \"%V\"", &value[1]); - return NGX_CONF_ERROR; - } - - imcf->webp_quality = (ngx_uint_t) n; - - } else { - imcf->wqcv = ngx_palloc(cf->pool, sizeof(ngx_http_complex_value_t)); - if (imcf->wqcv == NULL) { - return NGX_CONF_ERROR; - } - - *imcf->wqcv = cv; - } - - return NGX_CONF_OK; -} - - -static char * -ngx_http_image_filter_sharpen(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 value \"%V\"", &value[1]); - return NGX_CONF_ERROR; - } - - imcf->sharpen = (ngx_uint_t) n; - - } else { - imcf->shcv = ngx_palloc(cf->pool, sizeof(ngx_http_complex_value_t)); - if (imcf->shcv == NULL) { - return NGX_CONF_ERROR; - } - - *imcf->shcv = cv; - } - - return NGX_CONF_OK; -} - - -static ngx_int_t -ngx_http_image_filter_init(ngx_conf_t *cf) -{ - ngx_http_next_header_filter = ngx_http_top_header_filter; - ngx_http_top_header_filter = ngx_http_image_header_filter; - - ngx_http_next_body_filter = ngx_http_top_body_filter; - ngx_http_top_body_filter = ngx_http_image_body_filter; - - return NGX_OK; -} |