Hi everyone

I have used claude (Fable 5) for auditing our filters for premultiplied support
Niklas, has written the Prompt (see below)
and ive used claude to create a table based on it and run it over the filters 
starting with a*
ive also run an adverserical agent over it trying to poke holes in the choices

My main question to you is, should we change something on the prompt or design?
Should we run this over all the filters ? (that is, will this give us enough 
confidence on what to set each filter to)

Prompt:
    Go through the list of filters in libavfilter/ and categorize them based on 
how they would interact with premultiplied alpha.

    Categorize filters into the following types:
    * Color-agnostic filters that don't touch or read the pixel data at all 
(e.g. truly metadata-only filters)
    * Metadata-only filters that read, but don't write, pixel values (e.g. 
measuring a histogram). Subcategorize as best you can into filters that would 
prefer premul, prefer independent, or genuinely don't care. (Though note that 
this is ultimately a subjective judgement call in many cases)
    * Filters modify pixel data in a way that's truly invariant under alpha 
premultiplication (e.g. applying a linear scaling factor or swapping RGB 
channels), and thus permute under (premultiply,filter) -> (filter,premultiply)
    * "Alpha-dependent" filters that inherently preserve the rgb <= alpha 
property, and would thus work with either alpha mode, but may give different 
results under premultiplied vs nonpremultiplied by virtue of some non-linearity 
that affects how pixel values are processed. These would be filters that 
*don't* commute under premultiplication. This includes e.g. scaling filters, 
crossfades, and other "linear"-type functions of several pixels that mix 
channels according to the same relative weights, as well as anything that 
applies a monotonic (non-linear) function to all channels (including A). Again, 
subcaterorize them as best you can into their alpha mode preference. (e.g. 
scaling and crossfading should always be done with premultiplied alpha, to 
avoid e.g. bleeding transparent pink padding pixels into actual pixel data)
    * Premul-incompatible filters that would definitely give broken results 
(e.g. rgb > alpha) when fed premultiplied pixel values. This would include any 
filter that does some nontrivial per-channel processing that might break the 
alpha-scaling, e.g. a brightening filter that adds some offset to the RGB 
values, or applying a nonlinear transfer function to only the R,G,B channels 
while leaving the A channel intact.

    For filters that may span multiple types depending on the selected options, 
list them under both headings; and clarify under which condition/option it 
belongs to that category. Flag edge cases and filters you're unsure about 
explicitly for human review.

Adverserial agent finding:
  1. alphamerge — wrong mechanism and wrong I/O. The straight-alpha pin is on 
the output only and does not propagate to the inputs: a premultiplied main 
input is negotiated as premul and then merely relabeled straight in 
do_alphamerge — no
  unpremultiply is inserted upstream, contrary to what the row claimed. Also 
the filter has two video inputs, so I/O is now VV->V, not V->V.
  2. avgblur-family E conditions were proper subsets of the truth. Not just 
"planes without bit 3": any mask that blurs A and color unequally (e.g. 
planes=8, alpha blurred under sharp premul RGB) is E; planes=0xF is the only D 
mask. All three D/E row
  pairs reworded.
  3. avgblur_vulkan "OOB reads return 0" was unsupported. FFmpeg enables no 
Vulkan robustness feature, so out-of-bounds imageLoad is undefined per spec; 
zero-return is common driver behavior, not a guarantee. Row now says so and 
keeps a human-review
  flag.
  4. Header convention gap: Alpha pref was undefined for E rows — now 
documented as "the only mode in which the filter is safe".

And the current results
# libavfilter video filters: premultiplied-alpha survey

Every filter from libavfilter/allfilters.c with a video INPUT (prefixes vf,
vaf, vsink), FFmpeg master 88bc27643a, 392 filters. Pure sources and
audio-to-video visualizers are excluded.

The I/O column is derived from the extern symbol prefix; a few multi-media
filters carry more streams than shown (e.g. concat is N media in/out).

Categories (how the filter behaves on premultiplied-alpha input):

- A-agnostic:  does not read or write pixel data at all (truly metadata-only)
- B-meta:      reads but never writes pixels (measurements); Alpha pref says on
               which representation the measurement is more meaningful
- C-invariant: pixel changes commute with premultiplication
               (premultiply,filter) == (filter,premultiply)
- D-alpha-dep: preserves rgb <= alpha, works in either mode, but does NOT
               commute (resampling/mixing with shared weights, monotonic
               functions applied to all channels incl. A); Alpha pref says
               which mode gives the better result (e.g. scaling: premul)
- E-incompat:  can produce broken results (rgb > alpha) on premultiplied input
               (per-channel offsets/nonlinearities on RGB with A untouched,
               writing A independently of RGB)
- sink:        consumes frames

Option-dependent filters get one row per category with the condition in Notes.

Columns:

- Alpha formats: whether the filter accepts/produces pixel formats with an
  alpha channel at all ("none" = alpha never passes through it)
- Alpha pref:  n/a | premul | straight | dont-care | option-dependent
               (for E rows: the only mode in which the filter is safe)
- Confidence:  high | medium | low (how sure the categorization is)
- Checked:     empty until a human or an adversarial agent verified the row
| Filter              | I/O        | Alpha formats                | Category    
| Alpha pref       | Confidence | Checked          | Notes
|---------------------|------------|------------------------------|-------------|------------------|------------|------------------|-------
| addroi              | V->V       | all (no constraint)          | A-agnostic  
| n/a              | high       | agent 2026-08-05 | edits ROI side data only; 
AVFILTER_FLAG_METADATA_ONLY, never touches frame->data
| alphaextract        | V->V       | all alpha fmts               | C-invariant 
| dont-care        | high       | agent 2026-08-05 | copies the A plane only, 
output has no alpha -- premultiplied input changes nothing; graph still pins 
input straight (constraint shared with extractplanes)
| alphamerge          | VV->V      | YUVA420/22/44,GBRAP,RGBA-fam | E-incompat  
| straight         | high       | agent 2026-08-05 | overwrites A verbatim from 
2nd input luma, RGB untouched -> rgb>alpha on premul; pins alpha_mode=straight 
on the OUTPUT only -- the pin does not propagate to the inputs, a premul main 
input is just relabeled straight (no conversion inserted)
| amplify             | V->V       | planar YUVA/GBRAP 8-16,f32   | E-incompat  
| straight         | high       | agent 2026-08-05 | temporal push from mean, 
threshold-gated per plane; default planes=7 leaves A untouched; nonlinear 
per-plane even with planes=15 -> still E
| ass                 | V->V       | all alpha fmts (drawutils)   | E-incompat  
| straight         | high       | agent 2026-08-05 | default alpha=0: A plane 
excluded from blending, subtitle RGB written under transparent premul px -> 
rgb>alpha; harmless on opaque video
| ass                 | V->V       | all alpha fmts (drawutils)   | D-alpha-dep 
| option-dependent | high       | agent 2026-08-05 | alpha=1: A blended, src 
color premultiplied when negotiated mode=premul; blend carries a_c^2 term 
(non-textbook OVER, exact only for opaque subtitle colors) -- confirmed in 
drawutils blend_pixel/ff_blend_mask
| atadenoise          | V->V       | planar YUVA/GBRAP 8-16       | E-incompat  
| straight         | high       | agent 2026-08-05 | default planes=7: color 
denoised per-plane (frame subsets differ per channel), A copied -> rgb>alpha 
reachable; planes=15: plane-3 ptrs never filled -> NULL deref, confirmed by 2nd 
independent code read, never executed -- human test wanted
| avgblur             | V->V       | planar YUVA/GBRAP 8-16       | D-alpha-dep 
| premul           | high       | agent 2026-08-05 | planes=0xF (default) is 
the ONLY D mask: box blur, uniform weights shared across all planes incl. A; 
420/422 chroma footprint differs from A plane
| avgblur             | V->V       | planar YUVA/GBRAP 8-16       | E-incompat  
| straight         | high       | agent 2026-08-05 | any mask blurring A and 
color unequally (e.g. 7: A stays sharp; 8: only A blurred) -> rgb>alpha at edges
| avgblur_opencl      | V->V       | hw (device-dependent)        | D-alpha-dep 
| premul           | medium     | agent 2026-08-05 | planes=0xF (default) is 
the ONLY D mask: uniform box kernel per plane, edge-renormalized; packed RGBA 
is one image, A never blurred separately (bit 0 gates the whole pixel)
| avgblur_opencl      | V->V       | hw (device-dependent)        | E-incompat  
| straight         | medium     | agent 2026-08-05 | any mask blurring a planar 
alpha plane and color unequally (e.g. 7 or 8 on GBRAP) -> rgb>alpha
| avgblur_vulkan      | V->V       | hw (device-dependent)        | D-alpha-dep 
| premul           | medium     | agent 2026-08-05 | planes=0xF (default) is 
the ONLY D mask: shared filter_norm for all channels; border imageLoad is OOB 
and FFmpeg enables no Vulkan robustness -- undefined per spec, common driver 
zero-return hits RGB and A equally (invariant kept if so) -- human review
| avgblur_vulkan      | V->V       | hw (device-dependent)        | E-incompat  
| straight         | medium     | agent 2026-08-05 | any mask blurring a planar 
alpha plane and color unequally (e.g. 7 or 8 on GBRAP) -> rgb>alpha
| backgroundkey       | V->V       |                              |             
|                  |            |                  |
| bbox                | V->V       |                              |             
|                  |            |                  |
| bench               | V->V       |                              |             
|                  |            |                  |
| bilateral           | V->V       |                              |             
|                  |            |                  |
| bilateral_cuda      | V->V       |                              |             
|                  |            |                  |




-- 
Michael     GnuPG fingerprint: 9FF2128B147EF6730BADF133611EC787040B0FAB

Take away the freedom of one citizen and you will be jailed, take away
the freedom of all citizens and you will be congratulated by your peers
in Parliament.

Attachment: signature.asc
Description: PGP signature

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

Reply via email to