{"id":48286375,"url":"https://github.com/ghosthack/imageio-native","last_synced_at":"2026-04-04T22:57:22.655Z","repository":{"id":343417604,"uuid":"1177632479","full_name":"ghosthack/imageio-native","owner":"ghosthack","description":"Java ImageIO readers that delegate to platform-native image decoding APIs via Project Panama (FFM API, Java 22+). HEIC, AVIF, WEBP, JPEG 2000, camera RAW, and more through standard ImageIO.read().","archived":false,"fork":false,"pushed_at":"2026-03-10T15:21:19.000Z","size":88,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-03-10T15:54:19.760Z","etag":null,"topics":["avif","heic","image-processing","imageio","java","native","panama","webp"],"latest_commit_sha":null,"homepage":null,"language":"Java","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/ghosthack.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,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-10T08:05:14.000Z","updated_at":"2026-03-10T15:21:26.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/ghosthack/imageio-native","commit_stats":null,"previous_names":["ghosthack/imageio-native"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/ghosthack/imageio-native","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ghosthack%2Fimageio-native","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ghosthack%2Fimageio-native/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ghosthack%2Fimageio-native/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ghosthack%2Fimageio-native/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ghosthack","download_url":"https://codeload.github.com/ghosthack/imageio-native/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ghosthack%2Fimageio-native/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31418276,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-04T20:09:54.854Z","status":"ssl_error","status_checked_at":"2026-04-04T20:09:44.350Z","response_time":60,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["avif","heic","image-processing","imageio","java","native","panama","webp"],"created_at":"2026-04-04T22:57:21.144Z","updated_at":"2026-04-04T22:57:22.643Z","avatar_url":"https://github.com/ghosthack.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# imageio-native\n\n[![CI](https://github.com/ghosthack/imageio-native/actions/workflows/ci.yml/badge.svg)](https://github.com/ghosthack/imageio-native/actions/workflows/ci.yml) [![Javadocs](https://javadoc.io/badge/io.github.ghosthack/imageio-native.svg)](https://javadoc.io/doc/io.github.ghosthack/imageio-native) [![Maven Central](https://img.shields.io/maven-central/v/io.github.ghosthack/imageio-native)](https://central.sonatype.com/artifact/io.github.ghosthack/imageio-native)\n\nJava ImageIO readers that delegate to **platform-native image decoding APIs** via [Project Panama](https://openjdk.org/jeps/454) (Foreign Function \u0026 Memory API, Java 22+).\n\nDrop the JAR on your classpath and `ImageIO.read()` gains support for **HEIC, AVIF, WEBP, JPEG 2000, JPEG XL, camera RAW, PSD, EXR**, and more. No JNI, no native builds, no manual SPI wiring.\n\nDecode only. Still images only (video files yield a single poster frame). All modules are pure Java — they compile on any OS and auto-detect the platform at runtime.\n\n## Quick start\n\nAdd the dependency and the JVM flag:\n\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003eio.github.ghosthack\u003c/groupId\u003e\n    \u003cartifactId\u003eimageio-native\u003c/artifactId\u003e\n    \u003cversion\u003e1.0.2\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\n```\n--enable-native-access=ALL-UNNAMED\n```\n\nThen use standard ImageIO:\n\n```java\nBufferedImage img = ImageIO.read(new File(\"photo.heic\"));\nBufferedImage img = ImageIO.read(new File(\"photo.avif\"));\nBufferedImage img = ImageIO.read(new File(\"photo.webp\"));\n```\n\nAll standard lookup methods work: `getImageReadersByFormatName`, `getImageReadersByMIMEType`, `getImageReadersBySuffix`.\n\nThe `imageio-native` aggregator pulls in both platform modules and auto-selects at runtime. You can also depend on `imageio-native-apple` or `imageio-native-windows` directly.\n\n\u003cdetails\u003e\n\u003csummary\u003eGradle\u003c/summary\u003e\n\n```kotlin\nimplementation(\"io.github.ghosthack:imageio-native:1.0.2\")\n```\n\n\u003c/details\u003e\n\n## Format configuration\n\nThis implementation has modules for two native APIs:\n\n| Module | Platform | Native API | Formats |\n|--------|----------|------------|---------|\n| `imageio-native-apple` | macOS | CGImageSource (Apple ImageIO framework) | 60+ |\n| `imageio-native-windows` | Windows 10+ | Windows Imaging Component (WIC) | 30+ |\n\nControlled by the system property `imageio.native.formats`:\n\n| Value | Behaviour |\n|-------|-----------|\n| `supplemental` (default) | Only formats Java can't decode natively. JPEG/PNG/GIF/BMP/TIFF are left to Java's built-in readers. |\n| `all` | Every format the platform can decode, including JPEG/PNG/GIF/BMP/TIFF. |\n| `none` | Disabled entirely. |\n| comma-separated list | Explicit whitelist, e.g. `heic,avif,webp,jp2`. |\n\n### Supported formats (supplemental defaults)\n\n**Both platforms:** HEIC, HEIF, AVIF, WebP, DNG, CR2, CR3, NEF, ARW, ICO, CUR, DDS, and many more camera RAW formats\n\n**Apple-only:** JPEG 2000, JPEG XL, PSD, OpenEXR, Radiance HDR, DICOM, ICNS, TGA, SGI, PBM/PGM/PPM, PICT, MPO, KTX, KTX2, ASTC, PVR, ATX\n\n**Windows-only:** JPEG-XR (JXR/WDP/HDP)\n\n### Windows codec requirements\n\n| Format | Requirement |\n|--------|-------------|\n| HEIC/HEIF | [HEVC Video Extensions](https://apps.microsoft.com/detail/9nmzlz57r3t7) from Microsoft Store |\n| AVIF | [AV1 Video Extensions](https://apps.microsoft.com/detail/9mvzqvxjbq9v) from Microsoft Store |\n| WebP | Built-in (Windows 10 1809+) |\n| JPEG-XR | Built-in |\n\nTo check whether the required codecs are already installed:\n\n```powershell\nGet-AppxPackage -Name *hevc*   # HEVC (for HEIC/HEIF)\nGet-AppxPackage -Name *av1*    # AV1 (for AVIF)\n```\n\n\u003e **Minimum image dimensions:** The HEVC and AV1 codec extensions cannot decode very small images. HEIC/HEIF requires at least 8×8 pixels and AVIF requires at least 8×8 pixels. Smaller images will fail with `E_INVALIDARG` during pixel decoding even though header parsing and format detection succeed. This is a limitation of the Windows codec extensions, not of WIC or this library.\n\n## Runtime detection\n\nTo check at runtime whether imageio-native is on the classpath (e.g. when it's an optional dependency), probe a class from the `imageio-native-common` module — it's a transitive dependency of every platform module, so it's always present regardless of which artifact was included:\n\n```java\nboolean available = false;\ntry {\n    Class.forName(\"io.github.ghosthack.imageio.common.FormatRegistry\");\n    available = true;\n} catch (ClassNotFoundException ignored) { }\n```\n\nTo detect a specific platform module, check its SPI class:\n\n```java\n// macOS (imageio-native-apple)\nClass.forName(\"io.github.ghosthack.imageio.apple.AppleImageReaderSpi\");\n\n// Windows (imageio-native-windows)\nClass.forName(\"io.github.ghosthack.imageio.windows.WicImageReaderSpi\");\n```\n\n## Optional backends\n\nThe `imageio-native-vips` module is an optional backend that delegates to [libvips](https://www.libvips.org/) for image decoding. It is **not** included in the `imageio-native` aggregator -- add it explicitly to opt in.\n\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003eio.github.ghosthack\u003c/groupId\u003e\n    \u003cartifactId\u003eimageio-native-vips\u003c/artifactId\u003e\n    \u003cversion\u003e1.0.2\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\nRequires libvips installed on the system:\n\n```sh\n# macOS (MacPorts)\nsudo port install vips\n\n# macOS (Homebrew)\nbrew install vips\n\n# Debian/Ubuntu\nsudo apt install libvips-dev\n```\n\nThe vips backend adds cross-platform support (macOS, Linux, Windows) for HEIC, AVIF, WebP, JPEG 2000, PDF, SVG, EXR, FITS, Netpbm, HDR, and more -- depending on the libvips build configuration. It respects the `imageio.native.formats` property (supplemental mode by default).\n\nThe SPI declares a fixed set of common formats. Formats not in the list but supported by the installed libvips can still be decoded via the direct `VipsNative` API -- they just won't be auto-discovered by `ImageIO.read()`.\n\n### ImageMagick\n\nThe `imageio-native-magick` module is an optional backend that delegates to [ImageMagick 7](https://imagemagick.org/) (MagickWand C API). Supports 200+ formats including EPS, PSD, XCF, DPX, TGA, PCX, XBM/XPM, and more.\n\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003eio.github.ghosthack\u003c/groupId\u003e\n    \u003cartifactId\u003eimageio-native-magick\u003c/artifactId\u003e\n    \u003cversion\u003e1.0.2\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\nRequires ImageMagick 7 installed on the system. Both Q16 and Q16HDRI builds are supported.\n\n```sh\n# macOS (MacPorts)\nsudo port install ImageMagick7\n\n# macOS (Homebrew)\nbrew install imagemagick\n\n# Debian/Ubuntu\nsudo apt install libmagickwand-7-dev\n```\n\n### Backend priority\n\nWhen multiple backends are on the classpath (e.g. platform-native + vips), the consumer controls which backend handles each format via system properties:\n\n```\n# Global ordering (left = highest priority). Default: native,vips,magick,ffmpeg\n-Dimageio.native.backend.priority=native,vips,magick,ffmpeg\n\n# Per-format override\n-Dimageio.native.backend.priority.jpeg=vips,native\n-Dimageio.native.backend.priority.tiff=vips\n```\n\nWith no properties set, the default ordering is: platform-native first, then vips, then magick, then ffmpeg. Existing users see no change when adding optional backends -- they only activate for formats the higher-priority backends can't handle.\n\n## Video poster frames\n\nThe optional `imageio-native-video` module extracts a **single still image** from a video file -- the same way the image modules decode a still image from a HEIC or WebP file. The output is always a `BufferedImage`; no video playback, no audio, no frame sequences.\n\nThis means `ImageIO.read(new File(\"clip.mp4\"))` works exactly like `ImageIO.read(new File(\"photo.heic\"))` -- same API, same result type.\n\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003eio.github.ghosthack\u003c/groupId\u003e\n    \u003cartifactId\u003eimageio-native-video\u003c/artifactId\u003e\n    \u003cversion\u003e1.0.2\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\nThrough the standard ImageIO SPI:\n\n```java\n// Poster frame via ImageIO -- identical to reading any image\nBufferedImage poster = ImageIO.read(new File(\"clip.mp4\"));\n```\n\nOr through the direct API for more control:\n\n```java\n// Thumbnail (poster frame at or near t=0)\nBufferedImage thumb = VideoFrameExtractor.extractThumbnail(Path.of(\"clip.mp4\"));\n\n// Frame at a specific time\nBufferedImage frame = VideoFrameExtractor.extractFrame(\n        Path.of(\"clip.mp4\"), Duration.ofSeconds(30));\n\n// Video metadata (dimensions, duration, codec, frame rate)\nVideoInfo info = VideoFrameExtractor.getInfo(Path.of(\"clip.mp4\"));\n```\n\n| Module | Platform | Native API | Containers |\n|--------|----------|------------|------------|\n| `imageio-native-video-apple` | macOS | AVFoundation (AVAssetImageGenerator) | MP4, MOV, M4V, 3GP |\n| `imageio-native-video-windows` | Windows 10+ | Media Foundation (IMFSourceReader) | *In progress* |\n| `imageio-native-video-ffmpeg` | Any (optional) | FFmpeg libavformat/libavcodec | All FFmpeg-supported containers |\n\nThe Windows video backend is not yet complete -- `isAvailable()` returns `false` until the implementation is finished. See `TODO-windows.md` for details.\n\n### FFmpeg video backend\n\nThe `imageio-native-video-ffmpeg` module is an optional cross-platform video backend. It works on any OS where FFmpeg libraries are installed, including **Linux** (the only video backend available on Linux).\n\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003eio.github.ghosthack\u003c/groupId\u003e\n    \u003cartifactId\u003eimageio-native-video-ffmpeg\u003c/artifactId\u003e\n    \u003cversion\u003e1.0.2\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\n```sh\n# macOS (MacPorts)\nsudo port install ffmpeg\n\n# macOS (Homebrew)\nbrew install ffmpeg\n\n# Debian/Ubuntu\nsudo apt install libavformat-dev libavcodec-dev libswscale-dev libavutil-dev\n```\n\nStruct offsets are version-specific. Currently supports FFmpeg 4.x (libavcodec major 58). The backend detects the FFmpeg version at runtime via `avcodec_version()` and disables itself if the version is not recognized. Additional version support can be added by providing offset tables.\n\n## Architecture\n\n```\nImageIO.read(file)\n    │\n    ▼\nImageReaderSpi              one universal SPI per platform\n    │ canDecodeInput():\n    │   1. skip Java-native formats in \"supplemental\" mode\n    │   2. probe via native API (CGImageSource / WIC)\n    ▼\nImageReader                 lazy decode + cache\n    │\n    ▼\n┌─────────────────────┬──────────────────────────────┐\n│ macOS               │ Windows                      │\n│ AppleNative         │ WicNative                    │\n│ Panama downcalls    │ Panama COM vtable dispatch   │\n│ CoreGraphics +      │ ole32 + windowscodecs        │\n│   ImageIO.framework │ IWICImagingFactory →         │\n│ CGImageSource →     │   IWICStream → Decoder →     │\n│   CGBitmapContext → │   FormatConverter →          │\n│   pixel copy        │   CopyPixels                 │\n└─────────────────────┴──────────────────────────────┘\n    │\n    ▼\nBufferedImage (TYPE_INT_ARGB_PRE)\n```\n\nBoth platforms output BGRA premultiplied pixels that map directly to `TYPE_INT_ARGB_PRE` when read as little-endian ints — zero pixel conversion overhead.\n\nOne universal SPI per platform means `canDecodeInput` delegates to the native API to probe headers, so any format the OS adds in a future update works automatically. Both modules compile on all OSes; native loading is guarded by OS checks.\n\n## EXIF orientation\n\nImages from phones and cameras often carry an EXIF orientation tag (values 1-8) that describes how the sensor image should be rotated or flipped for correct display. Both backends apply this transform automatically during decode so the returned `BufferedImage` is always display-ready.\n\n| EXIF value | Transform |\n|------------|-----------|\n| 1 | None (identity) |\n| 2 | Flip horizontal |\n| 3 | Rotate 180 |\n| 4 | Flip vertical |\n| 5 | Rotate 90 + flip horizontal |\n| 6 | Rotate 90 |\n| 7 | Rotate 270 + flip horizontal |\n| 8 | Rotate 270 |\n\n**macOS (CGImageSource):** Uses `CGImageSourceCreateThumbnailAtIndex` with `kCGImageSourceCreateThumbnailWithTransform = true` and the thumbnail size set to the full image dimensions. CoreGraphics applies the EXIF transform internally during hardware-accelerated decode -- same decode path, same performance, correct orientation.\n\n**Windows (WIC):** Reads the EXIF orientation tag via `IWICMetadataQueryReader`, then inserts an `IWICBitmapFlipRotator` between the frame decoder and the format converter. The flip-rotator is a zero-copy coordinate remap -- it transforms pixel coordinates during `CopyPixels` rather than allocating a second buffer. For orientation 1 (no rotation), the flip-rotator is skipped entirely.\n\nBoth `getSize()` and `decode()` are orientation-aware: dimensions are swapped for orientations 5-8 (90/270 rotations), so width and height always reflect the display-oriented image.\n\n**Performance impact:** Negligible. Both platforms apply the transform as part of the existing decode pipeline with no extra buffer allocations or pixel copies. The macOS path creates a small options dictionary per decode call; the Windows flip-rotator is a zero-copy coordinate remap. Orientation 1 (the common case for non-phone images) skips the transform entirely.\n\n## Project structure\n\n```\n├── pom.xml                          parent POM (reactor)\n├── imageio-native-common/           shared format registry \u0026 detection\n├── imageio-native-apple/            macOS image module\n├── imageio-native-windows/          Windows image module\n├── imageio-native/                  cross-platform image aggregator\n├── imageio-native-video-common/     shared video SPI \u0026 format detection\n├── imageio-native-video-apple/      macOS video module (AVFoundation)\n├── imageio-native-video-windows/    Windows video module (Media Foundation)\n├── imageio-native-video/            cross-platform video aggregator\n├── imageio-native-vips/             optional libvips backend\n├── imageio-native-magick/           optional ImageMagick 7 backend\n├── imageio-native-video-ffmpeg/     optional FFmpeg video backend\n├── scripts/                         test fixture generators\n└── example-consumer/                standalone demo (not in reactor)\n```\n\n## Building\n\nRequires Java 22+ and Maven 3.9+.\n\n```sh\nmvn clean test                        # compile + test\nmvn install -DskipTests               # install to local repo\nmvn -f example-consumer/pom.xml test  # example-consumer\n\nswift scripts/generate-heic-avif-cgimage.swift   # HEIC + AVIF (macOS CGImage)\n./scripts/generate-png-webp-chrome.sh            # PNG + WebP  (Chrome headless)\npython scripts/generate-all-pillow.py            # all formats (pip: pillow + plugins)\nswift scripts/generate-video-fixtures.swift      # video fixtures (macOS AVFoundation)\n./scripts/generate-video-bframes.sh              # B-frame video fixture (ffmpeg)\n```\n\n## Releasing\n\n1. Set the release version in `pom.xml` (remove `-SNAPSHOT`)\n2. Commit, push, and merge via PR\n3. CI detects the version change, creates a GitHub release, and deploys to Maven Central\n4. Publish the deployment at https://central.sonatype.com/publishing/deployments\n\n## License\n\n[MIT](LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fghosthack%2Fimageio-native","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fghosthack%2Fimageio-native","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fghosthack%2Fimageio-native/lists"}