MythTV  0.26-pre
filter_adjust.c
Go to the documentation of this file.
00001 /* range / gamma adjustment filter
00002 */
00003 
00004 #include <stdlib.h>
00005 #include <stdio.h>
00006 
00007 #include "config.h"
00008 #if HAVE_STDINT_H
00009 #include <stdint.h>
00010 #endif
00011 
00012 #include <string.h>
00013 #include <math.h>
00014 
00015 #include "filter.h"
00016 #include "frame.h"
00017 
00018 #if HAVE_MMX
00019 
00020 #include "libavutil/mem.h"
00021 #include "libavcodec/dsputil.h"
00022 #include "ffmpeg-mmx.h"
00023 
00024 static const mmx_t mm_cpool[] = {
00025     { w: {1, 1, 1, 1} },
00026     { ub: {36, 36, 36, 36, 36, 36, 36, 36} },
00027     { ub: {20, 20, 20, 20, 20, 20, 20, 20} },
00028     { ub: {31, 31, 31, 31, 31, 31, 31, 31} },
00029     { ub: {15, 15, 15, 15, 15, 15, 15, 15} }
00030 };
00031     
00032 #endif /* HAVE_MMX */
00033 
00034 typedef struct ThisFilter
00035 {
00036     VideoFilter vf;
00037 
00038 #if HAVE_MMX
00039     int yfilt;
00040     int cfilt;
00041 
00042     mmx_t yscale;
00043     mmx_t yshift;
00044     mmx_t ymin;
00045 
00046     mmx_t cscale;
00047     mmx_t cshift;
00048     mmx_t cmin;
00049 #endif /* HAVE_MMX */
00050 
00051     uint8_t ytable[256];
00052     uint8_t ctable[256];
00053 
00054     TF_STRUCT;
00055 } ThisFilter;
00056 
00057 static void adjustRegion(uint8_t *buf, uint8_t *end, const uint8_t *table)
00058 {
00059     while (buf < end)
00060     {
00061         *buf = table[*buf];
00062         buf++;
00063     }
00064 }
00065 
00066 #if HAVE_MMX
00067 static void adjustRegionMMX(uint8_t *buf, uint8_t *end, const uint8_t *table,
00068                      const mmx_t *shift, const mmx_t *scale, const mmx_t *min,
00069                      const mmx_t *clamp1, const mmx_t *clamp2)
00070 {
00071     movq_m2r (*scale, mm6);
00072     movq_m2r (*min, mm7);
00073     while (buf < end - 15)
00074     {
00075         movq_m2r (buf[0], mm0);
00076         movq_m2r (buf[8], mm2);
00077         movq_m2r (*shift, mm4);
00078         pxor_r2r (mm5, mm5);
00079 
00080         psubusb_r2r (mm7, mm0);
00081         psubusb_r2r (mm7, mm2);
00082 
00083         movq_r2r (mm0, mm1);
00084         movq_r2r (mm2, mm3);
00085 
00086         punpcklbw_r2r (mm5, mm0);
00087         punpckhbw_r2r (mm5, mm1);
00088         punpcklbw_r2r (mm5, mm2);
00089         punpckhbw_r2r (mm5, mm3);
00090 
00091         movq_m2r (mm_cpool[0], mm5);
00092 
00093         psllw_r2r (mm4, mm0);
00094         psllw_r2r (mm4, mm1);
00095         psllw_r2r (mm4, mm2);
00096         psllw_r2r (mm4, mm3);
00097 
00098         movq_m2r (*clamp1, mm4);
00099 
00100         pmulhw_r2r (mm6, mm0);
00101         pmulhw_r2r (mm6, mm1);
00102         pmulhw_r2r (mm6, mm2);
00103         pmulhw_r2r (mm6, mm3);
00104 
00105         paddw_r2r (mm5, mm0);
00106         paddw_r2r (mm5, mm1);
00107         paddw_r2r (mm5, mm2);
00108         paddw_r2r (mm5, mm3);
00109 
00110         movq_m2r (*clamp2, mm5);
00111 
00112         psrlw_i2r (1, mm0);
00113         psrlw_i2r (1, mm1);
00114         psrlw_i2r (1, mm2);
00115         psrlw_i2r (1, mm3);
00116 
00117         packuswb_r2r (mm1, mm0);
00118         packuswb_r2r (mm3, mm2);
00119 
00120         paddusb_r2r (mm4, mm0);
00121         paddusb_r2r (mm4, mm2);
00122 
00123         psubusb_r2r (mm5, mm0);
00124         psubusb_r2r (mm5, mm2);
00125 
00126         movq_r2m (mm0, buf[0]);
00127         movq_r2m (mm2, buf[8]);
00128 
00129         buf += 16;
00130     }
00131     while (buf < end)
00132     {
00133         *buf = table[*buf];
00134         buf++;
00135     }
00136 }
00137 #endif /* HAVE_MMX */
00138 
00139 static int adjustFilter (VideoFilter *vf, VideoFrame *frame, int field)
00140 {
00141     (void)field;
00142     ThisFilter *filter = (ThisFilter *) vf;
00143     TF_VARS;
00144 
00145     TF_START;
00146     {
00147         unsigned char *ybeg = frame->buf + frame->offsets[0];
00148         unsigned char *yend = ybeg + (frame->pitches[0] * frame->height);
00149         int cheight = (frame->codec == FMT_YV12) ?
00150             (frame->height >> 1) : frame->height;
00151         unsigned char *ubeg = frame->buf + frame->offsets[1];
00152         unsigned char *uend = ubeg + (frame->pitches[1] * cheight);
00153         unsigned char *vbeg = frame->buf + frame->offsets[2];
00154         unsigned char *vend = ubeg + (frame->pitches[2] * cheight);
00155 
00156 #if HAVE_MMX
00157         if (filter->yfilt)
00158             adjustRegionMMX(ybeg, yend, filter->ytable,
00159                             &(filter->yshift), &(filter->yscale),
00160                             &(filter->ymin), mm_cpool + 1, mm_cpool + 2);
00161         else
00162             adjustRegion(ybeg, yend, filter->ytable);
00163 
00164         if (filter->cfilt)
00165         {
00166             adjustRegionMMX(ubeg, uend, filter->ctable,
00167                             &(filter->cshift), &(filter->cscale),
00168                             &(filter->cmin), mm_cpool + 3, mm_cpool + 4);
00169             adjustRegionMMX(vbeg, vend, filter->ctable,
00170                             &(filter->cshift), &(filter->cscale),
00171                             &(filter->cmin), mm_cpool + 3, mm_cpool + 4);
00172         }
00173         else
00174         {
00175             adjustRegion(ubeg, uend, filter->ctable);
00176             adjustRegion(vbeg, vend, filter->ctable);
00177         }
00178 
00179         if (filter->yfilt || filter->cfilt)
00180             emms();
00181 
00182 #else /* HAVE_MMX */
00183         adjustRegion(ybeg, yend, filter->ytable);
00184         adjustRegion(ubeg, uend, filter->ctable);
00185         adjustRegion(vbeg, vend, filter->ctable);
00186 #endif /* HAVE_MMX */
00187     }
00188     TF_END(filter, "Adjust: ");
00189     return 0;
00190 }
00191 
00192 static void fillTable(uint8_t *table, int in_min, int in_max, int out_min,
00193                 int out_max, float gamma)
00194 {
00195     int i;
00196     float f;
00197 
00198     for (i = 0; i < 256; i++)
00199     {
00200         f = ((float)i - in_min) / (in_max - in_min);
00201         f = f < 0.0 ? 0.0 : f;
00202         f = f > 1.0 ? 1.0 : f;
00203         table[i] = (pow (f, gamma) * (out_max - out_min) + out_min + 0.5);
00204     }
00205 }
00206 
00207 #if HAVE_MMX
00208 static int fillTableMMX(uint8_t *table, mmx_t *shift, mmx_t *scale, mmx_t *min,
00209                    int in_min, int in_max, int out_min, int out_max,
00210                    float gamma)
00211 {
00212     int shiftc, scalec, i;
00213 
00214     fillTable(table, in_min, in_max, out_min, out_max, gamma);
00215     scalec = ((out_max - out_min) << 15)/(in_max - in_min);
00216     if ((av_get_cpu_flags() & AV_CPU_FLAG_MMX) == 0 || gamma < 0.9999 || 
00217         gamma > 1.00001 || scalec > 32767 << 7)
00218         return 0;
00219     shiftc = 2;
00220     while (scalec > 32767)
00221     {
00222         shiftc++;
00223         scalec >>= 1;
00224     }
00225     if (shiftc > 7)
00226         return 0;
00227     for (i = 0; i < 4; i++)
00228     {
00229         scale->w[i] = scalec;
00230     }
00231     for (i = 0; i < 8; i++)
00232         min->b[i] = in_min;
00233     shift->q = shiftc;
00234     return 1;
00235 }
00236 #endif /* HAVE_MMX */
00237 
00238 static VideoFilter *
00239 newAdjustFilter (VideoFrameType inpixfmt, VideoFrameType outpixfmt, 
00240                  int *width, int *height, char *options, int threads)
00241 {
00242     ThisFilter *filter;
00243     int numopts = 0, ymin = 16, ymax = 253, cmin = 16, cmax = 240;
00244     float ygamma = 1.0f, cgamma = 1.0f;
00245     (void) width;
00246     (void) height;
00247     (void) threads;
00248 
00249     if (inpixfmt != outpixfmt ||
00250         (inpixfmt != FMT_YV12 && inpixfmt != FMT_YUV422P))
00251     {
00252         fprintf(stderr, "adjust: only YV12->YV12 and YUV422P->YUV422P"
00253                 " conversions are supported\n");
00254         return NULL;
00255     }
00256 
00257     if (options)
00258     {
00259         numopts = sscanf(options, "%20d:%20d:%20f:%20d:%20d:%20f",
00260                          &ymin, &ymax, &ygamma,
00261                          &cmin, &cmax, &cgamma);
00262     }
00263 
00264     if (numopts != 6 && (numopts !=1 && ymin != -1))
00265     {
00266         ymin = 16;
00267         ymax = 253;
00268         ygamma = 1.0f;
00269         cmin = 16;
00270         cmax = 240;
00271         cgamma = 1.0f;
00272     }
00273 
00274     filter = malloc (sizeof (ThisFilter));
00275 
00276     if (filter == NULL)
00277     {
00278         fprintf (stderr, "adjust: failed to allocate memory for filter\n");
00279         return NULL;
00280     }
00281 
00282     if (ymin == -1)
00283     {
00284         filter->vf.filter = NULL;
00285         filter->vf.cleanup = NULL;
00286         return (VideoFilter *) filter;
00287     }
00288 
00289 #if HAVE_MMX
00290     filter->yfilt = fillTableMMX (filter->ytable, &(filter->yshift),
00291                                     &(filter->yscale), &(filter->ymin),
00292                                     ymin, ymax, 16, 235, ygamma);
00293     filter->cfilt = fillTableMMX (filter->ctable, &(filter->cshift),
00294                                     &(filter->cscale), &(filter->cmin),
00295                                     cmin, cmax, 16, 240, cgamma);
00296 #else
00297     fillTable (filter->ytable, ymin, ymax, 16, 235, ygamma);
00298     fillTable (filter->ctable, cmin, cmax, 16, 240, cgamma);
00299 #endif
00300 
00301     filter->vf.filter = &adjustFilter;
00302     filter->vf.cleanup = NULL;
00303     
00304     TF_INIT(filter);
00305     return (VideoFilter *) filter;
00306 }
00307 
00308 static FmtConv FmtList[] = 
00309 {
00310     { FMT_YV12, FMT_YV12 },
00311     { FMT_YUV422P, FMT_YUV422P },
00312     FMT_NULL
00313 };
00314 
00315 ConstFilterInfo filter_table[] = 
00316 {
00317     {
00318         filter_init: &newAdjustFilter,
00319         name:       "adjust",
00320         descript:   "adjust range and gamma of video",
00321         formats:    FmtList,
00322         libname:    NULL
00323     },
00324     FILT_NULL
00325 };
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends