Originally, ColorMatrix supported four matrices:
- Rec.709
- FCC
- Rec.601
- SMPTE 240M
With four matrices, a flattened conversion table was indexed using:
That was correct for a 4×4 table.
In ColorMatrix 2.6, Rec.2020 was added as a fifth matrix. The coefficient array was expanded to five entries, and the conversion table consequently became 5×5. The matrices are stored in this order:
The coefficient-generation code iterates through all five source matrices and all five destination matrices, incrementing one flattened table index after every pair. In other words, the table is now laid out with a row width of five.^1
But the constructor still contains:
It was never changed to multiply by five.^2
What happens with a Rec.601 → Rec.709 conversion
The mode parser correctly translates^2:
ColorMatrix then calculates:
But in the new 5×5 coefficient table:
The correct index for Rec.601→Rec.709 is:
So the result is:
| Requested operation | Operation actually executed |
|---|---|
| Rec.601 → Rec.709 | FCC → SMPTE 240M |
GetFrame() then loads its working coefficients directly from yuv_convert[modef], so once modef has incorrectly become 8, the wrong matrix is genuinely applied to every frame.^2
The bug was introduced with Rec.2020 support
The repository history identifies a February 22, 2018 commit named “add Rec.2020 support.” That commit changed:
from 4 to 5, but the same diff shows that the old:
calculation was left unchanged. ^3
That explains the chronology:
- ColorMatrix 2.5’s four-wide table and
source*4+destwere internally consistent. - ColorMatrix 2.6 expanded the table to five columns without updating all indexing logic.
- Your February 2020 Hybrid version was already using code descended from this broken 2018 release.
- Current Hybrid still carries the affected DLL.
The proper source fix
The principal correction is:
However, changing only that line is not sufficient for the current GitHub master.
The hints/D2V path also retains old four-wide row offsets:
Those need to become five-wide calculations. A safer replacement is:
The stale hint offsets and old SIMD mode-number groups are visible together in the current source. ^2
SIMD dispatcher also needs updating
Under the corrected five-wide numbering, the existing SIMD groups must change to:
Alternatively, a corrected build could initially force the C route until SIMD dispatch is repaired.
This matters because the correct Rec.601→Rec.709 mode becomes index 10. The current SIMD dispatcher does not recognize mode 10, returns NULL, and the YV12 processing code immediately calls the returned pointer. A one-line index fix in an SIMD-enabled build could therefore turn the color error into a crash. ^2
The CTS2 diagnostic mapping also needs to be updated or, preferably, replaced with labels generated from: