The horizontal scaler intermediate buffer holds signed 16-bit samples
that can be negative when scaling with filters that have negative lobes
(such as bicubic). The 16-pixel and 8-pixel NEON vector loops in
ff_yuv2planeX_10_neon treat src as signed int16_t via smlal, but the
scalar tail loop for remaining (dstW % 8) pixels used unsigned ldrh
before sxtw, interpreting negative intermediate values as large positive
values around 65536 and saturating output pixels.
Use ldrsh instead of ldrh so negative intermediate samples are properly
sign-extended in the scalar tail loop.
Signed-off-by: Marvin Scholz <epirat07@gmail.com>
exact_prod() only checked the products with the two ends of the input
range, so a coefficient times inputs in [0, 1] or [0, 2] passed as
exact although the values in between are not, and with SWS_BITEXACT the
x86 backend fused those products with FMA where the C backend rounds
them. A product is exact for every input when the coefficient is a power
of two, or when the inputs are integers whose bits fit in the
significand next to those of the coefficient.
Signed-off-by: Kacper Michajłow <kasper93@gmail.com>
With x87 math the compiler keeps the intermediate results of an
expression with excess precision. Round them, so that they produce the
same results.
Signed-off-by: Kacper Michajłow <kasper93@gmail.com>
Without AltiVec and VSX the *_altivec.c and swscale_vsx.c files only
build init functions with empty bodies that are never called. Build
them as ALTIVEC-OBJS and VSX-OBJS and drop their HAVE_ALTIVEC and
HAVE_VSX checks.
Signed-off-by: Kacper Michajłow <kasper93@gmail.com>
The PPC init functions run on every CPU to check whether AltiVec and VSX
are available, so they must not be built with the AltiVec and VSX flags
that the code next to them needs. Move the checks to swscale.c,
yuv2rgb.c and yuv2yuv.c, which hold no AltiVec code and call init
functions in the AltiVec and VSX files after them.
Signed-off-by: Kacper Michajłow <kasper93@gmail.com>
ff_sws_init_altivec_bufs() and ff_sws_free_altivec_bufs() run for every
context on every CPU and fill the banks with plain C. Keep the bank
pointers in SwsInternal as pointers to arrays of 8 int16_t, which need
no AltiVec header, and move the two functions to a file without AltiVec
code.
Signed-off-by: Kacper Michajłow <kasper93@gmail.com>
The ops backends remain disabled by default at runtime, and are only
auto-selected with SWS_UNSTABLE (or explicitly via the backends option).
The SPIR-V backend still depends on CONFIG_UNSTABLE.
The main motivating reason here is the fact that the test framework itself
wants to depend on formats only properly introduced by the new unstable
ops code. It also means we can start preferring the ops path for format
conversions that are wholly unsupported otherwise.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Niklas Haas <git@haasn.dev>
The SPIR-V backend is built on top of the ops code, which is only compiled
with CONFIG_UNSTABLE. Express this dependency in configure instead of
guarding the vulkan Makefile include and ff_sws_vk_init() call separately.
This also fixes linking with --disable-unstable when vulkan is enabled, as
graph.c referenced ff_sws_vk_device_ref() from the excluded vulkan objects.
Fixes: 51d4406e07 ("swscale/graph: support allocating hardware intermediate frames")
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Niklas Haas <git@haasn.dev>
As for op lists, avoid formatting the names of all uops when av_log()
discards them anyway.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Niklas Haas <git@haasn.dev>
ff_sws_op_list_print() formatted every op even when av_log() then
discarded the result. Op lists are printed during every graph setup, so
at the default log level this took about a quarter of the swscale
self-test runtime.
Signed-off-by: Niklas Haas <git@haasn.dev>
Print intermediate chroma results for both range directions at
8/9/10/12/14/16 bits, including neutral and nearby values. Test both
planes with the C and available native implementations, and keep
expected values in tests/ref/fate/sws-range-convert.
Based on the test implementation from Forgejo_Fairy in PR #25549.
Signed-off-by: Jun Zhao <barryjzhao@tencent.com>
solve_range_convert() anchored the range conversion offset at the upper
endpoint (src_max -> dst_max). For chroma, mapping the limited-range
interval [16, 240] to the full-range interval [0, 255] via the upper
endpoint shifts the neutral chroma center (128 << (bit_depth - 8)) to
127.5 in 8-bit units.
After 4bee010844 added the fixed-point rounding bias, that -0.5 LSB
shift canceled out the final output rounding bias for colored chroma,
leaving a systematic -0.5 LSB truncation bias in full-range chroma and
causing neutral gray to still round down to 127 on the x86 4:2:0 vertical
filter path.
Per ITU-T H.273 Section 8.3, the achromatic chroma center is
128 << (bit_depth - 8) in both limited and full range. Anchor the chroma
range conversion offset at the neutral chroma center while keeping the
luma offset anchored at the upper endpoint.
Based on the root-cause analysis and patch from Forgejo_Fairy and alec
in issues #24005 and #25521.
Fixes: https://code.ffmpeg.org/FFmpeg/FFmpeg/issues/24005
Fixes: https://code.ffmpeg.org/FFmpeg/FFmpeg/issues/25521
Signed-off-by: Jun Zhao <barryjzhao@tencent.com>
A scaled conversion between two XYZ formats decoded the codes into
gamma 2.2 RGB, scaled that and encoded it back, which quantizes twice
and clips every color outside the BT.709 gamut. Scale the codes like
those of an RGB format instead, as the unscaled conversion already did,
and round the result to 12 bits, which keeps the 4 padding bits zero.
The slice contexts of the graph take the XYZ flags of their parent for
that.
Signed-off-by: Kacper Michajłow <kasper93@gmail.com>
The tables were computed from the exponents 2.6 and 2.2 written into
the code. Build them from the lavu functions of SMPTE ST 428-1 and
gamma 2.2 instead, normalized to the top code of each curve with the
peaks in the matrix as before, so that the conversion can be set up for
any tagged transfer. The values are unchanged.
Signed-off-by: Kacper Michajłow <kasper93@gmail.com>
The gamma tables put the top code of X'Y'Z' at linear 1.0, so the
48 cd/m2 reference white of a DCDM, code 3960, came out at 0.918, about
8 percent dark. ST 428-1 (H.273 transfer 17) puts 52.37 cd/m2 at the top
code, L = 52.37/48 * V^2.6, so that the white lands on 1.0 and the codes
above it are headroom. Fold the factor into the matrices, since the X and
Z of a white exceed 1.0 and a 16-bit table cannot hold them: the headroom
clips in RGB after the matrix, and RGB white encodes to code 3960.
Signed-off-by: Kacper Michajłow <kasper93@gmail.com>
The matrices were rounded by hand from constants that do not quite
match the BT.709 chromaticities, the largest coefficient is off by 5
in Q12.
Signed-off-by: Kacper Michajłow <kasper93@gmail.com>
The XYZ12 formats are converted with the 2.6 power of ST 428-1 (DCDM)
whatever a frame is tagged with, the path is chosen by the pixel format
alone and there is no other XYZ in swscale. Say so in the name of the
table setup, to make clear that it's ST 428.
Signed-off-by: Kacper Michajłow <kasper93@gmail.com>
ff_sws_rgb2xyz built its columns from X/Y and Z/Y of each primary and
then solved for the scale that fits the white point, so the division by
y cancels out, while av_div_q by y = 0 turned the matrix into infinities
and NaNs. Use the xyz chromaticity vectors directly, which gives the
same matrix and also works for a primary with y = 0, such as the Z axis
of the CIE 1931 XYZ.
Signed-off-by: Kacper Michajłow <kasper93@gmail.com>
Regressed by the introduction of planar XYZ formats, which unintentionally
exposed XYZ formats as "supported" in libswscale's unstable path, despite XYZ
formats not yet being supported by the new ops code.
This incomplete partial support was then poorly and incorrectly worked
around by 2ed5830108, a20ae16999 and 60c8273b35. The test framework still
spams errors as a result of these three incomplete work-arounds.
Rather than pile on a third work-around (e.g. skipping XYZ in the test
framework), just do the sensible thing and properly disable support for
formats that are not yet handled correctly by the ops path.
Correct handling will require the introduction of 1D LUTs / TRCs, which
is a larger change on the roadmap.
Fixes: 2983c23762
Signed-off-by: Niklas Haas <git@haasn.dev>
This is still slightly abstraction-violating in that it needs the op list
to set up the plane mapping, image view formats and to know if the source
is interlaced or not. I don't have a good solution to these issues at
the moment, but at the very least, this decouples the actual codegen
from changes to the SwsOp layout.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Niklas Haas <git@haasn.dev>
They are currently emitted identically to an explicit zero row, which is
wrong - a zero row would not be masked as it contains an operation (setting
the component to zero *is* an explicit operation; only leaving it untouched
is an implicit noop).
And also emit a 1 coefficient in the expected location in the data matrix.
I have verified that the resulting x86 and NEON kernels are bit-identical.
Signed-off-by: Niklas Haas <git@haasn.dev>
These are entirely unneeded; prerequisite to the following fix, which fixes
the wrong `zero` mask for the masked identity rows.
Signed-off-by: Niklas Haas <git@haasn.dev>
The current CPU-based backends don't suffer any penalty or consequence
of intermixing integers and float operations on the same registers. However,
this is not a universal assumption - in particular, SPIR-V expects fully
typed operations.
In theory, older x86 CPUs also have a penalty for switching from float to
int, but ENOCARE.
Signed-off-by: Niklas Haas <git@haasn.dev>
Both libavfilter and libswscale currently put framepool.o in their regular
OBJS, defining the same four ff_frame_pool_* symbols. Loading both static
archives in full therefore fails with multiple definitions.
Move the implementation and internal header to libavutil. Include the
object in its STLIBOBJS when either consumer is enabled, and use SHLIBOBJS
for the private copies in libavfilter and libswscale. This preserves
independent builds and shared-library symbol visibility without adding
an inter-library dependency or exposing a new public API.
Verified whole-archive links with both consumers, and with each consumer
configured independently. Shared builds pass fate-filter-rgbtestsrc-gbrp12
and fate-filter-rgbtestsrc-gbrap12; static and shared video scaling and
audio volume smoke tests produce identical frame checksums. Also checked
static consumers linked against shared dependencies.
Fixes: 6c89a30ecdFixes: #25419
Assisted-by: Fairy
Interlaced cascaded scaling otherwise skips every second line.
Technically, setting the field to FIELD_TOP here is unnecessary, but it
can't hurt to be explicit.
Signed-off-by: Niklas Haas <git@haasn.dev>
Previously, the uop macros were generated from the union of uops over all
backends, so each backend would also instantiate uops that only the other
one can ever use. Split off a separate "x86" backend, and generate a separate
C header for it, together with its NASM macros in a single pass.
The x86 backend only enables SWS_UOP_FLAG_FMA on CPUs with FMA3, so generate
its uops both with and without FMA.
This does not reduce the checkasm coverage: the uops removed from
uops_macros.h are all LINEAR_FMA, READ_PLANAR_FV_FMA or RW_SHUFFLE, which
checkasm does not test, since the C reference backend cannot run them. The
only tests that disappear are the SSE4 and AVX2 variants of two U8 PERMUTE
kernels, which the x86 backend no longer builds because it never uses them.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Niklas Haas <git@haasn.dev>
Allows generating the set of uops for a specific backend. For now, only a
single "c" backend is defined, which still covers the union of uops used by
all backends.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Niklas Haas <git@haasn.dev>
Replaces the hack of running the C preprocessor over the C macros to produce
NASM syntax at build time. The generated file is now committed and checked by
FATE, as with uops_macros.h.
As a side effect, the DECL_* macros are now proper multi-line NASM macros,
so drop the parentheses around their arguments.
The FATE test is split up per generated file, with fate-sws-uops-macros
running all of them.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Niklas Haas <git@haasn.dev>
Additionally writes the uops as NASM macros to the given file, as one
DECL_<TYPE>_<UOP> macro per uop group invoking DECL_OP for each uop. This is
an alternative to running the C preprocessor over the C macros.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Niklas Haas <git@haasn.dev>
Print the hex masks with the same zero padding in both variants, and use
flat designators for the dither params. This prepares for sharing the
printing code between both variants.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Niklas Haas <git@haasn.dev>