[nginx] Image filter: support for WebP.
Valentin Bartenev
vbart at nginx.com
Fri Oct 21 12:19:35 UTC 2016
details: http://hg.nginx.org/nginx/rev/e4b00a021cea
branches:
changeset: 6779:e4b00a021cea
user: Valentin Bartenev <vbart at nginx.com>
date: Fri Oct 21 15:18:44 2016 +0300
description:
Image filter: support for WebP.
In collaboration with Ivan Poluyanov.
diffstat:
auto/lib/libgd/conf | 5 +
src/http/modules/ngx_http_image_filter_module.c | 164 +++++++++++++++++++++++-
2 files changed, 164 insertions(+), 5 deletions(-)
diffs (295 lines):
diff -r 5e95b9fb33b7 -r e4b00a021cea auto/lib/libgd/conf
--- a/auto/lib/libgd/conf Thu Oct 20 16:15:03 2016 +0300
+++ b/auto/lib/libgd/conf Fri Oct 21 15:18:44 2016 +0300
@@ -74,6 +74,11 @@ if [ $ngx_found = yes ]; then
NGX_LIB_LIBGD=$ngx_feature_libs
+ ngx_feature="GD WebP support"
+ ngx_feature_name="NGX_HAVE_GD_WEBP"
+ ngx_feature_test="gdImagePtr img = gdImageCreateFromWebpPtr(1, NULL);"
+ . auto/feature
+
else
cat << END
diff -r 5e95b9fb33b7 -r e4b00a021cea src/http/modules/ngx_http_image_filter_module.c
--- a/src/http/modules/ngx_http_image_filter_module.c Thu Oct 20 16:15:03 2016 +0300
+++ b/src/http/modules/ngx_http_image_filter_module.c Fri Oct 21 15:18:44 2016 +0300
@@ -31,6 +31,7 @@
#define NGX_HTTP_IMAGE_JPEG 1
#define NGX_HTTP_IMAGE_GIF 2
#define NGX_HTTP_IMAGE_PNG 3
+#define NGX_HTTP_IMAGE_WEBP 4
#define NGX_HTTP_IMAGE_BUFFERED 0x08
@@ -42,6 +43,7 @@ typedef struct {
ngx_uint_t height;
ngx_uint_t angle;
ngx_uint_t jpeg_quality;
+ ngx_uint_t webp_quality;
ngx_uint_t sharpen;
ngx_flag_t transparency;
@@ -51,6 +53,7 @@ typedef struct {
ngx_http_complex_value_t *hcv;
ngx_http_complex_value_t *acv;
ngx_http_complex_value_t *jqcv;
+ ngx_http_complex_value_t *wqcv;
ngx_http_complex_value_t *shcv;
size_t buffer_size;
@@ -109,6 +112,8 @@ static char *ngx_http_image_filter(ngx_c
void *conf);
static char *ngx_http_image_filter_jpeg_quality(ngx_conf_t *cf,
ngx_command_t *cmd, void *conf);
+static char *ngx_http_image_filter_webp_quality(ngx_conf_t *cf,
+ ngx_command_t *cmd, void *conf);
static char *ngx_http_image_filter_sharpen(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static ngx_int_t ngx_http_image_filter_init(ngx_conf_t *cf);
@@ -130,6 +135,13 @@ static ngx_command_t ngx_http_image_fil
0,
NULL },
+ { ngx_string("image_filter_webp_quality"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_http_image_filter_webp_quality,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ 0,
+ NULL },
+
{ ngx_string("image_filter_sharpen"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_http_image_filter_sharpen,
@@ -200,7 +212,8 @@ static ngx_http_output_body_filter_pt
static ngx_str_t ngx_http_image_types[] = {
ngx_string("image/jpeg"),
ngx_string("image/gif"),
- ngx_string("image/png")
+ ngx_string("image/png"),
+ ngx_string("image/webp")
};
@@ -441,6 +454,13 @@ ngx_http_image_test(ngx_http_request_t *
/* PNG */
return NGX_HTTP_IMAGE_PNG;
+
+ } else if (p[0] == 'R' && p[1] == 'I' && p[2] == 'F' && p[3] == 'F'
+ && p[8] == 'W' && p[9] == 'E' && p[10] == 'B' && p[11] == 'P')
+ {
+ /* WebP */
+
+ return NGX_HTTP_IMAGE_WEBP;
}
return NGX_HTTP_IMAGE_NONE;
@@ -731,6 +751,56 @@ ngx_http_image_size(ngx_http_request_t *
break;
+ case NGX_HTTP_IMAGE_WEBP:
+
+ if (ctx->length < 30) {
+ return NGX_DECLINED;
+ }
+
+ if (p[12] != 'V' || p[13] != 'P' || p[14] != '8') {
+ return NGX_DECLINED;
+ }
+
+ switch (p[15]) {
+
+ case ' ':
+ if (p[20] & 1) {
+ /* not a key frame */
+ return NGX_DECLINED;
+ }
+
+ if (p[23] != 0x9d || p[24] != 0x01 || p[25] != 0x2a) {
+ /* invalid start code */
+ return NGX_DECLINED;
+ }
+
+ width = (p[26] | p[27] << 8) & 0x3fff;
+ height = (p[28] | p[29] << 8) & 0x3fff;
+
+ break;
+
+ case 'L':
+ if (p[20] != 0x2f) {
+ /* invalid signature */
+ return NGX_DECLINED;
+ }
+
+ width = ((p[21] | p[22] << 8) & 0x3fff) + 1;
+ height = ((p[22] >> 6 | p[23] << 2 | p[24] << 10) & 0x3fff) + 1;
+
+ break;
+
+ case 'X':
+ width = (p[24] | p[25] << 8 | p[26] << 16) + 1;
+ height = (p[27] | p[28] << 8 | p[29] << 16) + 1;
+ break;
+
+ default:
+ return NGX_DECLINED;
+ }
+
+ break;
+
default:
return NGX_DECLINED;
@@ -1043,6 +1113,15 @@ ngx_http_image_source(ngx_http_request_t
failed = "gdImageCreateFromPngPtr() failed";
break;
+ case NGX_HTTP_IMAGE_WEBP:
+#if (NGX_HAVE_GD_WEBP)
+ img = gdImageCreateFromWebpPtr(ctx->length, ctx->image);
+ failed = "gdImageCreateFromWebpPtr() failed";
+#else
+ failed = "nginx was built without GD WebP support";
+#endif
+ break;
+
default:
failed = "unknown image type";
break;
@@ -1090,7 +1169,7 @@ ngx_http_image_out(ngx_http_request_t *r
{
char *failed;
u_char *out;
- ngx_int_t jq;
+ ngx_int_t q;
ngx_http_image_filter_conf_t *conf;
out = NULL;
@@ -1100,12 +1179,12 @@ ngx_http_image_out(ngx_http_request_t *r
case NGX_HTTP_IMAGE_JPEG:
conf = ngx_http_get_module_loc_conf(r, ngx_http_image_filter_module);
- jq = ngx_http_image_filter_get_value(r, conf->jqcv, conf->jpeg_quality);
- if (jq <= 0) {
+ q = ngx_http_image_filter_get_value(r, conf->jqcv, conf->jpeg_quality);
+ if (q <= 0) {
return NULL;
}
- out = gdImageJpegPtr(img, size, jq);
+ out = gdImageJpegPtr(img, size, q);
failed = "gdImageJpegPtr() failed";
break;
@@ -1119,6 +1198,22 @@ ngx_http_image_out(ngx_http_request_t *r
failed = "gdImagePngPtr() failed";
break;
+ case NGX_HTTP_IMAGE_WEBP:
+#if (NGX_HAVE_GD_WEBP)
+ conf = ngx_http_get_module_loc_conf(r, ngx_http_image_filter_module);
+
+ q = ngx_http_image_filter_get_value(r, conf->wqcv, conf->webp_quality);
+ if (q <= 0) {
+ return NULL;
+ }
+
+ out = gdImageWebpPtrEx(img, size, q);
+ failed = "gdImageWebpPtrEx() failed";
+#else
+ failed = "nginx was built without GD WebP support";
+#endif
+ break;
+
default:
failed = "unknown image type";
break;
@@ -1196,11 +1291,13 @@ ngx_http_image_filter_create_conf(ngx_co
* conf->hcv = NULL;
* conf->acv = NULL;
* conf->jqcv = NULL;
+ * conf->wqcv = NULL;
* conf->shcv = NULL;
*/
conf->filter = NGX_CONF_UNSET_UINT;
conf->jpeg_quality = NGX_CONF_UNSET_UINT;
+ conf->webp_quality = NGX_CONF_UNSET_UINT;
conf->sharpen = NGX_CONF_UNSET_UINT;
conf->transparency = NGX_CONF_UNSET;
conf->interlace = NGX_CONF_UNSET;
@@ -1242,6 +1339,16 @@ ngx_http_image_filter_merge_conf(ngx_con
}
}
+ if (conf->webp_quality == NGX_CONF_UNSET_UINT) {
+
+ /* 80 is libwebp default quality */
+ ngx_conf_merge_uint_value(conf->webp_quality, prev->webp_quality, 80);
+
+ if (conf->wqcv == NULL) {
+ conf->wqcv = prev->wqcv;
+ }
+ }
+
if (conf->sharpen == NGX_CONF_UNSET_UINT) {
ngx_conf_merge_uint_value(conf->sharpen, prev->sharpen, 0);
@@ -1462,6 +1569,53 @@ ngx_http_image_filter_jpeg_quality(ngx_c
static char *
+ngx_http_image_filter_webp_quality(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf)
+{
+ ngx_http_image_filter_conf_t *imcf = conf;
+
+ ngx_str_t *value;
+ ngx_int_t n;
+ ngx_http_complex_value_t cv;
+ ngx_http_compile_complex_value_t ccv;
+
+ value = cf->args->elts;
+
+ ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
+
+ ccv.cf = cf;
+ ccv.value = &value[1];
+ ccv.complex_value = &cv;
+
+ if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ if (cv.lengths == NULL) {
+ n = ngx_http_image_filter_value(&value[1]);
+
+ if (n <= 0) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid value \"%V\"", &value[1]);
+ return NGX_CONF_ERROR;
+ }
+
+ imcf->webp_quality = (ngx_uint_t) n;
+
+ } else {
+ imcf->wqcv = ngx_palloc(cf->pool, sizeof(ngx_http_complex_value_t));
+ if (imcf->wqcv == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ *imcf->wqcv = cv;
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
ngx_http_image_filter_sharpen(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf)
{
More information about the nginx-devel
mailing list