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