1 ////////////////////////////////////////////////////////////////////////////////////////
2 //
3 // Nestopia - NES/Famicom emulator written in C++
4 //
5 // Copyright (C) 2003-2008 Martin Freij
6 //
7 // This file is part of Nestopia.
8 //
9 // Nestopia is free software; you can redistribute it and/or modify
10 // it under the terms of the GNU General Public License as published by
11 // the Free Software Foundation; either version 2 of the License, or
12 // (at your option) any later version.
13 //
14 // Nestopia is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 // GNU General Public License for more details.
18 //
19 // You should have received a copy of the GNU General Public License
20 // along with Nestopia; if not, write to the Free Software
21 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22 //
23 ////////////////////////////////////////////////////////////////////////////////////////
24 
25 #include <cstring>
26 #include <cmath>
27 #include <new>
28 #include "NstCore.hpp"
29 #include "NstAssert.hpp"
30 #include "NstFpuPrecision.hpp"
31 #include "api/NstApiVideo.hpp"
32 #include "NstVideoRenderer.hpp"
33 #include "NstVideoFilterNone.hpp"
34 
35 #ifndef NO_NTSC
36 #include "NstVideoFilterNtsc.hpp"
37 #endif
38 #ifndef NST_NO_SCALEX
39 #include "NstVideoFilterScaleX.hpp"
40 #endif
41 #ifndef NST_NO_HQ2X
42 #include "NstVideoFilterHqX.hpp"
43 #endif
44 #ifndef NST_NO_2XSAI
45 #include "NstVideoFilter2xSaI.hpp"
46 #endif
47 #ifndef NST_NO_XBR
48 #include "NstVideoFilterxBR.hpp"
49 #endif
50 
51 namespace Nes
52 {
53 	namespace Core
54 	{
55 		namespace Video
56 		{
57 			const byte Renderer::Palette::pc10Palette[64][3] =
58 			{
59 				{0x6D,0x6D,0x6D}, {0x00,0x24,0x92}, {0x00,0x00,0xDB}, {0x6D,0x49,0xDB},
60 				{0x92,0x00,0x6D}, {0xB6,0x00,0x6D}, {0xB6,0x24,0x00}, {0x92,0x49,0x00},
61 				{0x6D,0x49,0x00}, {0x24,0x49,0x00}, {0x00,0x6D,0x24}, {0x00,0x92,0x00},
62 				{0x00,0x49,0x49}, {0x00,0x00,0x00}, {0x00,0x00,0x00}, {0x00,0x00,0x00},
63 				{0xB6,0xB6,0xB6}, {0x00,0x6D,0xDB}, {0x00,0x49,0xFF}, {0x92,0x00,0xFF},
64 				{0xB6,0x00,0xFF}, {0xFF,0x00,0x92}, {0xFF,0x00,0x00}, {0xDB,0x6D,0x00},
65 				{0x92,0x6D,0x00}, {0x24,0x92,0x00}, {0x00,0x92,0x00}, {0x00,0xB6,0x6D},
66 				{0x00,0x92,0x92}, {0x24,0x24,0x24}, {0x00,0x00,0x00}, {0x00,0x00,0x00},
67 				{0xFF,0xFF,0xFF}, {0x6D,0xB6,0xFF}, {0x92,0x92,0xFF}, {0xDB,0x6D,0xFF},
68 				{0xFF,0x00,0xFF}, {0xFF,0x6D,0xFF}, {0xFF,0x92,0x00}, {0xFF,0xB6,0x00},
69 				{0xDB,0xDB,0x00}, {0x6D,0xDB,0x00}, {0x00,0xFF,0x00}, {0x49,0xFF,0xDB},
70 				{0x00,0xFF,0xFF}, {0x49,0x49,0x49}, {0x00,0x00,0x00}, {0x00,0x00,0x00},
71 				{0xFF,0xFF,0xFF}, {0xB6,0xDB,0xFF}, {0xDB,0xB6,0xFF}, {0xFF,0xB6,0xFF},
72 				{0xFF,0x92,0xFF}, {0xFF,0xB6,0xB6}, {0xFF,0xDB,0x92}, {0xFF,0xFF,0x49},
73 				{0xFF,0xFF,0x6D}, {0xB6,0xFF,0x49}, {0x92,0xFF,0x6D}, {0x49,0xFF,0xDB},
74 				{0x92,0xDB,0xFF}, {0x92,0x92,0x92}, {0x00,0x00,0x00}, {0x00,0x00,0x00}
75 			};
76 
77 			const byte Renderer::Palette::vsPalette[4][64][3] =
78 			{
79 				{
80 					{0xFF,0xB6,0xB6}, {0xDB,0x6D,0xFF}, {0xFF,0x00,0x00}, {0x92,0x92,0xFF},
81 					{0x00,0x92,0x92}, {0x24,0x49,0x00}, {0x49,0x49,0x49}, {0xFF,0x00,0x92},
82 					{0xFF,0xFF,0xFF}, {0x6D,0x6D,0x6D}, {0xFF,0xB6,0x00}, {0xB6,0x00,0x6D},
83 					{0x92,0x00,0x6D}, {0xDB,0xDB,0x00}, {0x6D,0x49,0x00}, {0xFF,0xFF,0xFF},
84 					{0x6D,0xB6,0xFF}, {0xDB,0xB6,0x6D}, {0x6D,0x24,0x00}, {0x6D,0xDB,0x00},
85 					{0x92,0xDB,0xFF}, {0xDB,0xB6,0xFF}, {0xFF,0xDB,0x92}, {0x00,0x49,0xFF},
86 					{0xFF,0xDB,0x00}, {0x49,0xFF,0xDB}, {0x00,0x00,0x00}, {0x49,0x00,0x00},
87 					{0xDB,0xDB,0xDB}, {0x92,0x92,0x92}, {0xFF,0x00,0xFF}, {0x00,0x24,0x92},
88 					{0x00,0x00,0x6D}, {0xB6,0xDB,0xFF}, {0xFF,0xB6,0xFF}, {0x00,0xFF,0x00},
89 					{0x00,0xFF,0xFF}, {0x00,0x49,0x49}, {0x00,0xB6,0x6D}, {0xB6,0x00,0xFF},
90 					{0x00,0x00,0x00}, {0x92,0x49,0x00}, {0xFF,0x92,0xFF}, {0xB6,0x24,0x00},
91 					{0x92,0x00,0xFF}, {0x00,0x00,0xDB}, {0xFF,0x92,0x00}, {0x00,0x00,0x00},
92 					{0x00,0x00,0x00}, {0x24,0x92,0x00}, {0xB6,0xB6,0xB6}, {0x00,0x6D,0x24},
93 					{0xB6,0xFF,0x49}, {0x6D,0x49,0xDB}, {0xFF,0xFF,0x00}, {0xDB,0x6D,0x00},
94 					{0x00,0x49,0x00}, {0x00,0x6D,0xDB}, {0x00,0x92,0x00}, {0x24,0x24,0x24},
95 					{0xFF,0xFF,0x6D}, {0xFF,0x6D,0xFF}, {0x92,0x6D,0x00}, {0x92,0xFF,0x6D}
96 				},
97 				{
98 					{0x00,0x00,0x00}, {0xFF,0xB6,0x00}, {0x92,0x6D,0x00}, {0xB6,0xFF,0x49},
99 					{0x92,0xFF,0x6D}, {0xFF,0x6D,0xFF}, {0x00,0x92,0x92}, {0xB6,0xDB,0xFF},
100 					{0xFF,0x00,0x00}, {0x92,0x00,0xFF}, {0xFF,0xFF,0x6D}, {0xFF,0x92,0xFF},
101 					{0xFF,0xFF,0xFF}, {0xDB,0x6D,0xFF}, {0x92,0xDB,0xFF}, {0x00,0x92,0x00},
102 					{0x00,0x49,0x00}, {0x6D,0xB6,0xFF}, {0xB6,0x24,0x00}, {0xDB,0xDB,0xDB},
103 					{0x00,0xB6,0x6D}, {0x6D,0xDB,0x00}, {0x49,0x00,0x00}, {0x92,0x92,0xFF},
104 					{0x49,0x49,0x49}, {0xFF,0x00,0xFF}, {0x00,0x00,0x6D}, {0x49,0xFF,0xDB},
105 					{0xDB,0xB6,0xFF}, {0x6D,0x49,0x00}, {0x00,0x00,0x00}, {0x6D,0x49,0xDB},
106 					{0x92,0x00,0x6D}, {0xFF,0xDB,0x92}, {0xFF,0x92,0x00}, {0xFF,0xB6,0xFF},
107 					{0x00,0x6D,0xDB}, {0x6D,0x24,0x00}, {0xB6,0xB6,0xB6}, {0x00,0x00,0xDB},
108 					{0xB6,0x00,0xFF}, {0xFF,0xDB,0x00}, {0x6D,0x6D,0x6D}, {0x24,0x49,0x00},
109 					{0x00,0x49,0xFF}, {0x00,0x00,0x00}, {0xDB,0xDB,0x00}, {0xFF,0xFF,0xFF},
110 					{0xDB,0xB6,0x6D}, {0x24,0x24,0x24}, {0x00,0xFF,0x00}, {0xDB,0x6D,0x00},
111 					{0x00,0x49,0x49}, {0x00,0x24,0x92}, {0xFF,0x00,0x92}, {0x24,0x92,0x00},
112 					{0x00,0x00,0x00}, {0x00,0xFF,0xFF}, {0x92,0x49,0x00}, {0xFF,0xFF,0x00},
113 					{0xFF,0xB6,0xB6}, {0xB6,0x00,0x6D}, {0x00,0x6D,0x24}, {0x92,0x92,0x92}
114 				},
115 				{
116 					{0xB6,0x00,0xFF}, {0xFF,0x6D,0xFF}, {0x92,0xFF,0x6D}, {0xB6,0xB6,0xB6},
117 					{0x00,0x92,0x00}, {0xFF,0xFF,0xFF}, {0xB6,0xDB,0xFF}, {0x24,0x49,0x00},
118 					{0x00,0x24,0x92}, {0x00,0x00,0x00}, {0xFF,0xDB,0x92}, {0x6D,0x49,0x00},
119 					{0xFF,0x00,0x92}, {0xDB,0xDB,0xDB}, {0xDB,0xB6,0x6D}, {0x92,0xDB,0xFF},
120 					{0x92,0x92,0xFF}, {0x00,0x92,0x92}, {0xB6,0x00,0x6D}, {0x00,0x49,0xFF},
121 					{0x24,0x92,0x00}, {0x92,0x6D,0x00}, {0xDB,0x6D,0x00}, {0x00,0xB6,0x6D},
122 					{0x6D,0x6D,0x6D}, {0x6D,0x49,0xDB}, {0x00,0x00,0x00}, {0x00,0x00,0xDB},
123 					{0xFF,0x00,0x00}, {0xB6,0x24,0x00}, {0xFF,0x92,0xFF}, {0xFF,0xB6,0xB6},
124 					{0xDB,0x6D,0xFF}, {0x00,0x49,0x00}, {0x00,0x00,0x6D}, {0xFF,0xFF,0x00},
125 					{0x24,0x24,0x24}, {0xFF,0xB6,0x00}, {0xFF,0x92,0x00}, {0xFF,0xFF,0xFF},
126 					{0x6D,0xDB,0x00}, {0x92,0x00,0x6D}, {0x6D,0xB6,0xFF}, {0xFF,0x00,0xFF},
127 					{0x00,0x6D,0xDB}, {0x92,0x92,0x92}, {0x00,0x00,0x00}, {0x6D,0x24,0x00},
128 					{0x00,0xFF,0xFF}, {0x49,0x00,0x00}, {0xB6,0xFF,0x49}, {0xFF,0xB6,0xFF},
129 					{0x92,0x49,0x00}, {0x00,0xFF,0x00}, {0xDB,0xDB,0x00}, {0x49,0x49,0x49},
130 					{0x00,0x6D,0x24}, {0x00,0x00,0x00}, {0xDB,0xB6,0xFF}, {0xFF,0xFF,0x6D},
131 					{0x92,0x00,0xFF}, {0x49,0xFF,0xDB}, {0xFF,0xDB,0x00}, {0x00,0x49,0x49}
132 				},
133 				{
134 					{0x92,0x6D,0x00}, {0x6D,0x49,0xDB}, {0x00,0x92,0x92}, {0xDB,0xDB,0x00},
135 					{0x00,0x00,0x00}, {0xFF,0xB6,0xB6}, {0x00,0x24,0x92}, {0xDB,0x6D,0x00},
136 					{0xB6,0xB6,0xB6}, {0x6D,0x24,0x00}, {0x00,0xFF,0x00}, {0x00,0x00,0x6D},
137 					{0xFF,0xDB,0x92}, {0xFF,0xFF,0x00}, {0x00,0x92,0x00}, {0xB6,0xFF,0x49},
138 					{0xFF,0x6D,0xFF}, {0x49,0x00,0x00}, {0x00,0x49,0xFF}, {0xFF,0x92,0xFF},
139 					{0x00,0x00,0x00}, {0x49,0x49,0x49}, {0xB6,0x24,0x00}, {0xFF,0x92,0x00},
140 					{0xDB,0xB6,0x6D}, {0x00,0xB6,0x6D}, {0x92,0x92,0xFF}, {0x24,0x92,0x00},
141 					{0x92,0x00,0x6D}, {0x00,0x00,0x00}, {0x92,0xFF,0x6D}, {0x6D,0xB6,0xFF},
142 					{0xB6,0x00,0x6D}, {0x00,0x6D,0x24}, {0x92,0x49,0x00}, {0x00,0x00,0xDB},
143 					{0x92,0x00,0xFF}, {0xB6,0x00,0xFF}, {0x6D,0x6D,0x6D}, {0xFF,0x00,0x92},
144 					{0x00,0x49,0x49}, {0xDB,0xDB,0xDB}, {0x00,0x6D,0xDB}, {0x00,0x49,0x00},
145 					{0x24,0x24,0x24}, {0xFF,0xFF,0x6D}, {0x92,0x92,0x92}, {0xFF,0x00,0xFF},
146 					{0xFF,0xB6,0xFF}, {0xFF,0xFF,0xFF}, {0x6D,0x49,0x00}, {0xFF,0x00,0x00},
147 					{0xFF,0xDB,0x00}, {0x49,0xFF,0xDB}, {0xFF,0xFF,0xFF}, {0x92,0xDB,0xFF},
148 					{0x00,0x00,0x00}, {0xFF,0xB6,0x00}, {0xDB,0x6D,0xFF}, {0xB6,0xDB,0xFF},
149 					{0x6D,0xDB,0x00}, {0xDB,0xB6,0xFF}, {0x00,0xFF,0xFF}, {0x24,0x49,0x00}
150 				}
151 			};
152 
153 			const double Renderer::Palette::Constants::pi = 3.141592653589793;
154 			const double Renderer::Palette::Constants::deg = 0.017453292519943296;
155 
156 			const double Renderer::Palette::Constants::levels[2][4] =
157 			{
158 				{-0.12, 0.00, 0.31, 0.72 },
159 				{ 0.40, 0.68, 1.00, 1.00 }
160 			};
161 
162 			#ifdef NST_MSVC_OPTIMIZE
163 			#pragma optimize("s", on)
164 			#endif
165 
Custom()166 			inline Renderer::Palette::Custom::Custom()
167 			: emphasis(NULL) {}
168 
~Custom()169 			inline Renderer::Palette::Custom::~Custom()
170 			{
171 				delete [] emphasis;
172 			}
173 
EnableEmphasis(bool enable)174 			bool Renderer::Palette::Custom::EnableEmphasis(bool enable)
175 			{
176 				if (!enable)
177 				{
178 					delete [] emphasis;
179 					emphasis = NULL;
180 				}
181 				else if (!emphasis)
182 				{
183 					emphasis = new (std::nothrow) byte [7][64][3];
184 				}
185 
186 				return bool(emphasis) == enable;
187 			}
188 
Palette()189 			Renderer::Palette::Palette()
190 			: type(PALETTE_YUV), custom(NULL)
191 			{
192 			}
193 
~Palette()194 			Renderer::Palette::~Palette()
195 			{
196 				delete custom;
197 			}
198 
SetDecoder(const Decoder & d)199 			Result Renderer::Palette::SetDecoder(const Decoder& d)
200 			{
201 				if (decoder == d)
202 					return RESULT_NOP;
203 
204 				for (uint i=0; i < 3; ++i)
205 				{
206 					if (d.axes[i].angle >= 360 || d.axes[i].gain > 2.0)
207 						return RESULT_ERR_INVALID_PARAM;
208 				}
209 
210 				decoder = d;
211 				return RESULT_OK;
212 			}
213 
Store(const double (& src)[3],byte (& dst)[3])214 			void Renderer::Palette::Store(const double (&src)[3],byte (&dst)[3])
215 			{
216 				for (uint i=0; i < 3; ++i)
217 					dst[i] = Clamp<0,255>( src[i] * 255 + 0.5 );
218 			}
219 
LoadCustom(const byte (* colors)[3],const bool emphasis)220 			Result Renderer::Palette::LoadCustom(const byte (*colors)[3],const bool emphasis)
221 			{
222 				if (!colors)
223 					return RESULT_ERR_INVALID_PARAM;
224 
225 				if ((custom == NULL && NULL == (custom = new (std::nothrow) Custom)) || !custom->EnableEmphasis( emphasis ))
226 					return RESULT_ERR_OUT_OF_MEMORY;
227 
228 				std::memcpy( custom->palette, colors, 64*3 );
229 
230 				if (emphasis)
231 					std::memcpy( custom->emphasis, colors + 64, 7*64*3 );
232 
233 				return RESULT_OK;
234 			}
235 
SaveCustom(byte (* colors)[3],const bool emphasis) const236 			uint Renderer::Palette::SaveCustom(byte (*colors)[3],const bool emphasis) const
237 			{
238 				if (!colors)
239 					return 0;
240 
241 				std::memcpy( colors, custom ? custom->palette : pc10Palette, 64*3 );
242 
243 				if (!emphasis || !custom || !custom->emphasis)
244 					return 64;
245 
246 				std::memcpy( colors + 64, custom->emphasis, 7*64*3 );
247 
248 				return 7*64;
249 			}
250 
ResetCustom()251 			bool Renderer::Palette::ResetCustom()
252 			{
253 				if (custom)
254 				{
255 					custom->EnableEmphasis( false );
256 					std::memcpy( custom->palette, pc10Palette, 64*3 );
257 					return true;
258 				}
259 
260 				return false;
261 			}
262 
SetType(PaletteType t)263 			Result Renderer::Palette::SetType(PaletteType t)
264 			{
265 				if (t == type)
266 					return RESULT_NOP;
267 
268 				if (t == PALETTE_CUSTOM && !custom)
269 				{
270 					if (NULL == (custom = new (std::nothrow) Custom))
271 						return RESULT_ERR_OUT_OF_MEMORY;
272 
273 					ResetCustom();
274 				}
275 
276 				type = t;
277 
278 				return RESULT_OK;
279 			}
280 
GenerateEmphasis(uint tint,double s,double & y,double & i,double & q)281 			void Renderer::Palette::GenerateEmphasis(uint tint,double s,double& y,double& i,double& q)
282 			{
283 				if (tint == 7)
284 				{
285 					y = y * (0.79399 * 1.13) - (0.0782838 * 1.13);
286 				}
287 				else
288 				{
289 					s = s * (0.5 - 0.79399 * 0.5) + 0.0782838 * 0.5;
290 					y -= s * 0.5;
291 
292 					if (tint >= 3 && tint != 4)
293 					{
294 						s *= 0.6;
295 						y -= s;
296 					}
297 
298 					static const byte tints[8] =
299 					{
300 						0, 6, 10, 8, 2, 4, 0, 0
301 					};
302 
303 					const double a = Constants::pi / 12 * (tints[tint] * 2 - 7);
304 
305 					i += std::sin( a ) * s;
306 					q += std::cos( a ) * s;
307 				}
308 			}
309 
Build(const int bi,const int si,const int ci,const int hue)310 			void Renderer::Palette::Build(const int bi,const int si,const int ci,const int hue)
311 			{
312 				NST_ASSERT( type != PALETTE_YUV );
313 
314 				const double brightness = bi / 200.0;
315 				const double saturation = (si + 100) / 100.0;
316 				const double contrast = (ci + 100) / 100.0;
317 
318 				const double matrix[6] =
319 				{
320 					std::sin( (90  - 33 - hue) * Constants::deg ) * (0.570 * 2),
321 					std::cos( (90  - 33 - hue) * Constants::deg ) * (0.570 * 2),
322 					std::sin( (236 - 33 - hue) * Constants::deg ) * (0.351 * 2),
323 					std::cos( (236 - 33 - hue) * Constants::deg ) * (0.351 * 2),
324 					std::sin( (0   - 33 - hue) * Constants::deg ) * (1.015 * 2),
325 					std::cos( (0   - 33 - hue) * Constants::deg ) * (1.015 * 2)
326 				};
327 
328 				const byte (*from)[3] =
329 				(
330 					type == PALETTE_CUSTOM ? custom->palette :
331 					type == PALETTE_VS1    ? vsPalette[0] :
332 					type == PALETTE_VS2    ? vsPalette[1] :
333 					type == PALETTE_VS3    ? vsPalette[2] :
334 					type == PALETTE_VS4    ? vsPalette[3] :
335                                              pc10Palette
336 				);
337 
338 				NST_ASSERT( from );
339 
340 				for (uint i=0; i < 8; ++i)
341 				{
342 					if (i && type == PALETTE_CUSTOM && custom->emphasis)
343 						from = custom->emphasis[i-1];
344 
345 					for (uint j=0; j < 64; ++j)
346 					{
347 						double rgb[3] =
348 						{
349 							from[j][0] / 255.0,
350 							from[j][1] / 255.0,
351 							from[j][2] / 255.0
352 						};
353 
354 						if (i && type != PALETTE_CUSTOM)
355 						{
356 							switch (i)
357 							{
358 								case 1: rgb[0] = 1;                         break;
359 								case 2: rgb[1] = 1;                         break;
360 								case 3: rgb[0] = 1; rgb[1] = 1;             break;
361 								case 4: rgb[2] = 1;                         break;
362 								case 5: rgb[0] = 1; rgb[2] = 1;             break;
363 								case 6: rgb[1] = 1; rgb[2] = 1;             break;
364 								case 7: rgb[0] = 1; rgb[1] = 1; rgb[2] = 1; break;
365 							}
366 						}
367 
368 						double yiq[3] =
369 						{
370 							0.299 * rgb[0] + 0.587 * rgb[1] + 0.114 * rgb[2],
371 							0.596 * rgb[0] - 0.275 * rgb[1] - 0.321 * rgb[2],
372 							0.212 * rgb[0] - 0.523 * rgb[1] + 0.311 * rgb[2]
373 						};
374 
375 						if (i && type == PALETTE_CUSTOM && !custom->emphasis && (j & 0xF) <= 0xD)
376 							GenerateEmphasis( i, Constants::levels[(j & 0xF) != 0xD][j >> 4 & 0x3], yiq[0], yiq[1], yiq[2] );
377 
378 						yiq[0] = yiq[0] * contrast + brightness;
379 						yiq[1] *= saturation;
380 						yiq[2] *= saturation;
381 
382 						for (uint k=0; k < 3; ++k)
383 							rgb[k] = yiq[0] + matrix[k*2+0] * yiq[1] + matrix[k*2+1] * yiq[2];
384 
385 						Store( rgb, palette[(i * 64) + j] );
386 					}
387 				}
388 			}
389 
Generate(const int b,const int s,const int c,int hue)390 			void Renderer::Palette::Generate(const int b,const int s,const int c,int hue)
391 			{
392 				NST_ASSERT( type == PALETTE_YUV );
393 
394 				const double brightness = b / 200.0;
395 				const double saturation = (s + 100) / 100.0;
396 				const double contrast = (c + 100) / 100.0;
397 				hue += 33;
398 
399 				const double matrix[6] =
400 				{
401 					std::sin( (int(decoder.axes[0].angle) - hue) * Constants::deg ) * decoder.axes[0].gain * 2,
402 					std::cos( (int(decoder.axes[0].angle) - hue) * Constants::deg ) * decoder.axes[0].gain * 2,
403 					std::sin( (int(decoder.axes[1].angle) - hue) * Constants::deg ) * decoder.axes[1].gain * 2,
404 					std::cos( (int(decoder.axes[1].angle) - hue) * Constants::deg ) * decoder.axes[1].gain * 2,
405 					std::sin( (int(decoder.axes[2].angle) - hue) * Constants::deg ) * decoder.axes[2].gain * 2,
406 					std::cos( (int(decoder.axes[2].angle) - hue) * Constants::deg ) * decoder.axes[2].gain * 2
407 				};
408 
409 				for (uint n=0; n < PALETTE; ++n)
410 				{
411 					double level[2] =
412 					{
413 						Constants::levels[0][n >> 4 & 3],
414 						Constants::levels[1][n >> 4 & 3]
415 					};
416 
417 					const int color = n & 0x0F;
418 
419 					if (color == 0x00)
420 					{
421 						level[0] = level[1];
422 					}
423 					else if (color == 0x0D)
424 					{
425 						level[1] = level[0];
426 					}
427 					else if (color > 0x0D)
428 					{
429 						level[0] = 0;
430 						level[1] = 0;
431 					}
432 
433 					double y = (level[1] + level[0]) * 0.5;
434 					double s = (level[1] - level[0]) * 0.5;
435 					double h = Constants::pi / 6 * (color - 3);
436 
437 					double i = std::sin( h ) * s;
438 					double q = std::cos( h ) * s;
439 
440 					const uint tint = n >> 6 & 7;
441 
442 					if (tint && color <= 0x0D)
443 						GenerateEmphasis( tint, level[1], y, i, q );
444 
445 					if (decoder.boostYellow)
446 					{
447 						const double yellowness = i - q;
448 
449 						if (yellowness > DBL_EPSILON)
450 						{
451 							i = i + yellowness * ((n >> 4 & 3) / 4.0);
452 							q = q - yellowness * ((n >> 4 & 3) / 4.0);
453 						}
454 					}
455 
456 					i *= saturation;
457 					q *= saturation;
458 					y = y * contrast + brightness;
459 
460 					const double rgb[3] =
461 					{
462 						y + matrix[0] * i + matrix[1] * q,
463 						y + matrix[2] * i + matrix[3] * q,
464 						y + matrix[4] * i + matrix[5] * q
465 					};
466 
467 					Store( rgb, palette[n] );
468 				}
469 			}
470 
Update(int brightness,int saturation,int contrast,int hue)471 			void Renderer::Palette::Update(int brightness,int saturation,int contrast,int hue)
472 			{
473 				FpuPrecision precision;
474 				(*this.*(type == PALETTE_YUV ? &Palette::Generate : &Palette::Build))( brightness, saturation, contrast, hue );
475 			}
476 
Get() const477 			inline const Renderer::PaletteEntries& Renderer::Palette::Get() const
478 			{
479 				return palette;
480 			}
481 
Format(const RenderState & state)482 			Renderer::Filter::Format::Format(const RenderState& state)
483 			: bpp(state.bits.count)
484 			{
485 				for (uint i=0; i < 3; ++i)
486 				{
487 					ulong mask = (i == 0 ? state.bits.mask.r : i == 1 ? state.bits.mask.g : state.bits.mask.b);
488 
489 					shifts[i] = 0;
490 
491 					if (mask)
492 					{
493 						while (!(mask & 0x1))
494 						{
495 							mask >>= 1;
496 							shifts[i]++;
497 						}
498 					}
499 
500 					masks[i] = mask;
501 				}
502 			}
503 
Filter(const RenderState & state)504 			Renderer::Filter::Filter(const RenderState& state)
505 			: format(state) {}
506 
Transform(const byte (& src)[PALETTE][3],Input::Palette & dst) const507 			void Renderer::Filter::Transform(const byte (&src)[PALETTE][3],Input::Palette& dst) const
508 			{
509 				for (uint i=0; i < PALETTE; ++i)
510 				{
511 					dst[i] =
512 					(
513 						((src[i][0] * format.masks[0] + 0x7F) / 0xFF) << format.shifts[0] |
514 						((src[i][1] * format.masks[1] + 0x7F) / 0xFF) << format.shifts[1] |
515 						((src[i][2] * format.masks[2] + 0x7F) / 0xFF) << format.shifts[2]
516 					);
517 				}
518 			}
519 
State()520 			Renderer::State::State()
521 			:
522 			width        (0),
523 			height       (0),
524 			filter       (RenderState::FILTER_NONE),
525 			update       (UPDATE_PALETTE),
526 			fieldMerging (0),
527 			brightness   (0),
528 			saturation   (0),
529 			hue          (0),
530 			contrast     (0),
531 			sharpness    (0),
532 			resolution   (0),
533 			bleed        (0),
534 			artifacts    (0),
535 			fringing     (0),
536 			blendPixels	 (1),
537 			xbr_corner_rounding(0)
538 			{
539 				mask.r = 0;
540 				mask.g = 0;
541 				mask.b = 0;
542 			}
543 
Renderer()544 			Renderer::Renderer()
545 			: filter(NULL) {}
546 
~Renderer()547 			Renderer::~Renderer()
548 			{
549 				delete filter;
550 			}
551 
SetState(const RenderState & renderState)552 			Result Renderer::SetState(const RenderState& renderState)
553 			{
554 				if (filter)
555 				{
556 					if
557 					(
558 						state.filter == renderState.filter &&
559 						state.width == renderState.width &&
560 						state.height == renderState.height &&
561 						filter->format.bpp == renderState.bits.count &&
562 						state.mask.r == renderState.bits.mask.r &&
563 						state.mask.g == renderState.bits.mask.g &&
564 						state.mask.b == renderState.bits.mask.b
565 					)
566 						return RESULT_NOP;
567 
568 					delete filter;
569 					filter = NULL;
570 				}
571 
572 				try
573 				{
574 					switch (renderState.filter)
575 					{
576 						case RenderState::FILTER_NONE:
577 
578 							if (FilterNone::Check( renderState ))
579 								filter = new FilterNone( renderState );
580 
581 							break;
582 
583 					#ifndef NST_NO_SCALEX
584 
585 						case RenderState::FILTER_SCALE2X:
586 						case RenderState::FILTER_SCALE3X:
587 
588 							if (FilterScaleX::Check( renderState ))
589 								filter = new FilterScaleX( renderState );
590 
591 							break;
592 					#endif
593 					#ifndef NST_NO_HQ2X
594 
595 						case RenderState::FILTER_HQ2X:
596 						case RenderState::FILTER_HQ3X:
597 						case RenderState::FILTER_HQ4X:
598 
599 							if (FilterHqX::Check( renderState ))
600 								filter = new FilterHqX( renderState );
601 
602 							break;
603 
604 					#endif
605 					#ifndef NST_NO_2XSAI
606 
607 						case RenderState::FILTER_2XSAI:
608 
609 							if (Filter2xSaI::Check( renderState ))
610 								filter = new Filter2xSaI( renderState );
611 
612 							break;
613 
614 					#endif
615 
616 					#ifndef NO_NTSC
617 						case RenderState::FILTER_NTSC:
618 
619 							if (FilterNtsc::Check( renderState ))
620 							{
621 								filter = new FilterNtsc
622 								(
623 									renderState,
624 									GetPalette(),
625 									state.sharpness,
626 									state.resolution,
627 									state.bleed,
628 									state.artifacts,
629 									state.fringing,
630 									state.fieldMerging
631 								);
632 							}
633 							break;
634 					#endif
635 
636 					#ifndef NST_NO_XBR
637 						case RenderState::FILTER_2XBR:
638 						case RenderState::FILTER_3XBR:
639 						case RenderState::FILTER_4XBR:
640 
641 							if (FilterxBR::Check( renderState ))
642 								filter = new FilterxBR( renderState, state.blendPixels, state.xbr_corner_rounding );
643 							break;
644 					#endif
645 					}
646 				}
647 				catch (const std::bad_alloc&)
648 				{
649 					delete filter;
650 					filter = NULL;
651 
652 					return RESULT_ERR_OUT_OF_MEMORY;
653 				}
654 
655 				if (filter)
656 				{
657 					state.filter = renderState.filter;
658 					state.width = renderState.width;
659 					state.height = renderState.height;
660 					state.mask = renderState.bits.mask;
661 
662 					if (state.filter == RenderState::FILTER_NTSC)
663 						state.update = 0;
664 					else
665 						state.update |= uint(State::UPDATE_FILTER);
666 
667 					return RESULT_OK;
668 				}
669 				else
670 				{
671 					return RESULT_ERR_UNSUPPORTED;
672 				}
673 			}
674 
GetState(RenderState & output) const675 			Result Renderer::GetState(RenderState& output) const
676 			{
677 				if (filter)
678 				{
679 					output.filter = static_cast<RenderState::Filter>(state.filter);
680 					output.width = state.width;
681 					output.height = state.height;
682 					output.bits.count = filter->format.bpp;
683 					output.bits.mask = state.mask;
684 
685 					return RESULT_OK;
686 				}
687 
688 				return RESULT_ERR_NOT_READY;
689 			}
690 
EnableFieldMerging(bool fieldMerging)691 			void Renderer::EnableFieldMerging(bool fieldMerging)
692 			{
693 				const bool old = state.fieldMerging;
694 				state.fieldMerging &= uint(State::FIELD_MERGING_FORCED);
695 
696 				if (fieldMerging)
697 					state.fieldMerging |= uint(State::FIELD_MERGING_USER);
698 
699 				if (bool(state.fieldMerging) != old)
700 					state.update |= uint(State::UPDATE_NTSC);
701 			}
702 
EnableForcedFieldMerging(bool fieldMerging)703 			void Renderer::EnableForcedFieldMerging(bool fieldMerging)
704 			{
705 				const bool old = state.fieldMerging;
706 				state.fieldMerging &= uint(State::FIELD_MERGING_USER);
707 
708 				if (fieldMerging)
709 					state.fieldMerging |= uint(State::FIELD_MERGING_FORCED);
710 
711 				if (bool(state.fieldMerging) != old)
712 					state.update |= uint(State::UPDATE_NTSC);
713 			}
714 
SetHue(int hue)715 			Result Renderer::SetHue(int hue)
716 			{
717 				if (hue < -45 || hue > 45)
718 					return RESULT_ERR_INVALID_PARAM;
719 
720 				if (state.hue == hue)
721 					return RESULT_NOP;
722 
723 				state.hue = hue;
724 				state.update |= uint(State::UPDATE_PALETTE|State::UPDATE_FILTER);
725 
726 				return RESULT_OK;
727 			}
728 
SetLevel(schar & type,int value,uint update)729 			Result Renderer::SetLevel(schar& type,int value,uint update)
730 			{
731 				if (value < -100 || value > 100)
732 					return RESULT_ERR_INVALID_PARAM;
733 
734 				if (type == value)
735 					return RESULT_NOP;
736 
737 				type = value;
738 				state.update |= update;
739 
740 				return RESULT_OK;
741 			}
742 
SetDecoder(const Decoder & decoder)743 			Result Renderer::SetDecoder(const Decoder& decoder)
744 			{
745 				const Result result = palette.SetDecoder( decoder );
746 
747 				if (NES_SUCCEEDED(result) && result != RESULT_NOP && palette.GetType() == PALETTE_YUV)
748 					state.update |= uint(State::UPDATE_PALETTE|State::UPDATE_FILTER);
749 
750 				return result;
751 			}
752 
SetPaletteType(PaletteType type)753 			Result Renderer::SetPaletteType(PaletteType type)
754 			{
755 				const Result result = palette.SetType( type );
756 
757 				if (NES_SUCCEEDED(result) && result != RESULT_NOP)
758 					state.update |= uint(State::UPDATE_PALETTE|State::UPDATE_FILTER);
759 
760 				return result;
761 			}
762 
LoadCustomPalette(const byte (* colors)[3],const bool emphasis)763 			Result Renderer::LoadCustomPalette(const byte (*colors)[3],const bool emphasis)
764 			{
765 				const Result result = palette.LoadCustom( colors, emphasis );
766 
767 				if (NES_SUCCEEDED(result) && result != RESULT_NOP && palette.GetType() == PALETTE_CUSTOM)
768 					state.update |= uint(State::UPDATE_PALETTE|State::UPDATE_FILTER);
769 
770 				return result;
771 			}
772 
ResetCustomPalette()773 			void Renderer::ResetCustomPalette()
774 			{
775 				if (palette.ResetCustom() && palette.GetType() == PALETTE_CUSTOM)
776 					state.update |= uint(State::UPDATE_PALETTE|State::UPDATE_FILTER);
777 			}
778 
GetPalette()779 			const Renderer::PaletteEntries& Renderer::GetPalette()
780 			{
781 				if (state.update & uint(State::UPDATE_PALETTE))
782 				{
783 					state.update &= ~uint(State::UPDATE_PALETTE);
784 					palette.Update( state.brightness, state.saturation, state.contrast, state.hue );
785 				}
786 
787 				return palette.Get();
788 			}
789 
UpdateFilter(Input & input)790 			void Renderer::UpdateFilter(Input& input)
791 			{
792 				NST_VERIFY( state.update );
793 
794 				if (state.filter == RenderState::FILTER_NTSC || state.update == 1)
795 				{
796 					RenderState renderState;
797 					GetState( renderState );
798 
799 					delete filter;
800 					filter = NULL;
801 
802 					SetState( renderState );
803 				}
804 				else if (state.update & uint(State::UPDATE_FILTER))
805 				{
806 					filter->Transform( GetPalette(), input.palette );
807 				}
808 
809 				state.update = 0;
810 			}
811 
812 			#ifdef NST_MSVC_OPTIMIZE
813 			#pragma optimize("", on)
814 			#endif
815 
Blit(Output & output,Input & input,uint burstPhase)816 			void Renderer::Blit(Output& output,Input& input,uint burstPhase)
817 			{
818 				if (filter)
819 				{
820 					if (state.update)
821 						UpdateFilter( input );
822 
823 					if (Output::lockCallback( output ))
824 					{
825 						NST_VERIFY( std::labs(output.pitch) >= dword(state.width) << (filter->format.bpp / 16) );
826 
827 						filter->bgColor = bgColor;
828 
829 						if (std::labs(output.pitch) >= dword(state.width) << (filter->format.bpp / 16))
830 							filter->Blit( input, output, burstPhase );
831 
832 						Output::unlockCallback( output );
833 					}
834 				}
835 			}
836 		}
837 	}
838 }
839