PageRenderTime 792ms CodeModel.GetById 74ms app.highlight 498ms RepoModel.GetById 26ms app.codeStats 1ms

/filter/filter_logoaway.c

https://bitbucket.org/france/transcode-tcforge
C | 1098 lines | 789 code | 197 blank | 112 comment | 147 complexity | 6960aad4706dedfa1c1d913e30fa8fb0 MD5 | raw file
   1/*
   2 *  filter_logoaway.c
   3 *
   4 *  Copyright (C) Thomas Wehrspann - 2002/2003
   5 *
   6 *  This plugin is based on ideas of Krzysztof Wojdon's
   7 *  logoaway filter for VirtualDub
   8 *
   9 *  This file is part of transcode, a video stream processing tool
  10 *
  11 *  transcode is free software; you can redistribute it and/or modify
  12 *  it under the terms of the GNU General Public License as published by
  13 *  the Free Software Foundation; either version 2, or (at your option)
  14 *  any later version.
  15 *
  16 *  transcode is distributed in the hope that it will be useful,
  17 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  18 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  19 *  GNU General Public License for more details.
  20 *
  21 *  You should have received a copy of the GNU General Public License
  22 *  along with GNU Make; see the file COPYING.  If not, write to
  23 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
  24 *
  25 */
  26
  27/* TODO:
  28 - reSTYLE
  29 - docs
  30 - retesting
  31 */
  32 
  33
  34#define MOD_NAME    "filter_logoaway.so"
  35#define MOD_VERSION "v0.6.0 (2009-02-24)"
  36#define MOD_CAP     "remove an image from the video"
  37#define MOD_AUTHOR  "Thomas Wehrspann"
  38
  39#define MOD_FEATURES \
  40    TC_MODULE_FEATURE_FILTER|TC_MODULE_FEATURE_VIDEO
  41#define MOD_FLAGS \
  42    TC_MODULE_FLAG_RECONFIGURABLE
  43
  44#include "src/transcode.h"
  45#include "src/filter.h"
  46#include "libtc/libtc.h"
  47#include "libtcutil/optstr.h"
  48#include "libtcvideo/tcvideo.h"
  49#include "libtcext/tc_magick.h"
  50#include "libtcmodule/tcmodule-plugin.h"
  51
  52
  53/* FIXME */
  54enum {
  55    MODE_NONE,
  56    MODE_SOLID,
  57    MODE_XY,
  58    MODE_SHAPE
  59};
  60
  61static char *mode_name[] = {
  62    "NONE",
  63    "SOLID",
  64    "XY",
  65    "SHAPE"
  66};
  67
  68
  69static const char logoaway_help[] = ""
  70    "* Overview\n"
  71    "    This filter removes an image in a user specified area from the video.\n"
  72    "    You can choose from different methods.\n"
  73    "\n"
  74    "* Options\n"
  75    "       'range' Frame Range      (0-oo)                        [0-end]\n"
  76    "         'pos' Position         (0-width x 0-height)          [0x0]\n"
  77    "        'size' Size             (0-width x 0-height)          [10x10]\n"
  78    "        'mode' Filter Mode      (0=none,1=solid,2=xy,3=shape) [0]\n"
  79    "      'border' Visible Border\n"
  80    "        'dump' Dump filter area to file\n"
  81    "     'xweight' X-Y Weight       (0%%-100%%)                   [50]\n"
  82    "        'fill' Solid Fill Color (RRGGBB)                      [000000]\n"
  83    "        'file' Image with alpha/shape information             []\n"
  84    "\n";
  85
  86
  87typedef struct logoawayprivatedata_ LogoAwayPrivateData;
  88struct logoawayprivatedata_ {
  89    unsigned int    start, end;
  90    int             xpos, ypos;
  91    int             width, height;
  92    int             mode;
  93    int             border;
  94    int             xweight, yweight;
  95    int             rcolor, gcolor, bcolor;
  96    int             ycolor, ucolor, vcolor;
  97    char            file[PATH_MAX];
  98    int             instance;
  99
 100    int             alpha;
 101
 102    TCMagickContext logo_ctx;
 103    TCMagickContext dump_ctx;
 104    PixelPacket     *pixels;
 105
 106    int             dump;
 107    uint8_t         *dump_buf;
 108
 109    char            conf_str[TC_BUF_MIN];
 110
 111    int (*process_frame)(LogoAwayPrivateData *pd,
 112                         uint8_t *buffer, int width, int height);
 113};
 114
 115
 116/*********************************************************
 117 * blend two pixel
 118 * this function blends two pixel with the given
 119 * weight
 120 * @param   srcPixel        source pixel value
 121 *          destPixel       source pixel value
 122 *          alpha           weight
 123 * @return  unsigned char   new pixel value
 124 *********************************************************/
 125static uint8_t alpha_blending(uint8_t srcPixel, uint8_t destPixel, int alpha)
 126{
 127  return (((alpha * (srcPixel - destPixel) ) >> 8 ) + destPixel);
 128}
 129
 130
 131static void dump_image_rgb(LogoAwayPrivateData *pd,
 132                           uint8_t *buffer, int width, int height)
 133{
 134    int row = 0, col = 0, buf_off = 0, pkt_off = 0;
 135    int ret = TC_OK;
 136
 137    for (row = pd->ypos; row < pd->height; row++) {
 138        for (col = pd->xpos; col < pd->width; col++) {
 139            pkt_off = ((row-pd->ypos)*(pd->width-pd->xpos)+(col-pd->xpos)) * 3;
 140            buf_off = ((height-row)*width+col) * 3;
 141            /* R */
 142            pd->dump_buf[pkt_off +0] = buffer[buf_off +0];
 143            /* G */
 144            pd->dump_buf[pkt_off +1] = buffer[buf_off +1];
 145            /* B */
 146            pd->dump_buf[pkt_off +2] = buffer[buf_off +2];
 147        }
 148    }
 149
 150    ret = tc_magick_RGBin(&pd->dump_ctx,
 151                          pd->width  - pd->xpos,
 152                          pd->height - pd->ypos,
 153                          pd->dump_buf);
 154    if (ret != TC_OK) {
 155        tc_log_error(MOD_NAME, "FIXME");
 156    } else {
 157        tc_snprintf(pd->dump_ctx.image_info->filename,
 158                    MaxTextExtent, "dump[%d].png", pd->instance); /* FIXME */
 159
 160        WriteImage(pd->dump_ctx.image_info, pd->dump_ctx.image);
 161    }
 162}
 163
 164/* FIXME: both the inner if(N&1)s can be factored out */
 165static void draw_border_rgb(LogoAwayPrivateData *pd,
 166                            uint8_t *buffer, int width, int height)
 167{
 168    int row = 0, col = 0, buf_off = 0;
 169
 170    for (row = pd->ypos; row < pd->height; row++) {
 171        if ((row == pd->ypos) || (row==pd->height-1)) {
 172            for (col = pd->xpos*3; col < pd->width*3; col++)
 173                if (col & 1)
 174                    buffer[((height-row)*width*3+col)] = 255 & 0xff;
 175        }
 176        if (row & 1) {
 177            buf_off = ((height-row)*width+pd->xpos)*3;
 178            buffer[buf_off +0] = 255;
 179            buffer[buf_off +1] = 255;
 180            buffer[buf_off +2] = 255;
 181
 182            buf_off = ((height-row)*width+pd->width)*3;
 183            buffer[buf_off +0] = 255;
 184            buffer[buf_off +1] = 255;
 185            buffer[buf_off +2] = 255;
 186        }
 187    }
 188}
 189
 190
 191/* FIXME: both the inner if(N&1)s can be factored out */
 192static void draw_border_yuv(LogoAwayPrivateData *pd,
 193                            uint8_t *buffer, int width, int height)
 194{
 195    int row = 0, col = 0;
 196
 197    for (row = pd->ypos; row < pd->height; row++) {
 198        if ((row == pd->ypos) || (row == pd->height - 1)) {
 199            for (col = pd->xpos; col < pd->width; col++)
 200                if (col & 1)
 201                    buffer[row*width+col] = 255 & 0xff;
 202        }
 203        if (row & 1) {
 204            buffer[row*width+pd->xpos]  = 255 & 0xff;
 205            buffer[row*width+pd->width] = 255 & 0xff;
 206        }
 207    }
 208}
 209
 210/*************************************************************************/
 211
 212static int process_frame_null(LogoAwayPrivateData *pd,
 213                              uint8_t *buffer, int width, int height)
 214{
 215    return TC_OK;
 216}
 217
 218/*************************************************************************/
 219
 220static int process_frame_rgb_solid(LogoAwayPrivateData *pd,
 221                                   uint8_t *buffer, int width, int height)
 222{
 223    int row, col, buf_off, pkt_off;
 224    uint8_t px;
 225
 226    if (pd->dump) {
 227        dump_image_rgb(pd, buffer, width, height);
 228    }
 229
 230    for (row = pd->ypos; row < pd->height; row++) {
 231        for (col = pd->xpos; col < pd->width; col++) {
 232            buf_off = ((height-row)*width+col) * 3;
 233            pkt_off = (row-pd->ypos) * (pd->width-pd->xpos) + (col-pd->xpos);
 234            /* R */
 235            if (!pd->alpha) {
 236                buffer[buf_off +0] = pd->rcolor;
 237                buffer[buf_off +1] = pd->gcolor;
 238                buffer[buf_off +2] = pd->bcolor;
 239            } else {
 240                px = (uint8_t)ScaleQuantumToChar(pd->pixels[pkt_off].red);
 241                buffer[buf_off +0] = alpha_blending(buffer[buf_off +0], pd->rcolor, px);
 242                /* G */
 243                px = (uint8_t)ScaleQuantumToChar(pd->pixels[pkt_off].green);
 244                buffer[buf_off +1] = alpha_blending(buffer[buf_off +1], pd->gcolor, px);
 245                /* B */
 246                px = (uint8_t)ScaleQuantumToChar(pd->pixels[pkt_off].blue);
 247                buffer[buf_off +2] = alpha_blending(buffer[buf_off +2], pd->bcolor, px);
 248            }
 249        }
 250    }
 251 
 252    if (pd->border) {
 253        draw_border_rgb(pd, buffer, width, height);
 254    }
 255    return TC_OK;
 256}
 257
 258
 259static int process_frame_rgb_xy(LogoAwayPrivateData *pd,
 260                                uint8_t *buffer, int width, int height)
 261{
 262    int row, col, xdistance, ydistance, distance_west, distance_north;
 263    unsigned char hcalc, vcalc;
 264    int buf_off, pkt_off, buf_off_xpos, buf_off_width, buf_off_ypos, buf_off_height;
 265    int alpha_hori, alpha_vert;
 266    uint8_t npx[3], px[3];
 267
 268    if (pd->dump) {
 269        dump_image_rgb(pd, buffer, width, height);
 270    }
 271
 272    xdistance = 256 / (pd->width - pd->xpos);
 273    ydistance = 256 / (pd->height - pd->ypos);
 274    for (row = pd->ypos; row < pd->height; row++) {
 275        distance_north = pd->height - row;
 276        alpha_vert = ydistance * distance_north;
 277
 278        buf_off_xpos = ((height-row)*width+pd->xpos) * 3;
 279        buf_off_width = ((height-row)*width+pd->width) * 3;
 280
 281        for (col = pd->xpos; col < pd->width; col++) {
 282            distance_west  = pd->width - col;
 283
 284            alpha_hori = xdistance * distance_west;
 285
 286            buf_off_ypos = ((height-pd->ypos)*width+col) * 3;
 287            buf_off_height = ((height-pd->height)*width+col) * 3;
 288            buf_off = ((height-row)*width+col) * 3;
 289
 290            pkt_off = (row-pd->ypos) * (pd->width-pd->xpos) + (col-pd->xpos);
 291
 292            /* R */
 293            hcalc  = alpha_blending(buffer[buf_off_xpos +0], buffer[buf_off_width  +0], alpha_hori);
 294            vcalc  = alpha_blending(buffer[buf_off_ypos +0], buffer[buf_off_height +0], alpha_vert);
 295            npx[0] = ((hcalc*pd->xweight + vcalc*pd->yweight)/100);
 296            /* G */
 297            hcalc  = alpha_blending(buffer[buf_off_xpos +1], buffer[buf_off_width  +1], alpha_hori);
 298            vcalc  = alpha_blending(buffer[buf_off_ypos +1], buffer[buf_off_height +1], alpha_vert);
 299            npx[1] = ((hcalc*pd->xweight + vcalc*pd->yweight)/100);
 300            /* B */
 301            hcalc  = alpha_blending(buffer[buf_off_xpos +2], buffer[buf_off_width  +2], alpha_hori);
 302            vcalc  = alpha_blending(buffer[buf_off_ypos +2], buffer[buf_off_height +2], alpha_vert);
 303            npx[2] = ((hcalc*pd->xweight + vcalc*pd->yweight)/100);
 304            if (!pd->alpha) {
 305                buffer[buf_off +0] = npx[0];
 306                buffer[buf_off +1] = npx[1];
 307                buffer[buf_off +2] = npx[2];
 308            } else {
 309                px[0] = (uint8_t)ScaleQuantumToChar(pd->pixels[pkt_off].red);
 310                px[1] = (uint8_t)ScaleQuantumToChar(pd->pixels[pkt_off].green);
 311                px[2] = (uint8_t)ScaleQuantumToChar(pd->pixels[pkt_off].blue);
 312                buffer[buf_off +0] = alpha_blending(buffer[buf_off +0], npx[0], px[0]);
 313                buffer[buf_off +1] = alpha_blending(buffer[buf_off +1], npx[1], px[1]);
 314                buffer[buf_off +2] = alpha_blending(buffer[buf_off +2], npx[2], px[2]);
 315            }
 316        }
 317    }
 318
 319    if (pd->border) {
 320        draw_border_rgb(pd, buffer, width, height);
 321    }
 322    return TC_OK;
 323}
 324
 325
 326static int process_frame_rgb_shape(LogoAwayPrivateData *pd,
 327                                   uint8_t *buffer, int width, int height)
 328{
 329    int row, col, i = 0;
 330    int xdistance, ydistance, distance_west, distance_north;
 331    unsigned char hcalc, vcalc;
 332    int buf_off, pkt_off, buf_off_xpos, buf_off_width, buf_off_ypos, buf_off_height;
 333    int alpha_hori, alpha_vert;
 334    uint8_t tmpx, px[3], npx[3];
 335
 336    if (pd->dump) {
 337        dump_image_rgb(pd, buffer, width, height);
 338    }
 339
 340    xdistance = 256 / (pd->width - pd->xpos);
 341    ydistance = 256 / (pd->height - pd->ypos);
 342    for (row = pd->ypos; row<pd->height; row++) {
 343        distance_north = pd->height - row;
 344
 345        alpha_vert = ydistance * distance_north;
 346
 347        for (col = pd->xpos; col<pd->width; col++) {
 348            distance_west  = pd->width - col;
 349
 350            alpha_hori = xdistance * distance_west;
 351
 352            buf_off = ((height-row)*width+col) * 3;
 353            pkt_off = (row-pd->ypos) * (pd->width-pd->xpos) + (col-pd->xpos);
 354
 355            buf_off_xpos = ((height-row)*width+pd->xpos) * 3;
 356            buf_off_width = ((height-row)*width+pd->width) * 3;
 357            buf_off_ypos = ((height-pd->ypos)*width+col) * 3;
 358            buf_off_height = ((height-pd->height)*width+col) * 3;
 359
 360            tmpx = (uint8_t)ScaleQuantumToChar(pd->pixels[pkt_off-i].red);
 361            i = 0;
 362            while ((tmpx != 255) && (col-i > pd->xpos))
 363                i++;
 364            buf_off_xpos   = ((height-row)*width + col-i) * 3;
 365            tmpx = (uint8_t)ScaleQuantumToChar(pd->pixels[pkt_off+i].red);
 366            i = 0;
 367            while ((tmpx != 255) && (col + i < pd->width))
 368                i++;
 369            buf_off_width  = ((height-row)*width + col+i) * 3;
 370
 371            tmpx = (uint8_t)ScaleQuantumToChar(pd->pixels[pkt_off-i*(pd->width-pd->xpos)].red);
 372            i = 0;
 373            while ((tmpx != 255) && (row - i > pd->ypos))
 374                i++;
 375            buf_off_ypos   = (height*width*3)-((row-i)*width - col) * 3;
 376            tmpx = (uint8_t)ScaleQuantumToChar(pd->pixels[pkt_off+i*(pd->width-pd->xpos)].red);
 377            i = 0;
 378            while ((tmpx != 255) && (row + i < pd->height))
 379                i++;
 380            buf_off_height = (height*width*3)-((row+i)*width - col) * 3;
 381
 382            /* R */
 383            hcalc  = alpha_blending(buffer[buf_off_xpos +0], buffer[buf_off_width  +0], alpha_hori);
 384            vcalc  = alpha_blending(buffer[buf_off_ypos +0], buffer[buf_off_height +0], alpha_vert);
 385            npx[0] = (hcalc*pd->xweight + vcalc*pd->yweight)/100;
 386            px[0]  = (uint8_t)ScaleQuantumToChar(pd->pixels[pkt_off].red);
 387            /* G */
 388            hcalc = alpha_blending(buffer[buf_off_xpos +1], buffer[buf_off_width  +1], alpha_hori);
 389            vcalc = alpha_blending(buffer[buf_off_ypos +1], buffer[buf_off_height +1], alpha_vert);
 390            npx[1] = (hcalc*pd->xweight + vcalc*pd->yweight)/100;
 391            px[1] = (uint8_t)ScaleQuantumToChar(pd->pixels[pkt_off].green);
 392            /* B */
 393            hcalc = alpha_blending(buffer[buf_off_xpos +2], buffer[buf_off_width  +2], alpha_hori);
 394            vcalc = alpha_blending(buffer[buf_off_ypos +2], buffer[buf_off_height +2], alpha_vert);
 395            npx[2] = (hcalc*pd->xweight + vcalc*pd->yweight)/100;
 396            px[2] = (uint8_t)ScaleQuantumToChar(pd->pixels[pkt_off].blue);
 397
 398            buffer[buf_off +0] = alpha_blending(buffer[buf_off +0], npx[0], px[0]);
 399            buffer[buf_off +0] = alpha_blending(buffer[buf_off +0], npx[1], px[1]);
 400            buffer[buf_off +0] = alpha_blending(buffer[buf_off +0], npx[2], px[2]);
 401        }
 402    }
 403
 404    if (pd->border) {
 405        draw_border_rgb(pd, buffer, width, height);
 406    }
 407    return TC_OK;
 408}
 409
 410static int process_frame_yuv_solid(LogoAwayPrivateData *pd,
 411                                   uint8_t *buffer, int width, int height)
 412{
 413    uint8_t px;
 414    int row, col, craddr, cbaddr, buf_off, pkt_off=0;
 415
 416    craddr = (width * height);
 417    cbaddr = (width * height) * 5 / 4;
 418
 419    /* Y */
 420    for (row = pd->ypos; row < pd->height; row++) {
 421        for (col = pd->xpos; col < pd->width; col++) {
 422
 423            buf_off = row * width + col;
 424            pkt_off = (row - pd->ypos) * (pd->width - pd->xpos) + (col - pd->xpos);
 425            if (!pd->alpha) {
 426                buffer[buf_off] = pd->ycolor;
 427            } else {
 428                px = (uint8_t)ScaleQuantumToChar(pd->pixels[pkt_off].red);
 429                buffer[buf_off] = alpha_blending(buffer[buf_off], pd->ycolor, px);
 430            }
 431        }
 432    }
 433
 434    /* Cb, Cr */
 435    for(row = pd->ypos/2+1; row < pd->height/2; row++) {
 436        for(col = pd->xpos/2+1; col < pd->width/2; col++) {
 437            buf_off = row * width/2 + col;
 438            pkt_off = (row * 2 - pd->ypos) * (pd->width - pd->xpos) + (col * 2 - pd->xpos);
 439
 440            if (!pd->alpha) {
 441                buffer[craddr + buf_off] = pd->ucolor;
 442                buffer[cbaddr + buf_off] = pd->vcolor;
 443            } else {
 444                /* sic */
 445                px = (uint8_t)ScaleQuantumToChar(pd->pixels[pkt_off].red);
 446                buffer[craddr + buf_off] = alpha_blending(buffer[craddr + buf_off], pd->ucolor, px);
 447                buffer[cbaddr + buf_off] = alpha_blending(buffer[cbaddr + buf_off], pd->vcolor, px);
 448            }
 449        }
 450    }
 451
 452    if (pd->border) {
 453        draw_border_yuv(pd, buffer, width, height);
 454    }
 455    return TC_OK;
 456}
 457
 458static int process_frame_yuv_xy(LogoAwayPrivateData *pd,
 459                                uint8_t *buffer, int width, int height)
 460{
 461    int row, col, craddr, cbaddr;
 462    int xdistance, ydistance, distance_west, distance_north;
 463    unsigned char hcalc, vcalc;
 464    int buf_off, pkt_off=0, buf_off_xpos, buf_off_width, buf_off_ypos, buf_off_height;
 465    int alpha_hori, alpha_vert;
 466    uint8_t px;
 467
 468    craddr = (width * height);
 469    cbaddr = (width * height) * 5 / 4;
 470
 471    /* Y' */
 472      xdistance = 256 / (pd->width - pd->xpos);
 473      ydistance = 256 / (pd->height - pd->ypos);
 474      for(row=pd->ypos; row<pd->height; row++) {
 475        distance_north = pd->height - row;
 476
 477        alpha_vert = ydistance * distance_north;
 478
 479        buf_off_xpos = row*width+pd->xpos;
 480        buf_off_width = row*width+pd->width;
 481
 482        for(col=pd->xpos; col<pd->width; col++) {
 483          uint8_t npx;
 484          distance_west  = pd->width - col;
 485
 486          alpha_hori = xdistance * distance_west;
 487
 488          buf_off = row*width+col;
 489          buf_off_ypos = pd->ypos*width+col;
 490          buf_off_height = pd->height*width+col;
 491
 492          pkt_off = (row-pd->ypos) * (pd->width-pd->xpos) + (col-pd->xpos);
 493
 494          hcalc = alpha_blending(buffer[buf_off_xpos], buffer[buf_off_width],  alpha_hori);
 495          vcalc = alpha_blending(buffer[buf_off_ypos], buffer[buf_off_height], alpha_vert);
 496          npx   = ((hcalc*pd->xweight + vcalc*pd->yweight)/100);
 497          if (!pd->alpha) {
 498            buffer[buf_off] = npx;
 499          } else {
 500            px = (uint8_t)ScaleQuantumToChar(pd->pixels[pkt_off].red);
 501            buffer[buf_off] = alpha_blending(buffer[buf_off], npx, px);
 502          }
 503        }
 504      }
 505
 506      /* Cb, Cr */
 507      xdistance = 512 / (pd->width - pd->xpos);
 508      ydistance = 512 / (pd->height - pd->ypos);
 509      for (row=pd->ypos/2+1; row<pd->height/2; row++) {
 510        distance_north = pd->height/2 - row;
 511
 512        alpha_vert = ydistance * distance_north;
 513
 514        buf_off_xpos = row*width/2+pd->xpos/2;
 515        buf_off_width = row*width/2+pd->width/2;
 516
 517        for (col=pd->xpos/2+1; col<pd->width/2; col++) {
 518          uint8_t npx[2];
 519          distance_west  = pd->width/2 - col;
 520
 521          alpha_hori = xdistance * distance_west;
 522
 523          buf_off = row*width/2+col;
 524          buf_off_ypos = pd->ypos/2*width/2+col;
 525          buf_off_height = pd->height/2*width/2+col;
 526
 527          pkt_off = (row*2-pd->ypos) * (pd->width-pd->xpos) + (col*2-pd->xpos);
 528
 529          hcalc  = alpha_blending(buffer[craddr + buf_off_xpos], buffer[craddr + buf_off_width],  alpha_hori);
 530          vcalc  = alpha_blending(buffer[craddr + buf_off_ypos], buffer[craddr + buf_off_height], alpha_vert);
 531          npx[0] = ((hcalc*pd->xweight + vcalc*pd->yweight)/100);
 532          hcalc = alpha_blending( buffer[cbaddr + buf_off_xpos], buffer[cbaddr + buf_off_width],  alpha_hori );
 533          vcalc = alpha_blending( buffer[cbaddr + buf_off_ypos], buffer[cbaddr + buf_off_height], alpha_vert );
 534          npx[1] = ((hcalc*pd->xweight + vcalc*pd->yweight)/100);
 535
 536          if (!pd->alpha) {
 537            buffer[craddr + buf_off] = npx[0];
 538            buffer[cbaddr + buf_off] = npx[1];
 539          } else {
 540            px = (uint8_t)ScaleQuantumToChar(pd->pixels[pkt_off].red); /* sic */
 541            buffer[craddr + buf_off] = alpha_blending(buffer[craddr + buf_off], npx[0], px);
 542            buffer[craddr + buf_off] = alpha_blending(buffer[craddr + buf_off], npx[1], px);
 543          }
 544        }
 545      }
 546
 547    if (pd->border) {
 548        draw_border_yuv(pd, buffer, width, height);
 549    }
 550    return TC_OK;
 551}
 552
 553static int process_frame_yuv_shape(LogoAwayPrivateData *pd,
 554                                   uint8_t *buffer, int width, int height)
 555{
 556    int row, col, i;
 557    int craddr, cbaddr;
 558    int xdistance, ydistance, distance_west, distance_north;
 559    unsigned char hcalc, vcalc;
 560    int buf_off, pkt_off=0, buf_off_xpos, buf_off_width, buf_off_ypos, buf_off_height;
 561    int alpha_hori, alpha_vert;
 562    uint8_t px, npx[3];
 563
 564    craddr = (width * height);
 565    cbaddr = (width * height) * 5 / 4;
 566
 567      xdistance = 256 / (pd->width - pd->xpos);
 568      ydistance = 256 / (pd->height - pd->ypos);
 569      for(row=pd->ypos; row<pd->height; row++) {
 570        distance_north = pd->height - row;
 571
 572        alpha_vert = ydistance * distance_north;
 573
 574        for(col=pd->xpos; col<pd->width; col++) {
 575          distance_west  = pd->width - col;
 576
 577          alpha_hori = xdistance * distance_west;
 578
 579          buf_off = (row*width+col);
 580          pkt_off = (row-pd->ypos) * (pd->width-pd->xpos) + (col-pd->xpos);
 581
 582          i=0;
 583          px = (uint8_t)ScaleQuantumToChar(pd->pixels[pkt_off-i].red);
 584          while( (px != 255) && (col-i>pd->xpos) ) i++;
 585          buf_off_xpos   = (row*width + col-i);
 586          i=0;
 587          px = (uint8_t)ScaleQuantumToChar(pd->pixels[pkt_off+i].red);
 588          while( (px != 255) && (col+i<pd->width) ) i++;
 589          buf_off_width  = (row*width + col+i);
 590
 591          i=0;
 592          px = (uint8_t)ScaleQuantumToChar(pd->pixels[pkt_off-i*(pd->width-pd->xpos)].red);
 593          while( (px != 255) && (row-i>pd->ypos) ) i++;
 594          buf_off_ypos   = ((row-i)*width + col);
 595          i=0;
 596          px = (uint8_t)ScaleQuantumToChar(pd->pixels[pkt_off+i*(pd->width-pd->xpos)].red);
 597          while( (px != 255) && (row+i<pd->height) ) i++;
 598          buf_off_height = ((row+i)*width + col);
 599
 600          hcalc  = alpha_blending( buffer[buf_off_xpos], buffer[buf_off_width],  alpha_hori );
 601          vcalc  = alpha_blending( buffer[buf_off_ypos], buffer[buf_off_height], alpha_vert );
 602          px     = (uint8_t)ScaleQuantumToChar(pd->pixels[pkt_off].red);
 603          npx[0] = ((hcalc*pd->xweight + vcalc*pd->yweight)/100); /* FIXME */
 604          buffer[buf_off] = alpha_blending(buffer[buf_off], npx[0], px);
 605        }
 606      }
 607
 608      /* Cb, Cr */
 609      xdistance = 512 / (pd->width - pd->xpos);
 610      ydistance = 512 / (pd->height - pd->ypos);
 611      for (row=pd->ypos/2+1; row<pd->height/2; row++) {
 612        distance_north = pd->height/2 - row;
 613
 614        alpha_vert = ydistance * distance_north;
 615
 616        for (col=pd->xpos/2+1; col<pd->width/2; col++) {
 617          distance_west  = pd->width/2 - col;
 618
 619          alpha_hori = xdistance * distance_west;
 620
 621          i=0;
 622          px = (uint8_t)ScaleQuantumToChar(pd->pixels[pkt_off-i].red);
 623          while( (px != 255) && (col-i>pd->xpos) ) i++;
 624          buf_off_xpos   = (row*width/2 + col-i);
 625          i=0;
 626          px = (uint8_t)ScaleQuantumToChar(pd->pixels[pkt_off+i].red);
 627          while( (px != 255) && (col+i<pd->width) ) i++;
 628          buf_off_width  = (row*width/2 + col+i);
 629
 630          i=0;
 631          px = (uint8_t)ScaleQuantumToChar(pd->pixels[pkt_off-i*(pd->width-pd->xpos)].red);
 632          while( (px != 255) && (row-i>pd->ypos) ) i++;
 633          buf_off_ypos   = ((row-i)*width/2 + col);
 634          i=0;
 635          px = (uint8_t)ScaleQuantumToChar(pd->pixels[pkt_off+i*(pd->width-pd->xpos)].red);
 636          while( (px != 255) && (row+i<pd->height) ) i++;
 637          buf_off_height = ((row+i)*width/2 + col);
 638
 639          buf_off = row*width/2+col;
 640          buf_off_ypos = pd->ypos/2*width/2+col;
 641          buf_off_height = pd->height/2*width/2+col;
 642
 643          pkt_off = (row*2-pd->ypos) * (pd->width-pd->xpos) + (col*2-pd->xpos);
 644
 645          px     = (uint8_t)ScaleQuantumToChar(pd->pixels[pkt_off].red);
 646          /* sic */
 647          hcalc  = alpha_blending(buffer[craddr + buf_off_xpos], buffer[craddr + buf_off_width],  alpha_hori);
 648          vcalc  = alpha_blending(buffer[craddr + buf_off_ypos], buffer[craddr + buf_off_height], alpha_vert);
 649          npx[1] = ((hcalc*pd->xweight + vcalc*pd->yweight)/100);
 650          hcalc  = alpha_blending(buffer[cbaddr + buf_off_xpos], buffer[cbaddr + buf_off_width],  alpha_hori);
 651          vcalc  = alpha_blending(buffer[cbaddr + buf_off_ypos], buffer[cbaddr + buf_off_height], alpha_vert);
 652          npx[2] = ((hcalc*pd->xweight + vcalc*pd->yweight)/100);
 653
 654          buffer[craddr + buf_off] = alpha_blending(buffer[craddr + buf_off], npx[1], px);
 655          buffer[cbaddr + buf_off] = alpha_blending(buffer[cbaddr + buf_off], npx[2], px); 
 656        }
 657      }
 658
 659
 660    if (pd->border) {
 661        draw_border_yuv(pd, buffer, width, height);
 662    }
 663    return TC_OK;
 664}
 665
 666/*************************************************************************/
 667
 668static void free_dump_buf(LogoAwayPrivateData *pd)
 669{
 670    if (pd->dump) {
 671        tc_free(pd->dump_buf);
 672        pd->dump_buf = NULL;
 673    }   
 674}
 675
 676static int logoaway_setup(LogoAwayPrivateData *pd, vob_t *vob)
 677{
 678    if (pd->dump) {
 679        pd->dump_buf = tc_malloc((pd->width-pd->xpos)*(pd->height-pd->ypos)*3);
 680        /* FIXME */
 681        if (pd->dump_buf == NULL){
 682            tc_log_error(MOD_NAME, "out of memory");
 683            return TC_ERROR; /* FIXME */
 684        }
 685
 686        tc_magick_init(&pd->dump_ctx, TC_MAGICK_QUALITY_DEFAULT);
 687    }
 688
 689    if (pd->alpha) {
 690        int ret = TC_OK;
 691
 692        tc_magick_init(&pd->logo_ctx, TC_MAGICK_QUALITY_DEFAULT);
 693
 694        ret =  tc_magick_filein(&pd->logo_ctx, pd->file);
 695        if (ret != TC_OK) {
 696            free_dump_buf(pd);
 697            return ret;
 698        }
 699
 700        if ((pd->logo_ctx.image->columns != (pd->width-pd->xpos))
 701          || (pd->logo_ctx.image->rows != (pd->height-pd->ypos))) {
 702            tc_log_error(MOD_NAME, "\"%s\" has incorrect size", pd->file);
 703            free_dump_buf(pd);
 704            return TC_ERROR;
 705        }
 706
 707        pd->pixels = GetImagePixels(pd->logo_ctx.image, 0, 0,
 708                                    pd->logo_ctx.image->columns,
 709                                    pd->logo_ctx.image->rows);
 710    }
 711
 712    /* FIXME: this can be improved. What about a LUT? */
 713    switch (pd->mode) {
 714      case MODE_SOLID:
 715        pd->process_frame = (vob->im_v_codec == TC_CODEC_RGB24)
 716                                ?process_frame_rgb_solid
 717                                :process_frame_yuv_solid;
 718        break;
 719      case MODE_XY:
 720        pd->process_frame = (vob->im_v_codec == TC_CODEC_RGB24)
 721                                ?process_frame_rgb_xy
 722                                :process_frame_yuv_xy;
 723        break;
 724      case MODE_SHAPE:
 725        pd->process_frame = (vob->im_v_codec == TC_CODEC_RGB24)
 726                                ?process_frame_rgb_shape
 727                                :process_frame_yuv_shape;
 728        break;
 729      case MODE_NONE:
 730      default:
 731        pd->process_frame = process_frame_null; /* catchall */
 732        break;
 733    }
 734    return TC_OK;
 735}
 736
 737static void logoaway_defaults(LogoAwayPrivateData *pd)
 738{
 739    pd->start    = 0;
 740    pd->end      = (unsigned int)-1;
 741    pd->xpos     = -1;
 742    pd->ypos     = -1;
 743    pd->width    = -1;
 744    pd->height   = -1;
 745    pd->mode     = 0;
 746    pd->border   = 0;
 747    pd->xweight  = 50;
 748    pd->yweight  = 50;
 749    pd->rcolor   = 0;
 750    pd->gcolor   = 0;
 751    pd->bcolor   = 0;
 752    pd->ycolor   = 16;
 753    pd->ucolor   = 128;
 754    pd->vcolor   = 128;
 755    pd->alpha    = 0;
 756    pd->dump     = 0;
 757}
 758
 759static int logoaway_check_options(LogoAwayPrivateData *pd, vob_t *vob)
 760{
 761    if (vob->im_v_codec != TC_CODEC_RGB24
 762      && vob->im_v_codec != TC_CODEC_YUV420P) {
 763        tc_log_error(MOD_NAME, "unsupported colorspace");
 764        return TC_ERROR;
 765    }
 766    if ((pd->xpos > vob->im_v_width) || (pd->ypos > vob->im_v_height)
 767     || (pd->xpos < 0) || (pd->ypos < 0))  {
 768        tc_log_error(MOD_NAME, "invalid position");
 769        return TC_ERROR;
 770    }
 771    if ((pd->width > vob->im_v_width) || (pd->height > vob->im_v_height)
 772     || (pd->width-pd->xpos < 0) || (pd->height-pd->ypos < 0)) {
 773        tc_log_error(MOD_NAME, "invalid size");
 774        return TC_ERROR;
 775    }
 776    if ((pd->xweight > 100) || (pd->xweight < 0)) {
 777        tc_log_error(MOD_NAME, "invalid x weight");
 778        return TC_ERROR;
 779    }
 780    if ((pd->mode < 0) || (pd->mode > 3)) {
 781        tc_log_error(MOD_NAME, "invalid mode");
 782        return TC_ERROR;
 783    }
 784    if ((pd->mode == 3) && (pd->alpha == 0)) {
 785        tc_log_error(MOD_NAME, "alpha/shape file needed for SHAPE-mode");
 786        return TC_ERROR;
 787    }
 788    return TC_OK;
 789}
 790
 791static void logoaway_show_options(LogoAwayPrivateData *pd)
 792{
 793    tc_log_info(MOD_NAME, " LogoAway Filter Settings:");
 794    tc_log_info(MOD_NAME, "            pos = %dx%d", pd->xpos, pd->ypos);
 795    tc_log_info(MOD_NAME, "           size = %dx%d", pd->width-pd->xpos, pd->height-pd->ypos);
 796    tc_log_info(MOD_NAME, "           mode = %d(%s)", pd->mode, mode_name[pd->mode]);
 797    tc_log_info(MOD_NAME, "         border = %d", pd->border);
 798    tc_log_info(MOD_NAME, "     x-y weight = %d:%d", pd->xweight, pd->yweight);
 799    tc_log_info(MOD_NAME, "     fill color = %2X%2X%2X", pd->rcolor, pd->gcolor, pd->bcolor);
 800    if (pd->alpha) {
 801        tc_log_info (MOD_NAME, "           file = %s", pd->file);
 802    }
 803    if (pd->dump) {
 804        tc_log_info (MOD_NAME, "           dump = %d", pd->dump);
 805    }
 806}
 807
 808
 809
 810/*************************************************************************/
 811
 812/* Module interface routines and data. */
 813
 814/*************************************************************************/
 815
 816/**
 817 * logoaway_init:  Initialize this instance of the module.  See
 818 * tcmodule-data.h for function details.
 819 */
 820
 821TC_MODULE_GENERIC_INIT(logoaway, LogoAwayPrivateData)
 822
 823/*************************************************************************/
 824
 825/**
 826 * logoaway_fini:  Clean up after this instance of the module.  See
 827 * tcmodule-data.h for function details.
 828 */
 829
 830TC_MODULE_GENERIC_FINI(logoaway)
 831
 832
 833/*************************************************************************/
 834
 835/**
 836 * logoaway_configure:  Configure this instance of the module.  See
 837 * tcmodule-data.h for function details.
 838 */
 839
 840static int logoaway_configure(TCModuleInstance *self,
 841                              const char *options,
 842                              vob_t *vob,
 843                              TCModuleExtraData *xdata[])
 844{
 845    LogoAwayPrivateData *pd = NULL;
 846    int ret = TC_OK;
 847
 848    TC_MODULE_SELF_CHECK(self, "configure");
 849
 850    pd = self->userdata;
 851
 852    logoaway_defaults(pd);
 853
 854    if (options) {
 855        optstr_get(options,  "range",   "%d-%d",     &pd->start,  &pd->end);
 856        optstr_get(options,  "pos",     "%dx%d",     &pd->xpos,   &pd->ypos);
 857        optstr_get(options,  "size",    "%dx%d",     &pd->width,  &pd->height);
 858        pd->width += pd->xpos; pd->height += pd->ypos;
 859        optstr_get(options,  "mode",    "%d",        &pd->mode);
 860        if (optstr_lookup (options,  "border") != NULL) {
 861            pd->border = 1;
 862        }
 863        optstr_get(options,  "xweight", "%d",        &pd->xweight);
 864        pd->yweight = 100 - pd->xweight;
 865        optstr_get(options,  "fill",    "%2x%2x%2x", &pd->rcolor, &pd->gcolor, &pd->bcolor);
 866        pd->ycolor =  (0.257 * pd->rcolor) + (0.504 * pd->gcolor) + (0.098 * pd->bcolor) + 16;
 867        pd->ucolor =  (0.439 * pd->rcolor) - (0.368 * pd->gcolor) - (0.071 * pd->bcolor) + 128;
 868        pd->vcolor = -(0.148 * pd->rcolor) - (0.291 * pd->gcolor) + (0.439 * pd->bcolor) + 128;
 869        if (optstr_get(options, "file", "%[^:]", pd->file) >= 0) {
 870            pd->alpha = 1;
 871        }
 872        if (optstr_lookup(options,  "dump") != NULL) {
 873            pd->dump = 1;
 874        }
 875    }
 876
 877    ret = logoaway_check_options(pd, vob);
 878    if (ret == TC_OK) {
 879        if (verbose) {
 880            logoaway_show_options(pd);
 881        }
 882        ret = logoaway_setup(pd, vob);
 883    }
 884    return ret;
 885}
 886
 887/*************************************************************************/
 888
 889/**
 890 * logoaway_stop:  Reset this instance of the module.  See tcmodule-data.h
 891 * for function details.
 892 */
 893
 894static int logoaway_stop(TCModuleInstance *self)
 895{
 896    LogoAwayPrivateData *pd = NULL;
 897
 898    TC_MODULE_SELF_CHECK(self, "stop");
 899
 900    pd = self->userdata;
 901
 902    tc_magick_fini(&pd->logo_ctx);
 903    tc_magick_fini(&pd->dump_ctx);
 904
 905    free_dump_buf(pd);
 906    return TC_OK;
 907}
 908
 909/*************************************************************************/
 910
 911/**
 912 * logoaway_inspect:  Return the value of an option in this instance of
 913 * the module.  See tcmodule-data.h for function details.
 914 */
 915
 916static int logoaway_inspect(TCModuleInstance *self,
 917                            const char *param, const char **value)
 918{
 919    LogoAwayPrivateData *pd = NULL;
 920
 921    TC_MODULE_SELF_CHECK(self, "inspect");
 922    TC_MODULE_SELF_CHECK(param, "inspect");
 923    
 924    pd = self->userdata;
 925
 926    if (optstr_lookup(param, "help")) {
 927        *value = logoaway_help;
 928    }
 929
 930    if (optstr_lookup(param, "pos")) {
 931        tc_snprintf(pd->conf_str, sizeof(pd->conf_str),
 932                    "%ix%i", pd->xpos, pd->ypos);
 933        *value = pd->conf_str;
 934    }
 935    if (optstr_lookup(param, "size")) {
 936        tc_snprintf(pd->conf_str, sizeof(pd->conf_str),
 937                    "%ix%i", pd->width-pd->xpos, pd->height-pd->ypos);
 938        *value = pd->conf_str;
 939    }
 940    if (optstr_lookup(param, "mode")) {
 941        tc_snprintf(pd->conf_str, sizeof(pd->conf_str),
 942                    "%i", pd->mode);
 943        *value = pd->conf_str;
 944    }
 945    if (optstr_lookup(param, "border")) {
 946        tc_snprintf(pd->conf_str, sizeof(pd->conf_str),
 947                    "%i", pd->border);
 948        *value = pd->conf_str;
 949    }
 950    if (optstr_lookup(param, "xweight")) {
 951        tc_snprintf(pd->conf_str, sizeof(pd->conf_str),
 952                    "%i:%i", pd->xweight, pd->yweight);
 953        *value = pd->conf_str;
 954    }
 955    if (optstr_lookup(param, "fill")) {
 956        tc_snprintf(pd->conf_str, sizeof(pd->conf_str),
 957                    "%2X%2X%2X", pd->rcolor, pd->gcolor, pd->bcolor);
 958        *value = pd->conf_str;
 959    }
 960    if (optstr_lookup(param, "dump")) {
 961        tc_snprintf(pd->conf_str, sizeof(pd->conf_str),
 962                    "%i", pd->dump);
 963        *value = pd->conf_str;
 964    }
 965    if (optstr_lookup(param, "alpha")) {
 966        *value = pd->file;
 967    }
 968
 969    return TC_OK;
 970}
 971
 972/*************************************************************************/
 973
 974/**
 975 * logoaway_filter_video:  perform the logo removal for each frame of
 976 * this video stream. See tcmodule-data.h for function details.
 977 */
 978
 979static int logoaway_filter_video(TCModuleInstance *self,
 980                                 vframe_list_t *frame)
 981{
 982    LogoAwayPrivateData *pd = NULL;
 983    int ret = TC_OK;
 984
 985    TC_MODULE_SELF_CHECK(self, "filter");
 986    TC_MODULE_SELF_CHECK(frame, "filter");
 987
 988    pd = self->userdata;
 989
 990    if (frame->id >= pd->start && frame->id <= pd->end) {
 991        ret = pd->process_frame(pd, frame->video_buf,
 992                                frame->v_width, frame->v_height);
 993    }
 994
 995    return ret;
 996}
 997
 998
 999/*************************************************************************/
1000
1001static const TCCodecID logoaway_codecs_video_in[] = {
1002    TC_CODEC_RGB24, TC_CODEC_YUV420P, TC_CODEC_ERROR
1003};
1004static const TCCodecID logoaway_codecs_video_out[] = { 
1005    TC_CODEC_RGB24, TC_CODEC_YUV420P, TC_CODEC_ERROR
1006};
1007TC_MODULE_AUDIO_UNSUPPORTED(logoaway);
1008TC_MODULE_FILTER_FORMATS(logoaway);
1009
1010TC_MODULE_INFO(logoaway);
1011
1012static const TCModuleClass logoaway_class = {
1013    TC_MODULE_CLASS_HEAD(logoaway),
1014
1015    .init         = logoaway_init,
1016    .fini         = logoaway_fini,
1017    .configure    = logoaway_configure,
1018    .stop         = logoaway_stop,
1019    .inspect      = logoaway_inspect,
1020
1021    .filter_video = logoaway_filter_video,
1022};
1023
1024TC_MODULE_ENTRY_POINT(logoaway)
1025
1026
1027/*************************************************************************/
1028
1029static int logoaway_get_config(TCModuleInstance *self, char *options)
1030{
1031    LogoAwayPrivateData *pd = NULL;
1032    char buf[TC_BUF_MIN];
1033
1034    TC_MODULE_SELF_CHECK(self, "get_config");
1035
1036    pd = self->userdata;
1037
1038    optstr_filter_desc(options, MOD_NAME, MOD_CAP, MOD_VERSION, MOD_AUTHOR, "VRYOM", "1");
1039
1040    tc_snprintf(buf, sizeof(buf), "%u-%u", pd->start, pd->end);
1041    optstr_param(options, "range", "Frame Range", "%d-%d", buf, "0", "oo", "0", "oo");
1042
1043    tc_snprintf(buf, sizeof(buf), "%dx%d", pd->xpos, pd->ypos);
1044    optstr_param(options, "pos", "Position of logo", "%dx%d", buf, "0", "width", "0", "height");
1045
1046    tc_snprintf(buf, sizeof(buf), "%dx%d", pd->width, pd->height);
1047    optstr_param(options, "size", "Size of logo", "%dx%d", buf, "0", "width", "0", "height");
1048
1049    tc_snprintf(buf, sizeof(buf), "%d", pd->mode);
1050    optstr_param(options, "mode", "Filter Mode (0=none,1=solid,2=xy,3=shape)", "%d", buf, "0", "3");
1051
1052    tc_snprintf(buf, sizeof(buf), "%d", pd->border);
1053    optstr_param(options, "border", "Visible Border", "", buf);
1054
1055    tc_snprintf(buf, sizeof(buf), "%d", pd->dump);
1056    optstr_param(options, "dump", "Dump filterarea to file", "", buf);
1057
1058    tc_snprintf(buf, sizeof(buf), "%d", pd->xweight);
1059    optstr_param(options, "xweight","X-Y Weight(0%-100%)", "%d", buf, "0", "100");
1060
1061    tc_snprintf(buf, sizeof(buf), "%x%x%x", pd->rcolor, pd->gcolor, pd->bcolor);
1062    optstr_param(options, "fill", "Solid Fill Color(RGB)", "%2x%2x%2x", buf, "00", "FF", "00", "FF", "00", "FF");
1063
1064    tc_snprintf(buf, sizeof(buf), "%s", pd->file);
1065    optstr_param(options, "file", "Image with alpha/shape information", "%s", buf);
1066
1067    return TC_OK;
1068}
1069
1070static int logoaway_process(TCModuleInstance *self, frame_list_t *frame)
1071{
1072    TC_MODULE_SELF_CHECK(self, "process");
1073
1074    if (frame->tag & TC_PRE_M_PROCESS && frame->tag & TC_VIDEO
1075      && !(frame->attributes & TC_FRAME_IS_SKIPPED)) {
1076        return logoaway_filter_video(self, (vframe_list_t*)frame);
1077    }
1078    return TC_OK;
1079}
1080
1081/*************************************************************************/
1082
1083/* Old-fashioned module interface. */
1084
1085TC_FILTER_OLDINTERFACE_M(logoaway)
1086
1087/*************************************************************************/
1088
1089/*
1090 * Local variables:
1091 *   c-file-style: "stroustrup"
1092 *   c-file-offsets: ((case-label . *) (statement-case-intro . *))
1093 *   indent-tabs-mode: nil
1094 * End:
1095 *
1096 * vim: expandtab shiftwidth=4:
1097 */
1098