PR #25757 opened by Ramiro Polla (ramiro)
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25757
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25757.patch

Range only applies to integer formats, so float formats should always be full 
range. This series documents that, then fixes the places in legacy swscale that 
still treated float gray as limited range.


>From 3fd675f8fe7e067812a09be938615823dc1a8268 Mon Sep 17 00:00:00 2001
From: Ramiro Polla <[email protected]>
Date: Thu, 29 Oct 2026 19:43:15 +0100
Subject: [PATCH 1/7] avutil/pixfmt: document color range for floating point
 formats

The narrow and full range definitions only exist for integer
representations: H.273 (VideoFullRangeFlag), BT.709, BT.2020 and BT.2100
all specify them as equations on n-bit integer code values, and none of
them define range for floating point. Floating point samples hold the
normalized signal E directly, and may lie outside of its nominal range
(e.g. HDR highlights above 1.0, out-of-gamut colors below 0.0).

This matches other implementations: zimg applies pixel_range to integer
formats only, and VapourSynth uses fixed float bounds regardless of
_Range (Y in [0, 1], chroma centered on 0).

Since AVFrame and AVCodecContext still carry a range for these formats,
document that it should be set to AVCOL_RANGE_UNSPECIFIED, matching what
libavfilter already does for them, and that readers must treat it as
full range whatever its value, since some decoders tag these formats as
AVCOL_RANGE_JPEG.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Signed-off-by: Ramiro Polla <[email protected]>
---
 libavcodec/avcodec.h | 5 +++++
 libavutil/frame.h    | 5 +++++
 libavutil/pixfmt.h   | 4 ++++
 3 files changed, 14 insertions(+)

diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h
index bb654ec5a6f..de99df778d8 100644
--- a/libavcodec/avcodec.h
+++ b/libavcodec/avcodec.h
@@ -678,6 +678,11 @@ typedef struct AVCodecContext {
      *   output format.
      * - decoding: Set by libavcodec, can be set by the user to propagate the
      *   color range to components reading from the decoder context.
+     *
+     * @note Floating point pixel formats are always full range. For them,
+     *       this should be set to AVCOL_RANGE_UNSPECIFIED, and must be
+     *       treated as full range when reading, whatever its value. See
+     *       AVColorRange.
      */
     enum AVColorRange color_range;
 
diff --git a/libavutil/frame.h b/libavutil/frame.h
index 1fd296aa835..40a51294d6c 100644
--- a/libavutil/frame.h
+++ b/libavutil/frame.h
@@ -726,6 +726,11 @@ typedef struct AVFrame {
      * MPEG vs JPEG YUV range.
      * - encoding: Set by user
      * - decoding: Set by libavcodec
+     *
+     * @note Floating point pixel formats are always full range. For them,
+     *       this should be set to AVCOL_RANGE_UNSPECIFIED, and must be
+     *       treated as full range when reading, whatever its value. See
+     *       AVColorRange.
      */
     enum AVColorRange color_range;
 
diff --git a/libavutil/pixfmt.h b/libavutil/pixfmt.h
index 750e05b644f..b0bf9524c9a 100644
--- a/libavutil/pixfmt.h
+++ b/libavutil/pixfmt.h
@@ -749,6 +749,10 @@ enum AVColorSpace {
  *   - 'n' is the output bit depth.
  *   - For additional definitions such as rounding and clipping to valid n
  *     bit unsigned integer range, please refer to BT.2100 (Table 9).
+ *
+ * These definitions only apply to integer representations. Floating point
+ * representations hold 'E' directly, without any scaling or offset, and are
+ * not limited to its nominal range.
  */
 enum AVColorRange {
     AVCOL_RANGE_UNSPECIFIED = 0,
-- 
2.54.0


>From 1ab7d2efb24d66ccf18c7328ea460cfce9de4c36 Mon Sep 17 00:00:00 2001
From: Ramiro Polla <[email protected]>
Date: Sat, 31 Oct 2026 17:53:13 +0100
Subject: [PATCH 2/7] swscale: treat float gray formats as full range

Legacy swscale was still reading float gray values as limited range,
whereas swscale2 correctly ignores the range for float gray formats.

Reproducible with sample EXR file with 2 levels of gray:
$ ffmpeg -f lavfi \
         -i "color=black:s=128x64,format=grayf32,geq=lum='if(lt(X,64),1,0.93)'" 
\
         -frames:v 1 grayf32.exr

Legacy only shows 1 level
$ ffplay -sws_backends legacy grayf32.exr

swscale2 shows the 2 levels
$ ffplay -sws_backends c      grayf32.exr

Signed-off-by: Ramiro Polla <[email protected]>
---
 libswscale/utils.c                           | 9 +++++++++
 tests/ref/fate/exr-ya-scanline-zip-half-12x8 | 2 +-
 tests/ref/fate/jpeg-mpf-gainmap              | 6 +++---
 3 files changed, 13 insertions(+), 4 deletions(-)

diff --git a/libswscale/utils.c b/libswscale/utils.c
index c148813cd14..2361bb94c0e 100644
--- a/libswscale/utils.c
+++ b/libswscale/utils.c
@@ -836,6 +836,15 @@ static int handle_jpeg(/* enum AVPixelFormat */ int 
*format)
     case AV_PIX_FMT_GRAY16BE:
     case AV_PIX_FMT_YA16BE:
     case AV_PIX_FMT_YA16LE:
+    /* Floating point formats */
+    case AV_PIX_FMT_GRAYF16BE:
+    case AV_PIX_FMT_GRAYF16LE:
+    case AV_PIX_FMT_GRAYF32BE:
+    case AV_PIX_FMT_GRAYF32LE:
+    case AV_PIX_FMT_YAF16BE:
+    case AV_PIX_FMT_YAF16LE:
+    case AV_PIX_FMT_YAF32BE:
+    case AV_PIX_FMT_YAF32LE:
         return 1;
     default:
         return 0;
diff --git a/tests/ref/fate/exr-ya-scanline-zip-half-12x8 
b/tests/ref/fate/exr-ya-scanline-zip-half-12x8
index 6c07f198461..4861eda52fd 100644
--- a/tests/ref/fate/exr-ya-scanline-zip-half-12x8
+++ b/tests/ref/fate/exr-ya-scanline-zip-half-12x8
@@ -3,4 +3,4 @@
 #codec_id 0: rawvideo
 #dimensions 0: 12x8
 #sar 0: 1/1
-0,          0,          0,        1,     1536, 0xa33f5f69
+0,          0,          0,        1,     1536, 0x826d7637
diff --git a/tests/ref/fate/jpeg-mpf-gainmap b/tests/ref/fate/jpeg-mpf-gainmap
index 2c2cadac2cf..17f3723651c 100644
--- a/tests/ref/fate/jpeg-mpf-gainmap
+++ b/tests/ref/fate/jpeg-mpf-gainmap
@@ -1,5 +1,5 @@
-a94724be0101759474066d233ef14001 *tests/data/fate/jpeg-mpf-gainmap.jpeg_mpf
-5649 tests/data/fate/jpeg-mpf-gainmap.jpeg_mpf
+dcf02a726b98394f2084bb83c3f45311 *tests/data/fate/jpeg-mpf-gainmap.jpeg_mpf
+5607 tests/data/fate/jpeg-mpf-gainmap.jpeg_mpf
 #tb 0: 1/25
 #media_type 0: video
 #codec_id 0: rawvideo
@@ -11,7 +11,7 @@ a94724be0101759474066d233ef14001 
*tests/data/fate/jpeg-mpf-gainmap.jpeg_mpf
 #dimensions 1: 64x48
 #sar 1: 1/1
 0,          0,          0,        1,     9216, 0xe5834dd7
-1,          0,          0,        1,     9216, 0x662fcc6a
+1,          0,          0,        1,     9216, 0x23c5fae7
 frame|stream_index=0
 frame|stream_index=1|side_datum/gain_map_parameters:side_data_type=Gain map 
parameters|side_datum/gain_map_parameters:version=0|side_datum/gain_map_parameters:use_base_color_space=1|side_datum/gain_map_parameters:base_hdr_headroom=0/1|side_datum/gain_map_parameters:alternate_hdr_headroom=5895489/1048576|component_entry/rgb:gain_map_min=-3392315/4194304|component_entry/rgb:gain_map_max=13278945/2097152|component_entry/rgb:gamma=691797/1048576|component_entry/rgb:base_offset=1/64|component_entry/rgb:alternate_offset=1/64
 stream_group|index=0|nb_streams=2|type=Gain Map (Split base rendition and gain 
map)
-- 
2.54.0


>From 704c10a50b1cb250e5d4a270fe68a37082153966 Mon Sep 17 00:00:00 2001
From: Ramiro Polla <[email protected]>
Date: Thu, 29 Oct 2026 18:09:47 +0100
Subject: [PATCH 3/7] Revert "swscale/swscale: Do not crash on floats"

This reverts commit d16a058dbc9974f5919da3315e8cb41f8e6102f0.

The crashes it addressed came from init_range_convert_constants() deriving
the range constants from a bit depth of 32. That had already been fixed at
the source by 8ab40ca984 ("swscale: fix gray -> grayf32 SIGFPE"), which
clamps the bit depth to 16 to match the 19-bit intermediates these outputs
use, and which was committed before this one.

Instead of fixing the computation, this commit disabled range conversion
for all 32-bit outputs. This had no effect while float gray outputs were
treated as limited range, but now that they are treated as full range, it
leaves limited range input unexpanded in the float output. With the clamp
in place, the 16-bit range converters handle these outputs correctly.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Signed-off-by: Ramiro Polla <[email protected]>
---
 libswscale/swscale.c                     | 2 +-
 tests/ref/fate/filter-pixdesc-grayf32be  | 2 +-
 tests/ref/fate/filter-pixdesc-grayf32le  | 2 +-
 tests/ref/fate/filter-pixfmts-copy       | 4 ++--
 tests/ref/fate/filter-pixfmts-crop       | 4 ++--
 tests/ref/fate/filter-pixfmts-field      | 4 ++--
 tests/ref/fate/filter-pixfmts-fieldorder | 4 ++--
 tests/ref/fate/filter-pixfmts-hflip      | 4 ++--
 tests/ref/fate/filter-pixfmts-il         | 4 ++--
 tests/ref/fate/filter-pixfmts-null       | 4 ++--
 tests/ref/fate/filter-pixfmts-scale      | 4 ++--
 tests/ref/fate/filter-pixfmts-transpose  | 4 ++--
 tests/ref/fate/filter-pixfmts-vflip      | 4 ++--
 tests/ref/lavf/grayf32be.pfm             | 4 ++--
 tests/ref/lavf/grayf32le.pfm             | 4 ++--
 tests/ref/lavf/none.grayf32le.exr        | 2 +-
 tests/ref/lavf/rle.grayf32le.exr         | 2 +-
 tests/ref/lavf/zip1.grayf32le.exr        | 2 +-
 tests/ref/lavf/zip16.grayf32le.exr       | 2 +-
 19 files changed, 31 insertions(+), 31 deletions(-)

diff --git a/libswscale/swscale.c b/libswscale/swscale.c
index 05a59a5d330..a7715a79a96 100644
--- a/libswscale/swscale.c
+++ b/libswscale/swscale.c
@@ -653,7 +653,7 @@ av_cold void ff_sws_init_range_convert(SwsInternal *c)
 {
     c->lumConvertRange = NULL;
     c->chrConvertRange = NULL;
-    if (c->opts.src_range != c->opts.dst_range && 
!isAnyRGB(c->opts.dst_format) && c->dstBpc < 32) {
+    if (c->opts.src_range != c->opts.dst_range && 
!isAnyRGB(c->opts.dst_format)) {
         init_range_convert_constants(c);
         if (c->dstBpc <= 14) {
             if (c->opts.src_range) {
diff --git a/tests/ref/fate/filter-pixdesc-grayf32be 
b/tests/ref/fate/filter-pixdesc-grayf32be
index 171475483ad..2f870674f5d 100644
--- a/tests/ref/fate/filter-pixdesc-grayf32be
+++ b/tests/ref/fate/filter-pixdesc-grayf32be
@@ -1 +1 @@
-pixdesc-grayf32be   9b23c74e8e8ffae5d7c7e82bbf5929da
+pixdesc-grayf32be   f271715b20c9d6eb1315f0a47f0b0e5d
diff --git a/tests/ref/fate/filter-pixdesc-grayf32le 
b/tests/ref/fate/filter-pixdesc-grayf32le
index d598d123b4e..2a2b6522f98 100644
--- a/tests/ref/fate/filter-pixdesc-grayf32le
+++ b/tests/ref/fate/filter-pixdesc-grayf32le
@@ -1 +1 @@
-pixdesc-grayf32le   291f074a24c44799a1f437d1c55556f1
+pixdesc-grayf32le   e611536308fa5f74e64dbf30c4d8513f
diff --git a/tests/ref/fate/filter-pixfmts-copy 
b/tests/ref/fate/filter-pixfmts-copy
index 15e4aee8cf6..25e8a6c5863 100644
--- a/tests/ref/fate/filter-pixfmts-copy
+++ b/tests/ref/fate/filter-pixfmts-copy
@@ -59,8 +59,8 @@ gray16be            5b71eb9a890a3c52210512b9622231eb
 gray16le            7d159a5c1735ffe4ebd01f84ffffa2d7
 gray9be             2bcfb9b929ed9fa0ed4577ed0500a7eb
 gray9le             0171e3a1dfa892fb881207d43ae54997
-grayf32be           f3bf178835f8146aa09d1da94bba4d8a
-grayf32le           fb6ea85bfbc8cd21c51fc0e110197294
+grayf32be           1bbc4ae0407753553e802c5a740d62e6
+grayf32le           6982b2fd5319bcbcb185d1aa08ec8eae
 monob               8b04f859fee6a0be856be184acd7a0b5
 monow               54d16d2c01abfd72ecdb5e51e283937c
 nv12                8e24feb2c544dc26a20047a71e4c27aa
diff --git a/tests/ref/fate/filter-pixfmts-crop 
b/tests/ref/fate/filter-pixfmts-crop
index 09e0a1e9ea2..53b74ffbfbb 100644
--- a/tests/ref/fate/filter-pixfmts-crop
+++ b/tests/ref/fate/filter-pixfmts-crop
@@ -59,8 +59,8 @@ gray16be            9a87fd9facebf9858b61e2996a701a75
 gray16le            8b23f96b32c91654f5d8698372c4fc6a
 gray9be             77a834b1795d540074a64d9fb6452b91
 gray9le             1ef37496619ba80bfb870da6573178e6
-grayf32be           cf40ec06a8abe54852b7f85a00549eec
-grayf32le           b672526c9da9c8959ab881f242f6890a
+grayf32be           6a702a8e026b81692cda21b5704391d0
+grayf32le           e80eaddbf234d6cbe9bad1692b778eaa
 nv12                92cda427f794374731ec0321ee00caac
 nv16                3264b16aaae554c21f052102b491c13b
 nv20be              a69262f462f71973860ab08f2789ee6b
diff --git a/tests/ref/fate/filter-pixfmts-field 
b/tests/ref/fate/filter-pixfmts-field
index 8ed99eb5751..9f30c636489 100644
--- a/tests/ref/fate/filter-pixfmts-field
+++ b/tests/ref/fate/filter-pixfmts-field
@@ -59,8 +59,8 @@ gray16be            3f9a4ee6683542a840a8767c4da2158a
 gray16le            b258756e59051a2d0a51905df56d1c69
 gray9be             2cf4a1297d5231917669df224712b582
 gray9le             46c9cc0de8525d36ed6052afa70de6c3
-grayf32be           72fbfa47b2863658a8a80d588f23b3e7
-grayf32le           6b856bdbf2a2bfcd2bc7d50f109daaf0
+grayf32be           6c4c5e8a8c132ef8687bf8fa474c489a
+grayf32le           4e9a3ed7c56218fc1df60aaeb3cf7a7e
 monob               2129cc72a484d7e10a44de9117aa9f80
 monow               03d783611d265cae78293f88ea126ea1
 nv12                16f7a46708ef25ebd0b72e47920cc11e
diff --git a/tests/ref/fate/filter-pixfmts-fieldorder 
b/tests/ref/fate/filter-pixfmts-fieldorder
index 3a0e28db5d5..2245eebc9e3 100644
--- a/tests/ref/fate/filter-pixfmts-fieldorder
+++ b/tests/ref/fate/filter-pixfmts-fieldorder
@@ -59,8 +59,8 @@ gray16be            507d141b00748b23457ae5c61c4ca548
 gray16le            d4327602da9343272ecb7ca5e9b43129
 gray9be             2bf69a7bae4954221d9f7f18cd5bb390
 gray9le             c45eb848ab86f63f30ceb7206fb41be9
-grayf32be           1aa7960131f880c54fe3c77f13448674
-grayf32le           4029ac9d197f255794c1b9e416520fc7
+grayf32be           c68bf0d74437d70a8d238bbf895b1dca
+grayf32le           d0c1066d3750739b433f5fbb218f53e7
 nv16                085deb984ab986eb5cc961fe265e30c0
 nv20be              16998bc4aa2bfa3255d115a0a19ad80e
 nv20le              c8bd359ec00903dd2b5526b4d8ecb09f
diff --git a/tests/ref/fate/filter-pixfmts-hflip 
b/tests/ref/fate/filter-pixfmts-hflip
index caa817caeeb..cfa86f776c8 100644
--- a/tests/ref/fate/filter-pixfmts-hflip
+++ b/tests/ref/fate/filter-pixfmts-hflip
@@ -59,8 +59,8 @@ gray16be            20fc0cc0894d2fca3d86dcdea47acc90
 gray16le            50b8a0225c2866e7b16c56cb58efdf48
 gray9be             7747115e0cb893bd62ddba43c2009cb7
 gray9le             f5a7332cb42857a87290f87b1ddbbc0e
-grayf32be           a69add7bbf892a71fe81b3b75982dbe2
-grayf32le           4563e176a35dc8a8a07e0829fad5eb88
+grayf32be           47b748257f49e6a0b28a5564addaff0f
+grayf32le           a965138bc51c3a4f8a9134631c34a355
 nv12                801e58f1be5fd0b5bc4bf007c604b0b4
 nv16                06ba714cb8b220c203f5898ef39abf93
 nv20be              979181e11f7bb74ad2d891daeebb7649
diff --git a/tests/ref/fate/filter-pixfmts-il b/tests/ref/fate/filter-pixfmts-il
index 71ca6160a68..dde63f7dd65 100644
--- a/tests/ref/fate/filter-pixfmts-il
+++ b/tests/ref/fate/filter-pixfmts-il
@@ -59,8 +59,8 @@ gray16be            06703c79bc5f2a19649ffd5920898174
 gray16le            288f58021aa00237ebbf2b399ad19756
 gray9be             1e398c2af40ea86f0db4998793e7a633
 gray9le             43029010dad140bf06d71d5811dc3049
-grayf32be           f36197c9e2ef5c50a995e980c1a37203
-grayf32le           8bf3d295c3ffd53da0e06d0702e7c1ca
+grayf32be           89d482c3b638875f79e20899a5176535
+grayf32le           a30835dd8e98166a018bd68c754e440d
 monob               faba75df28033ba7ce3d82ff2a99ee68
 monow               6e9cfb8d3a344c5f0c3e1d5e1297e580
 nv12                3c3ba9b1b4c4dfff09c26f71b51dd146
diff --git a/tests/ref/fate/filter-pixfmts-null 
b/tests/ref/fate/filter-pixfmts-null
index 15e4aee8cf6..25e8a6c5863 100644
--- a/tests/ref/fate/filter-pixfmts-null
+++ b/tests/ref/fate/filter-pixfmts-null
@@ -59,8 +59,8 @@ gray16be            5b71eb9a890a3c52210512b9622231eb
 gray16le            7d159a5c1735ffe4ebd01f84ffffa2d7
 gray9be             2bcfb9b929ed9fa0ed4577ed0500a7eb
 gray9le             0171e3a1dfa892fb881207d43ae54997
-grayf32be           f3bf178835f8146aa09d1da94bba4d8a
-grayf32le           fb6ea85bfbc8cd21c51fc0e110197294
+grayf32be           1bbc4ae0407753553e802c5a740d62e6
+grayf32le           6982b2fd5319bcbcb185d1aa08ec8eae
 monob               8b04f859fee6a0be856be184acd7a0b5
 monow               54d16d2c01abfd72ecdb5e51e283937c
 nv12                8e24feb2c544dc26a20047a71e4c27aa
diff --git a/tests/ref/fate/filter-pixfmts-scale 
b/tests/ref/fate/filter-pixfmts-scale
index cf362676581..d6ea9496d37 100644
--- a/tests/ref/fate/filter-pixfmts-scale
+++ b/tests/ref/fate/filter-pixfmts-scale
@@ -59,8 +59,8 @@ gray16be            4be62cba0e89e8121fcfe257b8fd270f
 gray16le            74dac11c70aa42e7b782dba73cd8855d
 gray9be             82586e4dd7c141493dd445c900a7bdcb
 gray9le             787f5c48ad9008636ba78de2cade71e1
-grayf32be           5e4c715519f53c15f1345df90481e5f5
-grayf32le           2ff1b84023e820307b1ba7a9550115bc
+grayf32be           5f41ae12f606c85b99772ce89606dbbd
+grayf32le           fbb4010a4fce44c8b133556410162017
 monob               f01cb0b623357387827902d9d0963435
 monow               35c68b86c226d6990b2dcb573a05ff6b
 nv12                b118d24a3653fe66e5d9e079033aef79
diff --git a/tests/ref/fate/filter-pixfmts-transpose 
b/tests/ref/fate/filter-pixfmts-transpose
index bc3173c1817..e4f87841789 100644
--- a/tests/ref/fate/filter-pixfmts-transpose
+++ b/tests/ref/fate/filter-pixfmts-transpose
@@ -59,8 +59,8 @@ gray16be            570c9f15778f767cd5cca8fb7908f119
 gray16le            b497d74284c38464e18cf5692c523a1c
 gray9be             6ef99621245fcc00806e520a0a4419d4
 gray9le             6bbd43b7d385b5c3e299bee18a88f9e2
-grayf32be           823288e1ec497bb1f22c070e502e5272
-grayf32le           6e9ec0e1cac3617f3041e681afd2c575
+grayf32be           6d9f450742f24d969dd935f943e52b74
+grayf32le           e6188ebafd89996fb264d1f6da592f34
 nv12                1965e3826144686748f2f6b516fca5ba
 nv21                292adaf5271c5c8516b71640458c01f4
 nv24                ea9de8b47faed722ee40182f89489beb
diff --git a/tests/ref/fate/filter-pixfmts-vflip 
b/tests/ref/fate/filter-pixfmts-vflip
index 87a1b268e8a..8e6ea439b4a 100644
--- a/tests/ref/fate/filter-pixfmts-vflip
+++ b/tests/ref/fate/filter-pixfmts-vflip
@@ -59,8 +59,8 @@ gray16be            483c6e570d97fbed9e51a61212910632
 gray16le            65d76c53eef28ba6e74990760c57f98a
 gray9be             cd524d242f2ac6ea39ef4bb85c9691f3
 gray9le             8a3264fc4192fcd9ca2008ca4782a451
-grayf32be           c1ba5943a0d24d70e6a280f37e4f4593
-grayf32le           8e6c048a5b3b8b26d3a5ddfce255f3f6
+grayf32be           982544dfa8cfab9e0911cdf9fa652173
+grayf32le           41c495aeb4d2e2c8c8e36cc46f07097e
 monob               7810c4857822ccfc844d78f5e803269a
 monow               90a947bfcd5f2261e83b577f48ec57b1
 nv12                261ebe585ae2aa4e70d39a10c1679294
diff --git a/tests/ref/lavf/grayf32be.pfm b/tests/ref/lavf/grayf32be.pfm
index 3ce4ad51337..c1999bb796d 100644
--- a/tests/ref/lavf/grayf32be.pfm
+++ b/tests/ref/lavf/grayf32be.pfm
@@ -1,3 +1,3 @@
-d2c3a37f7bf52be25f3f56239b5fdd92 
*tests/data/images/grayf32be.pfm/02.grayf32be.pfm
+e215d84196bccbb649e55954fd5423dc 
*tests/data/images/grayf32be.pfm/02.grayf32be.pfm
 405524 tests/data/images/grayf32be.pfm/02.grayf32be.pfm
-tests/data/images/grayf32be.pfm/%02d.grayf32be.pfm CRC=0xe3fda443
+tests/data/images/grayf32be.pfm/%02d.grayf32be.pfm CRC=0x6e324f85
diff --git a/tests/ref/lavf/grayf32le.pfm b/tests/ref/lavf/grayf32le.pfm
index 3a883ad2ac6..01a18ee72bb 100644
--- a/tests/ref/lavf/grayf32le.pfm
+++ b/tests/ref/lavf/grayf32le.pfm
@@ -1,3 +1,3 @@
-ea7aad8650d06c7cc8c80cc57cbac672 
*tests/data/images/grayf32le.pfm/02.grayf32le.pfm
+0dd9c6c475b28a91158acb316a493a93 
*tests/data/images/grayf32le.pfm/02.grayf32le.pfm
 405525 tests/data/images/grayf32le.pfm/02.grayf32le.pfm
-tests/data/images/grayf32le.pfm/%02d.grayf32le.pfm CRC=0x5443a443
+tests/data/images/grayf32le.pfm/%02d.grayf32le.pfm CRC=0x03714f85
diff --git a/tests/ref/lavf/none.grayf32le.exr 
b/tests/ref/lavf/none.grayf32le.exr
index 9db5bb78ef9..54dc59e9525 100644
--- a/tests/ref/lavf/none.grayf32le.exr
+++ b/tests/ref/lavf/none.grayf32le.exr
@@ -1 +1 @@
-tests/data/images/none.grayf32le.exr/%02d.none.grayf32le.exr CRC=0x5443a443
+tests/data/images/none.grayf32le.exr/%02d.none.grayf32le.exr CRC=0x03714f85
diff --git a/tests/ref/lavf/rle.grayf32le.exr b/tests/ref/lavf/rle.grayf32le.exr
index 5f44e019378..5846dc77ae2 100644
--- a/tests/ref/lavf/rle.grayf32le.exr
+++ b/tests/ref/lavf/rle.grayf32le.exr
@@ -1 +1 @@
-tests/data/images/rle.grayf32le.exr/%02d.rle.grayf32le.exr CRC=0x5443a443
+tests/data/images/rle.grayf32le.exr/%02d.rle.grayf32le.exr CRC=0x03714f85
diff --git a/tests/ref/lavf/zip1.grayf32le.exr 
b/tests/ref/lavf/zip1.grayf32le.exr
index c74f2aed4de..db601f45bc5 100644
--- a/tests/ref/lavf/zip1.grayf32le.exr
+++ b/tests/ref/lavf/zip1.grayf32le.exr
@@ -1 +1 @@
-tests/data/images/zip1.grayf32le.exr/%02d.zip1.grayf32le.exr CRC=0x5443a443
+tests/data/images/zip1.grayf32le.exr/%02d.zip1.grayf32le.exr CRC=0x03714f85
diff --git a/tests/ref/lavf/zip16.grayf32le.exr 
b/tests/ref/lavf/zip16.grayf32le.exr
index 25cb361be7e..c642e972d81 100644
--- a/tests/ref/lavf/zip16.grayf32le.exr
+++ b/tests/ref/lavf/zip16.grayf32le.exr
@@ -1 +1 @@
-tests/data/images/zip16.grayf32le.exr/%02d.zip16.grayf32le.exr CRC=0x5443a443
+tests/data/images/zip16.grayf32le.exr/%02d.zip16.grayf32le.exr CRC=0x03714f85
-- 
2.54.0


>From d61ae1aeb6415e244549495ce9958c3ce54549b2 Mon Sep 17 00:00:00 2001
From: Ramiro Polla <[email protected]>
Date: Sat, 31 Oct 2026 17:53:32 +0100
Subject: [PATCH 4/7] swscale/graph: do not flag an unspecified float or RGB
 range as incomplete

Legacy swscale was still expecting range information and rejecting
float formats that had the value unspecified under SWS_STRICT, whereas
swscale2 correctly ignores the range for float gray formats.

Reproducible with:
$ ffmpeg -f lavfi \
         -i 
"color=gray:s=64x64,format=grayf32,setparams=color_primaries=bt709:color_trc=bt709:colorspace=unknown"
 \
         -frames:v 1
         -sws_flags strict -sws_backends legacy
         -vf "scale=out_range=pc:out_chroma_loc=left,format=yuv420p" -f null -

Signed-off-by: Ramiro Polla <[email protected]>
---
 libswscale/graph.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/libswscale/graph.c b/libswscale/graph.c
index f4ae0f5871d..abb107f1c6e 100644
--- a/libswscale/graph.c
+++ b/libswscale/graph.c
@@ -612,8 +612,10 @@ static int add_legacy_sws_pass(SwsGraph *graph, const 
SwsFormat *src,
     ff_sws_chroma_pos(src, &graph->incomplete, &sws->src_h_chr_pos, 
&sws->src_v_chr_pos);
     ff_sws_chroma_pos(dst, &graph->incomplete, &sws->dst_h_chr_pos, 
&sws->dst_v_chr_pos);
 
-    graph->incomplete |= src->range == AVCOL_RANGE_UNSPECIFIED;
-    graph->incomplete |= dst->range == AVCOL_RANGE_UNSPECIFIED;
+    if (!isFloat(src->format) && !isAnyRGB(src->format))
+        graph->incomplete |= src->range == AVCOL_RANGE_UNSPECIFIED;
+    if (!isFloat(dst->format) && !isAnyRGB(dst->format))
+        graph->incomplete |= dst->range == AVCOL_RANGE_UNSPECIFIED;
 
     /* Allow overriding chroma position with the legacy API */
     legacy_chr_pos(graph, &sws->src_h_chr_pos, ctx->src_h_chr_pos, &warned);
-- 
2.54.0


>From 3e715c24119326e713b0e1b3833fefe7d8b3ffe6 Mon Sep 17 00:00:00 2001
From: Ramiro Polla <[email protected]>
Date: Wed, 4 Nov 2026 18:31:55 +0100
Subject: [PATCH 5/7] swscale/format: leave the range of float formats
 unspecified

Color range does not apply to floating point formats. Float gray formats
were already sanitized to AVCOL_RANGE_UNSPECIFIED, but float RGB formats
were sanitized to AVCOL_RANGE_JPEG along with the rest of the RGB family.
Move the float check out of the gray branch so that it applies to all
float formats.

This does not change any output: legacy swscale ignores the range of RGB
formats, and the ops backend passes floats through regardless of range.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Signed-off-by: Ramiro Polla <[email protected]>
---
 libswscale/format.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/libswscale/format.c b/libswscale/format.c
index fbe2d813f9c..447f11a894f 100644
--- a/libswscale/format.c
+++ b/libswscale/format.c
@@ -315,12 +315,12 @@ static void sanitize_fmt(SwsFormat *fmt, const 
AVPixFmtDescriptor *desc)
         /* Grayscale formats */
         fmt->color.prim = AVCOL_PRI_UNSPECIFIED;
         fmt->csp        = AVCOL_SPC_UNSPECIFIED;
-        if (desc->flags & AV_PIX_FMT_FLAG_FLOAT)
-            fmt->range = AVCOL_RANGE_UNSPECIFIED;
-        else
-            fmt->range = AVCOL_RANGE_JPEG; // FIXME: this restriction should 
be lifted
+        fmt->range      = AVCOL_RANGE_JPEG; // FIXME: this restriction should 
be lifted
     }
 
+    if (desc->flags & AV_PIX_FMT_FLAG_FLOAT)
+        fmt->range = AVCOL_RANGE_UNSPECIFIED;
+
     switch (av_pix_fmt_desc_get_id(desc)) {
     case AV_PIX_FMT_YUVJ420P:
     case AV_PIX_FMT_YUVJ411P:
-- 
2.54.0


>From 9aa880397c7d712a78306d766cab336c9ffbe6ef Mon Sep 17 00:00:00 2001
From: Ramiro Polla <[email protected]>
Date: Wed, 4 Nov 2026 19:26:51 +0100
Subject: [PATCH 6/7] swscale/utils: keep float gray formats full range in
 sws_setColorspaceDetails()

sws_init_context() treats float gray formats as full range, but a later
call to sws_setColorspaceDetails() could set them to limited range,
enabling range conversion on float values. Force them to full range
there as well, as is already done with a fixed range for RGB formats.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Signed-off-by: Ramiro Polla <[email protected]>
---
 libswscale/utils.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/libswscale/utils.c b/libswscale/utils.c
index 2361bb94c0e..eaa352a30c1 100644
--- a/libswscale/utils.c
+++ b/libswscale/utils.c
@@ -919,8 +919,12 @@ int sws_setColorspaceDetails(SwsContext *sws, const int 
inv_table[4],
 
     if(range_override_needed(sws->dst_format))
         dstRange = 0;
+    else if (isFloat(sws->dst_format))
+        dstRange = 1; /* float gray formats are always full range */
     if(range_override_needed(sws->src_format))
         srcRange = 0;
+    else if (isFloat(sws->src_format))
+        srcRange = 1;
 
     if (sws->src_range != srcRange ||
         sws->dst_range != dstRange ||
-- 
2.54.0


>From ef6358018c03506f65011fc7f0d469e2447cf715 Mon Sep 17 00:00:00 2001
From: Ramiro Polla <[email protected]>
Date: Wed, 4 Nov 2026 19:26:51 +0100
Subject: [PATCH 7/7] swscale/utils: remove the float exception from the
 unscaled range check

It allowed the gray8 <-> grayf32 unscaled converters to be used while
grayf32 was treated as limited range. Float gray formats are now always
full range, like gray8, so the range check passes on its own.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
---
 libswscale/utils.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libswscale/utils.c b/libswscale/utils.c
index eaa352a30c1..6aabce39aba 100644
--- a/libswscale/utils.c
+++ b/libswscale/utils.c
@@ -1700,7 +1700,7 @@ av_cold int ff_sws_init_single_context(SwsContext *sws, 
SwsFilter *srcFilter,
     /* unscaled special cases */
     if (unscaled && !usesHFilter && !usesVFilter &&
         (sws->src_range == sws->dst_range || isAnyRGB(dstFormat) ||
-         isFloat(srcFormat) || isFloat(dstFormat) || isBayer(srcFormat))){
+         isBayer(srcFormat))){
 
         ff_get_unscaled_swscale(c);
 
-- 
2.54.0

_______________________________________________
ffmpeg-devel mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to