~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Linux Cross Reference
Nginx/http/modules/ngx_http_image_filter_module.c

Version: ~ [ nginx-0.8.20 ] ~ [ nginx-0.7.62 ] ~ [ nginx-0.6.39 ] ~

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

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

This page was automatically generated by the LXR engine.
Visit the LXR main site for more information.