@@ -25243,6 +25243,7 @@ ffplay -f lavfi life=s=300x200:mold=10:r=60:ratio=0.1:death_color=#C83232:life_c
@anchor{allrgb}
@anchor{allyuv}
@anchor{color}
+@anchor{colorspectrum}
@anchor{haldclutsrc}
@anchor{nullsrc}
@anchor{pal75bars}
@@ -25253,7 +25254,7 @@ ffplay -f lavfi life=s=300x200:mold=10:r=60:ratio=0.1:death_color=#C83232:life_c
@anchor{testsrc}
@anchor{testsrc2}
@anchor{yuvtestsrc}
-@section allrgb, allyuv, color, haldclutsrc, nullsrc, pal75bars, pal100bars, rgbtestsrc, smptebars, smptehdbars, testsrc, testsrc2, yuvtestsrc
+@section allrgb, allyuv, color, colorspectrum, haldclutsrc, nullsrc, pal75bars, pal100bars, rgbtestsrc, smptebars, smptehdbars, testsrc, testsrc2, yuvtestsrc
The @code{allrgb} source returns frames of size 4096x4096 of all rgb colors.
@@ -25261,6 +25262,8 @@ The @code{allyuv} source returns frames of size 4096x4096 of all yuv colors.
The @code{color} source provides an uniformly colored input.
+The @code{colorspectrum} source provides a color spectrum input.
+
The @code{haldclutsrc} source provides an identity Hald CLUT. See also
@ref{haldclut} filter.
@@ -25352,6 +25355,16 @@ Set the number of decimals to show in the timestamp, only available in the
The displayed timestamp value will correspond to the original
timestamp value multiplied by the power of 10 of the specified
value. Default value is 0.
+
+@item type
+Set the type of the color spectrum, only available in the
+@code{colorspectrum} source. Can be one of the following:
+
+@table @samp
+@item black
+@item white
+@item all
+@end table
@end table
@subsection Examples
@@ -528,6 +528,7 @@ OBJS-$(CONFIG_ALLRGB_FILTER) += vsrc_testsrc.o
OBJS-$(CONFIG_ALLYUV_FILTER) += vsrc_testsrc.o
OBJS-$(CONFIG_CELLAUTO_FILTER) += vsrc_cellauto.o
OBJS-$(CONFIG_COLOR_FILTER) += vsrc_testsrc.o
+OBJS-$(CONFIG_COLORSPECTRUM_FILTER) += vsrc_testsrc.o
OBJS-$(CONFIG_COREIMAGESRC_FILTER) += vf_coreimage.o
OBJS-$(CONFIG_FREI0R_SRC_FILTER) += vf_frei0r.o
OBJS-$(CONFIG_GRADIENTS_FILTER) += vsrc_gradients.o
@@ -503,6 +503,7 @@ extern const AVFilter ff_vsrc_allrgb;
extern const AVFilter ff_vsrc_allyuv;
extern const AVFilter ff_vsrc_cellauto;
extern const AVFilter ff_vsrc_color;
+extern const AVFilter ff_vsrc_colorspectrum;
extern const AVFilter ff_vsrc_coreimagesrc;
extern const AVFilter ff_vsrc_frei0r_src;
extern const AVFilter ff_vsrc_gradients;
@@ -70,6 +70,9 @@ typedef struct TestSourceContext {
/* only used by testsrc2 */
int alpha;
+ /* only used by colorspectrum */
+ int type;
+
/* only used by color */
FFDrawContext draw;
FFDrawColor color;
@@ -1791,3 +1794,92 @@ const AVFilter ff_vsrc_allrgb = {
};
#endif /* CONFIG_ALLRGB_FILTER */
+
+#if CONFIG_ALLRGB_FILTER
+
+static const AVOption colorspectrum_options[] = {
+ COMMON_OPTIONS
+ { "type", "set the color spectrum type", OFFSET(type), AV_OPT_TYPE_INT, {.i64=0}, 0, 2, FLAGS, "type" },
+ { "black","fade to black", 0, AV_OPT_TYPE_CONST,{.i64=0},0, 0, FLAGS, "type" },
+ { "white","fade to white", 0, AV_OPT_TYPE_CONST,{.i64=1},0, 0, FLAGS, "type" },
+ { "all", "white to black", 0, AV_OPT_TYPE_CONST,{.i64=2},0, 0, FLAGS, "type" },
+ { NULL }
+};
+
+AVFILTER_DEFINE_CLASS(colorspectrum);
+
+static inline float mix(float a, float b, float mix)
+{
+ return a * mix + b * (1.f - mix);
+}
+
+static void hsb2rgb(const float *c, float *rgb)
+{
+ rgb[0] = av_clipf(fabsf(fmodf(c[0] * 6.f + 0.f, 6.f) - 3.f) - 1.f, 0.f, 1.f);
+ rgb[1] = av_clipf(fabsf(fmodf(c[0] * 6.f + 4.f, 6.f) - 3.f) - 1.f, 0.f, 1.f);
+ rgb[2] = av_clipf(fabsf(fmodf(c[0] * 6.f + 2.f, 6.f) - 3.f) - 1.f, 0.f, 1.f);
+ rgb[0] = mix(c[3], (rgb[0] * rgb[0] * (3.f - 2.f * rgb[0])), c[1]) * c[2];
+ rgb[1] = mix(c[3], (rgb[1] * rgb[1] * (3.f - 2.f * rgb[1])), c[1]) * c[2];
+ rgb[2] = mix(c[3], (rgb[2] * rgb[2] * (3.f - 2.f * rgb[2])), c[1]) * c[2];
+}
+
+static void colorspectrum_fill_picture(AVFilterContext *ctx, AVFrame *frame)
+{
+ TestSourceContext *test = ctx->priv;
+ const float w = frame->width - 1.f;
+ const float h = frame->height - 1.f;
+ float c[4];
+
+ for (int y = 0; y < frame->height; y++) {
+ float *r = (float *)(frame->data[2] + y * frame->linesize[2]);
+ float *g = (float *)(frame->data[0] + y * frame->linesize[0]);
+ float *b = (float *)(frame->data[1] + y * frame->linesize[1]);
+ const float yh = y / h;
+
+ c[1] = test->type == 2 ? yh > 0.5f ? 2.f * (yh - 0.5f) : 1.f - 2.f * yh : test->type == 1 ? 1.f - yh : yh;
+ c[2] = 1.f;
+ c[3] = test->type == 1 ? 1.f : test->type == 2 ? (yh > 0.5f ? 0.f : 1.f): 0.f;
+ for (int x = 0; x < frame->width; x++) {
+ float rgb[3];
+
+ c[0] = x / w;
+ hsb2rgb(c, rgb);
+
+ r[x] = rgb[0];
+ g[x] = rgb[1];
+ b[x] = rgb[2];
+ }
+ }
+}
+
+static av_cold int colorspectrum_init(AVFilterContext *ctx)
+{
+ TestSourceContext *test = ctx->priv;
+
+ test->draw_once = 1;
+ test->fill_picture_fn = colorspectrum_fill_picture;
+ return init(ctx);
+}
+
+static const AVFilterPad avfilter_vsrc_colorspectrum_outputs[] = {
+ {
+ .name = "default",
+ .type = AVMEDIA_TYPE_VIDEO,
+ .config_props = config_props,
+ },
+};
+
+const AVFilter ff_vsrc_colorspectrum = {
+ .name = "colorspectrum",
+ .description = NULL_IF_CONFIG_SMALL("Generate colors spectrum."),
+ .priv_size = sizeof(TestSourceContext),
+ .priv_class = &colorspectrum_class,
+ .init = colorspectrum_init,
+ .uninit = uninit,
+ .activate = activate,
+ .inputs = NULL,
+ FILTER_OUTPUTS(avfilter_vsrc_colorspectrum_outputs),
+ FILTER_SINGLE_PIXFMT(AV_PIX_FMT_GBRPF32),
+};
+
+#endif /* CONFIG_ALLRGB_FILTER */
Signed-off-by: Paul B Mahol <onemda@gmail.com> --- doc/filters.texi | 15 ++++++- libavfilter/Makefile | 1 + libavfilter/allfilters.c | 1 + libavfilter/vsrc_testsrc.c | 92 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 108 insertions(+), 1 deletion(-)