beta 10.4

This commit is contained in:
DerKoun 2021-02-20 00:03:31 +01:00
parent 036c2ed3f3
commit f1a45ff0a1
162 changed files with 246450 additions and 257 deletions

View file

@ -1,12 +1,12 @@
name: CI
on:
push:
pull_request:
on: [push, pull_request]
jobs:
Windows:
runs-on: windows-latest
env:
POWERSHELL_TELEMETRY_OPTOUT: 1
steps:
- uses: actions/checkout@v2
with:
@ -15,16 +15,38 @@ jobs:
run: mingw32-make -j $env:NUMBER_OF_PROCESSORS -C bsnes
- name: Prepare artifacts
run: |
mkdir bsnes/out/pack
unzip packraw.zip -d bsnes/out/pack
cp README.md bsnes/out/pack
cp bsnes/out/bsnes.exe bsnes/out/pack
mv bsnes/out/pack/bsnes.exe bsnes/out/pack/bsnes_hd.exe
cp -R pack bsnes\out\
cp README.md bsnes\out\pack\readme.md
cp bsnes\out\bsnes.exe bsnes\out\pack\bsnes_hd.exe
- name: Upload artifacts
uses: actions/upload-artifact@v1.0.0
uses: actions/upload-artifact@v2
with:
name: bsnes_hd_build-${{ runner.os }}
name: bsnes_hd_beta_X_windows
path: bsnes\out\pack
if-no-files-found: error
Windows-libretro:
runs-on: windows-latest
env:
POWERSHELL_TELEMETRY_OPTOUT: 1
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Build
run: mingw32-make -j $env:NUMBER_OF_PROCESSORS -C bsnes target=libretro
- name: Prepare artifacts
run: |
mkdir bsnes/out/pack
cp README.md bsnes/out/pack
cp LICENSE bsnes/out/pack
cp -R bsnes/out/*bsnes* bsnes/out/pack
- name: Upload artifacts
uses: actions/upload-artifact@v2
with:
name: bsnes_hd_beta_X_windows_libretro
path: bsnes\out\pack
if-no-files-found: error
Linux:
runs-on: ubuntu-latest
@ -35,51 +57,37 @@ jobs:
- name: Setup
run: |
sudo apt-get update
sudo apt-get -y install build-essential libgtk2.0-dev libpulse-dev mesa-common-dev libgtksourceview2.0-dev libcairo2-dev libsdl2-dev libxv-dev libao-dev libopenal-dev libudev-dev zip
sudo apt-get -y install build-essential libgtk2.0-dev libpulse-dev mesa-common-dev libgtksourceview2.0-dev libcairo2-dev libsdl2-dev libxv-dev libao-dev libopenal-dev libudev-dev
- name: Build
run: make -j $(nproc) -C bsnes
- name: Prepare artifacts
run: |
mkdir bsnes/out/pack
unzip packraw.zip -d bsnes/out/pack
cp README.md bsnes/out/pack
cp -R bsnes/out/*bsnes* bsnes/out/pack
mv bsnes/out/pack/bsnes bsnes/out/pack/bsnes_hd
cp -R pack bsnes/out/
cp README.md bsnes/out/pack/readme.md
cp bsnes/out/bsnes bsnes/out/pack/bsnes_hd
chmod +x bsnes/out/pack/bsnes_hd
mkdir bsnes/out/tar
cd bsnes/out/pack
tar -jcvpf ../tar/bsnes_hd_beta_X_linux.tar.bz2 *
- name: Upload artifacts
uses: actions/upload-artifact@v1.0.0
uses: actions/upload-artifact@v2
with:
name: bsnes_hd_build-${{ runner.os }}
path: bsnes/out/pack
name: UNPACK-LINUX
path: bsnes/out/tar
if-no-files-found: error
macOS:
runs-on: macos
Linux-libretro:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Build
run: make -j $(sysctl -n hw.ncpu) -C bsnes
- name: Prepare artifacts
- name: Setup
run: |
mkdir bsnes/out/pack
unzip packraw.zip -d bsnes/out/pack
cp README.md bsnes/out/pack
cp -R bsnes/out/*bsnes* bsnes/out/pack
mv bsnes/out/pack/bsnes.app bsnes/out/pack/bsnes_hd.app
- name: Upload artifacts
uses: actions/upload-artifact@v1.0.0
with:
name: bsnes_hd_build-${{ runner.os }}
path: bsnes/out/pack
macOS-libretro:
runs-on: macos
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0
sudo apt-get update
sudo apt-get -y install build-essential libgtk2.0-dev libpulse-dev mesa-common-dev libgtksourceview2.0-dev libcairo2-dev libsdl2-dev libxv-dev libao-dev libopenal-dev libudev-dev
- name: Build
run: make -j $(sysctl -n hw.ncpu) -C bsnes target=libretro
run: make -j $(nproc) -C bsnes target=libretro
- name: Prepare artifacts
run: |
mkdir bsnes/out/pack
@ -87,7 +95,82 @@ jobs:
cp LICENSE bsnes/out/pack
cp -R bsnes/out/*bsnes* bsnes/out/pack
- name: Upload artifacts
uses: actions/upload-artifact@v1.0.0
uses: actions/upload-artifact@v2
with:
name: bsnes_hd_libretro_build-${{ runner.os }}
name: bsnes_hd_beta_X_linux_libretro
path: bsnes/out/pack
if-no-files-found: error
macOS:
runs-on: macos-latest
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Build
run: |
export MACOSX_DEPLOYMENT_TARGET=10.14
make -j $(sysctl -n hw.ncpu) -C bsnes
- name: Prepare artifacts
run: |
mkdir bsnes/out/pack
cp LICENSE bsnes/out/pack
cp README.md bsnes/out/pack
cp -R bsnes/out/*bsnes* bsnes/out/pack
mv bsnes/out/pack/bsnes.app bsnes/out/pack/bsnes_hd.app
chmod +x bsnes/out/pack/bsnes_hd.app/Contents/MacOS/bsnes
mkdir bsnes/out/tar
cd bsnes/out/pack
tar -jcvpf ../tar/bsnes_hd_beta_X_mac.tar.bz2 *
- name: Upload artifacts
uses: actions/upload-artifact@v2
with:
name: UNPACK-MAC
path: bsnes/out/tar
if-no-files-found: error
macOS-libretro:
runs-on: macos-latest
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Build
run: |
export MACOSX_DEPLOYMENT_TARGET=10.14
make -j $(sysctl -n hw.ncpu) -C bsnes target=libretro
- name: Prepare artifacts
run: |
mkdir bsnes/out/pack
cp README.md bsnes/out/pack
cp LICENSE bsnes/out/pack
cp -R bsnes/out/*bsnes* bsnes/out/pack
- name: Upload artifacts
uses: actions/upload-artifact@v2
with:
name: bsnes_hd_beta_X_mac_libretro
path: bsnes/out/pack
if-no-files-found: error
Android-libretro:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0
- uses: nttld/setup-ndk@v1
with:
ndk-version: r22
- name: Build
run: |
cd bsnes/target-libretro/jni
ndk-build
cd ../../..
cp README.md bsnes/target-libretro/libs
cp LICENSE bsnes/target-libretro/libs
- name: Upload artifacts
uses: actions/upload-artifact@v2
with:
name: bsnes_hd_beta_X_android_libretro
path: bsnes/target-libretro/libs
if-no-files-found: error

View file

@ -1,10 +1,9 @@
# bsnes-hd *beta 10.3*
# bsnes-hd *beta 10.4*
- [downloads](https://github.com/DerKoun/bsnes-hd/releases) for the latest betas
- [downloads](https://github.com/DerKoun/bsnes-hd/releases) for the latest betas (there are only beta) / also on the libretro auto-updater
- [GitHub project](https://github.com/DerKoun/bsnes-hd) for source code, issues, feature requests, ...
- [Reddit](https://www.reddit.com/r/emulation/search/?q=bsnes-hd&restrict_sr=1&sort=new) for announcements and discussions on *r/emulation*
- [Discord](https://discord.gg/7ahAzCV) if you prefer chatting (Thanks to everyone who set it up and keeps it running)
- [Forum](https://www.retrowide.com/forums) for widescreen discussions, both ROM-hacking and technical
1. [What is it?](#what-is-it)
2. [Help wanted](#help-wanted)
@ -54,10 +53,9 @@ Feel free to suggest features. Please remember that this fork focuses on HD and
It would be helpful to know how the framerates of different scale levels is on various devices, especially handhelds and consoles, Android and others.
### OpenGL/Vulkan
### Vulkan/OpenGL
If someone could set me up a very basic libretro core, that can just display 3 textures, than can have their image data and 3D position changed each frame, I could try out some experiments to improve both performance and quality for most games.
If anyone with OpenGL/Vulkan programming and/or shader knowledge would assit me, we could work on a fork that improves both quality and performance using the GPU. This is a long-term goal that I won't start on without help.
### Logo
@ -202,18 +200,34 @@ The file must contain alternating letters and numbers, each pair overriding a se
| widescreen mode | w | 0:off 1:on(always) 2:on(mode7) |
| widescreen sprites | s | 0:safe 1:unsafe(widescreen) 2:clip |
| widescreen aspect ratio | W | 0-200:widescreen-extension 201+:AR(*see below*) |
| widescreen background 1/2/3/4 | b/B/c/C | 0:off 1:on 2:auto(horizontal and vertical) |
| widescreen background 1/2/3/4 | b/B/c/C | 0+:WS 10+:crop 20:disab 1000+:line(*see below*) |
| widescreen marker | m | 0:off 1+:line 11+:darken (*see below*) |
| mode 7 perspective correction | P | 0:off 1-3:auto 4-6+:on (*see below*) |
| pixel aspect ratio correction | p | 0:off 1:on |
| overscan | o | 0:off(216 lines(5th HD)) 1:on(224 lines) |
| ignore window | i | 0:none 1:outside 2:outside&always 3:all |
| ignore window fallback x-coordinate | I | 0-255:x-coordinate |
| overclock CPU | O | 100+:percentage(100 is normal) |
#### Widescreen values
#### Widescreen Aspect Ratio values
Values of 200 and less specify the widescreen extension on each side in pixel columns. It is recommended to use values dividable by as large a power of 2 as possible, at least by 4.
Values larger than 200 specify the aspect ratio as (horizontal*100+vertical), e.g. 16:10, 16:9, 2:1 and 21:9 as 1610, 1609, 201 and 2109, respectively. From this AR the widescreen extension is computed in the same way as for ARs specified in the settings dialog, except that arbitrary ARs can be specified here.
#### Widescreen Background
0:off, 1:on, 2:auto(horizanotal+vertical), 2:auto(horizanotal), 10:crop, 11:crop(auto), 20:disable.
To enable widescreen for the background only above or below a certain line the value must be the line number plus 1000 or 2000 respectively.
#### Widescreen Marker
Values from 1-10 and 11-20 enable lines and darkening respectively. The values in the respective ranges vary the opacity.
#### Perspective Correction
1-3 and 4-6 trigger auto an on respectively. In both cases the 3 settings are in the order: wide, medium, narrow.
#### Sample
To force enable widescreen, including for sprites and setting a widescreen extension of 64 the file can simply be:
@ -226,9 +240,11 @@ w : 1
s : 1
W : 64
```
Only the last letter before a number is taken into account, basically allowing comments:
The percent character ("%") disabled and enables interpretation, allowing comments:
```
widescreen always on: w: 1
widescreen sprites on: s: 1
widescreen extension : W:64
% HyperZone %
B : 2100 % bg 2 includes dashboard (non-ws) and far away bg (ws):
enable widescreen for line 100 and below %
s : 1 % unlimited sprites (not perfect, but worth trying) %
m : 0 % no ws markers %
```

View file

@ -1,5 +1,5 @@
target ?= bsnes
binary ?= application
target := bsnes
binary := application
build := performance
openmp := true
local := false

View file

@ -31,7 +31,7 @@ using namespace nall;
namespace Emulator {
static const string Name = "bsnes-hd beta";
static const string Version = "10.3";//bsnes/target-bsnes/presentation/presentation.cpp:create:about:setVersion
static const string Version = "10.4";//bsnes/target-bsnes/presentation/presentation.cpp:create:about:setVersion
static const string Author = "DerKoun(byuu)";
static const string License = "GPLv3";
static const string Website = "https://github.com/DerKoun/bsnes-hd";

View file

@ -1,23 +1,23 @@
namespace HdToolkit {
static constexpr auto determineWsExt(int ws, bool overscan, bool aspectCorrection) -> int {
if (ws < 200) {
return ws;
double val = ws;
if (ws > 200) {
int w = ws / 100;
int h = ws % 100;
val = overscan ? 224.0 : 216.0;
val *= w;
val /= h;
if (aspectCorrection) {
val *= 7;
val /= 8;
}
if (val <= 256) {
return 0;
}
val -= 256;
val /= 2;
}
int w = ws / 100;
int h = ws % 100;
double val = overscan ? 224.0 : 216.0;
val *= w;
val /= h;
if (aspectCorrection) {
val *= 7;
val /= 8;
}
if (val <= 256) {
return 0;
}
val -= 256;
val /= 2;
val /= 4;
val /= 8;
if (overscan || aspectCorrection) {
val += 0.5;
}
@ -25,7 +25,7 @@ namespace HdToolkit {
if (ws <= 0) {
return 0;
}
ws *= 4;
ws *= 8;
return ws;
}
}

View file

@ -42,6 +42,7 @@ auto Configuration::process(Markup::Node document, bool load) -> void {
bind(natural, "Hacks/PPU/Mode7/Wsobj", hacks.ppu.mode7.wsobj);
bind(natural, "Hacks/PPU/Mode7/Igwin", hacks.ppu.mode7.igwin);
bind(natural, "Hacks/PPU/Mode7/Igwinx", hacks.ppu.mode7.igwinx);
bind(boolean, "Hacks/PPU/Mode7/Strwin", hacks.ppu.mode7.strwin);
bind(natural, "Hacks/PPU/Mode7/BgGrad", hacks.ppu.mode7.bgGrad);
bind(natural, "Hacks/PPU/Mode7/WindRad", hacks.ppu.mode7.windRad);
bind(natural, "Hacks/PPU/Mode7/WsMode", hacks.ppu.mode7.wsMode);

View file

@ -57,6 +57,7 @@ struct Configuration {
uint wsobj = 0;
uint igwin = 1;
uint igwinx = 128;
bool strwin = false;
uint bgGrad = 4;
uint windRad = 0;
uint wsMode = 1;

View file

@ -21,15 +21,20 @@ auto PPU::Line::renderBackground(PPU::IO::Background& self, uint8 source) -> voi
if (self.tileSize == 0 && self.hoffset == 0) ws = 0;
} else if(wsConf == 16) {
if (self.tileSize == 0 && self.hoffset == 0 && self.voffset == 0) ws = 0;
} else if(wsConf == 0 || (wsConf != 1 && (((wsConf % 2) != 0) == (y < (((int)(wsConf / 2)) * 40))))) {
} else if(wsConf == 0
|| (wsConf >= 2 && wsConf <= 11 && (((wsConf % 2) != 0) == (y < (((int)(wsConf / 2)) * 40))))
|| (wsConf >= 1000 && wsConf < 3000 && ((wsConf < 2000) != (y < ((int)(wsConf % 1000)))))
) {
ws = 0;
}
}
bool windowAbove[256];
bool windowBelow[256];
renderWindow(self.window, self.window.aboveEnable, windowAbove);
renderWindow(self.window, self.window.belowEnable, windowBelow);
bool windowAbove[1024];
bool windowBelow[1024];
renderWindow(self.window, self.window.aboveEnable, windowAbove,
ppufast.widescreen(), ppufast.strwin());
renderWindow(self.window, self.window.belowEnable, windowBelow,
ppufast.widescreen(), ppufast.strwin());
bool hires = io.bgMode == 5 || io.bgMode == 6;
bool offsetPerTileMode = io.bgMode == 2 || io.bgMode == 4 || io.bgMode == 6;

View file

@ -110,9 +110,18 @@ auto PPU::Line::avgBgC(uint dist, uint offset) const -> uint32 {
auto luma = ppu.lightTable[io.displayBrightness];
uint32 t = luma[io.col.fixedColor];
if(dist < 1) return t;
uint32 a = (t >> 16) & 255;
uint32 b = (t >> 8) & 255;
uint32 c = (t >> 0) & 255;
int32 aBase = (t >> 16) & 255;
int32 bBase = (t >> 8) & 255;
int32 cBase = (t >> 0) & 255;
uint32 a = aBase;
uint32 b = bBase;
uint32 c = cBase;
int32 aU = 0;
int32 bU = 0;
int32 cU = 0;
int32 aD = 0;
int32 bD = 0;
int32 cD = 0;
int scale = ppufast.hd() ? ppufast.hdScale() : 1;
int hdY = y * scale + offset;
int count = 1;
@ -137,13 +146,18 @@ auto PPU::Line::avgBgC(uint dist, uint offset) const -> uint32 {
io.bg3.tileMode != dL.io.bg3.tileMode || io.bg3.tileMode != uL.io.bg3.tileMode ||
io.bg4.tileMode != dL.io.bg4.tileMode || io.bg4.tileMode != uL.io.bg4.tileMode) break;
t = luma[uL.io.col.fixedColor];
a += (t >> 16) & 255;
b += (t >> 8) & 255;
c += (t >> 0) & 255;
aU = (t >> 16) & 255;
bU = (t >> 8) & 255;
cU = (t >> 0) & 255;
t = luma[dL.io.col.fixedColor];
a += (t >> 16) & 255;
b += (t >> 8) & 255;
c += (t >> 0) & 255;
aD = (t >> 16) & 255;
bD = (t >> 8) & 255;
cD = (t >> 0) & 255;
if (((abs(aU - aBase) + abs(bU - bBase) + abs(cU - cBase)) > 76) ||
((abs(aD - aBase) + abs(bD - bBase) + abs(cD - cBase)) > 76)) break;
a += aU + aD;
b += bU + bD;
c += cU + cD;
count += 2;
}
a /= count;
@ -219,10 +233,10 @@ auto PPU::Line::render(bool fieldID) -> void {
y = this->y;
uint windRad = ppufast.windRad();
for (int offset = 0; offset < scale; offset++) {
uint oneLeft = io.window.oneLeft;
uint oneRight = io.window.oneRight;
uint twoLeft = io.window.twoLeft;
uint twoRight = io.window.twoRight;
int oneLeft = io.window.oneLeft;
int oneRight = io.window.oneRight;
int twoLeft = io.window.twoLeft;
int twoRight = io.window.twoRight;
int hdY = y * scale + offset;
int count = 1;
@ -279,15 +293,56 @@ auto PPU::Line::render(bool fieldID) -> void {
count += 2;
}
oneLeft = oneLeft * scale / count;
oneRight = oneRight * scale / count + scale - 1;
twoLeft = twoLeft * scale / count;
twoRight = twoRight * scale / count + scale - 1;
if (ppu.strwin()) {
oneLeft *= 2;
oneRight *= 2;
twoLeft *= 2;
twoRight *= 2;
}
int ws = ppu.widescreen() * scale;
oneLeft = oneLeft * scale / count + ws;
oneRight = oneRight * scale / count + ws + scale - 1;
twoLeft = twoLeft * scale / count + ws;
twoRight = twoRight * scale / count + ws + scale - 1;
if (ppu.strwin()) {
oneLeft -= 128 * scale;
oneRight -= 128 * scale;
twoLeft -= 128 * scale;
twoRight -= 128 * scale;
} else {
int lw = 0 - scale * 10;
int l = ws + scale;
int r = ws + 255 * scale;
int rw = ws + 255 * scale + ws + scale * 10;
if (oneLeft < l) {
oneLeft = lw;
} else if (oneLeft >= r) {
oneLeft = rw;
}
if (oneRight < l) {
oneRight = lw;
} else if (oneRight >= r) {
oneRight = rw;
}
if (twoLeft < l) {
twoLeft = lw;
} else if (twoLeft >= r) {
twoLeft = rw;
}
if (twoRight < l) {
twoRight = lw;
} else if (twoRight >= r) {
twoRight = rw;
}
}
renderWindow(io.col.window, io.col.window.aboveMask, windowAbove,
oneLeft, oneRight, twoLeft, twoRight, scale, 256*scale*offset);
oneLeft, oneRight, twoLeft, twoRight, scale,
(256 + 2 * ppu.widescreen())*scale*offset, ppu.widescreen());
renderWindow(io.col.window, io.col.window.belowMask, windowBelow,
oneLeft, oneRight, twoLeft, twoRight, scale, 256*scale*offset);
oneLeft, oneRight, twoLeft, twoRight, scale,
(256 + 2 * ppu.widescreen())*scale*offset, ppu.widescreen());
}
uint wsm = (ppu.widescreen() == 0 || ppu.wsOverride()) ? 0 : ppu.wsMarker();
@ -295,40 +350,25 @@ auto PPU::Line::render(bool fieldID) -> void {
uint curr = 0, prev = 0;
if(hd) {
int x = 0;
int xWindow = 0;
for(uint ySub : range(scale)) {
for(uint i : range(ppufast.widescreen() * scale)) {
*output++ = pixel(xWindow, above[x], below[x], wsm, wsma, bgFixedColors[ySub]);
x++;
for(uint x : range((256 + 2 * ppufast.widescreen() ) * scale)) {
*output++ = pixel(x, above[x], below[x], ppu.widescreen(), wsm, wsma, bgFixedColors[ySub]);
}
for(uint i : range(256 * scale)) {
*output++ = pixel(xWindow, above[x], below[x], wsm, wsma, bgFixedColors[ySub]);
x++;
xWindow++;
}
xWindow--;
for(uint i : range(ppufast.widescreen() * scale)) {
*output++ = pixel(xWindow, above[x], below[x], wsm, wsma, bgFixedColors[ySub]);
x++;
}
xWindow++;
}
} else if(width == 256) for(uint x : range(256)) {
*output++ = pixel(x, above[x], below[x], wsm, wsma, bgFixedColors[0]);
*output++ = pixel(x, above[x], below[x], 0, 0, 0, bgFixedColors[0]);
} else if(!hires) for(uint x : range(256)) {
auto color = pixel(x, above[x], below[x], wsm, wsma, bgFixedColors[0]);
auto color = pixel(x, above[x], below[x], 0, 0, 0, bgFixedColors[0]);
*output++ = color;
*output++ = color;
} else if(!configuration.video.blurEmulation) for(uint x : range(256)) {
*output++ = pixel(x, below[x], above[x], wsm, wsma, bgFixedColors[0]);
*output++ = pixel(x, above[x], below[x], wsm, wsma, bgFixedColors[0]);
*output++ = pixel(x, below[x], above[x], 0, 0, 0, bgFixedColors[0]);
*output++ = pixel(x, above[x], below[x], 0, 0, 0, bgFixedColors[0]);
} else for(uint x : range(256)) {
curr = pixel(x, below[x], above[x], wsm, wsm, bgFixedColors[0]);
curr = pixel(x, below[x], above[x], 0, 0, 0, bgFixedColors[0]);
*output++ = (prev + curr - ((prev ^ curr) & 0x00010101)) >> 1;
prev = curr;
curr = pixel(x, above[x], below[x], wsm, wsma, bgFixedColors[0]);
curr = pixel(x, above[x], below[x], 0, 0, 0, bgFixedColors[0]);
*output++ = (prev + curr - ((prev ^ curr) & 0x00010101)) >> 1;
prev = curr;
}
@ -338,7 +378,8 @@ auto PPU::Line::render(bool fieldID) -> void {
}
auto PPU::Line::pixel(uint x, Pixel above, Pixel below, uint wsm, uint wsma, uint32 bgFixedColor) const -> uint32 {
auto PPU::Line::pixel(uint x, Pixel above, Pixel below, uint ws, uint wsm,
uint wsma, uint32 bgFixedColor) const -> uint32 {
uint32 r = 0;
if(!windowAbove[ppufast.winXadHd(x, false)]) above.color = 0x0000;
if(!windowBelow[ppufast.winXadHd(x, true)]) r = above.color;
@ -346,9 +387,8 @@ auto PPU::Line::pixel(uint x, Pixel above, Pixel below, uint wsm, uint wsma, uin
else if(!io.col.blendMode) r = blend(above.color, bgFixedColor, io.col.halve && windowAbove[x]);
else r = blend(above.color, below.color, io.col.halve && windowAbove[x] && below.source != Source::COL);
if(wsm > 0) {
x = (x / ppufast.hdScale()) % 256;
if(wsm == 1 && (x == 1 || x == 254)
|| wsm == 2 && (x == 0 || x == 255)) {
x /= ppufast.hdScale();
if(x == ws - 1 || x == ws + 256 || wsm == 2 && (x <= ws - 1 || x >= ws + 256)) {
int b = wsm == 2 ? 0 : ((y / 4) % 2 == 0) ? 0 : 255;
r = ((((((r >> 16) & 255) * wsma) + b) / (wsma + 1)) << 16)
+ ((((((r >> 8) & 255) * wsma) + b) / (wsma + 1)) << 8)

View file

@ -27,10 +27,12 @@ auto PPU::Line::renderMode7(PPU::IO::Background& self, uint8 source) -> void {
int originX = (a * clip(hoffset - hcenter) & ~63) + (b * clip(voffset - vcenter) & ~63) + (b * y & ~63) + (hcenter << 8);
int originY = (c * clip(hoffset - hcenter) & ~63) + (d * clip(voffset - vcenter) & ~63) + (d * y & ~63) + (vcenter << 8);
bool windowAbove[256];
bool windowBelow[256];
renderWindow(self.window, self.window.aboveEnable, windowAbove);
renderWindow(self.window, self.window.belowEnable, windowBelow);
bool windowAbove[1024];
bool windowBelow[1024];
renderWindow(self.window, self.window.aboveEnable, windowAbove,
ppufast.widescreen(), ppufast.strwin());
renderWindow(self.window, self.window.belowEnable, windowBelow,
ppufast.widescreen(), ppufast.strwin());
auto luma = ppu.lightTable[io.displayBrightness];
for(int X : range(256)) {

View file

@ -7,7 +7,7 @@ auto PPU::Line::cacheMode7HD() -> void {
))
bool state = false;
uint y;
//find the moe 7 groups
//find the mode 7 groups
for(y = 0; y < Line::count; y++) {
if(state != isLineMode7(ppu.lines[Line::start + y])) {
state = !state;
@ -156,10 +156,12 @@ auto PPU::Line::renderMode7HD(PPU::IO::Background& self, uint8 source) -> void {
y_b = 255 - y_b;
}
bool windowAbove[256];
bool windowBelow[256];
renderWindow(self.window, self.window.aboveEnable, windowAbove);
renderWindow(self.window, self.window.belowEnable, windowBelow);
bool windowAbove[1024];
bool windowBelow[1024];
renderWindow(self.window, self.window.aboveEnable, windowAbove,
ppufast.widescreen(), ppufast.strwin());
renderWindow(self.window, self.window.belowEnable, windowBelow,
ppufast.widescreen(), ppufast.strwin());
auto luma = ppu.lightTable[io.displayBrightness];
int pixelYp = INT_MIN;

View file

@ -4,10 +4,12 @@ auto PPU::Line::renderObject(PPU::IO::Object& self) -> void {
uint wsobj = ppufast.wsOverride() ? 3 : ppufast.wsobj();
bool windowAbove[256];
bool windowBelow[256];
renderWindow(self.window, self.window.aboveEnable, windowAbove);
renderWindow(self.window, self.window.belowEnable, windowBelow);
bool windowAbove[1024];
bool windowBelow[1024];
renderWindow(self.window, self.window.aboveEnable, windowAbove,
ppufast.widescreen(), ppufast.strwin());
renderWindow(self.window, self.window.belowEnable, windowBelow,
ppufast.widescreen(), ppufast.strwin());
uint itemCount = 0;
uint tileCount = 0;
@ -137,13 +139,13 @@ auto PPU::Line::renderObject(PPU::IO::Object& self) -> void {
if(ppufast.hires() && ppufast.hd()) {
// Match non-hires backgrounds in hires mode (see background)
if(self.aboveEnable && !windowAbove[ppufast.winXad(x, false)]) plotHD(above, xc, source, priority[x], mctc, true, false);
if(self.aboveEnable && !windowAbove[ppufast.winXad(x, false)]) plotHD(above, xc, source, priority[x], mctc, true, true);
if(self.aboveEnable && !windowAbove[ppufast.winXad(x, false)]) plotHD(below, xc, source, priority[x], mctc, true, false);
if(self.aboveEnable && !windowAbove[ppufast.winXad(x, false)]) plotHD(below, xc, source, priority[x], mctc, true, true);
if(self.aboveEnable && !windowAbove[ppufast.winXad(xc, false)]) plotHD(above, xc, source, priority[x], mctc, true, false);
if(self.aboveEnable && !windowAbove[ppufast.winXad(xc, false)]) plotHD(above, xc, source, priority[x], mctc, true, true);
if(self.aboveEnable && !windowAbove[ppufast.winXad(xc, false)]) plotHD(below, xc, source, priority[x], mctc, true, false);
if(self.aboveEnable && !windowAbove[ppufast.winXad(xc, false)]) plotHD(below, xc, source, priority[x], mctc, true, true);
} else {
if(self.aboveEnable && !windowAbove[ppufast.winXad(x, false)]) plotAbove(xc, source, priority[x], mctc);
if(self.belowEnable && !windowBelow[ppufast.winXad(x, true)]) plotBelow(xc, source, priority[x], mctc);
if(self.aboveEnable && !windowAbove[ppufast.winXad(xc, false)]) plotAbove(xc, source, priority[x], mctc);
if(self.belowEnable && !windowBelow[ppufast.winXad(xc, true)]) plotBelow(xc, source, priority[x], mctc);
}
}
}

View file

@ -38,15 +38,16 @@ auto PPU::wsbg(uint bg) const -> uint {
return 0; }
auto PPU::wsobj() const -> uint { return configuration.hacks.ppu.mode7.wsobj; }
auto PPU::winXad(uint x, bool bel) const -> uint {
return (configuration.hacks.ppu.mode7.igwin != 0 && (configuration.hacks.ppu.mode7.igwin >= 3
return ((configuration.hacks.ppu.mode7.igwin != 0 && (configuration.hacks.ppu.mode7.igwin >= 3
|| configuration.hacks.ppu.mode7.igwin >= 2 && ((bel ? io.col.window.belowMask : io.col.window.aboveMask) == 0)
|| configuration.hacks.ppu.mode7.igwin >= 1 && ((bel ? io.col.window.belowMask : io.col.window.aboveMask) == 2)))
? configuration.hacks.ppu.mode7.igwinx : (x < 0 ? 0 : (x > 255 ? 255 : x)); }
? configuration.hacks.ppu.mode7.igwinx : x) + widescreen(); }
auto PPU::winXadHd(uint x, bool bel) const -> uint {
return (configuration.hacks.ppu.mode7.igwin != 0 && (configuration.hacks.ppu.mode7.igwin >= 3
|| configuration.hacks.ppu.mode7.igwin >= 2 && ((bel ? io.col.window.belowMask : io.col.window.aboveMask) == 0)
|| configuration.hacks.ppu.mode7.igwin >= 1 && ((bel ? io.col.window.belowMask : io.col.window.aboveMask) == 2)))
? configuration.hacks.ppu.mode7.igwinx * PPU::hdScale() : x; }
auto PPU::strwin() const -> bool { return configuration.hacks.ppu.mode7.strwin; }
auto PPU::bgGrad() const -> uint { return !hd() ? 0 : configuration.hacks.ppu.mode7.bgGrad; }
auto PPU::windRad() const -> uint { return !hd() ? 0 : configuration.hacks.ppu.mode7.windRad; }
auto PPU::wsOverrideCandidate() const -> bool { return configuration.hacks.ppu.mode7.wsMode == 1; }

View file

@ -24,6 +24,7 @@ struct PPU : PPUcounter {
alwaysinline auto wsobj() const -> uint;
alwaysinline auto winXad(uint x, bool bel) const -> uint;
alwaysinline auto winXadHd(uint x, bool bel) const -> uint;
alwaysinline auto strwin() const -> bool;
alwaysinline auto bgGrad() const -> uint;
alwaysinline auto windRad() const -> uint;
alwaysinline auto wsOverrideCandidate() const -> bool;
@ -309,7 +310,8 @@ public:
auto cache() -> void;
auto render(bool field) -> void;
alwaysinline auto pixel(uint x, Pixel above, Pixel below, uint wsm, uint wsma, uint32 bgFixedColor) const -> uint32;
alwaysinline auto pixel(uint x, Pixel above, Pixel below, uint ws, uint wsm,
uint wsma, uint32 bgFixedColor) const -> uint32;
alwaysinline auto blend(uint x, uint y, bool halve) const -> uint32;
alwaysinline auto directColor(uint paletteIndex, uint paletteColor) const -> uint32;
alwaysinline auto plotAbove(int x, uint8 source, uint8 priority, uint32 color) -> void;
@ -330,23 +332,15 @@ public:
auto renderMode7HD(PPU::IO::Background&, uint8 source) -> void;
alwaysinline auto lerp(float pa, float va, float pb, float vb, float pr) -> float;
//mode7hd-avx2.cpp
auto renderMode7HD_AVX2(
PPU::IO::Background&, uint8 source,
Pixel* above, Pixel* below,
bool* windowAbove, bool* windowBelow,
float originX, float a,
float originY, float c
) -> void;
//object.cpp
auto renderObject(PPU::IO::Object&) -> void;
//window.cpp
alwaysinline auto renderWindow(PPU::IO::WindowLayer&, bool enable, bool output[256]) -> void;
alwaysinline auto renderWindow(PPU::IO::WindowLayer&, bool enable,
bool output[1024], uint ws, bool stretch) -> void;
alwaysinline auto renderWindow(PPU::IO::WindowColor&, uint mask, bool *output,
uint oneLeft, uint oneRight, uint twoLeft, uint twoRight,
uint scale, uint offset) -> void;
int oneLeft, int oneRight, int twoLeft, int twoRight,
uint scale, uint offset, uint ws) -> void;
//unserialized:
uint y; //constant
@ -361,8 +355,8 @@ public:
Pixel *above = new Pixel[61440];
Pixel *below = new Pixel[61440];
bool *windowAbove = new bool[25600] ;
bool *windowBelow = new bool[25600] ;
bool *windowAbove = new bool[61440] ;
bool *windowBelow = new bool[61440] ;
//flush()
static uint start;

View file

@ -1,28 +1,74 @@
auto PPU::Line::renderWindow(PPU::IO::WindowLayer& self, bool enable, bool output[256]) -> void {
auto PPU::Line::renderWindow(PPU::IO::WindowLayer& self, bool enable,
bool output[1024], uint ws, bool stretch) -> void {
uint width = 256 + 2 * ws;
if(!enable || (!self.oneEnable && !self.twoEnable)) {
memory::fill<bool>(output, 256, 0);
memory::fill<bool>(output, width, 0);
return;
}
int oneLeft = io.window.oneLeft;
int oneRight = io.window.oneRight;
if (stretch) {
oneLeft = (oneLeft << 1) - 128 + ws;
oneRight = (oneRight << 1) - 128 + ws;
} else {
if (oneLeft == 0) {
oneLeft = -1;
} else if (oneLeft == 255) {
oneLeft = width;
} else {
oneLeft += ws;
}
if (oneRight == 0) {
oneRight = -1;
} else if (oneRight == 255) {
oneRight = width;
} else {
oneRight += ws;
}
}
if(self.oneEnable && !self.twoEnable) {
bool set = 1 ^ self.oneInvert, clear = !set;
for(uint x : range(256)) {
output[x] = x >= io.window.oneLeft && x <= io.window.oneRight ? set : clear;
for(int x : range(width)) {
output[x] = x >= oneLeft && x <= oneRight ? set : clear;
}
return;
}
int twoLeft = io.window.twoLeft;
int twoRight = io.window.twoRight;
if (stretch) {
twoLeft = (twoLeft << 1) - 128 + ws;
twoRight = (twoRight << 1) - 128 + ws;
} else { // extend edges
if (twoLeft == 0) {
twoLeft = -1;
} else if (twoLeft == 255) {
twoLeft = width;
} else {
twoLeft += ws;
}
if (twoRight == 0) {
twoRight = -1;
} else if (twoRight == 255) {
twoRight = width;
} else {
twoRight += ws;
}
}
if(self.twoEnable && !self.oneEnable) {
bool set = 1 ^ self.twoInvert, clear = !set;
for(uint x : range(256)) {
output[x] = x >= io.window.twoLeft && x <= io.window.twoRight ? set : clear;
for(int x : range(width)) {
output[x] = x >= twoLeft && x <= twoRight ? set : clear;
}
return;
}
for(uint x : range(256)) {
bool oneMask = (x >= io.window.oneLeft && x <= io.window.oneRight) ^ self.oneInvert;
bool twoMask = (x >= io.window.twoLeft && x <= io.window.twoRight) ^ self.twoInvert;
for(int x : range(width)) {
bool oneMask = (x >= oneLeft && x <= oneRight) ^ self.oneInvert;
bool twoMask = (x >= twoLeft && x <= twoRight) ^ self.twoInvert;
switch(self.mask) {
case 0: output[x] = (oneMask | twoMask) == 1; break;
case 1: output[x] = (oneMask & twoMask) == 1; break;
@ -33,24 +79,25 @@ auto PPU::Line::renderWindow(PPU::IO::WindowLayer& self, bool enable, bool outpu
}
auto PPU::Line::renderWindow(PPU::IO::WindowColor& self, uint mask, bool *output,
uint oneLeft, uint oneRight, uint twoLeft, uint twoRight,
uint scale, uint offset) -> void {
int oneLeft, int oneRight, int twoLeft, int twoRight,
uint scale, uint offset, uint ws) -> void {
uint width = 256 + 2 * ws;
bool set, clear;
switch(mask) {
case 0: memory::fill<bool>(output, 256 * scale * scale, 1); return; //always
case 0: memory::fill<bool>(output, width * scale * scale, 1); return; //always
case 1: set = 1, clear = 0; break; //inside
case 2: set = 0, clear = 1; break; //outside
case 3: memory::fill<bool>(output, 256 * scale * scale, 0); return; //never
case 3: memory::fill<bool>(output, width * scale * scale, 0); return; //never
}
if(!self.oneEnable && !self.twoEnable) {
memory::fill<bool>(output, 256 * scale * scale, clear);
memory::fill<bool>(output, width * scale * scale, clear);
return;
}
if(self.oneEnable && !self.twoEnable) {
if(self.oneInvert) set ^= 1, clear ^= 1;
for(uint x : range(256 * scale)) {
for(int x : range(width * scale)) {
output[x+offset] = x >= oneLeft && x <= oneRight ? set : clear;
}
return;
@ -58,13 +105,13 @@ auto PPU::Line::renderWindow(PPU::IO::WindowColor& self, uint mask, bool *output
if(self.twoEnable && !self.oneEnable) {
if(self.twoInvert) set ^= 1, clear ^= 1;
for(uint x : range(256 * scale)) {
for(int x : range(width * scale)) {
output[x+offset] = x >= twoLeft && x <= twoRight ? set : clear;
}
return;
}
for(uint x : range(256 * scale)) {
for(int x : range(width * scale)) {
bool oneMask = (x >= oneLeft && x <= oneRight) ^ self.oneInvert;
bool twoMask = (x >= twoLeft && x <= twoRight) ^ self.twoInvert;
switch(self.mask) {

View file

@ -165,7 +165,6 @@ auto Program::loadSuperFamicom(string location) -> bool {
}
// load and apply simple settings override file (if found)
vector<uint8_t> rso;
if(location.endsWith("/")) {
rso = file::read({location, "gamesettings.bso"});
} else if(location.iendsWith(".zip")) {
@ -182,71 +181,7 @@ auto Program::loadSuperFamicom(string location) -> bool {
} else {
rso = file::read(path("gamesettings", location, ".bso"));
}
if(rso) {
int i = 0;
int v = 0;
int c = -1;
int n = 0;
while (i < rso.size()) {
v = rso[i];
if ((v >= 'a' && v <= 'z') || (v >= 'A' && v <= 'Z')) {
c = v;
} else if (c > -1 && v >= '0' && v <= '9') {
n = (n * 10) + (v - '0');
if (i+1 == rso.size() || rso[i+1] < '0' || rso[i+1] > '9') {
switch (c) {
case 'p': //pixelAspectCorrect 0:off 1:on
settings.video.aspectCorrection = n == 1;
emulator->configure("Video/AspectCorrection", settings.video.aspectCorrection);
break;
case 'o': //overscan 0:216 1:224 (2:240 3:240f)
settings.video.overscan = n == 1;
emulator->configure("Video/Overscan", settings.video.overscan);
break;
case 'w': //widescreenMode 0:none 1:on 2:mode7
settings.emulator.hack.ppu.mode7.wsMode = n == 1 ? 2 : (n == 2 ? 1 : 0);
emulator->configure("Hacks/PPU/Mode7/WsMode", settings.emulator.hack.ppu.mode7.wsMode);
break;
case 'W': //WSaspectRatio int [<=200:wsExt, >200:ar]
settings.emulator.hack.ppu.mode7.widescreen = n;
emulator->configure("Hacks/PPU/Mode7/Widescreen", settings.emulator.hack.ppu.mode7.widescreen);
break;
case 's': //WSsprites 0:safe 1:unsafe 2:clip
settings.emulator.hack.ppu.mode7.wsobj = n == 1 ? 1 : (n == 2 ? 3 : 0);
emulator->configure("Hacks/PPU/Mode7/Wsobj", settings.emulator.hack.ppu.mode7.wsobj);
break;
case 'i': //igwin 0:none 1:outside 2:outside&always 3:all
settings.emulator.hack.ppu.mode7.igwin = n > 3 ? 0 : n;
emulator->configure("Hacks/PPU/Mode7/Igwin", settings.emulator.hack.ppu.mode7.igwin);
break;
case 'I': //igwinx int
settings.emulator.hack.ppu.mode7.igwinx = n > 255 ? 128 : n;
emulator->configure("Hacks/PPU/Mode7/Igwinx", settings.emulator.hack.ppu.mode7.igwinx);
break;
case 'b': //bg1WS 0:off 1:on 2:auto(h+v)
settings.emulator.hack.ppu.mode7.wsbg1 = n == 1 ? 1 : (n == 2 ? 16 : 0);
emulator->configure("Hacks/PPU/Mode7/Wsbg1", settings.emulator.hack.ppu.mode7.wsbg1);
break;
case 'B': //bg2WS 0:off 1:on 2:auto(h+v)
settings.emulator.hack.ppu.mode7.wsbg2 = n == 1 ? 1 : (n == 2 ? 16 : 0);
emulator->configure("Hacks/PPU/Mode7/Wsbg2", settings.emulator.hack.ppu.mode7.wsbg2);
break;
case 'c': //bg3WS 0:off 1:on 2:auto(h+v)
settings.emulator.hack.ppu.mode7.wsbg3 = n == 1 ? 1 : (n == 2 ? 16 : 0);
emulator->configure("Hacks/PPU/Mode7/Wsbg3", settings.emulator.hack.ppu.mode7.wsbg3);
break;
case 'C': //bg4WS 0:off 1:on 2:auto(h+v)
settings.emulator.hack.ppu.mode7.wsbg4 = n == 1 ? 1 : (n == 2 ? 16 : 0);
emulator->configure("Hacks/PPU/Mode7/Wsbg4", settings.emulator.hack.ppu.mode7.wsbg4);
break;
}
c = -1;
n = 0;
}
}
i++;
}
}
return true;
}

View file

@ -71,9 +71,139 @@ auto Program::hackCompatibility() -> void {
emulator->configure("Hacks/PPU/Fast", fastPPU);
emulator->configure("Hacks/PPU/NoSpriteLimit", fastPPUNoSpriteLimit);
emulator->configure("Hacks/PPU/RenderCycle", renderCycle);
emulator->configure("Hacks/PPU/Mode7/Scale", settings.emulator.hack.ppu.mode7.scale);
emulator->configure("Hacks/PPU/Mode7/Perspective", settings.emulator.hack.ppu.mode7.perspective);
emulator->configure("Hacks/PPU/Mode7/Widescreen", settings.emulator.hack.ppu.mode7.widescreen);
emulator->configure("Hacks/PPU/Mode7/Wsbg1", settings.emulator.hack.ppu.mode7.wsbg1);
emulator->configure("Hacks/PPU/Mode7/Wsbg2", settings.emulator.hack.ppu.mode7.wsbg2);
emulator->configure("Hacks/PPU/Mode7/Wsbg3", settings.emulator.hack.ppu.mode7.wsbg3);
emulator->configure("Hacks/PPU/Mode7/Wsbg4", settings.emulator.hack.ppu.mode7.wsbg4);
emulator->configure("Hacks/PPU/Mode7/Wsobj", settings.emulator.hack.ppu.mode7.wsobj);
emulator->configure("Hacks/PPU/Mode7/Igwin", settings.emulator.hack.ppu.mode7.igwin);
emulator->configure("Hacks/PPU/Mode7/Igwinx", settings.emulator.hack.ppu.mode7.igwinx);
emulator->configure("Hacks/PPU/Mode7/Strwin", false);
emulator->configure("Hacks/PPU/Mode7/BgGrad", settings.emulator.hack.ppu.mode7.bgGrad);
emulator->configure("Hacks/PPU/Mode7/WindRad", settings.emulator.hack.ppu.mode7.windRad);
emulator->configure("Hacks/PPU/Mode7/WsMode", settings.emulator.hack.ppu.mode7.wsMode);
emulator->configure("Hacks/PPU/Mode7/WsBgCol", settings.emulator.hack.ppu.mode7.wsBgCol);
emulator->configure("Hacks/PPU/Mode7/WsMarker", settings.emulator.hack.ppu.mode7.wsMarker);
emulator->configure("Hacks/PPU/Mode7/WsMarkerAlpha", settings.emulator.hack.ppu.mode7.wsMarkerAlpha);
emulator->configure("Hacks/PPU/Mode7/Supersample", settings.emulator.hack.ppu.mode7.supersample);
emulator->configure("Hacks/PPU/Mode7/Mosaic", settings.emulator.hack.ppu.mode7.mosaic);
emulator->configure("Hacks/DSP/Fast", fastDSP);
emulator->configure("Hacks/DSP/Cubic", settings.emulator.hack.dsp.cubic);
emulator->configure("Hacks/Coprocessor/DelayedSync", coprocessorDelayedSync);
if(rso) {
int i = 0;
int v = 0;
int c = -1;
int n = 0;
bool e = true;
while (i < rso.size()) {
v = rso[i++];
if (v == '%') {
e = !e;
}
if (!e) {
continue;
}
if ((v >= 'a' && v <= 'z') || (v >= 'A' && v <= 'Z')) {
c = v;
} else if (c > -1 && v >= '0' && v <= '9') {
n = (n * 10) + (v - '0');
if (i == rso.size() || rso[i] < '0' || rso[i] > '9') {
switch (c) {
case 'p': //pixelAspectCorrect 0:off 1:on
emulator->configure("Video/AspectCorrection", n == 1);
break;
case 'o': //overscan 0:216 1:224 (2:240 3:240f)
emulator->configure("Video/Overscan", n == 1);
break;
case 'w': //widescreenMode 0:none 1:on 2:mode7
emulator->configure("Hacks/PPU/Mode7/WsMode", n == 1 ? 2 : (n == 2 ? 1 : 0));
break;
case 'W': //WSaspectRatio int [<=200:wsExt, >200:ar]
emulator->configure("Hacks/PPU/Mode7/Widescreen", n);
break;
case 's': //WSsprites 0:safe 1:unsafe 2:clip
emulator->configure("Hacks/PPU/Mode7/Wsobj", n == 1 ? 1 : (n == 2 ? 3 : 0));
break;
case 'i': //igwin 0:none 1:outside 2:outside&always 3:all
emulator->configure("Hacks/PPU/Mode7/Igwin", n > 3 ? 0 : n);
break;
case 'I': //igwinx int
emulator->configure("Hacks/PPU/Mode7/Igwinx", n > 255 ? 128 : n);
break;
case 'b': //bg1WS 0:off 1:on 2:auto(h+v)
emulator->configure("Hacks/PPU/Mode7/Wsbg1",
n >= 1000 ? n : //above/below line (processed later)
(n == 1 ? 1 : //on
(n == 2 ? 16 : //auto H+V
(n == 3 ? 15 : //auto H
(n == 10 ? 12 : //crop
(n == 11 ? 13 : //crop auto
(n == 20 ? 14 : //disable
0)))))) //off
);
break;
case 'B': //bg2WS 0:off 1:on 2:auto(h+v)
emulator->configure("Hacks/PPU/Mode7/Wsbg2",
n >= 1000 ? n : //above/below line (processed later)
(n == 1 ? 1 : //on
(n == 2 ? 16 : //auto H+V
(n == 3 ? 15 : //auto H
(n == 10 ? 12 : //crop
(n == 11 ? 13 : //crop auto
(n == 20 ? 14 : //disable
0)))))) //off
);
break;
case 'c': //bg3WS 0:off 1:on 2:auto(h+v)
emulator->configure("Hacks/PPU/Mode7/Wsbg3",
n >= 1000 ? n : //above/below line (processed later)
(n == 1 ? 1 : //on
(n == 2 ? 16 : //auto H+V
(n == 3 ? 15 : //auto H
(n == 10 ? 12 : //crop
(n == 11 ? 13 : //crop auto
(n == 20 ? 14 : //disable
0)))))) //off
);
break;
case 'C': //bg4WS 0:off 1:on 2:auto(h+v)
emulator->configure("Hacks/PPU/Mode7/Wsbg4",
n >= 1000 ? n : //above/below line (processed later)
(n == 1 ? 1 : //on
(n == 2 ? 16 : //auto H+V
(n == 3 ? 15 : //auto H
(n == 10 ? 12 : //crop
(n == 11 ? 13 : //crop auto
(n == 20 ? 14 : //disable
0)))))) //off
);
break;
case 'm': //wsMarker 0:off 1-10:lines 11-20:darken, wsMarkerAlpha 1-10/11-20:opaque-transparent
emulator->configure("Hacks/PPU/Mode7/WsMarker", n < 1 || n > 20 ? 0 : (n - 1) / 10 + 1);
emulator->configure("Hacks/PPU/Mode7/WsMarkerAlpha", (n - 1) % 10 + 1);
break;
case 'P': //Perspective 0:off 1-3:auto 4-6+:on (wide, medium, narrow)
emulator->configure("Hacks/PPU/Mode7/Perspective", n < 1 || n > 6 ? 0 : n );
break;
case 'O': //Overlock CPU percentage
emulator->configure("Hacks/CPU/Overclock", n );
break;
case 'S': //Stretch Window [EXPERIMENTAL!! DO NOT USE!!]
emulator->configure("Hacks/PPU/Mode7/Strwin", n == 2 );
break;
}
c = -1;
n = 0;
}
}
}
}
}
auto Program::hackPatchMemory(vector<uint8_t>& data) -> void {

View file

@ -27,6 +27,8 @@ struct Program : Lock, Emulator::Platform {
auto unload() -> void;
auto verified() const -> bool;
vector<uint8_t> rso;
//game-pak.cpp
auto openPakSuperFamicom(string name, vfs::file::mode mode) -> shared_pointer<vfs::file>;
auto openPakGameBoy(string name, vfs::file::mode mode) -> shared_pointer<vfs::file>;

View file

@ -41,8 +41,10 @@ static vector<string> cheatList;
#define RETRO_DEVICE_LIGHTGUN_JUSTIFIERS RETRO_DEVICE_SUBCLASS(RETRO_DEVICE_LIGHTGUN, 2)
#define RETRO_GAME_TYPE_SGB 0x101 | 0x1000
#define RETRO_GAME_TYPE_BSX 0x110 | 0x1000
#define RETRO_MEMORY_SGB_SRAM ((1 << 8) | RETRO_MEMORY_SAVE_RAM)
#define RETRO_MEMORY_GB_SRAM ((2 << 8) | RETRO_MEMORY_SAVE_RAM)
#define RETRO_MEMORY_BSX_SRAM ((3 << 8) | RETRO_MEMORY_SAVE_RAM)
static bool flush_variables() // returns whether video dimensions have changed (overscan, aspectcorrection scale or widescreen AR)
{
@ -499,7 +501,7 @@ static bool flush_variables() // returns whether video dimensions have changed (
int v = variable.value[0] - '0';
if (v >= 0 && v <= 8) val = v;
}
emulator->configure("Hacks/PPU/Mode7/'WindRad", val);
emulator->configure("Hacks/PPU/Mode7/WindRad", val);
}
bool aspectcorrection = program->aspectcorrection;
@ -598,14 +600,24 @@ static void set_environment_info(retro_environment_t cb)
static const struct retro_subsystem_memory_info gb_memory[] = {
{ "srm", RETRO_MEMORY_GB_SRAM },
};
static const struct retro_subsystem_memory_info bsx_memory[] = {
{ "srm", RETRO_MEMORY_BSX_SRAM },
};
static const struct retro_subsystem_rom_info sgb_roms[] = {
{ "Game Boy ROM", "gb|gbc", true, false, true, gb_memory, 1 },
{ "Super Game Boy ROM", "smc|sfc|swc|fig|bs", true, false, true, sgb_memory, 1 },
{ "Super Game Boy ROM", "smc|sfc|swc|fig", true, false, true, sgb_memory, 1 },
};
static const struct retro_subsystem_rom_info bsx_roms[] = {
{ "BS-X ROM", "bs", true, false, true, bsx_memory, 1 },
{ "BS-X BIOS ROM", "smc|sfc|swc|fig", true, false, true, bsx_memory, 1 },
};
static const struct retro_subsystem_info subsystems[] = {
{ "Super Game Boy", "sgb", sgb_roms, 2, RETRO_GAME_TYPE_SGB },
{ "BS-X Satellaview", "bsx", bsx_roms, 2, RETRO_GAME_TYPE_BSX },
{}
};
@ -808,7 +820,7 @@ RETRO_API void retro_get_system_info(retro_system_info *info)
info->library_name = Emulator::Name;
info->library_version = Emulator::Version;
info->need_fullpath = true;
info->valid_extensions = "smc|sfc";
info->valid_extensions = "smc|sfc|gb|gbc|bs";
info->block_extract = false;
}
@ -933,17 +945,54 @@ RETRO_API bool retro_load_game(const retro_game_info *game)
flush_variables();
if (string(game->path).endsWith(".gb") || string(game->path).endsWith(".gbc"))
if (string(game->path).endsWith(".gb"))
{
const char *system_dir;
environ_cb(RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY, &system_dir);
string sgb_full_path = string(system_dir, "/", sgb_bios).transform("\\", "/");
if (!file::exists(sgb_full_path)) {
string sgb_full_path = string(game->path).transform("\\", "/");
string sgb_full_path2 = string(sgb_full_path).replace(".gb", ".sfc");
if (!file::exists(sgb_full_path2)) {
string sgb_full_path = string(system_dir, "/", sgb_bios).transform("\\", "/");
program->superFamicom.location = sgb_full_path;
}
else {
program->superFamicom.location = sgb_full_path2;
}
program->gameBoy.location = string(game->path);
if (!file::exists(program->superFamicom.location)) {
return false;
}
}
else if (string(game->path).endsWith(".gbc"))
{
const char *system_dir;
environ_cb(RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY, &system_dir);
string sgb_full_path = string(game->path).transform("\\", "/");
string sgb_full_path2 = string(sgb_full_path).replace(".gbc", ".sfc");
if (!file::exists(sgb_full_path2)) {
string sgb_full_path = string(system_dir, "/", sgb_bios).transform("\\", "/");
program->superFamicom.location = sgb_full_path;
}
else {
program->superFamicom.location = sgb_full_path2;
}
program->gameBoy.location = string(game->path);
if (!file::exists(program->superFamicom.location)) {
return false;
}
program->superFamicom.location = sgb_full_path;
program->gameBoy.location = string(game->path);
}
else if (string(game->path).endsWith(".bs"))
{
const char *system_dir;
environ_cb(RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY, &system_dir);
string bs_full_path = string(system_dir, "/", "BS-X.bin").transform("\\", "/");
if (!file::exists(bs_full_path)) {
return false;
}
program->superFamicom.location = bs_full_path;
program->bsMemory.location = string(game->path);
}
else
{
@ -979,6 +1028,14 @@ RETRO_API bool retro_load_game_special(unsigned game_type,
program->superFamicom.location = info[1].path;
}
break;
case RETRO_GAME_TYPE_BSX:
{
libretro_print(RETRO_LOG_INFO, "BS-X ROM: %s\n", info[0].path);
libretro_print(RETRO_LOG_INFO, "BS-X BIOS ROM: %s\n", info[1].path);
program->bsMemory.location = info[0].path;
program->superFamicom.location = info[1].path;
}
break;
default:
return false;
}

View file

@ -15,6 +15,7 @@ using namespace nall;
#include <heuristics/heuristics.cpp>
#include <heuristics/super-famicom.cpp>
#include <heuristics/game-boy.cpp>
#include <heuristics/bs-memory.cpp>
#include "resources.hpp"
@ -36,11 +37,13 @@ struct Program : Emulator::Platform
auto loadFile(string location) -> vector<uint8_t>;
auto loadSuperFamicom(string location) -> bool;
auto loadGameBoy(string location) -> bool;
auto loadBSMemory(string location) -> bool;
auto save() -> void;
auto openRomSuperFamicom(string name, vfs::file::mode mode) -> shared_pointer<vfs::file>;
auto openRomGameBoy(string name, vfs::file::mode mode) -> shared_pointer<vfs::file>;
auto openRomBSMemory(string name, vfs::file::mode mode) -> shared_pointer<vfs::file>;
auto hackPatchMemory(vector<uint8_t>& data) -> void;
@ -76,6 +79,10 @@ public:
struct GameBoy : Game {
vector<uint8_t> program;
} gameBoy;
struct BSMemory : Game {
vector<uint8_t> program;
} bsMemory;
};
static Program *program = nullptr;
@ -136,6 +143,21 @@ auto Program::open(uint id, string name, vfs::file::mode mode, bool required) ->
result = openRomGameBoy(name, mode);
}
}
else if (id == 3) { //BS Memory
if (name == "manifest.bml" && mode == vfs::file::mode::read) {
result = vfs::memory::file::open(bsMemory.manifest.data<uint8_t>(), bsMemory.manifest.size());
}
else if (name == "program.rom" && mode == vfs::file::mode::read) {
result = vfs::memory::file::open(bsMemory.program.data(), bsMemory.program.size());
}
else if(name == "program.flash") {
//writes are not flushed to disk in bsnes
result = vfs::memory::file::open(bsMemory.program.data(), bsMemory.program.size());
}
else {
result = openRomBSMemory(name, mode);
}
}
return result;
}
@ -221,6 +243,11 @@ auto Program::load(uint id, string name, string type, vector<string> options) ->
return { id, NULL };
}
}
else if (id == 3) {
if (loadBSMemory(bsMemory.location)) {
return { id, NULL };
}
}
return { id, options(0) };
}
@ -421,6 +448,21 @@ auto Program::openRomGameBoy(string name, vfs::file::mode mode) -> shared_pointe
return {};
}
auto Program::openRomBSMemory(string name, vfs::file::mode mode) -> shared_pointer<vfs::file> {
if (name == "program.rom" && mode == vfs::file::mode::read)
{
return vfs::memory::file::open(bsMemory.program.data(), bsMemory.program.size());
}
if (name == "program.flash")
{
//writes are not flushed to disk
return vfs::memory::file::open(bsMemory.program.data(), bsMemory.program.size());
}
return {};
}
auto Program::loadFile(string location) -> vector<uint8_t>
{
if(Location::suffix(location).downcase() == ".zip") {
@ -510,6 +552,23 @@ auto Program::loadGameBoy(string location) -> bool {
return true;
}
auto Program::loadBSMemory(string location) -> bool {
vector<uint8_t> rom;
rom = loadFile(location);
if (rom.size() < 0x8000) return false;
auto heuristics = Heuristics::BSMemory(rom, location);
auto sha256 = Hash::SHA256(rom).digest();
bsMemory.manifest = heuristics.manifest();
bsMemory.document = BML::unserialize(bsMemory.manifest);
bsMemory.location = location;
bsMemory.program = rom;
return true;
}
auto Program::hackPatchMemory(vector<uint8_t>& data) -> void
{
auto title = superFamicom.title;

View file

@ -124,7 +124,7 @@ ifeq ($(platform),windows)
options += -mthreads -lpthread -lws2_32 -lole32
options += $(if $(findstring g++,$(compiler)),-static -static-libgcc -static-libstdc++)
options += $(if $(findstring true,$(console)),-mconsole,-mwindows)
windres ?= windres
windres := windres
endif
# macos settings

View file

@ -0,0 +1,44 @@
database
revision: 2020-01-01
//BS Memory (JPN)
database
revision: 2018-04-14
game
sha256: 80c34b50817d58820bc8c88d2d9fa462550b4a76372e19c6467cbfbc8cf5d9ef
label: 鮫亀 キャラカセット
name: Same Game - Chara Cassette
region: BSMC-ZS5J-JPN
revision: BSMC-ZS5J-0
board: BSMC-CR-01
memory
type: ROM
size: 0x80000
content: Program
game
sha256: 859c7f7b4771d920a5bdb11f1d247ab6b43fb026594d1062f6f72d32cd340a0a
label: 鮫亀 キャラデータ集
name: Same Game - Chara Data Shuu
region: BSMC-YS5J-JPN
revision: BSMC-YS5J-0
board: BSMC-CR-01
memory
type: ROM
size: 0x80000
content: Program
game
sha256: c92a15fdd9b0133f9ea69105d0230a3acd1cdeef98567462eca86ea02a959e4e
label: SDガンダム ジーネクスト ユニット&マップコレクション
name: SD Gundam G Next - Unit & Map Collection
region: BSMC-ZX3J-JPN
revision: BSMC-ZX3J-0
board: BSMC-BR-01
memory
type: ROM
size: 0x80000
content: Program

87497
pack/Database/Cheat Codes.bml Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,213 @@
database
revision: 2020-01-01
//Sufami Turbo (JPN)
database
revision: 2018-04-14
game
sha256: f73bda08743565e0bd101632ebbac2d363d703a3ab39d23f49d95217cab29269
label: 美少女戦士セーラームーン セーラースターズ ふわふわパニック2
name: Bishoujo Senshi Sailor Moon Sailor Stars - Fuwafuwa Panic 2
region: SFT-0112-JPN
revision: SAILOR MOON
board: PT-911
memory
type: ROM
size: 0x100000
content: Program
note: Unlinkable
game
sha256: afb3f2a83b5bfcb1b8829b6995f108cc4d64ca322d1ba4a50b83af6e1f2e89bd
label: クレヨンしんちゃん 長ぐつドボン!!
name: Crayon Shin-chan - Nagagutsu Dobon!!
region: SFT-0113-JPN
revision: SHINCYAN
board: PT-911
memory
type: ROM
size: 0x80000
content: Program
note: Unlinkable
game
sha256: d93b3a570e7cf343f680ab0768a50b77e3577f9c555007e2de3decd6bc4765c8
label: ゲゲゲの鬼太郎 妖怪ドンジャラ
name: Gegege no Kitarou - Youkai Donjara
region: SFT-0106-JPN
revision: KITARO DONJYAR
board: PT-911
memory
type: ROM
size: 0x80000
content: Program
note: Unlinkable
game
sha256: 89aecd4e23d8219c8de3e71bb75db3dfe667d51c1eba4ea7239d2f772743d0cc
label: 激走戦隊カーレンジャ 全開!レーサー戦士
name: Gekisou Sentai Carranger - Zenkai! Racer Senshi
region: SFT-0109-JPN
revision: CAR RANGER
board: PT-911
memory
type: ROM
size: 0x80000
content: Program
note: Unlinkable
game
sha256: 602b20b788640f5743487108a10f3f77bca5ce2d24208b25b1ca498a96eb0d69
label: ぽいぽい忍者ワールド
name: Poi Poi Ninja World
region: SFT-0103-JPN
revision: POI POI NINJYA
board: PT-911
memory
type: ROM
size: 0x80000
content: Program
memory
type: RAM
size: 0x800
content: Save
note: Linkable: SFT-0103-JPN
game
sha256: 2a9d7c9a61318861028a73ca03e32a48cff162d76cba36fbaab8690b212efe9b
label: SDガンダムジェネレーション アクシズ戦記
name: SD Gundam Generation - Axis Senki
region: SFT-0107-JPN
revision: GUNDAM C
board: PT-912
memory
type: ROM
size: 0x80000
content: Program
memory
type: RAM
size: 0x2000
content: Save
note: Linkable: SFT-0104-JPN, SFT-0105-JPN, SFT-0107-JPN, SFT-0108-JPN, SFT-0110-JPN, SFT-0111-JPN
game
sha256: 60ac017c18f534e8cf24ca7f38e22ce92db95ea6c30b2d59d76f13c4f1c8a6e4
label: SDガンダムジェネレーション バビロニア建国戦記
name: SD Gundam Generation - Babylonia Kenkoku Senki
region: SFT-0108-JPN
revision: GUNDAM D
board: PT-912
memory
type: ROM
size: 0x80000
content: Program
memory
type: RAM
size: 0x2000
content: Save
note: Linkable: SFT-0104-JPN, SFT-0105-JPN, SFT-0107-JPN, SFT-0108-JPN, SFT-0110-JPN, SFT-0111-JPN
game
sha256: e639b5d5d722432b6809ccc6801dc584e1a3016379f34b335ed2dfa73b1ebf69
label: SDガンダムジェネレーション コロニー格闘記
name: SD Gundam Generation - Colony Kakutouki
region: SFT-0111-JPN
revision: GUNDAM F
board: PT-912
memory
type: ROM
size: 0x80000
content: Program
memory
type: RAM
size: 0x2000
content: Save
note: Linkable: SFT-0104-JPN, SFT-0105-JPN, SFT-0107-JPN, SFT-0108-JPN, SFT-0110-JPN, SFT-0111-JPN
game
sha256: 8547a08ed11fe408eac282a90ac46654bd2e5f49bda3aec8e5edf166a0a4b9af
label: SDガンダムジェネレーション グリプス戦記
name: SD Gundam Generation - Gryps Senki
region: SFT-0105-JPN
revision: GUNDAM B
board: PT-912
memory
type: ROM
size: 0x80000
content: Program
memory
type: RAM
size: 0x2000
content: Save
note: Linkable: SFT-0104-JPN, SFT-0105-JPN, SFT-0107-JPN, SFT-0108-JPN, SFT-0110-JPN, SFT-0111-JPN
game
sha256: 3e82215bed08274874b30d461fc4a965c6bca932229da5d46d56e36f484d65eb
label: SDガンダムジェネレーション 一年戦争記
name: SD Gundam Generation - Ichinen Sensouki
region: SFT-0104-JPN
revision: GUNDAM A
board: PT-912
memory
type: ROM
size: 0x80000
content: Program
memory
type: RAM
size: 0x2000
content: Save
note: Linkable: SFT-0104-JPN, SFT-0105-JPN, SFT-0107-JPN, SFT-0108-JPN, SFT-0110-JPN, SFT-0111-JPN
game
sha256: 5951a58a91d8e397d0a237ccc2b1248e17c7312cb9cc11cbc350200a97b4e021
label: SDガンダムジェネレーション ザンスカール戦記
name: SD Gundam Generation - Zanscare Senki
region: SFT-0110-JPN
revision: GUNDAM E
board: PT-912
memory
type: ROM
size: 0x80000
content: Program
memory
type: RAM
size: 0x2000
content: Save
note: Linkable: SFT-0104-JPN, SFT-0105-JPN, SFT-0107-JPN, SFT-0108-JPN, SFT-0110-JPN, SFT-0111-JPN
game
sha256: 2fec5f2bc7dee010af10569a3d2bc18715a79a126940800c3eade5abbd625e3f
label: SDウルトラバトル セブン伝説
name: SD Ultra Battle - Seven Densetsu
region: SFT-0102-JPN
revision: ULTRA SEVEN 1
board: PT-911
memory
type: ROM
size: 0x80000
content: Program
memory
type: RAM
size: 0x800
content: Save
note: Linkable: SFT-0101-JPN, SFT-0102-JPN
game
sha256: 2bb55214fb668ca603d7b944b14f105dfb10b987a8902d420fe4ae1cb69c1d4a
label: SDウルトラバトル ウルトラマン伝説
name: SD Ultra Battle - Ultraman Densetsu
region: SFT-0101-JPN
revision: ULTRA MAN 1
board: PT-911
memory
type: ROM
size: 0x80000
content: Program
memory
type: RAM
size: 0x800
content: Save
note: Linkable: SFT-0101-JPN, SFT-0102-JPN

File diff suppressed because it is too large Load diff

View file

674
pack/GPLv3.txt Normal file
View file

@ -0,0 +1,674 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<https://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<https://www.gnu.org/licenses/why-not-lgpl.html>.

View file

@ -0,0 +1,93 @@
#version 150
// AntiAliased Nearest Neighbor
// by jimbo1qaz and wareya
// Licensed MIT
precision highp float;
uniform sampler2D source[];
uniform vec4 sourceSize[];
uniform vec4 targetSize[];
in Vertex {
vec2 texCoord;
};
out vec4 fragColor;
#define NOT(fl) (1-fl)
#define YES(fl) fl
vec4 vpow(vec4 n, float e)
{
return vec4(pow(n.x, e), pow(n.y, e), pow(n.z, e), pow(n.w, e));
}
vec4 getLQV(vec3 mine) {
return vec4
( mine.r
, mine.g
, mine.b
, mine.r*0.2989 + mine.g*0.5870 + mine.b*0.1140);
}
vec3 fromLQV(vec4 mine) {
float f = mine.w/(mine.r*0.2989 + mine.g*0.5870 + mine.b*0.1140);
return vec3(mine.rgb)*f;
}
vec3 percent(float ssize, float tsize, float coord) {
float minfull = (coord*tsize - 0.5) /tsize*ssize;
float maxfull = (coord*tsize + 0.5) /tsize*ssize;
float realfull = floor(maxfull);
if (minfull > realfull) {
return vec3(1, (realfull+0.5)/ssize, (realfull+0.5)/ssize);
}
return vec3(
(maxfull - realfull) / (maxfull - minfull),
(realfull-0.5) / ssize,
(realfull+0.5) / ssize
);
}
void main() {
float cheapsrgb = 2.1;
float gamma = 3.0;
vec3 xstuff = percent(sourceSize[0].x, targetSize[0].x, texCoord.x);
vec3 ystuff = percent(sourceSize[0].y, targetSize[0].y, texCoord.y);
float xkeep = xstuff[0];
float ykeep = ystuff[0];
// get points to interpolate across, in linear rgb
vec4 a = getLQV(vpow(texture(source[0],vec2(xstuff[1],ystuff[1])), cheapsrgb).rgb);
vec4 b = getLQV(vpow(texture(source[0],vec2(xstuff[2],ystuff[1])), cheapsrgb).rgb);
vec4 c = getLQV(vpow(texture(source[0],vec2(xstuff[1],ystuff[2])), cheapsrgb).rgb);
vec4 d = getLQV(vpow(texture(source[0],vec2(xstuff[2],ystuff[2])), cheapsrgb).rgb);
// use perceptual gamma for luminance component
a.w = pow(a.w, 1/gamma);
b.w = pow(b.w, 1/gamma);
c.w = pow(c.w, 1/gamma);
d.w = pow(d.w, 1/gamma);
// interpolate
vec4 gammaLQVresult =
NOT(xkeep)*NOT(ykeep)*a +
YES(xkeep)*NOT(ykeep)*b +
NOT(xkeep)*YES(ykeep)*c +
YES(xkeep)*YES(ykeep)*d;
// change luminance gamma back to linear
vec4 LQVresult = gammaLQVresult;
LQVresult.w = pow(gammaLQVresult.w, gamma);
// convert back to srgb; lqv -> lrgb -> srgb
vec4 c1 = vpow(vec4(fromLQV(LQVresult), 1), 1/cheapsrgb);
fragColor = c1;
}

View file

@ -0,0 +1,8 @@
input
filter: nearest
program
fragment: AANN.fs
output
filter: nearest

View file

@ -0,0 +1,297 @@
#version 150
/*
CRT-interlaced
Copyright (C) 2010-2012 cgwg, Themaister and DOLLS
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation; either version 2 of the License, or (at your option)
any later version.
(cgwg gave their consent to have the original version of this shader
distributed under the GPL in this message:
http://board.byuu.org/viewtopic.php?p=26075#p26075
"Feel free to distribute my shaders under the GPL. After all, the
barrel distortion code was taken from the Curvature shader, which is
under the GPL."
)
This shader variant is pre-configured with screen curvature
*/
//#define INTERLACED
uniform sampler2D source[];
uniform vec4 sourceSize[];
uniform vec4 targetSize;
uniform int phase;
in Vertex {
vec2 texCoord;
};
out vec4 fragColor;
// Comment the next line to disable interpolation in linear gamma (and gain speed).
#define LINEAR_PROCESSING
// Enable screen curvature.
#define CURVATURE
// Enable 3x oversampling of the beam profile
#define OVERSAMPLE
// Use the older, purely gaussian beam profile
//#define USEGAUSSIAN
// vertex params //
// gamma of simulated CRT
#define CRTgamma 2.4
// gamma of display monitor (typically 2.2 is correct)
#define monitorgamma 2.2
// overscan (e.g. 1.02 for 2% overscan)
#define overscan vec2(1.0 , 1.0)//(0.93 , 0.915)
// aspect ratio
#define aspect vec2(1.0, 0.75)
// lengths are measured in units of (approximately) the width of the monitor
// simulated distance from viewer to monitor
#define d 2.0
// radius of curvature
#define R 2.0
// tilt angle in radians
// (behavior might be a bit wrong if both components are nonzero)
#define angle vec2(0.0,-0.0)
// size of curved corners
#define cornersize 0.02
// border smoothness parameter
// decrease if borders are too aliased
#define cornersmooth 80.0
#define sinangle sin(angle)
#define cosangle cos(angle)
#define stretch maxscale()
#define ilfac vec2(1.0, floor(sourceSize[0].y / 200.0))
#define one (ilfac / sourceSize[0].xy)
#define mod_factor (texCoord.x * targetSize.x)
// END of vertex params //
// Macros.
#define FIX(c) max(abs(c), 1e-5);
#define PI 3.141592653589
#ifdef LINEAR_PROCESSING
# define TEX2D(c) pow(texture(source[0], (c)), vec4(CRTgamma))
#else
# define TEX2D(c) texture(source[0], (c))
#endif
#define FIX(c) max(abs(c), 1e-5);
float intersect(vec2 xy)
{
float A = dot(xy,xy)+d*d;
float B = 2.0*(R*(dot(xy,sinangle)-d*cosangle.x*cosangle.y)-d*d);
float C = d*d + 2.0*R*d*cosangle.x*cosangle.y;
return (-B-sqrt(B*B-4.0*A*C))/(2.0*A);
}
vec2 bkwtrans(vec2 xy)
{
float c = intersect(xy);
vec2 point = vec2(c)*xy;
point -= vec2(-R)*sinangle;
point /= vec2(R);
vec2 tang = sinangle/cosangle;
vec2 poc = point/cosangle;
float A = dot(tang,tang)+1.0;
float B = -2.0*dot(poc,tang);
float C = dot(poc,poc)-1.0;
float a = (-B+sqrt(B*B-4.0*A*C))/(2.0*A);
vec2 uv = (point-a*sinangle)/cosangle;
float r = R*acos(a);
return uv*r/sin(r/R);
}
vec2 fwtrans(vec2 uv)
{
float r = FIX(sqrt(dot(uv,uv)));
uv *= sin(r/R)/r;
float x = 1.0-cos(r/R);
float D = d/R + x*cosangle.x*cosangle.y+dot(uv,sinangle);
return d*(uv*cosangle-x*sinangle)/D;
}
vec3 maxscale()
{
vec2 c = bkwtrans(-R * sinangle / (1.0 + R/d*cosangle.x*cosangle.y));
vec2 a = vec2(0.5,0.5)*aspect;
vec2 lo = vec2(fwtrans(vec2(-a.x,c.y)).x,
fwtrans(vec2(c.x,-a.y)).y)/aspect;
vec2 hi = vec2(fwtrans(vec2(+a.x,c.y)).x,
fwtrans(vec2(c.x,+a.y)).y)/aspect;
return vec3((hi+lo)*aspect*0.5,max(hi.x-lo.x,hi.y-lo.y));
}
vec2 transform(vec2 coord)
{
coord = (coord-vec2(0.5))*aspect*stretch.z+stretch.xy;
return (bkwtrans(coord)/overscan/aspect+vec2(0.5));
}
float corner(vec2 coord)
{
coord = (coord - vec2(0.5)) * overscan + vec2(0.5);
coord = min(coord, vec2(1.0)-coord) * aspect;
vec2 cdist = vec2(cornersize);
coord = (cdist - min(coord,cdist));
float dist = sqrt(dot(coord,coord));
return clamp((cdist.x-dist)*cornersmooth,0.0, 1.0);
}
// Calculate the influence of a scanline on the current pixel.
//
// 'distance' is the distance in texture coordinates from the current
// pixel to the scanline in question.
// 'color' is the colour of the scanline at the horizontal location of
// the current pixel.
vec4 scanlineWeights(float distance, vec4 color)
{
// "wid" controls the width of the scanline beam, for each RGB channel
// The "weights" lines basically specify the formula that gives
// you the profile of the beam, i.e. the intensity as
// a function of distance from the vertical center of the
// scanline. In this case, it is gaussian if width=2, and
// becomes nongaussian for larger widths. Ideally this should
// be normalized so that the integral across the beam is
// independent of its width. That is, for a narrower beam
// "weights" should have a higher peak at the center of the
// scanline than for a wider beam.
#ifdef USEGAUSSIAN
vec4 wid = 0.3 + 0.1 * pow(color, vec4(3.0));
vec4 weights = vec4(distance / wid);
return 0.4 * exp(-weights * weights) / wid;
#else
vec4 wid = 2.0 + 2.0 * pow(color, vec4(4.0));
vec4 weights = vec4(distance / 0.3);
return 1.4 * exp(-pow(weights * inversesqrt(0.5 * wid), wid)) / (0.6 + 0.2 * wid);
#endif
}
void main()
{
// Here's a helpful diagram to keep in mind while trying to
// understand the code:
//
// | | | | |
// -------------------------------
// | | | | |
// | 01 | 11 | 21 | 31 | <-- current scanline
// | | @ | | |
// -------------------------------
// | | | | |
// | 02 | 12 | 22 | 32 | <-- next scanline
// | | | | |
// -------------------------------
// | | | | |
//
// Each character-cell represents a pixel on the output
// surface, "@" represents the current pixel (always somewhere
// in the bottom half of the current scan-line, or the top-half
// of the next scanline). The grid of lines represents the
// edges of the texels of the underlying texture.
// Texture coordinates of the texel containing the active pixel.
#ifdef CURVATURE
vec2 xy = transform(texCoord);
#else
vec2 xy = texCoord;
#endif
float cval = corner(xy);
// Of all the pixels that are mapped onto the texel we are
// currently rendering, which pixel are we currently rendering?
#ifdef INTERLACED
vec2 ilvec = vec2(0.0,ilfac.y > 1.5 ? mod(float(phase),2.0) : 0.0);
#else
vec2 ilvec = vec2(0.0,ilfac.y);
#endif
vec2 ratio_scale = (xy * sourceSize[0].xy - vec2(0.5) + ilvec)/ilfac;
#ifdef OVERSAMPLE
float filter_ = sourceSize[0].y / targetSize.y;
#endif
vec2 uv_ratio = fract(ratio_scale);
// Snap to the center of the underlying texel.
xy = (floor(ratio_scale)*ilfac + vec2(0.5) - ilvec) / sourceSize[0].xy;
// Calculate Lanczos scaling coefficients describing the effect
// of various neighbour texels in a scanline on the current
// pixel.
vec4 coeffs = PI * vec4(1.0 + uv_ratio.x, uv_ratio.x, 1.0 - uv_ratio.x, 2.0 - uv_ratio.x);
// Prevent division by zero.
coeffs = FIX(coeffs);
// Lanczos2 kernel.
coeffs = 2.0 * sin(coeffs) * sin(coeffs / 2.0) / (coeffs * coeffs);
// Normalize.
coeffs /= dot(coeffs, vec4(1.0));
// Calculate the effective colour of the current and next
// scanlines at the horizontal location of the current pixel,
// using the Lanczos coefficients above.
vec4 col = clamp(mat4(
TEX2D(xy + vec2(-one.x, 0.0)),
TEX2D(xy),
TEX2D(xy + vec2(one.x, 0.0)),
TEX2D(xy + vec2(2.0 * one.x, 0.0))) * coeffs,
0.0, 1.0);
vec4 col2 = clamp(mat4(
TEX2D(xy + vec2(-one.x, one.y)),
TEX2D(xy + vec2(0.0, one.y)),
TEX2D(xy + one),
TEX2D(xy + vec2(2.0 * one.x, one.y))) * coeffs,
0.0, 1.0);
#ifndef LINEAR_PROCESSING
col = pow(col , vec4(CRTgamma));
col2 = pow(col2, vec4(CRTgamma));
#endif
// Calculate the influence of the current and next scanlines on
// the current pixel.
vec4 weights = scanlineWeights(uv_ratio.y, col);
vec4 weights2 = scanlineWeights(1.0 - uv_ratio.y, col2);
#ifdef OVERSAMPLE
uv_ratio.y =uv_ratio.y+1.0/3.0*filter_;
weights = (weights+scanlineWeights(uv_ratio.y, col))/3.0;
weights2=(weights2+scanlineWeights(abs(1.0-uv_ratio.y), col2))/3.0;
uv_ratio.y =uv_ratio.y-2.0/3.0*filter_;
weights=weights+scanlineWeights(abs(uv_ratio.y), col)/3.0;
weights2=weights2+scanlineWeights(abs(1.0-uv_ratio.y), col2)/3.0;
#endif
vec3 mul_res = (col * weights + col2 * weights2).rgb * vec3(cval);
// dot-mask emulation:
// Output pixels are alternately tinted green and magenta.
vec3 dotMaskWeights = mix(
vec3(1.0, 0.7, 1.0),
vec3(0.7, 1.0, 0.7),
floor(mod(mod_factor, 2.0))
);
mul_res *= dotMaskWeights;
// Convert the image gamma for display on our output device.
mul_res = pow(mul_res, vec3(1.0 / monitorgamma));
// Color the texel.
fragColor = vec4(mul_res, 1.0);
}

View file

@ -0,0 +1,121 @@
#version 150
in vec4 position;
in vec2 texCoord;
in vec4 sourceSize[];
in vec4 targetSize;
out Vertex {
vec2 texCoord;
} vertexOut;
float CRTgamma;
float monitorgamma;
vec2 overscan;
vec2 aspect;
float d;
float R;
float cornersize;
float cornersmooth;
vec3 stretch;
vec2 sinangle;
vec2 cosangle;
vec2 one;
float mod_factor;
vec2 ilfac;
#define FIX(c) max(abs(c), 1e-5);
float intersect(vec2 xy)
{
float A = dot(xy,xy)+d*d;
float B = 2.0*(R*(dot(xy,sinangle)-d*cosangle.x*cosangle.y)-d*d);
float C = d*d + 2.0*R*d*cosangle.x*cosangle.y;
return (-B-sqrt(B*B-4.0*A*C))/(2.0*A);
}
vec2 bkwtrans(vec2 xy)
{
float c = intersect(xy);
vec2 point = vec2(c)*xy;
point -= vec2(-R)*sinangle;
point /= vec2(R);
vec2 tang = sinangle/cosangle;
vec2 poc = point/cosangle;
float A = dot(tang,tang)+1.0;
float B = -2.0*dot(poc,tang);
float C = dot(poc,poc)-1.0;
float a = (-B+sqrt(B*B-4.0*A*C))/(2.0*A);
vec2 uv = (point-a*sinangle)/cosangle;
float r = R*acos(a);
return uv*r/sin(r/R);
}
vec2 fwtrans(vec2 uv)
{
float r = FIX(sqrt(dot(uv,uv)));
uv *= sin(r/R)/r;
float x = 1.0-cos(r/R);
float D = d/R + x*cosangle.x*cosangle.y+dot(uv,sinangle);
return d*(uv*cosangle-x*sinangle)/D;
}
vec3 maxscale()
{
vec2 c = bkwtrans(-R * sinangle / (1.0 + R/d*cosangle.x*cosangle.y));
vec2 a = vec2(0.5,0.5)*aspect;
vec2 lo = vec2(fwtrans(vec2(-a.x,c.y)).x,
fwtrans(vec2(c.x,-a.y)).y)/aspect;
vec2 hi = vec2(fwtrans(vec2(+a.x,c.y)).x,
fwtrans(vec2(c.x,+a.y)).y)/aspect;
return vec3((hi+lo)*aspect*0.5,max(hi.x-lo.x,hi.y-lo.y));
}
void main()
{
// START of parameters
// gamma of simulated CRT
CRTgamma = 2.4;
// gamma of display monitor (typically 2.2 is correct)
monitorgamma = 2.2;
// overscan (e.g. 1.02 for 2% overscan)
overscan = vec2(0.99,0.99);
// aspect ratio
aspect = vec2(1.0, 0.75);
// lengths are measured in units of (approximately) the width of the monitor
// simulated distance from viewer to monitor
d = 2.0;
// radius of curvature
R = 2.0;
// tilt angle in radians
// (behavior might be a bit wrong if both components are nonzero)
const vec2 angle = vec2(0.0,-0.0);
// size of curved corners
cornersize = 0.03;
// border smoothness parameter
// decrease if borders are too aliased
cornersmooth = 80.0;
// END of parameters
vertexOut.texCoord = texCoord.xy;
gl_Position = position;
// Precalculate a bunch of useful values we'll need in the fragment
// shader.
sinangle = sin(angle);
cosangle = cos(angle);
stretch = maxscale();
ilfac = vec2(1.0,floor(sourceSize[0].y/200.0));
// The size of one texel, in texture-coordinates.
one = ilfac / sourceSize[0].xy;
// Resulting X pixel-coordinate of the pixel we're drawing.
mod_factor = texCoord.x * targetSize.x;
}

View file

@ -0,0 +1,21 @@
#version 150
#define distortion 0.08
uniform sampler2D source[];
uniform vec4 sourceSize[];
in Vertex {
vec2 texCoord;
};
out vec4 fragColor;
vec2 radialDistortion(vec2 coord) {
vec2 cc = coord - vec2(0.5);
float dist = dot(cc, cc) * distortion;
return coord + cc * (1.0 - dist) * dist;
}
void main(void) {
fragColor = texture(source[0], radialDistortion(texCoord));
}

View file

@ -0,0 +1,5 @@
program
vertex: crt-geom.vs
fragment: crt-geom.fs
modulo: 2

View file

@ -0,0 +1,32 @@
#version 150
uniform sampler2D source[];
uniform vec4 sourceSize[];
in Vertex {
vec2 vTexCoord;
};
out vec4 FragColor;
// Higher value, more centered glow.
// Lower values might need more taps.
#define GLOW_FALLOFF 0.35
#define TAPS 4
#define kernel(x) exp(-GLOW_FALLOFF * (x) * (x))
void main() {
vec3 col = vec3(0.0);
float dx = 4.0 * sourceSize[0].z; // Mipmapped
float k_total = 0.0;
for (int i = -TAPS; i <= TAPS; i++)
{
float k = kernel(i);
k_total += k;
col += k * texture(source[0], vTexCoord + vec2(float(i) * dx, 0.0)).rgb;
}
FragColor = vec4(col / k_total, 1.0);
}

View file

@ -0,0 +1,16 @@
#version 150
in vec4 position;
in vec2 texCoord;
out Vertex {
vec2 vTexCoord;
};
uniform vec4 targetSize;
uniform vec4 sourceSize[];
void main() {
gl_Position = position;
vTexCoord = texCoord;
}

View file

@ -0,0 +1,32 @@
#version 150
uniform sampler2D source[];
uniform vec4 sourceSize[];
in Vertex {
vec2 vTexCoord;
};
out vec4 FragColor;
// Higher value, more centered glow.
// Lower values might need more taps.
#define GLOW_FALLOFF 0.35
#define TAPS 4
#define kernel(x) exp(-GLOW_FALLOFF * (x) * (x))
void main() {
vec3 col = vec3(0.0);
float dy = sourceSize[0].w;
float k_total = 0.0;
for (int i = -TAPS; i <= TAPS; i++)
{
float k = kernel(i);
k_total += k;
col += k * texture(source[0], vTexCoord + vec2(0.0, float(i) * dy)).rgb;
}
FragColor = vec4(col / k_total, 1.0);
}

View file

@ -0,0 +1,13 @@
#version 150
in vec4 position;
in vec2 texCoord;
out Vertex {
vec2 vTexCoord;
};
void main() {
gl_Position = position;
vTexCoord = texCoord;
}

View file

@ -0,0 +1,33 @@
#version 150
#define INV_SQRT_2_PI 0.38 // Doesn't have to be accurate.
#define HORIZ_GAUSS_WIDTH 0.5
uniform sampler2D source[];
uniform vec4 sourceSize[];
in Vertex {
vec2 vTexCoord;
float data_pix_no;
float data_one;
};
out vec4 FragColor;
void main() {
float texel = floor(data_pix_no);
float phase = data_pix_no - texel;
float base_phase = phase - 0.5;
vec2 tex = vec2((texel + 0.5) * sourceSize[0].z, vTexCoord.y);
vec3 col = vec3(0.0);
for (int i = -2; i <= 2; i++)
{
float phase = base_phase - float(i);
float g = INV_SQRT_2_PI * exp(-0.5 * phase * phase / (HORIZ_GAUSS_WIDTH * HORIZ_GAUSS_WIDTH)) / HORIZ_GAUSS_WIDTH;
col += texture(source[0], tex + vec2(float(i) * data_one, 0.0)).rgb * g;
}
FragColor = vec4(col, 1.0);
}

View file

@ -0,0 +1,20 @@
#version 150
in vec4 position;
in vec2 texCoord;
out Vertex {
vec2 vTexCoord;
float data_pix_no;
float data_one;
};
uniform vec4 sourceSize[];
void main() {
gl_Position = position;
vTexCoord = texCoord;
data_pix_no = vTexCoord.x * sourceSize[0].x;
data_one = sourceSize[0].z;
}

View file

@ -0,0 +1,49 @@
#version 150
#define BOOST 1.0
#define CRT_GEOM_BEAM 1
uniform sampler2D source[];
uniform vec4 sourceSize[];
in Vertex {
vec2 vTexCoord;
vec2 data_pix_no;
float data_one;
};
out vec4 FragColor;
vec3 beam(vec3 color, float dist)
{
#if CRT_GEOM_BEAM
vec3 wid = 2.0 + 2.0 * pow(color, vec3(4.0));
vec3 weights = vec3(abs(dist) * 3.333333333);
return 2.0 * color * exp(-pow(weights * inversesqrt(0.5 * wid), wid)) / (0.6 + 0.2 * wid);
#else
float reciprocal_width = 4.0;
vec3 x = dist * reciprocal_width;
return 2.0 * color * exp(-0.5 * x * x) * reciprocal_width;
#endif
}
void main() {
vec2 texel = floor(data_pix_no);
float phase = data_pix_no.y - texel.y;
vec2 tex = vec2(texel + 0.5) * sourceSize[0].zw;
vec3 top = texture(source[0], tex + vec2(0.0, 0 * data_one)).rgb;
vec3 bottom = texture(source[0], tex + vec2(0.0, 1 * data_one)).rgb;
float dist0 = phase;
float dist1 = 1.0 - phase;
vec3 scanline = vec3(0.0);
scanline += beam(top, dist0);
scanline += beam(bottom, dist1);
FragColor = vec4(BOOST * scanline * 0.869565217391304, 1.0);
}

View file

@ -0,0 +1,20 @@
#version 150
in vec4 position;
in vec2 texCoord;
out Vertex {
vec2 vTexCoord;
vec2 data_pix_no;
float data_one;
};
uniform vec4 sourceSize[];
void main() {
gl_Position = position;
vTexCoord = texCoord;
data_pix_no = vTexCoord.xy * sourceSize[0].xy - vec2(0.0, 0.5);
data_one = sourceSize[0].w;
}

View file

@ -0,0 +1,13 @@
#version 150
uniform sampler2D source[];
in Vertex {
vec2 vTexCoord;
};
out vec4 FragColor;
void main() {
FragColor = texture(source[0], vTexCoord);
}

View file

@ -0,0 +1,15 @@
#version 150
in vec4 position;
in vec2 texCoord;
out Vertex {
vec2 vTexCoord;
};
uniform vec4 sourceSize[];
void main() {
gl_Position = position;
vTexCoord = texCoord;
}

View file

@ -0,0 +1,15 @@
#version 150
#define INPUT_GAMMA 2.2
uniform sampler2D source[];
in Vertex {
vec2 vTexCoord;
};
out vec4 FragColor;
void main() {
FragColor = pow(texture(source[0], vTexCoord), vec4(INPUT_GAMMA));
}

View file

@ -0,0 +1,15 @@
#version 150
in vec4 position;
in vec2 texCoord;
out Vertex {
vec2 vTexCoord;
};
uniform vec4 sourceSize[];
void main() {
gl_Position = position;
vTexCoord = texCoord;
}

View file

@ -0,0 +1,54 @@
input
filter: nearest
program
format: srgb8
filter: nearest
vertex: linearize.vs
fragment: linearize.fs
program
format: srgb8
filter: nearest
height: 25%
width: 100%
vertex: gauss_horiz.vs
fragment: gauss_horiz.fs
program
format: srgb8
filter: nearest
vertex: gauss_vert.vs
fragment: gauss_vert.fs
program
format: srgb8
filter: nearest
height: 100%
width: 100%
vertex: threshold.vs
fragment: threshold.fs
program
format: srgb8
filter: linear
height: 25%
width: 25%
vertex: blur_horiz.vs
fragment: blur_horiz.fs
program
format: srgb8
filter: linear
height: 100%
width: 100%
vertex: blur_vert.vs
fragment: blur_vert.fs
program
filter: linear
vertex: resolve.vs
fragment: resolve.fs
output
filter: linear

View file

@ -0,0 +1,28 @@
#version 150
#define BLOOM_STRENGTH 0.25
#define OUTPUT_GAMMA 2.2
uniform sampler2D source[];
in Vertex {
vec2 vTexCoord;
};
out vec4 FragColor;
// For debugging
#define BLOOM_ONLY 0
#define CRT_PASS source[3]
void main() {
#if BLOOM_ONLY
vec3 source = BLOOM_STRENGTH * texture(source[0], vTexCoord).rgb;
#else
vec3 source_ = 1.15 * texture(CRT_PASS, vTexCoord).rgb;
vec3 bloom = texture(source[0], vTexCoord).rgb;
source_ += BLOOM_STRENGTH * bloom;
#endif
FragColor = vec4(pow(clamp(source_, 0.0, 1.0), vec3(1.0 / OUTPUT_GAMMA)), 1.0);
}

View file

@ -0,0 +1,13 @@
#version 150
in vec4 position;
in vec2 texCoord;
out Vertex {
vec2 vTexCoord;
};
void main() {
gl_Position = position;
vTexCoord = texCoord;
}

View file

@ -0,0 +1,19 @@
#version 150
#define GLOW_WHITEPOINT 1.0
#define GLOW_ROLLOFF 3.0
uniform sampler2D source[];
in Vertex {
vec2 vTexCoord;
};
out vec4 FragColor;
void main() {
vec3 color = 1.15 * texture(source[0], vTexCoord).rgb;
vec3 factor = clamp(color / GLOW_WHITEPOINT, 0.0, 1.0);
FragColor = vec4(pow(factor, vec3(GLOW_ROLLOFF)), 1.0);
}

View file

@ -0,0 +1,13 @@
#version 150
in vec4 position;
in vec2 texCoord;
out Vertex {
vec2 vTexCoord;
};
void main() {
gl_Position = position;
vTexCoord = texCoord;
}

View file

@ -0,0 +1,306 @@
#version 150
// PUBLIC DOMAIN CRT STYLED SCAN-LINE SHADER
//
// by Timothy Lottes
//
// This is more along the style of a really good CGA arcade monitor.
// With RGB inputs instead of NTSC.
// The shadow mask example has the mask rotated 90 degrees for less chromatic aberration.
//
// Left it unoptimized to show the theory behind the algorithm.
//
// It is an example what I personally would want as a display option for pixel art games.
// Please take and use, change, or whatever.
#define hardScan -8.0
#define hardPix -3.0
#define warpX 0.031
#define warpY 0.041
#define maskDark 0.5
#define maskLight 1.5
#define scaleInLinearGamma 1.0
#define shadowMask 3.0
#define brightBoost 1.0
#define hardBloomPix -1.5
#define hardBloomScan -2.0
#define bloomAmount 0.4
#define shape 2.0
uniform sampler2D source[];
uniform vec4 sourceSize[];
in Vertex {
vec2 vTexCoord;
};
out vec4 FragColor;
//Uncomment to reduce instructions with simpler linearization
//(fixes HD3000 Sandy Bridge IGP)
//#define SIMPLE_LINEAR_GAMMA
#define DO_BLOOM 1
// ------------- //
// sRGB to Linear.
// Assuming using sRGB typed textures this should not be needed.
#ifdef SIMPLE_LINEAR_GAMMA
float ToLinear1(float c)
{
return c;
}
vec3 ToLinear(vec3 c)
{
return c;
}
vec3 ToSrgb(vec3 c)
{
return pow(c, vec3(1.0 / 2.2));
}
#else
float ToLinear1(float c)
{
if (scaleInLinearGamma == 0)
return c;
return(c<=0.04045) ? c/12.92 : pow((c + 0.055)/1.055, 2.4);
}
vec3 ToLinear(vec3 c)
{
if (scaleInLinearGamma==0)
return c;
return vec3(ToLinear1(c.r), ToLinear1(c.g), ToLinear1(c.b));
}
// Linear to sRGB.
// Assuming using sRGB typed textures this should not be needed.
float ToSrgb1(float c)
{
if (scaleInLinearGamma == 0)
return c;
return(c<0.0031308 ? c*12.92 : 1.055*pow(c, 0.41666) - 0.055);
}
vec3 ToSrgb(vec3 c)
{
if (scaleInLinearGamma == 0)
return c;
return vec3(ToSrgb1(c.r), ToSrgb1(c.g), ToSrgb1(c.b));
}
#endif
// Nearest emulated sample given floating point position and texel offset.
// Also zero's off screen.
vec3 Fetch(vec2 pos,vec2 off){
pos=(floor(pos*sourceSize[0].xy+off)+vec2(0.5,0.5))/sourceSize[0].xy;
#ifdef SIMPLE_LINEAR_GAMMA
return ToLinear(brightBoost * pow(texture(source[0],pos.xy).rgb, vec3(2.2)));
#else
return ToLinear(brightBoost * texture(source[0],pos.xy).rgb);
#endif
}
// Distance in emulated pixels to nearest texel.
vec2 Dist(vec2 pos)
{
pos = pos*sourceSize[0].xy;
return -((pos - floor(pos)) - vec2(0.5));
}
// 1D Gaussian.
float Gaus(float pos, float scale)
{
return exp2(scale*pow(abs(pos), shape));
}
// 3-tap Gaussian filter along horz line.
vec3 Horz3(vec2 pos, float off)
{
vec3 b = Fetch(pos, vec2(-1.0, off));
vec3 c = Fetch(pos, vec2( 0.0, off));
vec3 d = Fetch(pos, vec2( 1.0, off));
float dst = Dist(pos).x;
// Convert distance to weight.
float scale = hardPix;
float wb = Gaus(dst-1.0,scale);
float wc = Gaus(dst+0.0,scale);
float wd = Gaus(dst+1.0,scale);
// Return filtered sample.
return (b*wb+c*wc+d*wd)/(wb+wc+wd);
}
// 5-tap Gaussian filter along horz line.
vec3 Horz5(vec2 pos,float off){
vec3 a = Fetch(pos,vec2(-2.0, off));
vec3 b = Fetch(pos,vec2(-1.0, off));
vec3 c = Fetch(pos,vec2( 0.0, off));
vec3 d = Fetch(pos,vec2( 1.0, off));
vec3 e = Fetch(pos,vec2( 2.0, off));
float dst = Dist(pos).x;
// Convert distance to weight.
float scale = hardPix;
float wa = Gaus(dst - 2.0, scale);
float wb = Gaus(dst - 1.0, scale);
float wc = Gaus(dst + 0.0, scale);
float wd = Gaus(dst + 1.0, scale);
float we = Gaus(dst + 2.0, scale);
// Return filtered sample.
return (a*wa+b*wb+c*wc+d*wd+e*we)/(wa+wb+wc+wd+we);
}
// 7-tap Gaussian filter along horz line.
vec3 Horz7(vec2 pos,float off)
{
vec3 a = Fetch(pos, vec2(-3.0, off));
vec3 b = Fetch(pos, vec2(-2.0, off));
vec3 c = Fetch(pos, vec2(-1.0, off));
vec3 d = Fetch(pos, vec2( 0.0, off));
vec3 e = Fetch(pos, vec2( 1.0, off));
vec3 f = Fetch(pos, vec2( 2.0, off));
vec3 g = Fetch(pos, vec2( 3.0, off));
float dst = Dist(pos).x;
// Convert distance to weight.
float scale = hardBloomPix;
float wa = Gaus(dst - 3.0, scale);
float wb = Gaus(dst - 2.0, scale);
float wc = Gaus(dst - 1.0, scale);
float wd = Gaus(dst + 0.0, scale);
float we = Gaus(dst + 1.0, scale);
float wf = Gaus(dst + 2.0, scale);
float wg = Gaus(dst + 3.0, scale);
// Return filtered sample.
return (a*wa+b*wb+c*wc+d*wd+e*we+f*wf+g*wg)/(wa+wb+wc+wd+we+wf+wg);
}
// Return scanline weight.
float Scan(vec2 pos, float off)
{
float dst = Dist(pos).y;
return Gaus(dst + off, hardScan);
}
// Return scanline weight for bloom.
float BloomScan(vec2 pos, float off)
{
float dst = Dist(pos).y;
return Gaus(dst + off, hardBloomScan);
}
// Allow nearest three lines to effect pixel.
vec3 Tri(vec2 pos)
{
vec3 a = Horz3(pos,-1.0);
vec3 b = Horz5(pos, 0.0);
vec3 c = Horz3(pos, 1.0);
float wa = Scan(pos,-1.0);
float wb = Scan(pos, 0.0);
float wc = Scan(pos, 1.0);
return a*wa + b*wb + c*wc;
}
// Small bloom.
vec3 Bloom(vec2 pos)
{
vec3 a = Horz5(pos,-2.0);
vec3 b = Horz7(pos,-1.0);
vec3 c = Horz7(pos, 0.0);
vec3 d = Horz7(pos, 1.0);
vec3 e = Horz5(pos, 2.0);
float wa = BloomScan(pos,-2.0);
float wb = BloomScan(pos,-1.0);
float wc = BloomScan(pos, 0.0);
float wd = BloomScan(pos, 1.0);
float we = BloomScan(pos, 2.0);
return a*wa+b*wb+c*wc+d*wd+e*we;
}
// Distortion of scanlines, and end of screen alpha.
vec2 Warp(vec2 pos)
{
pos = pos*2.0-1.0;
pos *= vec2(1.0 + (pos.y*pos.y)*warpX, 1.0 + (pos.x*pos.x)*warpY);
return pos*0.5 + 0.5;
}
// Shadow mask.
vec3 Mask(vec2 pos)
{
vec3 mask = vec3(maskDark, maskDark, maskDark);
// Very compressed TV style shadow mask.
if (shadowMask == 1.0)
{
float line = maskLight;
float odd = 0.0;
if (fract(pos.x*0.166666666) < 0.5) odd = 1.0;
if (fract((pos.y + odd) * 0.5) < 0.5) line = maskDark;
pos.x = fract(pos.x*0.333333333);
if (pos.x < 0.333) mask.r = maskLight;
else if (pos.x < 0.666) mask.g = maskLight;
else mask.b = maskLight;
mask*=line;
}
// Aperture-grille.
else if (shadowMask == 2.0)
{
pos.x = fract(pos.x*0.333333333);
if (pos.x < 0.333) mask.r = maskLight;
else if (pos.x < 0.666) mask.g = maskLight;
else mask.b = maskLight;
}
// Stretched VGA style shadow mask (same as prior shaders).
else if (shadowMask == 3.0)
{
pos.x += pos.y*3.0;
pos.x = fract(pos.x*0.166666666);
if (pos.x < 0.333) mask.r = maskLight;
else if (pos.x < 0.666) mask.g = maskLight;
else mask.b = maskLight;
}
// VGA style shadow mask.
else if (shadowMask == 4.0)
{
pos.xy = floor(pos.xy*vec2(1.0, 0.5));
pos.x += pos.y*3.0;
pos.x = fract(pos.x*0.166666666);
if (pos.x < 0.333) mask.r = maskLight;
else if (pos.x < 0.666) mask.g = maskLight;
else mask.b = maskLight;
}
return mask;
}
void main() {
FragColor = vec4(Bloom(vTexCoord)*bloomAmount, 1.0);
}

View file

@ -0,0 +1,15 @@
#version 150
in vec4 position;
in vec2 texCoord;
out Vertex {
vec2 vTexCoord;
};
uniform vec4 sourceSize[];
void main() {
gl_Position = position;
vTexCoord = texCoord;
}

View file

@ -0,0 +1,18 @@
input
filter: nearest
program
filter: nearest
format: srgb8
height: 100%
width: 100%
vertex: bloompass.vs
fragment: bloompass.fs
program
filter: nearest
vertex: scanpass.vs
fragment: scanpass.fs
output
filter: linear

View file

@ -0,0 +1,317 @@
#version 150
// PUBLIC DOMAIN CRT STYLED SCAN-LINE SHADER
//
// by Timothy Lottes
//
// This is more along the style of a really good CGA arcade monitor.
// With RGB inputs instead of NTSC.
// The shadow mask example has the mask rotated 90 degrees for less chromatic aberration.
//
// Left it unoptimized to show the theory behind the algorithm.
//
// It is an example what I personally would want as a display option for pixel art games.
// Please take and use, change, or whatever.
#define hardScan -8.0
#define hardPix -3.0
#define warpX 0.031
#define warpY 0.041
#define maskDark 0.5
#define maskLight 1.5
#define scaleInLinearGamma 1.0
#define shadowMask 3.0
#define brightBoost 1.0
#define hardBloomPix -1.5
#define hardBloomScan -2.0
#define bloomAmount 0.4
#define shape 2.0
uniform sampler2D source[];
uniform vec4 sourceSize[];
uniform vec4 outputSize;
in Vertex {
vec2 vTexCoord;
};
out vec4 FragColor;
//Uncomment to reduce instructions with simpler linearization
//(fixes HD3000 Sandy Bridge IGP)
//#define SIMPLE_LINEAR_GAMMA
#define DO_BLOOM 1
// ------------- //
// sRGB to Linear.
// Assuming using sRGB typed textures this should not be needed.
#ifdef SIMPLE_LINEAR_GAMMA
float ToLinear1(float c)
{
return c;
}
vec3 ToLinear(vec3 c)
{
return c;
}
vec3 ToSrgb(vec3 c)
{
return pow(c, vec3(1.0 / 2.2));
}
#else
float ToLinear1(float c)
{
if (scaleInLinearGamma == 0)
return c;
return(c<=0.04045) ? c/12.92 : pow((c + 0.055)/1.055, 2.4);
}
vec3 ToLinear(vec3 c)
{
if (scaleInLinearGamma==0)
return c;
return vec3(ToLinear1(c.r), ToLinear1(c.g), ToLinear1(c.b));
}
// Linear to sRGB.
// Assuming using sRGB typed textures this should not be needed.
float ToSrgb1(float c)
{
if (scaleInLinearGamma == 0)
return c;
return(c<0.0031308 ? c*12.92 : 1.055*pow(c, 0.41666) - 0.055);
}
vec3 ToSrgb(vec3 c)
{
if (scaleInLinearGamma == 0)
return c;
return vec3(ToSrgb1(c.r), ToSrgb1(c.g), ToSrgb1(c.b));
}
#endif
// Nearest emulated sample given floating point position and texel offset.
// Also zero's off screen.
vec3 Fetch(vec2 pos,vec2 off){
pos=(floor(pos*sourceSize[0].xy+off)+vec2(0.5,0.5))/sourceSize[0].xy;
#ifdef SIMPLE_LINEAR_GAMMA
return ToLinear(brightBoost * pow(texture(source[1],pos.xy).rgb, vec3(2.2)));
#else
return ToLinear(brightBoost * texture(source[1],pos.xy).rgb);
#endif
}
// Distance in emulated pixels to nearest texel.
vec2 Dist(vec2 pos)
{
pos = pos*sourceSize[0].xy;
return -((pos - floor(pos)) - vec2(0.5));
}
// 1D Gaussian.
float Gaus(float pos, float scale)
{
return exp2(scale*pow(abs(pos), shape));
}
// 3-tap Gaussian filter along horz line.
vec3 Horz3(vec2 pos, float off)
{
vec3 b = Fetch(pos, vec2(-1.0, off));
vec3 c = Fetch(pos, vec2( 0.0, off));
vec3 d = Fetch(pos, vec2( 1.0, off));
float dst = Dist(pos).x;
// Convert distance to weight.
float scale = hardPix;
float wb = Gaus(dst-1.0,scale);
float wc = Gaus(dst+0.0,scale);
float wd = Gaus(dst+1.0,scale);
// Return filtered sample.
return (b*wb+c*wc+d*wd)/(wb+wc+wd);
}
// 5-tap Gaussian filter along horz line.
vec3 Horz5(vec2 pos,float off){
vec3 a = Fetch(pos,vec2(-2.0, off));
vec3 b = Fetch(pos,vec2(-1.0, off));
vec3 c = Fetch(pos,vec2( 0.0, off));
vec3 d = Fetch(pos,vec2( 1.0, off));
vec3 e = Fetch(pos,vec2( 2.0, off));
float dst = Dist(pos).x;
// Convert distance to weight.
float scale = hardPix;
float wa = Gaus(dst - 2.0, scale);
float wb = Gaus(dst - 1.0, scale);
float wc = Gaus(dst + 0.0, scale);
float wd = Gaus(dst + 1.0, scale);
float we = Gaus(dst + 2.0, scale);
// Return filtered sample.
return (a*wa+b*wb+c*wc+d*wd+e*we)/(wa+wb+wc+wd+we);
}
// 7-tap Gaussian filter along horz line.
vec3 Horz7(vec2 pos,float off)
{
vec3 a = Fetch(pos, vec2(-3.0, off));
vec3 b = Fetch(pos, vec2(-2.0, off));
vec3 c = Fetch(pos, vec2(-1.0, off));
vec3 d = Fetch(pos, vec2( 0.0, off));
vec3 e = Fetch(pos, vec2( 1.0, off));
vec3 f = Fetch(pos, vec2( 2.0, off));
vec3 g = Fetch(pos, vec2( 3.0, off));
float dst = Dist(pos).x;
// Convert distance to weight.
float scale = hardBloomPix;
float wa = Gaus(dst - 3.0, scale);
float wb = Gaus(dst - 2.0, scale);
float wc = Gaus(dst - 1.0, scale);
float wd = Gaus(dst + 0.0, scale);
float we = Gaus(dst + 1.0, scale);
float wf = Gaus(dst + 2.0, scale);
float wg = Gaus(dst + 3.0, scale);
// Return filtered sample.
return (a*wa+b*wb+c*wc+d*wd+e*we+f*wf+g*wg)/(wa+wb+wc+wd+we+wf+wg);
}
// Return scanline weight.
float Scan(vec2 pos, float off)
{
float dst = Dist(pos).y;
return Gaus(dst + off, hardScan);
}
// Return scanline weight for bloom.
float BloomScan(vec2 pos, float off)
{
float dst = Dist(pos).y;
return Gaus(dst + off, hardBloomScan);
}
// Allow nearest three lines to effect pixel.
vec3 Tri(vec2 pos)
{
vec3 a = Horz3(pos,-1.0);
vec3 b = Horz5(pos, 0.0);
vec3 c = Horz3(pos, 1.0);
float wa = Scan(pos,-1.0);
float wb = Scan(pos, 0.0);
float wc = Scan(pos, 1.0);
return a*wa + b*wb + c*wc;
}
// Small bloom.
vec3 Bloom(vec2 pos)
{
vec3 a = Horz5(pos,-2.0);
vec3 b = Horz7(pos,-1.0);
vec3 c = Horz7(pos, 0.0);
vec3 d = Horz7(pos, 1.0);
vec3 e = Horz5(pos, 2.0);
float wa = BloomScan(pos,-2.0);
float wb = BloomScan(pos,-1.0);
float wc = BloomScan(pos, 0.0);
float wd = BloomScan(pos, 1.0);
float we = BloomScan(pos, 2.0);
return a*wa+b*wb+c*wc+d*wd+e*we;
}
// Distortion of scanlines, and end of screen alpha.
vec2 Warp(vec2 pos)
{
pos = pos*2.0-1.0;
pos *= vec2(1.0 + (pos.y*pos.y)*warpX, 1.0 + (pos.x*pos.x)*warpY);
return pos*0.5 + 0.5;
}
// Shadow mask.
vec3 Mask(vec2 pos)
{
vec3 mask = vec3(maskDark, maskDark, maskDark);
// Very compressed TV style shadow mask.
if (shadowMask == 1.0)
{
float line = maskLight;
float odd = 0.0;
if (fract(pos.x*0.166666666) < 0.5) odd = 1.0;
if (fract((pos.y + odd) * 0.5) < 0.5) line = maskDark;
pos.x = fract(pos.x*0.333333333);
if (pos.x < 0.333) mask.r = maskLight;
else if (pos.x < 0.666) mask.g = maskLight;
else mask.b = maskLight;
mask*=line;
}
// Aperture-grille.
else if (shadowMask == 2.0)
{
pos.x = fract(pos.x*0.333333333);
if (pos.x < 0.333) mask.r = maskLight;
else if (pos.x < 0.666) mask.g = maskLight;
else mask.b = maskLight;
}
// Stretched VGA style shadow mask (same as prior shaders).
else if (shadowMask == 3.0)
{
pos.x += pos.y*3.0;
pos.x = fract(pos.x*0.166666666);
if (pos.x < 0.333) mask.r = maskLight;
else if (pos.x < 0.666) mask.g = maskLight;
else mask.b = maskLight;
}
// VGA style shadow mask.
else if (shadowMask == 4.0)
{
pos.xy = floor(pos.xy*vec2(1.0, 0.5));
pos.x += pos.y*3.0;
pos.x = fract(pos.x*0.166666666);
if (pos.x < 0.333) mask.r = maskLight;
else if (pos.x < 0.666) mask.g = maskLight;
else mask.b = maskLight;
}
return mask;
}
void main() {
vec2 pos = Warp(vTexCoord);
vec3 outColor = Tri(pos).rgb;
if (shadowMask > 0.0)
outColor.rgb *= Mask(vTexCoord.xy * outputSize.xy * 1.000001);
#ifdef DO_BLOOM
//Add Bloom
outColor.rgb += mix( vec3(0.0), texture(source[0], pos).rgb, bloomAmount);
#endif
FragColor = vec4(ToSrgb(outColor.rgb), 1.0);
}

View file

@ -0,0 +1,15 @@
#version 150
in vec4 position;
in vec2 texCoord;
out Vertex {
vec2 vTexCoord;
};
uniform vec4 sourceSize[];
void main() {
gl_Position = position;
vTexCoord = texCoord;
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,214 @@
input
filter: nearest
// IMPORTANT:
// Shader passes need to know details about the image in the mask_texture LUT
// files, so set the following constants in user-preset-constants.h accordingly:
// 1.) mask_triads_per_tile = (number of horizontal triads in mask texture LUT's)
// 2.) mask_texture_small_size = (texture size of mask*texture_small LUT's)
// 3.) mask_texture_large_size = (texture size of mask*texture_large LUT's)
// 4.) mask_grille_avg_color = (avg. brightness of mask_grille_texture* LUT's, in [0, 1])
// 5.) mask_slot_avg_color = (avg. brightness of mask_slot_texture* LUT's, in [0, 1])
// 6.) mask_shadow_avg_color = (avg. brightness of mask_shadow_texture* LUT's, in [0, 1])
// Shader passes also need to know certain scales set in this preset, but their
// compilation model doesn't currently allow the preset file to tell them. Make
// sure to set the following constants in user-preset-constants.h accordingly too:
// 1.) bloom_approx_scale_x = scale_x2
// 2.) mask_resize_viewport_scale = vec2(scale_x6, scale_y5)
// Finally, shader passes need to know the value of geom_max_aspect_ratio used to
// calculate scale_y5 (among other values):
// 1.) geom_max_aspect_ratio = (geom_max_aspect_ratio used to calculate scale_y5)
// Pass0: Linearize the input based on CRT gamma and bob interlaced fields.
// (Bobbing ensures we can immediately blur without getting artifacts.)
program
filter: nearest
vertex: first-pass-linearize-crt-gamma-bob-fields.vs
fragment: first-pass-linearize-crt-gamma-bob-fields.fs
format: rgba16f
height: 100%
width: 100%
// Pass1: Resample interlaced (and misconverged) scanlines vertically.
// Separating vertical/horizontal scanline sampling is faster: It lets us
// consider more scanlines while calculating weights for fewer pixels, and
// it reduces our samples from vertical*horizontal to vertical+horizontal.
// This has to come right after ORIG_LINEARIZED, because there's no
// "original_source" scale_type we can use later.
program
filter: linear
vertex: scanlines-vertical-interlacing.vs
fragment: scanlines-vertical-interlacing.fs
height: 400%
width: 100%
format: rgba16f
// Pass2: Do a small resize blur of ORIG_LINEARIZED at an absolute size, and
// account for convergence offsets. We want to blur a predictable portion of the
// screen to match the phosphor bloom, and absolute scale works best for
// reliable results with a fixed-size bloom. Picking a scale is tricky:
// a.) 400x300 is a good compromise for the "fake-bloom" version: It's low enough
// to blur high-res/interlaced sources but high enough that resampling
// doesn't smear low-res sources too much.
// b.) 320x240 works well for the "real bloom" version: It's 1-1.5% faster, and
// the only noticeable visual difference is a larger halation spread (which
// may be a good thing for people who like to crank it up).
// Note the 4:3 aspect ratio assumes the input has cropped geom_overscan (so it's
// *intended* for an ~4:3 aspect ratio).
program
filter: linear
vertex: bloom-approx.vs
fragment: bloom-approx.fs
format: rgba16f
width: 320 px
height: 240 px
// Pass3: Vertically blur the input for halation and refractive diffusion.
// Base this on BLOOM_APPROX: This blur should be small and fast, and blurring
// a constant portion of the screen is probably physically correct if the
// viewport resolution is proportional to the simulated CRT size.
program
filter: linear
vertex: blur9fast-vertical.vs
fragment: blur9fast-vertical.fs
format: rgba16f
height: 100%
width: 100%
// Pass4: Horizontally blur the input for halation and refractive diffusion.
// Note: Using a one-pass 9x9 blur is about 1% slower.
program
filter: linear
vertex: blur9fast-horizontal.vs
fragment: blur9fast-horizontal.fs
format: rgba16f
height: 100%
width: 100%
// Pass5: Lanczos-resize the phosphor mask vertically. Set the absolute
// scale_x5 == mask_texture_small_size.x (see IMPORTANT above). Larger scales
// will blur, and smaller scales could get nasty. The vertical size must be
// based on the viewport size and calculated carefully to avoid artifacts later.
// First calculate the minimum number of mask tiles we need to draw.
// Since curvature is computed after the scanline masking pass:
// num_resized_mask_tiles = 2.0;
// If curvature were computed in the scanline masking pass (it's not):
// max_mask_texel_border = ~3.0 * (1/3.0 + 4.0*sqrt(2.0) + 0.5 + 1.0);
// max_mask_tile_border = max_mask_texel_border/
// (min_resized_phosphor_triad_size * mask_triads_per_tile);
// num_resized_mask_tiles = max(2.0, 1.0 + max_mask_tile_border * 2.0);
// At typical values (triad_size >= 2.0, mask_triads_per_tile == 8):
// num_resized_mask_tiles = ~3.8
// Triad sizes are given in horizontal terms, so we need geom_max_aspect_ratio
// to relate them to vertical resolution. The widest we expect is:
// geom_max_aspect_ratio = 4.0/3.0 // Note: Shader passes need to know this!
// The fewer triads we tile across the screen, the larger each triad will be as a
// fraction of the viewport size, and the larger scale_y5 must be to draw a full
// num_resized_mask_tiles. Therefore, we must decide the smallest number of
// triads we'll guarantee can be displayed on screen. We'll set this according
// to 3-pixel triads at 768p resolution (the lowest anyone's likely to use):
// min_allowed_viewport_triads = 768.0*geom_max_aspect_ratio / 3.0 = 341.333333
// Now calculate the viewport scale that ensures we can draw resized_mask_tiles:
// min_scale_x = resized_mask_tiles * mask_triads_per_tile /
// min_allowed_viewport_triads
// scale_y5 = geom_max_aspect_ratio * min_scale_x
// # Some code might depend on equal scales:
// scale_x6 = scale_y5
// Given our default geom_max_aspect_ratio and min_allowed_viewport_triads:
// scale_y5 = 4.0/3.0 * 2.0/(341.33333 / 8.0) = 0.0625
// IMPORTANT: The scales MUST be calculated in this way. If you wish to change
// geom_max_aspect_ratio, update that constant in user-preset-constants.h!
program
filter: linear
format: rgba16f
width: 64 px
height: 6.25%
vertex: mask-resize-vertical.vs
fragment: mask-resize-vertical.fs
pixmap: textures/TileableLinearApertureGrille15Wide8And5d5SpacingResizeTo64.png
filter: linear
wrap: repeat
pixmap: textures/TileableLinearApertureGrille15Wide8And5d5Spacing.png
filter: linear
wrap: repeat
pixmap: textures/TileableLinearSlotMaskTall15Wide9And4d5Horizontal9d14VerticalSpacingResizeTo64.png
filter: linear
wrap: repeat
pixmap: textures/TileableLinearSlotMaskTall15Wide9And4d5Horizontal9d14VerticalSpacing.png
filter: linear
wrap: repeat
pixmap: textures/TileableLinearShadowMaskEDPResizeTo64.png
filter: linear
wrap: repeat
pixmap: textures/TileableLinearShadowMaskEDP.png
filter: linear
wrap: repeat
// Pass6: Lanczos-resize the phosphor mask horizontally. scale_x6 = scale_y5.
// TODO: Check again if the shaders actually require equal scales.
program
filter: nearest
vertex: mask-resize-horizontal.vs
fragment: mask-resize-horizontal.fs
format: rgba16f
// Pass7: Resample (misconverged) scanlines horizontally, apply halation, and
// apply the phosphor mask.
program
filter: linear
format: rgba16f
height: 100%
width: 100%
vertex: scanlines-horizontal-apply-mask.vs
fragment: scanlines-horizontal-apply-mask.fs
pixmap: textures/TileableLinearApertureGrille15Wide8And5d5SpacingResizeTo64.png
filter: linear
wrap: repeat
pixmap: textures/TileableLinearApertureGrille15Wide8And5d5Spacing.png
filter: linear
wrap: repeat
pixmap: textures/TileableLinearSlotMaskTall15Wide9And4d5Horizontal9d14VerticalSpacingResizeTo64.png
filter: linear
wrap: repeat
pixmap: textures/TileableLinearSlotMaskTall15Wide9And4d5Horizontal9d14VerticalSpacing.png
filter: linear
wrap: repeat
pixmap: textures/TileableLinearShadowMaskEDPResizeTo64.png
filter: linear
wrap: repeat
pixmap: textures/TileableLinearShadowMaskEDP.png
filter: linear
wrap: repeat
// Pass 8: Compute a brightpass. This will require reading the final mask.
program
filter: linear
format: rgba16f
vertex: brightpass.vs
fragment: brightpass.fs
// Pass 9: Blur the brightpass vertically
program
filter: linear
format: rgba16f
vertex: bloom-vertical.vs
fragment: bloom-vertical.fs
// Pass 10: Blur the brightpass horizontally and combine it with the dimpass:
program
filter: linear
format: rgba16f
height: 100%
width: 100%
vertex: bloom-horizontal-reconstitute.vs
fragment: bloom-horizontal-reconstitute.fs
// Pass 11: Compute curvature/AA:
program
filter: linear
format: rgba16f
vertex: geometry-aa-last-pass.vs
fragment: geometry-aa-last-pass.fs
output
filter: nearest

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 194 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 214 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 202 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 200 KiB

View file

@ -0,0 +1,52 @@
#version 150
////////////////////////////////////////////////////////
// GTU version 0.40
// Author: aliaspider - aliaspider@gmail.com
// License: GPLv3
////////////////////////////////////////////////////////
////////////////////////////////////////////////////////
// SETTINGS
////////////////////////////////////////////////////////
//#define CROP_OVERSCAN
#define TV_COLOR_LEVELS
//#define COMPOSITE_CONNECTION
//#define NO_SCANLINES
#define TV_HORIZONTAL_RESOLUTION 400.0
#define TV_VERTICAL_RESOLUTION 300.0
#define SIGNAL_RESOLUTION 280.0
#define SIGNAL_RESOLUTION_I 83.0
#define SIGNAL_RESOLUTION_Q 25.0
#define TV_DISPLAY_GAMMA 2.4
#define OUTPUT_DISPLAY_GAMMA 2.2
////////////////////////////////////////////////////////
//
////////////////////////////////////////////////////////
#define RGB_to_YIQ mat3x3( 0.299 , 0.595716 , 0.211456 , 0.587 , -0.274453 , -0.522591 , 0.114 , -0.321263 , 0.311135 )
#define YIQ_to_RGB mat3x3( 1.0 , 1.0 , 1.0 , 0.9563 , -0.2721 , -1.1070 , 0.6210 , -0.6474 , 1.7046 )
#define LEVELS(C) clamp((C -16/ 255.0)*255.0/(235.0-16.0),0.0,1.0)
uniform sampler2D source[];
uniform vec4 sourceSize[];
in Vertex {
vec2 texCoord;
};
out vec4 fragColor;
void main() {
vec3 c=texture2D(source[0], texCoord.xy).xyz;
#ifdef TV_COLOR_LEVELS
c=LEVELS(c);
#endif
#ifdef COMPOSITE_CONNECTION
c=RGB_to_YIQ*c;
#endif
fragColor = vec4(c, 1.0);
}

View file

@ -0,0 +1,77 @@
#version 150
////////////////////////////////////////////////////////
// GTU version 0.40
// Author: aliaspider - aliaspider@gmail.com
// License: GPLv3
////////////////////////////////////////////////////////
////////////////////////////////////////////////////////
// SETTINGS
////////////////////////////////////////////////////////
//#define CROP_OVERSCAN
#define TV_COLOR_LEVELS
//#define COMPOSITE_CONNECTION
//#define NO_SCANLINES
#define TV_HORIZONTAL_RESOLUTION 400.0
#define TV_VERTICAL_RESOLUTION 300.0
#define SIGNAL_RESOLUTION 280.0
#define SIGNAL_RESOLUTION_I 83.0
#define SIGNAL_RESOLUTION_Q 25.0
#define TV_DISPLAY_GAMMA 2.4
#define OUTPUT_DISPLAY_GAMMA 2.2
////////////////////////////////////////////////////////
//
////////////////////////////////////////////////////////
#define YIQ_to_RGB mat3x3( 1.0 , 1.0 , 1.0 , 0.9563 , -0.2721 , -1.1070 , 0.6210 , -0.6474 , 1.7046 )
#define pi 3.14159265358
#define a(x) abs(x)
#define d(x,b) (pi*b*min(a(x)+0.5,1.0/b))
#define e(x,b) (pi*b*min(max(a(x)-0.5,-1.0/b),1.0/b))
#define STU(x,b) ((d(x,b)+sin(d(x,b))-e(x,b)-sin(e(x,b)))/(2.0*pi))
#define X(i) (offset-(i))
#define GETC (texture(source[0], vec2(texCoord.x - X*sourceSize[0].z,texCoord.y)).xyz)
#ifdef COMPOSITE_CONNECTION
#define VAL vec3((c.x*STU(X,(SIGNAL_RESOLUTION*sourceSize[0].z))),(c.y*STU(X,(SIGNAL_RESOLUTION_I*sourceSize[0].z))),(c.z*STU(X,(SIGNAL_RESOLUTION_Q*sourceSize[0].z))))
#else
#define VAL (c*STU(X,(SIGNAL_RESOLUTION*sourceSize[0].z)))
#endif //COMPOSITE_CONNECTION
#define PROCESS(i) X=X(i);c=GETC;tempColor+=VAL;
uniform sampler2D source[];
uniform vec4 sourceSize[];
in Vertex {
vec2 texCoord;
};
out vec4 fragColor;
void main() {
float offset = fract((texCoord.x * sourceSize[0].x) - 0.5);
vec3 tempColor = vec3(0.0);
float X;
vec3 c;
PROCESS(-6)PROCESS(-5)PROCESS(-4)PROCESS(-3)PROCESS(-2)PROCESS(-1)PROCESS( 0)
PROCESS( 7)PROCESS( 6)PROCESS( 5)PROCESS( 4)PROCESS( 3)PROCESS( 2)PROCESS( 1)
#ifdef COMPOSITE_CONNECTION
tempColor=clamp(YIQ_to_RGB*tempColor,0.0,1.0);
#endif
tempColor=clamp(pow(tempColor,vec3(TV_DISPLAY_GAMMA)),0.0,1.0);
fragColor = vec4(tempColor,1.0);
}

View file

@ -0,0 +1,46 @@
#version 150
////////////////////////////////////////////////////////
// GTU version 0.40
// Author: aliaspider - aliaspider@gmail.com
// License: GPLv3
////////////////////////////////////////////////////////
////////////////////////////////////////////////////////
// SETTINGS
////////////////////////////////////////////////////////
//#define CROP_OVERSCAN
#define TV_COLOR_LEVELS
//#define COMPOSITE_CONNECTION
//#define NO_SCANLINES
#define TV_HORIZONTAL_RESOLUTION 400.0
#define TV_VERTICAL_RESOLUTION 300.0
#define SIGNAL_RESOLUTION 280.0
#define SIGNAL_RESOLUTION_I 83.0
#define SIGNAL_RESOLUTION_Q 25.0
#define TV_DISPLAY_GAMMA 2.4
#define OUTPUT_DISPLAY_GAMMA 2.2
////////////////////////////////////////////////////////
//
////////////////////////////////////////////////////////
in vec4 position;
in vec2 texCoord;
out Vertex{
vec2 texCoord;
}vertexOut;
void main(void){
#ifdef CROP_OVERSCAN
gl_Position=position;
gl_Position.x/=(224.0/240.0);
#else
gl_Position=position;
#endif
vertexOut.texCoord=texCoord;
}

View file

@ -0,0 +1,63 @@
#version 150
////////////////////////////////////////////////////////
// GTU version 0.40
// Author: aliaspider - aliaspider@gmail.com
// License: GPLv3
////////////////////////////////////////////////////////
////////////////////////////////////////////////////////
// SETTINGS
////////////////////////////////////////////////////////
//#define CROP_OVERSCAN
#define TV_COLOR_LEVELS
//#define COMPOSITE_CONNECTION
//#define NO_SCANLINES
#define TV_HORIZONTAL_RESOLUTION 400.0
#define TV_VERTICAL_RESOLUTION 300.0
#define SIGNAL_RESOLUTION 280.0
#define SIGNAL_RESOLUTION_I 83.0
#define SIGNAL_RESOLUTION_Q 25.0
#define TV_DISPLAY_GAMMA 2.4
#define OUTPUT_DISPLAY_GAMMA 2.2
////////////////////////////////////////////////////////
//
////////////////////////////////////////////////////////
#define pi 3.14159265358
#define a(x) abs(x)
#define d(x,b) (pi*b*min(a(x)+0.5,1.0/b))
#define e(x,b) (pi*b*min(max(a(x)-0.5,-1.0/b),1.0/b))
#define STU(x,b) ((d(x,b)+sin(d(x,b))-e(x,b)-sin(e(x,b)))/(2.0*pi))
#define X(i) (offset-(i))
#define GETC (texture(source[0], vec2(texCoord.x - X*sourceSize[0].z,texCoord.y)).xyz)
#define VAL (c*STU(X,(TV_HORIZONTAL_RESOLUTION*sourceSize[0].z)))
#define PROCESS(i) X=X(i);c=GETC;tempColor+=VAL;
uniform sampler2D source[];
uniform vec4 sourceSize[];
in Vertex {
vec2 texCoord;
};
out vec4 fragColor;
void main() {
float offset = fract((texCoord.x * sourceSize[0].x) - 0.5);
vec3 tempColor = vec3(0.0);
float X;
vec3 c;
PROCESS(-6)PROCESS(-5)PROCESS(-4)PROCESS(-3)PROCESS(-2)PROCESS(-1)PROCESS( 0)
PROCESS( 7)PROCESS( 6)PROCESS( 5)PROCESS( 4)PROCESS( 3)PROCESS( 2)PROCESS( 1)
// tempColor=pow(tempColor,vec3(1.0/2.2));
fragColor = vec4(tempColor,1.0);
}

View file

@ -0,0 +1,78 @@
#version 150
////////////////////////////////////////////////////////
// GTU version 0.40
// Author: aliaspider - aliaspider@gmail.com
// License: GPLv3
////////////////////////////////////////////////////////
////////////////////////////////////////////////////////
// SETTINGS
////////////////////////////////////////////////////////
//#define CROP_OVERSCAN
#define TV_COLOR_LEVELS
//#define COMPOSITE_CONNECTION
//#define NO_SCANLINES
#define TV_HORIZONTAL_RESOLUTION 400.0
#define TV_VERTICAL_RESOLUTION 300.0
#define SIGNAL_RESOLUTION 280.0
#define SIGNAL_RESOLUTION_I 83.0
#define SIGNAL_RESOLUTION_Q 25.0
#define TV_DISPLAY_GAMMA 2.4
#define OUTPUT_DISPLAY_GAMMA 2.2
////////////////////////////////////////////////////////
//
////////////////////////////////////////////////////////
#define SCANLINE_WIDTH (1.5*sourceSize[0].y/TV_VERTICAL_RESOLUTION)
uniform sampler2D source[];
uniform sampler2D texture[];
uniform vec4 sourceSize[];
in Vertex {
vec2 texCoord;
};
out vec4 fragColor;
#define GAMMAOUT(c0) (pow(c0, vec3(1.0/OUTPUTG2)))
#define pi 3.14159265358
#define GAUSS(x,w) ((sqrt(2.0) / (w)) * (exp((-2.0 * pi * (x) * (x)) / ((w) * (w)))))
#define Y(j) (offset.y-(j))
#define a(x) abs(x)
#define d(x,b) (pi*b*min(a(x)+0.5,1.0/b))
#define e(x,b) (pi*b*min(max(a(x)-0.5,-1.0/b),1.0/b))
#define STU(x,b) ((d(x,b)+sin(d(x,b))-e(x,b)-sin(e(x,b)))/(2.0*pi))
#define SOURCE(j) vec2(texCoord.x,texCoord.y - Y(j)*sourceSize[0].w)
#define C(j) (texture2D(source[0], SOURCE(j)).xyz)
#ifdef NO_SCANLINES
#define VAL(j) (C(j)*STU(Y(j),(TV_VERTICAL_RESOLUTION*sourceSize[0].w)))
#else
#define VAL(j) (C(j)*GAUSS(Y(j),SCANLINE_WIDTH))
#endif
void main() {
vec2 offset = fract((texCoord.xy * sourceSize[0].xy) - 0.5);
vec3 tempColor = vec3(0.0);
tempColor+=VAL(-3.0);
tempColor+=VAL(-2.0);
tempColor+=VAL(-1.0);
tempColor+=VAL(0.0);
tempColor+=VAL(1.0);
tempColor+=VAL(2.0);
tempColor+=VAL(3.0);
tempColor+=VAL(4.0);
fragColor = vec4(pow(tempColor,vec3(1.0/OUTPUT_DISPLAY_GAMMA)), 1.0);
}

View file

@ -0,0 +1,46 @@
#version 150
////////////////////////////////////////////////////////
// GTU version 0.40
// Author: aliaspider - aliaspider@gmail.com
// License: GPLv3
////////////////////////////////////////////////////////
////////////////////////////////////////////////////////
// SETTINGS
////////////////////////////////////////////////////////
//#define CROP_OVERSCAN
#define TV_COLOR_LEVELS
//#define COMPOSITE_CONNECTION
//#define NO_SCANLINES
#define TV_HORIZONTAL_RESOLUTION 400.0
#define TV_VERTICAL_RESOLUTION 300.0
#define SIGNAL_RESOLUTION 280.0
#define SIGNAL_RESOLUTION_I 83.0
#define SIGNAL_RESOLUTION_Q 25.0
#define TV_DISPLAY_GAMMA 2.4
#define OUTPUT_DISPLAY_GAMMA 2.2
////////////////////////////////////////////////////////
//
////////////////////////////////////////////////////////
in vec4 position;
in vec2 texCoord;
out Vertex{
vec2 texCoord;
}vertexOut;
void main(void){
#ifdef CROP_OVERSCAN
gl_Position=position;
gl_Position.y/=(224.0/240.0);
#else
gl_Position=position;
#endif
vertexOut.texCoord=texCoord;
}

View file

@ -0,0 +1,28 @@
program
width: 100%
height: 100%
format: rgba32f
filter: nearest
wrap: edge
fragment: GTU-pass1.fs
program
height: 100%
format: rgba32f
filter: nearest
wrap: edge
vertex: GTU-pass2.vs
fragment: GTU-pass2.fs
program
height: 100%
format: rgba32f
filter: nearest
wrap: edge
fragment: GTU-pass3.fs
program
format: rgba8
filter: nearest
wrap: edge
vertex: GTU-pass4.vs
fragment: GTU-pass4.fs
output
filter: nearest

View file

@ -0,0 +1,88 @@
#version 150
#in red
#in green
#in blue
#in gain
#in gamma
#in blacklevel
#in ambient
#in BGR
#define outgamma 2.2
uniform sampler2D source[];
uniform vec4 sourceSize[];
uniform vec4 targetSize;
in Vertex {
vec2 texCoord;
};
out vec4 fragColor;
#define fetch_offset(coord,offset) (pow(vec3(gain) * texelFetchOffset(source[0], (coord), 0, (offset)).rgb + vec3(blacklevel), vec3(gamma)) + vec3(ambient))
// integral of (1 - x^2 - x^4 + x^6)^2
const float coeffs_x[] = float[](1.0, -2.0/3.0, -1.0/5.0, 4.0/7.0, -1.0/9.0, -2.0/11.0, 1.0/13.0);
// integral of (1 - 2x^4 + x^6)^2
const float coeffs_y[] = float[](1.0, 0.0, -4.0/5.0, 2.0/7.0, 4.0/9.0, -4.0/11.0, 1.0/13.0);
float intsmear_func(float z, float coeffs[7])
{
float z2 = z*z;
float zn = z;
float ret = 0.0;
for (int i = 0; i < 7; i++) {
ret += zn*coeffs[i];
zn *= z2;
}
return ret;
}
float intsmear(float x, float dx, float d, float coeffs[7])
{
float zl = clamp((x-dx*0.5)/d,-1.0,1.0);
float zh = clamp((x+dx*0.5)/d,-1.0,1.0);
return d * ( intsmear_func(zh,coeffs) - intsmear_func(zl,coeffs) )/dx;
}
void main()
{
vec2 texelSize = 1.0 / sourceSize[0].xy;
vec2 range = sourceSize[0].xy / (targetSize.xy * sourceSize[0].xy);
vec3 cred = pow(red, vec3(outgamma));
vec3 cgreen = pow(green, vec3(outgamma));
vec3 cblue = pow(blue, vec3(outgamma));
ivec2 tli = ivec2(floor(texCoord/texelSize-vec2(0.4999)));
vec3 lcol, rcol;
float subpix = (texCoord.x/texelSize.x - 0.4999 - float(tli.x))*3.0;
float rsubpix = range.x/texelSize.x * 3.0;
lcol = vec3(intsmear(subpix+1.0,rsubpix, 1.5, coeffs_x),
intsmear(subpix ,rsubpix, 1.5, coeffs_x),
intsmear(subpix-1.0,rsubpix, 1.5, coeffs_x));
rcol = vec3(intsmear(subpix-2.0,rsubpix, 1.5, coeffs_x),
intsmear(subpix-3.0,rsubpix, 1.5, coeffs_x),
intsmear(subpix-4.0,rsubpix, 1.5, coeffs_x));
#ifdef BGR
lcol.rgb = lcol.bgr;
rcol.rgb = rcol.bgr;
#endif
float tcol, bcol;
subpix = texCoord.y/texelSize.y - 0.4999 - float(tli.y);
rsubpix = range.y/texelSize.y;
tcol = intsmear(subpix ,rsubpix, 0.63, coeffs_y);
bcol = intsmear(subpix-1.0,rsubpix, 0.63, coeffs_y);
vec3 topLeftColor = fetch_offset(tli, ivec2(0,0)) * lcol * vec3(tcol);
vec3 bottomRightColor = fetch_offset(tli, ivec2(1,1)) * rcol * vec3(bcol);
vec3 bottomLeftColor = fetch_offset(tli, ivec2(0,1)) * lcol * vec3(bcol);
vec3 topRightColor = fetch_offset(tli, ivec2(1,0)) * rcol * vec3(tcol);
vec3 averageColor = topLeftColor + bottomRightColor + bottomLeftColor + topRightColor;
averageColor = mat3x3(cred, cgreen, cblue) * averageColor;
fragColor = vec4(pow(averageColor,vec3(1.0/outgamma)),0.0);
}

View file

@ -0,0 +1,23 @@
settings
persistence: 0.5
red: vec3(1,0,0)
green: vec3(0,1,0)
blue: vec3(0,0,1)
gain: 1.0
gamma: 3.0
blacklevel: 0.05
ambient: 0.0
BGR
input
history: 8
filter: nearest
program
fragment: motion-blur.fs
filter: nearest
width: 100%
height: 100%
program
fragment: lcd-grid.fs

View file

@ -0,0 +1,23 @@
#version 150
uniform sampler2D source[];
uniform sampler2D history[];
in Vertex {
vec2 texCoord;
};
out vec4 fragColor;
void main() {
vec4 color = pow(texture(history[7], texCoord).rgba, vec4(2.2));
color = (color + pow(texture(history[6], texCoord).rgba, vec4(2.2))) / 2.0;
color = (color + pow(texture(history[5], texCoord).rgba, vec4(2.2))) / 2.0;
color = (color + pow(texture(history[4], texCoord).rgba, vec4(2.2))) / 2.0;
color = (color + pow(texture(history[3], texCoord).rgba, vec4(2.2))) / 2.0;
color = (color + pow(texture(history[2], texCoord).rgba, vec4(2.2))) / 2.0;
color = (color + pow(texture(history[1], texCoord).rgba, vec4(2.2))) / 2.0;
color = (color + pow(texture(history[0], texCoord).rgba, vec4(2.2))) / 2.0;
color = (color + pow(texture(source[0], texCoord).rgba, vec4(2.2))) / 2.0;
fragColor = pow(color, vec4(1.0 / 2.2));
}

View file

@ -0,0 +1,24 @@
program
width: 100%
height: 100%
filter: nearest
fragment: mdapt-pass1.fs
wrap: edge
program
width: 100%
height: 100%
filter: nearest
fragment: mdapt-pass2.fs
wrap: edge
program
width: 100%
height: 100%
filter: nearest
fragment: mdapt-pass3.fs
wrap: edge
program
width: 100%
height: 100%
filter: nearest
fragment: mdapt-pass4.fs
wrap: edge

View file

@ -0,0 +1,200 @@
// This is a port of the original CG shader to the quark format
// the original shader can be found here :
// https://github.com/libretro/common-shaders/tree/master/dithering/mdapt-4p
/*
Merge Dithering and Pseudo Transparency Shader v1.5 - Pass 1
by Sp00kyFox, 2013
Finds specific patterns and tags their central pixel.
*/
#version 150
//#define HORI
//#define VERT
#define dtt vec3(65536,255,1)
#define eq_threshold 5.0
uniform sampler2D source[];
uniform vec4 sourceSize[];
uniform vec4 targetSize;
in Vertex{
vec2 texCoord;
};
out vec4 fragColor;
float reduce(vec3 color)
{
return dot(color, dtt);
}
float df(float A, float B)
{
return abs(A-B);
}
bool eq(float A, float B)
{
return (A == B);
}
float remapTo01(float v, float low, float high)
{
return clamp((v - low)/(high-low),0.0,1.0);
}
void main(void) {
vec2 pos = texCoord*sourceSize[0].xy; // pos = pixel position
vec2 dir = sign(pos); // dir = pixel direction
vec2 g1 = dir*vec2(sourceSize[0].z,0.0);
vec2 g2 = dir*vec2(0.0,sourceSize[0].w);;
/*
U3
UUL U2 UUR
ULL UL U1 UR URR
L3 L2 L1 C R1 R2 R3
DLL DL D1 DR DRR
DDL D2 DDR
D3
*/
vec3 c = texture(source[0], texCoord).xyz;
vec3 l1 = texture(source[0], texCoord - g1).xyz;
vec3 l2 = texture(source[0], texCoord - 2*g1).xyz;
vec3 r1 = texture(source[0], texCoord + g1).xyz;
vec3 r2 = texture(source[0], texCoord + 2*g1).xyz;
vec3 u1 = texture(source[0], texCoord - g2).xyz;
vec3 u2 = texture(source[0], texCoord - 2*g2).xyz;
vec3 d1 = texture(source[0], texCoord + g2).xyz;
vec3 d2 = texture(source[0], texCoord + 2*g2).xyz;
vec3 ul = texture(source[0], texCoord - g1 - g2).xyz;
vec3 ur = texture(source[0], texCoord + g1 - g2).xyz;
vec3 dl = texture(source[0], texCoord - g1 + g2).xyz;
vec3 dr = texture(source[0], texCoord + g1 + g2).xyz;
vec3 ull = texture(source[0], texCoord - 2*g1 - g2).xyz;
vec3 uul = texture(source[0], texCoord - g1 - 2*g2).xyz;
vec3 uur = texture(source[0], texCoord + g1 - 2*g2).xyz;
vec3 urr = texture(source[0], texCoord + 2*g1 - g2).xyz;
vec3 drr = texture(source[0], texCoord + 2*g1 + g2).xyz;
vec3 ddr = texture(source[0], texCoord + g1 + 2*g2).xyz;
vec3 ddl = texture(source[0], texCoord - g1 + 2*g2).xyz;
vec3 dll = texture(source[0], texCoord - 2*g1 + g2).xyz;
vec3 l3 = texture(source[0], texCoord - 3*g1).xyz;
vec3 r3 = texture(source[0], texCoord + 3*g1).xyz;
vec3 u3 = texture(source[0], texCoord - 3*g2).xyz;
vec3 d3 = texture(source[0], texCoord + 3*g2).xyz;
float C = reduce( c );
float L1 = reduce( l1 ); float U1 = reduce( u1 );
float L2 = reduce( l2 ); float U2 = reduce( u2 );
float R1 = reduce( r1 ); float D1 = reduce( d1 );
float R2 = reduce( r2 ); float D2 = reduce( d2 );
float UL = reduce( ul ); float L3 = reduce( l3 );
float UR = reduce( ur ); float R3 = reduce( r3 );
float DL = reduce( dl ); float U3 = reduce( u3 );
float DR = reduce( dr ); float D3 = reduce( d3 );
float ULL = reduce( ull ); float DRR = reduce( drr );
float UUL = reduce( uul ); float DDR = reduce( ddr );
float UUR = reduce( uur ); float DDL = reduce( ddl );
float URR = reduce( urr ); float DLL = reduce( dll );
/*
tag values:
0 nothing
checkerboard pattern
9 DL
8 DR
7 UR
6 UL
5 full
horizontal two-line checkerboard
4 bottom line
3 upper line
vertical two-line checkerboard
2 left line
1 right line
one line dither
-1 horizontal
-2 vertical
*/
float type=0;
// checkerboard pattern
if(!eq(C,D1) && !eq(C,U1) && !eq(C,L1) && !eq(C,R1))
{
if(eq(C,UL))
{
if(eq(C,UR))
{
if(eq(C,DR))
{
if(eq(C,DL))
type = 5;
else if(!eq(D1,L1) || eq(D1,DL))
type = 9;
}
else
{
if(eq(C,DL) && (!eq(D1,R1) || eq(D1,DR)))
type = 8;
}
}
else
{
if(eq(C,DR) && eq(C,DL) && (!eq(U1,R1) || eq(U1,UR)))
type = 7;
}
}
else if(eq(C,UR) && eq(C,DR) && eq(C,DL) && (!eq(U1,L1) || eq(U1,UL)))
type = 6;
}
// horizontal two-line checkerboard
else if(eq(C,L2) && eq(C,R2) && eq(C,UL) && eq(C,UR) && !eq(C,L1) && !eq(C,R1) && !eq(C,ULL) && !eq(C,U1) && !eq(C,URR))
{
type = 4;
}
else if(eq(C,L2) && eq(C,R2) && eq(C,DL) && eq(C,DR) && !eq(C,L1) && !eq(C,R1) && !eq(C,DLL) && !eq(C,D1) && !eq(C,DRR))
{
type = 3;
}
// vertical two-line checkerboard
else if(eq(C,U2) && eq(C,D2) && eq(C,UR) && eq(C,DR) && !eq(C,U1) && !eq(C,D1) && !eq(C,R1) && !eq(C,UUR) && !eq(C,DDR))
{
type = 2;
}
else if(eq(C,U2) && eq(C,D2) && eq(C,UL) && eq(C,DL) && !eq(C,U1) && !eq(C,D1) && !eq(C,L1) && !eq(C,UUL) && !eq(C,DDL))
{
type = 1;
}
#ifdef HORI
// horizontal one line dither
else if(eq(C,L2) && eq(C,R2) && eq(L1,R1) && !eq(C,L1) && !eq(C,L3) && !eq(C,R3))
type = -1;
#endif
#ifdef VERT
// vertical one line dither
else if(eq(C,U2) && eq(C,D2) && eq(U1,D1) && !eq(C,U1) && !eq(C,U3) && !eq(C,D3))
type = -2;
#endif
fragColor=vec4(c, remapTo01(type+2, 0, 15));
}

Some files were not shown because too many files have changed in this diff Show more