Filter support for Cocoa port + 7 basic filters
This commit is contained in:
parent
dde983db8f
commit
8d59bfcbdd
@ -24,6 +24,8 @@
|
|||||||
@"GBStart": @"\r",
|
@"GBStart": @"\r",
|
||||||
|
|
||||||
@"GBTurbo": @" ",
|
@"GBTurbo": @" ",
|
||||||
|
|
||||||
|
@"GBFilter": @"NearestNeighbor",
|
||||||
}];
|
}];
|
||||||
#undef KEY
|
#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)
|
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
|
@implementation Document
|
||||||
|
@ -2,4 +2,5 @@
|
|||||||
|
|
||||||
@interface GBPreferencesWindow : NSWindow <NSTableViewDelegate, NSTableViewDataSource>
|
@interface GBPreferencesWindow : NSWindow <NSTableViewDelegate, NSTableViewDataSource>
|
||||||
@property IBOutlet NSTableView *controlsTableView;
|
@property IBOutlet NSTableView *controlsTableView;
|
||||||
|
@property IBOutlet NSPopUpButton *graphicsFilterPopupButton;
|
||||||
@end
|
@end
|
||||||
|
@ -6,6 +6,38 @@
|
|||||||
{
|
{
|
||||||
bool is_button_being_modified;
|
bool is_button_being_modified;
|
||||||
NSInteger 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
|
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView
|
||||||
@ -56,5 +88,12 @@
|
|||||||
[self.controlsTableView reloadData];
|
[self.controlsTableView reloadData];
|
||||||
[self makeFirstResponder:self.controlsTableView];
|
[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
|
@end
|
||||||
|
6
Cocoa/GBShader.h
Normal file
6
Cocoa/GBShader.h
Normal file
@ -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
|
170
Cocoa/GBShader.m
Normal file
170
Cocoa/GBShader.m
Normal file
@ -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 <Cocoa/Cocoa.h>
|
||||||
|
#import "GBShader.h"
|
||||||
#include "gb.h"
|
#include "gb.h"
|
||||||
|
|
||||||
@interface GBView : NSOpenGLView
|
@interface GBView : NSOpenGLView
|
||||||
@ -6,4 +7,5 @@
|
|||||||
- (uint32_t *) pixels;
|
- (uint32_t *) pixels;
|
||||||
@property GB_gameboy_t *gb;
|
@property GB_gameboy_t *gb;
|
||||||
@property BOOL shouldBlendFrameWithPrevious;
|
@property BOOL shouldBlendFrameWithPrevious;
|
||||||
|
@property GBShader *shader;
|
||||||
@end
|
@end
|
||||||
|
@ -3,6 +3,8 @@
|
|||||||
#import "GBButtons.h"
|
#import "GBButtons.h"
|
||||||
#import "NSString+StringForKey.h"
|
#import "NSString+StringForKey.h"
|
||||||
|
|
||||||
|
static GBShader *shader = nil;
|
||||||
|
|
||||||
@implementation GBView
|
@implementation GBView
|
||||||
{
|
{
|
||||||
uint32_t *image_buffers[3];
|
uint32_t *image_buffers[3];
|
||||||
@ -15,6 +17,12 @@
|
|||||||
image_buffers[1] = malloc(160 * 144 * 4);
|
image_buffers[1] = malloc(160 * 144 * 4);
|
||||||
image_buffers[2] = malloc(160 * 144 * 4);
|
image_buffers[2] = malloc(160 * 144 * 4);
|
||||||
_shouldBlendFrameWithPrevious = 1;
|
_shouldBlendFrameWithPrevious = 1;
|
||||||
|
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(filterChanged) name:@"GBFilterChanged" object:nil];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void) filterChanged
|
||||||
|
{
|
||||||
|
self.shader = nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (unsigned char) numberOfBuffers
|
- (unsigned char) numberOfBuffers
|
||||||
@ -27,6 +35,7 @@
|
|||||||
free(image_buffers[0]);
|
free(image_buffers[0]);
|
||||||
free(image_buffers[1]);
|
free(image_buffers[1]);
|
||||||
free(image_buffers[2]);
|
free(image_buffers[2]);
|
||||||
|
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||||
}
|
}
|
||||||
- (instancetype)initWithCoder:(NSCoder *)coder
|
- (instancetype)initWithCoder:(NSCoder *)coder
|
||||||
{
|
{
|
||||||
@ -49,16 +58,21 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (void)drawRect:(NSRect)dirtyRect {
|
- (void)drawRect:(NSRect)dirtyRect {
|
||||||
|
if (!self.shader) {
|
||||||
|
self.shader = [[GBShader alloc] initWithName:[[NSUserDefaults standardUserDefaults] objectForKey:@"GBFilter"]];
|
||||||
|
}
|
||||||
double scale = self.window.backingScaleFactor;
|
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) {
|
if (_shouldBlendFrameWithPrevious) {
|
||||||
glEnable(GL_BLEND);
|
[self.shader renderBitmap:image_buffers[current_buffer]
|
||||||
glBlendFunc(GL_CONSTANT_ALPHA, GL_ONE_MINUS_CONSTANT_ALPHA);
|
previous:image_buffers[(current_buffer + 2) % self.numberOfBuffers]
|
||||||
glBlendColor(1, 1, 1, 0.5);
|
inSize:self.bounds.size
|
||||||
glDrawPixels(160, 144, GL_ABGR_EXT, GL_UNSIGNED_BYTE, image_buffers[(current_buffer + 2) % self.numberOfBuffers]);
|
scale:scale];
|
||||||
glDisable(GL_BLEND);
|
}
|
||||||
|
else {
|
||||||
|
[self.shader renderBitmap:image_buffers[current_buffer]
|
||||||
|
previous:NULL
|
||||||
|
inSize:self.bounds.size
|
||||||
|
scale:scale];
|
||||||
}
|
}
|
||||||
glFlush();
|
glFlush();
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
<?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>
|
<dependencies>
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="9531"/>
|
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="9531"/>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
@ -14,11 +14,11 @@
|
|||||||
<window title="Preferences" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" oneShot="NO" releasedWhenClosed="NO" animationBehavior="default" id="QvC-M9-y7g" customClass="GBPreferencesWindow">
|
<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"/>
|
<windowStyleMask key="styleMask" titled="YES" closable="YES"/>
|
||||||
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="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"/>
|
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1417"/>
|
||||||
<view key="contentView" id="EiT-Mj-1SZ">
|
<view key="contentView" id="EiT-Mj-1SZ">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="292" height="237"/>
|
<rect key="frame" x="0.0" y="0.0" width="292" height="292"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Utu-t4-cLx">
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Utu-t4-cLx">
|
||||||
<rect key="frame" x="18" y="201" width="256" height="17"/>
|
<rect key="frame" x="18" y="201" width="256" height="17"/>
|
||||||
@ -28,6 +28,39 @@
|
|||||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||||
</textFieldCell>
|
</textFieldCell>
|
||||||
</textField>
|
</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">
|
<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"/>
|
<rect key="frame" x="20" y="20" width="252" height="173"/>
|
||||||
<clipView key="contentView" focusRingType="none" ambiguous="YES" drawsBackground="NO" id="AMs-PO-nid">
|
<clipView key="contentView" focusRingType="none" ambiguous="YES" drawsBackground="NO" id="AMs-PO-nid">
|
||||||
@ -90,8 +123,9 @@
|
|||||||
<connections>
|
<connections>
|
||||||
<outlet property="controlsTableView" destination="UDd-IJ-fxX" id="a1D-Md-yXv"/>
|
<outlet property="controlsTableView" destination="UDd-IJ-fxX" id="a1D-Md-yXv"/>
|
||||||
<outlet property="delegate" destination="-2" id="ASc-vN-Zbq"/>
|
<outlet property="delegate" destination="-2" id="ASc-vN-Zbq"/>
|
||||||
|
<outlet property="graphicsFilterPopupButton" destination="6pP-kK-EEC" id="LS7-HY-kHC"/>
|
||||||
</connections>
|
</connections>
|
||||||
<point key="canvasLocation" x="179" y="403.5"/>
|
<point key="canvasLocation" x="179" y="431"/>
|
||||||
</window>
|
</window>
|
||||||
</objects>
|
</objects>
|
||||||
</document>
|
</document>
|
||||||
|
7
Makefile
7
Makefile
@ -67,6 +67,8 @@ $(OBJ)/%.m.o: %.m
|
|||||||
|
|
||||||
# Cocoa Port
|
# Cocoa Port
|
||||||
|
|
||||||
|
Shaders:$(shell echo Shaders/*.fsh)
|
||||||
|
|
||||||
$(BIN)/Sameboy.app: $(BIN)/Sameboy.app/Contents/MacOS/Sameboy \
|
$(BIN)/Sameboy.app: $(BIN)/Sameboy.app/Contents/MacOS/Sameboy \
|
||||||
$(shell echo Cocoa/*.icns) \
|
$(shell echo Cocoa/*.icns) \
|
||||||
Cocoa/License.html \
|
Cocoa/License.html \
|
||||||
@ -75,11 +77,14 @@ $(BIN)/Sameboy.app: $(BIN)/Sameboy.app/Contents/MacOS/Sameboy \
|
|||||||
$(BIN)/BootROMs/cgb_boot.bin \
|
$(BIN)/BootROMs/cgb_boot.bin \
|
||||||
$(BIN)/Sameboy.app/Contents/Resources/Base.lproj/Document.nib \
|
$(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/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
|
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/
|
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
|
sed s/@VERSION/$(VERSION)/ < Cocoa/info.plist > $(BIN)/Sameboy.app/Contents/info.plist
|
||||||
cp Cocoa/License.html $(BIN)/Sameboy.app/Contents/Resources/Credits.html
|
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)
|
$(BIN)/Sameboy.app/Contents/MacOS/Sameboy: $(CORE_OBJECTS) $(COCOA_OBJECTS)
|
||||||
-@mkdir -p $(dir $@)
|
-@mkdir -p $(dir $@)
|
||||||
|
47
Shaders/AAScale2x.fsh
Normal file
47
Shaders/AAScale2x.fsh
Normal file
@ -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);
|
||||||
|
}
|
87
Shaders/AAScale4x.fsh
Normal file
87
Shaders/AAScale4x.fsh
Normal file
@ -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);
|
||||||
|
}
|
16
Shaders/Bilinear.fsh
Normal file
16
Shaders/Bilinear.fsh
Normal file
@ -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));
|
||||||
|
}
|
17
Shaders/MasterShader.fsh
Normal file
17
Shaders/MasterShader.fsh
Normal file
@ -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);
|
||||||
|
}
|
||||||
|
}
|
6
Shaders/NearestNeighbor.fsh
Normal file
6
Shaders/NearestNeighbor.fsh
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
vec4 filter(sampler2D image)
|
||||||
|
{
|
||||||
|
vec2 texCoord = vec2(gl_FragCoord.x, uResolution.y - gl_FragCoord.y) / uResolution;
|
||||||
|
|
||||||
|
return texture2D(image, texCoord);
|
||||||
|
}
|
42
Shaders/Scale2x.fsh
Normal file
42
Shaders/Scale2x.fsh
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
80
Shaders/Scale4x.fsh
Normal file
80
Shaders/Scale4x.fsh
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
18
Shaders/SmoothBilinear.fsh
Normal file
18
Shaders/SmoothBilinear.fsh
Normal file
@ -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
Block a user