Commit a532a8383f for ffmpeg
commit a532a8383f971af24d803ce59cb918103cefa920
Author: Kacper Michajłow <kasper93@gmail.com>
Date: Wed Sep 9 07:30:25 2026 +0200
swscale: do not use the unscaled converters across a chroma position change
The unscaled special converters copy the chroma samples as they are, or
average or interpolate them when the subsampling changes, so the samples
keep their offset from the center of the luma area they cover and a change
of chroma sample position was silently dropped. Duplicating the samples to
full resolution is a nearest neighbor upsampling that suits any position,
and gray sources have no chroma, so those conversions keep the converters.
Signed-off-by: Kacper Michajłow <kasper93@gmail.com>
diff --git a/libswscale/utils.c b/libswscale/utils.c
index d8b29aeaa3..1c2e03eab4 100644
--- a/libswscale/utils.c
+++ b/libswscale/utils.c
@@ -173,6 +173,11 @@ static av_cold int get_local_pos(SwsInternal *s, int chr_subsample, int pos, int
return pos >> chr_subsample;
}
+static av_cold int get_center_offset(SwsInternal *s, int chr_subsample, int pos, int dir)
+{
+ return (get_local_pos(s, chr_subsample, pos, dir) - 128) * (1 << chr_subsample);
+}
+
typedef struct {
int flag; ///< flag associated to the algorithm
const char *description; ///< human-readable description
@@ -1530,6 +1535,15 @@ av_cold int ff_sws_init_single_context(SwsContext *sws, SwsFilter *srcFilter,
return 0;
}
+ if (!isGray(srcFormat) &&
+ ((desc_dst->log2_chroma_w &&
+ get_center_offset(c, desc_src->log2_chroma_w, sws->src_h_chr_pos, 0) !=
+ get_center_offset(c, desc_dst->log2_chroma_w, sws->dst_h_chr_pos, 0)) ||
+ (desc_dst->log2_chroma_h &&
+ get_center_offset(c, desc_src->log2_chroma_h, sws->src_v_chr_pos, 1) !=
+ get_center_offset(c, desc_dst->log2_chroma_h, sws->dst_v_chr_pos, 1))))
+ unscaled = 0;
+
if (isBayer(srcFormat)) {
if (!unscaled ||
(dstFormat != AV_PIX_FMT_RGB24 && dstFormat != AV_PIX_FMT_YUV420P &&
diff --git a/tests/fate/filter-video.mak b/tests/fate/filter-video.mak
index 5f0387b527..6de375fcfb 100644
--- a/tests/fate/filter-video.mak
+++ b/tests/fate/filter-video.mak
@@ -568,6 +568,12 @@ FATE_FILTER_VSYNTH-$(call ALLYES, TESTSRC_FILTER SCALE_FILTER NULLSINK_FILTER FR
fate-filter-scale2ref_keep_aspect: tests/data/filtergraphs/scale2ref_keep_aspect
fate-filter-scale2ref_keep_aspect: CMD = framemd5 -frames:v 5 -/filter_complex $(TARGET_PATH)/tests/data/filtergraphs/scale2ref_keep_aspect -map "[main]"
+FATE_FILTER-$(call FILTERFRAMECRC, TESTSRC2 FORMAT SCALE) += fate-filter-scalechroma-yuyv422 fate-filter-scalechroma-uyvy422 fate-filter-scalechroma-yuyv422-topleft fate-filter-scalechroma-nv24
+fate-filter-scalechroma-yuyv422: CMD = framecrc -flags bitexact -lavfi testsrc2=s=64x64:r=1:d=1,scale=flags=accurate_rnd+bitexact,format=yuyv422,scale=in_chroma_loc=left:out_chroma_loc=center:flags=accurate_rnd+bitexact,format=yuv420p -frames:v 1
+fate-filter-scalechroma-uyvy422: CMD = framecrc -flags bitexact -lavfi testsrc2=s=64x64:r=1:d=1,scale=flags=accurate_rnd+bitexact,format=uyvy422,scale=in_chroma_loc=left:out_chroma_loc=center:flags=accurate_rnd+bitexact,format=yuv420p -frames:v 1
+fate-filter-scalechroma-yuyv422-topleft: CMD = framecrc -flags bitexact -lavfi testsrc2=s=64x64:r=1:d=1,scale=flags=accurate_rnd+bitexact,format=yuyv422,scale=in_chroma_loc=left:out_chroma_loc=topleft:flags=accurate_rnd+bitexact,format=yuv420p -frames:v 1
+fate-filter-scalechroma-nv24: CMD = framecrc -flags bitexact -lavfi testsrc2=s=64x64:r=1:d=1,scale=flags=accurate_rnd+bitexact,format=nv24,scale=out_chroma_loc=left:flags=accurate_rnd+bitexact,format=yuv420p -frames:v 1
+
FATE_FILTER_VSYNTH-$(call FILTERDEMDEC, SCALE, RAWVIDEO, RAWVIDEO) += fate-filter-scalechroma
fate-filter-scalechroma: tests/data/vsynth1.yuv
fate-filter-scalechroma: CMD = framecrc -flags bitexact -s 352x288 -pix_fmt yuv444p -i $(TARGET_PATH)/tests/data/vsynth1.yuv -pix_fmt yuv420p -sws_flags +bitexact -vf scale=out_chroma_loc=bottomleft
diff --git a/tests/ref/fate/filter-scalechroma-nv24 b/tests/ref/fate/filter-scalechroma-nv24
new file mode 100644
index 0000000000..90b2c3624e
--- /dev/null
+++ b/tests/ref/fate/filter-scalechroma-nv24
@@ -0,0 +1,6 @@
+#tb 0: 1/1
+#media_type 0: video
+#codec_id 0: rawvideo
+#dimensions 0: 64x64
+#sar 0: 1/1
+0, 0, 0, 1, 6144, 0x00255709
diff --git a/tests/ref/fate/filter-scalechroma-uyvy422 b/tests/ref/fate/filter-scalechroma-uyvy422
new file mode 100644
index 0000000000..d9625aa115
--- /dev/null
+++ b/tests/ref/fate/filter-scalechroma-uyvy422
@@ -0,0 +1,6 @@
+#tb 0: 1/1
+#media_type 0: video
+#codec_id 0: rawvideo
+#dimensions 0: 64x64
+#sar 0: 1/1
+0, 0, 0, 1, 6144, 0x589a5099
diff --git a/tests/ref/fate/filter-scalechroma-yuyv422 b/tests/ref/fate/filter-scalechroma-yuyv422
new file mode 100644
index 0000000000..d9625aa115
--- /dev/null
+++ b/tests/ref/fate/filter-scalechroma-yuyv422
@@ -0,0 +1,6 @@
+#tb 0: 1/1
+#media_type 0: video
+#codec_id 0: rawvideo
+#dimensions 0: 64x64
+#sar 0: 1/1
+0, 0, 0, 1, 6144, 0x589a5099
diff --git a/tests/ref/fate/filter-scalechroma-yuyv422-topleft b/tests/ref/fate/filter-scalechroma-yuyv422-topleft
new file mode 100644
index 0000000000..6155da5e11
--- /dev/null
+++ b/tests/ref/fate/filter-scalechroma-yuyv422-topleft
@@ -0,0 +1,6 @@
+#tb 0: 1/1
+#media_type 0: video
+#codec_id 0: rawvideo
+#dimensions 0: 64x64
+#sar 0: 1/1
+0, 0, 0, 1, 6144, 0x9868546e