{"id":26023895,"url":"https://github.com/neri/mpic","last_synced_at":"2025-03-06T11:50:40.877Z","repository":{"id":152508211,"uuid":"625352381","full_name":"neri/mpic","owner":"neri","description":"Simple Lossy Compression Image Format for Embedded Platforms","archived":false,"fork":false,"pushed_at":"2024-08-14T05:41:28.000Z","size":2062,"stargazers_count":9,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2024-08-14T06:48:12.652Z","etag":null,"topics":["embedded-graphics","embeded","rust"],"latest_commit_sha":null,"homepage":"","language":"Rust","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/neri.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-04-08T21:06:49.000Z","updated_at":"2024-08-14T05:41:06.000Z","dependencies_parsed_at":null,"dependency_job_id":"a802a623-1669-4067-a4d3-776b360f81c6","html_url":"https://github.com/neri/mpic","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/neri%2Fmpic","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/neri%2Fmpic/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/neri%2Fmpic/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/neri%2Fmpic/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/neri","download_url":"https://codeload.github.com/neri/mpic/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":242206006,"owners_count":20089252,"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":["embedded-graphics","embeded","rust"],"created_at":"2025-03-06T11:50:40.148Z","updated_at":"2025-03-06T11:50:40.865Z","avatar_url":"https://github.com/neri.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# MPIC\n\nSimple Lossy Compression Image Format for Embedded Platforms\n\n![](images/img_3246.jpg)\n\n- [Repository](https://github.com/neri/mpic)\n- [Online API Documents](https://neri.github.io/mpic/mpic/)\n\n## Features\n\n- Simple.\n- Lossy compression.\n- A typical image compression ratio is somewhere between PNG and JPG.\n- Small memory footprint, only a few hundred bytes of stack memory required for decoding.\n- Designed for 16bpp color images and supports `embedded-graphics`; add `features = [\"embedded\"]` to Cargo.toml.\n- Support for `no_std`, No `alloc` is needed for decoding.\n\n## Example Apps\n\n### `/cli`: Command Line File Converter\n\n- Example of a command line application that converts files in MPIC format and other formats such as PNG to each other\n\n```sh\n$ cargo run -p cli INFILE OUTFILE\n```\n\n### `/viewer`: Image Viewer\n\n- Example of a GUI application that displays MPIC format files with `embedded-graphics`\n\n```sh\n$ cargo run -p viewer FILE_NAME\n```\n\n## Other Apps\n\n- [Online Image Viewer](https://github.com/neri/image-viewer)\n\n----\n\n## How it works\n\n- Divide the image into blocks of 8 x 8 pixels.\n- Convert RGB with 8 bits per channel to YUV with 6 bits per channel.\n- Thin out the U and V channels to 1/4.\n- Because the color difference information is thinned out, even in the worst case, the compression is guaranteed to be more than half of the raw bitmap.\n- Finally, lossless compression is performed using the sliding dictionary method.\n- When decoding, these processes are performed in reverse order.\n\n## Comparison with sample images\n\n| Mandrill              | Original Size | Converted PNG Size |                              |\n| --------------------- | ------------- | ------------------ | ---------------------------- |\n| Original 24bit Bitmap | 197KB         | 155KB              | ![](images/Mandrill-org.png) |\n| MPIC                  | 72KB          | 135KB              | ![](images/Mandrill.png)     |\n| JPEG                  | 40KB          | -                  | ![](images/Mandrill.jpeg)    |\n\n| Parrots               | Original Size | Converted PNG Size |                             |\n| --------------------- | ------------- | ------------------ | --------------------------- |\n| Original 24bit Bitmap | 197KB         | 105KB              | ![](images/Parrots-org.png) |\n| MPIC                  | 49KB          | 87KB               | ![](images/Parrots.png)     |\n| JPEG                  | 21KB          | -                  | ![](images/Parrots.jpeg)    |\n\n| Pepper                | Original Size | Converted PNG Size |                            |\n| --------------------- | ------------- | ------------------ | -------------------------- |\n| Original 24bit Bitmap | 197KB         | 117KB              | ![](images/Pepper-org.png) |\n| MPIC                  | 58KB          | 105KB              | ![](images/Pepper.png)     |\n| JPEG                  | 28KB          | -                  | ![](images/Pepper.jpeg)    |\n\n## File Format\n\n### File Header\n\n- All multi-byte data is encoded in little-endian.\n\n```\n#[repr(C, packed)]\npub struct FileHeader {\n    magic: [u8; 4], // b\"\\x00mpi\"\n    width: u16,\n    height: u16,\n    version: u8,    // The current version is 1.\n}\n```\n\n#### Differences between versions\n\n- In version `0`, only multiples of 8 are allowed for `width` and `height`.\n- There is no limit to image size in version `1` or later.\n\n\n### Image Data\n\n- Image data follows the header.\n- Image data is divided into 8 x 8 blocks and stored in chunks.\n- If the image size is not a multiple of 8 x 8, the right and bottom edges are filled with a color interpolated from the surroundings to match a multiple of 8.\n- Number of Chunks = ceil(`width` / 8) * ceil(`height` / 8)\n\n### Chunk\n\n- The first byte of each chunk of data indicates the data size, followed by the payload. The chunk size also indicates how the chunks are compressed.\n- For uncompressed chunks, the data size (96), followed by the 64-byte Y channel, 16-byte U channel, and 16-byte V channel. `96` also serves as an identifier for uncompressed data. In practice, normal encoders do not use this mode.\n- The Y channel stores all 8x8 data, while the U and V channels store only 4x4 pixels. The method of thinning the U and V channels is left to the encoder. The decoder should use nearest-neighbor interpolation to expand them by a factor of 2 in height and width.\n- For a 6-bit compacted chunk, the data size is `72`. The order of the data is the same as for the uncompressed chunk, but the 6 bits of the uncompressed chunk are compacted into 8 bits, so the data size is 3/4 of the uncompressed chunk.\n- If the data size after compression exceeds 72 with other compression methods, the 6-bit compaction method shall be selected.\n\n### Color Conversion Methods\n\n* RGB888 to YUV666\n\n```\n    y = ((66 * r + 129 * g + 25 * b + 128) \u003e\u003e 10) + 4;\n\n    u = (((-38 * r - 74 * g + 112 * b + 128) / 256) + 128) \u003e\u003e 2;\n\n    v = (((112 * r - 94 * g - 18 * b + 128) / 256) + 128) \u003e\u003e 2;\n```\n\n* YUV666 to RGB666 or RGB888\n\n```\nfn u6_to_u8(val) {\n    (val \u003c\u003c 2) | (val \u003e\u003e 4)\n}\n\n    y = u6_to_u8(y - 4);\n    u = (u6_to_u8(u) - 128);\n    v = (u6_to_u8(v) - 128);\n\n    r6 = ((298 * y + 409 * v + 128) \u003e\u003e 10).clamp(0, 63);\n    g6 = ((298 * y - 100 * u - 208 * v + 128) \u003e\u003e 10).clamp(0, 63);\n    b6 = ((298 * y + 516 * u + 128) \u003e\u003e 10).clamp(0, 63);\n\n    r8 = u6_to_u8(r6)\n    g8 = u6_to_u8(g6)\n    b8 = u6_to_u8(b6)\n```\n\n### LZ Compression Data Encoding\n\n| Representation          | Meaning                                                                                                             |\n| ----------------------- | ------------------------------------------------------------------------------------------------------------------- |\n| `00vv_vvvv`             | Raw Value                                                                                                           |\n| `01nn_nnnn` `00mm_mmmm` | Together with the trailing byte value, it indicates the length `(n+3)` and offset `-(m+1)` of the slide dictionary. |\n| `01xx_xxxx` `NNxx_xxxx` | RESERVED (NN!=00)                                                                                                   |\n| `1nnm_mmmm`             | Short form of sliding dictionary, it indicates the length `(n+2)` and offset `-(m+1)`.                              |\n\n----\n\n## License\n\nMIT\n\n(C) 2023 Nerry\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fneri%2Fmpic","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fneri%2Fmpic","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fneri%2Fmpic/lists"}