RetroArch
snes_ntsc_impl.h
Go to the documentation of this file.
1 /* snes_ntsc 0.2.2. http://www.slack.net/~ant/ */
2 #ifndef __BLARGG_SNES_NTSC_IMPLEMENTATION_H
3 #define __BLARGG_SNES_NTSC_IMPLEMENTATION_H
4 
5 /* Common implementation of NTSC filters */
6 
7 #include <retro_assert.h>
8 #include <math.h>
9 
10 /* Copyright (C) 2006 Shay Green. This module is free software; you
11 can redistribute it and/or modify it under the terms of the GNU Lesser
12 General Public License as published by the Free Software Foundation; either
13 version 2.1 of the License, or (at your option) any later version. This
14 module is distributed in the hope that it will be useful, but WITHOUT ANY
15 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
17 details. You should have received a copy of the GNU Lesser General Public
18 License along with this module; if not, write to the Free Software Foundation,
19 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
20 
21 #define DISABLE_CORRECTION 0
22 
23 #undef PI
24 #define PI 3.14159265358979323846f
25 
26 #ifndef LUMA_CUTOFF
27  #define LUMA_CUTOFF 0.20
28 #endif
29 #ifndef gamma_size
30  #define gamma_size 1
31 #endif
32 #ifndef rgb_bits
33  #define rgb_bits 8
34 #endif
35 #ifndef artifacts_max
36  #define artifacts_max (artifacts_mid * 1.5f)
37 #endif
38 #ifndef fringing_max
39  #define fringing_max (fringing_mid * 2)
40 #endif
41 #ifndef STD_HUE_CONDITION
42  #define STD_HUE_CONDITION( setup ) 1
43 #endif
44 
45 #define ext_decoder_hue (std_decoder_hue + 15)
46 #define rgb_unit (1 << rgb_bits)
47 #define rgb_offset (rgb_unit * 2 + 0.5f)
48 
50 enum { kernel_half = 16 };
51 enum { kernel_size = kernel_half * 2 + 1 };
52 
53 typedef struct init_t
54 {
55  float to_rgb [burst_count * 6];
57  float contrast;
58  float brightness;
59  float artifacts;
60  float fringing;
62 } init_t;
63 
64 #define ROTATE_IQ( i, q, sin_b, cos_b ) {\
65  float t;\
66  t = i * cos_b - q * sin_b;\
67  q = i * sin_b + q * cos_b;\
68  i = t;\
69 }
70 
71 static void init_filters( init_t* impl, snes_ntsc_setup_t const* setup )
72 {
73 #if rescale_out > 1
74  float kernels [kernel_size * 2];
75 #else
76  float* const kernels = impl->kernel;
77 #endif
78 
79  /* generate luma (y) filter using sinc kernel */
80  {
81  /* sinc with rolloff (dsf) */
82  float const rolloff = 1 + (float) setup->sharpness * (float) 0.032;
83  float const maxh = 32;
84  float const pow_a_n = (float) pow( rolloff, maxh );
85  float sum;
86  int i;
87  /* quadratic mapping to reduce negative (blurring) range */
88  float to_angle = (float) setup->resolution + 1;
89  to_angle = PI / maxh * (float) LUMA_CUTOFF * (to_angle * to_angle + 1);
90 
91  kernels [kernel_size * 3 / 2] = maxh; /* default center value */
92  for ( i = 0; i < kernel_half * 2 + 1; i++ )
93  {
94  int x = i - kernel_half;
95  float angle = x * to_angle;
96  /* instability occurs at center point with rolloff very close to 1.0 */
97  if ( x || pow_a_n > (float) 1.056 || pow_a_n < (float) 0.981 )
98  {
99  float rolloff_cos_a = rolloff * (float) cos( angle );
100  float num = 1 - rolloff_cos_a -
101  pow_a_n * (float) cos( maxh * angle ) +
102  pow_a_n * rolloff * (float) cos( (maxh - 1) * angle );
103  float den = 1 - rolloff_cos_a - rolloff_cos_a + rolloff * rolloff;
104  float dsf = num / den;
105  kernels [kernel_size * 3 / 2 - kernel_half + i] = dsf - (float) 0.5;
106  }
107  }
108 
109  /* apply blackman window and find sum */
110  sum = 0;
111  for ( i = 0; i < kernel_half * 2 + 1; i++ )
112  {
113  float x = PI * 2 / (kernel_half * 2) * i;
114  float blackman = 0.42f - 0.5f * (float) cos( x ) + 0.08f * (float) cos( x * 2 );
115  sum += (kernels [kernel_size * 3 / 2 - kernel_half + i] *= blackman);
116  }
117 
118  /* normalize kernel */
119  sum = 1.0f / sum;
120  for ( i = 0; i < kernel_half * 2 + 1; i++ )
121  {
122  int x = kernel_size * 3 / 2 - kernel_half + i;
123  kernels [x] *= sum;
124  retro_assert( kernels [x] == kernels [x] ); /* catch numerical instability */
125  }
126  }
127 
128  /* generate chroma (iq) filter using gaussian kernel */
129  {
130  float const cutoff_factor = -0.03125f;
131  float cutoff = (float) setup->bleed;
132  int i;
133 
134  if ( cutoff < 0 )
135  {
136  /* keep extreme value accessible only near upper end of scale (1.0) */
137  cutoff *= cutoff;
138  cutoff *= cutoff;
139  cutoff *= cutoff;
140  cutoff *= -30.0f / 0.65f;
141  }
142  cutoff = cutoff_factor - 0.65f * cutoff_factor * cutoff;
143 
144  for ( i = -kernel_half; i <= kernel_half; i++ )
145  kernels [kernel_size / 2 + i] = (float) exp( i * i * cutoff );
146 
147  /* normalize even and odd phases separately */
148  for ( i = 0; i < 2; i++ )
149  {
150  float sum = 0;
151  int x;
152  for ( x = i; x < kernel_size; x += 2 )
153  sum += kernels [x];
154 
155  sum = 1.0f / sum;
156  for ( x = i; x < kernel_size; x += 2 )
157  {
158  kernels [x] *= sum;
159  retro_assert( kernels [x] == kernels [x] ); /* catch numerical instability */
160  }
161  }
162  }
163 
164  /*
165  printf( "luma:\n" );
166  for ( i = kernel_size; i < kernel_size * 2; i++ )
167  printf( "%f\n", kernels [i] );
168  printf( "chroma:\n" );
169  for ( i = 0; i < kernel_size; i++ )
170  printf( "%f\n", kernels [i] );
171  */
172 
173  /* generate linear rescale kernels */
174  #if rescale_out > 1
175  {
176  float weight = 1.0f;
177  float* out = impl->kernel;
178  int n = rescale_out;
179  do
180  {
181  float remain = 0;
182  int i;
183  weight -= 1.0f / rescale_in;
184  for ( i = 0; i < kernel_size * 2; i++ )
185  {
186  float cur = kernels [i];
187  float m = cur * weight;
188  *out++ = m + remain;
189  remain = cur - m;
190  }
191  }
192  while ( --n );
193  }
194  #endif
195 }
196 
197 static float const default_decoder [6] =
198  { 0.956f, 0.621f, -0.272f, -0.647f, -1.105f, 1.702f };
199 
200 static void init( init_t* impl, snes_ntsc_setup_t const* setup )
201 {
202  impl->brightness = (float) setup->brightness * (0.5f * rgb_unit) + rgb_offset;
203  impl->contrast = (float) setup->contrast * (0.5f * rgb_unit) + rgb_unit;
204  #ifdef default_palette_contrast
205  if ( !setup->palette )
206  impl->contrast *= default_palette_contrast;
207  #endif
208 
209  impl->artifacts = (float) setup->artifacts;
210  if ( impl->artifacts > 0 )
213 
214  impl->fringing = (float) setup->fringing;
215  if ( impl->fringing > 0 )
217  impl->fringing = impl->fringing * fringing_mid + fringing_mid;
218 
219  init_filters( impl, setup );
220 
221  /* generate gamma table */
222  if ( gamma_size > 1 )
223  {
224  float const to_float = 1.0f / (gamma_size - (gamma_size > 1));
225  float const gamma = 1.1333f - (float) setup->gamma * 0.5f;
226  /* match common PC's 2.2 gamma to TV's 2.65 gamma */
227  int i;
228  for ( i = 0; i < gamma_size; i++ )
229  impl->to_float [i] =
230  (float) pow( i * to_float, gamma ) * impl->contrast + impl->brightness;
231  }
232 
233  /* setup decoder matricies */
234  {
235  float hue = (float) setup->hue * PI + PI / 180 * ext_decoder_hue;
236  float sat = (float) setup->saturation + 1;
237  float const* decoder = setup->decoder_matrix;
238  if ( !decoder )
239  {
240  decoder = default_decoder;
241  if ( STD_HUE_CONDITION( setup ) )
242  hue += PI / 180 * (std_decoder_hue - ext_decoder_hue);
243  }
244 
245  {
246  float s = (float) sin( hue ) * sat;
247  float c = (float) cos( hue ) * sat;
248  float* out = impl->to_rgb;
249  int n;
250 
251  n = burst_count;
252  do
253  {
254  float const* in = decoder;
255  int n = 3;
256  do
257  {
258  float i = *in++;
259  float q = *in++;
260  *out++ = i * c - q * s;
261  *out++ = i * s + q * c;
262  }
263  while ( --n );
264  if ( burst_count <= 1 )
265  break;
266  ROTATE_IQ( s, c, 0.866025f, -0.5f ); /* +120 degrees */
267  }
268  while ( --n );
269  }
270  }
271 }
272 
273 /* kernel generation */
274 
275 #define RGB_TO_YIQ( r, g, b, y, i ) (\
276  (y = (r) * 0.299f + (g) * 0.587f + (b) * 0.114f),\
277  (i = (r) * 0.596f - (g) * 0.275f - (b) * 0.321f),\
278  ((r) * 0.212f - (g) * 0.523f + (b) * 0.311f)\
279 )
280 
281 #define YIQ_TO_RGB( y, i, q, to_rgb, type, r, g ) (\
282  r = (type) (y + to_rgb [0] * i + to_rgb [1] * q),\
283  g = (type) (y + to_rgb [2] * i + to_rgb [3] * q),\
284  (type) (y + to_rgb [4] * i + to_rgb [5] * q)\
285 )
286 
287 #define PACK_RGB( r, g, b ) ((r) << 21 | (g) << 11 | (b) << 1)
288 
291 
292 typedef struct pixel_info_t
293 {
294  int offset;
295  float negate;
296  float kernel [4];
297 } pixel_info_t;
298 
299 #if rescale_in > 1
300  #define PIXEL_OFFSET_( ntsc, scaled ) \
301  (kernel_size / 2 + ntsc + (scaled != 0) + (rescale_out - scaled) % rescale_out + \
302  (kernel_size * 2 * scaled))
303 
304  #define PIXEL_OFFSET( ntsc, scaled ) \
305  PIXEL_OFFSET_( ((ntsc) - (scaled) / rescale_out * rescale_in),\
306  (((scaled) + rescale_out * 10) % rescale_out) ),\
307  (1.0f - (((ntsc) + 100) & 2))
308 #else
309  #define PIXEL_OFFSET( ntsc, scaled ) \
310  (kernel_size / 2 + (ntsc) - (scaled)),\
311  (1.0f - (((ntsc) + 100) & 2))
312 #endif
313 
315 
316 /* Generate pixel at all burst phases and column alignments */
317 static void gen_kernel( init_t* impl, float y, float i, float q, snes_ntsc_rgb_t* out )
318 {
319  /* generate for each scanline burst phase */
320  float const* to_rgb = impl->to_rgb;
321  int burst_remain = burst_count;
322  y -= rgb_offset;
323  do
324  {
325  /* Encode yiq into *two* composite signals (to allow control over artifacting).
326  Convolve these with kernels which: filter respective components, apply
327  sharpening, and rescale horizontally. Convert resulting yiq to rgb and pack
328  into integer. Based on algorithm by NewRisingSun. */
329  pixel_info_t const* pixel = snes_ntsc_pixels;
330  int alignment_remain = alignment_count;
331  do
332  {
333  /* negate is -1 when composite starts at odd multiple of 2 */
334  float const yy = y * impl->fringing * pixel->negate;
335  float const ic0 = (i + yy) * pixel->kernel [0];
336  float const qc1 = (q + yy) * pixel->kernel [1];
337  float const ic2 = (i - yy) * pixel->kernel [2];
338  float const qc3 = (q - yy) * pixel->kernel [3];
339 
340  float const factor = impl->artifacts * pixel->negate;
341  float const ii = i * factor;
342  float const yc0 = (y + ii) * pixel->kernel [0];
343  float const yc2 = (y - ii) * pixel->kernel [2];
344 
345  float const qq = q * factor;
346  float const yc1 = (y + qq) * pixel->kernel [1];
347  float const yc3 = (y - qq) * pixel->kernel [3];
348 
349  float const* k = &impl->kernel [pixel->offset];
350  int n;
351  ++pixel;
352  for ( n = rgb_kernel_size; n; --n )
353  {
354  float i = k[0]*ic0 + k[2]*ic2;
355  float q = k[1]*qc1 + k[3]*qc3;
356  float y = k[kernel_size+0]*yc0 + k[kernel_size+1]*yc1 +
357  k[kernel_size+2]*yc2 + k[kernel_size+3]*yc3 + rgb_offset;
358  if ( rescale_out <= 1 )
359  k--;
360  else if ( k < &impl->kernel [kernel_size * 2 * (rescale_out - 1)] )
361  k += kernel_size * 2 - 1;
362  else
363  k -= kernel_size * 2 * (rescale_out - 1) + 2;
364  {
365  int r, g, b = YIQ_TO_RGB( y, i, q, to_rgb, int, r, g );
366  *out++ = PACK_RGB( r, g, b ) - rgb_bias;
367  }
368  }
369  }
370  while ( alignment_count > 1 && --alignment_remain );
371 
372  if ( burst_count <= 1 )
373  break;
374 
375  to_rgb += 6;
376 
377  ROTATE_IQ( i, q, -0.866025f, -0.5f ); /* -120 degrees */
378  }
379  while ( --burst_remain );
380 }
381 
383 
384 #if DISABLE_CORRECTION
385  #define CORRECT_ERROR( a ) { out [i] += rgb_bias; }
386  #define DISTRIBUTE_ERROR( a, b, c ) { out [i] += rgb_bias; }
387 #else
388  #define CORRECT_ERROR( a ) { out [a] += error; }
389  #define DISTRIBUTE_ERROR( a, b, c ) {\
390  snes_ntsc_rgb_t fourth = (error + 2 * snes_ntsc_rgb_builder) >> 2;\
391  fourth &= (rgb_bias >> 1) - snes_ntsc_rgb_builder;\
392  fourth -= rgb_bias >> 2;\
393  out [a] += fourth;\
394  out [b] += fourth;\
395  out [c] += fourth;\
396  out [i] += error - (fourth * 3);\
397  }
398 #endif
399 
400 #define RGB_PALETTE_OUT( rgb, out_ )\
401 {\
402  unsigned char* out = (out_);\
403  snes_ntsc_rgb_t clamped = (rgb);\
404  SNES_NTSC_CLAMP_( clamped, (8 - rgb_bits) );\
405  out [0] = (unsigned char) (clamped >> 21);\
406  out [1] = (unsigned char) (clamped >> 11);\
407  out [2] = (unsigned char) (clamped >> 1);\
408 }
409 
410 /* blitter related */
411 
412 #ifndef restrict
413  #if defined (__GNUC__)
414  #define restrict __restrict__
415  #elif defined (_MSC_VER) && _MSC_VER > 1300
416  #define restrict __restrict
417  #else
418  /* no support for restricted pointers */
419  #define restrict
420  #endif
421 #endif
422 
423 #include <limits.h>
424 
425 #if SNES_NTSC_OUT_DEPTH <= 16
426  #if USHRT_MAX == 0xFFFF
427  typedef unsigned short snes_ntsc_out_t;
428  #else
429  #error "Need 16-bit int type"
430  #endif
431 
432 #else
433  #if UINT_MAX == 0xFFFFFFFF
434  typedef unsigned int snes_ntsc_out_t;
435  #elif ULONG_MAX == 0xFFFFFFFF
436  typedef unsigned long snes_ntsc_out_t;
437  #else
438  #error "Need 32-bit int type"
439  #endif
440 
441 #endif
442 
443 #endif
float fringing
Definition: snes_ntsc_impl.h:60
Definition: snes_ntsc_impl.h:51
#define burst_count
Definition: snes_ntsc.c:24
unsigned long snes_ntsc_rgb_t
Definition: snes_ntsc.h:136
Definition: snes_ntsc_impl.h:292
GLdouble GLdouble GLdouble r
Definition: glext.h:6406
static overlayled_t * cur
Definition: led_overlay.c:18
GLfloat f
Definition: glext.h:8207
GLfloat angle
Definition: glext.h:11760
GLdouble s
Definition: glext.h:6390
struct passwd out
Definition: missing_libc_functions.c:51
#define snes_ntsc_rgb_builder
Definition: snes_ntsc.h:171
#define exp(a)
Definition: math.h:32
static void correct_errors(snes_ntsc_rgb_t color, snes_ntsc_rgb_t *out)
#define PI
Definition: snes_ntsc_impl.h:24
#define YIQ_TO_RGB(y, i, q, to_rgb, type, r, g)
Definition: snes_ntsc_impl.h:281
GLuint GLuint GLfloat weight
Definition: glext.h:12626
#define ext_decoder_hue
Definition: snes_ntsc_impl.h:45
Definition: snes_ntsc.h:15
const GLubyte * c
Definition: glext.h:9812
GLboolean GLboolean GLboolean b
Definition: glext.h:6844
GLuint GLuint num
Definition: glext.h:10525
float contrast
Definition: snes_ntsc_impl.h:57
Definition: snes_ntsc.h:134
#define std_decoder_hue
Definition: snes_ntsc.c:30
Definition: snes_ntsc_impl.h:289
set set set set set set set macro pixldst1 abits if abits op else op endif endm macro pixldst2 abits if abits op else op endif endm macro pixldst4 abits if abits op else op endif endm macro pixldst0 abits op endm macro pixldst3 mem_operand op endm macro pixldst30 mem_operand op endm macro pixldst abits if abits elseif abits elseif abits elseif abits elseif abits pixldst0 abits else pixldst0 abits pixldst0 abits pixldst0 abits pixldst0 abits endif elseif abits else pixldst0 abits pixldst0 abits endif elseif abits else error unsupported bpp *numpix else pixst endif endm macro pixld1_s mem_operand if asr adds SRC_WIDTH_FIXED bpl add asl mov asr adds SRC_WIDTH_FIXED bpl add asl mov asr adds SRC_WIDTH_FIXED bpl add asl mov asr adds SRC_WIDTH_FIXED bpl add asl elseif asr adds SRC_WIDTH_FIXED bpl add asl mov asr adds SRC_WIDTH_FIXED bpl add asl else error unsupported endif endm macro pixld2_s mem_operand if mov asr add asl add asl mov asr sub UNIT_X add asl mov asr add asl add asl mov asr add UNIT_X add asl else pixld1_s mem_operand pixld1_s mem_operand endif endm macro pixld0_s mem_operand if asr adds SRC_WIDTH_FIXED bpl add asl elseif asr adds SRC_WIDTH_FIXED bpl add asl endif endm macro pixld_s_internal mem_operand if mem_operand pixld2_s mem_operand pixdeinterleave basereg elseif mem_operand elseif mem_operand elseif mem_operand elseif mem_operand pixld0_s mem_operand else pixld0_s mem_operand pixld0_s mem_operand pixld0_s mem_operand pixld0_s mem_operand endif elseif mem_operand else pixld0_s mem_operand pixld0_s mem_operand endif elseif mem_operand else error unsupported mem_operand if bpp mem_operand endif endm macro vuzp8 reg2 vuzp d d &reg2 endm macro vzip8 reg2 vzip d d &reg2 endm macro pixdeinterleave basereg basereg basereg basereg basereg endif endm macro pixinterleave basereg basereg basereg basereg basereg endif endm macro PF boost_increment endif if endif PF tst PF addne PF subne PF cmp ORIG_W if endif if endif if endif PF subge ORIG_W PF subges if endif if endif if endif endif endm macro cache_preload_simple endif if dst_r_bpp pld [DST_R, #(PREFETCH_DISTANCE_SIMPLE *dst_r_bpp/8)] endif if mask_bpp pld if[MASK, #(PREFETCH_DISTANCE_SIMPLE *mask_bpp/8)] endif endif endm macro fetch_mask_pixblock pixld mask_basereg pixblock_size MASK endm macro ensure_destination_ptr_alignment process_pixblock_tail_head if beq irp local skip1(dst_w_bpp<=(lowbit *8)) &&((lowbit *8)<(pixblock_size *dst_w_bpp)) .if lowbit< 16 tst DST_R
Definition: pixman-arm-neon-asm.h:469
Definition: snes_ntsc_impl.h:53
#define alignment_count
Definition: snes_ntsc.c:23
#define fringing_max
Definition: snes_ntsc_impl.h:39
float artifacts
Definition: snes_ntsc_impl.h:59
#define artifacts_mid
Definition: snes_ntsc.c:28
int offset
Definition: snes_ntsc_impl.h:294
struct init_t init_t
struct pixel_info_t pixel_info_t
#define gamma_size
Definition: snes_ntsc_impl.h:30
pixel_info_t const snes_ntsc_pixels[alignment_count]
Definition: snes_ntsc.c:38
GLint GLint GLint GLint GLint GLint y
Definition: glext.h:6295
#define artifacts_max
Definition: snes_ntsc_impl.h:36
GLint GLint GLint GLint GLint x
Definition: glext.h:6295
GLuint in
Definition: glext.h:10523
#define rgb_offset
Definition: snes_ntsc_impl.h:47
GLdouble GLdouble GLdouble GLdouble q
Definition: glext.h:6414
#define retro_assert(cond)
Definition: retro_assert.h:34
#define PACK_RGB(r, g, b)
Definition: snes_ntsc_impl.h:287
float to_float[gamma_size]
Definition: snes_ntsc_impl.h:56
static void init(init_t *impl, snes_ntsc_setup_t const *setup)
Definition: snes_ntsc_impl.h:200
float kernel[rescale_out *kernel_size *2]
Definition: snes_ntsc_impl.h:61
#define rescale_out
Definition: snes_ntsc.c:26
#define sin(x)
Definition: math.h:23
Definition: snes_ntsc_impl.h:49
#define pow(x, y)
Definition: math.h:22
static float const default_decoder[6]
Definition: snes_ntsc_impl.h:197
float kernel[4]
Definition: snes_ntsc_impl.h:296
#define rgb_unit
Definition: snes_ntsc_impl.h:46
Definition: snes_ntsc_impl.h:50
float brightness
Definition: snes_ntsc_impl.h:58
GLboolean GLboolean g
Definition: glext.h:6844
GLuint color
Definition: glext.h:6883
static void gen_kernel(init_t *impl, float y, float i, float q, snes_ntsc_rgb_t *out)
Definition: snes_ntsc_impl.h:317
#define ROTATE_IQ(i, q, sin_b, cos_b)
Definition: snes_ntsc_impl.h:64
#define fringing_mid
Definition: snes_ntsc.c:29
#define cos(x)
Definition: math.h:21
#define STD_HUE_CONDITION(setup)
Definition: snes_ntsc_impl.h:42
float negate
Definition: snes_ntsc_impl.h:295
#define LUMA_CUTOFF
Definition: snes_ntsc_impl.h:27
static void init_filters(init_t *impl, snes_ntsc_setup_t const *setup)
Definition: snes_ntsc_impl.h:71
float to_rgb[burst_count *6]
Definition: snes_ntsc_impl.h:55
#define rescale_in
Definition: snes_ntsc.c:25
GLdouble n
Definition: glext.h:8396
const GLfloat * m
Definition: glext.h:11755
Definition: setup.py:1
Definition: snes_ntsc_impl.h:290