{"id":45803809,"url":"https://github.com/lexluthor0304/negativeconverter","last_synced_at":"2026-06-11T02:01:35.624Z","repository":{"id":284512123,"uuid":"955172143","full_name":"lexluthor0304/NegativeConverter","owner":"lexluthor0304","description":null,"archived":false,"fork":false,"pushed_at":"2026-05-27T01:54:53.000Z","size":8320,"stargazers_count":33,"open_issues_count":0,"forks_count":4,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-05-27T03:23:22.646Z","etag":null,"topics":["film","javascript","linux","macos","negative","photography","tauri","windows"],"latest_commit_sha":null,"homepage":"https://negative-converter.tokugai.com","language":"JavaScript","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/lexluthor0304.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":"2025-03-26T08:14:47.000Z","updated_at":"2026-05-27T01:50:26.000Z","dependencies_parsed_at":"2026-02-11T07:07:03.995Z","dependency_job_id":"e21cd032-ad2d-4b30-9d56-ce3f1001f179","html_url":"https://github.com/lexluthor0304/NegativeConverter","commit_stats":null,"previous_names":["lexluthor0304/negativeconverter"],"tags_count":62,"template":false,"template_full_name":null,"purl":"pkg:github/lexluthor0304/NegativeConverter","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lexluthor0304%2FNegativeConverter","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lexluthor0304%2FNegativeConverter/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lexluthor0304%2FNegativeConverter/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lexluthor0304%2FNegativeConverter/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lexluthor0304","download_url":"https://codeload.github.com/lexluthor0304/NegativeConverter/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lexluthor0304%2FNegativeConverter/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34178819,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-11T02:00:06.485Z","response_time":57,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":["film","javascript","linux","macos","negative","photography","tauri","windows"],"created_at":"2026-02-26T13:05:34.581Z","updated_at":"2026-06-11T02:01:35.618Z","avatar_url":"https://github.com/lexluthor0304.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Film Negative → Positive Converter\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"img/SAMPLE.jpg\" alt=\"Negative Converter sample output\" width=\"100%\"\u003e\n\u003c/p\u003e\n\nNegative Converter helps film photographers turn scanned or camera-digitized film into clean, natural-looking positives with a workflow made for everyday editing. It brings frame cleanup, film-aware conversion, roll consistency, finishing controls, and export into one focused workspace.\n\nWhether you are reviewing a fresh roll, restoring family negatives, or preparing a consistent set for sharing, the app is designed to make film conversion feel fast, approachable, and dependable while keeping your photos private.\n\n## 🌟 Features\n\n- 📷 **Supports PNG/JPG file uploads** (including 16-bit PNGs via UPNG.js and .cr2, .nef, .arw, .dng, .raw, .rw2 raw files via LibRaw-Wasm)\n- 🔄 **Rotation correction** via slider or number input\n- ✂️ **Visual cropping** with drag-and-drop overlay\n- ⚖️ **One-click white balance** by clicking a gray area in the image\n- 🎛️ **Live controls** for:\n  - Temperature \u0026 Tint\n  - Vibrance \u0026 Saturation\n  - Cyan / Magenta / Yellow (CMY) channels\n- 🎞️ **Film presets** for color negative, B\u0026W negative, and positive slide stocks across Kodak / Fujifilm / Ilford\n- 🗂️ **Data-driven preset system** loaded from `negative2positive/presets/film_presets.json` (supports alias fallback for older preset IDs)\n- 🔍 **Optional lens profile workflow**: search/select Lensfun profiles manually, or skip lens correction and continue\n- 🧷 **Roll-level lens settings**: lens correction on/off and parameters can be applied to selected files or reused via roll reference\n- 🛡️ **Privacy-friendly**: all image processing happens locally in your browser\n- 💾 **Flexible export**: PNG / JPEG / TIFF with selectable bit depth (8-bit, plus 16-bit for PNG/TIFF)\n\n## 🚀 How to Use\n\n### Workflow (Step 1 → 3)\n\n1. **Step 1: Crop**\n   - Rotate / Auto Frame / Crop until only the film area remains\n   - Click **Next: Film Settings** (negatives) or **Next: Positive Mode** (slides)\n2. **Step 2: Film Settings**\n   - Pick film type: **Color**, **B\u0026W**, or **Positive**\n   - **Color negatives**: set the mask baseline (sample manually / auto-detect / roll reference)\n   - Click **Next: Convert and Continue**\n3. **Step 3: Adjust \u0026 Export**\n   - White balance + sliders + curves to taste\n   - Export PNG / JPEG / TIFF\n\n### Film type quickstart\n\n- **Color negative**: Step 1 → **Next: Film Settings** → keep **Color** → set mask → **Next: Convert and Continue** → Step 3\n- **B\u0026W negative**: Step 1 → **Next: Film Settings** → select **B\u0026W** → **Next: Convert and Continue** (no mask) → Step 3\n- **Positive slide**: Step 1 → **Next: Positive Mode** (or select **Positive** in Step 2) → **Next: Convert and Continue** → Step 3\n\n### Batch workflow (multiple files)\n\n1. Click **Add** and choose multiple images (File List appears)\n2. Process one frame fully to Step 3\n3. Use **Save Settings** for the current frame, or **Apply to Selected** for roll-wide settings\n4. (Optional) Use **Set Current as Reference** + **Apply Reference to Selected** for roll reference\n5. Export via **Export All (ZIP)** or **Download All Individually**\n\n### Guided Mode\n\n- The Workflow panel includes a **Guide** toggle to show/hide in-app instructions (stored in localStorage)\n\n## ⚙️ Technical Highlights\n\n- Uses [`UPNG.js`](https://github.com/photopea/UPNG.js) to decode 16-bit PNGs  \n- Uses a custom WebAssembly module based on [`LibRaw-Wasm`](https://github.com/ybouane/LibRaw-Wasm) to support `.cr2`, `.nef`, `.arw`, `.dng`, `.raw`, `.rw2` formats  \n- Uses UTIF.js + an in-app PNG encoder path to support TIFF export and 16-bit PNG/TIFF output options  \n- Includes a simplified AHD demosaicing algorithm for Bayer-pattern raw data  \n- Color adjustment logic is based on RGB ↔ HSL and RGB ↔ CMY conversions  \n- Film preset metadata is loaded from JSON and grouped dynamically by film type in the UI  \n- Optional lens correction uses [`@neoanaloglabkk/lensfun-wasm`](https://www.jsdelivr.com/package/npm/@neoanaloglabkk/lensfun-wasm) with **npm local assets first + CDN fallback**  \n- Auto frame detection uses [`@techstark/opencv-js`](https://www.npmjs.com/package/@techstark/opencv-js) loaded dynamically from the npm package asset URL  \n- Performance optimizations include:\n  - Cached DOM access\n  - Offscreen canvas reuse\n  - Throttled rendering with `requestAnimationFrame`\n\n## Live Demo\n\n[Film Negative → Positive Converter](https://negative-converter.tokugai.com)\n\n## 🖥️ Desktop App (Tauri)\n\nThis repo includes a Tauri wrapper to package the web app as an offline desktop application for Windows / macOS / Linux.\n\n### Development\n\n```bash\nnpm ci\nnpm run dev:web\n```\n\n### Vercel deployment (important)\n\nThis app must be deployed from the **Vite build output**, not by serving source files directly.\n\nRequired settings:\n\n- Root Directory: repository root\n- Install Command: `npm ci`\n- Build Command: `npm run build:web`\n- Output Directory: `dist`\n\n`npm run build:web` generates `negative2positive/dist` (for local/Tauri) and also syncs it to root `dist` (for Vercel output pickup).\n\nIf Vercel serves `negative2positive/index.html` directly, module imports like `pako` / `utif` / `jszip` will not resolve in browser and upload buttons can stop working.\n\n### Desktop dev (Tauri)\n\n```bash\nnpm run tauri:dev\n```\n\n### Build installers\n\n```bash\nnpm run tauri:build\n```\n\nBuild outputs are placed under:\n- `src-tauri/target/release/bundle/`\n\n### macOS installation troubleshooting\n\nIf macOS shows **\"Negative Converter is damaged and can't be opened\"**, this is because the app is not yet notarized by Apple. Use one of these methods:\n\n**Method 1 — Terminal command (recommended):**\n```bash\nxattr -cr /Applications/Negative\\ Converter.app\n```\n\n**Method 2 — Right-click open:**\nRight-click (or Control-click) the app → select **Open** → click **Open** in the confirmation dialog.\n\n**Method 3 — System Settings:**\nGo to **System Settings → Privacy \u0026 Security**, scroll down and click **Open Anyway** next to the blocked app message.\n\n### Linux AppImage troubleshooting\n\n- Run AppImage directly, not with `sudo`.\n- The desktop app now applies AppImage-only runtime guards:\n  - isolates GIO module loading to avoid host `gvfs`/GLib ABI mismatches\n  - standard AppImage keeps DMABUF when render nodes are usable, and auto-falls back when not\n  - compatibility AppImage (`*_legacy-glibc235.AppImage`) defaults DMABUF off for startup stability\n- Optional override for DMABUF behavior:\n  - force enable: `NEGATIVE_CONVERTER_DMABUF=on ./Negative\\ Converter*.AppImage`\n  - force disable: `NEGATIVE_CONVERTER_DMABUF=off ./Negative\\ Converter*.AppImage`\n- If startup still fails on older distros, use the compatibility AppImage (`*_legacy-glibc235.AppImage`).\n\n### Release (GitHub Actions)\n\n1. Update versions:\n   - `src-tauri/tauri.conf.json`\n   - `src-tauri/Cargo.toml`\n2. Merge to `main`\n3. GitHub Actions automatically:\n   - creates a `vX.Y.Z` tag\n   - publishes a GitHub Release with the installers\n   - (optional) syncs installers to Cloudflare R2 under `negative-converter/release/vX.Y.Z/`\n\n#### Cloudflare R2 sync (optional)\n\nIf you want the release workflow to upload installers to R2, add **one** of these GitHub Actions secret sets:\n\n**Option A: R2 S3 API token**\n\n- `R2_ACCESS_KEY_ID`\n- `R2_SECRET_ACCESS_KEY`\n- `R2_BUCKET`\n- `R2_ENDPOINT` (e.g. `https://\u003caccountid\u003e.r2.cloudflarestorage.com/`)\n\n**Option B: Cloudflare API token (no S3 keys)**\n\n- `CLOUDFLARE_API_TOKEN`\n- `CLOUDFLARE_ACCOUNT_ID`\n- `R2_BUCKET`\n\n## 💡 Development \u0026 Contributions\n\nFeel free to fork, open issues, or submit pull requests with ideas or improvements.  \nThis tool is designed to be simple, fast, and modifiable.\n\n## 📄 License\n\nMIT License\n\n## 🙏 Acknowledgments\n\nSpecial thanks to [LibRaw-Wasm by ybouane](https://github.com/ybouane/LibRaw-Wasm),  \nwhich made it possible to support various raw image formats such as `.cr2`, `.nef`, `.arw`, `.dng`, `.raw`, and `.rw2` directly in the browser via WebAssembly.  \nYour work was an essential reference and greatly accelerated development.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flexluthor0304%2Fnegativeconverter","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flexluthor0304%2Fnegativeconverter","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flexluthor0304%2Fnegativeconverter/lists"}