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

Merge with current.
author Maxim Dounin <mdounin@mdounin.ru>
date Thu, 11 Mar 2010 21:27:17 +0300
parents 2da4537168f8
children 8246d8a2c2be
comparison
equal deleted inserted replaced
539:5f4de8cf0d9d 578:f3a9e57d2e17
38 ngx_uint_t filter; 38 ngx_uint_t filter;
39 ngx_uint_t width; 39 ngx_uint_t width;
40 ngx_uint_t height; 40 ngx_uint_t height;
41 ngx_int_t jpeg_quality; 41 ngx_int_t jpeg_quality;
42 42
43 ngx_flag_t transparency;
44
43 ngx_http_complex_value_t *wcv; 45 ngx_http_complex_value_t *wcv;
44 ngx_http_complex_value_t *hcv; 46 ngx_http_complex_value_t *hcv;
45 47
46 size_t buffer_size; 48 size_t buffer_size;
47 } ngx_http_image_filter_conf_t; 49 } ngx_http_image_filter_conf_t;
59 ngx_uint_t max_width; 61 ngx_uint_t max_width;
60 ngx_uint_t max_height; 62 ngx_uint_t max_height;
61 63
62 ngx_uint_t phase; 64 ngx_uint_t phase;
63 ngx_uint_t type; 65 ngx_uint_t type;
66 ngx_uint_t force;
64 } ngx_http_image_filter_ctx_t; 67 } ngx_http_image_filter_ctx_t;
65 68
66 69
67 static ngx_int_t ngx_http_image_send(ngx_http_request_t *r, 70 static ngx_int_t ngx_http_image_send(ngx_http_request_t *r,
68 ngx_http_image_filter_ctx_t *ctx, ngx_chain_t *in); 71 ngx_http_image_filter_ctx_t *ctx, ngx_chain_t *in);
111 { ngx_string("image_filter_jpeg_quality"), 114 { ngx_string("image_filter_jpeg_quality"),
112 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, 115 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
113 ngx_conf_set_num_slot, 116 ngx_conf_set_num_slot,
114 NGX_HTTP_LOC_CONF_OFFSET, 117 NGX_HTTP_LOC_CONF_OFFSET,
115 offsetof(ngx_http_image_filter_conf_t, jpeg_quality), 118 offsetof(ngx_http_image_filter_conf_t, jpeg_quality),
119 NULL },
120
121 { ngx_string("image_filter_transparency"),
122 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
123 ngx_conf_set_flag_slot,
124 NGX_HTTP_LOC_CONF_OFFSET,
125 offsetof(ngx_http_image_filter_conf_t, transparency),
116 NULL }, 126 NULL },
117 127
118 { ngx_string("image_filter_buffer"), 128 { ngx_string("image_filter_buffer"),
119 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, 129 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
120 ngx_conf_set_size_slot, 130 ngx_conf_set_size_slot,
490 return NULL; 500 return NULL;
491 } 501 }
492 502
493 if (rc == NGX_OK 503 if (rc == NGX_OK
494 && ctx->width <= ctx->max_width 504 && ctx->width <= ctx->max_width
495 && ctx->height <= ctx->max_height) 505 && ctx->height <= ctx->max_height
506 && !ctx->force)
496 { 507 {
497 return ngx_http_image_asis(r, ctx); 508 return ngx_http_image_asis(r, ctx);
498 } 509 }
499 510
500 return ngx_http_image_resize(r, ctx); 511 return ngx_http_image_resize(r, ctx);
590 601
591 static ngx_int_t 602 static ngx_int_t
592 ngx_http_image_size(ngx_http_request_t *r, ngx_http_image_filter_ctx_t *ctx) 603 ngx_http_image_size(ngx_http_request_t *r, ngx_http_image_filter_ctx_t *ctx)
593 { 604 {
594 u_char *p, *last; 605 u_char *p, *last;
606 size_t len, app;
595 ngx_uint_t width, height; 607 ngx_uint_t width, height;
596 608
597 p = ctx->image; 609 p = ctx->image;
598 610
599 switch (ctx->type) { 611 switch (ctx->type) {
600 612
601 case NGX_HTTP_IMAGE_JPEG: 613 case NGX_HTTP_IMAGE_JPEG:
602 614
603 p += 2; 615 p += 2;
604 last = ctx->image + ctx->length - 10; 616 last = ctx->image + ctx->length - 10;
617 width = 0;
618 height = 0;
619 app = 0;
605 620
606 while (p < last) { 621 while (p < last) {
607 622
608 if (p[0] == 0xff && p[1] != 0xff) { 623 if (p[0] == 0xff && p[1] != 0xff) {
609 624
610 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 625 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
611 "JPEG: %02xd %02xd", *p, *(p + 1)); 626 "JPEG: %02xd %02xd", p[0], p[1]);
612 627
613 p++; 628 p++;
614 629
615 if (*p == 0xc0 || *p == 0xc1 || *p == 0xc2 || *p == 0xc3 630 if ((*p == 0xc0 || *p == 0xc1 || *p == 0xc2 || *p == 0xc3
616 || *p == 0xc9 || *p == 0xca || *p == 0xcb) 631 || *p == 0xc9 || *p == 0xca || *p == 0xcb)
632 && (width == 0 || height == 0))
617 { 633 {
618 goto found; 634 width = p[6] * 256 + p[7];
635 height = p[4] * 256 + p[5];
619 } 636 }
620 637
621 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 638 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
622 "JPEG: %02xd %02xd", p[1], p[2]); 639 "JPEG: %02xd %02xd", p[1], p[2]);
623 640
624 p += p[1] * 256 + p[2]; 641 len = p[1] * 256 + p[2];
642
643 if (*p >= 0xe1 && *p <= 0xef) {
644 /* application data, e.g., EXIF, Adobe XMP, etc. */
645 app += len;
646 }
647
648 p += len;
625 649
626 continue; 650 continue;
627 } 651 }
628 652
629 p++; 653 p++;
630 } 654 }
631 655
632 return NGX_DECLINED; 656 if (width == 0 || height == 0) {
633 657 return NGX_DECLINED;
634 found: 658 }
635 659
636 width = p[6] * 256 + p[7]; 660 if (ctx->length / 20 < app) {
637 height = p[4] * 256 + p[5]; 661 /* force conversion if application data consume more than 5% */
662 ctx->force = 1;
663 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
664 "app data size: %uz", app);
665 }
638 666
639 break; 667 break;
640 668
641 case NGX_HTTP_IMAGE_GIF: 669 case NGX_HTTP_IMAGE_GIF:
642 670
676 704
677 705
678 static ngx_buf_t * 706 static ngx_buf_t *
679 ngx_http_image_resize(ngx_http_request_t *r, ngx_http_image_filter_ctx_t *ctx) 707 ngx_http_image_resize(ngx_http_request_t *r, ngx_http_image_filter_ctx_t *ctx)
680 { 708 {
681 int sx, sy, dx, dy, ox, oy, 709 int sx, sy, dx, dy, ox, oy, size,
682 colors, transparent, red, green, blue, size; 710 colors, palette, transparent,
711 red, green, blue;
683 u_char *out; 712 u_char *out;
684 ngx_buf_t *b; 713 ngx_buf_t *b;
685 ngx_uint_t resize; 714 ngx_uint_t resize;
686 gdImagePtr src, dst; 715 gdImagePtr src, dst;
687 ngx_pool_cleanup_t *cln; 716 ngx_pool_cleanup_t *cln;
696 sx = gdImageSX(src); 725 sx = gdImageSX(src);
697 sy = gdImageSY(src); 726 sy = gdImageSY(src);
698 727
699 conf = ngx_http_get_module_loc_conf(r, ngx_http_image_filter_module); 728 conf = ngx_http_get_module_loc_conf(r, ngx_http_image_filter_module);
700 729
701 if ((ngx_uint_t) sx <= ctx->max_width 730 if (!ctx->force
731 && (ngx_uint_t) sx <= ctx->max_width
702 && (ngx_uint_t) sy <= ctx->max_height) 732 && (ngx_uint_t) sy <= ctx->max_height)
703 { 733 {
704 gdImageDestroy(src); 734 gdImageDestroy(src);
705 return ngx_http_image_asis(r, ctx); 735 return ngx_http_image_asis(r, ctx);
706 } 736 }
707 737
708 colors = gdImageColorsTotal(src); 738 colors = gdImageColorsTotal(src);
709 transparent = gdImageGetTransparent(src); 739
710 740 if (colors && conf->transparency) {
711 if (transparent != -1 && colors) { 741 transparent = gdImageGetTransparent(src);
712 red = gdImageRed(src, transparent); 742
713 green = gdImageGreen(src, transparent); 743 if (transparent != -1) {
714 blue = gdImageBlue(src, transparent); 744 palette = colors;
715 gdImageColorTransparent(src, -1); 745 red = gdImageRed(src, transparent);
716 746 green = gdImageGreen(src, transparent);
717 } else { 747 blue = gdImageBlue(src, transparent);
718 red = 0; green = 0; blue = 0; 748
719 } 749 goto transparent;
750 }
751 }
752
753 palette = 0;
754 transparent = -1;
755 red = 0;
756 green = 0;
757 blue = 0;
758
759 transparent:
760
761 gdImageColorTransparent(src, -1);
720 762
721 dx = sx; 763 dx = sx;
722 dy = sy; 764 dy = sy;
723 765
724 if (conf->filter == NGX_HTTP_IMAGE_RESIZE) { 766 if (conf->filter == NGX_HTTP_IMAGE_RESIZE) {
760 } 802 }
761 } 803 }
762 } 804 }
763 805
764 if (resize) { 806 if (resize) {
765 dst = ngx_http_image_new(r, dx, dy, colors); 807 dst = ngx_http_image_new(r, dx, dy, palette);
766 if (dst == NULL) { 808 if (dst == NULL) {
767 gdImageDestroy(src); 809 gdImageDestroy(src);
768 return NULL; 810 return NULL;
769 } 811 }
770 812
813 if (colors == 0) {
814 gdImageSaveAlpha(dst, 1);
815 gdImageAlphaBlending(dst, 0);
816 }
817
771 gdImageCopyResampled(dst, src, 0, 0, 0, 0, dx, dy, sx, sy); 818 gdImageCopyResampled(dst, src, 0, 0, 0, 0, dx, dy, sx, sy);
819
820 if (colors) {
821 gdImageTrueColorToPalette(dst, 1, 256);
822 }
772 823
773 gdImageDestroy(src); 824 gdImageDestroy(src);
774 825
775 } else { 826 } else {
776 dst = src; 827 dst = src;
808 859
809 ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 860 ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
810 "image crop: %d x %d @ %d x %d", 861 "image crop: %d x %d @ %d x %d",
811 dx, dy, ox, oy); 862 dx, dy, ox, oy);
812 863
864 if (colors == 0) {
865 gdImageSaveAlpha(dst, 1);
866 gdImageAlphaBlending(dst, 0);
867 }
868
813 gdImageCopy(dst, src, 0, 0, ox, oy, dx - ox, dy - oy); 869 gdImageCopy(dst, src, 0, 0, ox, oy, dx - ox, dy - oy);
870
871 if (colors) {
872 gdImageTrueColorToPalette(dst, 1, 256);
873 }
814 874
815 gdImageDestroy(src); 875 gdImageDestroy(src);
816 } 876 }
817 } 877 }
818 878
1019 return NULL; 1079 return NULL;
1020 } 1080 }
1021 1081
1022 conf->filter = NGX_CONF_UNSET_UINT; 1082 conf->filter = NGX_CONF_UNSET_UINT;
1023 conf->jpeg_quality = NGX_CONF_UNSET; 1083 conf->jpeg_quality = NGX_CONF_UNSET;
1084 conf->transparency = NGX_CONF_UNSET;
1024 conf->buffer_size = NGX_CONF_UNSET_SIZE; 1085 conf->buffer_size = NGX_CONF_UNSET_SIZE;
1025 1086
1026 return conf; 1087 return conf;
1027 } 1088 }
1028 1089
1048 } 1109 }
1049 1110
1050 /* 75 is libjpeg default quality */ 1111 /* 75 is libjpeg default quality */
1051 ngx_conf_merge_value(conf->jpeg_quality, prev->jpeg_quality, 75); 1112 ngx_conf_merge_value(conf->jpeg_quality, prev->jpeg_quality, 75);
1052 1113
1114 ngx_conf_merge_value(conf->transparency, prev->transparency, 1);
1115
1053 ngx_conf_merge_size_value(conf->buffer_size, prev->buffer_size, 1116 ngx_conf_merge_size_value(conf->buffer_size, prev->buffer_size,
1054 1 * 1024 * 1024); 1117 1 * 1024 * 1024);
1055 1118
1056 return NGX_CONF_OK; 1119 return NGX_CONF_OK;
1057 } 1120 }