#include "phosphor_blend.hpp"
#include "../emucore/Console.hxx"
namespace ale {
PhosphorBlend::PhosphorBlend(OSystem* osystem) : m_osystem(osystem) {
m_phosphor_blend_ratio = 77;
makeAveragePalette();
}
void PhosphorBlend::process(ALEScreen& screen) {
Console& console = m_osystem->console();
uInt8* current_buffer = console.mediaSource().currentFrameBuffer();
uInt8* previous_buffer = console.mediaSource().previousFrameBuffer();
for (size_t i = 0; i < screen.arraySize(); i++) {
int cv = current_buffer[i];
int pv = previous_buffer[i];
uInt32 rgb = m_avg_palette[cv][pv];
screen.getArray()[i] = rgbToNTSC(rgb);
}
}
void PhosphorBlend::makeAveragePalette() {
ColourPalette& palette = m_osystem->colourPalette();
for (int c1 = 0; c1 < 256; c1 += 2) {
for (int c2 = 0; c2 < 256; c2 += 2) {
int r1, g1, b1;
int r2, g2, b2;
palette.getRGB(c1, r1, g1, b1);
palette.getRGB(c2, r2, g2, b2);
uInt8 r = getPhosphor(r1, r2);
uInt8 g = getPhosphor(g1, g2);
uInt8 b = getPhosphor(b1, b2);
m_avg_palette[c1][c2] = makeRGB(r, g, b);
}
}
for (int r = 0; r < 256; r += 4) {
for (int g = 0; g < 256; g += 4) {
for (int b = 0; b < 256; b += 4) {
int minDist = 256 * 3 + 1;
int minIndex = -1;
for (int c1 = 0; c1 < 256; c1 += 2) {
int r1, g1, b1;
palette.getRGB(c1, r1, g1, b1);
int dist = abs(r1 - r) + abs(g1 - g) + abs(b1 - b);
if (dist < minDist) {
minDist = dist;
minIndex = c1;
}
}
m_rgb_ntsc[r >> 2][g >> 2][b >> 2] = minIndex;
}
}
}
}
uInt8 PhosphorBlend::getPhosphor(uInt8 v1, uInt8 v2) {
if (v1 < v2) {
int tmp = v1;
v1 = v2;
v2 = tmp;
}
uInt32 blendedValue = ((v1 - v2) * m_phosphor_blend_ratio) / 100 + v2;
if (blendedValue > 255)
return 255;
else
return (uInt8)blendedValue;
}
uInt32 PhosphorBlend::makeRGB(uInt8 r, uInt8 g, uInt8 b) {
return (r << 16) | (g << 8) | b;
}
uInt8 PhosphorBlend::rgbToNTSC(uInt32 rgb) {
int r = (rgb >> 16) & 0xFF;
int g = (rgb >> 8) & 0xFF;
int b = rgb & 0xFF;
return m_rgb_ntsc[r >> 2][g >> 2][b >> 2];
}
}