1+ #include "softfilter.h"
2+ #include <stdlib.h>
3+ #include <string.h>
4+
5+ #ifdef RARCH_INTERNAL
6+ #define softfilter_get_implementation dedither_get_implementation
7+ #define softfilter_thread_data dedither_softfilter_thread_data
8+ #define filter_data dedither_filter_data
9+ #endif
10+
11+ struct softfilter_thread_data {
12+ void * out_data ;
13+ const void * in_data ;
14+ size_t out_pitch ;
15+ size_t in_pitch ;
16+ unsigned width ;
17+ unsigned height ;
18+ };
19+
20+ struct filter_data {
21+ unsigned threads ;
22+ struct softfilter_thread_data * workers ;
23+ unsigned in_fmt ;
24+ };
25+
26+ static unsigned dedither_generic_input_fmts (void ) {
27+ return SOFTFILTER_FMT_XRGB8888 | SOFTFILTER_FMT_RGB565 ;
28+ }
29+
30+ static unsigned dedither_generic_output_fmts (unsigned input_fmts ) {
31+ return input_fmts ;
32+ }
33+
34+ static unsigned dedither_generic_threads (void * data ) {
35+ struct filter_data * filt = (struct filter_data * )data ;
36+ return filt -> threads ;
37+ }
38+
39+ static void * dedither_generic_create (const struct softfilter_config * config ,
40+ unsigned in_fmt , unsigned out_fmt ,
41+ unsigned max_width , unsigned max_height ,
42+ unsigned threads , softfilter_simd_mask_t simd , void * userdata ) {
43+ struct filter_data * filt = (struct filter_data * )calloc (1 , sizeof (* filt ));
44+ if (!filt ) return NULL ;
45+ filt -> workers = (struct softfilter_thread_data * )calloc (1 , sizeof (struct softfilter_thread_data ));
46+ filt -> threads = 1 ;
47+ filt -> in_fmt = in_fmt ;
48+ return filt ;
49+ }
50+
51+ static void dedither_generic_destroy (void * data ) {
52+ struct filter_data * filt = (struct filter_data * )data ;
53+ if (filt ) { free (filt -> workers ); free (filt ); }
54+ }
55+
56+ static void dedither_generic_output (void * data , unsigned * out_width , unsigned * out_height ,
57+ unsigned width , unsigned height ) {
58+ * out_width = width ; * out_height = height ;
59+ }
60+
61+ /* Color comparison with threshold */
62+ static inline int pix_equal (uint32_t c1 , uint32_t c2 , int threshold ) {
63+ int r = abs ((int )((c1 >> 16 ) & 0xFF ) - (int )((c2 >> 16 ) & 0xFF ));
64+ int g = abs ((int )((c1 >> 8 ) & 0xFF ) - (int )((c2 >> 8 ) & 0xFF ));
65+ int b = abs ((int )(c1 & 0xFF ) - (int )(c2 & 0xFF ));
66+ return (r + g + b ) < threshold ;
67+ }
68+
69+ /* XRGB8888 Kernel - 2D Dither Detection (6x6) */
70+ static void dedither_work_cb_xrgb8888 (void * data , void * thread_data ) {
71+ struct softfilter_thread_data * thr = (struct softfilter_thread_data * )thread_data ;
72+ const uint32_t * in = (const uint32_t * )thr -> in_data ;
73+ uint32_t * out = (uint32_t * )thr -> out_data ;
74+ uint32_t in_stride = (uint32_t )(thr -> in_pitch >> 2 );
75+ uint32_t out_stride = (uint32_t )(thr -> out_pitch >> 2 );
76+ const int threshold = 40 ;
77+
78+ for (uint32_t y = 0 ; y < thr -> height ; ++ y ) {
79+ for (uint32_t x = 0 ; x < thr -> width ; ++ x ) {
80+ /* Check safety bounds for 6-pixel horizontal and vertical patterns */
81+ if (x >= 2 && x < thr -> width - 3 && y >= 2 && y < thr -> height - 3 ) {
82+
83+ const uint32_t * line = in + y * in_stride ;
84+
85+ /* Horizontal samples (Row y) */
86+ uint32_t h1 = line [x - 2 ]; uint32_t h2 = line [x - 1 ];
87+ uint32_t h3 = line [x ]; /* Center Pixel */
88+ uint32_t h4 = line [x + 1 ]; uint32_t h5 = line [x + 2 ];
89+ uint32_t h6 = line [x + 3 ];
90+
91+ /* Vertical samples (Column x) */
92+ uint32_t v1 = (line - 2 * in_stride )[x ];
93+ uint32_t v2 = (line - 1 * in_stride )[x ];
94+ uint32_t v4 = (line + 1 * in_stride )[x ];
95+ uint32_t v5 = (line + 2 * in_stride )[x ];
96+ uint32_t v6 = (line + 3 * in_stride )[x ];
97+
98+ /* Pattern check: A-B-A-B-A-B */
99+ int h_dit = (pix_equal (h1 , h3 , threshold ) && pix_equal (h3 , h5 , threshold ) &&
100+ pix_equal (h2 , h4 , threshold ) && pix_equal (h4 , h6 , threshold ) &&
101+ !pix_equal (h3 , h4 , threshold ));
102+
103+ int v_dit = (pix_equal (v1 , h3 , threshold ) && pix_equal (h3 , v5 , threshold ) &&
104+ pix_equal (v2 , v4 , threshold ) && pix_equal (v4 , v6 , threshold ) &&
105+ !pix_equal (h3 , v4 , threshold ));
106+
107+ if (h_dit || v_dit ) {
108+ uint32_t avg ;
109+ if (h_dit )
110+ avg = (((h2 & 0xFEFEFEFE ) >> 1 ) + ((h4 & 0xFEFEFEFE ) >> 1 ));
111+ else
112+ avg = (((v2 & 0xFEFEFEFE ) >> 1 ) + ((v4 & 0xFEFEFEFE ) >> 1 ));
113+
114+ /* Apply soft blend (1:2:1 weighting) */
115+ out [y * out_stride + x ] = (((avg & 0xFEFEFEFE ) >> 1 ) + ((h3 & 0xFEFEFEFE ) >> 1 ));
116+ } else {
117+ out [y * out_stride + x ] = h3 ; /* Keep original sharp pixel */
118+ }
119+ } else {
120+ out [y * out_stride + x ] = in [y * in_stride + x ];
121+ }
122+ }
123+ }
124+ }
125+
126+ /* RGB565 Kernel - 2D Dither Detection (6x6) */
127+ static void dedither_work_cb_rgb565 (void * data , void * thread_data ) {
128+ struct softfilter_thread_data * thr = (struct softfilter_thread_data * )thread_data ;
129+ const uint16_t * in = (const uint16_t * )thr -> in_data ;
130+ uint16_t * out = (uint16_t * )thr -> out_data ;
131+ uint16_t in_stride = (uint16_t )(thr -> in_pitch >> 1 );
132+ uint16_t out_stride = (uint16_t )(thr -> out_pitch >> 1 );
133+
134+ for (uint32_t y = 0 ; y < thr -> height ; ++ y ) {
135+ for (uint32_t x = 0 ; x < thr -> width ; ++ x ) {
136+ if (x >= 2 && x < thr -> width - 3 && y >= 2 && y < thr -> height - 3 ) {
137+ const uint16_t * line = in + y * in_stride ;
138+ uint16_t h1 = line [x - 2 ]; uint16_t h2 = line [x - 1 ];
139+ uint16_t h3 = line [x ]; uint16_t h4 = line [x + 1 ];
140+ uint16_t h5 = line [x + 2 ]; uint16_t h6 = line [x + 3 ];
141+
142+ uint16_t v1 = (line - 2 * in_stride )[x ]; uint16_t v2 = (line - 1 * in_stride )[x ];
143+ uint16_t v4 = (line + 1 * in_stride )[x ]; uint16_t v5 = (line + 2 * in_stride )[x ];
144+ uint16_t v6 = (line + 3 * in_stride )[x ];
145+
146+ int h_dit = (h1 == h3 && h3 == h5 && h2 == h4 && h4 == h6 && h3 != h4 );
147+ int v_dit = (v1 == h3 && h3 == v5 && v2 == v4 && v4 == v6 && h3 != v2 );
148+
149+ if (h_dit || v_dit ) {
150+ uint16_t avg_s = (h_dit ) ? (((h2 & 0xF7DE ) >> 1 ) + ((h4 & 0xF7DE ) >> 1 )) : (((v2 & 0xF7DE ) >> 1 ) + ((v4 & 0xF7DE ) >> 1 ));
151+ out [y * out_stride + x ] = (((avg_s & 0xF7DE ) >> 1 ) + ((h3 & 0xF7DE ) >> 1 ));
152+ } else out [y * out_stride + x ] = h3 ;
153+ } else out [y * out_stride + x ] = in [y * in_stride + x ];
154+ }
155+ }
156+ }
157+
158+ static void dedither_generic_packets (void * data , struct softfilter_work_packet * packets ,
159+ void * output , size_t output_stride , const void * input , unsigned width , unsigned height , size_t input_stride ) {
160+ struct filter_data * filt = (struct filter_data * )data ;
161+ struct softfilter_thread_data * thr = & filt -> workers [0 ];
162+ thr -> out_data = output ; thr -> in_data = input ;
163+ thr -> out_pitch = output_stride ; thr -> in_pitch = input_stride ;
164+ thr -> width = width ; thr -> height = height ;
165+
166+ if (filt -> in_fmt == SOFTFILTER_FMT_XRGB8888 )
167+ packets [0 ].work = dedither_work_cb_xrgb8888 ;
168+ else if (filt -> in_fmt == SOFTFILTER_FMT_RGB565 )
169+ packets [0 ].work = dedither_work_cb_rgb565 ;
170+ packets [0 ].thread_data = thr ;
171+ }
172+
173+ static const struct softfilter_implementation dedither_generic = {
174+ dedither_generic_input_fmts , dedither_generic_output_fmts ,
175+ dedither_generic_create , dedither_generic_destroy ,
176+ dedither_generic_threads , dedither_generic_output , dedither_generic_packets ,
177+ SOFTFILTER_API_VERSION , "Master De-Dither 2D (6x6)" , "dedither" ,
178+ };
179+
180+ const struct softfilter_implementation * softfilter_get_implementation (softfilter_simd_mask_t simd ) {
181+ (void )simd ; return & dedither_generic ;
182+ }
0 commit comments