atari_py/ale_interface/src/environment/phosphor_blend.cpp (70 lines of code) (raw):
/* *****************************************************************************
* A.L.E (Arcade Learning Environment)
* Copyright (c) 2009-2013 by Yavar Naddaf, Joel Veness, Marc G. Bellemare and
* the Reinforcement Learning and Artificial Intelligence Laboratory
* Released under the GNU General Public License; see License.txt for details.
*
* Based on: Stella -- "An Atari 2600 VCS Emulator"
* Copyright (c) 1995-2007 by Bradford W. Mott and the Stella team
*
* *****************************************************************************
* phosphor_blend.cpp
*
* Methods for performing colour averaging over the screen.
*
**************************************************************************** */
#include "phosphor_blend.hpp"
#include "../emucore/Console.hxx"
PhosphorBlend::PhosphorBlend(OSystem * osystem):
m_osystem(osystem) {
// Taken from default Stella settings
m_phosphor_blend_ratio = 77;
makeAveragePalette();
}
void PhosphorBlend::process(ALEScreen& screen) {
Console& console = m_osystem->console();
// Fetch current and previous frame buffers from the emulator
uInt8 * current_buffer = console.mediaSource().currentFrameBuffer();
uInt8 * previous_buffer = console.mediaSource().previousFrameBuffer();
// Process each pixel in turn
for (size_t i = 0; i < screen.arraySize(); i++) {
int cv = current_buffer[i];
int pv = previous_buffer[i];
// Find out the corresponding rgb color
uInt32 rgb = m_avg_palette[cv][pv];
// Set the corresponding pixel in the array
screen.getArray()[i] = rgbToNTSC(rgb);
}
}
void PhosphorBlend::makeAveragePalette() {
ColourPalette &palette = m_osystem->colourPalette();
// Precompute the average RGB values for phosphor-averaged colors c1 and c2.
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);
}
}
// Also make a RGB to NTSC color map. We drop the lowest two bits to speed
// the initialization a little. TODO(mgbellemare): Find a better solution.
for (int r = 0; r < 256; r += 4) {
for (int g = 0; g < 256; g += 4) {
for (int b = 0; b < 256; b += 4) {
// For each RGB point, we find its closest NTSC match
int minDist = 256 * 3 + 1;
int minIndex = -1;
// Look for the closest NTSC value matching (r,g,b). Odd palette
// entries correspond to grayscale values and are ignored.
for (int c1 = 0; c1 < 256; c1 += 2) {
// Get the RGB corresponding to c1
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;
}
/** Converts a RGB value to an 8-bit format */
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];
}