{"id":19353492,"url":"https://github.com/ioppermann/libmodjpeg","last_synced_at":"2025-04-23T07:31:29.699Z","repository":{"id":16386183,"uuid":"19136827","full_name":"ioppermann/libmodjpeg","owner":"ioppermann","description":"A library for JPEG masking and composition in the DCT domain.","archived":false,"fork":false,"pushed_at":"2024-10-02T19:59:17.000Z","size":3649,"stargazers_count":6,"open_issues_count":1,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-02T10:11:52.607Z","etag":null,"topics":["composition","dct","jpeg","logo","masking","overlay","watermark"],"latest_commit_sha":null,"homepage":"","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","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":"2014-04-25T06:47:50.000Z","updated_at":"2024-10-02T19:59:21.000Z","dependencies_parsed_at":"2022-09-24T11:51:47.835Z","dependency_job_id":null,"html_url":"https://github.com/ioppermann/libmodjpeg","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ioppermann%2Flibmodjpeg","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ioppermann%2Flibmodjpeg/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ioppermann%2Flibmodjpeg/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ioppermann%2Flibmodjpeg/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ioppermann","download_url":"https://codeload.github.com/ioppermann/libmodjpeg/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250391277,"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":["composition","dct","jpeg","logo","masking","overlay","watermark"],"created_at":"2024-11-10T04:43:05.285Z","updated_at":"2025-04-23T07:31:28.257Z","avatar_url":"https://github.com/ioppermann.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"# libmodjpeg\n\nA library for JPEG masking and composition in the DCT domain.\n\n-   [Background](#background)\n-   [Compiling and installing](#compiling-and-installing)\n-   [Compatibility](#compatibility)\n-   [Synopsis](#synopsis)\n    -   [Header](#header)\n    -   [Dropon](#dropon)\n    -   [Image](#image)\n    -   [Composition](#composition)\n    -   [Effect](#effect)\n    -   [Return values](#return-values)\n    -   [Supported color spaces](#supported-color-spaces)\n-   [Example](#example)\n-   [Who is using libmodjpeg](#who-is-using-libmodjpeg)\n-   [License](#license)\n-   [Acknowledgement](#acknowledgement)\n-   [References](#references)\n\n## Background\n\nWith libmodjpeg you can overlay a (masked) image onto an existing JPEG as lossless as possible. Changes in the JPEG only\ntake place where the overlayed image is applied. All modifications happen in the [DCT domain](#references), thus the JPEG is decoded and\nencoded losslessly.\n\nAdding an overlay (e.g. logo, watermark, ...) to an existing JPEG image usually will result in loss of quality because the JPEG\nneeds to get decoded and then re-encoded after the overlay has been applied. [Read more about JPEG on Wikipedia](https://en.wikipedia.org/wiki/JPEG).\n\nThe usual process of applying a (masked) overlay involved these steps:\n\n1. Huffman decode\n2. de-quantize\n3. inverse DCT \\*\n4. colorspace transformation from YCbCr to RGB \\*\n5. applying the (masked) overlay\n6. colorspace transformation from RGB to YCbCr \\*\n7. DCT \\*\n8. quantize \\*\n9. Huffman encode\n\nThe steps marked with a \\* will lead to loss of quality.\n\n```bash\ngm convert image.png -filter Lanczos -resize 256x256 -quality 85 image.jpg\ngm composite dropon.png image.jpg -quality 86 image_composed.jpg\ngm compare -highlight-style assign -highlight-color lime -file image_composed_diff.png image.jpg image_composed.jpg\n```\n\n| Original                                            | Overlay                                             | Composed                                                   | Difference                                                       |\n| --------------------------------------------------- | --------------------------------------------------- | ---------------------------------------------------------- | ---------------------------------------------------------------- |\n| ![Original](../master/src/contrib/images/image.jpg) | ![Overlay](../master/src/contrib/images/dropon.png) | ![Result](../master/src/contrib/images/image_composed.jpg) | ![Overlay](../master/src/contrib/images/image_composed_diff.png) |\n\nThe composed image above has been saved with a quality setting of 86. Only if the quality settings of the original image are known, the composed image can be saved with the same quality settings in order to have almost no changes outside of the area of the overlay.\n\n```bash\ngm convert image.png -filter Lanczos -resize 256x256 -quality 85 image.jpg\ngm composite dropon.png image.jpg -quality 85 image_composed_sameq.jpg\ngm compare -highlight-style assign -highlight-color lime -file image_composed_sameq_diff.png image.jpg image_composed_sameq.jpg\n```\n\n| Original                                            | Overlay                                             | Composed                                                         | Difference                                                             |\n| --------------------------------------------------- | --------------------------------------------------- | ---------------------------------------------------------------- | ---------------------------------------------------------------------- |\n| ![Original](../master/src/contrib/images/image.jpg) | ![Overlay](../master/src/contrib/images/dropon.png) | ![Result](../master/src/contrib/images/image_composed_sameq.jpg) | ![Overlay](../master/src/contrib/images/image_composed_sameq_diff.png) |\n\nThe composed image above has been saved with a quality setting of 85, which is the same quality setting as the original image.\n\nlibmodjpeg avoids the lossy decoding and re-encoding of the JPEG image by applying the overlay directly on the un-transformed DCT\ncoefficients:\n\n1. Huffman decode\n2. de-quantize\n3. applying the (masked) overlay\n4. quantize\n5. Huffman encode\n\nIn step 4, the quantization is lossless compared to the usual process because the same DCT and quantization\nvalues are used as in step 2.\n\nOnly the area where the overlay is applied to is affected by changes and the rest of the image will remain untouched.\n\n```bash\ngm convert image.png -filter Lanczos -resize 256x256 -quality 85 image.jpg\nmodjpeg --in image.jpg --dropon dropon.png --out image_dropon.jpg\ngm compare -highlight-style assign -highlight-color lime -file image_dropon_diff.png image.jpg image_dropon.jpg\n```\n\n| Original                                            | Overlay                                             | Composed                                                 | Difference                                                     |\n| --------------------------------------------------- | --------------------------------------------------- | -------------------------------------------------------- | -------------------------------------------------------------- |\n| ![Original](../master/src/contrib/images/image.jpg) | ![Overlay](../master/src/contrib/images/dropon.png) | ![Result](../master/src/contrib/images/image_dropon.jpg) | ![Overlay](../master/src/contrib/images/image_dropon_diff.png) |\n\nThe overlay is applied with the `modjpeg` CLI program, that uses libmodjpeg in order to apply an overlay to a JPEG image. The quality settings of the original image can remain unknown. Changes to the image will only happen where the overlay is applied.\n\nThe overlay itself will experience a loss of quality because it needs to be transformed into the DCT domain\nwith the same colorspace, sampling, and quantization as the image it will be applied to.\n\n## Compiling and installing\n\nlibmodjpeg requires the [libjpeg](http://www.ijg.org/) or compatible ([libjpeg-turbo](https://github.com/libjpeg-turbo/libjpeg-turbo)\nor [mozjpeg](https://github.com/mozilla/mozjpeg)), however the IJG libjpeg or\nlibjpeg-turbo are recommended because mozjpeg will always produce progressive JPEGs which is slower and may not be desired.\n\nIt will be checked for [libpng-1.6.x](https://libpng.sourceforge.io/) as well in order to support overlays in PNG format. PNG support is optional.\n\n```bash\ngit clone https://github.com/ioppermann/libmodjpeg.git\ncd libmodjpeg/src\ncmake ..\nmake\nmake install\n```\n\nIn case libjpeg (or compatible) are installed in a non-standard location you can set the environment variable `CMAKE_PREFIX_PATH`\nto the location where the libjpeg is installed, e.g.:\n\n```bash\nenv CMAKE_PREFIX_PATH=/usr/local/opt/jpeg-turbo/ cmake .\n```\n\n## Example\n\n```C\n#include \u003clibmodjpeg.h\u003e\n\nint main(int argc, char **argv) {\n    // Initialize dropon struct\n    struct mj_dropon_t d;\n    mj_init_dropon(\u0026d);\n\n    // Read a dropon from a JPEG, without mask and with 50% translucency\n    mj_read_dropon_from_file(\u0026d, \"logo.jpg\", NULL, 50);\n\n    // Initialize JPEG image struct\n    struct mj_jpeg_t m;\n    mj_init_jpeg(\u0026m);\n\n    // Read a JPEG image from a file\n    mj_read_jpeg_from_file(\u0026m, \"in.jpg\", 0);\n\n    // Place the dropon in the bottom right corner of the JPEG image\n    // with 10px distance to the bottom and right border\n    mj_compose(\u0026m, \u0026d, MJ_ALIGN_BOTTOM | MJ_ALIGN_RIGHT, -10, -10);\n\n    // Write the JPEG image to a file with optimzed Hufman tables and progressive mode\n    mj_write_jpeg_to_file(\u0026m, \"out.jpg\", MJ_OPTION_OPTIMIZE | MJ_OPTION_PROGRESSIVE);\n\n    // Free the dropon and JPEG image structs\n    mj_free_jpeg(\u0026m);\n    mj_free_dropon(\u0026d);\n\n    return 0;\n}\n```\n\nIn the [contrib](../../tree/master/src/contrib) directory you find an example program that implements all described functionality.\n\n```bash\ncd src/contrib\ncmake .\nmake\n```\n\nIn case the jpeglib (or compatible) is installed in a non-standard location, use the same environment variable for cmake as described above.\n\n## Compatibility\n\nlibmodjpeg has been tested with the following versions of libjpeg (and compatibles):\n\n-   libjpeg v6b (arithmetric coding will not work)\n-   libjpeg v7\n-   libjpeg v8d\n-   libjpeg v9c\n-   libjpeg-turbo v1.5.3\n-   mozjpeg v3.3.1\n\n## Synopsis\n\n### Header\n\n```C\n#include \u003clibmodjpeg.h\u003e\n```\n\nInclude the header file in order to have access to the library.\n\n### Dropon\n\n```C\nstruct mj_dropon_t;\n```\n\nA \"dropon\" denotes the overlay that will be applied to an image and is defined by the struct `mj_dropon_t`.\n\n```C\nvoid mj_init_dropon(mj_dropon_t *d);\n```\n\nInitialize the dropon in order to make it ready for use.\n\n```C\nint mj_read_dropon_from_raw(\n    mj_dropon_t *d,\n    const unsigned char *rawdata,\n    unsigned int colorspace,\n    size_t width,\n    size_t height,\n    short blend);\n```\n\nRead a dropon from raw data. The raw data is a pointer to an array of chars holding the raw image data in the given color space.\n\n```C\n#define MJ_COLORSPACE_RGB            1  // [0] = R0, [1] = G0, [2] = B0, [3] = R1, ...\n#define MJ_COLORSPACE_RGBA           2  // [0] = R0, [1] = G0, [2] = B0, [3] = A0, [4] = R1, ...\n#define MJ_COLORSPACE_GRAYSCALE      3  // [0] = Y0, [1] = Y1, ...\n#define MJ_COLORSPACE_GRAYSCALEA     4  // [0] = Y0, [1] = A0, [2] = Y1, ...\n#define MJ_COLORSPACE_YCC            5  // [0] = Y0, [1] = Cb0, [2] = Cr0, [3] = Y1, ...\n#define MJ_COLORSPACE_YCCA           6  // [0] = Y0, [1] = Cb0, [2] = Cr0, [3] = A0, [4] = Y1, ...\n```\n\n`width` and `height` are the dimensions of the raw image. `blend` is a value in [0, 255] for the translucency for the dropon if no alpha\nchannel is given, where 0 is fully transparent (the dropon will not be applied) and 255 is fully opaque.\n\n```C\nint mj_read_dropon_from_file(\n    mj_dropon_t *d,\n    const char *filename,\n    const char *maskfilename,\n    short blend);\n```\n\nRead a dropon from a file (`filename`). The file can be a JPEG or a PNG.\n\nIf the file is a JPEG, then the alpha channel can be given by a second JPEG file (`maskfilename`).\nUse `NULL` if no alpha channel is available or wanted. `blend` is a value for the translucency for the dropon if no alpha channel is given.\n\nIf the file is a PNG, then use `NULL` for `maskfilename` and any value for `blend` because they will be ignored. The alpha channel is taken\nfrom the PNG, if available. PNG files are only supported if the library is compiled with PNG support.\n\n```C\nint mj_read_dropon_from_memory(\n    mj_dropon_t *d,\n    const unsigned char *memory,\n    size_t len,\n    const unsigned char *maskmemory,\n    size_t masklen,\n    short blend);\n```\n\nRead a dropon from a JPEG or PNG bytestream (`memory` of `len` bytes length).\n\nIf the bytestream is a JPEG, then the alpha channel is given by a second JPEG bytestream (`maskmemory` of `masklen` bytes length).\nUse `NULL` for `maskmemory` or `0` for `masklen` if no alpha channel is available or wanted. `blend` is a value for the translucency for\nthe dropon if no alpha channel is given.\n\nIf the bytestream is a PNG, then use `NULL` for `maskmemory` or `0` for `masklen` and any value for `blend`. The alpha channel is taken\nfrom the PNG, if available. PNG files are only supported if the library is compiled with PNG support.\n\n```C\nvoid mj_free_dropon(mj_dropon_t *d);\n```\n\nFree the memory consumed by the dropon. The dropon struct can be reused for another dropon.\n\n### Image\n\n```C\nstruct mj_jpeg_t;\n```\n\nThe `mj_jpeg_t` holds the JPEG a dropon can be applied to.\n\n```C\nvoid mj_init_jpeg(mj_jpeg_t *m);\n```\n\nInitialize the image in order to make it ready for use.\n\n```C\nint mj_read_jpeg_from_memory(\n    mj_jpeg_t *m,\n    const unsigned char *memory,\n    size_t len,\n    size_t max_pixel);\n```\n\nRead a JPEG from a buffer. The buffer holds the JPEG bytestream of length `len` bytes. `max_pixel` is the maximum number of pixels allowed in the image\nto prevent processing too big images. Set it to `0` to allow any sized images.\n\n```C\nint mj_read_jpeg_from_file(\n    mj_jpeg_t *m,\n    const char *filename,\n    size_t max_pixel);\n```\n\nRead a JPEG from a file denoted by `filename`. `max_pixel` is the maximum number of pixels allowed in the image\nto prevent processing too big images. Set it to `0` to allow any sized images.\n\n```C\nint mj_write_jpeg_to_memory(\n    mj_jpeg_t *m,\n    unsigned char **memory,\n    size_t *len,\n    int options);\n```\n\nWrite an image to a buffer as a JPEG bytestream. The required memory for the buffer will be allocated and must be free'd after use. `len` holds\nthe length of the buffer in bytes. `options` are encoding features that can be OR'ed:\n\n-   `MJ_OPTION_NONE` - baseline encoding\n-   `MJ_OPTION_OPTIMIZE` - optimize Huffman tables\n-   `MJ_OPTION_PROGRESSIVE` - progressive encoding\n-   `MJ_OPTION_ARITHMETRIC` - arithmetric encoding (overrules Huffman optimizations)\n\n```C\nint mj_write_jpeg_to_file(\n    mj_jpeg_t *m,\n    char *filename,\n    int options);\n```\n\nWrite an image to a file (`filename`) as a JPEG bytestream. The options are the same as for `mj_write_jpeg_to_memory()`.\n\n```C\nvoid mj_free_jpeg(mj_jpeg_t *m);\n```\n\nFree the memory consumed by the JPEG. The jpeg struct can be reused for another image.\n\n### Composition\n\n```C\nint mj_compose(\n    mj_jpeg_t *m,\n    mj_dropon_t *d,\n    unsigned int align,\n    int offset_x,\n    int offset_y);\n```\n\nCompose an image with a dropon. Use these OR'ed values for `align`:\n\n-   `MJ_ALIGN_LEFT` - align the dropon to the left border of the image\n-   `MJ_ALIGN_RIGHT` - align the dropon to the right border of the image\n-   `MJ_ALIGN_TOP` - align the dropon to the top border of the image\n-   `MJ_ALIGN_BOTTOM` - align the dropon to the bottom border of the image\n-   `MJ_ALIGN_CENTER` - align the dropon to the center of the image\n\nUse `offset_x` and `offset_y` to move the dropon relative to the alignment. If parts of the dropon will be outside of the area\nof the image, it will be cropped accordingly, e.g. you can apply a dropon that is bigger than the image.\n\n### Effects\n\n```C\nint mj_effect_grayscale(mj_jpeg_t *m);\n```\n\nConvert the image to grayscale. This only works if the image was stored in YCbCr color space. It will keep all three components.\n\n```C\nint mj_effect_pixelate(mj_jpeg_t *m);\n```\n\nKeep only the DC coefficients from the components. This will remove the details from the image and keep only the \"base\" color of each block.\n\n```C\nint mj_effect_tint(\n    mj_jpeg_t *m,\n    int cb_value,\n    int cr_value);\n```\n\nColorize the image. Use `cb_value` to colorize in blue (positive value) or yellow (negative value). Use `cr_value` to colorize in\nred (positive value) or green (negative value). This only works if the image was stored in YCbCr color space.\n\n```C\nint mj_effect_luminance(\n    mj_jpeg_t *m,\n    int value);\n```\n\nChange the brightness of the image. Use a positive value to brighten or a negative value to darken then image.\nThis only works if the image was stored in YCbCr color space.\n\n### Return values\n\nAll non-void functions return `MJ_OK` if everything went fine. If something went wrong the return value indicates the source\nof error:\n\n-   `MJ_ERR_MEMORY` - failed to allocate enough memory\n-   `MJ_ERR_NULL_DATA` - some provided pointers are NULL\n-   `MJ_ERR_DROPON_DIMENSIONS` - the dimensions of the dropon image and alpha do not correspond\n-   `MJ_ERR_UNSUPPORTED_COLORSPACE` - the JPEG is in an unsupported color space\n-   `MJ_ERR_DECODE_JPEG` - error during decoding the JPEG\n-   `MJ_ERR_ENCODE_JPEG` - error during encoding the JPEG\n-   `MJ_ERR_FILEIO` - error while reading/writing from/to a file\n-   `MJ_ERR_IMAGE_SIZE` - the dimensions of the provided image are too large\n-   `MJ_ERR_UNSUPPORTED_FILETYPE` - the file type of the dropon is unsupported\n\n### Supported color spaces\n\nlibmodjpeg only supports the \"basic\" and most common color spaces in JPEG files: `JCS_RGB`, `JCS_GRAYSCALE`, and `JCS_YCbCr`\n\n## Who is using libmodjpeg\n\n-   [modjpeg-nginx](https://github.com/ioppermann/modjpeg-nginx) - NGINX filter module for adding overlays and logos to JPEGs on-the-fly without degrading the quality of the image.\n\n## License\n\nlibmodjpeg is released under the [MIT license](../master/LICENSE).\n\n## Acknowledgement\n\nMade with :pizza: and :beers: in Switzerland.\n\nThis software is based in part on the work of the Independent JPEG Group.\n\nPNG support is provided by [libpng](http://www.libpng.org/pub/png/libpng.html)\n\n## References\n\n[1] R. Jonsson, \"Efficient DCT Domain Implementation of Picture Masking and Composition and Compositing\", ICIP (2) 1997, pp. 366-369\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fioppermann%2Flibmodjpeg","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fioppermann%2Flibmodjpeg","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fioppermann%2Flibmodjpeg/lists"}