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