mirror of
https://github.com/nginx/nginx.git
synced 2024-12-22 15:13:28 -06:00
Image filter: support for WebP.
In collaboration with Ivan Poluyanov.
This commit is contained in:
parent
70d0530f88
commit
9ec0b1fe12
@ -74,6 +74,11 @@ if [ $ngx_found = yes ]; then
|
|||||||
|
|
||||||
NGX_LIB_LIBGD=$ngx_feature_libs
|
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
|
else
|
||||||
|
|
||||||
cat << END
|
cat << END
|
||||||
|
@ -31,6 +31,7 @@
|
|||||||
#define NGX_HTTP_IMAGE_JPEG 1
|
#define NGX_HTTP_IMAGE_JPEG 1
|
||||||
#define NGX_HTTP_IMAGE_GIF 2
|
#define NGX_HTTP_IMAGE_GIF 2
|
||||||
#define NGX_HTTP_IMAGE_PNG 3
|
#define NGX_HTTP_IMAGE_PNG 3
|
||||||
|
#define NGX_HTTP_IMAGE_WEBP 4
|
||||||
|
|
||||||
|
|
||||||
#define NGX_HTTP_IMAGE_BUFFERED 0x08
|
#define NGX_HTTP_IMAGE_BUFFERED 0x08
|
||||||
@ -42,6 +43,7 @@ typedef struct {
|
|||||||
ngx_uint_t height;
|
ngx_uint_t height;
|
||||||
ngx_uint_t angle;
|
ngx_uint_t angle;
|
||||||
ngx_uint_t jpeg_quality;
|
ngx_uint_t jpeg_quality;
|
||||||
|
ngx_uint_t webp_quality;
|
||||||
ngx_uint_t sharpen;
|
ngx_uint_t sharpen;
|
||||||
|
|
||||||
ngx_flag_t transparency;
|
ngx_flag_t transparency;
|
||||||
@ -51,6 +53,7 @@ typedef struct {
|
|||||||
ngx_http_complex_value_t *hcv;
|
ngx_http_complex_value_t *hcv;
|
||||||
ngx_http_complex_value_t *acv;
|
ngx_http_complex_value_t *acv;
|
||||||
ngx_http_complex_value_t *jqcv;
|
ngx_http_complex_value_t *jqcv;
|
||||||
|
ngx_http_complex_value_t *wqcv;
|
||||||
ngx_http_complex_value_t *shcv;
|
ngx_http_complex_value_t *shcv;
|
||||||
|
|
||||||
size_t buffer_size;
|
size_t buffer_size;
|
||||||
@ -109,6 +112,8 @@ static char *ngx_http_image_filter(ngx_conf_t *cf, ngx_command_t *cmd,
|
|||||||
void *conf);
|
void *conf);
|
||||||
static char *ngx_http_image_filter_jpeg_quality(ngx_conf_t *cf,
|
static char *ngx_http_image_filter_jpeg_quality(ngx_conf_t *cf,
|
||||||
ngx_command_t *cmd, void *conf);
|
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,
|
static char *ngx_http_image_filter_sharpen(ngx_conf_t *cf, ngx_command_t *cmd,
|
||||||
void *conf);
|
void *conf);
|
||||||
static ngx_int_t ngx_http_image_filter_init(ngx_conf_t *cf);
|
static ngx_int_t ngx_http_image_filter_init(ngx_conf_t *cf);
|
||||||
@ -130,6 +135,13 @@ static ngx_command_t ngx_http_image_filter_commands[] = {
|
|||||||
0,
|
0,
|
||||||
NULL },
|
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_string("image_filter_sharpen"),
|
||||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
|
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
|
||||||
ngx_http_image_filter_sharpen,
|
ngx_http_image_filter_sharpen,
|
||||||
@ -200,7 +212,8 @@ static ngx_http_output_body_filter_pt ngx_http_next_body_filter;
|
|||||||
static ngx_str_t ngx_http_image_types[] = {
|
static ngx_str_t ngx_http_image_types[] = {
|
||||||
ngx_string("image/jpeg"),
|
ngx_string("image/jpeg"),
|
||||||
ngx_string("image/gif"),
|
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 *r, ngx_chain_t *in)
|
|||||||
/* PNG */
|
/* PNG */
|
||||||
|
|
||||||
return NGX_HTTP_IMAGE_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;
|
return NGX_HTTP_IMAGE_NONE;
|
||||||
@ -731,6 +751,56 @@ ngx_http_image_size(ngx_http_request_t *r, ngx_http_image_filter_ctx_t *ctx)
|
|||||||
|
|
||||||
break;
|
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:
|
default:
|
||||||
|
|
||||||
return NGX_DECLINED;
|
return NGX_DECLINED;
|
||||||
@ -1043,6 +1113,15 @@ ngx_http_image_source(ngx_http_request_t *r, ngx_http_image_filter_ctx_t *ctx)
|
|||||||
failed = "gdImageCreateFromPngPtr() failed";
|
failed = "gdImageCreateFromPngPtr() failed";
|
||||||
break;
|
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:
|
default:
|
||||||
failed = "unknown image type";
|
failed = "unknown image type";
|
||||||
break;
|
break;
|
||||||
@ -1090,7 +1169,7 @@ ngx_http_image_out(ngx_http_request_t *r, ngx_uint_t type, gdImagePtr img,
|
|||||||
{
|
{
|
||||||
char *failed;
|
char *failed;
|
||||||
u_char *out;
|
u_char *out;
|
||||||
ngx_int_t jq;
|
ngx_int_t q;
|
||||||
ngx_http_image_filter_conf_t *conf;
|
ngx_http_image_filter_conf_t *conf;
|
||||||
|
|
||||||
out = NULL;
|
out = NULL;
|
||||||
@ -1100,12 +1179,12 @@ ngx_http_image_out(ngx_http_request_t *r, ngx_uint_t type, gdImagePtr img,
|
|||||||
case NGX_HTTP_IMAGE_JPEG:
|
case NGX_HTTP_IMAGE_JPEG:
|
||||||
conf = ngx_http_get_module_loc_conf(r, ngx_http_image_filter_module);
|
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);
|
q = ngx_http_image_filter_get_value(r, conf->jqcv, conf->jpeg_quality);
|
||||||
if (jq <= 0) {
|
if (q <= 0) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
out = gdImageJpegPtr(img, size, jq);
|
out = gdImageJpegPtr(img, size, q);
|
||||||
failed = "gdImageJpegPtr() failed";
|
failed = "gdImageJpegPtr() failed";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -1119,6 +1198,22 @@ ngx_http_image_out(ngx_http_request_t *r, ngx_uint_t type, gdImagePtr img,
|
|||||||
failed = "gdImagePngPtr() failed";
|
failed = "gdImagePngPtr() failed";
|
||||||
break;
|
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:
|
default:
|
||||||
failed = "unknown image type";
|
failed = "unknown image type";
|
||||||
break;
|
break;
|
||||||
@ -1196,11 +1291,13 @@ ngx_http_image_filter_create_conf(ngx_conf_t *cf)
|
|||||||
* conf->hcv = NULL;
|
* conf->hcv = NULL;
|
||||||
* conf->acv = NULL;
|
* conf->acv = NULL;
|
||||||
* conf->jqcv = NULL;
|
* conf->jqcv = NULL;
|
||||||
|
* conf->wqcv = NULL;
|
||||||
* conf->shcv = NULL;
|
* conf->shcv = NULL;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
conf->filter = NGX_CONF_UNSET_UINT;
|
conf->filter = NGX_CONF_UNSET_UINT;
|
||||||
conf->jpeg_quality = 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->sharpen = NGX_CONF_UNSET_UINT;
|
||||||
conf->transparency = NGX_CONF_UNSET;
|
conf->transparency = NGX_CONF_UNSET;
|
||||||
conf->interlace = NGX_CONF_UNSET;
|
conf->interlace = NGX_CONF_UNSET;
|
||||||
@ -1242,6 +1339,16 @@ ngx_http_image_filter_merge_conf(ngx_conf_t *cf, void *parent, void *child)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
if (conf->sharpen == NGX_CONF_UNSET_UINT) {
|
||||||
ngx_conf_merge_uint_value(conf->sharpen, prev->sharpen, 0);
|
ngx_conf_merge_uint_value(conf->sharpen, prev->sharpen, 0);
|
||||||
|
|
||||||
@ -1461,6 +1568,53 @@ ngx_http_image_filter_jpeg_quality(ngx_conf_t *cf, ngx_command_t *cmd,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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 *
|
static char *
|
||||||
ngx_http_image_filter_sharpen(ngx_conf_t *cf, ngx_command_t *cmd,
|
ngx_http_image_filter_sharpen(ngx_conf_t *cf, ngx_command_t *cmd,
|
||||||
void *conf)
|
void *conf)
|
||||||
|
Loading…
Reference in New Issue
Block a user