{"id":13788820,"url":"https://github.com/ioppermann/modjpeg-nginx","last_synced_at":"2025-04-23T07:31:28.838Z","repository":{"id":25494413,"uuid":"28925550","full_name":"ioppermann/modjpeg-nginx","owner":"ioppermann","description":"NGINX filter module for adding overlays and logos to JPEGs on-the-fly without degrading the quality of the image.","archived":false,"fork":false,"pushed_at":"2024-10-02T20:07:13.000Z","size":53,"stargazers_count":19,"open_issues_count":0,"forks_count":3,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-02T10:11:50.882Z","etag":null,"topics":["filter","jpeg","libmodjpeg","module","nginx"],"latest_commit_sha":null,"homepage":"","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-2-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ioppermann.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-01-07T17:33:28.000Z","updated_at":"2024-10-02T20:07:17.000Z","dependencies_parsed_at":"2022-07-12T09:22:37.229Z","dependency_job_id":null,"html_url":"https://github.com/ioppermann/modjpeg-nginx","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ioppermann%2Fmodjpeg-nginx","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ioppermann%2Fmodjpeg-nginx/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ioppermann%2Fmodjpeg-nginx/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ioppermann%2Fmodjpeg-nginx/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ioppermann","download_url":"https://codeload.github.com/ioppermann/modjpeg-nginx/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250391278,"owners_count":21422870,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["filter","jpeg","libmodjpeg","module","nginx"],"created_at":"2024-08-03T21:00:54.195Z","updated_at":"2025-04-23T07:31:28.440Z","avatar_url":"https://github.com/ioppermann.png","language":"C","funding_links":[],"categories":["Third Party Modules","Streaming, media and image processing"],"sub_categories":["C Modules"],"readme":"# modjpeg-nginx\n\nNginx filter module for adding overlays on JPEGs on-the-fly with [libmodjpeg](https://github.com/ioppermann/libmodjpeg).\n\n\u003e With libmodjpeg you can overlay a (masked) image onto an existing JPEG as lossless as possible. Changes in the JPEG only\n\u003e take place where the overlayed image is applied. All modifications happen in the DCT domain, thus the JPEG is decoded and\n\u003e encoded losslessly.\n\n-   [Typical Uses](#typical-uses)\n-   [Try it out](#try-it-out)\n-   [Installation](#installation)\n-   [Compatibility](#compatibility)\n-   [Synopsis](#synopsis)\n-   [Directives](#directives)\n    -   [jpeg_filter](#jpeg_filter)\n    -   [jpeg_filter_max_pixel](#jpeg_filter_max_pixel)\n    -   [jpeg_filter_buffer](#jpeg_filter_buffer)\n    -   [jpeg_filter_optimize](#jpeg_filter_optimize)\n    -   [jpeg_filter_progressive](#jpeg_filter_progressive)\n    -   [jpeg_filter_arithmetric](#jpeg_filter_arithmetric)\n    -   [jpeg_filter_graceful](#jpeg_filter_graceful)\n    -   [jpeg_filter_effect](#jpeg_filter_effect)\n    -   [jpeg_filter_dropon_align](#jpeg_filter_dropon_align)\n    -   [jpeg_filter_dropon_offset](#jpeg_filter_dropon_offset)\n    -   [jpeg_filter_dropon_file](#jpeg_filter_dropon_file)\n    -   [jpeg_filter_dropon_memory](#jpeg_filter_dropon_memory)\n    -   [Notes](#notes)\n-   [License](#license)\n-   [Acknowledgement](#acknowledgement)\n\n## Typical Uses\n\nThis filter module can add overlays (e.g. a logo, visual watermark) on JPEGs when they are requested.\n\nA few ideas:\n\n-   Consider you are a photographer and have a image gallery on your website. Without hardcoding your logo (brand, watermark, ...) into these images you can apply it the moment the image is requested. Whenever you update your logo, just update the nginx configuration and it's done. No need to re-process all your images.\n-   You have an online shop with thousands of product images. With just configuring nginx you can add your logo to all of the product images. You don't have to process all product images.\n-   You have a paid service. Add a watermark to all images if the user is not subscribed. If the user is subscribed, don't apply the watermark or put just a small logo on the images without touching the original images.\n-   On your website, registered users can upload images. Add the avatar of the user to the image who uploaded the image without processing it after the upload. If the user changes her avatar, all her images will automatically have the new avatar on them.\n\n## Try it out\n\nIn order to try out this filter module, pull the docker image\n\n```bash\ndocker pull ioppermann/modjpeg-nginx:latest\n```\n\nThe docker container exposes TCP port 80 and expects a directory with images mounted on `/images`, e.g.\n\n```bash\ndocker run -it --rm --name=modjpeg-nginx \\\n   --mount type=bind,src=$PWD/images,dst=/images,readonly \\\n   -p 8080:80 \\\n   ioppermann/modjpeg-nginx:latest\n```\n\nNow you can browse to [http://localhost:8080/](http://localhost:8080/) and click on the listed images. The modjpeg logo will be applied in the top left corner. By default\nonly images that are smaller than 10MB are processed by the filter. Stop the container by pressing `Ctrl-c`.\n\nThe filter can be controlled by these environment variables:\n\n| Name             | Default                            | Description                                                 |\n| ---------------- | ---------------------------------- | ----------------------------------------------------------- |\n| MJ_GRACEFUL      | on                                 | See [jpeg_filter_graceful](#jpeg_filter_graceful)           |\n| MJ_BUFFER        | 10M                                | See [jpeg_filter_buffer](#jpeg_filter_buffer)               |\n| MJ_MAX_PIXEL     | 0                                  | See [jpeg_filter_max_pixel](#jpeg_filter_max_pixel)         |\n| MJ_DROPON_ALIGN  | \"top left\"                         | See [jpeg_filter_dropon_align](#jpeg_filter_dropon_align)   |\n| MJ_DROPON_OFFSET | \"0 0\"                              | See [jpeg_filter_dropon_offset](#jpeg_filter_dropon_offset) |\n| MJ_DROPON_FILE   | \"/usr/local/nginx/conf/dropon.png\" | See [jpeg_filter_dropon_file](#jpeg_filter_dropon_file)     |\n\nThe following example will allow images with up to 150 megapixel (`MJ_MAX_PIXEL`) and 100MB in file size (`MJ_BUFFER`). The logo will be placed in bottom right corner (`MJ_DROPON_ALIGN`)\nwith an offset of -15px horizontally and vertically (`MJ_DROPON_OFFSET`).\n\n```bash\ndocker run -it --rm --name=modjpeg-nginx \\\n   --mount type=bind,src=$PWD/images,dst=/images,readonly \\\n   -p 8080:80 \\\n   -e MJ_MAX_PIXEL=150000000 \\\n   -e MJ_BUFFER=100M \\\n   -e MJ_DROPON_ALIGN=\"bottom right\" \\\n   -e MJ_DROPON_OFFSET=\"-15 -15\" \\\n   ioppermann/modjpeg-nginx:latest\n```\n\nIn order to change the logo, you can mount an additional volume or put it into the directory you already mount, e.g.\n\n```bash\ndocker run -it --rm --name=modjpeg-nginx \\\n   --mount type=bind,src=$PWD/images,dst=/images,readonly \\\n   -p 8080:80 \\\n   -e MJ_DROPON_FILE=\"/images/logo.png\" \\\n   ioppermann/modjpeg-nginx:latest\n```\n\n## Installation\n\n### CentOS / RedHat 7 packages\n\nAn easy way to use the module in CentOS or RedHat 7, is to use precompiled dynamic nginx module. It is built for nginx stable. The [repository](https://www.getpagespeed.com/redhat) includes latest stable nginx, the jpeg module, libmodjpeg and libpng16 dependencies:\n\n    yum -y install https://extras.getpagespeed.com/release-el7-latest.rpm\n    yum install nginx nginx-module-jpeg\n\n### Compilation\n\nFor using the modjpeg-nginx filter module, follow these steps:\n\n1. Clone and install [libmodjpeg](https://github.com/ioppermann/libmodjpeg) (libjpeg and cmake are required)\n2. Clone this repository\n3. Download and extract the [latest nginx](http://nginx.org/en/download.html)\n4. Configure, compile, and install nginx\n\n```bash\n# Clone and install libmodjpeg\ngit clone https://github.com/ioppermann/libmodjpeg.git\ncd libmodjpeg\ncmake .\nmake\nmake install\ncd ..\n\n# Clone modjpeg-nginx\ngit clone https://github.com/ioppermann/modjpeg-nginx.git\n\n# Download and install nginx\nwget 'http://nginx.org/download/nginx-1.19.2.tar.gz'\ntar -xvzf nginx-1.19.2.tar.gz\ncd nginx-1.19.2\n\n# Configure as static module, or ...\n./configure --add_module=../modjpeg-nginx\n\n# ... configure as dynamic module (as of nginx 1.9.11)\n./configure --add_dynamic_module=../modjpeg-nginx\n\n# If the libmodjpeg library is not found, add e.g. '--with-ld-opt=-L/usr/local/lib' to\n# the configure options if it was installed to /usr/local/lib\n\n# You may want to use the other './configure' options that are used\n# in your current nginx build. Check the output of 'nginx -V'.\n\nmake\nmake install\n```\n\nIf you configured modjpeg-nginx as dynamic module, you have to load the module in the beginning of the config\n\n```nginx\n...\nload_module modules/ngx_http_jpeg_filter_module.so;\n...\n```\n\n## Compatibility\n\nThis module has been tested with the following versions of nginx:\n\n-   1.26.2\n-   1.24.0\n-   1.22.1\n-   1.20.2\n-   1.19.2\n-   1.18.0\n-   1.17.6\n-   1.16.1\n-   1.15.3\n-   1.14.0\n-   1.13.10\n-   1.12.2\n-   1.10.3\n-   1.8.1 (only as static module)\n\n## Synopsis\n\n```nginx\n   ...\n\n   location /gallery {\n      # enable jpeg filter module\n      jpeg_filter on;\n\n      # limit image sizes to 9 megapixel\n      jpeg_filter_max_pixel 9000000;\n\n      # limit image file size to 5 megabytes\n      jpeg_filter_buffer 5M;\n\n      # deliver the images unmodified if one of the limits apply\n      jpeg_filter_graceful on;\n\n      # pixelate the image\n      jpeg_filter_effect pixelate;\n\n      # add a masked logo in the bottom right corner\n      # with a distance of 10 pixel from the border\n      jpeg_filter_dropon_align bottom right;\n      jpeg_filter_dropon_offset -10 -10;\n      jpeg_filter_dropon_file /path/to/logo.jpg /path/to/mask.jpg;\n   }\n\n   ...\n```\n\nOr use it with [OpenResty's ngx_http_lua_module](https://github.com/openresty/lua-nginx-module) and a PNG logo:\n\n```nginx\n   ...\n\n   location /gallery {\n      set_by_lua_block $valign {\n         local a = { 'top', 'center', 'bottom' }\n         return a[math.random(#a)]\n      }\n\n      set_by_lua_block $halign {\n         local a = { 'left', 'center', 'right' }\n         return a[math.random(#a)]\n      }\n\n      # enable jpeg filter module\n      jpeg_filter on;\n\n      # limit image sizes to 9 megapixel\n      jpeg_filter_max_pixel 9000000;\n\n      # limit image file size to 5 megabytes\n      jpeg_filter_buffer 5M;\n\n      # deliver the images unmodified if one of the limits apply\n      jpeg_filter_graceful on;\n\n      # pixelate the image\n      jpeg_filter_effect pixelate;\n\n      # add a logo in a random position\n      jpeg_filter_dropon_align $valign $halign;\n      jpeg_filter_dropon_file /path/to/logo.png;\n   }\n\n   ...\n```\n\nOr generate a logo with [Lua-GD](http://ittner.github.io/lua-gd/):\n\n```nginx\nhttp {\n   ...\n   lua_package_cpath '/usr/local/lib/lua/5.1/?.so;;';\n   ...\n   server {\n      ...\n      location /gallery {\n      \t   set_by_lua_block $logobytestream {\n              local gd = require \"gd\"\n\n              local im = gd.create(210, 70)\n              local white = im:colorAllocate(255, 255, 255)\n              local black = im:colorAllocate(0, 0, 0)\n              im:filledRectangle(0, 0, 140, 80, white)\n              im:string(gd.FONT_LARGE, 10, 10, \"Hello modjpeg\", black)\n              im:string(gd.FONT_LARGE, 10, 40, os.date(\"%c\"), black);\n              return im:jpegStr(85)\n      \t   }\n\n   \t   # enable jpeg filter module\n   \t   jpeg_filter on;\n\n           # limit image sizes to 9 megapixel\n           jpeg_filter_max_pixel 9000000;\n\n           # limit image file size to 5 megabytes\n           jpeg_filter_buffer 5M;\n\n           # deliver the images unmodified if one of the limits apply\n           jpeg_filter_graceful on;\n\n           # pixelate the image\n           jpeg_filter_effect pixelate;\n\n           # add a generated logo in the bottom right corner\n           # with a distance of 10 pixel from the border\n           jpeg_filter_dropon_align bottom right;\n           jpeg_filter_dropon_offset -10 -10;\n           jpeg_filter_dropon_memory $logobytestream;\n      }\n      ...\n   }\n   ...\n}\n```\n\n## Directives\n\n-   [jpeg_filter](#jpeg_filter)\n-   [jpeg_filter_max_pixel](#jpeg_filter_max_pixel)\n-   [jpeg_filter_buffer](#jpeg_filter_buffer)\n-   [jpeg_filter_optimize](#jpeg_filter_optimize)\n-   [jpeg_filter_progressive](#jpeg_filter_progressive)\n-   [jpeg_filter_arithmetric](#jpeg_filter_arithmetric)\n-   [jpeg_filter_graceful](#jpeg_filter_graceful)\n-   [jpeg_filter_effect](#jpeg_filter_effect)\n-   [jpeg_filter_dropon_align](#jpeg_filter_dropon_align)\n-   [jpeg_filter_dropon_offset](#jpeg_filter_dropon_offset)\n-   [jpeg_filter_dropon_file](#jpeg_filter_dropon_file)\n-   [jpeg_filter_dropon_memory](#jpeg_filter_dropon_memory)\n-   [Notes](#notes)\n\n### jpeg_filter\n\n**Syntax:** `jpeg_filter on | off`\n\n**Default:** `jpeg_filter off`\n\n**Context:** `location`\n\nEnable the jpeg filter module.\n\nThis directive is turned off by default.\n\n### jpeg_filter_max_pixel\n\n**Syntax:** `jpeg_filter_max_pixel pixel`\n\n**Default:** `0`\n\n**Context:** `http, server, location`\n\nMaximum number of pixel in image to operate on. If the image has more pixel (width \\* height) than `pixel`, the jpeg filter will return a \"415 Unsupported Media Type\".\nSet [jpeg_filter_graceful](#jpeg_filter_graceful) to `on` to deliver the image unchanged. Set the maximum pixel to 0 in order ignore the image dimensions.\n\nThis directive is set to 0 by default.\n\n### jpeg_filter_buffer\n\n**Syntax:** `jpeg_filter_buffer size`\n\n**Default:** `2M`\n\n**Context:** `http, server, location`\n\nThe maximum file size of the image to operate on. If the file size if bigger than `size`, the jpeg filter will return a \"415 Unsupported Media Type\".\nSet [jpeg_filter_graceful](#jpeg_filter_graceful) to `on` to deliver the image unchanged.\n\nThis directive is set to 2 megabyte by default.\n\n### jpeg_filter_optimize\n\n**Syntax:** `jpeg_filter_optimize on | off`\n\n**Default:** `off`\n\n**Context:** `http, server, location`\n\nUpon delivery, optimize the Huffman tables of the image.\n\nThis directive is turned off by default.\n\n### jpeg_filter_progressive\n\n**Syntax:** `jpeg_filter_progressive on | off`\n\n**Default:** `off`\n\n**Context:** `http, server, location`\n\nUpon delivery, enable progressive encoding of the image.\n\nThis directive is turned off by default.\n\n### jpeg_filter_arithmetric\n\n**Syntax:** `jpeg_filter_arithmetric on | off`\n\n**Default:** `off`\n\n**Context:** `http, server, location`\n\nUpon delivery, enable arithmetric encoding of the image.\nThis will override the [jpeg_filter_optimize](#jpeg_filter_optimize) directive.\nArithmetric encoding is usually not supported by browsers.\n\nThis directive is turned off by default.\n\n### jpeg_filter_graceful\n\n**Syntax:** `jpeg_filter_graceful on | off`\n\n**Default:** `off`\n\n**Context:** `http, server, location`\n\nAllow to deliver the unchanged image in case the directives [jpeg_filter_max_pixel](#jpeg_filter_max_pixel) or [jpeg_filter_buffer](#jpeg_filter_buffer) would return a \"415 Unsupported Media Type\" error.\n\nThis directive is turned off by default.\n\n### jpeg_filter_effect\n\n**Syntax:** `jpeg_filter_effect grayscale | pixelate`\n\n**Syntax:** `jpeg_filter_effect darken | brighten value`\n\n**Syntax:** `jpeg_filter_effect tintblue | tintyellow | tintred | tintgreen value`\n\n**Default:** `-`\n\n**Context:** `location`\n\nApply an effect to the image.\n\n`grayscale` will remove all color components from the image. This only applies to images in the YCbCr color space.\n\n`pixelate` will pixelate the image in blocks of 8x8 pixel by setting the AC coefficients in all components to 0.\n\n`darken` will darken the image by decreasing the DC coefficients in the Y component by `value`. This only applies to images in the YCbCr color space.\n\n`brighten` will brighten the image by increasing the DC coefficients in the Y component by `value`. This only applies to images in the YCbCr color space.\n\n`tintblue` will tint the image blue by increasing the DC coefficients in the Cb component by `value`. This only applies to images in the YCbCr color space.\n\n`tintyellow` will tint the image blue by decreasing the DC coefficients in the Cb component by `value`. This only applies to images in the YCbCr color space.\n\n`tintred` will tint the image red by increasing the DC coefficients in the Cr component by `value`. This only applies to images in the YCbCr color space.\n\n`tintgreen` will tint the image green by decreasing the DC coefficients in the Cr component by `value`. This only applies to images in the YCbCr color space.\n\nThis directive is not set by default.\n\nAll parameters can contain variables.\n\n### jpeg_filter_dropon_align\n\n**Syntax:** `jpeg_filter_dropon_align [top | center | bottom] [left | center | right]`\n\n**Default:** `center center`\n\n**Context:** `location`\n\nAlign the dropon on the image. Use the directive [jpeg_filter_dropon_offset](#jpeg_filter_dropon_offset) to offset the dropon from the alignment.\n\nThis directive must be set before [jpeg_filter_dropon](#jpeg_filter_dropon) in order to have an effect on the dropon.\n\nThis directive will apply the dropon in the center of the image by default.\n\nAll parameters can contain variables.\n\n### jpeg_filter_dropon_offset\n\n**Syntax:** `jpeg_filter_dropon_offset vertical horizontal`\n\n**Default:** `0 0`\n\n**Context:** `location`\n\nOffset the dropon by `vertical` and `horizontal` pixels from the alignment given with the [jpeg_filter_dropon_align](#jpeg_filter_dropon_align) directive.\nUse a negative value to move the dropon up or left and a positive value to move the dropon down or right.\n\nThis directive must be set before [jpeg_filter_dropon](#jpeg_filter_dropon) in order to have an effect on the dropon.\n\nThis directive will not apply an offset by default.\n\nAll parameters can contain variables.\n\n### jpeg_filter_dropon_file\n\n**Syntax:** `jpeg_filter_dropon_file image`\n\n**Syntax:** `jpeg_filter_dropon_file image mask`\n\n**Default:** `-`\n\n**Context:** `location`\n\nApply a dropon to the image. The dropon is given by a path to a JPEG or PNG image for `image` and optionally a path to a JPEG image for `mask`. If no mask image is\nprovided, the image will be applied without transcluency. If a mask image is provided, only the luminance component will be used. For the mask, black means\nfully transcluent and white means fully opaque. Any values inbetween will blend the underlying image and the dropon accordingly. If `image` is a path to a PNG, the\nmask will be ignored.\n\nThis directive is not set by default.\n\nAll parameters can contain variables.\n\nIf none of the parameters contain variables, the dropon is loaded during loading of the configuration. If at least one parameter contains variables, the dropon\nwill be loaded during processing of the request. After processing the request, the dropon will be unloaded.\n\nPNG files as dropon are supported only if libmodjpeg has been compiled with PNG support.\n\n### jpeg_filter_dropon_memory\n\n**Syntax:** `jpeg_filter_dropon_memory $image`\n\n**Syntax:** `jpeg_filter_dropon_memory $image $mask`\n\n**Default:** `-`\n\n**Context:** `location`\n\nApply a dropon to the image. The dropon is given by a variable holding a JPEG or PNG image bytestream for `$image` and optionally a variable to a JPEG image bytestream for `$mask`.\nIf no mask image is provided, the image will be applied without transcluency. If a mask image is provided, only the luminance component will be used. For the mask,\nblack means fully transcluent and white means fully opaque. Any values inbetween will blend the underlying image and the dropon accordingly. If `$image` is a PNG, the\nmask will be ignored.\n\nThis directive is not set by default.\n\nAll parameters are expected to be variables.\n\nThe dropon will always be loaded during processing of the request. After processing the request, the dropon will be unloaded.\n\nPNG bytestreams as dropon are supported only if libmodjpeg has been compiled with PNG support.\n\n### Notes\n\nThe directives `jpeg_filter_effect`, `jpeg_filter_dropon_align`, `jpeg_filter_dropon_offset`, and `jpeg_filter_dropon` are applied in the order they\nappear in the nginx config file, i.e. it makes a difference if you apply first an effect and then add a dropon or vice versa. In the former case the dropon will be\nunaffected by the effect and in the latter case the effect will be also applied on the dropon.\n\n## License\n\nThis module is distributed under the BSD license. Refer to [LICENSE](/blob/master/LICENSE).\n\n## Acknowledgement\n\nThis module is heavily inspired by the nginx image filter module with\ninsights from\n[\"Emiller’s Guide To Nginx Module Development\"](https://www.evanmiller.org/nginx-modules-guide.html)\nand the\n[nginx development guide](https://nginx.org/en/docs/dev/development_guide.html).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fioppermann%2Fmodjpeg-nginx","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fioppermann%2Fmodjpeg-nginx","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fioppermann%2Fmodjpeg-nginx/lists"}