1 /*
2  * Copyright (C) 2000-2018 the xine project
3  *
4  * This file is part of xine, a free video player.
5  *
6  * xine is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * xine is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
19  *
20  * mplayer's eq2 (soft video equalizer)
21  * Software equalizer (brightness, contrast, gamma, saturation)
22  *
23  * Hampa Hug <hampa@hampa.ch> (original LUT gamma/contrast/brightness filter)
24  * Daniel Moreno <comac@comac.darktech.org> (saturation, R/G/B gamma support)
25  * Richard Felker (original MMX contrast/brightness code (vf_eq.c))
26  */
27 
28 #ifdef HAVE_CONFIG_H
29 #include "config.h"
30 #endif
31 
32 #include "planar.h"
33 
34 #include <xine/xine_internal.h>
35 #include <xine/post.h>
36 #include <xine/xineutils.h>
37 #include <math.h>
38 #include <pthread.h>
39 
40 
41 /* Per channel parameters */
42 typedef struct eq2_param_t {
43   unsigned char lut[256];
44   int           lut_clean;
45 
46   void (*adjust) (struct eq2_param_t *par, unsigned char *dst, unsigned char *src,
47     unsigned w, unsigned h, unsigned dstride, unsigned sstride);
48 
49   double        c;
50   double        b;
51   double        g;
52 } eq2_param_t;
53 
54 typedef struct vf_priv_s {
55   eq2_param_t param[3];
56 
57   double        contrast;
58   double        brightness;
59   double        saturation;
60 
61   double        gamma;
62   double        rgamma;
63   double        ggamma;
64   double        bgamma;
65 
66   unsigned      buf_w[3];
67   unsigned      buf_h[3];
68   unsigned char *buf[3];
69 } vf_eq2_t;
70 
71 static
create_lut(eq2_param_t * par)72 void create_lut (eq2_param_t *par)
73 {
74   unsigned i;
75   double   g, v;
76 
77   g = par->g;
78 
79   if ((g < 0.001) || (g > 1000.0)) {
80     g = 1.0;
81   }
82 
83   g = 1.0 / g;
84 
85   for (i = 0; i < 256; i++) {
86     v = (double) i / 255.0;
87     v = par->c * (v - 0.5) + 0.5 + par->b;
88 
89     if (v <= 0.0) {
90       par->lut[i] = 0;
91     }
92     else {
93       v = pow (v, g);
94 
95       if (v >= 1.0) {
96         par->lut[i] = 255;
97       }
98       else {
99         par->lut[i] = (unsigned char) (256.0 * v);
100       }
101     }
102   }
103 
104   par->lut_clean = 1;
105 }
106 
107 
108 #if defined(ARCH_X86)
109 
110 #if defined(ARCH_X86_64)
111 #  define MEM1(reg) "(%"reg")"
112 #  define BUMPPTR(offs,reg) "\n\tleaq\t"offs"(%"reg"), %"reg
113 #elif defined(ARCH_X86_X32)
114 #  define MEM1(reg) "(%q"reg")"
115 #  define BUMPPTR(offs,reg) "\n\tleaq\t"offs"(%q"reg"), %q"reg
116 #else
117 #  define MEM1(reg) "(%"reg")"
118 #  define BUMPPTR(offs,reg) "\n\tleal\t"offs"(%"reg"), %"reg
119 #endif
120 
121 
122 static
affine_1d_MMX(eq2_param_t * par,unsigned char * dst,unsigned char * src,unsigned w,unsigned h,unsigned dstride,unsigned sstride)123 void affine_1d_MMX (eq2_param_t *par, unsigned char *dst, unsigned char *src,
124   unsigned w, unsigned h, unsigned dstride, unsigned sstride)
125 {
126   unsigned i;
127   int      contrast, brightness;
128   unsigned dstep, sstep;
129   int      pel;
130   short    brvec[4];
131   short    contvec[4];
132 
133   contrast = (int) (par->c * 256 * 16);
134   brightness = ((int) (100.0 * par->b + 100.0) * 511) / 200 - 128 - contrast / 32;
135 
136   brvec[0] = brvec[1] = brvec[2] = brvec[3] = brightness;
137   contvec[0] = contvec[1] = contvec[2] = contvec[3] = contrast;
138 
139   sstep = sstride - w;
140   dstep = dstride - w;
141 
142   __asm__ __volatile__ (
143     "\n\tmovq\t"MEM1("0")", %%mm3"
144     "\n\tmovq\t"MEM1("1")", %%mm4"
145     "\n\tpxor\t%%mm0, %%mm0"
146     :
147     : "r" (brvec), "r" (contvec)
148   );
149 
150   while (h-- > 0) {
151     __asm__ __volatile__ (
152       "\n\tmovl\t%4, %%eax"
153       "\n\t"ASMALIGN(4)
154       "\n1:"
155       "\n\tmovq\t"MEM1("0")", %%mm1"
156       "\n\tmovq\t"MEM1("0")", %%mm2"
157       "\n\tpunpcklbw\t%%mm0, %%mm1"
158       "\n\tpunpckhbw\t%%mm0, %%mm2"
159       "\n\tpsllw\t$4, %%mm1"
160       "\n\tpsllw\t$4, %%mm2"
161       "\n\tpmulhw\t%%mm4, %%mm1"
162       "\n\tpmulhw\t%%mm4, %%mm2"
163       "\n\tpaddw\t%%mm3, %%mm1"
164       "\n\tpaddw\t%%mm3, %%mm2"
165       "\n\tpackuswb\t%%mm2, %%mm1"
166       BUMPPTR("8","0")
167       "\n\tmovq\t%%mm1, "MEM1("1")
168       BUMPPTR("8","1")
169       "\n\tdecl\t%%eax"
170       "\n\tjnz\t1b"
171       : "=r" (src), "=r" (dst)
172       : "0"  (src), "1"  (dst), "r" (w >> 3)
173       : "%eax"
174     );
175 
176     for (i = w & 7; i > 0; i--) {
177       pel = ((*src++ * contrast) >> 12) + brightness;
178       if (pel & 768) {
179         pel = (-pel) >> 31;
180       }
181       *dst++ = pel;
182     }
183 
184     src += sstep;
185     dst += dstep;
186   }
187 
188   __asm__ __volatile__ ( "emms \n\t" ::: "memory" );
189 }
190 #endif
191 
192 static
apply_lut(eq2_param_t * par,unsigned char * dst,unsigned char * src,unsigned w,unsigned h,unsigned dstride,unsigned sstride)193 void apply_lut (eq2_param_t *par, unsigned char *dst, unsigned char *src,
194   unsigned w, unsigned h, unsigned dstride, unsigned sstride)
195 {
196   unsigned      i, j;
197   unsigned char *lut;
198 
199   if (!par->lut_clean) {
200     create_lut (par);
201   }
202 
203   lut = par->lut;
204 
205   for (j = 0; j < h; j++) {
206     for (i = 0; i < w; i++) {
207       dst[i] = lut[src[i]];
208     }
209 
210     src += sstride;
211     dst += dstride;
212   }
213 }
214 
215 static
check_values(eq2_param_t * par)216 void check_values (eq2_param_t *par)
217 {
218   /* yuck! floating point comparisons... */
219 
220   if ((par->c == 1.0) && (par->b == 0.0) && (par->g == 1.0)) {
221     par->adjust = NULL;
222   }
223 #if defined(ARCH_X86)
224   else if (par->g == 1.0 && (xine_mm_accel() & MM_ACCEL_X86_MMX) ) {
225     par->adjust = &affine_1d_MMX;
226   }
227 #endif
228   else {
229     par->adjust = &apply_lut;
230   }
231 }
232 
233 
234 static
set_contrast(vf_eq2_t * eq2,double c)235 void set_contrast (vf_eq2_t *eq2, double c)
236 {
237   eq2->contrast = c;
238   eq2->param[0].c = c;
239   eq2->param[0].lut_clean = 0;
240   check_values (&eq2->param[0]);
241 }
242 
243 static
set_brightness(vf_eq2_t * eq2,double b)244 void set_brightness (vf_eq2_t *eq2, double b)
245 {
246   eq2->brightness = b;
247   eq2->param[0].b = b;
248   eq2->param[0].lut_clean = 0;
249   check_values (&eq2->param[0]);
250 }
251 
252 static
set_gamma(vf_eq2_t * eq2,double g)253 void set_gamma (vf_eq2_t *eq2, double g)
254 {
255   eq2->gamma = g;
256 
257   eq2->param[0].g = eq2->gamma * eq2->ggamma;
258   eq2->param[1].g = sqrt (eq2->bgamma / eq2->ggamma);
259   eq2->param[2].g = sqrt (eq2->rgamma / eq2->ggamma);
260 
261   eq2->param[0].lut_clean = 0;
262   eq2->param[1].lut_clean = 0;
263   eq2->param[2].lut_clean = 0;
264 
265   check_values (&eq2->param[0]);
266   check_values (&eq2->param[1]);
267   check_values (&eq2->param[2]);
268 }
269 
270 static
set_saturation(vf_eq2_t * eq2,double s)271 void set_saturation (vf_eq2_t *eq2, double s)
272 {
273   eq2->saturation = s;
274 
275   eq2->param[1].c = s;
276   eq2->param[2].c = s;
277 
278   eq2->param[1].lut_clean = 0;
279   eq2->param[2].lut_clean = 0;
280 
281   check_values (&eq2->param[1]);
282   check_values (&eq2->param[2]);
283 }
284 
285 
286 typedef struct post_plugin_eq2_s post_plugin_eq2_t;
287 
288 /*
289  * this is the struct used by "parameters api"
290  */
291 typedef struct eq2_parameters_s {
292 
293   double gamma;
294   double contrast;
295   double brightness;
296   double saturation;
297 
298   double rgamma;
299   double ggamma;
300   double bgamma;
301 
302 } eq2_parameters_t;
303 
304 /*
305  * description of params struct
306  */
307 START_PARAM_DESCR( eq2_parameters_t )
308 PARAM_ITEM( POST_PARAM_TYPE_DOUBLE, gamma, NULL, 0, 5, 0,
309             "gamma" )
310 PARAM_ITEM( POST_PARAM_TYPE_DOUBLE, brightness, NULL, -1, 1, 0,
311             "brightness" )
312 PARAM_ITEM( POST_PARAM_TYPE_DOUBLE, contrast, NULL, 0, 2, 0,
313             "contrast" )
314 PARAM_ITEM( POST_PARAM_TYPE_DOUBLE, saturation, NULL, 0, 2, 0,
315             "saturation" )
316 PARAM_ITEM( POST_PARAM_TYPE_DOUBLE, rgamma, NULL, 0, 5, 0,
317             "rgamma" )
318 PARAM_ITEM( POST_PARAM_TYPE_DOUBLE, ggamma, NULL, 0, 5, 0,
319             "ggamma" )
320 PARAM_ITEM( POST_PARAM_TYPE_DOUBLE, bgamma, NULL, 0, 5, 0,
321             "bgamma" )
322 END_PARAM_DESCR( param_descr )
323 
324 
325 /* plugin structure */
326 struct post_plugin_eq2_s {
327   post_plugin_t post;
328 
329   /* private data */
330   eq2_parameters_t   params;
331 
332   vf_eq2_t           eq2;
333 
334   pthread_mutex_t    lock;
335 };
336 
337 
set_parameters(xine_post_t * this_gen,const void * param_gen)338 static int set_parameters (xine_post_t *this_gen, const void *param_gen) {
339   post_plugin_eq2_t *this = (post_plugin_eq2_t *)this_gen;
340   const eq2_parameters_t *param = (const eq2_parameters_t *)param_gen;
341   vf_eq2_t *eq2 = &this->eq2;
342 
343   pthread_mutex_lock (&this->lock);
344 
345   if( &this->params != param )
346     memcpy( &this->params, param, sizeof(eq2_parameters_t) );
347 
348   eq2->rgamma = param->rgamma;
349   eq2->ggamma = param->ggamma;
350   eq2->bgamma = param->bgamma;
351 
352   set_gamma (eq2, param->gamma);
353   set_contrast (eq2, param->contrast);
354   set_brightness (eq2, param->brightness);
355   set_saturation (eq2, param->saturation);
356 
357   pthread_mutex_unlock (&this->lock);
358 
359   return 1;
360 }
361 
get_parameters(xine_post_t * this_gen,void * param_gen)362 static int get_parameters (xine_post_t *this_gen, void *param_gen) {
363   post_plugin_eq2_t *this = (post_plugin_eq2_t *)this_gen;
364   eq2_parameters_t *param = (eq2_parameters_t *)param_gen;
365 
366 
367   memcpy( param, &this->params, sizeof(eq2_parameters_t) );
368 
369   return 1;
370 }
371 
get_param_descr(void)372 static xine_post_api_descr_t * get_param_descr (void) {
373   return &param_descr;
374 }
375 
get_help(void)376 static char * get_help (void) {
377   return _("Alternative software equalizer that uses lookup tables (very slow), "
378            "allowing gamma correction in addition to simple brightness, "
379            "contrast and saturation adjustment.\n"
380            "Note that it uses the same MMX optimized code as 'eq' if all "
381            "gamma values are 1.0.\n"
382            "\n"
383            "Parameters\n"
384            "  gamma\n"
385            "  brightness\n"
386            "  contrast\n"
387            "  saturation\n"
388            "  rgamma (gamma for the red component)\n"
389            "  ggamma (gamma for the green component)\n"
390            "  bgamma (gamma for the blue component)\n"
391            "\n"
392            "Value ranges are 0.1 - 10 for gammas, -2 - 2 for contrast "
393            "(negative values result in a negative image), -1 - 1 for "
394            "brightness and 0 - 3 for saturation.\n"
395            "\n"
396            "* mplayer's eq2 (C) Hampa Hug, Daniel Moreno, Richard Felker\n"
397            );
398 }
399 
400 
eq2_dispose(post_plugin_t * this_gen)401 static void eq2_dispose(post_plugin_t *this_gen)
402 {
403   post_plugin_eq2_t *this = (post_plugin_eq2_t *)this_gen;
404 
405   if (_x_post_dispose(this_gen)) {
406     pthread_mutex_destroy(&this->lock);
407     free(this);
408   }
409 }
410 
411 
eq2_get_property(xine_video_port_t * port_gen,int property)412 static int eq2_get_property(xine_video_port_t *port_gen, int property) {
413   post_video_port_t *port = (post_video_port_t *)port_gen;
414   post_plugin_eq2_t *this = (post_plugin_eq2_t *)port->post;
415 
416   if( property == XINE_PARAM_VO_BRIGHTNESS )
417     return 65535 * (this->params.brightness + 1.0) / 2.0;
418   else if( property == XINE_PARAM_VO_CONTRAST )
419     return 65535 * (this->params.contrast) / 2.0;
420   else if( property == XINE_PARAM_VO_SATURATION )
421     return 65535 * (this->params.saturation) / 2.0;
422   else
423     return port->original_port->get_property(port->original_port, property);
424 }
425 
eq2_set_property(xine_video_port_t * port_gen,int property,int value)426 static int eq2_set_property(xine_video_port_t *port_gen, int property, int value) {
427   post_video_port_t *port = (post_video_port_t *)port_gen;
428   post_plugin_eq2_t *this = (post_plugin_eq2_t *)port->post;
429 
430   if( property == XINE_PARAM_VO_BRIGHTNESS ) {
431     this->params.brightness = (2.0 * value / 65535) - 1.0;
432     set_parameters ((xine_post_t *)this, &this->params);
433     return value;
434   } else if( property == XINE_PARAM_VO_CONTRAST ) {
435     this->params.contrast = (2.0 * value / 65535);
436     set_parameters ((xine_post_t *)this, &this->params);
437     return value;
438   } else if( property == XINE_PARAM_VO_SATURATION ) {
439     this->params.saturation = (2.0 * value / 65535);
440     set_parameters ((xine_post_t *)this, &this->params);
441     return value;
442   } else
443     return port->original_port->set_property(port->original_port, property, value);
444 }
445 
446 
eq2_intercept_frame(post_video_port_t * port,vo_frame_t * frame)447 static int eq2_intercept_frame(post_video_port_t *port, vo_frame_t *frame)
448 {
449   (void)port;
450   return (frame->format == XINE_IMGFMT_YV12 || frame->format == XINE_IMGFMT_YUY2);
451 }
452 
453 
eq2_draw(vo_frame_t * frame,xine_stream_t * stream)454 static int eq2_draw(vo_frame_t *frame, xine_stream_t *stream)
455 {
456   post_video_port_t *port = (post_video_port_t *)frame->port;
457   post_plugin_eq2_t *this = (post_plugin_eq2_t *)port->post;
458   vo_frame_t *out_frame;
459   vo_frame_t *yv12_frame;
460   vf_eq2_t   *eq2 = &this->eq2;
461   int skip;
462   int i;
463 
464   if( !frame->bad_frame &&
465       (eq2->param[0].adjust || eq2->param[1].adjust || eq2->param[2].adjust) ) {
466 
467     /* convert to YV12 if needed */
468     if( frame->format != XINE_IMGFMT_YV12 ) {
469 
470       yv12_frame = port->original_port->get_frame(port->original_port,
471         frame->width, frame->height, frame->ratio, XINE_IMGFMT_YV12, frame->flags | VO_BOTH_FIELDS);
472 
473       _x_post_frame_copy_down(frame, yv12_frame);
474 
475       yuy2_to_yv12(frame->base[0], frame->pitches[0],
476                    yv12_frame->base[0], yv12_frame->pitches[0],
477                    yv12_frame->base[1], yv12_frame->pitches[1],
478                    yv12_frame->base[2], yv12_frame->pitches[2],
479                    frame->width, frame->height);
480 
481     } else {
482       yv12_frame = frame;
483       yv12_frame->lock(yv12_frame);
484     }
485 
486     out_frame = port->original_port->get_frame(port->original_port,
487       frame->width, frame->height, frame->ratio, XINE_IMGFMT_YV12, frame->flags | VO_BOTH_FIELDS);
488 
489     _x_post_frame_copy_down(frame, out_frame);
490 
491     pthread_mutex_lock (&this->lock);
492 
493     for (i = 0; i < 3; i++) {
494       int height, width;
495       height = (i==0) ? frame->height : frame->height/2;
496       width = (i==0) ? frame->width : frame->width/2;
497       if (eq2->param[i].adjust != NULL) {
498         eq2->param[i].adjust (&eq2->param[i], out_frame->base[i], yv12_frame->base[i],
499           width, height, out_frame->pitches[i], yv12_frame->pitches[i]);
500       }
501       else {
502         xine_fast_memcpy(out_frame->base[i],yv12_frame->base[i],
503                          yv12_frame->pitches[i] * height);
504       }
505     }
506 
507     pthread_mutex_unlock (&this->lock);
508 
509     skip = out_frame->draw(out_frame, stream);
510 
511     _x_post_frame_copy_up(frame, out_frame);
512 
513     out_frame->free(out_frame);
514     yv12_frame->free(yv12_frame);
515 
516   } else {
517     _x_post_frame_copy_down(frame, frame->next);
518     skip = frame->next->draw(frame->next, stream);
519     _x_post_frame_copy_up(frame, frame->next);
520   }
521 
522   return skip;
523 }
524 
eq2_open_plugin(post_class_t * class_gen,int inputs,xine_audio_port_t ** audio_target,xine_video_port_t ** video_target)525 static post_plugin_t *eq2_open_plugin(post_class_t *class_gen, int inputs,
526                                       xine_audio_port_t **audio_target,
527                                       xine_video_port_t **video_target)
528 {
529   post_plugin_eq2_t *this = calloc(1, sizeof(post_plugin_eq2_t));
530   post_in_t         *input;
531   post_out_t        *output;
532   post_video_port_t *port;
533 
534   static const xine_post_api_t post_api = {
535     .set_parameters  = set_parameters,
536     .get_parameters  = get_parameters,
537     .get_param_descr = get_param_descr,
538     .get_help        = get_help,
539   };
540   static const xine_post_in_t params_input = {
541     .name = "parameters",
542     .type = XINE_POST_DATA_PARAMETERS,
543     .data = (void *)&post_api,
544   };
545 
546   if (!this || !video_target || !video_target[0]) {
547     free(this);
548     return NULL;
549   }
550 
551   (void)class_gen;
552   (void)inputs;
553   (void)audio_target;
554 
555   _x_post_init(&this->post, 0, 1);
556 
557   memset(&this->eq2, 0, sizeof(this->eq2));
558 
559   this->eq2.gamma = this->params.gamma = 1.0;
560   this->eq2.contrast = this->params.contrast = 1.0;
561   this->eq2.brightness = this->params.brightness = 0.0;
562   this->eq2.saturation = this->params.saturation = 1.0;
563   this->eq2.rgamma = this->params.rgamma = 1.0;
564   this->eq2.ggamma = this->params.ggamma = 1.0;
565   this->eq2.bgamma = this->params.bgamma = 1.0;
566 
567   pthread_mutex_init(&this->lock, NULL);
568 
569   port = _x_post_intercept_video_port(&this->post, video_target[0], &input, &output);
570   port->new_port.get_property = eq2_get_property;
571   port->new_port.set_property = eq2_set_property;
572   port->intercept_frame       = eq2_intercept_frame;
573   port->new_frame->draw       = eq2_draw;
574 
575   xine_list_push_back(this->post.input, (void *)&params_input);
576 
577   input->xine_in.name     = "video";
578   output->xine_out.name   = "eqd video";
579 
580   this->post.xine_post.video_input[0] = &port->new_port;
581 
582   this->post.dispose = eq2_dispose;
583 
584   set_parameters ((xine_post_t *)this, &this->params);
585 
586   return &this->post;
587 }
588 
eq2_init_plugin(xine_t * xine,const void * data)589 void *eq2_init_plugin(xine_t *xine, const void *data)
590 {
591   static const post_class_t post_eq2_class = {
592     .open_plugin     = eq2_open_plugin,
593     .identifier      = "eq2",
594     .description     = N_("Software video equalizer"),
595     .dispose         = NULL,
596   };
597 
598   (void)xine;
599   (void)data;
600 
601   return (void *)&post_eq2_class;
602 }
603