1 /*
2     filter_xsharpen.c
3 
4     Copyright (C) 1999-2000 Donald A. Graft
5       modified 2002 by Tilmann Bitterberg for use with transcode
6 
7     This file is part of transcode, a video stream processing tool
8 
9     Xsharpen Filter for VirtualDub -- sharpen by mapping pixels
10     to the closest of window max or min.
11 
12     This program is free software; you can redistribute it and/or modify
13     it under the terms of the GNU General Public License as published by
14     the Free Software Foundation.
15 
16     This program 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 this program; if not, write to the Free Software
23     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 
25     The author can be contacted at:
26     Donald Graft
27     http://sauron.mordor.net/dgraft/
28     neuron2@home.com.
29 */
30 
31 #define MOD_NAME    "filter_xharpen.so"
32 #define MOD_VERSION "(1.0b2) (2003-02-12)"
33 #define MOD_CAP     "VirtualDub's XSharpen Filter"
34 #define MOD_AUTHOR  "Donald Graft, Tilmann Bitterberg"
35 
36 #include "transcode.h"
37 #include "filter.h"
38 #include "libtc/libtc.h"
39 #include "libtc/optstr.h"
40 #include "aclib/imgconvert.h"
41 
42 static vob_t *vob=NULL;
43 
44 /* vdub compat */
45 typedef unsigned int	Pixel;
46 typedef unsigned int	Pixel32;
47 typedef unsigned char	Pixel8;
48 typedef int		PixCoord;
49 typedef	int		PixDim;
50 typedef	int		PixOffset;
51 
52 ///////////////////////////////////////////////////////////////////////////
53 
54 typedef struct MyFilterData {
55 	Pixel32		*convertFrameIn;
56 	Pixel32		*convertFrameOut;
57 	int		strength;
58 	int		strengthInv;
59 	int		threshold;
60 	int		srcPitch;
61 	int		dstPitch;
62 	TCVHandle	tcvhandle;
63 } MyFilterData;
64 
65 static MyFilterData *mfd;
66 
help_optstr(void)67 static void help_optstr(void)
68 {
69    tc_log_info (MOD_NAME, "(%s) help\n"
70 "* Overview\n"
71 "    This filter performs a subtle but useful sharpening effect. The\n"
72 "    result is a sharpening effect that not only avoids amplifying\n"
73 "    noise, but also tends to reduce it. A welcome side effect is that\n"
74 "    files processed with this filter tend to compress to smaller files.\n"
75 "\n"
76 "* Options\n"
77 "  * Strength 'strength' (0-255) [200]\n"
78 "    When this value is 255, mapped pixels are not blended with the\n"
79 "    original pixel values, so a full-strength effect is obtained. As\n"
80 "    the value is reduced, each mapped pixel is blended with more of the\n"
81 "    original pixel. At a value of 0, the original pixels are passed\n"
82 "    through and there is no sharpening effect.\n"
83 "\n"
84 "  * Threshold 'threshold' (0-255) [255]\n"
85 "    This value determines how close a pixel must be to the brightest or\n"
86 "    dimmest pixel to be mapped. If a pixel is more than threshold away\n"
87 "    from the brightest or dimmest pixel, it is not mapped.  Thus, as\n"
88 "    the threshold is reduced, pixels in the mid range start to be\n"
89 "    spared.\n"
90 		, MOD_CAP);
91 }
92 
tc_filter(frame_list_t * ptr_,char * options)93 int tc_filter(frame_list_t *ptr_, char *options)
94 {
95   vframe_list_t *ptr = (vframe_list_t *)ptr_;
96 
97   //----------------------------------
98   //
99   // filter init
100   //
101   //----------------------------------
102 
103 
104   if(ptr->tag & TC_FILTER_INIT) {
105 
106 	int width, height;
107 
108 	if((vob = tc_get_vob())==NULL) return(-1);
109 
110 	mfd = tc_malloc(sizeof(MyFilterData));
111 
112 	if (!mfd) {
113 		tc_log_error(MOD_NAME, "No memory at %d!", __LINE__);
114 		return (-1);
115 	}
116 
117 	height = vob->ex_v_height;
118 	width  = vob->ex_v_width;
119 
120 	/* default values */
121 	mfd->strength       = 200; /* 255 is too much */
122 	mfd->strengthInv    = 255 - mfd->strength;
123 	mfd->threshold      = 255;
124 	mfd->srcPitch       = 0;
125 	mfd->dstPitch       = 0;
126 
127 	if (options != NULL) {
128 
129 	  if(verbose) tc_log_info(MOD_NAME, "options=%s", options);
130 
131 	  optstr_get (options, "strength",  "%d", &mfd->strength);
132 	  optstr_get (options, "threshold", "%d", &mfd->threshold);
133 
134 	}
135 
136 	mfd->strengthInv    = 255 - mfd->strength;
137 
138 	if (verbose > 1) {
139 
140 	  tc_log_info (MOD_NAME, " XSharpen Filter Settings (%dx%d):", width,height);
141 	  tc_log_info (MOD_NAME, "          strength = %d", mfd->strength);
142 	  tc_log_info (MOD_NAME, "         threshold = %d", mfd->threshold);
143 	}
144 
145 	if (options)
146 		if ( optstr_lookup(options, "help") != NULL) {
147 			help_optstr();
148 		}
149 
150 	/* fetch memory */
151 
152 	mfd->convertFrameIn = tc_malloc (width*height*sizeof(Pixel32));
153 	if (!mfd->convertFrameIn) {
154 		tc_log_error(MOD_NAME, "No memory at %d!", __LINE__);
155 		return (-1);
156 	}
157 	memset(mfd->convertFrameIn, 0, width*height*sizeof(Pixel32));
158 
159 	mfd->convertFrameOut = tc_malloc (width*height*sizeof(Pixel32));
160 	if (!mfd->convertFrameOut) {
161 		tc_log_error(MOD_NAME, "No memory at %d!", __LINE__);
162 		return (-1);
163 	}
164 	memset(mfd->convertFrameOut, 0, width*height*sizeof(Pixel32));
165 
166 	mfd->tcvhandle = tcv_init();
167 
168 	// filter init ok.
169 	if(verbose) tc_log_info(MOD_NAME, "%s %s", MOD_VERSION, MOD_CAP);
170 
171 	return 0;
172 
173   } /* TC_FILTER_INIT */
174 
175 
176   if(ptr->tag & TC_FILTER_GET_CONFIG) {
177     if (options) {
178 	    char buf[256];
179 	    optstr_filter_desc (options, MOD_NAME, MOD_CAP, MOD_VERSION, MOD_AUTHOR, "VRYO", "1");
180 	    tc_snprintf (buf, sizeof(buf), "%d", mfd->strength);
181 	    optstr_param (options, "strength", "How much  of the effect", "%d", buf, "0", "255");
182 
183 	    tc_snprintf (buf, sizeof(buf), "%d", mfd->threshold);
184 	    optstr_param (options, "threshold",
185 			  "How close a pixel must be to the brightest or dimmest pixel to be mapped",
186 			  "%d", buf, "0", "255");
187     }
188   }
189 
190 
191   if(ptr->tag & TC_FILTER_CLOSE) {
192 
193 	if (mfd->convertFrameIn)
194 		free (mfd->convertFrameIn);
195 	mfd->convertFrameIn = NULL;
196 
197 	if (mfd->convertFrameOut)
198 		free (mfd->convertFrameOut);
199 	mfd->convertFrameOut = NULL;
200 
201 	tcv_free(mfd->tcvhandle);
202 	mfd->tcvhandle = 0;
203 
204 	if (mfd)
205 		free(mfd);
206 	mfd = NULL;
207 
208 	return 0;
209 
210   } /* TC_FILTER_CLOSE */
211 
212 ///////////////////////////////////////////////////////////////////////////
213 
214   if(ptr->tag & TC_POST_M_PROCESS && ptr->tag & TC_VIDEO && !(ptr->attributes & TC_FRAME_IS_SKIPPED)) {
215 
216      if (vob->im_v_codec == CODEC_RGB) {
217 	const PixDim	width  = ptr->v_width;
218 	const PixDim	height = ptr->v_height;
219 	Pixel32         *src, *dst;
220 	int             x, y;
221 	int             r, g, b, R, G, B;
222 	Pixel32         p, min=1000, max=-1;
223 	int             luma, lumac, lumamax, lumamin, mindiff, maxdiff;
224 	const int	srcpitch = ptr->v_width*sizeof(Pixel32);
225 	const int	dstpitch = ptr->v_width*sizeof(Pixel32);
226 
227 	Pixel32 * dst_buf;
228 	Pixel32 * src_buf;
229 
230 	tcv_convert(mfd->tcvhandle, ptr->video_buf,
231 		    (uint8_t *)mfd->convertFrameIn,
232 		    ptr->v_width, ptr->v_height, IMG_RGB24, IMG_BGRA32);
233 
234 	src_buf = mfd->convertFrameIn;
235 	dst_buf = mfd->convertFrameOut;
236 
237 	/* First copy through the four border lines. */
238 	src	= src_buf;
239 	dst	= dst_buf;
240 	for (x = 0; x < width; x++)
241 	{
242 		dst[x] = src[x];
243 	}
244 	src	= (Pixel *)((char *)src_buf + (height - 1) * srcpitch);
245 	dst	= (Pixel *)((char *)dst_buf + (height - 1) * dstpitch);
246 	for (x = 0; x < width; x++)
247 	{
248 		dst[x] = src[x];
249 	}
250 	src	= src_buf;
251 	dst	= dst_buf;
252 	for (y = 0; y < height; y++)
253 	{
254 		dst[0] = src[0];
255 		dst[width-1] = src[width-1];
256 		src	= (Pixel *)((char *)src + srcpitch);
257 		dst	= (Pixel *)((char *)dst + dstpitch);
258 	}
259 
260 	/* Now calculate and store the pixel luminances for the remaining pixels. */
261 	src	= src_buf;
262 	for (y = 0; y < height; y++)
263 	{
264 		for (x = 0; x < width; x++)
265 		{
266 			r = (src[x] >> 16) & 0xff;
267 			g = (src[x] >> 8) & 0xff;
268 			b = src[x] & 0xff;
269 			luma = (55 * r + 182 * g + 19 * b) >> 8;
270 			src[x] &= 0x00ffffff;
271 			src[x] |= (luma << 24);
272 		}
273 		src	= (Pixel *)((char *)src + srcpitch);
274 	}
275 
276 	/* Finally run the 3x3 rank-order sharpening kernel over the pixels. */
277 	src	= (Pixel *)((char *)src_buf + srcpitch);
278 	dst	= (Pixel *)((char *)dst_buf + dstpitch);
279 	for (y = 1; y < height - 1; y++)
280 	{
281 		for (x = 1; x < width - 1; x++)
282 		{
283 			/* Find the brightest and dimmest pixels in the 3x3 window
284 			   surrounding the current pixel. */
285 			lumamax = -1;
286 			lumamin = 1000;
287 
288 			p = ((Pixel32 *)((char *)src - srcpitch))[x-1];
289 			luma = p >> 24;
290 			if (luma > lumamax)
291 			{
292 				lumamax = luma;
293 				max = p;
294 			}
295 			if (luma < lumamin)
296 			{
297 				lumamin = luma;
298 				min = p;
299 			}
300 
301 			p = ((Pixel32 *)((char *)src - srcpitch))[x];
302 			luma = p >> 24;
303 			if (luma > lumamax)
304 			{
305 				lumamax = luma;
306 				max = p;
307 			}
308 			if (luma < lumamin)
309 			{
310 				lumamin = luma;
311 				min = p;
312 			}
313 
314 			p = ((Pixel32 *)((char *)src - srcpitch))[x+1];
315 			luma = p >> 24;
316 			if (luma > lumamax)
317 			{
318 				lumamax = luma;
319 				max = p;
320 			}
321 			if (luma < lumamin)
322 			{
323 				lumamin = luma;
324 				min = p;
325 			}
326 
327 			p = src[x-1];
328 			luma = p >> 24;
329 			if (luma > lumamax)
330 			{
331 				lumamax = luma;
332 				max = p;
333 			}
334 			if (luma < lumamin)
335 			{
336 				lumamin = luma;
337 				min = p;
338 			}
339 
340 			p = src[x];
341 			lumac = luma = p >> 24;
342 			if (luma > lumamax)
343 			{
344 				lumamax = luma;
345 				max = p;
346 			}
347 			if (luma < lumamin)
348 			{
349 				lumamin = luma;
350 				min = p;
351 			}
352 
353 			p = src[x+1];
354 			luma = p >> 24;
355 			if (luma > lumamax)
356 			{
357 				lumamax = luma;
358 				max = p;
359 			}
360 			if (luma < lumamin)
361 			{
362 				lumamin = luma;
363 				min = p;
364 			}
365 
366 			p = ((Pixel32 *)((char *)src + srcpitch))[x-1];
367 			luma = p >> 24;
368 			if (luma > lumamax)
369 			{
370 				lumamax = luma;
371 				max = p;
372 			}
373 			if (luma < lumamin)
374 			{
375 				lumamin = luma;
376 				min = p;
377 			}
378 
379 			p = ((Pixel32 *)((char *)src + srcpitch))[x];
380 			luma = p >> 24;
381 			if (luma > lumamax)
382 			{
383 				lumamax = luma;
384 				max = p;
385 			}
386 			if (luma < lumamin)
387 			{
388 				lumamin = luma;
389 				min = p;
390 			}
391 
392 			p = ((Pixel32 *)((char *)src + srcpitch))[x+1];
393 			luma = p >> 24;
394 			if (luma > lumamax)
395 			{
396 				lumamax = luma;
397 				max = p;
398 			}
399 			if (luma < lumamin)
400 			{
401 				lumamin = luma;
402 				min = p;
403 			}
404 
405 			/* Determine whether the current pixel is closer to the
406 			   brightest or the dimmest pixel. Then compare the current
407 			   pixel to that closest pixel. If the difference is within
408 			   threshold, map the current pixel to the closest pixel;
409 			   otherwise pass it through. */
410 			p = -1;
411 			if (mfd->strength != 0)
412 			{
413 				mindiff = lumac - lumamin;
414 				maxdiff = lumamax - lumac;
415 				if (mindiff > maxdiff)
416 				{
417 					if (maxdiff < mfd->threshold)
418 					{
419 						p = max;
420 					}
421 				}
422 				else
423 				{
424 					if (mindiff < mfd->threshold)
425 					{
426 						p = min;
427 					}
428 				}
429 			}
430 			if (p == -1)
431 			{
432 				dst[x] = src[x];
433 			}
434 			else
435 			{
436 
437 				R = (src[x] >> 16) & 0xff;
438 				G = (src[x] >> 8) & 0xff;
439 				B = src[x] & 0xff;
440 				r = (p >> 16) & 0xff;
441 				g = (p >> 8) & 0xff;
442 				b = p & 0xff;
443 				r = (mfd->strength * r + mfd->strengthInv * R) / 255;
444 				g = (mfd->strength * g + mfd->strengthInv * G) / 255;
445 				b = (mfd->strength * b + mfd->strengthInv * B) / 255;
446 				dst[x] = (r << 16) | (g << 8) | b;
447 			}
448 		}
449 		src	= (Pixel *)((char *)src + srcpitch);
450 		dst	= (Pixel *)((char *)dst + dstpitch);
451 	}
452 
453 	tcv_convert(mfd->tcvhandle, (uint8_t *)mfd->convertFrameOut,
454 		    ptr->video_buf, ptr->v_width, ptr->v_height,
455 		    IMG_BGRA32, IMG_RGB24);
456 
457 	return 0;
458      }
459 
460 
461      if (vob->im_v_codec == CODEC_YUV) {
462 
463 	const PixDim       width = ptr->v_width;
464 	const PixDim       height = ptr->v_height;
465 	char               *src, *dst;
466 	int                x, y;
467 	int        	   luma, lumac, lumamax, lumamin;
468 	int 		   p, mindiff, maxdiff;
469 	const int	   srcpitch = ptr->v_width;
470 	const int	   dstpitch = ptr->v_width;
471 
472 	char * src_buf = ptr->video_buf;
473 	static char * dst_buf = NULL;
474 
475 	if (!dst_buf)
476 		dst_buf =  tc_malloc (width*height*3/2);
477 
478 	/* First copy through the four border lines. */
479 	/* first */
480 	src	= src_buf;
481 	dst	= dst_buf;
482 	ac_memcpy (dst, src, width);
483 
484 	/* last */
485 	src     = src_buf+srcpitch*(height-1);
486 	dst     = dst_buf+dstpitch*(height-1);
487 	ac_memcpy (dst, src, width);
488 
489 	/* copy Cb and Cr */
490 	ac_memcpy (dst_buf+dstpitch*height, src_buf+srcpitch*height, width*height>>1);
491 
492 	src	= src_buf;
493 	dst	= dst_buf;
494 	for (y = 0; y < height; y++)
495 	{
496 		*dst = *src;
497 		*(dst+width-1) = *(src+width-1);
498 		dst += dstpitch;
499 		src += srcpitch;
500 	}
501 
502 	src = src_buf+srcpitch;
503 	dst = dst_buf+dstpitch;
504 
505 	/* Finally run the 3x3 rank-order sharpening kernel over the pixels. */
506 	for (y = 1; y < height - 1; y++)
507 	{
508 		for (x = 1; x < width - 1; x++)
509 		{
510 			/* Find the brightest and dimmest pixels in the 3x3 window
511 			   surrounding the current pixel. */
512 			lumamax = -1000;
513 			lumamin = 1000;
514 
515 			luma = (src - srcpitch)[x-1] &0xff;
516 			if (luma > lumamax)
517 				lumamax = luma;
518 			if (luma < lumamin)
519 				lumamin = luma;
520 
521 			luma = (src - srcpitch)[x] &0xff;
522 			if (luma > lumamax)
523 				lumamax = luma;
524 			if (luma < lumamin)
525 				lumamin = luma;
526 
527 			luma = (src - srcpitch)[x+1] &0xff;
528 			if (luma > lumamax)
529 				lumamax = luma;
530 			if (luma < lumamin)
531 				lumamin = luma;
532 
533 			luma = src[x-1] &0xff;
534 			if (luma > lumamax)
535 				lumamax = luma;
536 			if (luma < lumamin)
537 				lumamin = luma;
538 
539 			luma = src[x] &0xff;
540 			lumac = luma;
541 			if (luma > lumamax)
542 				lumamax = luma;
543 			if (luma < lumamin)
544 				lumamin = luma;
545 
546 			luma = src[x+1] &0xff;
547 			if (luma > lumamax)
548 				lumamax = luma;
549 			if (luma < lumamin)
550 				lumamin = luma;
551 
552 			luma = (src + srcpitch)[x-1] &0xff;
553 			if (luma > lumamax)
554 				lumamax = luma;
555 			if (luma < lumamin)
556 				lumamin = luma;
557 
558 			luma = (src + srcpitch)[x] &0xff;
559 			if (luma > lumamax)
560 				lumamax = luma;
561 			if (luma < lumamin)
562 				lumamin = luma;
563 
564 			luma = (src + srcpitch)[x+1] &0xff;
565 			if (luma > lumamax)
566 				lumamax = luma;
567 			if (luma < lumamin)
568 				lumamin = luma;
569 
570 			/* Determine whether the current pixel is closer to the
571 			   brightest or the dimmest pixel. Then compare the current
572 			   pixel to that closest pixel. If the difference is within
573 			   threshold, map the current pixel to the closest pixel;
574 			   otherwise pass it through. */
575 
576 			p = -1;
577 			if (mfd->strength != 0)
578 			{
579 				mindiff = lumac   - lumamin;
580 				maxdiff = lumamax - lumac;
581 				if (mindiff > maxdiff)
582 				{
583 					if (maxdiff < mfd->threshold)
584 						p = lumamax&0xff;
585 				}
586 				else
587 				{
588 					if (mindiff < mfd->threshold)
589 						p = lumamin&0xff;
590 				}
591 			}
592 			if (p == -1)
593 			{
594 				dst[x] = src[x];
595 			}
596 			else
597 			{
598 			        int t;
599 				lumac = src[x] &0xff;
600 				t = ((mfd->strength*p + mfd->strengthInv*lumac)/255) & 0xff;
601 				if (t>240) t = 240;
602 				if (t<16)  t = 16;
603 				dst[x] = t&0xff;
604 
605 			}
606 		}
607 		src += srcpitch;
608 		dst += dstpitch;
609 	}
610 
611 	ac_memcpy (ptr->video_buf, dst_buf, width*height*3/2);
612 
613 	return 0;
614 
615      }
616   }
617   return 0;
618 }
619 
620