Commit Graph
134 Commits
Author SHA1 Message Date
ba3ea95007 raw: honour ActiveArea in Hasselblad 3FR/FFF (PR #9058)
The decoder set the active area and the crop to the whole plane and never
read ActiveArea, so a file with masked borders and no DefaultCrop developed
with the borders in. Read ActiveArea like the DNG path, use it as the
initial crop, and place DefaultCrop relative to it as DNG does. Also give
3FR/FFF its own row in the camera raw table instead of "embedded preview
only".

Co-Authored-By: Gotya-OFFCIAL <Gotya-OFFCIAL@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 19:39:30 +01:00
David Mann b8b539c845 Merge origin/main into PR #9058 (raw: Hasselblad 3FR/FFF) 2026-11-07 19:37:23 +01:00
FredApps 6ef1032f87 Merge pull request #9460 from vangel76/perf/9435-raw-linear-fill
raw: LinearRaw fill works out the black level once per row and phase (stacked on #9455)
2026-11-07 16:31:48 +01:00
David Mann f98218cbe8 Merge pull request #10529 from luben93/raw/nef-lossy-after-split-v2
raw: decode Nikon "lossy after split" NEFs
2026-11-07 16:06:23 +01:00
FredApps 5a05676a5e Merge main and retain raw row-fill and orientation regressions 2026-11-07 15:42:03 +01:00
FredApps 18123beecb Merge pull request #9464 from vangel76/perf/9435-raw-mhc-direct
raw: MHC and bilinear demosaic written out per row kind (stacked on #9455)
2026-11-07 15:39:40 +01:00
FredApps b33d2c6d82 Merge pull request #9459 from vangel76/perf/9435-raw-orient-transpose
raw: orientations 5-8 copy the frame in tiles (stacked on #9455)
2026-11-07 15:39:32 +01:00
FredApps 047a74f688 Merge pull request #9447 from vangel76/perf/9429-lift-clipped-histogram
raw: DNG saturation histogram per CFA row in parallel bands
2026-11-07 15:39:27 +01:00
Gotya-OFFCIAL e7241d2e26 raw: decode Hasselblad 3FR/FFF (X2D II 100C, uncompressed)
The X2D II 100C's 3FR files develop at full sensor resolution instead of
falling back to the camera's embedded JPEG preview. A 3FR is a TIFF whose
IFD0 holds the camera's JPEG and whose CFA image sits in a SubIFD with
DNG-style tags but no DNGVersion and no CFA-pattern tag (neither the
TIFF/EP pair nor the EXIF one), which is why the generic TIFF/EP path
reported "CFA pattern not recorded". The new decoder reuses the shared
strip reader, takes BlackLevel, WhiteLevel, DefaultCropOrigin/Size and
AsShotNeutral from the file, and takes the Bayer phase from a measured
per-body table (the X2D II 100C: RGGB at the data origin, measured
against the camera's own embedded JPEG; unmeasured bodies warn).
Lossless-JPEG 3FR stays preview-only, like uncompressed-only ORF and RAF
before it.

Clean-room: the TIFF 6.0 and DNG specifications, the sample file's bytes
and its embedded JPEG; no code or tables from dcraw, LibRaw, rawspeed,
rawler or darktable.

Adds round-trip and hostile tests through a mirrored synthetic builder
(tests/it/hasselblad.rs, testgen::Hasselblad3frSpec) and updates the
support matrix (file-format-parity, gaps G14, target-app-parity).
2026-11-06 12:05:10 +08:00
texjer 7ce4804b76 Merge remote-tracking branch 'upstream/main' into feat/cr2-sraw
# Conflicts:
#	crates/raw/src/lib.rs
2026-11-04 22:01:15 -05:00
Dylan M. Taylor 4c33a7b8f2 raw: keep at most 64 GainMap opcodes from one list (#11023)
Development applies every gain map to every pixel it covers, so a list
of thousands of maps multiplied the work of an otherwise admissible
image. Cap them like MapPolynomial; the rest are named as not applied.
2026-11-03 20:50:37 -05:00
Dylan M. Taylor 87f0bb3942 raw: refuse JPEG XL tiles with compressed metadata boxes or splines (#11039)
jxl-oxide inflates Brotli-compressed auxiliary boxes (Exif, XMP) into
storage its allocation tracker doesn't see while it reads a container,
and samples spline geometry into an untracked vector with no bound on
the sample count. A raw tile needs neither: refuse a container holding a
brob box before reading it, and a codestream whose frames draw splines
before rendering.
2026-11-03 20:37:49 -05:00
Dylan M. Taylor c004e38e75 raw: decode a tile range once when several tiles share it (#11041)
read_plane decoded every segment separately, even when a file pointed
every tile at the same bytes. Each lossless JPEG decode rebuilds its
Huffman tables and each result is retained until assembly, so one small
range repeated across many tiles multiplied header work and memory by
the tile count.

Decode each distinct (offset, length, geometry) once and scatter the
shared samples into every tile that names it.
2026-11-03 20:37:28 -05:00
Dylan M. Taylor 06bedca787 raw: refuse lossy DNG tiles that carry Extended XMP (#11042)
* raw: refuse lossy DNG tiles that carry Extended XMP

zune-jpeg keeps every Extended XMP fragment until its set is complete
and re-sorts and re-checks all pending fragments after each marker, so a
lossy (compression 34892) tile of many small incomplete fragments costs
quadratic time before a pixel is decoded. A raw tile has no metadata:
refuse one with an Extended XMP APP1 segment before parsing it.

* address review: bound Extended XMP identifier to APP1 payload
2026-11-03 20:37:07 -05:00
Dylan M. Taylor 4367131008 raw: check retone's pixel count with checked multiplication (#11043)
retone compared the buffer length with an unchecked w * h. In a release
build a product that wraps passed the check for a tiny buffer, and the
banded passes then ran over the original, huge height.
2026-11-03 20:31:35 -05:00
lucas perssonandClaude Opus 5.5 dda8ac6281 [ai] raw: test split strips with up to four extra fixed-rate rows
The end-of-strip rule accepts whole fixed-rate rows past the image (up to 4),
but no test covered a valid strip with such rows. Encode 1 to 5 extra rows at
12 and 14 bit: up to 4 decode with the image rows unchanged, the fifth is
refused (preview fallback).

Suggested by CodeRabbit on #10529.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 18:10:56 +01:00
lucas perssonandClaude Opus 5.5 b15e914431 [ai] raw: decode Nikon "lossy after split" NEFs
Some lossy type 2 NEFs store a split row at byte 562 of maker note 0x0096.
They were refused ("lossy after split") and opened as the camera's preview.
The 11 such CC0 raw.pixls.us files come from the D3400, D5, D5000, D5200,
D5500, D5600, D7000, Nikon 1 J5 and Z 6_2.

Decode them following the rule LightCraft derived by black-box analysis of
those 11 files (storytold/lightcraft#633), the same route the Huffman tables
took. Rows above the split are Huffman coded as before. From the split row on,
every pixel is one MSB-first word of bits - 4 bits: a prefix code for the
difference category followed by the top bits of the T.81 additional bits,
dequantised to the centre of the kept bin (fixed_words, ported from
LightCraft's fixed_word_table), with the same predictor and an unclamped
running value. The section ends the strip: whole rows (up to 4 more than the
image), then 0-7 one-bits.

A split file that fails to decode for any reason (a section that does not end
the strip, the all-ones word, truncation, size limits, a split row past the
image, a curve long enough to cover byte 562) stays unsupported, so it keeps
the embedded-preview fallback it had before. Apart from the all-ones word and
the padding, the content of the fixed-rate section is not validated: a damaged
word inside a well-formed section decodes without an error, as in LightCraft.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 16:04:10 +01:00
FredApps fd3a12dfde Merge approved PhotoCraft PR #3142: Read applied Sony RAW metadata and crop ILCE-6000 images
Port behavior and regression coverage into current modules; preserve current upstream behavior.
Integrate bounded Sony SR2 metadata decoding and ILCE-6000 image area; preserve newer bit-depth-aware camera profile application and explicit file crop precedence. Keep current parity counts and original metadata/crop tests.
2026-10-29 06:38:44 +01:00
FredApps 71afe098ca Merge approved PhotoCraft PR #9457: raw: faster tone stage (Shadows, Highlights, Clarity, Texture, Dehaze), same bytes 2026-10-29 03:29:53 +01:00
texjer fd601d7807 feat(raw): decode Canon sRAW / mRAW CR2 as camera RGB
Canon's subsampled lossless JPEG (Y 2x2 or 2x1, Cb/Cr per MCU) with its
own prediction chain, de-sliced into YCbCr planes, bilinear chroma, a
BT.601 inverse and the per-shot sRAW gains from ColorData (version 15),
developed like a three-sample LinearRaw so white balance, the fitted
camera look, tone and the Camera Raw dialog all apply. Verified on EOS
6D Mark II mRAW files; other bodies decode with a warning when their
gain offset is unknown.
2026-10-28 14:04:01 -04:00
Dylan M. Taylor 52800a7b3d Refactor raw for strict Clippy checks 2026-10-27 23:47:06 +00:00
David Mann 77f7914271 Merge PR #9455: raw: develop the default rendering band by band, without the f32 RGB frame 2026-10-27 14:28:29 +01:00
David Mann 3a689f217c Merge remote-tracking branch 'origin/main' into HEAD
# Conflicts:
#	crates/engine/src/file_cmds.rs
#	crates/engine/src/retouch_cmds.rs
#	crates/engine/src/vector_cmds.rs
#	crates/io/src/psd_export.rs
#	crates/io/src/psd_import.rs
#	crates/io/src/smart_map.rs
#	crates/psd/src/layer.rs
#	crates/raster/src/lib.rs
#	crates/ui-egui/src/camera_raw_ui.rs
#	crates/ui-egui/src/canvas.rs
#	crates/ui-egui/src/filter_dialog.rs
#	crates/ui-egui/src/gpu_canvas.rs
#	crates/ui-egui/src/panels.rs
#	crates/ui-egui/src/retouch_ui.rs
#	crates/ui-egui/src/transform_tool.rs
#	crates/ui-egui/src/vector_ui.rs
#	docs/gaps.md
2026-10-27 14:26:26 +01:00
0b09eb43a5 Integration: per-band AHD balancing, memory-capped fused bands (#9455)
`for_bands_grouped` always split the bands into `min(workers, jobs)` static
chunks, so on the default develop (jobs == workers) a worker held up on the
shared pool kept a fixed share of the bands waiting, where main's
`chunks_mut_limited` runs one rayon task per band. Now, when every worker may
hold a scratch, each band is its own task and borrows a scratch from a pool
(`par::chunks_mut_pooled`, at most one per worker, dropped as soon as no
waiting band can use it); the grouping stays for memory-limited jobs.

The fused bilinear/MHC, X-Trans and LinearRaw paths ran one band per worker
with no memory cap: a 262144 x 455 Bayer frame with bilinear needed 2.92 GB
at 15+ workers and failed with LimitExceeded under the default 2 GiB.
`working_memory` now caps those bands like AHD's and the fused paths use the
returned jobs. The fused LinearRaw and X-Trans band buffers are `band.min(h)`
rows, as charged (CodeRabbit, develop.rs:414).

Tests: a_slow_band_does_not_hold_up_the_bands_after_it and
fused_bands_at_once_are_capped_by_the_memory_limit fail without the fix;
for_bands_keeps_at_most_jobs_scratches_alive, chunks_mut_pooled_makes_one_state_per_worker_at_most,
multi-band and short X-Trans/LinearRaw fused-vs-full cases.

Co-authored-by: Valentin Klein <271991477+vangel76@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 14:24:57 +01:00
aiongg 85539a4f37 refactor(raw): split testgen.rs into testgen/ 2026-10-26 22:54:56 +00:00
aiongg a50edcebb0 refactor(raw): move nefc.rs tests to nefc/tests.rs 2026-10-26 22:30:14 +00:00
vangel76andClaude Sonnet 5.5 0131115c2e Merge origin/perf/9434-fused-develop into perf/9435-raw-linear-fill
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-26 01:24:20 +01:00
vangel76andClaude Sonnet 5.5 cb46d94137 Merge origin/perf/9434-fused-develop into perf/9435-raw-mhc-direct
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-26 01:21:49 +01:00
vangel76andClaude Sonnet 5.5 cb99d7f180 Merge origin/perf/9434-fused-develop into perf/9435-raw-orient-transpose
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-26 01:18:34 +01:00
vangel76andClaude Sonnet 5.5 3e144a381a Merge upstream/main into perf/9434-fused-develop
Main reverted the #9455 merge, so a plain merge would drop the band-by-band
develop; keep this PR's demosaic.rs, develop.rs and development_limits.rs and
take all other upstream changes.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-26 01:18:21 +01:00
vangel76andClaude Sonnet 5.5 1e52abfb28 raw: restore the LabF pair table that the main revert dropped from the merge
Main reverted the earlier #9455 merge, which also removed the AHD changes
that came in with it; keep this PR's AHD code and add clampf plus the
NaN-plane test from main.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-26 01:15:47 +01:00
vangel76andClaude Sonnet 5.5 0dcc17770e Merge upstream/main into perf/9427-ahd-demosaic
Keep the AHD scratch/band changes; take clampf and the NaN-plane test from main
(main reverted the earlier #9455 merge, so the AHD changes stay on this branch).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-26 01:14:30 +01:00
aiongg cece50d513 Merge pull request #9386 from davidmann4/integration-batch-8
Integration batch 8: 37 reviewed community PRs on main
2026-10-25 20:04:41 -04:00
Dylan M. Taylor 3877bb1dbe chore: resolve SonarCloud maintainability issues (#9044)
* chore: resolve SonarCloud maintainability issues

* fix: format imports and fix effect_entry argument type

* fix: restore test closure and wildcard import that broke compilation

* refactor: split composite_to_surface to reduce cognitive complexity
2026-10-25 19:38:31 -04:00
vangel76 64b7668598 Merge remote-tracking branch 'origin/perf/9434-fused-develop' into ahdfix-9464
# Conflicts:
#	crates/raw/src/demosaic.rs
2026-10-25 23:22:20 +01:00
vangel76 fe1282aa72 Merge remote-tracking branch 'origin/perf/9434-fused-develop' into ahdfix-9460 2026-10-25 23:19:06 +01:00
vangel76 7e4d320f46 Merge remote-tracking branch 'origin/perf/9434-fused-develop' into ahdfix-9459 2026-10-25 23:19:00 +01:00
vangel76 5d77de703e Merge remote-tracking branch 'origin/perf/9427-ahd-demosaic' into ahdfix-9455
# Conflicts:
#	crates/raw/src/demosaic.rs
2026-10-25 23:17:47 +01:00
vangel76andClaude Sonnet 5.5 3bceaf4d66 raw: AHD allocates its scratch once per worker, not once per band
The bands were split into four groups per worker, clamped to the number
of bands. A 24 MP raw has 126 bands, so with 32 workers every group held
one band and every band allocated and zeroed a fresh 16-plane scratch,
which cost more than the reuse saved. The bands are now split into one
group per worker, so the scratch is allocated once per worker whatever
the thread count. Pixels are unchanged.

Adds a test that runs 35 bands in groups of 9 on a 4-thread pool and
compares the result with the reference bit for bit, so the band index of
later groups and a scratch reused for several bands are checked.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-25 23:14:30 +01:00
David Mann 3370667b39 Integration: AHD scratch sized to the rows the memory check approved (#9450)
Each AHD worker allocated scratch for a full 38-row band while
develop's memory check budgets h.min(32) + 6 rows, so short images
allocated more than was approved: a 262144 x 1 strip was budgeted
~106 MB and allocated ~580 MB (5.4x). The scratch is now sized from
the same row count as the budget, and the budget also counts the two
u32 vertical-sum rows, so budget == allocation (~109 MB there).
Pixels are unchanged.

Tests: ahd_scratch_fits_its_budget (budget equals the real scratch for
h = 1..5000), ahd_matches_reference_with_reused_scratch (1100 rows,
jobs 1 and 2, all four phases: one scratch serves many bands).

Co-authored-by: vangel76 <271991477+vangel76@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
(cherry picked from commit 630e32c4238df452d9d5e70357bdb968045ab8c3)
2026-10-25 23:11:54 +01:00
David Mann 0dede9f952 Merge PR #9451: raw: faster lossless JPEG scan loop (inline diff, 2-component loop, shared tables) 2026-10-25 18:54:57 +01:00
David Mann 0134d2dc70 Revert "Merge PR #9455: raw: develop the default rendering band by band, without the f32 RGB frame"
This reverts commit ddb92be1fa, reversing
changes made to f12be92413.
2026-10-25 18:38:22 +01:00
David Mann 92f592684c Revert "Merge PR #9454: raw: develop_half, a fused half-size preview develop (part of #9433)"
This reverts commit f12be92413, reversing
changes made to ba503e3584.
2026-10-25 17:49:19 +01:00
6156d4b833 Integration: lossless JPEG round-trips with one Huffman table per component (#9451)
Every lossless-JPEG round-trip wrote a single DHT (id 0) shared by all
components, so the new per-component table mapping (comp_table, the t0/t1
split of the two-component loop) went untested, though real CR2 files
declare one table per component. testgen gains lj92_encode_tables (one DHT
per table id, Td chosen per component, code lengths rotated per table so
the tables differ); lj92_encode delegates to it with output unchanged.

New tests cover 2- and 4-component frames with distinct and mixed tables,
predictors 1, 4, 6 and 7, restart intervals and 16-bit 0/65535 extremes.
They now catch swapping t0/t1, pushing 0 into comp_table, and resolving
every component to tables[0]; they pass on main's decoder as well.

Co-authored-by: vangel76 <271991477+vangel76@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-25 17:22:28 +01:00
vangel76andClaude Sonnet 5.5 ce86a73a17 raw: MHC and bilinear demosaic written out per row kind instead of through tap loops
MHC evaluated every pixel through a Vec of (offset, weight) taps per
colour, and bilinear branched on the site's colour for every pixel.
A Bayer row has two kinds of pixel that alternate, so both methods now
get one function per row kind with the colours as constants, which
the compiler turns into straight-line code and vectorizes across the row.

The arithmetic is unchanged: MHC's taps keep their order and weights
(kernel / 8, exact in f32), so every sum is the one the tap loop made;
bilinear's expressions are as they were. The tap-loop code stays under
cfg(test) as the reference of a test that compares both methods on all
four Bayer phases, odd sizes, out-of-range values, signed zeros,
infinities and NaN (a NaN may differ in sign or payload only).

Part of #9435.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-25 15:18:21 +01:00
vangel76andClaude Sonnet 5.5 8e006dae43 raw: LinearRaw fill works out the black level once per row and column phase
The 3-sample and monochrome fill called `Scale::value` for every sample:
two modulo operations, checked multiplications and a bounds-checked
lookup in `BlackLevels::at` each time. With no per-column black deltas
the black level and range depend on the row and the column phase only,
so they are now worked out once per row and phase, and the samples go
through the same arithmetic as before ((v - black) / range, the finite
check, the white-balance multiply, the clamp). Frames with deltas, or a
repeat pattern wider than 16 columns, keep the per-sample path.

A test compares the new fill with `Scale::value` bit for bit for one and
three samples, several black patterns, deltas, a linearization table and
offset starting columns.

Part of #9435.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-25 15:07:10 +01:00
David Mann 34310d5ba6 Merge PR #8402: Raw: open lossy DNGs (floating-point JPEG XL raw data), and fall back to the JPEG XL preview, not the tiny thumbnail (fixes #8125) 2026-10-25 15:05:48 +01:00
David MannandClaude Opus 5.5 ddb92be1fa Merge PR #9455: raw: develop the default rendering band by band, without the f32 RGB frame
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-25 15:04:31 +01:00
David MannandClaude Opus 5.5 f12be92413 Merge PR #9454: raw: develop_half, a fused half-size preview develop (part of #9433)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-25 15:04:30 +01:00
David MannandClaude Opus 5.5 ba503e3584 Merge PR #9452: raw: block-wise final encode stage for the default rendering
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-25 15:04:29 +01:00