Mercurial > hg > nginx-ranges
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; |