Mesen-SX/Utilities/snes_ntsc.cpp
2020-12-19 23:32:47 +03:00

252 lines
8.3 KiB
C++

#include "stdafx.h"
/* snes_ntsc 0.2.2. http://www.slack.net/~ant/ */
#include "snes_ntsc.h"
/* Copyright (C) 2006-2007 Shay Green. This module is free software; you
can redistribute it and/or modify it under the terms of the GNU Lesser
General Public License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version. This
module is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
details. You should have received a copy of the GNU Lesser General Public
License along with this module; if not, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
snes_ntsc_setup_t const snes_ntsc_monochrome = {0, -1, 0, 0, .2, 0, .2, -.2, -.2, -1, 1, 0, 0};
snes_ntsc_setup_t const snes_ntsc_composite = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0};
snes_ntsc_setup_t const snes_ntsc_svideo = {0, 0, 0, 0, .2, 0, .2, -1, -1, 0, 1, 0, 0};
snes_ntsc_setup_t const snes_ntsc_rgb = {0, 0, 0, 0, .2, 0, .7, -1, -1, -1, 1, 0, 0};
#define alignment_count 3
#define burst_count 3
#define rescale_in 8
#define rescale_out 7
#define artifacts_mid 1.0f
#define fringing_mid 1.0f
#define std_decoder_hue 0
#define rgb_bits 7 /* half normal range to allow for doubled hires pixels */
#define gamma_size 32
#include "snes_ntsc_impl.h"
/* 3 input pixels -> 8 composite samples */
pixel_info_t const snes_ntsc_pixels[alignment_count] = {
{PIXEL_OFFSET(-4, -9), {1, 1, .6667f, 0}},
{PIXEL_OFFSET(-2, -7), {.3333f, 1, 1, .3333f}},
{PIXEL_OFFSET(0, -5), {0, .6667f, 1, 1}},
};
static void merge_kernel_fields(snes_ntsc_rgb_t* io)
{
int n;
for (n = burst_size; n; --n)
{
snes_ntsc_rgb_t p0 = io[burst_size * 0] + rgb_bias;
snes_ntsc_rgb_t p1 = io[burst_size * 1] + rgb_bias;
snes_ntsc_rgb_t p2 = io[burst_size * 2] + rgb_bias;
/* merge colors without losing precision */
io[burst_size * 0] =
((p0 + p1 - ((p0 ^ p1) & snes_ntsc_rgb_builder)) >> 1) - rgb_bias;
io[burst_size * 1] =
((p1 + p2 - ((p1 ^ p2) & snes_ntsc_rgb_builder)) >> 1) - rgb_bias;
io[burst_size * 2] =
((p2 + p0 - ((p2 ^ p0) & snes_ntsc_rgb_builder)) >> 1) - rgb_bias;
++io;
}
}
static void correct_errors(snes_ntsc_rgb_t color, snes_ntsc_rgb_t* out)
{
int n;
for (n = burst_count; n; --n)
{
unsigned i;
for (i = 0; i < rgb_kernel_size / 2; i++)
{
snes_ntsc_rgb_t error = color -
out[i] - out[(i + 12) % 14 + 14] - out[(i + 10) % 14 + 28] -
out[i + 7] - out[i + 5 + 14] - out[i + 3 + 28];
DISTRIBUTE_ERROR(i+3+28, i+5+14, i+7);
}
out += alignment_count * rgb_kernel_size;
}
}
void snes_ntsc_init(snes_ntsc_t* ntsc, snes_ntsc_setup_t const* setup)
{
int merge_fields;
int entry;
init_t impl;
if (!setup)
setup = &snes_ntsc_composite;
init(&impl, setup);
merge_fields = setup->merge_fields;
if (setup->artifacts <= -1 && setup->fringing <= -1)
merge_fields = 1;
for (entry = 0; entry < snes_ntsc_palette_size; entry++)
{
/* Reduce number of significant bits of source color. Clearing the
low bits of R and B were least notictable. Modifying green was too
noticeable. */
int ir = entry >> 8 & 0x1E;
int ig = entry >> 4 & 0x1F;
int ib = entry << 1 & 0x1E;
#if SNES_NTSC_BSNES_COLORTBL
if ( setup->bsnes_colortbl )
{
int bgr15 = (ib << 10) | (ig << 5) | ir;
unsigned long rgb16 = setup->bsnes_colortbl [bgr15];
ir = rgb16 >> 11 & 0x1E;
ig = rgb16 >> 6 & 0x1F;
ib = rgb16 & 0x1E;
}
#endif
{
float rr = impl.to_float[ir];
float gg = impl.to_float[ig];
float bb = impl.to_float[ib];
float y, i, q = RGB_TO_YIQ(rr, gg, bb, y, i);
int r, g, b = YIQ_TO_RGB(y, i, q, impl.to_rgb, int, r, g);
snes_ntsc_rgb_t rgb = PACK_RGB(r, g, b);
snes_ntsc_rgb_t* out = ntsc->table[entry];
gen_kernel(&impl, y, i, q, out);
if (merge_fields)
merge_kernel_fields(out);
correct_errors(rgb, out);
}
}
}
#ifndef SNES_NTSC_NO_BLITTERS
void snes_ntsc_blit(snes_ntsc_t const* ntsc, SNES_NTSC_IN_T const* input, long in_row_width,
int burst_phase, int in_width, int in_height, void* rgb_out, long out_pitch)
{
int chunk_count = (in_width - 1) / snes_ntsc_in_chunk;
for (; in_height; --in_height)
{
SNES_NTSC_IN_T const* line_in = input;
SNES_NTSC_BEGIN_ROW(ntsc, burst_phase,
snes_ntsc_black, snes_ntsc_black, SNES_NTSC_ADJ_IN( *line_in ));
snes_ntsc_out_t* restrict line_out = (snes_ntsc_out_t*)rgb_out;
int n;
++line_in;
for (n = chunk_count; n; --n)
{
/* order of input and output pixels must not be altered */
SNES_NTSC_COLOR_IN(0, SNES_NTSC_ADJ_IN( line_in [0] ));
SNES_NTSC_RGB_OUT(0, line_out [0], SNES_NTSC_OUT_DEPTH);
SNES_NTSC_RGB_OUT(1, line_out [1], SNES_NTSC_OUT_DEPTH);
SNES_NTSC_COLOR_IN(1, SNES_NTSC_ADJ_IN( line_in [1] ));
SNES_NTSC_RGB_OUT(2, line_out [2], SNES_NTSC_OUT_DEPTH);
SNES_NTSC_RGB_OUT(3, line_out [3], SNES_NTSC_OUT_DEPTH);
SNES_NTSC_COLOR_IN(2, SNES_NTSC_ADJ_IN( line_in [2] ));
SNES_NTSC_RGB_OUT(4, line_out [4], SNES_NTSC_OUT_DEPTH);
SNES_NTSC_RGB_OUT(5, line_out [5], SNES_NTSC_OUT_DEPTH);
SNES_NTSC_RGB_OUT(6, line_out [6], SNES_NTSC_OUT_DEPTH);
line_in += 3;
line_out += 7;
}
/* finish final pixels */
SNES_NTSC_COLOR_IN(0, snes_ntsc_black);
SNES_NTSC_RGB_OUT(0, line_out [0], SNES_NTSC_OUT_DEPTH);
SNES_NTSC_RGB_OUT(1, line_out [1], SNES_NTSC_OUT_DEPTH);
SNES_NTSC_COLOR_IN(1, snes_ntsc_black);
SNES_NTSC_RGB_OUT(2, line_out [2], SNES_NTSC_OUT_DEPTH);
SNES_NTSC_RGB_OUT(3, line_out [3], SNES_NTSC_OUT_DEPTH);
SNES_NTSC_COLOR_IN(2, snes_ntsc_black);
SNES_NTSC_RGB_OUT(4, line_out [4], SNES_NTSC_OUT_DEPTH);
SNES_NTSC_RGB_OUT(5, line_out [5], SNES_NTSC_OUT_DEPTH);
SNES_NTSC_RGB_OUT(6, line_out [6], SNES_NTSC_OUT_DEPTH);
burst_phase = (burst_phase + 1) % snes_ntsc_burst_count;
input += in_row_width;
rgb_out = (char*)rgb_out + out_pitch;
}
}
void snes_ntsc_blit_hires(snes_ntsc_t const* ntsc, SNES_NTSC_IN_T const* input, long in_row_width,
int burst_phase, int in_width, int in_height, void* rgb_out, long out_pitch)
{
int chunk_count = (in_width - 2) / (snes_ntsc_in_chunk * 2);
for (; in_height; --in_height)
{
SNES_NTSC_IN_T const* line_in = input;
SNES_NTSC_HIRES_ROW(ntsc, burst_phase,
snes_ntsc_black, snes_ntsc_black, snes_ntsc_black,
SNES_NTSC_ADJ_IN( line_in [0] ),
SNES_NTSC_ADJ_IN( line_in [1] ));
snes_ntsc_out_t* restrict line_out = (snes_ntsc_out_t*)rgb_out;
int n;
line_in += 2;
for (n = chunk_count; n; --n)
{
/* twice as many input pixels per chunk */
SNES_NTSC_COLOR_IN(0, SNES_NTSC_ADJ_IN( line_in [0] ));
SNES_NTSC_HIRES_OUT(0, line_out [0], SNES_NTSC_OUT_DEPTH);
SNES_NTSC_COLOR_IN(1, SNES_NTSC_ADJ_IN( line_in [1] ));
SNES_NTSC_HIRES_OUT(1, line_out [1], SNES_NTSC_OUT_DEPTH);
SNES_NTSC_COLOR_IN(2, SNES_NTSC_ADJ_IN( line_in [2] ));
SNES_NTSC_HIRES_OUT(2, line_out [2], SNES_NTSC_OUT_DEPTH);
SNES_NTSC_COLOR_IN(3, SNES_NTSC_ADJ_IN( line_in [3] ));
SNES_NTSC_HIRES_OUT(3, line_out [3], SNES_NTSC_OUT_DEPTH);
SNES_NTSC_COLOR_IN(4, SNES_NTSC_ADJ_IN( line_in [4] ));
SNES_NTSC_HIRES_OUT(4, line_out [4], SNES_NTSC_OUT_DEPTH);
SNES_NTSC_COLOR_IN(5, SNES_NTSC_ADJ_IN( line_in [5] ));
SNES_NTSC_HIRES_OUT(5, line_out [5], SNES_NTSC_OUT_DEPTH);
SNES_NTSC_HIRES_OUT(6, line_out [6], SNES_NTSC_OUT_DEPTH);
line_in += 6;
line_out += 7;
}
SNES_NTSC_COLOR_IN(0, snes_ntsc_black);
SNES_NTSC_HIRES_OUT(0, line_out [0], SNES_NTSC_OUT_DEPTH);
SNES_NTSC_COLOR_IN(1, snes_ntsc_black);
SNES_NTSC_HIRES_OUT(1, line_out [1], SNES_NTSC_OUT_DEPTH);
SNES_NTSC_COLOR_IN(2, snes_ntsc_black);
SNES_NTSC_HIRES_OUT(2, line_out [2], SNES_NTSC_OUT_DEPTH);
SNES_NTSC_COLOR_IN(3, snes_ntsc_black);
SNES_NTSC_HIRES_OUT(3, line_out [3], SNES_NTSC_OUT_DEPTH);
SNES_NTSC_COLOR_IN(4, snes_ntsc_black);
SNES_NTSC_HIRES_OUT(4, line_out [4], SNES_NTSC_OUT_DEPTH);
SNES_NTSC_COLOR_IN(5, snes_ntsc_black);
SNES_NTSC_HIRES_OUT(5, line_out [5], SNES_NTSC_OUT_DEPTH);
SNES_NTSC_HIRES_OUT(6, line_out [6], SNES_NTSC_OUT_DEPTH);
burst_phase = (burst_phase + 1) % snes_ntsc_burst_count;
input += in_row_width;
rgb_out = (char*)rgb_out + out_pitch;
}
}
#endif