MythTV  master
filter_adjust.c
Go to the documentation of this file.
1 /* range / gamma adjustment filter
2 */
3 
4 #include <stdlib.h>
5 #include <stdio.h>
6 
7 #include "config.h"
8 #if HAVE_STDINT_H
9 #include <stdint.h>
10 #endif
11 
12 #include <string.h>
13 #include <math.h>
14 
15 #include "filter.h"
16 #include "mythframe.h"
17 
18 #if HAVE_MMX
19 
20 #include "libavutil/mem.h"
21 #include "libavutil/cpu.h"
22 #include "ffmpeg-mmx.h"
23 
24 static const mmx_t mm_cpool[] = {
25  { .w = {1, 1, 1, 1} },
26  { .ub= {36, 36, 36, 36, 36, 36, 36, 36} },
27  { .ub= {20, 20, 20, 20, 20, 20, 20, 20} },
28  { .ub= {31, 31, 31, 31, 31, 31, 31, 31} },
29  { .ub= {15, 15, 15, 15, 15, 15, 15, 15} }
30 };
31 
32 #endif /* HAVE_MMX */
33 
34 typedef struct ThisFilter
35 {
37 
38 #if HAVE_MMX
39  int yfilt;
40  int cfilt;
41 
42  mmx_t yscale;
43  mmx_t yshift;
44  mmx_t ymin;
45 
46  mmx_t cscale;
47  mmx_t cshift;
48  mmx_t cmin;
49 #endif /* HAVE_MMX */
50 
51  uint8_t ytable[256];
52  uint8_t ctable[256];
53 
55 } ThisFilter;
56 
57 static void adjustRegion(uint8_t *buf, const uint8_t *end, const uint8_t *table)
58 {
59  while (buf < end)
60  {
61  *buf = table[*buf];
62  buf++;
63  }
64 }
65 
66 #if HAVE_MMX
67 static void adjustRegionMMX(uint8_t *buf, const uint8_t *end, const uint8_t *table,
68  const mmx_t *shift, const mmx_t *scale, const mmx_t *min,
69  const mmx_t *clamp1, const mmx_t *clamp2)
70 {
71  movq_m2r (*scale, mm6);
72  movq_m2r (*min, mm7);
73  while (buf < end - 15)
74  {
75  movq_m2r (buf[0], mm0);
76  movq_m2r (buf[8], mm2);
77  movq_m2r (*shift, mm4);
78  pxor_r2r (mm5, mm5);
79 
80  psubusb_r2r (mm7, mm0);
81  psubusb_r2r (mm7, mm2);
82 
83  movq_r2r (mm0, mm1);
84  movq_r2r (mm2, mm3);
85 
86  punpcklbw_r2r (mm5, mm0);
87  punpckhbw_r2r (mm5, mm1);
88  punpcklbw_r2r (mm5, mm2);
89  punpckhbw_r2r (mm5, mm3);
90 
91  movq_m2r (mm_cpool[0], mm5);
92 
93  psllw_r2r (mm4, mm0);
94  psllw_r2r (mm4, mm1);
95  psllw_r2r (mm4, mm2);
96  psllw_r2r (mm4, mm3);
97 
98  movq_m2r (*clamp1, mm4);
99 
100  pmulhw_r2r (mm6, mm0);
101  pmulhw_r2r (mm6, mm1);
102  pmulhw_r2r (mm6, mm2);
103  pmulhw_r2r (mm6, mm3);
104 
105  paddw_r2r (mm5, mm0);
106  paddw_r2r (mm5, mm1);
107  paddw_r2r (mm5, mm2);
108  paddw_r2r (mm5, mm3);
109 
110  movq_m2r (*clamp2, mm5);
111 
112  psrlw_i2r (1, mm0);
113  psrlw_i2r (1, mm1);
114  psrlw_i2r (1, mm2);
115  psrlw_i2r (1, mm3);
116 
117  packuswb_r2r (mm1, mm0);
118  packuswb_r2r (mm3, mm2);
119 
120  paddusb_r2r (mm4, mm0);
121  paddusb_r2r (mm4, mm2);
122 
123  psubusb_r2r (mm5, mm0);
124  psubusb_r2r (mm5, mm2);
125 
126  movq_r2m (mm0, buf[0]);
127  movq_r2m (mm2, buf[8]);
128 
129  buf += 16;
130  }
131  while (buf < end)
132  {
133  *buf = table[*buf];
134  buf++;
135  }
136 }
137 #endif /* HAVE_MMX */
138 
139 static int adjustFilter (VideoFilter *vf, VideoFrame *frame, int field)
140 {
141  (void)field;
142  ThisFilter *filter = (ThisFilter *) vf;
143  TF_VARS;
144 
145  TF_START;
146  {
147  unsigned char *ybeg = frame->buf + frame->offsets[0];
148  unsigned char *yend = ybeg + (frame->pitches[0] * frame->height);
149  int cheight = (frame->codec == FMT_YV12) ?
150  (frame->height >> 1) : frame->height;
151  unsigned char *ubeg = frame->buf + frame->offsets[1];
152  unsigned char *uend = ubeg + (frame->pitches[1] * cheight);
153  unsigned char *vbeg = frame->buf + frame->offsets[2];
154  unsigned char *vend = ubeg + (frame->pitches[2] * cheight);
155 
156 #if HAVE_MMX
157  if (filter->yfilt)
158  adjustRegionMMX(ybeg, yend, filter->ytable,
159  &(filter->yshift), &(filter->yscale),
160  &(filter->ymin), mm_cpool + 1, mm_cpool + 2);
161  else
162  adjustRegion(ybeg, yend, filter->ytable);
163 
164  if (filter->cfilt)
165  {
166  adjustRegionMMX(ubeg, uend, filter->ctable,
167  &(filter->cshift), &(filter->cscale),
168  &(filter->cmin), mm_cpool + 3, mm_cpool + 4);
169  adjustRegionMMX(vbeg, vend, filter->ctable,
170  &(filter->cshift), &(filter->cscale),
171  &(filter->cmin), mm_cpool + 3, mm_cpool + 4);
172  }
173  else
174  {
175  adjustRegion(ubeg, uend, filter->ctable);
176  adjustRegion(vbeg, vend, filter->ctable);
177  }
178 
179  if (filter->yfilt || filter->cfilt)
180  emms();
181 
182 #else /* HAVE_MMX */
183  adjustRegion(ybeg, yend, filter->ytable);
184  adjustRegion(ubeg, uend, filter->ctable);
185  adjustRegion(vbeg, vend, filter->ctable);
186 #endif /* HAVE_MMX */
187  }
188  TF_END(filter, "Adjust: ");
189  return 0;
190 }
191 
192 static void fillTable(uint8_t *table, int in_min, int in_max, int out_min,
193  int out_max, float gamma)
194 {
195  for (int i = 0; i < 256; i++)
196  {
197  float f = ((float)i - in_min) / (in_max - in_min);
198  f = f < 0.0F ? 0.0F : f;
199  f = f > 1.0F ? 1.0F : f;
200  table[i] = lround(powf (f, gamma) * (out_max - out_min) + out_min);
201  }
202 }
203 
204 #if HAVE_MMX
205 static int fillTableMMX(uint8_t *table, mmx_t *shift, mmx_t *scale, mmx_t *min,
206  int in_min, int in_max, int out_min, int out_max,
207  float gamma)
208 {
209  int shiftc, scalec, i;
210 
211  fillTable(table, in_min, in_max, out_min, out_max, gamma);
212  scalec = ((out_max - out_min) << 15)/(in_max - in_min);
213  if ((av_get_cpu_flags() & AV_CPU_FLAG_MMX) == 0 || gamma < 0.9999F ||
214  gamma > 1.00001F || scalec > 32767 << 7)
215  return 0;
216  shiftc = 2;
217  while (scalec > 32767)
218  {
219  shiftc++;
220  scalec >>= 1;
221  }
222  if (shiftc > 7)
223  return 0;
224  for (i = 0; i < 4; i++)
225  {
226  scale->w[i] = scalec;
227  }
228  for (i = 0; i < 8; i++)
229  min->b[i] = in_min;
230  shift->q = shiftc;
231  return 1;
232 }
233 #endif /* HAVE_MMX */
234 
235 static VideoFilter *
237  const int *width, const int *height, const char *options, int threads)
238 {
239  ThisFilter *filter;
240  int numopts = 0, ymin = 16, ymax = 253, cmin = 16, cmax = 240;
241  float ygamma = 1.0F, cgamma = 1.0F;
242  (void) width;
243  (void) height;
244  (void) threads;
245 
246  if (inpixfmt != outpixfmt ||
247  (inpixfmt != FMT_YV12 && inpixfmt != FMT_YUV422P))
248  {
249  fprintf(stderr, "adjust: only YV12->YV12 and YUV422P->YUV422P"
250  " conversions are supported\n");
251  return NULL;
252  }
253 
254  if (options)
255  {
256  numopts = sscanf(options, "%20d:%20d:%20f:%20d:%20d:%20f",
257  &ymin, &ymax, &ygamma,
258  &cmin, &cmax, &cgamma);
259  }
260 
261  if (numopts != 6 && (numopts !=1 && ymin != -1))
262  {
263  ymin = 16;
264  ymax = 253;
265  ygamma = 1.0F;
266  cmin = 16;
267  cmax = 240;
268  cgamma = 1.0F;
269  }
270 
271  filter = malloc (sizeof (ThisFilter));
272 
273  if (filter == NULL)
274  {
275  fprintf (stderr, "adjust: failed to allocate memory for filter\n");
276  return NULL;
277  }
278 
279  if (ymin == -1)
280  {
281  filter->vf.filter = NULL;
282  filter->vf.cleanup = NULL;
283  return (VideoFilter *) filter;
284  }
285 
286 #if HAVE_MMX
287  filter->yfilt = fillTableMMX (filter->ytable, &(filter->yshift),
288  &(filter->yscale), &(filter->ymin),
289  ymin, ymax, 16, 235, ygamma);
290  filter->cfilt = fillTableMMX (filter->ctable, &(filter->cshift),
291  &(filter->cscale), &(filter->cmin),
292  cmin, cmax, 16, 240, cgamma);
293 #else
294  fillTable (filter->ytable, ymin, ymax, 16, 235, ygamma);
295  fillTable (filter->ctable, cmin, cmax, 16, 240, cgamma);
296 #endif
297 
298  filter->vf.filter = &adjustFilter;
299  filter->vf.cleanup = NULL;
300 
301  TF_INIT(filter);
302  return (VideoFilter *) filter;
303 }
304 
305 static FmtConv FmtList[] =
306 {
307  { FMT_YV12, FMT_YV12 },
309  FMT_NULL
310 };
311 
313 {
314  {
316  .name= (char*)"adjust",
317  .descript= (char*)"adjust range and gamma of video",
318  .formats= FmtList,
319  .libname= NULL
320  },
321  FILT_NULL
322 };
int pitches[3]
Y, U, & V pitches.
Definition: mythframe.h:63
int(* filter)(struct VideoFilter_ *, VideoFrame *, int)
Definition: filter.h:37
init_filter filter_init
Definition: filter.h:28
stderr
Definition: ttvdb.py:1426
static void adjustRegion(uint8_t *buf, const uint8_t *end, const uint8_t *table)
Definition: filter_adjust.c:57
#define TF_VARS
Definition: filter.h:112
#define NULL
Definition: H264Parser.h:62
static FmtConv FmtList[]
static const mmx_t mm_cpool[]
void(* cleanup)(struct VideoFilter_ *)
Definition: filter.h:38
uint8_t ytable[256]
Definition: filter_adjust.c:51
enum FrameType_ VideoFrameType
static int adjustFilter(VideoFilter *vf, VideoFrame *frame, int field)
int offsets[3]
Y, U, & V offsets.
Definition: mythframe.h:64
VideoFilter vf
Definition: filter_adjust.c:36
int height
Definition: mythframe.h:42
#define FMT_NULL
Definition: filter.h:20
#define mmx_t
#define TF_END(filter, prefix)
Definition: filter.h:114
#define emms()
Definition: mm_arch.h:15
uint8_t ctable[256]
Definition: filter_adjust.c:52
#define FILT_NULL
Definition: filter.h:47
#define TF_INIT(filter)
Definition: filter.h:110
struct ThisFilter ThisFilter
const FilterInfo filter_table[]
static VideoFilter * newAdjustFilter(VideoFrameType inpixfmt, VideoFrameType outpixfmt, const int *width, const int *height, const char *options, int threads)
#define TF_START
Definition: filter.h:113
unsigned char * buf
Definition: mythframe.h:39
static void fillTable(uint8_t *table, int in_min, int in_max, int out_min, int out_max, float gamma)
VideoFrameType codec
Definition: mythframe.h:38