diff -Nur mplayer-cvs/libmpcodecs/vf_eq2.c mplayer-hampa/libmpcodecs/vf_eq2.c --- mplayer-cvs/libmpcodecs/vf_eq2.c 2002-12-06 19:47:27.000000000 +0100 +++ mplayer-hampa/libmpcodecs/vf_eq2.c 2003-01-30 18:57:48.000000000 +0100 @@ -1,19 +1,32 @@ /* * vf_eq2.c * - * LUT-based software equalizer (brightness, contrast, gamma) + * LUT-based software equalizer (brightness, contrast, gamma, hue, saturation) * - * Hampa Hug + * Hampa Hug * */ +/* + This filter involves three independant steps: + - Contrast/brightness/gamma adjustment is done by applying a LUT + to the Y channel + - Saturation adjustment is done by applying a LUT to the U and V + channels + - Hue adjustment is done by direct calculations on the U and V + channels + + Hue adjustment falls a bit out of place, but this seems as good + a place as any to implement it. +*/ + #include #include #include #include -#include "../config.h" -#include "../mp_msg.h" +#include "config.h" +#include "mp_msg.h" #include "img_format.h" #include "mp_image.h" @@ -25,102 +38,326 @@ typedef struct vf_priv_s { - unsigned char *buf; - int buf_w; - int buf_h; + int do_lut_y; + int do_lut_uv; + int do_hue; double contrast; - double bright; + double brightness; double gamma; + double hue; + double saturation; + + int cos_h; + int sin_h; - unsigned char lut[256]; + unsigned char lut_y[256]; + unsigned char lut_u[256]; + unsigned char lut_v[256]; + + unsigned char *buf; + unsigned long buf_n; } vf_eq2_t; static -void create_lut (vf_eq2_t *eq2) +void check_values (vf_eq2_t *eq2) +{ + /* yuck! floating point comparisons... */ + + if ((eq2->contrast == 1.0) && (eq2->brightness == 0.0) && (eq2->gamma == 1.0)) { + eq2->do_lut_y = 0; + } + else { + eq2->do_lut_y = 1; + } + + if (eq2->saturation == 1.0) { + eq2->do_lut_uv = 0; + } + else { + eq2->do_lut_uv = 1; + } + + if (eq2->hue == 0.0) { + eq2->do_hue = 0; + } + else { + eq2->do_hue = 1; + } + + mp_msg (MSGT_VFILTER, MSGL_INFO, "vf_eq2: c=%.2f b=%.2f g=%.4f h=%.2f s=%.2f\n", + eq2->contrast, eq2->brightness, eq2->gamma, + 180.0 * eq2->hue / 3.1415926, eq2->saturation + ); +} + +static +void create_lut_y (vf_eq2_t *eq2) { unsigned i; - double c, b, g; - double v; + double g, v; - c = eq2->contrast; - b = eq2->bright; g = eq2->gamma; if ((g < 0.001) || (g > 1000.0)) { g = 1.0; } - fprintf (stderr, "vf_eq2: c=%.2f b=%.2f g=%.4f\n", c, b, g); - g = 1.0 / g; for (i = 0; i < 256; i++) { v = (double) i / 255.0; - v = c * (v - 0.5) + 0.5 + b; + v = eq2->contrast * (v - 0.5) + 0.5 + eq2->brightness; if (v <= 0.0) { - eq2->lut[i] = 0; + eq2->lut_y[i] = 0; } else { v = pow (v, g); if (v >= 1.0) { - eq2->lut[i] = 255; + eq2->lut_y[i] = 255; } else { - /* we divided by 255.0 so now we also multiply by 255.0, not - by 256.0. "+ 0.5" ensures proper rounding */ - eq2->lut[i] = (unsigned char) (255.0 * v + 0.5); + eq2->lut_y[i] = (unsigned char) (256.0 * v); } } } } -/* could inline this */ static -void process (unsigned char *dst, int dstride, unsigned char *src, int sstride, - int w, int h, unsigned char lut[256]) +void create_lut_uv (vf_eq2_t *eq2) +{ + unsigned i; + double v; + + for (i = 0; i < 256; i++) { + v = (double) i / 255.0 - 0.5; + v *= eq2->saturation; + v += 0.5; + + if (v <= 0.0) { + eq2->lut_u[i] = 0; + } + else if (v >= 1.0) { + eq2->lut_u[i] = 255; + } + else { + eq2->lut_u[i] = (unsigned char) (256.0 * v); + } + + /* The U and V LUT are identical for now. This might change. */ + eq2->lut_v[i] = eq2->lut_u[i]; + } +} + +static +void set_contrast (vf_eq2_t *eq2, double c) +{ + eq2->contrast = c; + + check_values (eq2); + create_lut_y (eq2); +} + +static +void set_brightness (vf_eq2_t *eq2, double b) +{ + eq2->brightness = b; + + check_values (eq2); + create_lut_y (eq2); +} + +static +void set_gamma (vf_eq2_t *eq2, double g) { - int i, j; + eq2->gamma = g; + + check_values (eq2); + create_lut_y (eq2); +} + +static +void set_hue (vf_eq2_t *eq2, double h) +{ + eq2->hue = h; + eq2->cos_h = (int) (1024.0 * cos (h)); + eq2->sin_h = (int) (1024.0 * sin (h)); + + check_values (eq2); +} + +static +void set_saturation (vf_eq2_t *eq2, double s) +{ + eq2->saturation = s; + + check_values (eq2); + create_lut_uv (eq2); +} + +static +void init_params (vf_eq2_t *eq2) +{ + eq2->cos_h = (int) (1024.0 * cos (eq2->hue)); + eq2->sin_h = (int) (1024.0 * sin (eq2->hue)); + + check_values (eq2); + create_lut_y (eq2); + create_lut_uv (eq2); +} + +static +void adjust_y (vf_eq2_t *eq2, mp_image_t *dst, mp_image_t *src) +{ + int i, j; + unsigned char *src_y, *dst_y; + unsigned char *lut; + + lut = eq2->lut_y; + + src_y = src->planes[0]; + dst_y = dst->planes[0]; + + for (j = 0; j < dst->h; j++) { + for (i = 0; i < dst->w; i++) { + dst_y[i] = lut[src_y[i]]; + } + + src_y += src->stride[0]; + dst_y += dst->stride[0]; + } +} + +static +void adjust_uv (vf_eq2_t *eq2, mp_image_t *dst, mp_image_t *src) +{ + int i, j, w, h; + unsigned char *src_u, *src_v, *dst_u, *dst_v; + unsigned char *lut_u, *lut_v; + + lut_u = eq2->lut_u; + lut_v = eq2->lut_v; + + src_u = src->planes[1]; + src_v = src->planes[2]; + dst_u = dst->planes[1]; + dst_v = dst->planes[2]; + + w = src->w >> src->chroma_x_shift; + h = src->h >> src->chroma_y_shift; for (j = 0; j < h; j++) { for (i = 0; i < w; i++) { - dst[i] = lut[src[i]]; + dst_u[i] = lut_u[src_u[i]]; + dst_v[i] = lut_v[src_v[i]]; } - src += sstride; - dst += dstride; + + src_u += src->stride[1]; + src_v += src->stride[2]; + dst_u += dst->stride[1]; + dst_v += dst->stride[2]; + } +} + +static +void adjust_hue (vf_eq2_t *eq2, mp_image_t *dst, mp_image_t *src) +{ + int x, y, w, h; + long u, v, t; + unsigned char *src_u, *dst_u; + unsigned char *src_v, *dst_v; + + w = dst->w >> dst->chroma_x_shift; + h = dst->h >> dst->chroma_y_shift; + + src_u = src->planes[1]; + src_v = src->planes[2]; + dst_u = dst->planes[1]; + dst_v = dst->planes[2]; + + for (y = 0; y < h; y++) { + for (x = 0; x < w; x++) { + u = (long) src_u[x] - 128; + v = (long) src_v[x] - 128; + + t = (eq2->cos_h * u - eq2->sin_h * v) / 1024; + v = (eq2->sin_h * u + eq2->cos_h * v) / 1024; + u = t; + + u += 128; + v += 128; + + u = (u < 0) ? 0 : ((u > 255) ? 255 : u); + v = (v < 0) ? 0 : ((v > 255) ? 255 : v); + + dst_u[x] = (unsigned char) u; + dst_v[x] = (unsigned char) v; + } + + src_u += src->stride[1]; + src_v += src->stride[2]; + dst_u += dst->stride[1]; + dst_v += dst->stride[2]; } } static int put_image (vf_instance_t *vf, mp_image_t *src) { - mp_image_t *dst; - vf_eq2_t *eq2; + vf_eq2_t *eq2; + mp_image_t *dst; + unsigned long img_n; eq2 = vf->priv; - if ((eq2->buf == NULL) || (eq2->buf_w != src->stride[0]) || (eq2->buf_h != src->h)) { - eq2->buf = (unsigned char *) realloc (eq2->buf, src->stride[0] * src->h); - eq2->buf_w = src->stride[0]; - eq2->buf_h = src->h; + img_n = src->w * src->h; + + if (eq2->buf_n != (3 * img_n) / 2) { + eq2->buf_n = (3 * img_n) / 2; + eq2->buf = (unsigned char *) realloc (eq2->buf, eq2->buf_n); } dst = vf_get_image (vf->next, src->imgfmt, MP_IMGTYPE_EXPORT, 0, src->w, src->h); - dst->stride[0] = src->stride[0]; - dst->stride[1] = src->stride[1]; - dst->stride[2] = src->stride[2]; - dst->planes[0] = vf->priv->buf; - dst->planes[1] = src->planes[1]; - dst->planes[2] = src->planes[2]; - - process ( - dst->planes[0], dst->stride[0], src->planes[0], src->stride[0], - src->w, src->h, eq2->lut - ); + if (eq2->do_lut_y) { + dst->planes[0] = eq2->buf; + dst->stride[0] = src->w; + } + else { + dst->planes[0] = src->planes[0]; + dst->stride[0] = src->stride[0]; + } + + if (eq2->do_lut_uv || eq2->do_hue) { + dst->planes[1] = eq2->buf + img_n; + dst->planes[2] = eq2->buf + img_n + img_n / 4; + dst->stride[1] = src->w / 2; + dst->stride[2] = src->w / 2; + } + else { + dst->planes[1] = src->planes[1]; + dst->planes[2] = src->planes[2]; + dst->stride[1] = src->stride[1]; + dst->stride[2] = src->stride[2]; + } + + if (eq2->do_lut_y) { + adjust_y (eq2, dst, src); + } + + if (eq2->do_lut_uv && eq2->do_hue) { + adjust_hue (eq2, dst, src); + adjust_uv (eq2, dst, dst); + } + else if (eq2->do_lut_uv) { + adjust_uv (eq2, dst, src); + } + else if (eq2->do_hue) { + adjust_hue (eq2, dst, src); + } return vf_next_put_image (vf, dst); } @@ -135,18 +372,23 @@ eq = (vf_equalizer_t *) data; if (strcmp (eq->item, "gamma") == 0) { - vf->priv->gamma = exp (log (8.0) * eq->value / 100.0); - create_lut (vf->priv); + set_gamma (vf->priv, exp (log (8.0) * eq->value / 100.0)); return CONTROL_TRUE; } else if (strcmp (eq->item, "contrast") == 0) { - vf->priv->contrast = (1.0 / 100.0) * (eq->value + 100); - create_lut (vf->priv); + set_contrast (vf->priv, (1.0 / 100.0) * (eq->value + 100)); return CONTROL_TRUE; } else if (strcmp (eq->item, "brightness") == 0) { - vf->priv->bright = (1.0 / 100.0) * eq->value; - create_lut (vf->priv); + set_brightness (vf->priv, (1.0 / 100.0) * eq->value); + return CONTROL_TRUE; + } + else if (strcmp (eq->item, "hue") == 0) { + set_hue (vf->priv, 3.1415926 * eq->value / 100.0); + return CONTROL_TRUE; + } + else if (strcmp (eq->item, "saturation") == 0) { + set_saturation (vf->priv, (double) (eq->value + 100) / 100.0); return CONTROL_TRUE; } break; @@ -162,7 +404,15 @@ return CONTROL_TRUE; } else if (strcmp (eq->item, "brightness") == 0) { - eq->value = (int) (100.0 * vf->priv->bright); + eq->value = (int) (100.0 * vf->priv->brightness); + return CONTROL_TRUE; + } + else if (strcmp (eq->item, "hue") == 0) { + eq->value = (int) (100.0 * vf->priv->hue / 3.1415926); + return CONTROL_TRUE; + } + else if (strcmp (eq->item, "saturation") == 0) { + eq->value = (int) (100.0 * vf->priv->saturation) - 100; return CONTROL_TRUE; } break; @@ -175,18 +425,9 @@ int query_format (vf_instance_t *vf, unsigned fmt) { switch (fmt) { - case IMGFMT_YVU9: - case IMGFMT_IF09: case IMGFMT_YV12: case IMGFMT_I420: case IMGFMT_IYUV: - case IMGFMT_CLPL: - case IMGFMT_Y800: - case IMGFMT_Y8: - case IMGFMT_NV12: - case IMGFMT_444P: - case IMGFMT_422P: - case IMGFMT_411P: return vf_next_query_format (vf, fmt); } @@ -216,24 +457,28 @@ eq2 = vf->priv; eq2->buf = NULL; - eq2->buf_w = 0; - eq2->buf_h = 0; + eq2->buf_n = 0; - eq2->gamma = 1.0; eq2->contrast = 1.0; - eq2->bright = 0.0; + eq2->brightness = 0.0; + eq2->gamma = 1.0; + eq2->hue = 0.0; + eq2->saturation = 1.0; if (args != NULL) { #ifdef USE_SETLOCALE setlocale( LC_NUMERIC, "C" ); #endif - sscanf (args, "%lf:%lf:%lf", &eq2->gamma, &eq2->contrast, &eq2->bright); + sscanf (args, "%lf:%lf:%lf:%lf:%lf", + &eq2->gamma, &eq2->contrast, &eq2->brightness, + &eq2->hue, &eq2->saturation + ); #ifdef USE_SETLOCALE setlocale( LC_NUMERIC, "" ); #endif } - create_lut (eq2); + init_params (eq2); return 1; }