1//============================================================================
2//
3// SSSS tt lll lll
4// SS SS tt ll ll
5// SS tttttt eeee ll ll aaaa
6// SSSS tt ee ee ll ll aa
7// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
8// SS SS tt ee ll ll aa aa
9// SSSS ttt eeeee llll llll aaaaa
10//
11// Copyright (c) 1995-2019 by Bradford W. Mott, Stephen Anthony
12// and the Stella Team
13//
14// See the file "License.txt" for information on usage and redistribution of
15// this file, and for a DISCLAIMER OF ALL WARRANTIES.
16//============================================================================
17
18#include <thread>
19#include "AtariNTSC.hxx"
20
21// blitter related
22#ifndef restrict
23 #if defined (__GNUC__)
24 #define restrict __restrict__
25 #elif defined (_MSC_VER) && _MSC_VER > 1300
26 #define restrict __restrict
27 #else
28 /* no support for restricted pointers */
29 #define restrict
30 #endif
31#endif
32
33// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
34void AtariNTSC::initialize(const Setup& setup, const uInt8* palette)
35{
36 init(myImpl, setup);
37 initializePalette(palette);
38}
39
40// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
41void AtariNTSC::initializePalette(const uInt8* palette)
42{
43 // Palette stores R/G/B data for 'palette_size' entries
44 for ( uInt32 entry = 0; entry < palette_size; ++entry )
45 {
46 float r = myImpl.to_float [*palette++];
47 float g = myImpl.to_float [*palette++];
48 float b = myImpl.to_float [*palette++];
49
50 float y, i, q = RGB_TO_YIQ( r, g, b, y, i );
51
52 // Generate kernel
53 int ir, ig, ib = YIQ_TO_RGB( y, i, q, myImpl.to_rgb, int, ir, ig );
54 uInt32 rgb = PACK_RGB( ir, ig, ib );
55
56 uInt32* kernel = myColorTable[entry];
57 genKernel(myImpl, y, i, q, kernel);
58
59 for ( uInt32 c = 0; c < rgb_kernel_size / 2; ++c )
60 {
61 uInt32 error = rgb -
62 kernel [c ] - kernel [(c+10)%14+14] -
63 kernel [c + 7] - kernel [c + 3 +14];
64 kernel [c + 3 + 14] += error;
65 }
66 }
67}
68
69// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
70void AtariNTSC::enableThreading(bool enable)
71{
72 uInt32 systemThreads = enable ? std::thread::hardware_concurrency() : 0;
73 if(systemThreads <= 1)
74 {
75 myWorkerThreads = 0;
76 myTotalThreads = 1;
77 }
78 else
79 {
80 systemThreads = std::max(1u, std::min(4u, systemThreads - 1));
81
82 myWorkerThreads = systemThreads - 1;
83 myTotalThreads = systemThreads;
84
85 myThreads = make_unique<std::thread[]>(myWorkerThreads);
86 }
87}
88
89// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
90void AtariNTSC::render(const uInt8* atari_in, const uInt32 in_width, const uInt32 in_height,
91 void* rgb_out, const uInt32 out_pitch, uInt32* rgb_in)
92{
93 // Spawn the threads...
94 for(uInt32 i = 0; i < myWorkerThreads; ++i)
95 {
96 myThreads[i] = std::thread([=] {
97 rgb_in == nullptr ?
98 renderThread(atari_in, in_width, in_height, myTotalThreads, i+1, rgb_out, out_pitch) :
99 renderWithPhosphorThread(atari_in, in_width, in_height, myTotalThreads, i+1, rgb_in, rgb_out, out_pitch);
100 });
101 }
102 // Make the main thread busy too
103 rgb_in == nullptr ?
104 renderThread(atari_in, in_width, in_height, myTotalThreads, 0, rgb_out, out_pitch) :
105 renderWithPhosphorThread(atari_in, in_width, in_height, myTotalThreads, 0, rgb_in, rgb_out, out_pitch);
106 // ...and make them join again
107 for(uInt32 i = 0; i < myWorkerThreads; ++i)
108 myThreads[i].join();
109
110 // Copy phosphor values into out buffer
111 if(rgb_in != nullptr)
112 memcpy(rgb_out, rgb_in, in_height * out_pitch);
113}
114
115// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
116void AtariNTSC::renderThread(const uInt8* atari_in, const uInt32 in_width,
117 const uInt32 in_height, const uInt32 numThreads, const uInt32 threadNum,
118 void* rgb_out, const uInt32 out_pitch)
119{
120 // Adapt parameters to thread number
121 const uInt32 yStart = in_height * threadNum / numThreads;
122 const uInt32 yEnd = in_height * (threadNum + 1) / numThreads;
123 atari_in += in_width * yStart;
124 rgb_out = static_cast<char*>(rgb_out) + out_pitch * yStart;
125
126 uInt32 const chunk_count = (in_width - 1) / PIXEL_in_chunk;
127
128 for(uInt32 y = yStart; y < yEnd; ++y)
129 {
130 const uInt8* line_in = atari_in;
131 ATARI_NTSC_BEGIN_ROW(NTSC_black, line_in[0]);
132 uInt32* restrict line_out = static_cast<uInt32*>(rgb_out);
133 ++line_in;
134
135 // shift right by 2 pixel
136 line_out[0] = line_out[1] = 0;
137 line_out += 2;
138
139 for(uInt32 n = chunk_count; n; --n)
140 {
141 // order of input and output pixels must not be altered
142 ATARI_NTSC_COLOR_IN(0, line_in[0])
143 ATARI_NTSC_RGB_OUT_8888(0, line_out[0])
144 ATARI_NTSC_RGB_OUT_8888(1, line_out[1])
145 ATARI_NTSC_RGB_OUT_8888(2, line_out[2])
146 ATARI_NTSC_RGB_OUT_8888(3, line_out[3])
147
148 ATARI_NTSC_COLOR_IN(1, line_in[1])
149 ATARI_NTSC_RGB_OUT_8888(4, line_out[4])
150 ATARI_NTSC_RGB_OUT_8888(5, line_out[5])
151 ATARI_NTSC_RGB_OUT_8888(6, line_out[6])
152
153 line_in += 2;
154 line_out += 7;
155 }
156
157 // finish final pixels
158 ATARI_NTSC_COLOR_IN(0, line_in[0])
159 ATARI_NTSC_RGB_OUT_8888(0, line_out[0])
160 ATARI_NTSC_RGB_OUT_8888(1, line_out[1])
161 ATARI_NTSC_RGB_OUT_8888(2, line_out[2])
162 ATARI_NTSC_RGB_OUT_8888(3, line_out[3])
163
164 ATARI_NTSC_COLOR_IN(1, NTSC_black)
165 ATARI_NTSC_RGB_OUT_8888(4, line_out[4])
166 ATARI_NTSC_RGB_OUT_8888(5, line_out[5])
167 ATARI_NTSC_RGB_OUT_8888(6, line_out[6])
168
169 line_in += 2;
170 line_out += 7;
171
172 ATARI_NTSC_COLOR_IN(0, NTSC_black)
173 ATARI_NTSC_RGB_OUT_8888(0, line_out[0])
174 ATARI_NTSC_RGB_OUT_8888(1, line_out[1])
175 ATARI_NTSC_RGB_OUT_8888(2, line_out[2])
176 ATARI_NTSC_RGB_OUT_8888(3, line_out[3])
177
178 ATARI_NTSC_COLOR_IN(1, NTSC_black)
179 ATARI_NTSC_RGB_OUT_8888(4, line_out[4])
180#if 0
181 ATARI_NTSC_RGB_OUT_8888(5, line_out[5])
182 ATARI_NTSC_RGB_OUT_8888(6, line_out[6])
183#endif
184
185 atari_in += in_width;
186 rgb_out = static_cast<char*>(rgb_out) + out_pitch;
187 }
188}
189
190// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
191void AtariNTSC::renderWithPhosphorThread(const uInt8* atari_in, const uInt32 in_width,
192 const uInt32 in_height, const uInt32 numThreads, const uInt32 threadNum,
193 uInt32* rgb_in, void* rgb_out, const uInt32 out_pitch)
194{
195 // Adapt parameters to thread number
196 const uInt32 yStart = in_height * threadNum / numThreads;
197 const uInt32 yEnd = in_height * (threadNum + 1) / numThreads;
198 uInt32 bufofs = AtariNTSC::outWidth(in_width) * yStart;
199 uInt32* out = static_cast<uInt32*>(rgb_out);
200 atari_in += in_width * yStart;
201 rgb_out = static_cast<char*>(rgb_out) + out_pitch * yStart;
202
203 uInt32 const chunk_count = (in_width - 1) / PIXEL_in_chunk;
204
205 for(uInt32 y = yStart; y < yEnd; ++y)
206 {
207 const uInt8* line_in = atari_in;
208 ATARI_NTSC_BEGIN_ROW(NTSC_black, line_in[0]);
209 uInt32* restrict line_out = static_cast<uInt32*>(rgb_out);
210 ++line_in;
211
212 // shift right by 2 pixel
213 line_out[0] = line_out[1] = 0;
214 line_out += 2;
215
216 for(uInt32 n = chunk_count; n; --n)
217 {
218 // order of input and output pixels must not be altered
219 ATARI_NTSC_COLOR_IN(0, line_in[0])
220 ATARI_NTSC_RGB_OUT_8888(0, line_out[0])
221 ATARI_NTSC_RGB_OUT_8888(1, line_out[1])
222 ATARI_NTSC_RGB_OUT_8888(2, line_out[2])
223 ATARI_NTSC_RGB_OUT_8888(3, line_out[3])
224
225 ATARI_NTSC_COLOR_IN(1, line_in[1])
226 ATARI_NTSC_RGB_OUT_8888(4, line_out[4])
227 ATARI_NTSC_RGB_OUT_8888(5, line_out[5])
228 ATARI_NTSC_RGB_OUT_8888(6, line_out[6])
229
230 line_in += 2;
231 line_out += 7;
232 }
233
234 // finish final pixels
235 ATARI_NTSC_COLOR_IN(0, line_in[0])
236 ATARI_NTSC_RGB_OUT_8888(0, line_out[0])
237 ATARI_NTSC_RGB_OUT_8888(1, line_out[1])
238 ATARI_NTSC_RGB_OUT_8888(2, line_out[2])
239 ATARI_NTSC_RGB_OUT_8888(3, line_out[3])
240
241 ATARI_NTSC_COLOR_IN(1, NTSC_black)
242 ATARI_NTSC_RGB_OUT_8888(4, line_out[4])
243 ATARI_NTSC_RGB_OUT_8888(5, line_out[5])
244 ATARI_NTSC_RGB_OUT_8888(6, line_out[6])
245
246 line_in += 2;
247 line_out += 7;
248
249 ATARI_NTSC_COLOR_IN(0, NTSC_black)
250 ATARI_NTSC_RGB_OUT_8888(0, line_out[0])
251 ATARI_NTSC_RGB_OUT_8888(1, line_out[1])
252 ATARI_NTSC_RGB_OUT_8888(2, line_out[2])
253 ATARI_NTSC_RGB_OUT_8888(3, line_out[3])
254
255 ATARI_NTSC_COLOR_IN(1, NTSC_black)
256 ATARI_NTSC_RGB_OUT_8888(4, line_out[4])
257#if 0
258 ATARI_NTSC_RGB_OUT_8888(5, line_out[5])
259 ATARI_NTSC_RGB_OUT_8888(6, line_out[6])
260#endif
261
262 // Do phosphor mode (blend the resulting frames)
263 // Note: The unrolled code assumed that AtariNTSC::outWidth(kTIAW) == outPitch == 565
264 // Now this got changed to 568 so he final 5 calculations got removed.
265 for (uInt32 x = AtariNTSC::outWidth(in_width) / 8; x; --x)
266 {
267 // Store back into displayed frame buffer (for next frame)
268 rgb_in[bufofs] = getRGBPhosphor(out[bufofs], rgb_in[bufofs]);
269 ++bufofs;
270 rgb_in[bufofs] = getRGBPhosphor(out[bufofs], rgb_in[bufofs]);
271 ++bufofs;
272 rgb_in[bufofs] = getRGBPhosphor(out[bufofs], rgb_in[bufofs]);
273 ++bufofs;
274 rgb_in[bufofs] = getRGBPhosphor(out[bufofs], rgb_in[bufofs]);
275 ++bufofs;
276 rgb_in[bufofs] = getRGBPhosphor(out[bufofs], rgb_in[bufofs]);
277 ++bufofs;
278 rgb_in[bufofs] = getRGBPhosphor(out[bufofs], rgb_in[bufofs]);
279 ++bufofs;
280 rgb_in[bufofs] = getRGBPhosphor(out[bufofs], rgb_in[bufofs]);
281 ++bufofs;
282 rgb_in[bufofs] = getRGBPhosphor(out[bufofs], rgb_in[bufofs]);
283 ++bufofs;
284 }
285 // finish final 565 % 8 = 5 pixels
286 /*rgb_in[bufofs] = getRGBPhosphor(out[bufofs], rgb_in[bufofs]);
287 ++bufofs;
288 rgb_in[bufofs] = getRGBPhosphor(out[bufofs], rgb_in[bufofs]);
289 ++bufofs;
290 rgb_in[bufofs] = getRGBPhosphor(out[bufofs], rgb_in[bufofs]);
291 ++bufofs;
292 rgb_in[bufofs] = getRGBPhosphor(out[bufofs], rgb_in[bufofs]);
293 ++bufofs;
294 rgb_in[bufofs] = getRGBPhosphor(out[bufofs], rgb_in[bufofs]);
295 ++bufofs;*/
296#if 0
297 rgb_in[bufofs] = getRGBPhosphor(out[bufofs], rgb_in[bufofs]);
298 ++bufofs;
299 rgb_in[bufofs] = getRGBPhosphor(out[bufofs], rgb_in[bufofs]);
300 ++bufofs;
301#endif
302
303 atari_in += in_width;
304 rgb_out = static_cast<char*>(rgb_out) + out_pitch;
305 }
306}
307
308// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
309inline uInt32 AtariNTSC::getRGBPhosphor(const uInt32 c, const uInt32 p) const
310{
311#define TO_RGB(color, red, green, blue) \
312 const uInt8 red = uInt8(color >> 16); \
313 const uInt8 green = uInt8(color >> 8);\
314 const uInt8 blue = uInt8(color);
315
316 TO_RGB(c, rc, gc, bc)
317 TO_RGB(p, rp, gp, bp)
318
319 // Mix current calculated frame with previous displayed frame
320 const uInt8 rn = myPhosphorPalette[rc][rp];
321 const uInt8 gn = myPhosphorPalette[gc][gp];
322 const uInt8 bn = myPhosphorPalette[bc][bp];
323
324 return (rn << 16) | (gn << 8) | bn;
325}
326
327// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
328void AtariNTSC::init(init_t& impl, const Setup& setup)
329{
330 impl.brightness = setup.brightness * (0.5f * rgb_unit) + rgb_offset;
331 impl.contrast = setup.contrast * (0.5f * rgb_unit) + rgb_unit;
332
333 impl.artifacts = setup.artifacts;
334 if ( impl.artifacts > 0 )
335 impl.artifacts *= artifacts_max - artifacts_mid;
336 impl.artifacts = impl.artifacts * artifacts_mid + artifacts_mid;
337
338 impl.fringing = setup.fringing;
339 if ( impl.fringing > 0 )
340 impl.fringing *= fringing_max - fringing_mid;
341 impl.fringing = impl.fringing * fringing_mid + fringing_mid;
342
343 initFilters(impl, setup);
344
345 /* generate gamma table */
346 if (true) /* was (gamma_size > 1) */
347 {
348 float const to_float = 1.0f / (gamma_size - 1/*(gamma_size > 1)*/);
349 float const gamma = 1.1333f - setup.gamma * 0.5f;
350 /* match common PC's 2.2 gamma to TV's 2.65 gamma */
351 int i;
352 for ( i = 0; i < gamma_size; i++ )
353 impl.to_float [i] =
354 powf( i * to_float, gamma ) * impl.contrast + impl.brightness;
355 }
356
357 /* setup decoder matricies */
358 {
359 float hue = setup.hue * BSPF::PI_f + BSPF::PI_f / 180 * ext_decoder_hue;
360 float sat = setup.saturation + 1;
361 hue += BSPF::PI_f / 180 * (std_decoder_hue - ext_decoder_hue);
362
363 float s = sinf( hue ) * sat;
364 float c = cosf( hue ) * sat;
365 float* out = impl.to_rgb;
366 int n;
367
368 n = burst_count;
369 do
370 {
371 float const* in = default_decoder;
372 int n2 = 3;
373 do
374 {
375 float i = *in++;
376 float q = *in++;
377 *out++ = i * c - q * s;
378 *out++ = i * s + q * c;
379 }
380 while ( --n2 );
381 #if 0 // burst_count is always 0
382 if ( burst_count > 1 )
383 ROTATE_IQ( s, c, 0.866025f, -0.5f ); /* +120 degrees */
384 #endif
385 }
386 while ( --n );
387 }
388}
389
390// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
391void AtariNTSC::initFilters(init_t& impl, const Setup& setup)
392{
393 float kernels [kernel_size * 2];
394
395 /* generate luma (y) filter using sinc kernel */
396 {
397 /* sinc with rolloff (dsf) */
398 float const rolloff = 1 + setup.sharpness * 0.032f;
399 constexpr float maxh = 32;
400 float const pow_a_n = powf( rolloff, maxh );
401 float sum;
402 /* quadratic mapping to reduce negative (blurring) range */
403 float to_angle = setup.resolution + 1;
404 to_angle = BSPF::PI_f / maxh * LUMA_CUTOFF * (to_angle * to_angle + 1.f);
405
406 kernels [kernel_size * 3 / 2] = maxh; /* default center value */
407 for ( int i = 0; i < kernel_half * 2 + 1; i++ )
408 {
409 int x = i - kernel_half;
410 float angle = x * to_angle;
411 /* instability occurs at center point with rolloff very close to 1.0 */
412 if ( x || pow_a_n > 1.056f || pow_a_n < 0.981f )
413 {
414 float rolloff_cos_a = rolloff * cosf( angle );
415 float num = 1 - rolloff_cos_a -
416 pow_a_n * cosf( maxh * angle ) +
417 pow_a_n * rolloff * cosf( (maxh - 1) * angle );
418 float den = 1 - rolloff_cos_a - rolloff_cos_a + rolloff * rolloff;
419 float dsf = num / den;
420 kernels [kernel_size * 3 / 2 - kernel_half + i] = dsf - 0.5f;
421 }
422 }
423
424 /* apply blackman window and find sum */
425 sum = 0;
426 for ( int i = 0; i < kernel_half * 2 + 1; i++ )
427 {
428 float x = BSPF::PI_f * 2 / (kernel_half * 2) * i;
429 float blackman = 0.42f - 0.5f * cosf( x ) + 0.08f * cosf( x * 2 );
430 sum += (kernels [kernel_size * 3 / 2 - kernel_half + i] *= blackman);
431 }
432
433 /* normalize kernel */
434 sum = 1.0f / sum;
435 for ( int i = 0; i < kernel_half * 2 + 1; i++ )
436 {
437 int x = kernel_size * 3 / 2 - kernel_half + i;
438 kernels [x] *= sum;
439 }
440 }
441
442 /* generate chroma (iq) filter using gaussian kernel */
443 {
444 constexpr float cutoff_factor = -0.03125f;
445 float cutoff = setup.bleed;
446
447 if ( cutoff < 0 )
448 {
449 /* keep extreme value accessible only near upper end of scale (1.0) */
450 cutoff *= cutoff;
451 cutoff *= cutoff;
452 cutoff *= cutoff;
453 cutoff *= -30.0f / 0.65f;
454 }
455 cutoff = cutoff_factor - 0.65f * cutoff_factor * cutoff;
456
457 for ( int i = -kernel_half; i <= kernel_half; i++ )
458 kernels [kernel_size / 2 + i] = expf( i * i * cutoff );
459
460 /* normalize even and odd phases separately */
461 for ( int i = 0; i < 2; i++ )
462 {
463 float sum = 0;
464 int x;
465 for ( x = i; x < kernel_size; x += 2 )
466 sum += kernels [x];
467
468 sum = 1.0f / sum;
469 for ( x = i; x < kernel_size; x += 2 )
470 {
471 kernels [x] *= sum;
472 }
473 }
474 }
475
476 /* generate linear rescale kernels */
477 float weight = 1.0f;
478 float* out = impl.kernel;
479 int n = rescale_out;
480 do
481 {
482 float remain = 0;
483 weight -= 1.0f / rescale_in;
484 for ( int i = 0; i < kernel_size * 2; i++ )
485 {
486 float cur = kernels [i];
487 float m = cur * weight;
488 *out++ = m + remain;
489 remain = cur - m;
490 }
491 }
492 while ( --n );
493}
494
495// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
496// Generate pixel at all burst phases and column alignments
497void AtariNTSC::genKernel(init_t& impl, float y, float i, float q, uInt32* out)
498{
499 /* generate for each scanline burst phase */
500 float const* to_rgb = impl.to_rgb;
501 int burst_remain = burst_count;
502 y -= rgb_offset;
503 do
504 {
505 /* Encode yiq into *two* composite signals (to allow control over artifacting).
506 Convolve these with kernels which: filter respective components, apply
507 sharpening, and rescale horizontally. Convert resulting yiq to rgb and pack
508 into integer. Based on algorithm by NewRisingSun. */
509 pixel_info_t const* pixel = atari_ntsc_pixels;
510 int alignment_remain = alignment_count;
511 do
512 {
513 /* negate is -1 when composite starts at odd multiple of 2 */
514 float const yy = y * impl.fringing * pixel->negate;
515 float const ic0 = (i + yy) * pixel->kernel [0];
516 float const qc1 = (q + yy) * pixel->kernel [1];
517 float const ic2 = (i - yy) * pixel->kernel [2];
518 float const qc3 = (q - yy) * pixel->kernel [3];
519
520 float const factor = impl.artifacts * pixel->negate;
521 float const ii = i * factor;
522 float const yc0 = (y + ii) * pixel->kernel [0];
523 float const yc2 = (y - ii) * pixel->kernel [2];
524
525 float const qq = q * factor;
526 float const yc1 = (y + qq) * pixel->kernel [1];
527 float const yc3 = (y - qq) * pixel->kernel [3];
528
529 float const* k = &impl.kernel [pixel->offset];
530 int n;
531 ++pixel;
532 for ( n = rgb_kernel_size; n; --n )
533 {
534 float fi = k[0]*ic0 + k[2]*ic2;
535 float fq = k[1]*qc1 + k[3]*qc3;
536 float fy = k[kernel_size+0]*yc0 + k[kernel_size+1]*yc1 +
537 k[kernel_size+2]*yc2 + k[kernel_size+3]*yc3 + rgb_offset;
538 if ( k < &impl.kernel [kernel_size * 2 * (rescale_out - 1)] )
539 k += kernel_size * 2 - 1;
540 else
541 k -= kernel_size * 2 * (rescale_out - 1) + 2;
542 {
543 int r, g, b = YIQ_TO_RGB( fy, fi, fq, to_rgb, int, r, g );
544 *out++ = PACK_RGB( r, g, b ) - rgb_bias;
545 }
546 }
547 }
548 while ( /*alignment_count > 1 && */ --alignment_remain );
549 }
550 while ( --burst_remain );
551}
552
553// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
554const AtariNTSC::Setup AtariNTSC::TV_Composite = {
555 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.15f, 0.0f, 0.0f, 0.0f
556};
557const AtariNTSC::Setup AtariNTSC::TV_SVideo = {
558 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.45f, -1.0f, -1.0f, 0.0f
559};
560const AtariNTSC::Setup AtariNTSC::TV_RGB = {
561 0.0f, 0.0f, 0.0f, 0.0f, 0.2f, 0.0f, 0.70f, -1.0f, -1.0f, -1.0f
562};
563const AtariNTSC::Setup AtariNTSC::TV_Bad = {
564 0.1f, -0.3f, 0.3f, 0.25f, 0.2f, 0.0f, 0.1f, 0.5f, 0.5f, 0.5f
565};
566
567// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
568const AtariNTSC::pixel_info_t AtariNTSC::atari_ntsc_pixels[alignment_count] = {
569 { PIXEL_OFFSET( -4, -9 ), { 1, 1, 1, 1 } },
570 { PIXEL_OFFSET( 0, -5 ), { 1, 1, 1, 1 } },
571};
572
573const float AtariNTSC::default_decoder[6] = {
574 0.9563f, 0.6210f, -0.2721f, -0.6474f, -1.1070f, 1.7046f
575};
576