|
|
|
@ -68,11 +68,15 @@ typedef struct { |
|
|
|
|
float saturation; |
|
|
|
|
char *saturation_expr; |
|
|
|
|
AVExpr *saturation_pexpr; |
|
|
|
|
float brightness; |
|
|
|
|
char *brightness_expr; |
|
|
|
|
AVExpr *brightness_pexpr; |
|
|
|
|
int hsub; |
|
|
|
|
int vsub; |
|
|
|
|
int32_t hue_sin; |
|
|
|
|
int32_t hue_cos; |
|
|
|
|
double var_values[VAR_NB]; |
|
|
|
|
uint8_t lut_l[256]; |
|
|
|
|
uint8_t lut_u[256][256]; |
|
|
|
|
uint8_t lut_v[256][256]; |
|
|
|
|
} HueContext; |
|
|
|
@ -86,6 +90,8 @@ static const AVOption hue_options[] = { |
|
|
|
|
{ .str = "1" }, .flags = FLAGS }, |
|
|
|
|
{ "H", "set the hue angle radians expression", OFFSET(hue_expr), AV_OPT_TYPE_STRING, |
|
|
|
|
{ .str = NULL }, .flags = FLAGS }, |
|
|
|
|
{ "b", "set the brightness expression", OFFSET(brightness_expr), AV_OPT_TYPE_STRING, |
|
|
|
|
{ .str = "0" }, .flags = FLAGS }, |
|
|
|
|
{ NULL } |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
@ -102,6 +108,16 @@ static inline void compute_sin_and_cos(HueContext *hue) |
|
|
|
|
hue->hue_cos = rint(cos(hue->hue) * (1 << 16) * hue->saturation); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static inline void create_luma_lut(HueContext *h) |
|
|
|
|
{ |
|
|
|
|
const float b = h->brightness; |
|
|
|
|
int i; |
|
|
|
|
|
|
|
|
|
for (i = 0; i < 256; i++) { |
|
|
|
|
h->lut_l[i] = av_clip_uint8(i + b * 25.5); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static inline void create_chrominance_lut(HueContext *h, const int32_t c, |
|
|
|
|
const int32_t s) |
|
|
|
|
{ |
|
|
|
@ -181,14 +197,15 @@ static av_cold int init(AVFilterContext *ctx) |
|
|
|
|
if (ret < 0) \
|
|
|
|
|
return ret; \
|
|
|
|
|
} while (0) |
|
|
|
|
SET_EXPR(brightness, "b"); |
|
|
|
|
SET_EXPR(saturation, "s"); |
|
|
|
|
SET_EXPR(hue_deg, "h"); |
|
|
|
|
SET_EXPR(hue, "H"); |
|
|
|
|
#undef SET_EXPR |
|
|
|
|
|
|
|
|
|
av_log(ctx, AV_LOG_VERBOSE, |
|
|
|
|
"H_expr:%s h_deg_expr:%s s_expr:%s\n", |
|
|
|
|
hue->hue_expr, hue->hue_deg_expr, hue->saturation_expr); |
|
|
|
|
"H_expr:%s h_deg_expr:%s s_expr:%s b_expr:%s\n", |
|
|
|
|
hue->hue_expr, hue->hue_deg_expr, hue->saturation_expr, hue->brightness_expr); |
|
|
|
|
compute_sin_and_cos(hue); |
|
|
|
|
|
|
|
|
|
return 0; |
|
|
|
@ -198,6 +215,7 @@ static av_cold void uninit(AVFilterContext *ctx) |
|
|
|
|
{ |
|
|
|
|
HueContext *hue = ctx->priv; |
|
|
|
|
|
|
|
|
|
av_expr_free(hue->brightness_pexpr); |
|
|
|
|
av_expr_free(hue->hue_deg_pexpr); |
|
|
|
|
av_expr_free(hue->hue_pexpr); |
|
|
|
|
av_expr_free(hue->saturation_pexpr); |
|
|
|
@ -235,6 +253,22 @@ static int config_props(AVFilterLink *inlink) |
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void apply_luma_lut(HueContext *s, |
|
|
|
|
uint8_t *ldst, const int dst_linesize, |
|
|
|
|
uint8_t *lsrc, const int src_linesize, |
|
|
|
|
int w, int h) |
|
|
|
|
{ |
|
|
|
|
int i; |
|
|
|
|
|
|
|
|
|
while (h--) { |
|
|
|
|
for (i = 0; i < w; i++) |
|
|
|
|
ldst[i] = s->lut_l[lsrc[i]]; |
|
|
|
|
|
|
|
|
|
lsrc += src_linesize; |
|
|
|
|
ldst += dst_linesize; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void apply_lut(HueContext *s, |
|
|
|
|
uint8_t *udst, uint8_t *vdst, const int dst_linesize, |
|
|
|
|
uint8_t *usrc, uint8_t *vsrc, const int src_linesize, |
|
|
|
@ -267,6 +301,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *inpic) |
|
|
|
|
AVFilterLink *outlink = inlink->dst->outputs[0]; |
|
|
|
|
AVFrame *outpic; |
|
|
|
|
const int32_t old_hue_sin = hue->hue_sin, old_hue_cos = hue->hue_cos; |
|
|
|
|
const float old_brightness = hue->brightness; |
|
|
|
|
int direct = 0; |
|
|
|
|
|
|
|
|
|
if (av_frame_is_writable(inpic)) { |
|
|
|
@ -296,6 +331,17 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *inpic) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (hue->brightness_expr) { |
|
|
|
|
hue->brightness = av_expr_eval(hue->brightness_pexpr, hue->var_values, NULL); |
|
|
|
|
|
|
|
|
|
if (hue->brightness < -10 || hue->brightness > 10) { |
|
|
|
|
hue->brightness = av_clipf(hue->brightness, -10, 10); |
|
|
|
|
av_log(inlink->dst, AV_LOG_WARNING, |
|
|
|
|
"Brightness value not in range [%d,%d]: clipping value to %0.1f\n", |
|
|
|
|
-10, 10, hue->brightness); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (hue->hue_deg_expr) { |
|
|
|
|
hue->hue_deg = av_expr_eval(hue->hue_deg_pexpr, hue->var_values, NULL); |
|
|
|
|
hue->hue = hue->hue_deg * M_PI / 180; |
|
|
|
@ -305,18 +351,22 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *inpic) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
av_log(inlink->dst, AV_LOG_DEBUG, |
|
|
|
|
"H:%0.1f*PI h:%0.1f s:%0.f t:%0.1f n:%d\n", |
|
|
|
|
hue->hue/M_PI, hue->hue_deg, hue->saturation, |
|
|
|
|
"H:%0.1f*PI h:%0.1f s:%0.f b:%0.f t:%0.1f n:%d\n", |
|
|
|
|
hue->hue/M_PI, hue->hue_deg, hue->saturation, hue->brightness, |
|
|
|
|
hue->var_values[VAR_T], (int)hue->var_values[VAR_N]); |
|
|
|
|
|
|
|
|
|
compute_sin_and_cos(hue); |
|
|
|
|
if (old_hue_sin != hue->hue_sin || old_hue_cos != hue->hue_cos) |
|
|
|
|
create_chrominance_lut(hue, hue->hue_cos, hue->hue_sin); |
|
|
|
|
|
|
|
|
|
if (old_brightness != hue->brightness && hue->brightness) |
|
|
|
|
create_luma_lut(hue); |
|
|
|
|
|
|
|
|
|
if (!direct) { |
|
|
|
|
av_image_copy_plane(outpic->data[0], outpic->linesize[0], |
|
|
|
|
inpic->data[0], inpic->linesize[0], |
|
|
|
|
inlink->w, inlink->h); |
|
|
|
|
if (!hue->brightness) |
|
|
|
|
av_image_copy_plane(outpic->data[0], outpic->linesize[0], |
|
|
|
|
inpic->data[0], inpic->linesize[0], |
|
|
|
|
inlink->w, inlink->h); |
|
|
|
|
if (inpic->data[3]) |
|
|
|
|
av_image_copy_plane(outpic->data[3], outpic->linesize[3], |
|
|
|
|
inpic->data[3], inpic->linesize[3], |
|
|
|
@ -327,6 +377,9 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *inpic) |
|
|
|
|
inpic->data[1], inpic->data[2], inpic->linesize[1], |
|
|
|
|
FF_CEIL_RSHIFT(inlink->w, hue->hsub), |
|
|
|
|
FF_CEIL_RSHIFT(inlink->h, hue->vsub)); |
|
|
|
|
if (hue->brightness) |
|
|
|
|
apply_luma_lut(hue, outpic->data[0], outpic->linesize[0], |
|
|
|
|
inpic->data[0], inpic->linesize[0], inlink->w, inlink->h); |
|
|
|
|
|
|
|
|
|
if (!direct) |
|
|
|
|
av_frame_free(&inpic); |
|
|
|
@ -355,6 +408,8 @@ static int process_command(AVFilterContext *ctx, const char *cmd, const char *ar |
|
|
|
|
av_freep(&hue->hue_deg_expr); |
|
|
|
|
} else if (!strcmp(cmd, "s")) { |
|
|
|
|
SET_EXPR(saturation, "s"); |
|
|
|
|
} else if (!strcmp(cmd, "b")) { |
|
|
|
|
SET_EXPR(brightness, "b"); |
|
|
|
|
} else |
|
|
|
|
return AVERROR(ENOSYS); |
|
|
|
|
|
|
|
|
|