mirror of https://github.com/bsnes-emu/bsnes.git
Filter support for Cocoa port + 7 basic filters
This commit is contained in:
parent
dde983db8f
commit
8d59bfcbdd
|
@ -24,6 +24,8 @@
|
|||
@"GBStart": @"\r",
|
||||
|
||||
@"GBTurbo": @" ",
|
||||
|
||||
@"GBFilter": @"NearestNeighbor",
|
||||
}];
|
||||
#undef KEY
|
||||
}
|
||||
|
|
|
@ -41,7 +41,7 @@ static char *consoleInput(GB_gameboy_t *gb)
|
|||
|
||||
static uint32_t rgbEncode(GB_gameboy_t *gb, unsigned char r, unsigned char g, unsigned char b)
|
||||
{
|
||||
return (r << 24) | (g << 16) | (b << 8);
|
||||
return (r << 0) | (g << 8) | (b << 16);
|
||||
}
|
||||
|
||||
@implementation Document
|
||||
|
|
|
@ -2,4 +2,5 @@
|
|||
|
||||
@interface GBPreferencesWindow : NSWindow <NSTableViewDelegate, NSTableViewDataSource>
|
||||
@property IBOutlet NSTableView *controlsTableView;
|
||||
@property IBOutlet NSPopUpButton *graphicsFilterPopupButton;
|
||||
@end
|
||||
|
|
|
@ -6,6 +6,38 @@
|
|||
{
|
||||
bool is_button_being_modified;
|
||||
NSInteger button_being_modified;
|
||||
|
||||
NSPopUpButton *_graphicsFilterPopupButton;
|
||||
}
|
||||
|
||||
+ (NSArray *)filterList
|
||||
{
|
||||
/* The filter list as ordered in the popup button */
|
||||
static NSArray * filters = nil;
|
||||
if (!filters) {
|
||||
filters = @[
|
||||
@"NearestNeighbor",
|
||||
@"Bilinear",
|
||||
@"SmoothBilinear",
|
||||
@"Scale2x",
|
||||
@"Scale4x",
|
||||
@"AAScale2x",
|
||||
@"AAScale4x",
|
||||
];
|
||||
}
|
||||
return filters;
|
||||
}
|
||||
|
||||
- (NSPopUpButton *)graphicsFilterPopupButton
|
||||
{
|
||||
return _graphicsFilterPopupButton;
|
||||
}
|
||||
|
||||
- (void)setGraphicsFilterPopupButton:(NSPopUpButton *)graphicsFilterPopupButton
|
||||
{
|
||||
_graphicsFilterPopupButton = graphicsFilterPopupButton;
|
||||
NSString *filter = [[NSUserDefaults standardUserDefaults] objectForKey:@"GBFilter"];
|
||||
[_graphicsFilterPopupButton selectItemAtIndex:[[[self class] filterList] indexOfObject:filter]];
|
||||
}
|
||||
|
||||
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView
|
||||
|
@ -56,5 +88,12 @@
|
|||
[self.controlsTableView reloadData];
|
||||
[self makeFirstResponder:self.controlsTableView];
|
||||
}
|
||||
- (IBAction)graphicFilterChanged:(NSPopUpButton *)sender
|
||||
{
|
||||
|
||||
[[NSUserDefaults standardUserDefaults] setObject:[[self class] filterList][[sender indexOfSelectedItem]]
|
||||
forKey:@"GBFilter"];
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:@"GBFilterChanged" object:nil];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface GBShader : NSObject
|
||||
- (instancetype)initWithName:(NSString *) shaderName;
|
||||
- (void) renderBitmap: (void *)bitmap previous:(void*) previous inSize:(NSSize)size scale: (double) scale;
|
||||
@end
|
|
@ -0,0 +1,170 @@
|
|||
#import "GBShader.h"
|
||||
#import <OpenGL/gl.h>
|
||||
|
||||
/*
|
||||
Loosely based of https://www.raywenderlich.com/70208/opengl-es-pixel-shaders-tutorial
|
||||
*/
|
||||
|
||||
static NSString * const vertex_shader = @"\n\
|
||||
attribute vec2 aPosition;\n\
|
||||
\n\
|
||||
void main(void) {\n\
|
||||
gl_Position = vec4(aPosition, 0., 1.);\n\
|
||||
}\n\
|
||||
";
|
||||
|
||||
@implementation GBShader
|
||||
{
|
||||
GLuint resolution_uniform;
|
||||
GLuint texture_uniform;
|
||||
GLuint previous_texture_uniform;
|
||||
GLuint mix_previous_uniform;
|
||||
|
||||
GLuint position_attribute;
|
||||
GLuint texture;
|
||||
GLuint previous_texture;
|
||||
GLuint program;
|
||||
}
|
||||
|
||||
+ (NSString *) shaderSourceForName:(NSString *) name
|
||||
{
|
||||
return [NSString stringWithContentsOfFile:[[NSBundle mainBundle] pathForResource:name
|
||||
ofType:@"fsh"
|
||||
inDirectory:@"Shaders"]
|
||||
encoding:NSUTF8StringEncoding
|
||||
error:nil];
|
||||
}
|
||||
|
||||
- (instancetype)initWithName:(NSString *) shaderName
|
||||
{
|
||||
self = [super init];
|
||||
if (self) {
|
||||
// Program
|
||||
NSString *fragment_shader = [[self class] shaderSourceForName:@"MasterShader"];
|
||||
fragment_shader = [fragment_shader stringByReplacingOccurrencesOfString:@"\n" withString:@""];
|
||||
fragment_shader = [fragment_shader stringByReplacingOccurrencesOfString:@"{filter}"
|
||||
withString:[[self class] shaderSourceForName:shaderName]];
|
||||
program = [[self class] programWithVertexShader:vertex_shader fragmentShader:fragment_shader];
|
||||
// Attributes
|
||||
position_attribute = glGetAttribLocation(program, "aPosition");
|
||||
// Uniforms
|
||||
resolution_uniform = glGetUniformLocation(program, "uResolution");
|
||||
|
||||
glGenTextures(1, &texture);
|
||||
glBindTexture(GL_TEXTURE_2D, texture);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
texture_uniform = glGetUniformLocation(program, "image");
|
||||
|
||||
glGenTextures(1, &previous_texture);
|
||||
glBindTexture(GL_TEXTURE_2D, previous_texture);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
previous_texture_uniform = glGetUniformLocation(program, "previousImage");
|
||||
|
||||
mix_previous_uniform = glGetUniformLocation(program, "uMixPrevious");
|
||||
|
||||
// Configure OpenGL ES
|
||||
[self configureOpenGLES];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void) renderBitmap: (void *)bitmap previous:(void*) previous inSize:(NSSize)size scale: (double) scale
|
||||
{
|
||||
glUseProgram(program);
|
||||
glUniform2f(resolution_uniform, size.width * scale, size.height * scale);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, texture);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 160, 144, 0, GL_RGBA, GL_UNSIGNED_BYTE, bitmap);
|
||||
glUniform1i(texture_uniform, 0);
|
||||
glUniform1i(mix_previous_uniform, previous != NULL);
|
||||
if (previous) {
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
glBindTexture(GL_TEXTURE_2D, previous_texture);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 160, 144, 0, GL_RGBA, GL_UNSIGNED_BYTE, previous);
|
||||
glUniform1i(previous_texture_uniform, 1);
|
||||
}
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
}
|
||||
|
||||
- (void)configureOpenGLES
|
||||
{
|
||||
// Program
|
||||
glUseProgram(program);
|
||||
|
||||
// Attributes
|
||||
glEnableVertexAttribArray(position_attribute);
|
||||
static GLfloat const quad[8] = {
|
||||
-1.f, -1.f,
|
||||
-1.f, +1.f,
|
||||
+1.f, -1.f,
|
||||
+1.f, +1.f,
|
||||
};
|
||||
glVertexAttribPointer(position_attribute, 2, GL_FLOAT, GL_FALSE, 0, quad);
|
||||
}
|
||||
|
||||
+ (GLuint)programWithVertexShader:(NSString*)vsh fragmentShader:(NSString*)fsh
|
||||
{
|
||||
// Build shaders
|
||||
GLuint vertex_shader = [self shaderWithContents:vsh type:GL_VERTEX_SHADER];
|
||||
GLuint fragment_shader = [self shaderWithContents:fsh type:GL_FRAGMENT_SHADER];
|
||||
// Create program
|
||||
GLuint program = glCreateProgram();
|
||||
// Attach shaders
|
||||
glAttachShader(program, vertex_shader);
|
||||
glAttachShader(program, fragment_shader);
|
||||
// Link program
|
||||
glLinkProgram(program);
|
||||
// Check for errors
|
||||
GLint status;
|
||||
glGetProgramiv(program, GL_LINK_STATUS, &status);
|
||||
if (status == GL_FALSE) {
|
||||
GLchar messages[1024];
|
||||
glGetProgramInfoLog(program, sizeof(messages), 0, &messages[0]);
|
||||
NSLog(@"%@:- GLSL Program Error: %s", self, messages);
|
||||
}
|
||||
|
||||
// Delete shaders
|
||||
glDeleteShader(vertex_shader);
|
||||
glDeleteShader(fragment_shader);
|
||||
|
||||
return program;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
glDeleteProgram(program);
|
||||
glDeleteTextures(1, &texture);
|
||||
glDeleteTextures(1, &previous_texture);
|
||||
}
|
||||
|
||||
+ (GLuint)shaderWithContents:(NSString*)contents type:(GLenum)type
|
||||
{
|
||||
|
||||
const GLchar* source = [contents UTF8String];
|
||||
// Create the shader object
|
||||
GLuint shader = glCreateShader(type);
|
||||
// Load the shader source
|
||||
glShaderSource(shader, 1, &source, 0);
|
||||
// Compile the shader
|
||||
glCompileShader(shader);
|
||||
// Check for errors
|
||||
GLint status = 0;
|
||||
glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
|
||||
if (status == GL_FALSE) {
|
||||
GLchar messages[1024];
|
||||
glGetShaderInfoLog(shader, sizeof(messages), 0, &messages[0]);
|
||||
NSLog(@"%@:- GLSL Shader Error: %s", self, messages);
|
||||
}
|
||||
|
||||
return shader;
|
||||
}
|
||||
|
||||
@end
|
|
@ -1,4 +1,5 @@
|
|||
#import <Cocoa/Cocoa.h>
|
||||
#import "GBShader.h"
|
||||
#include "gb.h"
|
||||
|
||||
@interface GBView : NSOpenGLView
|
||||
|
@ -6,4 +7,5 @@
|
|||
- (uint32_t *) pixels;
|
||||
@property GB_gameboy_t *gb;
|
||||
@property BOOL shouldBlendFrameWithPrevious;
|
||||
@property GBShader *shader;
|
||||
@end
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
#import "GBButtons.h"
|
||||
#import "NSString+StringForKey.h"
|
||||
|
||||
static GBShader *shader = nil;
|
||||
|
||||
@implementation GBView
|
||||
{
|
||||
uint32_t *image_buffers[3];
|
||||
|
@ -15,6 +17,12 @@
|
|||
image_buffers[1] = malloc(160 * 144 * 4);
|
||||
image_buffers[2] = malloc(160 * 144 * 4);
|
||||
_shouldBlendFrameWithPrevious = 1;
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(filterChanged) name:@"GBFilterChanged" object:nil];
|
||||
}
|
||||
|
||||
- (void) filterChanged
|
||||
{
|
||||
self.shader = nil;
|
||||
}
|
||||
|
||||
- (unsigned char) numberOfBuffers
|
||||
|
@ -27,6 +35,7 @@
|
|||
free(image_buffers[0]);
|
||||
free(image_buffers[1]);
|
||||
free(image_buffers[2]);
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
}
|
||||
- (instancetype)initWithCoder:(NSCoder *)coder
|
||||
{
|
||||
|
@ -49,16 +58,21 @@
|
|||
}
|
||||
|
||||
- (void)drawRect:(NSRect)dirtyRect {
|
||||
if (!self.shader) {
|
||||
self.shader = [[GBShader alloc] initWithName:[[NSUserDefaults standardUserDefaults] objectForKey:@"GBFilter"]];
|
||||
}
|
||||
double scale = self.window.backingScaleFactor;
|
||||
glRasterPos2d(-1, 1);
|
||||
glPixelZoom(self.bounds.size.width / 160 * scale, self.bounds.size.height / -144 * scale);
|
||||
glDrawPixels(160, 144, GL_ABGR_EXT, GL_UNSIGNED_BYTE, image_buffers[current_buffer]);
|
||||
if (_shouldBlendFrameWithPrevious) {
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_CONSTANT_ALPHA, GL_ONE_MINUS_CONSTANT_ALPHA);
|
||||
glBlendColor(1, 1, 1, 0.5);
|
||||
glDrawPixels(160, 144, GL_ABGR_EXT, GL_UNSIGNED_BYTE, image_buffers[(current_buffer + 2) % self.numberOfBuffers]);
|
||||
glDisable(GL_BLEND);
|
||||
[self.shader renderBitmap:image_buffers[current_buffer]
|
||||
previous:image_buffers[(current_buffer + 2) % self.numberOfBuffers]
|
||||
inSize:self.bounds.size
|
||||
scale:scale];
|
||||
}
|
||||
else {
|
||||
[self.shader renderBitmap:image_buffers[current_buffer]
|
||||
previous:NULL
|
||||
inSize:self.bounds.size
|
||||
scale:scale];
|
||||
}
|
||||
glFlush();
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="9531" systemVersion="14F1509" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="9531" systemVersion="14F1713" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="9531"/>
|
||||
</dependencies>
|
||||
|
@ -14,11 +14,11 @@
|
|||
<window title="Preferences" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" oneShot="NO" releasedWhenClosed="NO" animationBehavior="default" id="QvC-M9-y7g" customClass="GBPreferencesWindow">
|
||||
<windowStyleMask key="styleMask" titled="YES" closable="YES"/>
|
||||
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
|
||||
<rect key="contentRect" x="196" y="240" width="292" height="237"/>
|
||||
<rect key="contentRect" x="196" y="240" width="292" height="292"/>
|
||||
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1417"/>
|
||||
<view key="contentView" id="EiT-Mj-1SZ">
|
||||
<rect key="frame" x="0.0" y="0.0" width="292" height="237"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="292" height="292"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Utu-t4-cLx">
|
||||
<rect key="frame" x="18" y="201" width="256" height="17"/>
|
||||
|
@ -28,6 +28,39 @@
|
|||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="T91-rh-rRp">
|
||||
<rect key="frame" x="18" y="255" width="256" height="17"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Graphics Filter:" id="pXg-WY-8Q5">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="6pP-kK-EEC">
|
||||
<rect key="frame" x="30" y="223" width="245" height="26"/>
|
||||
<popUpButtonCell key="cell" type="push" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" id="I1w-05-lGl">
|
||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
<menu key="menu" id="xDC-0T-Qg9">
|
||||
<items>
|
||||
<menuItem title="Nearest Neighbor (Pixelated)" id="neN-eo-LA7">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
</menuItem>
|
||||
<menuItem title="Bilinear (Blurry)" id="iDe-si-atu"/>
|
||||
<menuItem title="Smooth Bilinear (Less blurry)" id="1jN-pO-1iD"/>
|
||||
<menuItem title="Scale2x" id="C1I-L2-Up1"/>
|
||||
<menuItem title="Scale4x" id="uWA-Zp-JY9"/>
|
||||
<menuItem title="Anti-aliased Scale2x" id="iP6-DJ-CVH"/>
|
||||
<menuItem title="Anti-aliased Scale4x" id="zJR-ER-Ygo">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</popUpButtonCell>
|
||||
<connections>
|
||||
<action selector="graphicFilterChanged:" target="QvC-M9-y7g" id="n87-t4-fbV"/>
|
||||
</connections>
|
||||
</popUpButton>
|
||||
<scrollView focusRingType="none" fixedFrame="YES" autohidesScrollers="YES" horizontalLineScroll="19" horizontalPageScroll="10" verticalLineScroll="19" verticalPageScroll="10" hasHorizontalScroller="NO" hasVerticalScroller="NO" usesPredominantAxisScrolling="NO" horizontalScrollElasticity="none" verticalScrollElasticity="none" translatesAutoresizingMaskIntoConstraints="NO" id="PBp-dj-EIa">
|
||||
<rect key="frame" x="20" y="20" width="252" height="173"/>
|
||||
<clipView key="contentView" focusRingType="none" ambiguous="YES" drawsBackground="NO" id="AMs-PO-nid">
|
||||
|
@ -90,8 +123,9 @@
|
|||
<connections>
|
||||
<outlet property="controlsTableView" destination="UDd-IJ-fxX" id="a1D-Md-yXv"/>
|
||||
<outlet property="delegate" destination="-2" id="ASc-vN-Zbq"/>
|
||||
<outlet property="graphicsFilterPopupButton" destination="6pP-kK-EEC" id="LS7-HY-kHC"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="179" y="403.5"/>
|
||||
<point key="canvasLocation" x="179" y="431"/>
|
||||
</window>
|
||||
</objects>
|
||||
</document>
|
||||
|
|
7
Makefile
7
Makefile
|
@ -67,6 +67,8 @@ $(OBJ)/%.m.o: %.m
|
|||
|
||||
# Cocoa Port
|
||||
|
||||
Shaders:$(shell echo Shaders/*.fsh)
|
||||
|
||||
$(BIN)/Sameboy.app: $(BIN)/Sameboy.app/Contents/MacOS/Sameboy \
|
||||
$(shell echo Cocoa/*.icns) \
|
||||
Cocoa/License.html \
|
||||
|
@ -75,11 +77,14 @@ $(BIN)/Sameboy.app: $(BIN)/Sameboy.app/Contents/MacOS/Sameboy \
|
|||
$(BIN)/BootROMs/cgb_boot.bin \
|
||||
$(BIN)/Sameboy.app/Contents/Resources/Base.lproj/Document.nib \
|
||||
$(BIN)/Sameboy.app/Contents/Resources/Base.lproj/MainMenu.nib \
|
||||
$(BIN)/Sameboy.app/Contents/Resources/Base.lproj/Preferences.nib
|
||||
$(BIN)/Sameboy.app/Contents/Resources/Base.lproj/Preferences.nib \
|
||||
Shaders
|
||||
mkdir -p $(BIN)/Sameboy.app/Contents/Resources
|
||||
cp Cocoa/*.icns $(BIN)/BootROMs/dmg_boot.bin $(BIN)/BootROMs/cgb_boot.bin $(BIN)/Sameboy.app/Contents/Resources/
|
||||
sed s/@VERSION/$(VERSION)/ < Cocoa/info.plist > $(BIN)/Sameboy.app/Contents/info.plist
|
||||
cp Cocoa/License.html $(BIN)/Sameboy.app/Contents/Resources/Credits.html
|
||||
mkdir -p $(BIN)/Sameboy.app/Contents/Resources/Shaders
|
||||
cp Shaders/*.fsh $(BIN)/Sameboy.app/Contents/Resources/Shaders
|
||||
|
||||
$(BIN)/Sameboy.app/Contents/MacOS/Sameboy: $(CORE_OBJECTS) $(COCOA_OBJECTS)
|
||||
-@mkdir -p $(dir $@)
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
vec4 scale2x(sampler2D image)
|
||||
{
|
||||
// o = offset, the width of a pixel
|
||||
vec2 o = 1.0 / textureDimensions;
|
||||
vec2 texCoord = vec2(gl_FragCoord.x, uResolution.y - gl_FragCoord.y) / uResolution;
|
||||
|
||||
// texel arrangement
|
||||
// A B C
|
||||
// D E F
|
||||
// G H I
|
||||
vec4 A = texture2D(image, texCoord + vec2( -o.x, o.y));
|
||||
vec4 B = texture2D(image, texCoord + vec2( 0, o.y));
|
||||
vec4 C = texture2D(image, texCoord + vec2( o.x, o.y));
|
||||
vec4 D = texture2D(image, texCoord + vec2( -o.x, 0));
|
||||
vec4 E = texture2D(image, texCoord + vec2( 0, 0));
|
||||
vec4 F = texture2D(image, texCoord + vec2( o.x, 0));
|
||||
vec4 G = texture2D(image, texCoord + vec2( -o.x, -o.y));
|
||||
vec4 H = texture2D(image, texCoord + vec2( 0, -o.y));
|
||||
vec4 I = texture2D(image, texCoord + vec2( o.x, -o.y));
|
||||
vec2 p = texCoord * textureDimensions;
|
||||
// p = the position within a pixel [0...1]
|
||||
p = fract(p);
|
||||
if (p.x > .5) {
|
||||
if (p.y > .5) {
|
||||
// Top Right
|
||||
return B == F && B != D && F != H ? F : E;
|
||||
} else {
|
||||
// Bottom Right
|
||||
return H == F && D != H && B != F ? F : E;
|
||||
}
|
||||
} else {
|
||||
if (p.y > .5) {
|
||||
// Top Left
|
||||
return D == B && B != F && D != H ? D : E;
|
||||
} else {
|
||||
// Bottom Left
|
||||
return D == H && D != B && H != F ? D : E;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
vec4 filter(sampler2D image)
|
||||
{
|
||||
vec2 texCoord = vec2(gl_FragCoord.x, uResolution.y - gl_FragCoord.y) / uResolution;
|
||||
|
||||
return mix(texture2D(image, texCoord), scale2x(image), 0.5);
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
vec4 scale2x(sampler2D image, vec2 texCoord)
|
||||
{
|
||||
// o = offset, the width of a pixel
|
||||
vec2 o = 1.0 / textureDimensions;
|
||||
// texel arrangement
|
||||
// A B C
|
||||
// D E F
|
||||
// G H I
|
||||
vec4 A = texture2D(image, texCoord + vec2( -o.x, o.y));
|
||||
vec4 B = texture2D(image, texCoord + vec2( 0, o.y));
|
||||
vec4 C = texture2D(image, texCoord + vec2( o.x, o.y));
|
||||
vec4 D = texture2D(image, texCoord + vec2( -o.x, 0));
|
||||
vec4 E = texture2D(image, texCoord + vec2( 0, 0));
|
||||
vec4 F = texture2D(image, texCoord + vec2( o.x, 0));
|
||||
vec4 G = texture2D(image, texCoord + vec2( -o.x, -o.y));
|
||||
vec4 H = texture2D(image, texCoord + vec2( 0, -o.y));
|
||||
vec4 I = texture2D(image, texCoord + vec2( o.x, -o.y));
|
||||
vec2 p = texCoord * textureDimensions;
|
||||
// p = the position within a pixel [0...1]
|
||||
p = fract(p);
|
||||
if (p.x > .5) {
|
||||
if (p.y > .5) {
|
||||
// Top Right
|
||||
return B == F && B != D && F != H ? F : E;
|
||||
} else {
|
||||
// Bottom Right
|
||||
return H == F && D != H && B != F ? F : E;
|
||||
}
|
||||
} else {
|
||||
if (p.y > .5) {
|
||||
// Top Left
|
||||
return D == B && B != F && D != H ? D : E;
|
||||
} else {
|
||||
// Bottom Left
|
||||
return D == H && D != B && H != F ? D : E;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
vec4 aaScale2x(sampler2D image, vec2 texCoord)
|
||||
{
|
||||
return mix(texture2D(image, texCoord), scale2x(image, texCoord), 0.5);
|
||||
}
|
||||
|
||||
vec4 filter(sampler2D image)
|
||||
{
|
||||
// o = offset, the width of a pixel
|
||||
vec2 o = 1.0 / (textureDimensions * 2.);
|
||||
vec2 texCoord = vec2(gl_FragCoord.x, uResolution.y - gl_FragCoord.y) / uResolution;
|
||||
|
||||
// texel arrangement
|
||||
// A B C
|
||||
// D E F
|
||||
// G H I
|
||||
vec4 A = aaScale2x(image, texCoord + vec2( -o.x, o.y));
|
||||
vec4 B = aaScale2x(image, texCoord + vec2( 0, o.y));
|
||||
vec4 C = aaScale2x(image, texCoord + vec2( o.x, o.y));
|
||||
vec4 D = aaScale2x(image, texCoord + vec2( -o.x, 0));
|
||||
vec4 E = aaScale2x(image, texCoord + vec2( 0, 0));
|
||||
vec4 F = aaScale2x(image, texCoord + vec2( o.x, 0));
|
||||
vec4 G = aaScale2x(image, texCoord + vec2( -o.x, -o.y));
|
||||
vec4 H = aaScale2x(image, texCoord + vec2( 0, -o.y));
|
||||
vec4 I = aaScale2x(image, texCoord + vec2( o.x, -o.y));
|
||||
vec4 R;
|
||||
vec2 p = texCoord * textureDimensions * 2.;
|
||||
// p = the position within a pixel [0...1]
|
||||
p = fract(p);
|
||||
if (p.x > .5) {
|
||||
if (p.y > .5) {
|
||||
// Top Right
|
||||
R = B == F && B != D && F != H ? F : E;
|
||||
} else {
|
||||
// Bottom Right
|
||||
R = H == F && D != H && B != F ? F : E;
|
||||
}
|
||||
} else {
|
||||
if (p.y > .5) {
|
||||
// Top Left
|
||||
R = D == B && B != F && D != H ? D : E;
|
||||
} else {
|
||||
// Bottom Left
|
||||
R = D == H && D != B && H != F ? D : E;
|
||||
}
|
||||
}
|
||||
|
||||
return mix(R, E, 0.5);
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
vec4 filter(sampler2D image)
|
||||
{
|
||||
vec2 texCoord = vec2(gl_FragCoord.x, uResolution.y - gl_FragCoord.y) / uResolution;
|
||||
|
||||
vec2 pixel = texCoord * textureDimensions;
|
||||
|
||||
vec4 q11 = texture2D(image, vec2(floor(pixel.x) / textureDimensions.x, floor(pixel.y) / textureDimensions.y));
|
||||
vec4 q12 = texture2D(image, vec2(floor(pixel.x) / textureDimensions.x, ceil(pixel.y) / textureDimensions.y));
|
||||
vec4 q21 = texture2D(image, vec2(ceil(pixel.x) / textureDimensions.x, floor(pixel.y) / textureDimensions.y));
|
||||
vec4 q22 = texture2D(image, vec2(ceil(pixel.x) / textureDimensions.x, ceil(pixel.y) / textureDimensions.y));
|
||||
|
||||
vec4 r1 = mix(q11, q21, fract(pixel.x));
|
||||
vec4 r2 = mix(q12, q22, fract(pixel.x));
|
||||
|
||||
return mix (r1, r2, fract(pixel.y));
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
uniform sampler2D image;
|
||||
uniform sampler2D previousImage;
|
||||
uniform bool uMixPrevious;
|
||||
|
||||
uniform vec2 uResolution;
|
||||
const vec2 textureDimensions = vec2(160, 144);
|
||||
|
||||
{filter}
|
||||
|
||||
void main() {
|
||||
if (uMixPrevious) {
|
||||
gl_FragColor = mix(filter(image), filter(previousImage), 0.5);
|
||||
}
|
||||
else {
|
||||
gl_FragColor = filter(image);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
vec4 filter(sampler2D image)
|
||||
{
|
||||
vec2 texCoord = vec2(gl_FragCoord.x, uResolution.y - gl_FragCoord.y) / uResolution;
|
||||
|
||||
return texture2D(image, texCoord);
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
/* Shader implementation of Scale2x is adapted from https://gist.github.com/singron/3161079 */
|
||||
|
||||
vec4 filter(sampler2D image)
|
||||
{
|
||||
// o = offset, the width of a pixel
|
||||
vec2 o = 1.0 / textureDimensions;
|
||||
vec2 texCoord = vec2(gl_FragCoord.x, uResolution.y - gl_FragCoord.y) / uResolution;
|
||||
|
||||
// texel arrangement
|
||||
// A B C
|
||||
// D E F
|
||||
// G H I
|
||||
vec4 A = texture2D(image, texCoord + vec2( -o.x, o.y));
|
||||
vec4 B = texture2D(image, texCoord + vec2( 0, o.y));
|
||||
vec4 C = texture2D(image, texCoord + vec2( o.x, o.y));
|
||||
vec4 D = texture2D(image, texCoord + vec2( -o.x, 0));
|
||||
vec4 E = texture2D(image, texCoord + vec2( 0, 0));
|
||||
vec4 F = texture2D(image, texCoord + vec2( o.x, 0));
|
||||
vec4 G = texture2D(image, texCoord + vec2( -o.x, -o.y));
|
||||
vec4 H = texture2D(image, texCoord + vec2( 0, -o.y));
|
||||
vec4 I = texture2D(image, texCoord + vec2( o.x, -o.y));
|
||||
vec2 p = texCoord * textureDimensions;
|
||||
// p = the position within a pixel [0...1]
|
||||
p = fract(p);
|
||||
if (p.x > .5) {
|
||||
if (p.y > .5) {
|
||||
// Top Right
|
||||
return B == F && B != D && F != H ? F : E;
|
||||
} else {
|
||||
// Bottom Right
|
||||
return H == F && D != H && B != F ? F : E;
|
||||
}
|
||||
} else {
|
||||
if (p.y > .5) {
|
||||
// Top Left
|
||||
return D == B && B != F && D != H ? D : E;
|
||||
} else {
|
||||
// Bottom Left
|
||||
return D == H && D != B && H != F ? D : E;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
vec4 scale2x(sampler2D image, vec2 texCoord)
|
||||
{
|
||||
// o = offset, the width of a pixel
|
||||
vec2 o = 1.0 / textureDimensions;
|
||||
// texel arrangement
|
||||
// A B C
|
||||
// D E F
|
||||
// G H I
|
||||
vec4 A = texture2D(image, texCoord + vec2( -o.x, o.y));
|
||||
vec4 B = texture2D(image, texCoord + vec2( 0, o.y));
|
||||
vec4 C = texture2D(image, texCoord + vec2( o.x, o.y));
|
||||
vec4 D = texture2D(image, texCoord + vec2( -o.x, 0));
|
||||
vec4 E = texture2D(image, texCoord + vec2( 0, 0));
|
||||
vec4 F = texture2D(image, texCoord + vec2( o.x, 0));
|
||||
vec4 G = texture2D(image, texCoord + vec2( -o.x, -o.y));
|
||||
vec4 H = texture2D(image, texCoord + vec2( 0, -o.y));
|
||||
vec4 I = texture2D(image, texCoord + vec2( o.x, -o.y));
|
||||
vec2 p = texCoord * textureDimensions;
|
||||
// p = the position within a pixel [0...1]
|
||||
vec4 R;
|
||||
p = fract(p);
|
||||
if (p.x > .5) {
|
||||
if (p.y > .5) {
|
||||
// Top Right
|
||||
return B == F && B != D && F != H ? F : E;
|
||||
} else {
|
||||
// Bottom Right
|
||||
return H == F && D != H && B != F ? F : E;
|
||||
}
|
||||
} else {
|
||||
if (p.y > .5) {
|
||||
// Top Left
|
||||
return D == B && B != F && D != H ? D : E;
|
||||
} else {
|
||||
// Bottom Left
|
||||
return D == H && D != B && H != F ? D : E;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
vec4 filter(sampler2D image)
|
||||
{
|
||||
// o = offset, the width of a pixel
|
||||
vec2 o = 1.0 / (textureDimensions * 2.);
|
||||
vec2 texCoord = vec2(gl_FragCoord.x, uResolution.y - gl_FragCoord.y) / uResolution;
|
||||
|
||||
// texel arrangement
|
||||
// A B C
|
||||
// D E F
|
||||
// G H I
|
||||
vec4 A = scale2x(image, texCoord + vec2( -o.x, o.y));
|
||||
vec4 B = scale2x(image, texCoord + vec2( 0, o.y));
|
||||
vec4 C = scale2x(image, texCoord + vec2( o.x, o.y));
|
||||
vec4 D = scale2x(image, texCoord + vec2( -o.x, 0));
|
||||
vec4 E = scale2x(image, texCoord + vec2( 0, 0));
|
||||
vec4 F = scale2x(image, texCoord + vec2( o.x, 0));
|
||||
vec4 G = scale2x(image, texCoord + vec2( -o.x, -o.y));
|
||||
vec4 H = scale2x(image, texCoord + vec2( 0, -o.y));
|
||||
vec4 I = scale2x(image, texCoord + vec2( o.x, -o.y));
|
||||
vec2 p = texCoord * textureDimensions * 2.;
|
||||
// p = the position within a pixel [0...1]
|
||||
p = fract(p);
|
||||
if (p.x > .5) {
|
||||
if (p.y > .5) {
|
||||
// Top Right
|
||||
return B == F && B != D && F != H ? F : E;
|
||||
} else {
|
||||
// Bottom Right
|
||||
return H == F && D != H && B != F ? F : E;
|
||||
}
|
||||
} else {
|
||||
if (p.y > .5) {
|
||||
// Top Left
|
||||
return D == B && B != F && D != H ? D : E;
|
||||
} else {
|
||||
// Bottom Left
|
||||
return D == H && D != B && H != F ? D : E;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
vec4 filter(sampler2D image)
|
||||
{
|
||||
vec2 texCoord = vec2(gl_FragCoord.x, uResolution.y - gl_FragCoord.y) / uResolution;
|
||||
|
||||
vec2 pixel = texCoord * textureDimensions;
|
||||
|
||||
vec4 q11 = texture2D(image, vec2(floor(pixel.x) / textureDimensions.x, floor(pixel.y) / textureDimensions.y));
|
||||
vec4 q12 = texture2D(image, vec2(floor(pixel.x) / textureDimensions.x, ceil(pixel.y) / textureDimensions.y));
|
||||
vec4 q21 = texture2D(image, vec2(ceil(pixel.x) / textureDimensions.x, floor(pixel.y) / textureDimensions.y));
|
||||
vec4 q22 = texture2D(image, vec2(ceil(pixel.x) / textureDimensions.x, ceil(pixel.y) / textureDimensions.y));
|
||||
|
||||
vec2 smooth = smoothstep(0., 1., fract(pixel));
|
||||
|
||||
vec4 r1 = mix(q11, q21, fract(smooth.x));
|
||||
vec4 r2 = mix(q12, q22, fract(smooth.x));
|
||||
|
||||
return mix (r1, r2, fract(smooth.y));
|
||||
}
|
Loading…
Reference in New Issue