Annotation of embedaddon/nginx/src/http/modules/ngx_http_image_filter_module.c, revision 1.1.1.1

1.1       misho       1: 
                      2: /*
                      3:  * Copyright (C) Igor Sysoev
                      4:  * Copyright (C) Nginx, Inc.
                      5:  */
                      6: 
                      7: 
                      8: #include <ngx_config.h>
                      9: #include <ngx_core.h>
                     10: #include <ngx_http.h>
                     11: 
                     12: #include <gd.h>
                     13: 
                     14: 
                     15: #define NGX_HTTP_IMAGE_OFF       0
                     16: #define NGX_HTTP_IMAGE_TEST      1
                     17: #define NGX_HTTP_IMAGE_SIZE      2
                     18: #define NGX_HTTP_IMAGE_RESIZE    3
                     19: #define NGX_HTTP_IMAGE_CROP      4
                     20: #define NGX_HTTP_IMAGE_ROTATE    5
                     21: 
                     22: 
                     23: #define NGX_HTTP_IMAGE_START     0
                     24: #define NGX_HTTP_IMAGE_READ      1
                     25: #define NGX_HTTP_IMAGE_PROCESS   2
                     26: #define NGX_HTTP_IMAGE_PASS      3
                     27: #define NGX_HTTP_IMAGE_DONE      4
                     28: 
                     29: 
                     30: #define NGX_HTTP_IMAGE_NONE      0
                     31: #define NGX_HTTP_IMAGE_JPEG      1
                     32: #define NGX_HTTP_IMAGE_GIF       2
                     33: #define NGX_HTTP_IMAGE_PNG       3
                     34: 
                     35: 
                     36: #define NGX_HTTP_IMAGE_BUFFERED  0x08
                     37: 
                     38: 
                     39: typedef struct {
                     40:     ngx_uint_t                   filter;
                     41:     ngx_uint_t                   width;
                     42:     ngx_uint_t                   height;
                     43:     ngx_uint_t                   angle;
                     44:     ngx_uint_t                   jpeg_quality;
                     45:     ngx_uint_t                   sharpen;
                     46: 
                     47:     ngx_flag_t                   transparency;
                     48:     ngx_flag_t                   interlace;
                     49: 
                     50:     ngx_http_complex_value_t    *wcv;
                     51:     ngx_http_complex_value_t    *hcv;
                     52:     ngx_http_complex_value_t    *acv;
                     53:     ngx_http_complex_value_t    *jqcv;
                     54:     ngx_http_complex_value_t    *shcv;
                     55: 
                     56:     size_t                       buffer_size;
                     57: } ngx_http_image_filter_conf_t;
                     58: 
                     59: 
                     60: typedef struct {
                     61:     u_char                      *image;
                     62:     u_char                      *last;
                     63: 
                     64:     size_t                       length;
                     65: 
                     66:     ngx_uint_t                   width;
                     67:     ngx_uint_t                   height;
                     68:     ngx_uint_t                   max_width;
                     69:     ngx_uint_t                   max_height;
                     70:     ngx_uint_t                   angle;
                     71: 
                     72:     ngx_uint_t                   phase;
                     73:     ngx_uint_t                   type;
                     74:     ngx_uint_t                   force;
                     75: } ngx_http_image_filter_ctx_t;
                     76: 
                     77: 
                     78: static ngx_int_t ngx_http_image_send(ngx_http_request_t *r,
                     79:     ngx_http_image_filter_ctx_t *ctx, ngx_chain_t *in);
                     80: static ngx_uint_t ngx_http_image_test(ngx_http_request_t *r, ngx_chain_t *in);
                     81: static ngx_int_t ngx_http_image_read(ngx_http_request_t *r, ngx_chain_t *in);
                     82: static ngx_buf_t *ngx_http_image_process(ngx_http_request_t *r);
                     83: static ngx_buf_t *ngx_http_image_json(ngx_http_request_t *r,
                     84:     ngx_http_image_filter_ctx_t *ctx);
                     85: static ngx_buf_t *ngx_http_image_asis(ngx_http_request_t *r,
                     86:     ngx_http_image_filter_ctx_t *ctx);
                     87: static void ngx_http_image_length(ngx_http_request_t *r, ngx_buf_t *b);
                     88: static ngx_int_t ngx_http_image_size(ngx_http_request_t *r,
                     89:     ngx_http_image_filter_ctx_t *ctx);
                     90: 
                     91: static ngx_buf_t *ngx_http_image_resize(ngx_http_request_t *r,
                     92:     ngx_http_image_filter_ctx_t *ctx);
                     93: static gdImagePtr ngx_http_image_source(ngx_http_request_t *r,
                     94:     ngx_http_image_filter_ctx_t *ctx);
                     95: static gdImagePtr ngx_http_image_new(ngx_http_request_t *r, int w, int h,
                     96:     int colors);
                     97: static u_char *ngx_http_image_out(ngx_http_request_t *r, ngx_uint_t type,
                     98:     gdImagePtr img, int *size);
                     99: static void ngx_http_image_cleanup(void *data);
                    100: static ngx_uint_t ngx_http_image_filter_get_value(ngx_http_request_t *r,
                    101:     ngx_http_complex_value_t *cv, ngx_uint_t v);
                    102: static ngx_uint_t ngx_http_image_filter_value(ngx_str_t *value);
                    103: 
                    104: 
                    105: static void *ngx_http_image_filter_create_conf(ngx_conf_t *cf);
                    106: static char *ngx_http_image_filter_merge_conf(ngx_conf_t *cf, void *parent,
                    107:     void *child);
                    108: static char *ngx_http_image_filter(ngx_conf_t *cf, ngx_command_t *cmd,
                    109:     void *conf);
                    110: static char *ngx_http_image_filter_jpeg_quality(ngx_conf_t *cf,
                    111:     ngx_command_t *cmd, void *conf);
                    112: static char *ngx_http_image_filter_sharpen(ngx_conf_t *cf, ngx_command_t *cmd,
                    113:     void *conf);
                    114: static ngx_int_t ngx_http_image_filter_init(ngx_conf_t *cf);
                    115: 
                    116: 
                    117: static ngx_command_t  ngx_http_image_filter_commands[] = {
                    118: 
                    119:     { ngx_string("image_filter"),
                    120:       NGX_HTTP_LOC_CONF|NGX_CONF_TAKE123,
                    121:       ngx_http_image_filter,
                    122:       NGX_HTTP_LOC_CONF_OFFSET,
                    123:       0,
                    124:       NULL },
                    125: 
                    126:     { ngx_string("image_filter_jpeg_quality"),
                    127:       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
                    128:       ngx_http_image_filter_jpeg_quality,
                    129:       NGX_HTTP_LOC_CONF_OFFSET,
                    130:       0,
                    131:       NULL },
                    132: 
                    133:     { ngx_string("image_filter_sharpen"),
                    134:       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
                    135:       ngx_http_image_filter_sharpen,
                    136:       NGX_HTTP_LOC_CONF_OFFSET,
                    137:       0,
                    138:       NULL },
                    139: 
                    140:     { ngx_string("image_filter_transparency"),
                    141:       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
                    142:       ngx_conf_set_flag_slot,
                    143:       NGX_HTTP_LOC_CONF_OFFSET,
                    144:       offsetof(ngx_http_image_filter_conf_t, transparency),
                    145:       NULL },
                    146: 
                    147:    { ngx_string("image_filter_interlace"),
                    148:       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
                    149:       ngx_conf_set_flag_slot,
                    150:       NGX_HTTP_LOC_CONF_OFFSET,
                    151:       offsetof(ngx_http_image_filter_conf_t, interlace),
                    152:       NULL },
                    153: 
                    154:     { ngx_string("image_filter_buffer"),
                    155:       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
                    156:       ngx_conf_set_size_slot,
                    157:       NGX_HTTP_LOC_CONF_OFFSET,
                    158:       offsetof(ngx_http_image_filter_conf_t, buffer_size),
                    159:       NULL },
                    160: 
                    161:       ngx_null_command
                    162: };
                    163: 
                    164: 
                    165: static ngx_http_module_t  ngx_http_image_filter_module_ctx = {
                    166:     NULL,                                  /* preconfiguration */
                    167:     ngx_http_image_filter_init,            /* postconfiguration */
                    168: 
                    169:     NULL,                                  /* create main configuration */
                    170:     NULL,                                  /* init main configuration */
                    171: 
                    172:     NULL,                                  /* create server configuration */
                    173:     NULL,                                  /* merge server configuration */
                    174: 
                    175:     ngx_http_image_filter_create_conf,     /* create location configuration */
                    176:     ngx_http_image_filter_merge_conf       /* merge location configuration */
                    177: };
                    178: 
                    179: 
                    180: ngx_module_t  ngx_http_image_filter_module = {
                    181:     NGX_MODULE_V1,
                    182:     &ngx_http_image_filter_module_ctx,     /* module context */
                    183:     ngx_http_image_filter_commands,        /* module directives */
                    184:     NGX_HTTP_MODULE,                       /* module type */
                    185:     NULL,                                  /* init master */
                    186:     NULL,                                  /* init module */
                    187:     NULL,                                  /* init process */
                    188:     NULL,                                  /* init thread */
                    189:     NULL,                                  /* exit thread */
                    190:     NULL,                                  /* exit process */
                    191:     NULL,                                  /* exit master */
                    192:     NGX_MODULE_V1_PADDING
                    193: };
                    194: 
                    195: 
                    196: static ngx_http_output_header_filter_pt  ngx_http_next_header_filter;
                    197: static ngx_http_output_body_filter_pt    ngx_http_next_body_filter;
                    198: 
                    199: 
                    200: static ngx_str_t  ngx_http_image_types[] = {
                    201:     ngx_string("image/jpeg"),
                    202:     ngx_string("image/gif"),
                    203:     ngx_string("image/png")
                    204: };
                    205: 
                    206: 
                    207: static ngx_int_t
                    208: ngx_http_image_header_filter(ngx_http_request_t *r)
                    209: {
                    210:     off_t                          len;
                    211:     ngx_http_image_filter_ctx_t   *ctx;
                    212:     ngx_http_image_filter_conf_t  *conf;
                    213: 
                    214:     if (r->headers_out.status == NGX_HTTP_NOT_MODIFIED) {
                    215:         return ngx_http_next_header_filter(r);
                    216:     }
                    217: 
                    218:     ctx = ngx_http_get_module_ctx(r, ngx_http_image_filter_module);
                    219: 
                    220:     if (ctx) {
                    221:         ngx_http_set_ctx(r, NULL, ngx_http_image_filter_module);
                    222:         return ngx_http_next_header_filter(r);
                    223:     }
                    224: 
                    225:     conf = ngx_http_get_module_loc_conf(r, ngx_http_image_filter_module);
                    226: 
                    227:     if (conf->filter == NGX_HTTP_IMAGE_OFF) {
                    228:         return ngx_http_next_header_filter(r);
                    229:     }
                    230: 
                    231:     if (r->headers_out.content_type.len
                    232:             >= sizeof("multipart/x-mixed-replace") - 1
                    233:         && ngx_strncasecmp(r->headers_out.content_type.data,
                    234:                            (u_char *) "multipart/x-mixed-replace",
                    235:                            sizeof("multipart/x-mixed-replace") - 1)
                    236:            == 0)
                    237:     {
                    238:         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                    239:                       "image filter: multipart/x-mixed-replace response");
                    240: 
                    241:         return NGX_ERROR;
                    242:     }
                    243: 
                    244:     ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_image_filter_ctx_t));
                    245:     if (ctx == NULL) {
                    246:         return NGX_ERROR;
                    247:     }
                    248: 
                    249:     ngx_http_set_ctx(r, ctx, ngx_http_image_filter_module);
                    250: 
                    251:     len = r->headers_out.content_length_n;
                    252: 
                    253:     if (len != -1 && len > (off_t) conf->buffer_size) {
                    254:         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                    255:                       "image filter: too big response: %O", len);
                    256: 
                    257:         return NGX_HTTP_UNSUPPORTED_MEDIA_TYPE;
                    258:     }
                    259: 
                    260:     if (len == -1) {
                    261:         ctx->length = conf->buffer_size;
                    262: 
                    263:     } else {
                    264:         ctx->length = (size_t) len;
                    265:     }
                    266: 
                    267:     if (r->headers_out.refresh) {
                    268:         r->headers_out.refresh->hash = 0;
                    269:     }
                    270: 
                    271:     r->main_filter_need_in_memory = 1;
                    272:     r->allow_ranges = 0;
                    273: 
                    274:     return NGX_OK;
                    275: }
                    276: 
                    277: 
                    278: static ngx_int_t
                    279: ngx_http_image_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
                    280: {
                    281:     ngx_int_t                      rc;
                    282:     ngx_str_t                     *ct;
                    283:     ngx_chain_t                    out;
                    284:     ngx_http_image_filter_ctx_t   *ctx;
                    285:     ngx_http_image_filter_conf_t  *conf;
                    286: 
                    287:     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "image filter");
                    288: 
                    289:     if (in == NULL) {
                    290:         return ngx_http_next_body_filter(r, in);
                    291:     }
                    292: 
                    293:     ctx = ngx_http_get_module_ctx(r, ngx_http_image_filter_module);
                    294: 
                    295:     if (ctx == NULL) {
                    296:         return ngx_http_next_body_filter(r, in);
                    297:     }
                    298: 
                    299:     switch (ctx->phase) {
                    300: 
                    301:     case NGX_HTTP_IMAGE_START:
                    302: 
                    303:         ctx->type = ngx_http_image_test(r, in);
                    304: 
                    305:         conf = ngx_http_get_module_loc_conf(r, ngx_http_image_filter_module);
                    306: 
                    307:         if (ctx->type == NGX_HTTP_IMAGE_NONE) {
                    308: 
                    309:             if (conf->filter == NGX_HTTP_IMAGE_SIZE) {
                    310:                 out.buf = ngx_http_image_json(r, NULL);
                    311: 
                    312:                 if (out.buf) {
                    313:                     out.next = NULL;
                    314:                     ctx->phase = NGX_HTTP_IMAGE_DONE;
                    315: 
                    316:                     return ngx_http_image_send(r, ctx, &out);
                    317:                 }
                    318:             }
                    319: 
                    320:             return ngx_http_filter_finalize_request(r,
                    321:                                               &ngx_http_image_filter_module,
                    322:                                               NGX_HTTP_UNSUPPORTED_MEDIA_TYPE);
                    323:         }
                    324: 
                    325:         /* override content type */
                    326: 
                    327:         ct = &ngx_http_image_types[ctx->type - 1];
                    328:         r->headers_out.content_type_len = ct->len;
                    329:         r->headers_out.content_type = *ct;
                    330:         r->headers_out.content_type_lowcase = NULL;
                    331: 
                    332:         if (conf->filter == NGX_HTTP_IMAGE_TEST) {
                    333:             ctx->phase = NGX_HTTP_IMAGE_PASS;
                    334: 
                    335:             return ngx_http_image_send(r, ctx, in);
                    336:         }
                    337: 
                    338:         ctx->phase = NGX_HTTP_IMAGE_READ;
                    339: 
                    340:         /* fall through */
                    341: 
                    342:     case NGX_HTTP_IMAGE_READ:
                    343: 
                    344:         rc = ngx_http_image_read(r, in);
                    345: 
                    346:         if (rc == NGX_AGAIN) {
                    347:             return NGX_OK;
                    348:         }
                    349: 
                    350:         if (rc == NGX_ERROR) {
                    351:             return ngx_http_filter_finalize_request(r,
                    352:                                               &ngx_http_image_filter_module,
                    353:                                               NGX_HTTP_UNSUPPORTED_MEDIA_TYPE);
                    354:         }
                    355: 
                    356:         /* fall through */
                    357: 
                    358:     case NGX_HTTP_IMAGE_PROCESS:
                    359: 
                    360:         out.buf = ngx_http_image_process(r);
                    361: 
                    362:         if (out.buf == NULL) {
                    363:             return ngx_http_filter_finalize_request(r,
                    364:                                               &ngx_http_image_filter_module,
                    365:                                               NGX_HTTP_UNSUPPORTED_MEDIA_TYPE);
                    366:         }
                    367: 
                    368:         out.next = NULL;
                    369:         ctx->phase = NGX_HTTP_IMAGE_PASS;
                    370: 
                    371:         return ngx_http_image_send(r, ctx, &out);
                    372: 
                    373:     case NGX_HTTP_IMAGE_PASS:
                    374: 
                    375:         return ngx_http_next_body_filter(r, in);
                    376: 
                    377:     default: /* NGX_HTTP_IMAGE_DONE */
                    378: 
                    379:         rc = ngx_http_next_body_filter(r, NULL);
                    380: 
                    381:         /* NGX_ERROR resets any pending data */
                    382:         return (rc == NGX_OK) ? NGX_ERROR : rc;
                    383:     }
                    384: }
                    385: 
                    386: 
                    387: static ngx_int_t
                    388: ngx_http_image_send(ngx_http_request_t *r, ngx_http_image_filter_ctx_t *ctx,
                    389:     ngx_chain_t *in)
                    390: {
                    391:     ngx_int_t  rc;
                    392: 
                    393:     rc = ngx_http_next_header_filter(r);
                    394: 
                    395:     if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
                    396:         return NGX_ERROR;
                    397:     }
                    398: 
                    399:     rc = ngx_http_next_body_filter(r, in);
                    400: 
                    401:     if (ctx->phase == NGX_HTTP_IMAGE_DONE) {
                    402:         /* NGX_ERROR resets any pending data */
                    403:         return (rc == NGX_OK) ? NGX_ERROR : rc;
                    404:     }
                    405: 
                    406:     return rc;
                    407: }
                    408: 
                    409: 
                    410: static ngx_uint_t
                    411: ngx_http_image_test(ngx_http_request_t *r, ngx_chain_t *in)
                    412: {
                    413:     u_char  *p;
                    414: 
                    415:     p = in->buf->pos;
                    416: 
                    417:     if (in->buf->last - p < 16) {
                    418:         return NGX_HTTP_IMAGE_NONE;
                    419:     }
                    420: 
                    421:     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    422:                    "image filter: \"%c%c\"", p[0], p[1]);
                    423: 
                    424:     if (p[0] == 0xff && p[1] == 0xd8) {
                    425: 
                    426:         /* JPEG */
                    427: 
                    428:         return NGX_HTTP_IMAGE_JPEG;
                    429: 
                    430:     } else if (p[0] == 'G' && p[1] == 'I' && p[2] == 'F' && p[3] == '8'
                    431:                && p[5] == 'a')
                    432:     {
                    433:         if (p[4] == '9' || p[4] == '7') {
                    434:             /* GIF */
                    435:             return NGX_HTTP_IMAGE_GIF;
                    436:         }
                    437: 
                    438:     } else if (p[0] == 0x89 && p[1] == 'P' && p[2] == 'N' && p[3] == 'G'
                    439:                && p[4] == 0x0d && p[5] == 0x0a && p[6] == 0x1a && p[7] == 0x0a)
                    440:     {
                    441:         /* PNG */
                    442: 
                    443:         return NGX_HTTP_IMAGE_PNG;
                    444:     }
                    445: 
                    446:     return NGX_HTTP_IMAGE_NONE;
                    447: }
                    448: 
                    449: 
                    450: static ngx_int_t
                    451: ngx_http_image_read(ngx_http_request_t *r, ngx_chain_t *in)
                    452: {
                    453:     u_char                       *p;
                    454:     size_t                        size, rest;
                    455:     ngx_buf_t                    *b;
                    456:     ngx_chain_t                  *cl;
                    457:     ngx_http_image_filter_ctx_t  *ctx;
                    458: 
                    459:     ctx = ngx_http_get_module_ctx(r, ngx_http_image_filter_module);
                    460: 
                    461:     if (ctx->image == NULL) {
                    462:         ctx->image = ngx_palloc(r->pool, ctx->length);
                    463:         if (ctx->image == NULL) {
                    464:             return NGX_ERROR;
                    465:         }
                    466: 
                    467:         ctx->last = ctx->image;
                    468:     }
                    469: 
                    470:     p = ctx->last;
                    471: 
                    472:     for (cl = in; cl; cl = cl->next) {
                    473: 
                    474:         b = cl->buf;
                    475:         size = b->last - b->pos;
                    476: 
                    477:         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    478:                        "image buf: %uz", size);
                    479: 
                    480:         rest = ctx->image + ctx->length - p;
                    481:         size = (rest < size) ? rest : size;
                    482: 
                    483:         p = ngx_cpymem(p, b->pos, size);
                    484:         b->pos += size;
                    485: 
                    486:         if (b->last_buf) {
                    487:             ctx->last = p;
                    488:             return NGX_OK;
                    489:         }
                    490:     }
                    491: 
                    492:     ctx->last = p;
                    493:     r->connection->buffered |= NGX_HTTP_IMAGE_BUFFERED;
                    494: 
                    495:     return NGX_AGAIN;
                    496: }
                    497: 
                    498: 
                    499: static ngx_buf_t *
                    500: ngx_http_image_process(ngx_http_request_t *r)
                    501: {
                    502:     ngx_int_t                      rc;
                    503:     ngx_http_image_filter_ctx_t   *ctx;
                    504:     ngx_http_image_filter_conf_t  *conf;
                    505: 
                    506:     r->connection->buffered &= ~NGX_HTTP_IMAGE_BUFFERED;
                    507: 
                    508:     ctx = ngx_http_get_module_ctx(r, ngx_http_image_filter_module);
                    509: 
                    510:     rc = ngx_http_image_size(r, ctx);
                    511: 
                    512:     conf = ngx_http_get_module_loc_conf(r, ngx_http_image_filter_module);
                    513: 
                    514:     if (conf->filter == NGX_HTTP_IMAGE_SIZE) {
                    515:         return ngx_http_image_json(r, rc == NGX_OK ? ctx : NULL);
                    516:     }
                    517: 
                    518:     ctx->angle = ngx_http_image_filter_get_value(r, conf->acv, conf->angle);
                    519: 
                    520:     if (conf->filter == NGX_HTTP_IMAGE_ROTATE) {
                    521: 
                    522:         if (ctx->angle != 90 && ctx->angle != 180 && ctx->angle != 270) {
                    523:             return NULL;
                    524:         }
                    525: 
                    526:         return ngx_http_image_resize(r, ctx);
                    527:     }
                    528: 
                    529:     ctx->max_width = ngx_http_image_filter_get_value(r, conf->wcv, conf->width);
                    530:     if (ctx->max_width == 0) {
                    531:         return NULL;
                    532:     }
                    533: 
                    534:     ctx->max_height = ngx_http_image_filter_get_value(r, conf->hcv,
                    535:                                                       conf->height);
                    536:     if (ctx->max_height == 0) {
                    537:         return NULL;
                    538:     }
                    539: 
                    540:     if (rc == NGX_OK
                    541:         && ctx->width <= ctx->max_width
                    542:         && ctx->height <= ctx->max_height
                    543:         && ctx->angle == 0
                    544:         && !ctx->force)
                    545:     {
                    546:         return ngx_http_image_asis(r, ctx);
                    547:     }
                    548: 
                    549:     return ngx_http_image_resize(r, ctx);
                    550: }
                    551: 
                    552: 
                    553: static ngx_buf_t *
                    554: ngx_http_image_json(ngx_http_request_t *r, ngx_http_image_filter_ctx_t *ctx)
                    555: {
                    556:     size_t      len;
                    557:     ngx_buf_t  *b;
                    558: 
                    559:     b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
                    560:     if (b == NULL) {
                    561:         return NULL;
                    562:     }
                    563: 
                    564:     b->memory = 1;
                    565:     b->last_buf = 1;
                    566: 
                    567:     ngx_http_clean_header(r);
                    568: 
                    569:     r->headers_out.status = NGX_HTTP_OK;
                    570:     ngx_str_set(&r->headers_out.content_type, "text/plain");
                    571:     r->headers_out.content_type_lowcase = NULL;
                    572: 
                    573:     if (ctx == NULL) {
                    574:         b->pos = (u_char *) "{}" CRLF;
                    575:         b->last = b->pos + sizeof("{}" CRLF) - 1;
                    576: 
                    577:         ngx_http_image_length(r, b);
                    578: 
                    579:         return b;
                    580:     }
                    581: 
                    582:     len = sizeof("{ \"img\" : "
                    583:                  "{ \"width\": , \"height\": , \"type\": \"jpeg\" } }" CRLF) - 1
                    584:           + 2 * NGX_SIZE_T_LEN;
                    585: 
                    586:     b->pos = ngx_pnalloc(r->pool, len);
                    587:     if (b->pos == NULL) {
                    588:         return NULL;
                    589:     }
                    590: 
                    591:     b->last = ngx_sprintf(b->pos,
                    592:                           "{ \"img\" : "
                    593:                                        "{ \"width\": %uz,"
                    594:                                         " \"height\": %uz,"
                    595:                                         " \"type\": \"%s\" } }" CRLF,
                    596:                           ctx->width, ctx->height,
                    597:                           ngx_http_image_types[ctx->type - 1].data + 6);
                    598: 
                    599:     ngx_http_image_length(r, b);
                    600: 
                    601:     return b;
                    602: }
                    603: 
                    604: 
                    605: static ngx_buf_t *
                    606: ngx_http_image_asis(ngx_http_request_t *r, ngx_http_image_filter_ctx_t *ctx)
                    607: {
                    608:     ngx_buf_t  *b;
                    609: 
                    610:     b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
                    611:     if (b == NULL) {
                    612:         return NULL;
                    613:     }
                    614: 
                    615:     b->pos = ctx->image;
                    616:     b->last = ctx->last;
                    617:     b->memory = 1;
                    618:     b->last_buf = 1;
                    619: 
                    620:     ngx_http_image_length(r, b);
                    621: 
                    622:     return b;
                    623: }
                    624: 
                    625: 
                    626: static void
                    627: ngx_http_image_length(ngx_http_request_t *r, ngx_buf_t *b)
                    628: {
                    629:     r->headers_out.content_length_n = b->last - b->pos;
                    630: 
                    631:     if (r->headers_out.content_length) {
                    632:         r->headers_out.content_length->hash = 0;
                    633:     }
                    634: 
                    635:     r->headers_out.content_length = NULL;
                    636: }
                    637: 
                    638: 
                    639: static ngx_int_t
                    640: ngx_http_image_size(ngx_http_request_t *r, ngx_http_image_filter_ctx_t *ctx)
                    641: {
                    642:     u_char      *p, *last;
                    643:     size_t       len, app;
                    644:     ngx_uint_t   width, height;
                    645: 
                    646:     p = ctx->image;
                    647: 
                    648:     switch (ctx->type) {
                    649: 
                    650:     case NGX_HTTP_IMAGE_JPEG:
                    651: 
                    652:         p += 2;
                    653:         last = ctx->image + ctx->length - 10;
                    654:         width = 0;
                    655:         height = 0;
                    656:         app = 0;
                    657: 
                    658:         while (p < last) {
                    659: 
                    660:             if (p[0] == 0xff && p[1] != 0xff) {
                    661: 
                    662:                 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    663:                                "JPEG: %02xd %02xd", p[0], p[1]);
                    664: 
                    665:                 p++;
                    666: 
                    667:                 if ((*p == 0xc0 || *p == 0xc1 || *p == 0xc2 || *p == 0xc3
                    668:                      || *p == 0xc9 || *p == 0xca || *p == 0xcb)
                    669:                     && (width == 0 || height == 0))
                    670:                 {
                    671:                     width = p[6] * 256 + p[7];
                    672:                     height = p[4] * 256 + p[5];
                    673:                 }
                    674: 
                    675:                 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    676:                                "JPEG: %02xd %02xd", p[1], p[2]);
                    677: 
                    678:                 len = p[1] * 256 + p[2];
                    679: 
                    680:                 if (*p >= 0xe1 && *p <= 0xef) {
                    681:                     /* application data, e.g., EXIF, Adobe XMP, etc. */
                    682:                     app += len;
                    683:                 }
                    684: 
                    685:                 p += len;
                    686: 
                    687:                 continue;
                    688:             }
                    689: 
                    690:             p++;
                    691:         }
                    692: 
                    693:         if (width == 0 || height == 0) {
                    694:             return NGX_DECLINED;
                    695:         }
                    696: 
                    697:         if (ctx->length / 20 < app) {
                    698:             /* force conversion if application data consume more than 5% */
                    699:             ctx->force = 1;
                    700:             ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    701:                            "app data size: %uz", app);
                    702:         }
                    703: 
                    704:         break;
                    705: 
                    706:     case NGX_HTTP_IMAGE_GIF:
                    707: 
                    708:         if (ctx->length < 10) {
                    709:             return NGX_DECLINED;
                    710:         }
                    711: 
                    712:         width = p[7] * 256 + p[6];
                    713:         height = p[9] * 256 + p[8];
                    714: 
                    715:         break;
                    716: 
                    717:     case NGX_HTTP_IMAGE_PNG:
                    718: 
                    719:         if (ctx->length < 24) {
                    720:             return NGX_DECLINED;
                    721:         }
                    722: 
                    723:         width = p[18] * 256 + p[19];
                    724:         height = p[22] * 256 + p[23];
                    725: 
                    726:         break;
                    727: 
                    728:     default:
                    729: 
                    730:         return NGX_DECLINED;
                    731:     }
                    732: 
                    733:     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    734:                    "image size: %d x %d", width, height);
                    735: 
                    736:     ctx->width = width;
                    737:     ctx->height = height;
                    738: 
                    739:     return NGX_OK;
                    740: }
                    741: 
                    742: 
                    743: static ngx_buf_t *
                    744: ngx_http_image_resize(ngx_http_request_t *r, ngx_http_image_filter_ctx_t *ctx)
                    745: {
                    746:     int                            sx, sy, dx, dy, ox, oy, ax, ay, size,
                    747:                                    colors, palette, transparent, sharpen,
                    748:                                    red, green, blue, t;
                    749:     u_char                        *out;
                    750:     ngx_buf_t                     *b;
                    751:     ngx_uint_t                     resize;
                    752:     gdImagePtr                     src, dst;
                    753:     ngx_pool_cleanup_t            *cln;
                    754:     ngx_http_image_filter_conf_t  *conf;
                    755: 
                    756:     src = ngx_http_image_source(r, ctx);
                    757: 
                    758:     if (src == NULL) {
                    759:         return NULL;
                    760:     }
                    761: 
                    762:     sx = gdImageSX(src);
                    763:     sy = gdImageSY(src);
                    764: 
                    765:     conf = ngx_http_get_module_loc_conf(r, ngx_http_image_filter_module);
                    766: 
                    767:     if (!ctx->force
                    768:         && ctx->angle == 0
                    769:         && (ngx_uint_t) sx <= ctx->max_width
                    770:         && (ngx_uint_t) sy <= ctx->max_height)
                    771:     {
                    772:         gdImageDestroy(src);
                    773:         return ngx_http_image_asis(r, ctx);
                    774:     }
                    775: 
                    776:     colors = gdImageColorsTotal(src);
                    777: 
                    778:     if (colors && conf->transparency) {
                    779:         transparent = gdImageGetTransparent(src);
                    780: 
                    781:         if (transparent != -1) {
                    782:             palette = colors;
                    783:             red = gdImageRed(src, transparent);
                    784:             green = gdImageGreen(src, transparent);
                    785:             blue = gdImageBlue(src, transparent);
                    786: 
                    787:             goto transparent;
                    788:         }
                    789:     }
                    790: 
                    791:     palette = 0;
                    792:     transparent = -1;
                    793:     red = 0;
                    794:     green = 0;
                    795:     blue = 0;
                    796: 
                    797: transparent:
                    798: 
                    799:     gdImageColorTransparent(src, -1);
                    800: 
                    801:     dx = sx;
                    802:     dy = sy;
                    803: 
                    804:     if (conf->filter == NGX_HTTP_IMAGE_RESIZE) {
                    805: 
                    806:         if ((ngx_uint_t) dx > ctx->max_width) {
                    807:             dy = dy * ctx->max_width / dx;
                    808:             dy = dy ? dy : 1;
                    809:             dx = ctx->max_width;
                    810:         }
                    811: 
                    812:         if ((ngx_uint_t) dy > ctx->max_height) {
                    813:             dx = dx * ctx->max_height / dy;
                    814:             dx = dx ? dx : 1;
                    815:             dy = ctx->max_height;
                    816:         }
                    817: 
                    818:         resize = 1;
                    819: 
                    820:     } else if (conf->filter == NGX_HTTP_IMAGE_ROTATE) {
                    821: 
                    822:         resize = 0;
                    823: 
                    824:     } else { /* NGX_HTTP_IMAGE_CROP */
                    825: 
                    826:         resize = 0;
                    827: 
                    828:         if ((double) dx / dy < (double) ctx->max_width / ctx->max_height) {
                    829:             if ((ngx_uint_t) dx > ctx->max_width) {
                    830:                 dy = dy * ctx->max_width / dx;
                    831:                 dy = dy ? dy : 1;
                    832:                 dx = ctx->max_width;
                    833:                 resize = 1;
                    834:             }
                    835: 
                    836:         } else {
                    837:             if ((ngx_uint_t) dy > ctx->max_height) {
                    838:                 dx = dx * ctx->max_height / dy;
                    839:                 dx = dx ? dx : 1;
                    840:                 dy = ctx->max_height;
                    841:                 resize = 1;
                    842:             }
                    843:         }
                    844:     }
                    845: 
                    846:     if (resize) {
                    847:         dst = ngx_http_image_new(r, dx, dy, palette);
                    848:         if (dst == NULL) {
                    849:             gdImageDestroy(src);
                    850:             return NULL;
                    851:         }
                    852: 
                    853:         if (colors == 0) {
                    854:             gdImageSaveAlpha(dst, 1);
                    855:             gdImageAlphaBlending(dst, 0);
                    856:         }
                    857: 
                    858:         gdImageCopyResampled(dst, src, 0, 0, 0, 0, dx, dy, sx, sy);
                    859: 
                    860:         if (colors) {
                    861:             gdImageTrueColorToPalette(dst, 1, 256);
                    862:         }
                    863: 
                    864:         gdImageDestroy(src);
                    865: 
                    866:     } else {
                    867:         dst = src;
                    868:     }
                    869: 
                    870:     if (ctx->angle) {
                    871:         src = dst;
                    872: 
                    873:         ax = (dx % 2 == 0) ? 1 : 0;
                    874:         ay = (dy % 2 == 0) ? 1 : 0;
                    875: 
                    876:         switch (ctx->angle) {
                    877: 
                    878:         case 90:
                    879:         case 270:
                    880:             dst = ngx_http_image_new(r, dy, dx, palette);
                    881:             if (dst == NULL) {
                    882:                 gdImageDestroy(src);
                    883:                 return NULL;
                    884:             }
                    885:             if (ctx->angle == 90) {
                    886:                 ox = dy / 2 + ay;
                    887:                 oy = dx / 2 - ax;
                    888: 
                    889:             } else {
                    890:                 ox = dy / 2 - ay;
                    891:                 oy = dx / 2 + ax;
                    892:             }
                    893: 
                    894:             gdImageCopyRotated(dst, src, ox, oy, 0, 0,
                    895:                                dx + ax, dy + ay, ctx->angle);
                    896:             gdImageDestroy(src);
                    897: 
                    898:             t = dx;
                    899:             dx = dy;
                    900:             dy = t;
                    901:             break;
                    902: 
                    903:         case 180:
                    904:             dst = ngx_http_image_new(r, dx, dy, palette);
                    905:             if (dst == NULL) {
                    906:                 gdImageDestroy(src);
                    907:                 return NULL;
                    908:             }
                    909:             gdImageCopyRotated(dst, src, dx / 2 - ax, dy / 2 - ay, 0, 0,
                    910:                                dx + ax, dy + ay, ctx->angle);
                    911:             gdImageDestroy(src);
                    912:             break;
                    913:         }
                    914:     }
                    915: 
                    916:     if (conf->filter == NGX_HTTP_IMAGE_CROP) {
                    917: 
                    918:         src = dst;
                    919: 
                    920:         if ((ngx_uint_t) dx > ctx->max_width) {
                    921:             ox = dx - ctx->max_width;
                    922: 
                    923:         } else {
                    924:             ox = 0;
                    925:         }
                    926: 
                    927:         if ((ngx_uint_t) dy > ctx->max_height) {
                    928:             oy = dy - ctx->max_height;
                    929: 
                    930:         } else {
                    931:             oy = 0;
                    932:         }
                    933: 
                    934:         if (ox || oy) {
                    935: 
                    936:             dst = ngx_http_image_new(r, dx - ox, dy - oy, colors);
                    937: 
                    938:             if (dst == NULL) {
                    939:                 gdImageDestroy(src);
                    940:                 return NULL;
                    941:             }
                    942: 
                    943:             ox /= 2;
                    944:             oy /= 2;
                    945: 
                    946:             ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    947:                            "image crop: %d x %d @ %d x %d",
                    948:                            dx, dy, ox, oy);
                    949: 
                    950:             if (colors == 0) {
                    951:                 gdImageSaveAlpha(dst, 1);
                    952:                 gdImageAlphaBlending(dst, 0);
                    953:             }
                    954: 
                    955:             gdImageCopy(dst, src, 0, 0, ox, oy, dx - ox, dy - oy);
                    956: 
                    957:             if (colors) {
                    958:                 gdImageTrueColorToPalette(dst, 1, 256);
                    959:             }
                    960: 
                    961:             gdImageDestroy(src);
                    962:         }
                    963:     }
                    964: 
                    965:     if (transparent != -1 && colors) {
                    966:         gdImageColorTransparent(dst, gdImageColorExact(dst, red, green, blue));
                    967:     }
                    968: 
                    969:     sharpen = ngx_http_image_filter_get_value(r, conf->shcv, conf->sharpen);
                    970:     if (sharpen > 0) {
                    971:         gdImageSharpen(dst, sharpen);
                    972:     }
                    973: 
                    974:     gdImageInterlace(dst, (int) conf->interlace);
                    975: 
                    976:     out = ngx_http_image_out(r, ctx->type, dst, &size);
                    977: 
                    978:     ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    979:                    "image: %d x %d %d", sx, sy, colors);
                    980: 
                    981:     gdImageDestroy(dst);
                    982:     ngx_pfree(r->pool, ctx->image);
                    983: 
                    984:     if (out == NULL) {
                    985:         return NULL;
                    986:     }
                    987: 
                    988:     cln = ngx_pool_cleanup_add(r->pool, 0);
                    989:     if (cln == NULL) {
                    990:         gdFree(out);
                    991:         return NULL;
                    992:     }
                    993: 
                    994:     b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
                    995:     if (b == NULL) {
                    996:         gdFree(out);
                    997:         return NULL;
                    998:     }
                    999: 
                   1000:     cln->handler = ngx_http_image_cleanup;
                   1001:     cln->data = out;
                   1002: 
                   1003:     b->pos = out;
                   1004:     b->last = out + size;
                   1005:     b->memory = 1;
                   1006:     b->last_buf = 1;
                   1007: 
                   1008:     ngx_http_image_length(r, b);
                   1009: 
                   1010:     return b;
                   1011: }
                   1012: 
                   1013: 
                   1014: static gdImagePtr
                   1015: ngx_http_image_source(ngx_http_request_t *r, ngx_http_image_filter_ctx_t *ctx)
                   1016: {
                   1017:     char        *failed;
                   1018:     gdImagePtr   img;
                   1019: 
                   1020:     img = NULL;
                   1021: 
                   1022:     switch (ctx->type) {
                   1023: 
                   1024:     case NGX_HTTP_IMAGE_JPEG:
                   1025:         img = gdImageCreateFromJpegPtr(ctx->length, ctx->image);
                   1026:         failed = "gdImageCreateFromJpegPtr() failed";
                   1027:         break;
                   1028: 
                   1029:     case NGX_HTTP_IMAGE_GIF:
                   1030:         img = gdImageCreateFromGifPtr(ctx->length, ctx->image);
                   1031:         failed = "gdImageCreateFromGifPtr() failed";
                   1032:         break;
                   1033: 
                   1034:     case NGX_HTTP_IMAGE_PNG:
                   1035:         img = gdImageCreateFromPngPtr(ctx->length, ctx->image);
                   1036:         failed = "gdImageCreateFromPngPtr() failed";
                   1037:         break;
                   1038: 
                   1039:     default:
                   1040:         failed = "unknown image type";
                   1041:         break;
                   1042:     }
                   1043: 
                   1044:     if (img == NULL) {
                   1045:         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, failed);
                   1046:     }
                   1047: 
                   1048:     return img;
                   1049: }
                   1050: 
                   1051: 
                   1052: static gdImagePtr
                   1053: ngx_http_image_new(ngx_http_request_t *r, int w, int h, int colors)
                   1054: {
                   1055:     gdImagePtr  img;
                   1056: 
                   1057:     if (colors == 0) {
                   1058:         img = gdImageCreateTrueColor(w, h);
                   1059: 
                   1060:         if (img == NULL) {
                   1061:             ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                   1062:                           "gdImageCreateTrueColor() failed");
                   1063:             return NULL;
                   1064:         }
                   1065: 
                   1066:     } else {
                   1067:         img = gdImageCreate(w, h);
                   1068: 
                   1069:         if (img == NULL) {
                   1070:             ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                   1071:                           "gdImageCreate() failed");
                   1072:             return NULL;
                   1073:         }
                   1074:     }
                   1075: 
                   1076:     return img;
                   1077: }
                   1078: 
                   1079: 
                   1080: static u_char *
                   1081: ngx_http_image_out(ngx_http_request_t *r, ngx_uint_t type, gdImagePtr img,
                   1082:     int *size)
                   1083: {
                   1084:     char                          *failed;
                   1085:     u_char                        *out;
                   1086:     ngx_int_t                      jq;
                   1087:     ngx_http_image_filter_conf_t  *conf;
                   1088: 
                   1089:     out = NULL;
                   1090: 
                   1091:     switch (type) {
                   1092: 
                   1093:     case NGX_HTTP_IMAGE_JPEG:
                   1094:         conf = ngx_http_get_module_loc_conf(r, ngx_http_image_filter_module);
                   1095: 
                   1096:         jq = ngx_http_image_filter_get_value(r, conf->jqcv, conf->jpeg_quality);
                   1097:         if (jq <= 0) {
                   1098:             return NULL;
                   1099:         }
                   1100: 
                   1101:         out = gdImageJpegPtr(img, size, jq);
                   1102:         failed = "gdImageJpegPtr() failed";
                   1103:         break;
                   1104: 
                   1105:     case NGX_HTTP_IMAGE_GIF:
                   1106:         out = gdImageGifPtr(img, size);
                   1107:         failed = "gdImageGifPtr() failed";
                   1108:         break;
                   1109: 
                   1110:     case NGX_HTTP_IMAGE_PNG:
                   1111:         out = gdImagePngPtr(img, size);
                   1112:         failed = "gdImagePngPtr() failed";
                   1113:         break;
                   1114: 
                   1115:     default:
                   1116:         failed = "unknown image type";
                   1117:         break;
                   1118:     }
                   1119: 
                   1120:     if (out == NULL) {
                   1121:         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, failed);
                   1122:     }
                   1123: 
                   1124:     return out;
                   1125: }
                   1126: 
                   1127: 
                   1128: static void
                   1129: ngx_http_image_cleanup(void *data)
                   1130: {
                   1131:     gdFree(data);
                   1132: }
                   1133: 
                   1134: 
                   1135: static ngx_uint_t
                   1136: ngx_http_image_filter_get_value(ngx_http_request_t *r,
                   1137:     ngx_http_complex_value_t *cv, ngx_uint_t v)
                   1138: {
                   1139:     ngx_str_t  val;
                   1140: 
                   1141:     if (cv == NULL) {
                   1142:         return v;
                   1143:     }
                   1144: 
                   1145:     if (ngx_http_complex_value(r, cv, &val) != NGX_OK) {
                   1146:         return 0;
                   1147:     }
                   1148: 
                   1149:     return ngx_http_image_filter_value(&val);
                   1150: }
                   1151: 
                   1152: 
                   1153: static ngx_uint_t
                   1154: ngx_http_image_filter_value(ngx_str_t *value)
                   1155: {
                   1156:     ngx_int_t  n;
                   1157: 
                   1158:     if (value->len == 1 && value->data[0] == '-') {
                   1159:         return (ngx_uint_t) -1;
                   1160:     }
                   1161: 
                   1162:     n = ngx_atoi(value->data, value->len);
                   1163: 
                   1164:     if (n > 0) {
                   1165:         return (ngx_uint_t) n;
                   1166:     }
                   1167: 
                   1168:     return 0;
                   1169: }
                   1170: 
                   1171: 
                   1172: static void *
                   1173: ngx_http_image_filter_create_conf(ngx_conf_t *cf)
                   1174: {
                   1175:     ngx_http_image_filter_conf_t  *conf;
                   1176: 
                   1177:     conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_image_filter_conf_t));
                   1178:     if (conf == NULL) {
                   1179:         return NULL;
                   1180:     }
                   1181: 
                   1182:     /*
                   1183:      * set by ngx_pcalloc():
                   1184:      *
                   1185:      *     conf->width = 0;
                   1186:      *     conf->height = 0;
                   1187:      *     conf->angle = 0;
                   1188:      *     conf->wcv = NULL;
                   1189:      *     conf->hcv = NULL;
                   1190:      *     conf->acv = NULL;
                   1191:      *     conf->jqcv = NULL;
                   1192:      *     conf->shcv = NULL;
                   1193:      */
                   1194: 
                   1195:     conf->filter = NGX_CONF_UNSET_UINT;
                   1196:     conf->jpeg_quality = NGX_CONF_UNSET_UINT;
                   1197:     conf->sharpen = NGX_CONF_UNSET_UINT;
                   1198:     conf->transparency = NGX_CONF_UNSET;
                   1199:     conf->interlace = NGX_CONF_UNSET;
                   1200:     conf->buffer_size = NGX_CONF_UNSET_SIZE;
                   1201: 
                   1202:     return conf;
                   1203: }
                   1204: 
                   1205: 
                   1206: static char *
                   1207: ngx_http_image_filter_merge_conf(ngx_conf_t *cf, void *parent, void *child)
                   1208: {
                   1209:     ngx_http_image_filter_conf_t *prev = parent;
                   1210:     ngx_http_image_filter_conf_t *conf = child;
                   1211: 
                   1212:     if (conf->filter == NGX_CONF_UNSET_UINT) {
                   1213: 
                   1214:         if (prev->filter == NGX_CONF_UNSET_UINT) {
                   1215:             conf->filter = NGX_HTTP_IMAGE_OFF;
                   1216: 
                   1217:         } else {
                   1218:             conf->filter = prev->filter;
                   1219:             conf->width = prev->width;
                   1220:             conf->height = prev->height;
                   1221:             conf->angle = prev->angle;
                   1222:             conf->wcv = prev->wcv;
                   1223:             conf->hcv = prev->hcv;
                   1224:             conf->acv = prev->acv;
                   1225:         }
                   1226:     }
                   1227: 
                   1228:     if (conf->jpeg_quality == NGX_CONF_UNSET_UINT) {
                   1229: 
                   1230:         /* 75 is libjpeg default quality */
                   1231:         ngx_conf_merge_uint_value(conf->jpeg_quality, prev->jpeg_quality, 75);
                   1232: 
                   1233:         if (conf->jqcv == NULL) {
                   1234:             conf->jqcv = prev->jqcv;
                   1235:         }
                   1236:     }
                   1237: 
                   1238:     if (conf->sharpen == NGX_CONF_UNSET_UINT) {
                   1239:         ngx_conf_merge_uint_value(conf->sharpen, prev->sharpen, 0);
                   1240: 
                   1241:         if (conf->shcv == NULL) {
                   1242:             conf->shcv = prev->shcv;
                   1243:         }
                   1244:     }
                   1245: 
                   1246:     ngx_conf_merge_value(conf->transparency, prev->transparency, 1);
                   1247: 
                   1248:     ngx_conf_merge_value(conf->interlace, prev->interlace, 0);
                   1249: 
                   1250:     ngx_conf_merge_size_value(conf->buffer_size, prev->buffer_size,
                   1251:                               1 * 1024 * 1024);
                   1252: 
                   1253:     return NGX_CONF_OK;
                   1254: }
                   1255: 
                   1256: 
                   1257: static char *
                   1258: ngx_http_image_filter(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
                   1259: {
                   1260:     ngx_http_image_filter_conf_t *imcf = conf;
                   1261: 
                   1262:     ngx_str_t                         *value;
                   1263:     ngx_int_t                          n;
                   1264:     ngx_uint_t                         i;
                   1265:     ngx_http_complex_value_t           cv;
                   1266:     ngx_http_compile_complex_value_t   ccv;
                   1267: 
                   1268:     value = cf->args->elts;
                   1269: 
                   1270:     i = 1;
                   1271: 
                   1272:     if (cf->args->nelts == 2) {
                   1273:         if (ngx_strcmp(value[i].data, "off") == 0) {
                   1274:             imcf->filter = NGX_HTTP_IMAGE_OFF;
                   1275: 
                   1276:         } else if (ngx_strcmp(value[i].data, "test") == 0) {
                   1277:             imcf->filter = NGX_HTTP_IMAGE_TEST;
                   1278: 
                   1279:         } else if (ngx_strcmp(value[i].data, "size") == 0) {
                   1280:             imcf->filter = NGX_HTTP_IMAGE_SIZE;
                   1281: 
                   1282:         } else {
                   1283:             goto failed;
                   1284:         }
                   1285: 
                   1286:         return NGX_CONF_OK;
                   1287: 
                   1288:     } else if (cf->args->nelts == 3) {
                   1289: 
                   1290:         if (ngx_strcmp(value[i].data, "rotate") == 0) {
                   1291:             if (imcf->filter != NGX_HTTP_IMAGE_RESIZE
                   1292:                 && imcf->filter != NGX_HTTP_IMAGE_CROP)
                   1293:             {
                   1294:                 imcf->filter = NGX_HTTP_IMAGE_ROTATE;
                   1295:             }
                   1296: 
                   1297:             ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
                   1298: 
                   1299:             ccv.cf = cf;
                   1300:             ccv.value = &value[++i];
                   1301:             ccv.complex_value = &cv;
                   1302: 
                   1303:             if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
                   1304:                 return NGX_CONF_ERROR;
                   1305:             }
                   1306: 
                   1307:             if (cv.lengths == NULL) {
                   1308:                 n = ngx_http_image_filter_value(&value[i]);
                   1309: 
                   1310:                 if (n != 90 && n != 180 && n != 270) {
                   1311:                     goto failed;
                   1312:                 }
                   1313: 
                   1314:                 imcf->angle = (ngx_uint_t) n;
                   1315: 
                   1316:             } else {
                   1317:                 imcf->acv = ngx_palloc(cf->pool,
                   1318:                                        sizeof(ngx_http_complex_value_t));
                   1319:                 if (imcf->acv == NULL) {
                   1320:                     return NGX_CONF_ERROR;
                   1321:                 }
                   1322: 
                   1323:                 *imcf->acv = cv;
                   1324:             }
                   1325: 
                   1326:             return NGX_CONF_OK;
                   1327: 
                   1328:         } else {
                   1329:             goto failed;
                   1330:         }
                   1331:     }
                   1332: 
                   1333:     if (ngx_strcmp(value[i].data, "resize") == 0) {
                   1334:         imcf->filter = NGX_HTTP_IMAGE_RESIZE;
                   1335: 
                   1336:     } else if (ngx_strcmp(value[i].data, "crop") == 0) {
                   1337:         imcf->filter = NGX_HTTP_IMAGE_CROP;
                   1338: 
                   1339:     } else {
                   1340:         goto failed;
                   1341:     }
                   1342: 
                   1343:     ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
                   1344: 
                   1345:     ccv.cf = cf;
                   1346:     ccv.value = &value[++i];
                   1347:     ccv.complex_value = &cv;
                   1348: 
                   1349:     if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
                   1350:         return NGX_CONF_ERROR;
                   1351:     }
                   1352: 
                   1353:     if (cv.lengths == NULL) {
                   1354:         n = ngx_http_image_filter_value(&value[i]);
                   1355: 
                   1356:         if (n == 0) {
                   1357:             goto failed;
                   1358:         }
                   1359: 
                   1360:         imcf->width = (ngx_uint_t) n;
                   1361: 
                   1362:     } else {
                   1363:         imcf->wcv = ngx_palloc(cf->pool, sizeof(ngx_http_complex_value_t));
                   1364:         if (imcf->wcv == NULL) {
                   1365:             return NGX_CONF_ERROR;
                   1366:         }
                   1367: 
                   1368:         *imcf->wcv = cv;
                   1369:     }
                   1370: 
                   1371:     ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
                   1372: 
                   1373:     ccv.cf = cf;
                   1374:     ccv.value = &value[++i];
                   1375:     ccv.complex_value = &cv;
                   1376: 
                   1377:     if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
                   1378:         return NGX_CONF_ERROR;
                   1379:     }
                   1380: 
                   1381:     if (cv.lengths == NULL) {
                   1382:         n = ngx_http_image_filter_value(&value[i]);
                   1383: 
                   1384:         if (n == 0) {
                   1385:             goto failed;
                   1386:         }
                   1387: 
                   1388:         imcf->height = (ngx_uint_t) n;
                   1389: 
                   1390:     } else {
                   1391:         imcf->hcv = ngx_palloc(cf->pool, sizeof(ngx_http_complex_value_t));
                   1392:         if (imcf->hcv == NULL) {
                   1393:             return NGX_CONF_ERROR;
                   1394:         }
                   1395: 
                   1396:         *imcf->hcv = cv;
                   1397:     }
                   1398: 
                   1399:     return NGX_CONF_OK;
                   1400: 
                   1401: failed:
                   1402: 
                   1403:     ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid parameter \"%V\"",
                   1404:                        &value[i]);
                   1405: 
                   1406:     return NGX_CONF_ERROR;
                   1407: }
                   1408: 
                   1409: 
                   1410: static char *
                   1411: ngx_http_image_filter_jpeg_quality(ngx_conf_t *cf, ngx_command_t *cmd,
                   1412:     void *conf)
                   1413: {
                   1414:     ngx_http_image_filter_conf_t *imcf = conf;
                   1415: 
                   1416:     ngx_str_t                         *value;
                   1417:     ngx_int_t                          n;
                   1418:     ngx_http_complex_value_t           cv;
                   1419:     ngx_http_compile_complex_value_t   ccv;
                   1420: 
                   1421:     value = cf->args->elts;
                   1422: 
                   1423:     ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
                   1424: 
                   1425:     ccv.cf = cf;
                   1426:     ccv.value = &value[1];
                   1427:     ccv.complex_value = &cv;
                   1428: 
                   1429:     if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
                   1430:         return NGX_CONF_ERROR;
                   1431:     }
                   1432: 
                   1433:     if (cv.lengths == NULL) {
                   1434:         n = ngx_http_image_filter_value(&value[1]);
                   1435: 
                   1436:         if (n <= 0) {
                   1437:             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                   1438:                                "invalid value \"%V\"", &value[1]);
                   1439:             return NGX_CONF_ERROR;
                   1440:         }
                   1441: 
                   1442:         imcf->jpeg_quality = (ngx_uint_t) n;
                   1443: 
                   1444:     } else {
                   1445:         imcf->jqcv = ngx_palloc(cf->pool, sizeof(ngx_http_complex_value_t));
                   1446:         if (imcf->jqcv == NULL) {
                   1447:             return NGX_CONF_ERROR;
                   1448:         }
                   1449: 
                   1450:         *imcf->jqcv = cv;
                   1451:     }
                   1452: 
                   1453:     return NGX_CONF_OK;
                   1454: }
                   1455: 
                   1456: 
                   1457: static char *
                   1458: ngx_http_image_filter_sharpen(ngx_conf_t *cf, ngx_command_t *cmd,
                   1459:     void *conf)
                   1460: {
                   1461:     ngx_http_image_filter_conf_t *imcf = conf;
                   1462: 
                   1463:     ngx_str_t                         *value;
                   1464:     ngx_int_t                          n;
                   1465:     ngx_http_complex_value_t           cv;
                   1466:     ngx_http_compile_complex_value_t   ccv;
                   1467: 
                   1468:     value = cf->args->elts;
                   1469: 
                   1470:     ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
                   1471: 
                   1472:     ccv.cf = cf;
                   1473:     ccv.value = &value[1];
                   1474:     ccv.complex_value = &cv;
                   1475: 
                   1476:     if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
                   1477:         return NGX_CONF_ERROR;
                   1478:     }
                   1479: 
                   1480:     if (cv.lengths == NULL) {
                   1481:         n = ngx_http_image_filter_value(&value[1]);
                   1482: 
                   1483:         if (n < 0) {
                   1484:             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                   1485:                                "invalid value \"%V\"", &value[1]);
                   1486:             return NGX_CONF_ERROR;
                   1487:         }
                   1488: 
                   1489:         imcf->sharpen = (ngx_uint_t) n;
                   1490: 
                   1491:     } else {
                   1492:         imcf->shcv = ngx_palloc(cf->pool, sizeof(ngx_http_complex_value_t));
                   1493:         if (imcf->shcv == NULL) {
                   1494:             return NGX_CONF_ERROR;
                   1495:         }
                   1496: 
                   1497:         *imcf->shcv = cv;
                   1498:     }
                   1499: 
                   1500:     return NGX_CONF_OK;
                   1501: }
                   1502: 
                   1503: 
                   1504: static ngx_int_t
                   1505: ngx_http_image_filter_init(ngx_conf_t *cf)
                   1506: {
                   1507:     ngx_http_next_header_filter = ngx_http_top_header_filter;
                   1508:     ngx_http_top_header_filter = ngx_http_image_header_filter;
                   1509: 
                   1510:     ngx_http_next_body_filter = ngx_http_top_body_filter;
                   1511:     ngx_http_top_body_filter = ngx_http_image_body_filter;
                   1512: 
                   1513:     return NGX_OK;
                   1514: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>