Skip to content

Commit 4d728e3

Browse files
authored
Add files via upload (#18984)
1 parent 2336f1c commit 4d728e3

4 files changed

Lines changed: 336 additions & 0 deletions

File tree

gfx/video_filters/dedither.c

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
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+
}

gfx/video_filters/dedither.filt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
filter = dedither

gfx/video_filters/pixel_art_aa.c

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
#include "softfilter.h"
2+
#include <stdlib.h>
3+
#include <string.h>
4+
5+
#ifdef RARCH_INTERNAL
6+
#define softfilter_get_implementation paa_get_implementation
7+
#define softfilter_thread_data paa_softfilter_thread_data
8+
#define filter_data paa_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+
/* Support the 2 RetroArch formats */
27+
static unsigned paa_query_input_formats(void) {
28+
return SOFTFILTER_FMT_XRGB8888 | SOFTFILTER_FMT_RGB565;
29+
}
30+
31+
static unsigned paa_query_output_formats(unsigned input_format) {
32+
return input_format;
33+
}
34+
35+
static unsigned paa_query_num_threads(void *data) {
36+
return 1;
37+
}
38+
39+
static void *paa_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 paa_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 paa_query_output_size(void *data, unsigned *out_width, unsigned *out_height,
57+
unsigned width, unsigned height) {
58+
*out_width = width << 1;
59+
*out_height = height << 1;
60+
}
61+
62+
/* Helper για μίξη RGB565 */
63+
static inline uint16_t mix_565(uint16_t c1, uint16_t c2) {
64+
return (((c1 & 0xF7DE) >> 1) + ((c2 & 0xF7DE) >> 1));
65+
}
66+
67+
/* Helper για μίξη XRGB8888 */
68+
static inline uint32_t mix_8888(uint32_t c1, uint32_t c2) {
69+
return (((c1 & 0xFEFEFEFE) >> 1) + ((c2 & 0xFEFEFEFE) >> 1));
70+
}
71+
72+
/* Logic for XRGB8888 */
73+
static void paa_work_cb_xrgb8888(void *data, void *thread_data) {
74+
struct softfilter_thread_data *thr = (struct softfilter_thread_data*)thread_data;
75+
const uint32_t *in = (const uint32_t*)thr->in_data;
76+
uint32_t *out = (uint32_t*)thr->out_data;
77+
uint32_t in_stride = (uint32_t)(thr->in_pitch >> 2);
78+
uint32_t out_stride = (uint32_t)(thr->out_pitch >> 2);
79+
80+
for (uint32_t y = 0; y < thr->height; y++) {
81+
for (uint32_t x = 0; x < thr->width; x++) {
82+
uint32_t p = in[y * in_stride + x];
83+
uint32_t *out_ptr = out + (y * 2 * out_stride) + (x * 2);
84+
uint32_t n = (y > 0) ? in[(y-1)*in_stride + x] : p;
85+
uint32_t s = (y < thr->height-1) ? in[(y+1)*in_stride + x] : p;
86+
uint32_t w = (x > 0) ? in[y*in_stride + (x-1)] : p;
87+
uint32_t e = (x < thr->width-1) ? in[y*in_stride + (x+1)] : p;
88+
89+
out_ptr[0] = p; out_ptr[1] = p;
90+
out_ptr[out_stride] = p; out_ptr[out_stride + 1] = p;
91+
92+
if (n != p && w != p && n == w) out_ptr[0] = mix_8888(p, n);
93+
if (n != p && e != p && n == e) out_ptr[1] = mix_8888(p, n);
94+
if (s != p && w != p && s == w) out_ptr[out_stride] = mix_8888(p, s);
95+
if (s != p && e != p && s == e) out_ptr[out_stride + 1] = mix_8888(p, s);
96+
}
97+
}
98+
}
99+
100+
/* Logic for RGB565 */
101+
static void paa_work_cb_rgb565(void *data, void *thread_data) {
102+
struct softfilter_thread_data *thr = (struct softfilter_thread_data*)thread_data;
103+
const uint16_t *in = (const uint16_t*)thr->in_data;
104+
uint16_t *out = (uint16_t*)thr->out_data;
105+
uint16_t in_stride = (uint16_t)(thr->in_pitch >> 1);
106+
uint16_t out_stride = (uint16_t)(thr->out_pitch >> 1);
107+
108+
for (uint32_t y = 0; y < thr->height; y++) {
109+
for (uint32_t x = 0; x < thr->width; x++) {
110+
uint16_t p = in[y * in_stride + x];
111+
uint16_t *out_ptr = out + (y * 2 * out_stride) + (x * 2);
112+
uint16_t n = (y > 0) ? in[(y-1)*in_stride + x] : p;
113+
uint16_t s = (y < thr->height-1) ? in[(y+1)*in_stride + x] : p;
114+
uint16_t w = (x > 0) ? in[y*in_stride + (x-1)] : p;
115+
uint16_t e = (x < thr->width-1) ? in[y*in_stride + (x+1)] : p;
116+
117+
out_ptr[0] = p; out_ptr[1] = p;
118+
out_ptr[out_stride] = p; out_ptr[out_stride + 1] = p;
119+
120+
if (n != p && w != p && n == w) out_ptr[0] = mix_565(p, n);
121+
if (n != p && e != p && n == e) out_ptr[1] = mix_565(p, n);
122+
if (s != p && w != p && s == w) out_ptr[out_stride] = mix_565(p, s);
123+
if (s != p && e != p && s == e) out_ptr[out_stride + 1] = mix_565(p, s);
124+
}
125+
}
126+
}
127+
128+
static void paa_get_work_packets(void *data, struct softfilter_work_packet *packets,
129+
void *output, size_t output_stride, const void *input, unsigned width, unsigned height, size_t input_stride) {
130+
struct filter_data *filt = (struct filter_data*)data;
131+
struct softfilter_thread_data *thr = &filt->workers[0];
132+
thr->out_data = output; thr->in_data = input;
133+
thr->out_pitch = output_stride; thr->in_pitch = input_stride;
134+
thr->width = width; thr->height = height;
135+
136+
if (filt->in_fmt == SOFTFILTER_FMT_XRGB8888)
137+
packets[0].work = paa_work_cb_xrgb8888;
138+
else if (filt->in_fmt == SOFTFILTER_FMT_RGB565)
139+
packets[0].work = paa_work_cb_rgb565;
140+
packets[0].thread_data = thr;
141+
}
142+
143+
static const struct softfilter_implementation paa_impl = {
144+
paa_query_input_formats, paa_query_output_formats,
145+
paa_create, paa_destroy, paa_query_num_threads,
146+
paa_query_output_size, paa_get_work_packets,
147+
SOFTFILTER_API_VERSION, "Pixel Art AA", "pixel_art_aa",
148+
};
149+
150+
const struct softfilter_implementation *softfilter_get_implementation(softfilter_simd_mask_t simd) {
151+
(void)simd; return &paa_impl;
152+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
filter = pixel_art_aa

0 commit comments

Comments
 (0)