bsnes/higan/video/video.cpp
Tim Allen 8af3e4a6e2 Update to v102r22 release.
byuu says:

Changelog:

  - higan: Emulator::Interface::videoSize() renamed to videoResolution()
  - higan: Emulator::Interface::rtcsync() renamed to rtcSynchronize()
  - higan: added video display rotation support to Video
  - GBA: substantially improved audio mixing
      - fixed bug with FIFO 50%/100% volume setting
      - now properly using SOUNDBIAS amplitude to control output
        frequencies
      - reduced quantization noise
      - corrected relative volumes between PSG and FIFO channels
      - both PSG and FIFO values cached based on amplitude; resulting in
        cleaner PCM samples
      - treating PSG volume=3 as 200% volume instead of 0% volume now
        (unverified: to match mGBA)
  - GBA: properly initialize ALL CPU state; including the vital
    prefetch.wait=1 (fixes Classic NES series games)
  - GBA: added video rotation with automatic key translation support
  - PCE: reduced output resolution scalar from 285x242 to 285x240
      - the extra two scanlines won't be visible on most TVs; and they
        make all other cores look worse
      - this is because all other cores output at 240p or less; so they
        were all receiving black bars in windowed mode
  - tomoko: added "Rotate Display" hotkey setting
  - tomoko: changed hotkey multi-key logic to OR instead of AND
      - left support for flipping it back inside the core; for those so
        inclined; by uncommenting one line in input.hpp
  - tomoko: when choosing Settings→Configuration, it will
    automatically select the currently loaded system
      - for instance, if you're playing a Game Gear game, it'll take you
        to the Game Gear input settings
      - if no games are loaded, it will take you to the hotkeys panel
        instead
  - WS(C): merged "Hardware-Vertical", "Hardware-Horizontal" controls
    into combined "Hardware"
  - WS(C): converted rotation support from being inside the core to
    using Emulator::Video
      - this lets WS(C) video content scale larger now that it's not
        bounded by a 224x224 square box
  - WS(C): added automatic key rotation support
  - WS(C): removed emulator "Rotate" key (use the general hotkey
    instead; I recommend F8 for this)
  - nall: added serializer support for nall::Boolean (boolean) types
      - although I will probably prefer the usage of uint1 in most cases
2017-06-09 00:08:02 +10:00

187 lines
4.7 KiB
C++

#include <emulator/emulator.hpp>
namespace Emulator {
#include "sprite.cpp"
Video video;
Video::~Video() {
reset();
}
auto Video::reset() -> void {
interface = nullptr;
sprites.reset();
delete buffer;
buffer = nullptr;
delete rotate;
rotate = nullptr;
delete palette;
palette = nullptr;
width = 0;
height = 0;
effects.colorBleed = false;
effects.interframeBlending = false;
effects.rotateLeft = false;
}
auto Video::setInterface(Interface* interface) -> void {
this->interface = interface;
}
auto Video::setPalette() -> void {
if(!interface) return;
delete palette;
colors = interface->videoColors();
palette = new uint32[colors];
for(auto index : range(colors)) {
uint64 color = interface->videoColor(index);
uint16 b = color.bits( 0,15);
uint16 g = color.bits(16,31);
uint16 r = color.bits(32,47);
uint16 a = 0xffff;
if(saturation != 1.0) {
uint16 grayscale = uclamp<16>((r + g + b) / 3);
double inverse = max(0.0, 1.0 - saturation);
r = uclamp<16>(r * saturation + grayscale * inverse);
g = uclamp<16>(g * saturation + grayscale * inverse);
b = uclamp<16>(b * saturation + grayscale * inverse);
}
if(gamma != 1.0) {
double reciprocal = 1.0 / 32767.0;
r = r > 32767 ? r : uint16(32767 * pow(r * reciprocal, gamma));
g = g > 32767 ? g : uint16(32767 * pow(g * reciprocal, gamma));
b = b > 32767 ? b : uint16(32767 * pow(b * reciprocal, gamma));
}
if(luminance != 1.0) {
r = uclamp<16>(r * luminance);
g = uclamp<16>(g * luminance);
b = uclamp<16>(b * luminance);
}
//convert color from 16-bits/channel to 8-bits/channel; force alpha to 1.0
palette[index] = a.byte(1) << 24 | r.byte(1) << 16 | g.byte(1) << 8 | b.byte(1) << 0;
}
}
auto Video::setSaturation(double saturation) -> void {
this->saturation = saturation;
}
auto Video::setGamma(double gamma) -> void {
this->gamma = gamma;
}
auto Video::setLuminance(double luminance) -> void {
this->luminance = luminance;
}
auto Video::setEffect(Effect effect, const any& value) -> void {
if(effect == Effect::ColorBleed && value.is<bool>()) {
effects.colorBleed = value.get<bool>();
}
if(effect == Effect::InterframeBlending && value.is<bool>()) {
effects.interframeBlending = value.get<bool>();
}
if(effect == Effect::RotateLeft && value.is<bool>()) {
effects.rotateLeft = value.get<bool>();
}
}
auto Video::createSprite(uint width, uint height) -> shared_pointer<Sprite> {
shared_pointer<Sprite> sprite = new Sprite{width, height};
sprites.append(sprite);
return sprite;
}
auto Video::removeSprite(shared_pointer<Sprite> sprite) -> bool {
for(uint n : range(sprites)) {
if(sprite == sprites[n]) {
sprites.remove(n);
return true;
}
}
return false;
}
auto Video::refresh(uint32* input, uint pitch, uint width, uint height) -> void {
if(this->width != width || this->height != height) {
delete buffer;
delete rotate;
buffer = new uint32[width * height]();
rotate = new uint32[height * width]();
this->width = width;
this->height = height;
}
auto output = buffer;
pitch >>= 2; //bytes to words
for(uint y : range(height)) {
auto source = input + y * pitch;
auto target = output + y * width;
if(!effects.interframeBlending) {
for(uint x : range(width)) {
auto color = palette[*source++];
*target++ = color;
}
} else {
for(uint x : range(width)) {
auto a = *target;
auto b = palette[*source++];
*target++ = (a + b - ((a ^ b) & 0x01010101)) >> 1;
}
}
}
if(effects.colorBleed) {
for(uint y : range(height)) {
auto target = output + y * width;
for(uint x : range(width)) {
auto a = target[x];
auto b = target[x + (x != width - 1)];
target[x] = (a + b - ((a ^ b) & 0x01010101)) >> 1;
}
}
}
if(effects.rotateLeft) {
for(uint y : range(height)) {
auto source = buffer + y * width;
for(uint x : range(width)) {
auto target = rotate + (width - 1 - x) * height + y;
*target = *source++;
}
}
output = rotate;
swap(width, height);
}
for(auto& sprite : sprites) {
if(!sprite->visible) continue;
for(int y : range(sprite->height)) {
for(int x : range(sprite->width)) {
int pixelY = sprite->y + y;
if(pixelY < 0 || pixelY >= height) continue;
int pixelX = sprite->x + x;
if(pixelX < 0 || pixelX >= width) continue;
auto pixel = sprite->pixels[y * sprite->width + x];
if(pixel) output[pixelY * width + pixelX] = 0xff000000 | pixel;
}
}
}
platform->videoRefresh(output, width * sizeof(uint32), width, height);
}
}