mirror of
https://github.com/storytold/photocraft.git
synced 2026-10-28 20:07:05 -06:00
sonar-fix/enforce-https
19
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
8a6af66cf6 |
DNG: decode lossy (JPEG-compressed) DNG tiles (FILE-215-11) (#5112)
* TIFF: read and write CIE L*a*b* (CIELab, ICCLab); Lab documents stay Lab Photoshop saves Lab documents as CIELab TIFFs (PhotometricInterpretation 8, a* and b* signed), which PhotoCraft could not open, and PhotoCraft saved Lab documents as sRGB TIFFs. - codecs: a Lab channel layout (Lab, LabA) holding a* and b* as PhotoCraft's Lab documents do ((a + 128) / 255; 16 bits like the PSD importer), with its own Lab <-> sRGB conversion (same formulas as photocraft-color; codecs stays standalone) for formats without Lab, which now warn LabConverted. - TIFF read: CIELab and ICCLab (8 and 16 bits, alpha) are handed to the tiff crate as RGB and labelled Lab; a* and b* are converted from the file's encoding (signed a / 256a, or offset). - TIFF write: Lab and LabA as CIELab, as Photoshop writes it; float Lab is written with 16 bits (DepthReduced). - io: a Lab TIFF opens as a Lab document; a Lab document with one plain layer is written to TIFF in Lab (other formats still use sRGB). Photoshop 2025's Lab TIFFs open with the colours Photoshop shows (its own RGB conversion matches within one level at 8 bits and exactly at 16 bits), and Photoshop reads PhotoCraft's Lab TIFFs back with the same L*a*b* values. Fixes #4936 * DNG: decode lossy (JPEG-compressed) DNG tiles (FILE-215-11) DNG 1.4 lossy JPEG (compression 34892) stores each tile or strip as a baseline-DCT JPEG of 8-bit samples: a gray stream for CFA data, a YCbCr stream for three-sample LinearRaw. The decoder arm from #2021 read the streams but widened every sample by 257, so the values no longer matched the file's levels: an 8-bit WhiteLevel (or its default, 255) clipped everything to white and a 256-entry LinearizationTable was indexed past its end. It also required the JPEG frame to match the clipped row count, which rejected the padded edge tiles real tiled files carry. The arm is now `lossy_jpeg()` in sensor.rs. The decoded 8-bit values are the stored samples, and BlackLevel, WhiteLevel (default 255 for 8-bit data) and the LinearizationTable apply to them exactly as for every other compression. A tile may be coded at the full tile size (edge tiles padded, as the spec says) or at the image's row count; a strip at its row count; the width must be the segment's. BitsPerSample must be 8 and the JPEG's component count must equal SamplesPerPixel (1 or 3); anything else is an error, never a silent conversion. The frame header is checked before any pixel is decoded and the JPEG decoder's own size cap is the segment the IFD declares (already charged against the limits), so a hostile SOF cannot size a buffer past the tile. testgen writes true 8-bit lossy fixtures (`LossyJpegTiles`, and strips via the new `LossyJpegStrips`), accepts pre-encoded `lossy_segments` for hostile-input tests, exposes `baseline_jpeg()`, and leaves WhiteLevel out when `white` is 0. The DNG tests cover CFA and LinearRaw RGB from tiles and strips at 37x23 (partial edge tiles, a short last strip), the default white level, black/white scaling and a LinearizationTable against the uncompressed path, and hostile inputs: a truncated tile, frame-size and component-count mismatches, a 65535x65535 SOF under default and tight limits, a tile over the allocation budget, and 12/16-bit or 4-sample files. The hostile mutation set gains a lossy CFA and a lossy LinearRaw file. The parity doc's DNG row is updated; the scorecard row is left for the coordinating branch. * Raw: measure the Nikon D90 image area (4288x2848 at (10, 10)) Aligning the decoded D90 sensor plane with the camera's own JPEG puts the image at (10, 10) in the 4352x2868 sensor plane, giving the camera's 4288x2848 frame (10-pixel margins; the right border reads the camera's flat 255 from column 4310). Add the measured ImageArea so a D90 NEF opens on the camera's frame instead of the uncropped plane, and make a measured profile leave a file that carries its own colour calibration (a DNG) untouched. * Codecs: open Cineon (.cin) film frames read-only Cineon is the 10-bit logarithmic film-scan format DPX grew from. Read it from the same Kodak header family: the generic header (big-endian, orientation at 192, channel count at 193, the channel-1 bits/dimensions), one or three channels, three 10-bit samples packed into each 32-bit word on a word boundary. The 10-bit code values open as U16 (round(c x 65535 / 1023)); the file's log black/white points are a display choice and are not applied. A depth other than 10 bits or a channel count other than 1/3 is Unsupported; truncation errors rather than panicking. Read-only (no Cineon writer), so .cin joins the open filters and Cineon read closes part of #1049's G10 gap. * Vector: bound flattening memory, keep over-budget dashes, cheaper edge build (#5195) - flatten: a path's flattened points are capped at 1 << 20; past it every curve keeps a proportional share of its pieces (at least one), so hostile handles from files (vmsk, SVG, effect outlines) cannot exhaust memory. fill_rasterizer flattens the whole path under one budget. - stroke: past the dash step budget (or below coordinate precision), dash_polyline keeps the dashes made so far and draws the rest solid instead of dropping the stroke. Open polylines are no longer copied. - raster: coordinates are clamped to +-1e15 and edges with a non-finite slope (vanishingly short) are skipped, so huge finite coordinates no longer produce NaN coverage. fill_rasterizer sorts edges once instead of after every component (stable sort, identical order). - Cleanups: dead miter `half`, dead loop-length check in trace_mask, shared mask render body, map_knots helper, shared straight-segment test. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * PSD: cap pattern memory across a parse, reject mixed-depth pattern channels (#5198) - Patterns: one PatternBudget (decoded bytes + zero padding) is shared by every pattern of a block, a .pat file and the pattern blocks of one document; .abr patt sections are charged to the samples' budget. - Patterns: a channel whose depth differs from the first is rejected (the parsed pattern could not be written back). - Slices: descriptor numbers no longer wrap; out-of-range ids are no id, coordinates clamp to i32, and a v6 trailing entry without a usable id attaches its outsets to no slice. - LayerInfo::unpadded_len is computed from field sizes instead of serializing every record into a scratch buffer. - abr desc presets are moved out instead of cloned; UnFl and shmd counts use the bounded preallocation; ImageData::decode_channel (decoded every plane to return one, unused outside its test) is removed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Vector: bound stroke outline points (#5211) - stroke_polygons dashes all lines first, then counts round caps and joins. When their full-resolution tessellation would exceed 1 << 20 outline points, every round cap and join gets the same smaller segment count (down to 4 per circle, 1 per join; area-exact radius kept). Strokes under the budget are unchanged; no piece is dropped. - The dash step budget (100000) is now shared by all lines of a stroke instead of per polyline, so many subpaths cannot multiply the dash (and cap) count. Past it the rest is drawn solid, as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * PSD: write tiff blocks without copies, inflate merged ZIP once - ImageSourceData::to_bytes writes the global mask and global blocks straight from the model with 4-byte alignment instead of cloning each block (data included) to set its padding. Same bytes. - The tiff transcoder's nested regions and blocks write into the parent's buffer (taken and handed back, error or not) instead of building their own output and copying it up per nesting level. Bodies that fail or are not transcoded are truncated before being copied as stored or dropped. Same bytes. - PsdFile::from_bytes_and_merged decodes the merged image with the parse when asked: a ZIP stream is inflated once (the decode's stream check is the parse's); the parse-only check runs only when the decode fails, to keep from_bytes' errors. PSD import uses it with merged_needed. - PsdBuilder::composite documents that Gray/CMYK composites stay straight. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Content-Aware Fill: measure a thin hole by its thickness, not its bounds (#1366) Removing long, thin objects (coiled guitar strings, wires) left their outline as darker and lighter blotches and copied pieces of other strings into the fill. The pyramid shrank until the hole's bounding box was four patches across: for a string across the image that is a level where the hole has swallowed its surroundings, so the matches found there copy whatever looks like the blur, and the finer levels (refine-only for wide bounds) keep them. A hole at most a quarter as thick as its bounds are long now counts as thin: the pyramid stops while it is still about two patches thick, and that level starts from a membrane across it (over a few pixels, the surroundings themselves) instead of the onion peel, whose exhaustive search is what made a shallow pyramid unaffordable. Compact holes are unchanged bit for bit. Refs #1366 * perf(compose,gpu): effect caches reserve tile addresses instead of pinning layers Effect map cache entries kept a clone of their layer (a deep clone for groups) so the tile addresses the cache key hashes couldn't be reused. That kept an edited or deleted layer's pixels alive, uncounted, until the entry was evicted. Keep a Weak to each hashed tile instead: it reserves the address without keeping the pixels. The GPU compositor's styled-group pins and shape-source tile snapshots do the same. Part of #4530. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(history): count snapshot-only tiles against the history budget History panel snapshots outlived every budget: a Merged Layers snapshot is a full flattened copy that neither the history budget, the GPU budget's document bytes nor Purge accounted for. Trim now counts the tiles only snapshots hold as fixed cost, the GPU budget includes them, and Purge > Histories no longer reports tiles a kept snapshot still holds as freed. Part of #4530. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Content-Aware Fill: every part of a thin selection keeps its surroundings (#1366) Two more causes of ghosts when removing strings and the small knobs selected with them: * Auto sampling summarises the selection's surroundings by a few clusters. Around a long selection those come from the strings' surroundings, so a small part elsewhere (a tuning knob in a differently coloured corner) lost its own surroundings and was filled from far away in another colour. Around a thin selection the Auto area now always reaches as far as the selection is thick. * The membrane that starts a thin hole now starts a patch radius out, past the anti-aliased fringe or halo that a selection drawn close to the object leaves outside it. Refs #1366 * Content-Aware Fill: peel a thin hole where that stays affordable (#1366) Starting every thin hole from a membrane smoothed Spot Healing on film grain (spot_healing_keeps_film_grain_through_overlapping_strokes: grain 9.0 against 12.1 around). The onion peel copies the grain; it was only given up because its exhaustive search is too slow on the big level a long thin hole stops at. It now starts the thin hole whenever hole cells times sources stay within 2^28 comparisons, and the membrane only beyond. Refs #1366 * Select Subject: keep subject parts coloured like the backdrop, leave enclosed gaps open Users on #2788 reported Select Subject dropping clothes that are close in colour to a plain wall and bridging the gap between an arm and the torso. Both come from the same place: the saliency pass labels low-contrast parts of the subject as background, the background colour model then learns them and explains them away, and the final blind hole fill closes every enclosed gap up to 5 % of the mask. Two changes in crates/algo: - A second GrabCut pass whose background model only knows the backdrop. A colour model fitted to the image frame (minus a margin around the salient blob, in case the subject runs out of the picture) marks every pixel it explains as probable background; the blob and whatever the backdrop cannot account for start as probable foreground. Only components that touch the first pass's result survive. - Hole filling that consults the colour models: specks (<= 0.2 % of the mask) always fill, larger holes up to 5 % fill only when their pixels fit the foreground model better than the background's. Tests: two regressions (a cream-shouldered figure on a near-white wall keeps its shoulders; a ring keeps its backdrop-coloured hole open), plus ignored timing and picture-dump helpers. The 6 MP first run goes from about 100 ms to about 200 ms on one core; the P29 repeat path is untouched. * Keyboard shortcut sets: Set menu, New Set, Save, Delete, .kys Export and Summarize (K2) Edit › Keyboard Shortcuts gets Photoshop's set management. A set is a saved copy of the bindings (Preferences.shortcutSets, the one in use in shortcutSet; "" is PhotoCraft Defaults), and the Set menu shows "(modified)" when the bindings differ from it. New Set…, Save and Delete wait for OK like the rest of the dialog. Export… writes the dialog's bindings as a .kys file (commands then tools, Photoshop's macOS key spelling), each row carrying our id as pc-id so PhotoCraft imports its own sets back exactly, whatever the label; Summarize… writes Photoshop's HTML list of every bound command by menu. edit.keyboardShortcuts takes useSet (before the call's edits), saveSet, deleteSet, sets and activeSet (after them), exportKys and summarize; every reply reports the sets, the set in use and whether it is modified. Bad names, keys or counts are errors that change nothing. * .kys import: tool keys and sets saved on Windows (K3) Photoshop's <tool> rows now import: each matches our tool by name (our tool names are Photoshop's) or by pc-id in sets PhotoCraft exported, and a letter other than the tool's own becomes its override, while its own letter keeps the tool cycling with the others (#5036). A set saved on Windows spells the primary modifier Ctrl where a Mac set writes Cmd and keeps Control for the Control key. A set with no Cmd or Opt anywhere but some Ctrl is read as a Windows set, its Ctrl as our Cmd: Ctrl+L is the Cmd+L default, not a Control-key override that would also mean Control on a Mac. The import reports `windows`. * cargo fmt the extension lists Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Gert Pellin <gert@pellin.be> Co-authored-by: Vikram Bhaskaran <vikram.bhaskaran@outlook.com> Co-authored-by: vangel76 <vangel1976@googlemail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Finn Newick <finnnk@gmail.com> Co-authored-by: Brandon Thomas <echelon@gmail.com> Co-authored-by: Diego Chagas <drochachagas@gmail.com> Co-authored-by: Tanish Manku <tmanku.dev@gmail.com> |
||
|
|
7edc7aa03d |
Raw: measure the Nikon D90 image area (4288x2848 at (10, 10)) (#4323)
Aligning the decoded D90 sensor plane with the camera's own JPEG puts the image at (10, 10) in the 4352x2868 sensor plane, giving the camera's 4288x2848 frame (10-pixel margins; the right border reads the camera's flat 255 from column 4310). Add the measured ImageArea so a D90 NEF opens on the camera's frame instead of the uncropped plane, and make a measured profile leave a file that carries its own colour calibration (a DNG) untouched. Co-authored-by: Brandon Thomas <echelon@gmail.com> |
||
|
|
0e15fe77f7 |
Fix Nikon NEF lossy levels, packed decoding and metadata crops (#3238)
Co-authored-by: optimalbytee <optimalbytee@users.noreply.github.com> |
||
|
|
34ce5766d2 |
raw: decode Panasonic RW2 RawFormat 3 and 4 (vendor-compressed) (#3286)
The compressed RW2 layouts of earlier Lumix bodies (e.g. the DMC-GX7) decode at full sensor resolution instead of falling back to the camera's embedded JPEG preview. The layout was established by observation of sample files (fixed 16 bytes per 14 pixels, predictor groups with per-parity predictors, page-permuted downward bit order); the black level of these files reads 15 counts low. Adds round-trip tests through a mirrored synthetic encoder, cursor and predictor-group unit vectors, and updates the support matrix. Co-authored-by: Gotya-OFFCIAL <339410022+Gotya-OFFCIAL@users.noreply.github.com> |
||
|
|
70537058eb |
Open Canon CR3 camera JPEG previews with container orientation (#4595)
Co-authored-by: optimalbytee <optimalbytee@users.noreply.github.com> Co-authored-by: Brandon Thomas <echelon@gmail.com> |
||
|
|
876bb8e281 |
Check peak RAW development memory and bound AHD scratch (#4597)
Co-authored-by: optimalbytee <optimalbytee@users.noreply.github.com> Co-authored-by: Brandon Thomas <echelon@gmail.com> |
||
|
|
23daf16f82 |
Align tighter camera JPEG references for RAW colour fitting (#4598)
Co-authored-by: optimalbytee <optimalbytee@users.noreply.github.com> Co-authored-by: Brandon Thomas <echelon@gmail.com> |
||
|
|
bbe77354e0 |
Camera Raw on raw opens: Clarity, Texture and Dehaze in linear light, measured (#3815)
* Camera Raw: Whites, Blacks, Shadows and Highlights closer to Photoshop's Measured as a black box on Photoshop 25.4's Camera Raw Filter. Whites and Blacks are global curves of the grey level: the measured rows per slider value, Whites first, after Shadows and Highlights. The clipping directions adapt to the image's black and white points (Whites up to 2.7 times stronger on a dark image, Blacks at least 1.2). On ten public-domain photos the mean error against Photoshop drops from 4.2-36.8 to 0.9-8.9 levels of 255. Shadows and Highlights are a local Laplacian filter (Paris et al. 2011, fast form of Aubry et al. 2014) on the log luminance, the method Adobe credits for process version 2012. Shadows raises pixels darker than their surroundings by an amount set by their own and their surroundings' tone, so big edges keep no halo, and keeps the 99th percentile; Highlights pulls pixels brighter than their surroundings toward them and keeps the 1st. The strengths are fitted to the photos (mean error 2.8-9.4 levels, was 3.7-14.3); Photoshop's exact operator was not recovered. The tone stages decode light with the document's curve: filter.cameraRaw stores encodingGamma (1.8 for ProPhoto, where raws open). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Camera Raw Shadows/Highlights: no contour lines on smooth gradients Twelve reference values over a raw's whole range left more than a stop between them, and the remapping changes within half a stop: coefficients interpolated across that gap drew contour lines in out-of-focus areas (Shadows +100 on a Nikon D4 raw). References are now an eighth of a stop apart over the image's tones (the deepest 0.1 % share the lowest), on a copy at most 1600 px across whose change comes back to full size by a guided upsampling, so a 16-megapixel image takes about 2 s per slider and the preview matches the result. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Camera Raw on raw opens: Shadows/Highlights in linear light, D4 look and black point as Photoshop Measured black-box on 29 Nikon D4 NEFs opened through Camera Raw in Photoshop 25.4 (sliders at +-100 and +50, exposure +-1) against our own linear development of the same files. - Shadows and Highlights of the open-time dialog now act while developing, in linear light before the tone curve, as Camera Raw does for raw files (exposure -1 gives the same gains). The log2 gain is slider/100 * C(B - anchor): B is a guided-filter base of the log2 luminance on a copy 1232 px on its long side, the anchor is image-adaptive (Shadows: log2 power mean p=1.5 of the linear luminance; Highlights: mean log2 luminance). New module photocraft_raw::tone; the preview undoes the camera curve to show the same (photocraft_raw::retone). - The D4 profile copies Camera Raw's default rendering: look matrix folded into the ForwardMatrix, per-channel curve, and an adaptive black point (0.33 x the 0.1th percentile of the luminance, Sensor::black_point). Against Photoshop on the 29 frames (1/4 size, log2 Y): default rendering median difference 0.29 -> 0.04 stop; correlation of the slider's effect Shadows +100 0.96, -100 0.91, Highlights -100 0.87, +100 0.85 (the previous filter model: 0.68 and 0.55 on DSG_3464). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * docs: the open-time Camera Raw re-develop covers Shadows and Highlights Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Camera Raw raw-stage Shadows: keep shadow contrast as Photoshop does The user saw Photoshop's shadows sink deeper with more contrast. Measured on the 29 D4 NEFs: with Shadows +100 our shadow detail had 0.79 of Photoshop's local contrast and shadows sat 0.38 stop lighter. Two causes: - The black point now comes before the Shadows / Highlights gain, as in Camera Raw (lifting first let the lift wash out the black point's deep-shadow contrast). - The guided upsampling's epsilon goes from 0.01 to 1 stop^2: the change follows strong edges but stays smooth inside texture (with 0.01 it followed every detail and flattened it). Near strong edges the effect still matches Photoshop as well as elsewhere. Shadows +100: shadow detail 0.79 -> 0.91, shadow bias +0.38 -> +0.16 stop; effect correlation Shadows -100 0.906 -> 0.946, Highlights -100 0.870 -> 0.886, others unchanged. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Camera Raw on raw opens: Clarity, Texture and Dehaze in linear light, as Photoshop Measured black-box from Photoshop 25.4 opening 29 Nikon D4 NEFs through Camera Raw (descriptor keys Cl12, CrTx, Dhze; each at ±100 and +50, and +100 at exposure −1, 348 opens). All three act before the tone curve: the change at exposure −1 equals the one at 0 (correlation 0.98–0.99). - Clarity: log2 luminance split into bands between guided-filter bases (radii 2, 8, 32, 128 on the 1232 px work copy), each band scaled by a measured gain that follows its base's tone, plus a tone term; carried to full size with the Shadows / Highlights change. - Texture: Gaussian bands σ 0.5…16 on a half-size copy, each through its measured response (small detail raised more than strong edges, flat areas unchanged), guided-upsampled. - Dehaze: He, Sun and Tang's dark channel prior on linear ProPhoto (dark channel in Display P3, airlight from the brightest 0.1 % of it, guided-smoothed transmission), w 0.8 at +100; −100 a uniform veil. The black point stays the default rendering's. Against Photoshop, PhotoCraft's develop vs Photoshop's open on all 29 frames (median effect correlation, log2 luminance at 1/4 size, Texture at 1/2): Clarity +100 0.81, −100 0.85; Texture +100 0.85, −100 0.88; Dehaze +100 0.61 (0.78 in gamma 2.2: the crushed shadows differ), −100 0.96. Shadows / Highlights unchanged (0.964 / 0.886). The open-time Camera Raw dialog re-develops for them and its preview shows them (retone). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Co-authored-by: Brandon Thomas <echelon@gmail.com> |
||
|
|
39aa849b47 |
Raw: open monochrome LinearRaw DNGs (1 sample per pixel) (#3497) (#3503)
A one-sample LinearRaw DNG (VueScan film scans, Leica Monochrom) was rejected as "LinearRaw data with 1 samples per pixel". The DNG decoder now accepts it and develop renders it as neutral grey: the one sample fills all three channels, white balance, colour matrices and camera looks do not apply, and the output is R = G = B in 16-bit ProPhoto. io skips the preview colour fit for such files. Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com> |
||
|
|
aa1febaffb |
Keep fitted Camera Raw colour during tuning and crop Nikon D5100 borders (#3201)
* raw: fit non-DNG colour to the camera's embedded JPEG * raw: preserve fitted Camera Raw look and crop D5100 borders --------- Co-authored-by: Gert Pellin <gert@pellin.be> Co-authored-by: optimalbytee <optimalbytee@users.noreply.github.com> Co-authored-by: Brandon Thomas <echelon@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
303b7849e8 |
raw: fit non-DNG colour to the camera's embedded JPEG (#2135)
* raw: fit non-DNG colour to the camera's embedded JPEG * raw look: clippy (as_chunks) and rustfmt after merging main Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Brandon Thomas <echelon@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
fc42f81f42 |
Web: leave the JPEG XL DNG decoder out of the wasm (25.1 MB -> 24.3 MB) (#2233)
* Web: leave the JPEG XL DNG decoder out of the wasm (0.88 MB, 23.1 MiB now) The 0.6.0 web build went over packaging/web/package.sh's 24 MiB gate (26,980,503 bytes on |
||
|
|
510636f461 |
DNG: JPEG XL compression 52546 (DNG 1.7, Samsung Expert RAW) (#2047)
* DNG: JPEG XL compression 52546 (DNG 1.7, Samsung Expert RAW) Decode JPEG XL-compressed DNG strips and tiles with jxl-oxide (pinned =0.12.6), with geometry and channel checks, Limits/AllocTracker budgets and catch_unwind around the decoder. Warn when a DNG carries a ProfileGainTableMap (tag 52525), which is not applied yet. Tests encode fixtures in-process with zune-jpegxl (dev-dependency only). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * raw README: list Deflate DNG beside JPEG XL after rebasing onto #1971 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Brandon Thomas <echelon@gmail.com> |
||
|
|
c0541baf6a |
Raw: decode uncompressed Fujifilm RAF, Bayer and X-Trans (#1872)
RAF's header, metadata records and Fuji IFD give size, packing (16-bit, 12-bit LSB-first, 14-bit in 32-bit words), layout, black, WB, crop and exposure bias; X-Trans and other periodic non-Bayer CFAs go through a new edge-directed demosaic. Compressed and early SuperCCD RAF stay unsupported (preview fallback). Tests use synthetic files only. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
4716d33372 |
Raw: cRAW black level without a tag; measured image areas (D800E, ILCE-7) (#1902)
Sony cRAW files with no BlackLevel tag take the 512 their tone curve gives the black code instead of 0 (the ILCE-7 no longer develops washed out; the RX0 corpus oracle moves to 512 too). cameras.rs gains measured image areas without a full profile: the Nikon D800E and Sony ILCE-7 crop like the camera's JPEG. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
3d09578322 |
raw: decode Nikon compressed NEF (lossless + lossy) (#1310)
* raw: decode Nikon compressed NEF (lossless + lossy) Nikon's default NEF (TIFF compression 34713) opened only as the embedded 8-bit JPEG preview. Decode the Huffman-coded strip: lossless and lossy type 1 / 2, 12 and 14 bit, with the seeds and curve of maker note 0x0096 in the maker note's byte order. "Lossy after split" files stay unsupported and keep the preview fallback. Clean-room: format facts from prose write-ups (Clévy, Claff, ExifTool tag names, T.81). The fixed Huffman tables are not in the files; they come from LightCraft's black-box analysis of CC0 raw.pixls.us samples (storytold/lightcraft#86, verified there on ~45 files from ~35 bodies). No decoder code is shared: nefc.rs is written against this crate's TIFF reader. Also read Nikon BlackLevel (0x003d) in 14-bit units for 12-bit data, as measured for LightCraft (D750 / D850 / Z 50). Refs #50. * raw: Nikon NEF without a BlackLevel tag is black-subtracted in camera A D3200 NEF's darkest samples are 0-4 (noise around zero): assume 0 without the "black level not recorded" warning when the Nikon maker note is present but has no 0x003d tag. * raw: trim Nikon's right-edge padding columns A D3200 NEF pads every row with 46 columns of 256 (exact in 97.5% of the rows, lossy-coding noise elsewhere) after its 6034 image columns; they developed as a pink stripe on the right. Trailing columns (at most 128) that hold the last column's value in at least 90% of their rows, when that value is dark, are left out of the active area and crop. A white- clipped edge is never trimmed. |
||
|
|
eb11e1af46 | CR2: measure the colour filter row phase instead of assuming RGGB (#193) (#244) (#244) | ||
|
|
2149f7f646 |
raw: Sony cRAW, Panasonic RW2 (RawFormat 5), uncompressed ORF + ORF previews; NEF blocked (clean-room) (#83)
* raw: decode Sony cRAW, Panasonic RW2 (RawFormat 5) and uncompressed Olympus ORF - sony.rs: compressed ARW (SonyRawFileType 2): 32-pixel runs as two 16-byte blocks of 11-bit max/min, 4-bit positions and 7-bit deltas, then the SonyToneCurve (0x7010) to 14 bits. From Dietz (EI 2016) and the RawDigger write-up; bit order and curve scale by observation. - rw2.rs: RawFormat 5 12/14-bit packing (10/9 pixels per 16-byte LE block), stored in 0x4000-byte pages split at 0x1FF8 (observed), with PanasonicRaw CFA, black, linearity limit, WB and sensor borders. RawFormat 4 and older stay unsupported (preview fallback). - orf.rs: uncompressed 16-bit ORF (E-1, E-400): left-justified samples, EXIF CFAPattern, Olympus ImageProcessing black / WB / ValidBits / crop; compressed ORF stays unsupported but its maker-note preview (CameraSettings 0x0101/0x0102) is now found. - testgen: cRAW, RW2 and ORF encoders/builders; vendor_codecs tests round-trip each codec; hostile tests fuzz/truncate all three. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * raw: README support matrix for cRAW, RW2, ORF and the clean-room gaps Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
5bd3dac660 |
Camera raw: clean-room pure-Rust decoder (DNG, CR2, TIFF/EP raws) with develop pipeline (#65)
* raw: clean-room camera raw decoder and developer (photocraft-raw) A new standalone crate (no workspace deps, wasm-safe, no unsafe) written from TIFF 6.0, TIFF/EP, the DNG 1.7 spec, ITU-T T.81 Annex H and published demosaicing papers: - Lossless JPEG (LJ92) decoder: predictors 1-7, point transform, restart intervals, combined Huffman+extra-bits fast table. - DNG: uncompressed (8-16 bit, packed) and LJ92, strips and tiles, CFA and LinearRaw, linearization, black level (repeat, deltas), white level, active area, default crop, ColorMatrix/CameraCalibration/ForwardMatrix interpolation by CCT, AnalogBalance, AsShotNeutral/WhiteXY, BaselineExposure, orientation. - CR2 (LJ92 slices, SensorInfo borders, ColorData as-shot WB, measured black) and TIFF/EP raws (NEF/ARW/PEF uncompressed or LJ92, incl. Sony lossless ARW tiles, Nikon/Sony documented black and WB tags). - Develop: black/white scaling, WB with highlight clipping, bilinear / Malvar-He-Cutler / AHD demosaic (rayon bands, sequential on wasm), camera -> XYZ D50 -> ProPhoto, exposure, gamma 1.8, 16-bit, orientation. - Embedded JPEG preview finder; clear Unsupported errors for CR3, RAF, ORF, RW2, vendor-compressed NEF/ARW/PEF, lossy/JXL/float DNG, X-Trans. - Limits checked before every allocation; testgen feature with an LJ92 encoder and DNG/CR2/TIFF-EP writers; round-trip, colour-reference, truncation and corruption tests. Registered as a standalone crate in xtask layers. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * io: open camera raws through photocraft-raw (16-bit ProPhoto), preview fallback Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * raw: DNG GainMap opcode, fuzz targets, stress tools; open raws from the apps - Apply DNG OpcodeList2 GainMap (lens shading, e.g. phone DNGs); report other opcodes. - cargo-fuzz targets (develop, ljpeg) and a mutation stress example. - Raw extensions in the desktop/web open dialogs and the batch folder list. - file.open returns the import notes (how a raw was developed, preview fallback). - codecs: the camera raw refusal points at photocraft-raw. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * raw: clippy 1.99 (is_multiple_of, clamp) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |