PageRenderTime 70ms CodeModel.GetById 28ms RepoModel.GetById 1ms app.codeStats 0ms

/filter/filter_logoaway.c

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