comparison src/http/modules/ngx_http_image_filter_module.c @ 635:e67b227c8dbb default tip

Merge with current.
author Maxim Dounin <mdounin@mdounin.ru>
date Mon, 25 Apr 2011 04:07:55 +0400
parents b4dcae568a2a
children
comparison
equal deleted inserted replaced
578:f3a9e57d2e17 635:e67b227c8dbb
14 #define NGX_HTTP_IMAGE_OFF 0 14 #define NGX_HTTP_IMAGE_OFF 0
15 #define NGX_HTTP_IMAGE_TEST 1 15 #define NGX_HTTP_IMAGE_TEST 1
16 #define NGX_HTTP_IMAGE_SIZE 2 16 #define NGX_HTTP_IMAGE_SIZE 2
17 #define NGX_HTTP_IMAGE_RESIZE 3 17 #define NGX_HTTP_IMAGE_RESIZE 3
18 #define NGX_HTTP_IMAGE_CROP 4 18 #define NGX_HTTP_IMAGE_CROP 4
19 #define NGX_HTTP_IMAGE_ROTATE 5
19 20
20 21
21 #define NGX_HTTP_IMAGE_START 0 22 #define NGX_HTTP_IMAGE_START 0
22 #define NGX_HTTP_IMAGE_READ 1 23 #define NGX_HTTP_IMAGE_READ 1
23 #define NGX_HTTP_IMAGE_PROCESS 2 24 #define NGX_HTTP_IMAGE_PROCESS 2
36 37
37 typedef struct { 38 typedef struct {
38 ngx_uint_t filter; 39 ngx_uint_t filter;
39 ngx_uint_t width; 40 ngx_uint_t width;
40 ngx_uint_t height; 41 ngx_uint_t height;
41 ngx_int_t jpeg_quality; 42 ngx_uint_t angle;
43 ngx_uint_t jpeg_quality;
42 44
43 ngx_flag_t transparency; 45 ngx_flag_t transparency;
44 46
45 ngx_http_complex_value_t *wcv; 47 ngx_http_complex_value_t *wcv;
46 ngx_http_complex_value_t *hcv; 48 ngx_http_complex_value_t *hcv;
49 ngx_http_complex_value_t *acv;
50 ngx_http_complex_value_t *jqcv;
47 51
48 size_t buffer_size; 52 size_t buffer_size;
49 } ngx_http_image_filter_conf_t; 53 } ngx_http_image_filter_conf_t;
50 54
51 55
55 59
56 size_t length; 60 size_t length;
57 61
58 ngx_uint_t width; 62 ngx_uint_t width;
59 ngx_uint_t height; 63 ngx_uint_t height;
60
61 ngx_uint_t max_width; 64 ngx_uint_t max_width;
62 ngx_uint_t max_height; 65 ngx_uint_t max_height;
66 ngx_uint_t angle;
63 67
64 ngx_uint_t phase; 68 ngx_uint_t phase;
65 ngx_uint_t type; 69 ngx_uint_t type;
66 ngx_uint_t force; 70 ngx_uint_t force;
67 } ngx_http_image_filter_ctx_t; 71 } ngx_http_image_filter_ctx_t;
97 static void *ngx_http_image_filter_create_conf(ngx_conf_t *cf); 101 static void *ngx_http_image_filter_create_conf(ngx_conf_t *cf);
98 static char *ngx_http_image_filter_merge_conf(ngx_conf_t *cf, void *parent, 102 static char *ngx_http_image_filter_merge_conf(ngx_conf_t *cf, void *parent,
99 void *child); 103 void *child);
100 static char *ngx_http_image_filter(ngx_conf_t *cf, ngx_command_t *cmd, 104 static char *ngx_http_image_filter(ngx_conf_t *cf, ngx_command_t *cmd,
101 void *conf); 105 void *conf);
106 static char *ngx_http_image_filter_jpeg_quality(ngx_conf_t *cf,
107 ngx_command_t *cmd, void *conf);
102 static ngx_int_t ngx_http_image_filter_init(ngx_conf_t *cf); 108 static ngx_int_t ngx_http_image_filter_init(ngx_conf_t *cf);
103 109
104 110
105 static ngx_command_t ngx_http_image_filter_commands[] = { 111 static ngx_command_t ngx_http_image_filter_commands[] = {
106 112
107 { ngx_string("image_filter"), 113 { ngx_string("image_filter"),
108 NGX_HTTP_LOC_CONF|NGX_CONF_TAKE13, 114 NGX_HTTP_LOC_CONF|NGX_CONF_TAKE13|NGX_CONF_TAKE2,
109 ngx_http_image_filter, 115 ngx_http_image_filter,
110 NGX_HTTP_LOC_CONF_OFFSET, 116 NGX_HTTP_LOC_CONF_OFFSET,
111 0, 117 0,
112 NULL }, 118 NULL },
113 119
114 { ngx_string("image_filter_jpeg_quality"), 120 { ngx_string("image_filter_jpeg_quality"),
115 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, 121 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
116 ngx_conf_set_num_slot, 122 ngx_http_image_filter_jpeg_quality,
117 NGX_HTTP_LOC_CONF_OFFSET, 123 NGX_HTTP_LOC_CONF_OFFSET,
118 offsetof(ngx_http_image_filter_conf_t, jpeg_quality), 124 0,
119 NULL }, 125 NULL },
120 126
121 { ngx_string("image_filter_transparency"), 127 { ngx_string("image_filter_transparency"),
122 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, 128 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
123 ngx_conf_set_flag_slot, 129 ngx_conf_set_flag_slot,
226 232
227 if (len != -1 && len > (off_t) conf->buffer_size) { 233 if (len != -1 && len > (off_t) conf->buffer_size) {
228 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, 234 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
229 "image filter: too big response: %O", len); 235 "image filter: too big response: %O", len);
230 236
231 return NGX_ERROR; 237 return NGX_HTTP_UNSUPPORTED_MEDIA_TYPE;
232 } 238 }
233 239
234 if (len == -1) { 240 if (len == -1) {
235 ctx->length = conf->buffer_size; 241 ctx->length = conf->buffer_size;
236 242
487 493
488 if (conf->filter == NGX_HTTP_IMAGE_SIZE) { 494 if (conf->filter == NGX_HTTP_IMAGE_SIZE) {
489 return ngx_http_image_json(r, rc == NGX_OK ? ctx : NULL); 495 return ngx_http_image_json(r, rc == NGX_OK ? ctx : NULL);
490 } 496 }
491 497
498 ctx->angle = ngx_http_image_filter_get_value(r, conf->acv, conf->angle);
499
500 if (conf->filter == NGX_HTTP_IMAGE_ROTATE) {
501
502 if (ctx->angle != 90 && ctx->angle != 180 && ctx->angle != 270) {
503 return NULL;
504 }
505
506 return ngx_http_image_resize(r, ctx);
507 }
508
492 ctx->max_width = ngx_http_image_filter_get_value(r, conf->wcv, conf->width); 509 ctx->max_width = ngx_http_image_filter_get_value(r, conf->wcv, conf->width);
493 if (ctx->max_width == 0) { 510 if (ctx->max_width == 0) {
494 return NULL; 511 return NULL;
495 } 512 }
496 513
501 } 518 }
502 519
503 if (rc == NGX_OK 520 if (rc == NGX_OK
504 && ctx->width <= ctx->max_width 521 && ctx->width <= ctx->max_width
505 && ctx->height <= ctx->max_height 522 && ctx->height <= ctx->max_height
523 && ctx->angle == 0
506 && !ctx->force) 524 && !ctx->force)
507 { 525 {
508 return ngx_http_image_asis(r, ctx); 526 return ngx_http_image_asis(r, ctx);
509 } 527 }
510 528
527 b->last_buf = 1; 545 b->last_buf = 1;
528 546
529 ngx_http_clean_header(r); 547 ngx_http_clean_header(r);
530 548
531 r->headers_out.status = NGX_HTTP_OK; 549 r->headers_out.status = NGX_HTTP_OK;
532 r->headers_out.content_type.len = sizeof("text/plain") - 1; 550 ngx_str_set(&r->headers_out.content_type, "text/plain");
533 r->headers_out.content_type.data = (u_char *) "text/plain";
534 r->headers_out.content_type_lowcase = NULL; 551 r->headers_out.content_type_lowcase = NULL;
535 552
536 if (ctx == NULL) { 553 if (ctx == NULL) {
537 b->pos = (u_char *) "{}" CRLF; 554 b->pos = (u_char *) "{}" CRLF;
538 b->last = b->pos + sizeof("{}" CRLF) - 1; 555 b->last = b->pos + sizeof("{}" CRLF) - 1;
706 static ngx_buf_t * 723 static ngx_buf_t *
707 ngx_http_image_resize(ngx_http_request_t *r, ngx_http_image_filter_ctx_t *ctx) 724 ngx_http_image_resize(ngx_http_request_t *r, ngx_http_image_filter_ctx_t *ctx)
708 { 725 {
709 int sx, sy, dx, dy, ox, oy, size, 726 int sx, sy, dx, dy, ox, oy, size,
710 colors, palette, transparent, 727 colors, palette, transparent,
711 red, green, blue; 728 red, green, blue, t;
712 u_char *out; 729 u_char *out;
713 ngx_buf_t *b; 730 ngx_buf_t *b;
714 ngx_uint_t resize; 731 ngx_uint_t resize;
715 gdImagePtr src, dst; 732 gdImagePtr src, dst;
716 ngx_pool_cleanup_t *cln; 733 ngx_pool_cleanup_t *cln;
726 sy = gdImageSY(src); 743 sy = gdImageSY(src);
727 744
728 conf = ngx_http_get_module_loc_conf(r, ngx_http_image_filter_module); 745 conf = ngx_http_get_module_loc_conf(r, ngx_http_image_filter_module);
729 746
730 if (!ctx->force 747 if (!ctx->force
748 && ctx->angle == 0
731 && (ngx_uint_t) sx <= ctx->max_width 749 && (ngx_uint_t) sx <= ctx->max_width
732 && (ngx_uint_t) sy <= ctx->max_height) 750 && (ngx_uint_t) sy <= ctx->max_height)
733 { 751 {
734 gdImageDestroy(src); 752 gdImageDestroy(src);
735 return ngx_http_image_asis(r, ctx); 753 return ngx_http_image_asis(r, ctx);
776 dx = dx ? dx : 1; 794 dx = dx ? dx : 1;
777 dy = ctx->max_height; 795 dy = ctx->max_height;
778 } 796 }
779 797
780 resize = 1; 798 resize = 1;
799
800 } else if (conf->filter == NGX_HTTP_IMAGE_ROTATE) {
801
802 resize = 0;
781 803
782 } else { /* NGX_HTTP_IMAGE_CROP */ 804 } else { /* NGX_HTTP_IMAGE_CROP */
783 805
784 resize = 0; 806 resize = 0;
785 807
825 847
826 } else { 848 } else {
827 dst = src; 849 dst = src;
828 } 850 }
829 851
830 if (conf->filter == NGX_HTTP_IMAGE_CROP) { 852 if (ctx->angle) {
831
832 src = dst; 853 src = dst;
833 854
834 if ((ngx_uint_t) dx > ctx->max_width) { 855 switch (ctx->angle) {
835 ox = dx - ctx->max_width; 856
836 857 case 90:
837 } else { 858 case 270:
838 ox = 0; 859 dst = ngx_http_image_new(r, dy, dx, palette);
839 }
840
841 if ((ngx_uint_t) dy > ctx->max_height) {
842 oy = dy - ctx->max_height;
843
844 } else {
845 oy = 0;
846 }
847
848 if (ox || oy) {
849
850 dst = ngx_http_image_new(r, dx - ox, dy - oy, colors);
851
852 if (dst == NULL) { 860 if (dst == NULL) {
853 gdImageDestroy(src); 861 gdImageDestroy(src);
854 return NULL; 862 return NULL;
855 } 863 }
864 gdImageCopyRotated(dst, src, dy/2, dx/2, 0, 0, dx, dy, ctx->angle);
865 gdImageDestroy(src);
866
867 t = dx;
868 dx = dy;
869 dy = t;
870 break;
871
872 case 180:
873 dst = ngx_http_image_new(r, dx, dy, palette);
874 if (dst == NULL) {
875 gdImageDestroy(src);
876 return NULL;
877 }
878 gdImageCopyRotated(dst, src, dx/2, dy/2, 0, 0, dx, dy, ctx->angle);
879 gdImageDestroy(src);
880 break;
881 }
882 }
883
884 if (conf->filter == NGX_HTTP_IMAGE_CROP) {
885
886 src = dst;
887
888 if ((ngx_uint_t) dx > ctx->max_width) {
889 ox = dx - ctx->max_width;
890
891 } else {
892 ox = 0;
893 }
894
895 if ((ngx_uint_t) dy > ctx->max_height) {
896 oy = dy - ctx->max_height;
897
898 } else {
899 oy = 0;
900 }
901
902 if (ox || oy) {
903
904 dst = ngx_http_image_new(r, dx - ox, dy - oy, colors);
905
906 if (dst == NULL) {
907 gdImageDestroy(src);
908 return NULL;
909 }
856 910
857 ox /= 2; 911 ox /= 2;
858 oy /= 2; 912 oy /= 2;
859 913
860 ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 914 ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
988 ngx_http_image_out(ngx_http_request_t *r, ngx_uint_t type, gdImagePtr img, 1042 ngx_http_image_out(ngx_http_request_t *r, ngx_uint_t type, gdImagePtr img,
989 int *size) 1043 int *size)
990 { 1044 {
991 char *failed; 1045 char *failed;
992 u_char *out; 1046 u_char *out;
1047 ngx_int_t jq;
993 ngx_http_image_filter_conf_t *conf; 1048 ngx_http_image_filter_conf_t *conf;
994 1049
995 out = NULL; 1050 out = NULL;
996 1051
997 switch (type) { 1052 switch (type) {
998 1053
999 case NGX_HTTP_IMAGE_JPEG: 1054 case NGX_HTTP_IMAGE_JPEG:
1000 conf = ngx_http_get_module_loc_conf(r, ngx_http_image_filter_module); 1055 conf = ngx_http_get_module_loc_conf(r, ngx_http_image_filter_module);
1001 out = gdImageJpegPtr(img, size, conf->jpeg_quality); 1056
1057 jq = ngx_http_image_filter_get_value(r, conf->jqcv, conf->jpeg_quality);
1058 if (jq <= 0) {
1059 return NULL;
1060 }
1061
1062 out = gdImageJpegPtr(img, size, jq);
1002 failed = "gdImageJpegPtr() failed"; 1063 failed = "gdImageJpegPtr() failed";
1003 break; 1064 break;
1004 1065
1005 case NGX_HTTP_IMAGE_GIF: 1066 case NGX_HTTP_IMAGE_GIF:
1006 out = gdImageGifPtr(img, size); 1067 out = gdImageGifPtr(img, size);
1078 if (conf == NULL) { 1139 if (conf == NULL) {
1079 return NULL; 1140 return NULL;
1080 } 1141 }
1081 1142
1082 conf->filter = NGX_CONF_UNSET_UINT; 1143 conf->filter = NGX_CONF_UNSET_UINT;
1083 conf->jpeg_quality = NGX_CONF_UNSET; 1144 conf->jpeg_quality = NGX_CONF_UNSET_UINT;
1145 conf->angle = NGX_CONF_UNSET_UINT;
1084 conf->transparency = NGX_CONF_UNSET; 1146 conf->transparency = NGX_CONF_UNSET;
1085 conf->buffer_size = NGX_CONF_UNSET_SIZE; 1147 conf->buffer_size = NGX_CONF_UNSET_SIZE;
1086 1148
1087 return conf; 1149 return conf;
1088 } 1150 }
1107 conf->hcv = prev->hcv; 1169 conf->hcv = prev->hcv;
1108 } 1170 }
1109 } 1171 }
1110 1172
1111 /* 75 is libjpeg default quality */ 1173 /* 75 is libjpeg default quality */
1112 ngx_conf_merge_value(conf->jpeg_quality, prev->jpeg_quality, 75); 1174 ngx_conf_merge_uint_value(conf->jpeg_quality, prev->jpeg_quality, 75);
1175
1176 if (conf->jqcv == NULL) {
1177 conf->jqcv = prev->jqcv;
1178 }
1179
1180 ngx_conf_merge_uint_value(conf->angle, prev->angle, 0);
1181 if (conf->acv == NULL) {
1182 conf->acv = prev->acv;
1183 }
1113 1184
1114 ngx_conf_merge_value(conf->transparency, prev->transparency, 1); 1185 ngx_conf_merge_value(conf->transparency, prev->transparency, 1);
1115 1186
1116 ngx_conf_merge_size_value(conf->buffer_size, prev->buffer_size, 1187 ngx_conf_merge_size_value(conf->buffer_size, prev->buffer_size,
1117 1 * 1024 * 1024); 1188 1 * 1024 * 1024);
1148 } else { 1219 } else {
1149 goto failed; 1220 goto failed;
1150 } 1221 }
1151 1222
1152 return NGX_CONF_OK; 1223 return NGX_CONF_OK;
1224
1225 } else if (cf->args->nelts == 3) {
1226
1227 if (ngx_strcmp(value[i].data, "rotate") == 0) {
1228 imcf->filter = NGX_HTTP_IMAGE_ROTATE;
1229
1230 ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
1231
1232 ccv.cf = cf;
1233 ccv.value = &value[++i];
1234 ccv.complex_value = &cv;
1235
1236 if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
1237 return NGX_CONF_ERROR;
1238 }
1239
1240 if (cv.lengths == NULL) {
1241 n = ngx_http_image_filter_value(&value[i]);
1242
1243 if (n != 90 && n != 180 && n != 270) {
1244 goto failed;
1245 }
1246
1247 imcf->angle = (ngx_uint_t) n;
1248
1249 } else {
1250 imcf->acv = ngx_palloc(cf->pool,
1251 sizeof(ngx_http_complex_value_t));
1252 if (imcf->acv == NULL) {
1253 return NGX_CONF_ERROR;
1254 }
1255
1256 *imcf->acv = cv;
1257 }
1258
1259 return NGX_CONF_OK;
1260
1261 } else {
1262 goto failed;
1263 }
1153 } 1264 }
1154 1265
1155 if (ngx_strcmp(value[i].data, "resize") == 0) { 1266 if (ngx_strcmp(value[i].data, "resize") == 0) {
1156 imcf->filter = NGX_HTTP_IMAGE_RESIZE; 1267 imcf->filter = NGX_HTTP_IMAGE_RESIZE;
1157 1268
1227 1338
1228 return NGX_CONF_ERROR; 1339 return NGX_CONF_ERROR;
1229 } 1340 }
1230 1341
1231 1342
1343 static char *
1344 ngx_http_image_filter_jpeg_quality(ngx_conf_t *cf, ngx_command_t *cmd,
1345 void *conf)
1346 {
1347 ngx_http_image_filter_conf_t *imcf = conf;
1348
1349 ngx_str_t *value;
1350 ngx_int_t n;
1351 ngx_http_complex_value_t cv;
1352 ngx_http_compile_complex_value_t ccv;
1353
1354 value = cf->args->elts;
1355
1356 ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
1357
1358 ccv.cf = cf;
1359 ccv.value = &value[1];
1360 ccv.complex_value = &cv;
1361
1362 if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
1363 return NGX_CONF_ERROR;
1364 }
1365
1366 if (cv.lengths == NULL) {
1367 n = ngx_http_image_filter_value(&value[1]);
1368
1369 if (n <= 0) {
1370 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1371 "invalid parameter \"%V\"", &value[1]);
1372 return NGX_CONF_ERROR;
1373 }
1374
1375 imcf->jpeg_quality = (ngx_uint_t) n;
1376
1377 } else {
1378 imcf->jqcv = ngx_palloc(cf->pool, sizeof(ngx_http_complex_value_t));
1379 if (imcf->jqcv == NULL) {
1380 return NGX_CONF_ERROR;
1381 }
1382
1383 *imcf->jqcv = cv;
1384 }
1385
1386 return NGX_CONF_OK;
1387 }
1388
1389
1232 static ngx_int_t 1390 static ngx_int_t
1233 ngx_http_image_filter_init(ngx_conf_t *cf) 1391 ngx_http_image_filter_init(ngx_conf_t *cf)
1234 { 1392 {
1235 ngx_http_next_header_filter = ngx_http_top_header_filter; 1393 ngx_http_next_header_filter = ngx_http_top_header_filter;
1236 ngx_http_top_header_filter = ngx_http_image_header_filter; 1394 ngx_http_top_header_filter = ngx_http_image_header_filter;