├── .github └── workflows │ └── main.yml ├── .gitignore ├── readme.md └── source ├── app.h ├── crt_frame.h ├── crt_frame_pc.h ├── crtemu.h ├── crtemu_pc.h ├── frametimer.h ├── gif_load.h ├── jo_gif.h ├── main.c └── stb_image.h /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | workflow_dispatch: 9 | branches: [ main ] 10 | 11 | jobs: 12 | build-windows-msvc: 13 | runs-on: windows-2019 14 | steps: 15 | - uses: actions/checkout@v1 16 | # this runs vcvarsall for us, so we get the MSVC toolchain in PATH. 17 | - uses: seanmiddleditch/gha-setup-vsdevenv@master 18 | - name: build crtview 19 | run: | 20 | cl source\main.c /O2 /Fe:crtview.exe /MT /link /SUBSYSTEM:WINDOWS 21 | - name: Upload executable 22 | uses: actions/upload-artifact@v2 23 | with: 24 | name: crtview.exe 25 | path: crtview.exe 26 | build-macos: 27 | runs-on: macOS-latest 28 | steps: 29 | - uses: actions/checkout@v1 30 | - name: install dependencies 31 | run: brew install sdl2 glew 32 | - name: build crtview 33 | run: | 34 | clang -o crtview-mac source/main.c /usr/local/lib/libSDL2.a /usr/local/lib/libGLEW.a -framework OpenGL -Wl,-framework,CoreAudio -Wl,-framework,AudioToolbox -Wl,-weak_framework,CoreHaptics -Wl,-weak_framework,GameController -Wl,-framework,ForceFeedback -lobjc -Wl,-framework,CoreVideo -Wl,-framework,Cocoa -Wl,-framework,Carbon -Wl,-framework,IOKit -Wl,-weak_framework,QuartzCore -Wl,-weak_framework,Metal -liconv -lm 35 | chmod a+x crtview-mac 36 | tar -cvf crtview-mac.tar crtview-mac 37 | - name: Upload executable 38 | uses: actions/upload-artifact@v2 39 | with: 40 | name: crtview-mac.tar 41 | path: crtview-mac.tar 42 | build-linux-gcc: 43 | runs-on: ubuntu-latest 44 | steps: 45 | - uses: actions/checkout@v1 46 | - name: install dependencies 47 | run: | 48 | sudo apt-get update 49 | sudo apt-get install -qq libsdl2-dev 50 | sudo apt-get install -qq libglew-dev 51 | - name: build crtview 52 | run: | 53 | gcc -o crtview-linux source/main.c -lSDL2 -lGLEW -lGL -lm -lpthread 54 | chmod a+x crtview-linux 55 | tar -cvf crtview-linux.tar crtview-linux 56 | - name: Upload executable 57 | uses: actions/upload-artifact@v2 58 | with: 59 | name: crtview-linux.tar 60 | path: crtview-linux.tar 61 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.obj 2 | *.exe -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | ![build](https://github.com/mattiasgustavsson/crtview/actions/workflows/main.yml/badge.svg) 2 | 3 | # crtview 4 | View any image with my CRT filter applied 5 | Use Up/Down cursor keys to toggle display mode 6 | Use F11 to toggle fullscreen 7 | ESC to exit 8 | 9 | Pass the filename of your image as command-line parameter (or just drag-drop it onto the EXE) 10 | 11 | Pre-built binaries can be found here: https://mattiasgustavsson.itch.io/crtview 12 | 13 | Build instructions 14 | ------------------ 15 | 16 | ### Windows 17 | From a Visual Studio command prompt: 18 | ``` 19 | cl source\main.c 20 | ``` 21 | 22 | ### Mac 23 | ``` 24 | clang source/main.c -lSDL2 -lGLEW -framework OpenGL 25 | ``` 26 | 27 | SDL2 and GLEW are required - if you don't have then installed you can do so by running 28 | ``` 29 | brew install sdl2 glew 30 | ``` 31 | 32 | ### Linux 33 | ``` 34 | gcc source/main.c -lSDL2 -lGLEW -lGL -lm -lpthread 35 | ``` 36 | 37 | SDL2 and GLEW are required - if you don't have them installed you can do so by running 38 | ``` 39 | sudo apt-get install libsdl2-dev 40 | sudo apt-get install libglew-dev 41 | ``` 42 | 43 | 44 | -------------------------------------------------------------------------------- /source/crtemu.h: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------------------------------------------ 3 | Licensing information can be found at the end of the file. 4 | ------------------------------------------------------------------------------ 5 | 6 | crtemu.h - v0.1 - Cathode ray tube emulation shader for C/C++. 7 | 8 | Do this: 9 | #define CRTEMU_IMPLEMENTATION 10 | before you include this file in *one* C/C++ file to create the implementation. 11 | */ 12 | 13 | 14 | #ifndef crtemu_h 15 | #define crtemu_h 16 | 17 | #ifndef CRTEMU_U32 18 | #define CRTEMU_U32 unsigned int 19 | #endif 20 | #ifndef CRTEMU_U64 21 | #define CRTEMU_U64 unsigned long long 22 | #endif 23 | 24 | typedef struct crtemu_t crtemu_t; 25 | 26 | crtemu_t* crtemu_create( void* memctx ); 27 | 28 | void crtemu_destroy( crtemu_t* crtemu ); 29 | 30 | typedef struct crtemu_config_t 31 | { 32 | float curvature, scanlines, shadow_mask, separation, ghosting, noise, flicker, vignette, distortion, aspect_lock, 33 | hpos, vpos, hsize, vsize, contrast, brightness, saturation, blur, degauss; // range -1.0f to 1.0f, default=0.0f 34 | } crtemu_config_t; 35 | 36 | void crtemu_config(crtemu_t* crtemu, crtemu_config_t const* config); 37 | 38 | void crtemu_frame( crtemu_t* crtemu, CRTEMU_U32* frame_abgr, int frame_width, int frame_height ); 39 | 40 | void crtemu_present( crtemu_t* crtemu, CRTEMU_U64 time_us, CRTEMU_U32 const* pixels_xbgr, int width, int height, 41 | CRTEMU_U32 mod_xbgr, CRTEMU_U32 border_xbgr ); 42 | 43 | void crtemu_coordinates_window_to_bitmap( crtemu_t* crtemu, int width, int height, int* x, int* y ); 44 | void crtemu_coordinates_bitmap_to_window( crtemu_t* crtemu, int width, int height, int* x, int* y ); 45 | 46 | #endif /* crtemu_h */ 47 | 48 | /* 49 | ---------------------- 50 | IMPLEMENTATION 51 | ---------------------- 52 | */ 53 | #ifdef CRTEMU_IMPLEMENTATION 54 | #undef CRTEMU_IMPLEMENTATION 55 | 56 | #define _CRT_NONSTDC_NO_DEPRECATE 57 | #define _CRT_SECURE_NO_WARNINGS 58 | #include 59 | #include 60 | 61 | #ifndef CRTEMU_MALLOC 62 | #include 63 | #if defined(__cplusplus) 64 | #define CRTEMU_MALLOC( ctx, size ) ( ::malloc( size ) ) 65 | #define CRTEMU_FREE( ctx, ptr ) ( ::free( ptr ) ) 66 | #else 67 | #define CRTEMU_MALLOC( ctx, size ) ( malloc( size ) ) 68 | #define CRTEMU_FREE( ctx, ptr ) ( free( ptr ) ) 69 | #endif 70 | #endif 71 | 72 | #ifdef CRTEMU_REPORT_SHADER_ERRORS 73 | #ifndef CRTEMU_REPORT_ERROR 74 | #define _CRT_NONSTDC_NO_DEPRECATE 75 | #define _CRT_SECURE_NO_WARNINGS 76 | #include 77 | #define CRTEMU_REPORT_ERROR( str ) printf( "%s", str ) 78 | #endif 79 | #endif 80 | 81 | #ifndef _WIN32 82 | #define CRTEMU_SDL 83 | #endif 84 | 85 | 86 | #ifndef CRTEMU_SDL 87 | 88 | #ifdef __cplusplus 89 | extern "C" { 90 | #endif 91 | 92 | __declspec(dllimport) struct HINSTANCE__* __stdcall LoadLibraryA( char const* lpLibFileName ); 93 | __declspec(dllimport) int __stdcall FreeLibrary( struct HINSTANCE__* hModule ); 94 | #if defined(_WIN64) 95 | typedef __int64 (__stdcall* CRTEMU_PROC)( void ); 96 | __declspec(dllimport) CRTEMU_PROC __stdcall GetProcAddress( struct HINSTANCE__* hModule, char const* lpLibFileName ); 97 | #else 98 | typedef __int32 (__stdcall* CRTEMU_PROC)( void ); 99 | __declspec(dllimport) CRTEMU_PROC __stdcall GetProcAddress( struct HINSTANCE__* hModule, char const* lpLibFileName ); 100 | #endif 101 | 102 | #ifdef __cplusplus 103 | } 104 | #endif 105 | 106 | #define CRTEMU_GLCALLTYPE __stdcall 107 | typedef unsigned int CRTEMU_GLuint; 108 | typedef int CRTEMU_GLsizei; 109 | typedef unsigned int CRTEMU_GLenum; 110 | typedef int CRTEMU_GLint; 111 | typedef float CRTEMU_GLfloat; 112 | typedef char CRTEMU_GLchar; 113 | typedef unsigned char CRTEMU_GLboolean; 114 | typedef size_t CRTEMU_GLsizeiptr; 115 | typedef unsigned int CRTEMU_GLbitfield; 116 | 117 | #define CRTEMU_GL_FLOAT 0x1406 118 | #define CRTEMU_GL_FALSE 0 119 | #define CRTEMU_GL_FRAGMENT_SHADER 0x8b30 120 | #define CRTEMU_GL_VERTEX_SHADER 0x8b31 121 | #define CRTEMU_GL_COMPILE_STATUS 0x8b81 122 | #define CRTEMU_GL_LINK_STATUS 0x8b82 123 | #define CRTEMU_GL_INFO_LOG_LENGTH 0x8b84 124 | #define CRTEMU_GL_ARRAY_BUFFER 0x8892 125 | #define CRTEMU_GL_TEXTURE_2D 0x0de1 126 | #define CRTEMU_GL_TEXTURE0 0x84c0 127 | #define CRTEMU_GL_TEXTURE1 0x84c1 128 | #define CRTEMU_GL_TEXTURE2 0x84c2 129 | #define CRTEMU_GL_TEXTURE3 0x84c3 130 | #define CRTEMU_GL_TEXTURE_MIN_FILTER 0x2801 131 | #define CRTEMU_GL_TEXTURE_MAG_FILTER 0x2800 132 | #define CRTEMU_GL_NEAREST 0x2600 133 | #define CRTEMU_GL_LINEAR 0x2601 134 | #define CRTEMU_GL_STATIC_DRAW 0x88e4 135 | #define CRTEMU_GL_RGBA 0x1908 136 | #define CRTEMU_GL_UNSIGNED_BYTE 0x1401 137 | #define CRTEMU_GL_COLOR_BUFFER_BIT 0x00004000 138 | #define CRTEMU_GL_TRIANGLE_FAN 0x0006 139 | #define CRTEMU_GL_FRAMEBUFFER 0x8d40 140 | #define CRTEMU_GL_VIEWPORT 0x0ba2 141 | #define CRTEMU_GL_RGB 0x1907 142 | #define CRTEMU_GL_COLOR_ATTACHMENT0 0x8ce0 143 | #define CRTEMU_GL_TEXTURE_WRAP_S 0x2802 144 | #define CRTEMU_GL_TEXTURE_WRAP_T 0x2803 145 | #define CRTEMU_GL_CLAMP_TO_BORDER 0x812D 146 | #define CRTEMU_GL_TEXTURE_BORDER_COLOR 0x1004 147 | 148 | #else 149 | 150 | #include 151 | #include 152 | #define CRTEMU_GLCALLTYPE GLAPIENTRY 153 | 154 | typedef GLuint CRTEMU_GLuint; 155 | typedef GLsizei CRTEMU_GLsizei; 156 | typedef GLenum CRTEMU_GLenum; 157 | typedef GLint CRTEMU_GLint; 158 | typedef GLfloat CRTEMU_GLfloat; 159 | typedef GLchar CRTEMU_GLchar; 160 | typedef GLboolean CRTEMU_GLboolean; 161 | typedef GLsizeiptr CRTEMU_GLsizeiptr; 162 | typedef GLbitfield CRTEMU_GLbitfield; 163 | 164 | #define CRTEMU_GL_FLOAT GL_FLOAT 165 | #define CRTEMU_GL_FALSE GL_FALSE 166 | #define CRTEMU_GL_FRAGMENT_SHADER GL_FRAGMENT_SHADER 167 | #define CRTEMU_GL_VERTEX_SHADER GL_VERTEX_SHADER 168 | #define CRTEMU_GL_COMPILE_STATUS GL_COMPILE_STATUS 169 | #define CRTEMU_GL_LINK_STATUS GL_LINK_STATUS 170 | #define CRTEMU_GL_INFO_LOG_LENGTH GL_INFO_LOG_LENGTH 171 | #define CRTEMU_GL_ARRAY_BUFFER GL_ARRAY_BUFFER 172 | #define CRTEMU_GL_TEXTURE_2D GL_TEXTURE_2D 173 | #define CRTEMU_GL_TEXTURE0 GL_TEXTURE0 174 | #define CRTEMU_GL_TEXTURE1 GL_TEXTURE1 175 | #define CRTEMU_GL_TEXTURE2 GL_TEXTURE2 176 | #define CRTEMU_GL_TEXTURE3 GL_TEXTURE3 177 | #define CRTEMU_GL_TEXTURE_MIN_FILTER GL_TEXTURE_MIN_FILTER 178 | #define CRTEMU_GL_TEXTURE_MAG_FILTER GL_TEXTURE_MAG_FILTER 179 | #define CRTEMU_GL_NEAREST GL_NEAREST 180 | #define CRTEMU_GL_LINEAR GL_LINEAR 181 | #define CRTEMU_GL_STATIC_DRAW GL_STATIC_DRAW 182 | #define CRTEMU_GL_RGBA GL_RGBA 183 | #define CRTEMU_GL_UNSIGNED_BYTE GL_UNSIGNED_BYTE 184 | #define CRTEMU_GL_COLOR_BUFFER_BIT GL_COLOR_BUFFER_BIT 185 | #define CRTEMU_GL_TRIANGLE_FAN GL_TRIANGLE_FAN 186 | #define CRTEMU_GL_FRAMEBUFFER GL_FRAMEBUFFER 187 | #define CRTEMU_GL_VIEWPORT GL_VIEWPORT 188 | #define CRTEMU_GL_RGB GL_RGB 189 | #define CRTEMU_GL_COLOR_ATTACHMENT0 GL_COLOR_ATTACHMENT0 190 | #define CRTEMU_GL_TEXTURE_WRAP_S GL_TEXTURE_WRAP_S 191 | #define CRTEMU_GL_TEXTURE_WRAP_T GL_TEXTURE_WRAP_T 192 | #define CRTEMU_GL_CLAMP_TO_BORDER GL_CLAMP_TO_BORDER 193 | #define CRTEMU_GL_TEXTURE_BORDER_COLOR GL_TEXTURE_BORDER_COLOR 194 | #endif 195 | 196 | 197 | struct crtemu_t 198 | { 199 | void* memctx; 200 | crtemu_config_t config; 201 | 202 | CRTEMU_GLuint vertexbuffer; 203 | CRTEMU_GLuint backbuffer; 204 | 205 | CRTEMU_GLuint accumulatetexture_a; 206 | CRTEMU_GLuint accumulatetexture_b; 207 | CRTEMU_GLuint accumulatebuffer_a; 208 | CRTEMU_GLuint accumulatebuffer_b; 209 | 210 | CRTEMU_GLuint blurtexture_a; 211 | CRTEMU_GLuint blurtexture_b; 212 | CRTEMU_GLuint blurbuffer_a; 213 | CRTEMU_GLuint blurbuffer_b; 214 | 215 | CRTEMU_GLuint frametexture; 216 | float use_frame; 217 | 218 | CRTEMU_GLuint crt_shader; 219 | CRTEMU_GLuint blur_shader; 220 | CRTEMU_GLuint accumulate_shader; 221 | CRTEMU_GLuint blend_shader; 222 | CRTEMU_GLuint copy_shader; 223 | 224 | int last_present_width; 225 | int last_present_height; 226 | 227 | 228 | #ifndef CRTEMU_SDL 229 | struct HINSTANCE__* gl_dll; 230 | CRTEMU_PROC (CRTEMU_GLCALLTYPE *wglGetProcAddress) (char const* ); 231 | #endif 232 | 233 | void (CRTEMU_GLCALLTYPE* TexParameterfv) (CRTEMU_GLenum target, CRTEMU_GLenum pname, CRTEMU_GLfloat const* params); 234 | void (CRTEMU_GLCALLTYPE* DeleteFramebuffers) (CRTEMU_GLsizei n, CRTEMU_GLuint const* framebuffers); 235 | void (CRTEMU_GLCALLTYPE* GetIntegerv) (CRTEMU_GLenum pname, CRTEMU_GLint *data); 236 | void (CRTEMU_GLCALLTYPE* GenFramebuffers) (CRTEMU_GLsizei n, CRTEMU_GLuint *framebuffers); 237 | void (CRTEMU_GLCALLTYPE* BindFramebuffer) (CRTEMU_GLenum target, CRTEMU_GLuint framebuffer); 238 | void (CRTEMU_GLCALLTYPE* Uniform1f) (CRTEMU_GLint location, CRTEMU_GLfloat v0); 239 | void (CRTEMU_GLCALLTYPE* Uniform2f) (CRTEMU_GLint location, CRTEMU_GLfloat v0, CRTEMU_GLfloat v1); 240 | void (CRTEMU_GLCALLTYPE* FramebufferTexture2D) (CRTEMU_GLenum target, CRTEMU_GLenum attachment, CRTEMU_GLenum textarget, CRTEMU_GLuint texture, CRTEMU_GLint level); 241 | CRTEMU_GLuint (CRTEMU_GLCALLTYPE* CreateShader) (CRTEMU_GLenum type); 242 | void (CRTEMU_GLCALLTYPE* ShaderSource) (CRTEMU_GLuint shader, CRTEMU_GLsizei count, CRTEMU_GLchar const* const* string, CRTEMU_GLint const* length); 243 | void (CRTEMU_GLCALLTYPE* CompileShader) (CRTEMU_GLuint shader); 244 | void (CRTEMU_GLCALLTYPE* GetShaderiv) (CRTEMU_GLuint shader, CRTEMU_GLenum pname, CRTEMU_GLint *params); 245 | CRTEMU_GLuint (CRTEMU_GLCALLTYPE* CreateProgram) (void); 246 | void (CRTEMU_GLCALLTYPE* AttachShader) (CRTEMU_GLuint program, CRTEMU_GLuint shader); 247 | void (CRTEMU_GLCALLTYPE* BindAttribLocation) (CRTEMU_GLuint program, CRTEMU_GLuint index, CRTEMU_GLchar const* name); 248 | void (CRTEMU_GLCALLTYPE* LinkProgram) (CRTEMU_GLuint program); 249 | void (CRTEMU_GLCALLTYPE* GetProgramiv) (CRTEMU_GLuint program, CRTEMU_GLenum pname, CRTEMU_GLint *params); 250 | void (CRTEMU_GLCALLTYPE* GenBuffers) (CRTEMU_GLsizei n, CRTEMU_GLuint *buffers); 251 | void (CRTEMU_GLCALLTYPE* BindBuffer) (CRTEMU_GLenum target, CRTEMU_GLuint buffer); 252 | void (CRTEMU_GLCALLTYPE* EnableVertexAttribArray) (CRTEMU_GLuint index); 253 | void (CRTEMU_GLCALLTYPE* VertexAttribPointer) (CRTEMU_GLuint index, CRTEMU_GLint size, CRTEMU_GLenum type, CRTEMU_GLboolean normalized, CRTEMU_GLsizei stride, void const* pointer); 254 | void (CRTEMU_GLCALLTYPE* GenTextures) (CRTEMU_GLsizei n, CRTEMU_GLuint* textures); 255 | void (CRTEMU_GLCALLTYPE* Enable) (CRTEMU_GLenum cap); 256 | void (CRTEMU_GLCALLTYPE* ActiveTexture) (CRTEMU_GLenum texture); 257 | void (CRTEMU_GLCALLTYPE* BindTexture) (CRTEMU_GLenum target, CRTEMU_GLuint texture); 258 | void (CRTEMU_GLCALLTYPE* TexParameteri) (CRTEMU_GLenum target, CRTEMU_GLenum pname, CRTEMU_GLint param); 259 | void (CRTEMU_GLCALLTYPE* DeleteBuffers) (CRTEMU_GLsizei n, CRTEMU_GLuint const* buffers); 260 | void (CRTEMU_GLCALLTYPE* DeleteTextures) (CRTEMU_GLsizei n, CRTEMU_GLuint const* textures); 261 | void (CRTEMU_GLCALLTYPE* BufferData) (CRTEMU_GLenum target, CRTEMU_GLsizeiptr size, void const *data, CRTEMU_GLenum usage); 262 | void (CRTEMU_GLCALLTYPE* UseProgram) (CRTEMU_GLuint program); 263 | void (CRTEMU_GLCALLTYPE* Uniform1i) (CRTEMU_GLint location, CRTEMU_GLint v0); 264 | void (CRTEMU_GLCALLTYPE* Uniform3f) (CRTEMU_GLint location, CRTEMU_GLfloat v0, CRTEMU_GLfloat v1, CRTEMU_GLfloat v2); 265 | CRTEMU_GLint (CRTEMU_GLCALLTYPE* GetUniformLocation) (CRTEMU_GLuint program, CRTEMU_GLchar const* name); 266 | void (CRTEMU_GLCALLTYPE* TexImage2D) (CRTEMU_GLenum target, CRTEMU_GLint level, CRTEMU_GLint internalformat, CRTEMU_GLsizei width, CRTEMU_GLsizei height, CRTEMU_GLint border, CRTEMU_GLenum format, CRTEMU_GLenum type, void const* pixels); 267 | void (CRTEMU_GLCALLTYPE* ClearColor) (CRTEMU_GLfloat red, CRTEMU_GLfloat green, CRTEMU_GLfloat blue, CRTEMU_GLfloat alpha); 268 | void (CRTEMU_GLCALLTYPE* Clear) (CRTEMU_GLbitfield mask); 269 | void (CRTEMU_GLCALLTYPE* DrawArrays) (CRTEMU_GLenum mode, CRTEMU_GLint first, CRTEMU_GLsizei count); 270 | void (CRTEMU_GLCALLTYPE* Viewport) (CRTEMU_GLint x, CRTEMU_GLint y, CRTEMU_GLsizei width, CRTEMU_GLsizei height); 271 | void (CRTEMU_GLCALLTYPE* DeleteShader) (CRTEMU_GLuint shader); 272 | void (CRTEMU_GLCALLTYPE* DeleteProgram) (CRTEMU_GLuint program); 273 | #ifdef CRTEMU_REPORT_SHADER_ERRORS 274 | void (CRTEMU_GLCALLTYPE* GetShaderInfoLog) (CRTEMU_GLuint shader, CRTEMU_GLsizei bufSize, CRTEMU_GLsizei *length, CRTEMU_GLchar *infoLog); 275 | #endif 276 | }; 277 | 278 | 279 | static CRTEMU_GLuint crtemu_internal_build_shader( crtemu_t* crtemu, char const* vs_source, char const* fs_source ) 280 | { 281 | #ifdef CRTEMU_REPORT_SHADER_ERRORS 282 | char error_message[ 1024 ]; 283 | #endif 284 | 285 | CRTEMU_GLuint vs = crtemu->CreateShader( CRTEMU_GL_VERTEX_SHADER ); 286 | crtemu->ShaderSource( vs, 1, (char const**) &vs_source, NULL ); 287 | crtemu->CompileShader( vs ); 288 | CRTEMU_GLint vs_compiled; 289 | crtemu->GetShaderiv( vs, CRTEMU_GL_COMPILE_STATUS, &vs_compiled ); 290 | if( !vs_compiled ) 291 | { 292 | #ifdef CRTEMU_REPORT_SHADER_ERRORS 293 | char const* prefix = "Vertex Shader Error: "; 294 | strcpy( error_message, prefix ); 295 | int len = 0, written = 0; 296 | crtemu->GetShaderiv( vs, CRTEMU_GL_INFO_LOG_LENGTH, &len ); 297 | crtemu->GetShaderInfoLog( vs, (CRTEMU_GLsizei)( sizeof( error_message ) - strlen( prefix ) ), &written, 298 | error_message + strlen( prefix ) ); 299 | CRTEMU_REPORT_ERROR( error_message ); 300 | #endif 301 | return 0; 302 | } 303 | 304 | CRTEMU_GLuint fs = crtemu->CreateShader( CRTEMU_GL_FRAGMENT_SHADER ); 305 | crtemu->ShaderSource( fs, 1, (char const**) &fs_source, NULL ); 306 | crtemu->CompileShader( fs ); 307 | CRTEMU_GLint fs_compiled; 308 | crtemu->GetShaderiv( fs, CRTEMU_GL_COMPILE_STATUS, &fs_compiled ); 309 | if( !fs_compiled ) 310 | { 311 | #ifdef CRTEMU_REPORT_SHADER_ERRORS 312 | char const* prefix = "Fragment Shader Error: "; 313 | strcpy( error_message, prefix ); 314 | int len = 0, written = 0; 315 | crtemu->GetShaderiv( vs, CRTEMU_GL_INFO_LOG_LENGTH, &len ); 316 | crtemu->GetShaderInfoLog( fs, (CRTEMU_GLsizei)( sizeof( error_message ) - strlen( prefix ) ), &written, 317 | error_message + strlen( prefix ) ); 318 | CRTEMU_REPORT_ERROR( error_message ); 319 | #endif 320 | return 0; 321 | } 322 | 323 | 324 | CRTEMU_GLuint prg = crtemu->CreateProgram(); 325 | crtemu->AttachShader( prg, fs ); 326 | crtemu->AttachShader( prg, vs ); 327 | crtemu->BindAttribLocation( prg, 0, "pos" ); 328 | crtemu->LinkProgram( prg ); 329 | 330 | CRTEMU_GLint linked; 331 | crtemu->GetProgramiv( prg, CRTEMU_GL_LINK_STATUS, &linked ); 332 | if( !linked ) 333 | { 334 | #ifdef CRTEMU_REPORT_SHADER_ERRORS 335 | char const* prefix = "Shader Link Error: "; 336 | strcpy( error_message, prefix ); 337 | int len = 0, written = 0; 338 | crtemu->GetShaderiv( vs, CRTEMU_GL_INFO_LOG_LENGTH, &len ); 339 | crtemu->GetShaderInfoLog( prg, (CRTEMU_GLsizei)( sizeof( error_message ) - strlen( prefix ) ), &written, 340 | error_message + strlen( prefix ) ); 341 | CRTEMU_REPORT_ERROR( error_message ); 342 | #endif 343 | return 0; 344 | } 345 | 346 | return prg; 347 | } 348 | 349 | 350 | crtemu_t* crtemu_create( void* memctx ) 351 | { 352 | crtemu_t* crtemu = (crtemu_t*) CRTEMU_MALLOC( memctx, sizeof( crtemu_t ) ); 353 | memset( crtemu, 0, sizeof( crtemu_t ) ); 354 | crtemu->memctx = memctx; 355 | 356 | crtemu->config.curvature = 0.0f; 357 | crtemu->config.scanlines = 0.0f; 358 | crtemu->config.shadow_mask = 0.0f; 359 | crtemu->config.separation = 0.0f; 360 | crtemu->config.ghosting = 0.0f; 361 | crtemu->config.noise = 0.0f; 362 | crtemu->config.flicker = 0.0f; 363 | crtemu->config.vignette = 0.0f; 364 | crtemu->config.distortion = 0.0f; 365 | crtemu->config.aspect_lock = 0.0f; 366 | crtemu->config.hpos = 0.0f; 367 | crtemu->config.vpos = 0.0f; 368 | crtemu->config.hsize = 0.0f; 369 | crtemu->config.vsize = 0.0f; 370 | crtemu->config.contrast = 0.0f; 371 | crtemu->config.brightness = 0.0f; 372 | crtemu->config.saturation = 0.0f; 373 | crtemu->config.blur = 0.0f; 374 | crtemu->config.degauss = 0.0f; 375 | 376 | crtemu->use_frame = 0.0f; 377 | 378 | crtemu->last_present_width = 0; 379 | crtemu->last_present_height = 0; 380 | 381 | #ifndef CRTEMU_SDL 382 | 383 | crtemu->gl_dll = LoadLibraryA( "opengl32.dll" ); 384 | if( !crtemu->gl_dll ) goto failed; 385 | 386 | crtemu->wglGetProcAddress = (CRTEMU_PROC (CRTEMU_GLCALLTYPE*)(char const*)) (uintptr_t) GetProcAddress( crtemu->gl_dll, "wglGetProcAddress" ); 387 | if( !crtemu->gl_dll ) goto failed; 388 | 389 | // Attempt to bind opengl functions using GetProcAddress 390 | crtemu->TexParameterfv = ( void (CRTEMU_GLCALLTYPE*) (CRTEMU_GLenum, CRTEMU_GLenum, CRTEMU_GLfloat const*) ) (uintptr_t) GetProcAddress( crtemu->gl_dll, "glTexParameterfv" ); 391 | crtemu->DeleteFramebuffers = ( void (CRTEMU_GLCALLTYPE*) (CRTEMU_GLsizei, CRTEMU_GLuint const*) ) (uintptr_t) GetProcAddress( crtemu->gl_dll, "glDeleteFramebuffers" ); 392 | crtemu->GetIntegerv = ( void (CRTEMU_GLCALLTYPE*) (CRTEMU_GLenum, CRTEMU_GLint *) ) (uintptr_t) GetProcAddress( crtemu->gl_dll, "glGetIntegerv" ); 393 | crtemu->GenFramebuffers = ( void (CRTEMU_GLCALLTYPE*) (CRTEMU_GLsizei, CRTEMU_GLuint *) ) (uintptr_t) GetProcAddress( crtemu->gl_dll, "glGenFramebuffers" ); 394 | crtemu->BindFramebuffer = ( void (CRTEMU_GLCALLTYPE*) (CRTEMU_GLenum, CRTEMU_GLuint) ) (uintptr_t) GetProcAddress( crtemu->gl_dll, "glBindFramebuffer" ); 395 | crtemu->Uniform1f = ( void (CRTEMU_GLCALLTYPE*) (CRTEMU_GLint, CRTEMU_GLfloat) ) (uintptr_t) GetProcAddress( crtemu->gl_dll, "glUniform1f" ); 396 | crtemu->Uniform2f = ( void (CRTEMU_GLCALLTYPE*) (CRTEMU_GLint, CRTEMU_GLfloat, CRTEMU_GLfloat) ) (uintptr_t) GetProcAddress( crtemu->gl_dll, "glUniform2f" ); 397 | crtemu->FramebufferTexture2D = ( void (CRTEMU_GLCALLTYPE*) (CRTEMU_GLenum, CRTEMU_GLenum, CRTEMU_GLenum, CRTEMU_GLuint, CRTEMU_GLint) ) (uintptr_t) GetProcAddress( crtemu->gl_dll, "glFramebufferTexture2D" ); 398 | crtemu->CreateShader = ( CRTEMU_GLuint (CRTEMU_GLCALLTYPE*) (CRTEMU_GLenum) ) (uintptr_t) GetProcAddress( crtemu->gl_dll, "glCreateShader" ); 399 | crtemu->ShaderSource = ( void (CRTEMU_GLCALLTYPE*) (CRTEMU_GLuint, CRTEMU_GLsizei, CRTEMU_GLchar const* const*, CRTEMU_GLint const*) ) (uintptr_t) GetProcAddress( crtemu->gl_dll, "glShaderSource" ); 400 | crtemu->CompileShader = ( void (CRTEMU_GLCALLTYPE*) (CRTEMU_GLuint) ) (uintptr_t) GetProcAddress( crtemu->gl_dll, "glCompileShader" ); 401 | crtemu->GetShaderiv = ( void (CRTEMU_GLCALLTYPE*) (CRTEMU_GLuint, CRTEMU_GLenum, CRTEMU_GLint*) ) (uintptr_t) GetProcAddress( crtemu->gl_dll, "glGetShaderiv" ); 402 | crtemu->CreateProgram = ( CRTEMU_GLuint (CRTEMU_GLCALLTYPE*) (void) ) (uintptr_t) GetProcAddress( crtemu->gl_dll, "glCreateProgram" ); 403 | crtemu->AttachShader = ( void (CRTEMU_GLCALLTYPE*) (CRTEMU_GLuint, CRTEMU_GLuint) ) (uintptr_t) GetProcAddress( crtemu->gl_dll, "glAttachShader" ); 404 | crtemu->BindAttribLocation = ( void (CRTEMU_GLCALLTYPE*) (CRTEMU_GLuint, CRTEMU_GLuint, CRTEMU_GLchar const*) ) (uintptr_t) GetProcAddress( crtemu->gl_dll, "glBindAttribLocation" ); 405 | crtemu->LinkProgram = ( void (CRTEMU_GLCALLTYPE*) (CRTEMU_GLuint) ) (uintptr_t) GetProcAddress( crtemu->gl_dll, "glLinkProgram" ); 406 | crtemu->GetProgramiv = ( void (CRTEMU_GLCALLTYPE*) (CRTEMU_GLuint, CRTEMU_GLenum, CRTEMU_GLint*) ) (uintptr_t) GetProcAddress( crtemu->gl_dll, "glGetProgramiv" ); 407 | crtemu->GenBuffers = ( void (CRTEMU_GLCALLTYPE*) (CRTEMU_GLsizei, CRTEMU_GLuint*) ) (uintptr_t) GetProcAddress( crtemu->gl_dll, "glGenBuffers" ); 408 | crtemu->BindBuffer = ( void (CRTEMU_GLCALLTYPE*) (CRTEMU_GLenum, CRTEMU_GLuint) ) (uintptr_t) GetProcAddress( crtemu->gl_dll, "glBindBuffer" ); 409 | crtemu->EnableVertexAttribArray = ( void (CRTEMU_GLCALLTYPE*) (CRTEMU_GLuint) ) (uintptr_t) GetProcAddress( crtemu->gl_dll, "glEnableVertexAttribArray" ); 410 | crtemu->VertexAttribPointer = ( void (CRTEMU_GLCALLTYPE*) (CRTEMU_GLuint, CRTEMU_GLint, CRTEMU_GLenum, CRTEMU_GLboolean, CRTEMU_GLsizei, void const*) ) (uintptr_t) GetProcAddress( crtemu->gl_dll, "glVertexAttribPointer" ); 411 | crtemu->GenTextures = ( void (CRTEMU_GLCALLTYPE*) (CRTEMU_GLsizei, CRTEMU_GLuint*) ) (uintptr_t) GetProcAddress( crtemu->gl_dll, "glGenTextures" ); 412 | crtemu->Enable = ( void (CRTEMU_GLCALLTYPE*) (CRTEMU_GLenum) ) (uintptr_t) GetProcAddress( crtemu->gl_dll, "glEnable" ); 413 | crtemu->ActiveTexture = ( void (CRTEMU_GLCALLTYPE*) (CRTEMU_GLenum) ) (uintptr_t) GetProcAddress( crtemu->gl_dll, "glActiveTexture" ); 414 | crtemu->BindTexture = ( void (CRTEMU_GLCALLTYPE*) (CRTEMU_GLenum, CRTEMU_GLuint) ) (uintptr_t) GetProcAddress( crtemu->gl_dll, "glBindTexture" ); 415 | crtemu->TexParameteri = ( void (CRTEMU_GLCALLTYPE*) (CRTEMU_GLenum, CRTEMU_GLenum, CRTEMU_GLint) ) (uintptr_t) GetProcAddress( crtemu->gl_dll, "glTexParameteri" ); 416 | crtemu->DeleteBuffers = ( void (CRTEMU_GLCALLTYPE*) (CRTEMU_GLsizei, CRTEMU_GLuint const*) ) (uintptr_t) GetProcAddress( crtemu->gl_dll, "glDeleteBuffers" ); 417 | crtemu->DeleteTextures = ( void (CRTEMU_GLCALLTYPE*) (CRTEMU_GLsizei, CRTEMU_GLuint const*) ) (uintptr_t) GetProcAddress( crtemu->gl_dll, "glDeleteTextures" ); 418 | crtemu->BufferData = ( void (CRTEMU_GLCALLTYPE*) (CRTEMU_GLenum, CRTEMU_GLsizeiptr, void const *, CRTEMU_GLenum) ) (uintptr_t) GetProcAddress( crtemu->gl_dll, "glBufferData" ); 419 | crtemu->UseProgram = ( void (CRTEMU_GLCALLTYPE*) (CRTEMU_GLuint) ) (uintptr_t) GetProcAddress( crtemu->gl_dll, "glUseProgram" ); 420 | crtemu->Uniform1i = ( void (CRTEMU_GLCALLTYPE*) (CRTEMU_GLint, CRTEMU_GLint) ) (uintptr_t) GetProcAddress( crtemu->gl_dll, "glUniform1i" ); 421 | crtemu->Uniform3f = ( void (CRTEMU_GLCALLTYPE*) (CRTEMU_GLint, CRTEMU_GLfloat, CRTEMU_GLfloat, CRTEMU_GLfloat) ) (uintptr_t) GetProcAddress( crtemu->gl_dll, "glUniform3f" ); 422 | crtemu->GetUniformLocation = ( CRTEMU_GLint (CRTEMU_GLCALLTYPE*) (CRTEMU_GLuint, CRTEMU_GLchar const*) ) (uintptr_t) GetProcAddress( crtemu->gl_dll, "glGetUniformLocation" ); 423 | crtemu->TexImage2D = ( void (CRTEMU_GLCALLTYPE*) (CRTEMU_GLenum, CRTEMU_GLint, CRTEMU_GLint, CRTEMU_GLsizei, CRTEMU_GLsizei, CRTEMU_GLint, CRTEMU_GLenum, CRTEMU_GLenum, void const*) ) (uintptr_t) GetProcAddress( crtemu->gl_dll, "glTexImage2D" ); 424 | crtemu->ClearColor = ( void (CRTEMU_GLCALLTYPE*) (CRTEMU_GLfloat, CRTEMU_GLfloat, CRTEMU_GLfloat, CRTEMU_GLfloat) ) (uintptr_t) GetProcAddress( crtemu->gl_dll, "glClearColor" ); 425 | crtemu->Clear = ( void (CRTEMU_GLCALLTYPE*) (CRTEMU_GLbitfield) ) (uintptr_t) GetProcAddress( crtemu->gl_dll, "glClear" ); 426 | crtemu->DrawArrays = ( void (CRTEMU_GLCALLTYPE*) (CRTEMU_GLenum, CRTEMU_GLint, CRTEMU_GLsizei) ) (uintptr_t) GetProcAddress( crtemu->gl_dll, "glDrawArrays" ); 427 | crtemu->Viewport = ( void (CRTEMU_GLCALLTYPE*) (CRTEMU_GLint, CRTEMU_GLint, CRTEMU_GLsizei, CRTEMU_GLsizei) ) (uintptr_t) GetProcAddress( crtemu->gl_dll, "glViewport" ); 428 | crtemu->DeleteShader = ( void (CRTEMU_GLCALLTYPE*) (CRTEMU_GLuint) ) (uintptr_t) GetProcAddress( crtemu->gl_dll, "glDeleteShader" ); 429 | crtemu->DeleteProgram = ( void (CRTEMU_GLCALLTYPE*) (CRTEMU_GLuint) ) (uintptr_t) GetProcAddress( crtemu->gl_dll, "glDeleteProgram" ); 430 | #ifdef CRTEMU_REPORT_SHADER_ERRORS 431 | crtemu->GetShaderInfoLog = ( void (CRTEMU_GLCALLTYPE*) (CRTEMU_GLuint, CRTEMU_GLsizei, CRTEMU_GLsizei*, CRTEMU_GLchar*) ) (uintptr_t) GetProcAddress( crtemu->gl_dll, "glGetShaderInfoLog" ); 432 | #endif 433 | 434 | // Any opengl functions which didn't bind, try binding them using wglGetProcAddrss 435 | if( !crtemu->TexParameterfv ) crtemu->TexParameterfv = ( void (CRTEMU_GLCALLTYPE*) (CRTEMU_GLenum, CRTEMU_GLenum, CRTEMU_GLfloat const*) ) (uintptr_t) crtemu->wglGetProcAddress( "glTexParameterfv" ); 436 | if( !crtemu->DeleteFramebuffers ) crtemu->DeleteFramebuffers = ( void (CRTEMU_GLCALLTYPE*) (CRTEMU_GLsizei, CRTEMU_GLuint const*) ) (uintptr_t) crtemu->wglGetProcAddress( "glDeleteFramebuffers" ); 437 | if( !crtemu->GetIntegerv ) crtemu->GetIntegerv = ( void (CRTEMU_GLCALLTYPE*) (CRTEMU_GLenum, CRTEMU_GLint *) ) (uintptr_t) crtemu->wglGetProcAddress( "glGetIntegerv" ); 438 | if( !crtemu->GenFramebuffers ) crtemu->GenFramebuffers = ( void (CRTEMU_GLCALLTYPE*) (CRTEMU_GLsizei, CRTEMU_GLuint *) ) (uintptr_t) crtemu->wglGetProcAddress( "glGenFramebuffers" ); 439 | if( !crtemu->BindFramebuffer ) crtemu->BindFramebuffer = ( void (CRTEMU_GLCALLTYPE*) (CRTEMU_GLenum, CRTEMU_GLuint) ) (uintptr_t) crtemu->wglGetProcAddress( "glBindFramebuffer" ); 440 | if( !crtemu->Uniform1f ) crtemu->Uniform1f = ( void (CRTEMU_GLCALLTYPE*) (CRTEMU_GLint, CRTEMU_GLfloat) ) (uintptr_t) crtemu->wglGetProcAddress( "glUniform1f" ); 441 | if( !crtemu->Uniform2f ) crtemu->Uniform2f = ( void (CRTEMU_GLCALLTYPE*) (CRTEMU_GLint, CRTEMU_GLfloat, CRTEMU_GLfloat) ) (uintptr_t) crtemu->wglGetProcAddress( "glUniform2f" ); 442 | if( !crtemu->FramebufferTexture2D ) crtemu->FramebufferTexture2D = ( void (CRTEMU_GLCALLTYPE*) (CRTEMU_GLenum, CRTEMU_GLenum, CRTEMU_GLenum, CRTEMU_GLuint, CRTEMU_GLint) ) (uintptr_t) crtemu->wglGetProcAddress( "glFramebufferTexture2D" ); 443 | if( !crtemu->CreateShader ) crtemu->CreateShader = ( CRTEMU_GLuint (CRTEMU_GLCALLTYPE*) (CRTEMU_GLenum) ) (uintptr_t) crtemu->wglGetProcAddress( "glCreateShader" ); 444 | if( !crtemu->ShaderSource ) crtemu->ShaderSource = ( void (CRTEMU_GLCALLTYPE*) (CRTEMU_GLuint, CRTEMU_GLsizei, CRTEMU_GLchar const* const*, CRTEMU_GLint const*) ) (uintptr_t) crtemu->wglGetProcAddress( "glShaderSource" ); 445 | if( !crtemu->CompileShader ) crtemu->CompileShader = ( void (CRTEMU_GLCALLTYPE*) (CRTEMU_GLuint) ) (uintptr_t) crtemu->wglGetProcAddress( "glCompileShader" ); 446 | if( !crtemu->GetShaderiv ) crtemu->GetShaderiv = ( void (CRTEMU_GLCALLTYPE*) (CRTEMU_GLuint, CRTEMU_GLenum, CRTEMU_GLint*) ) (uintptr_t) crtemu->wglGetProcAddress( "glGetShaderiv" ); 447 | if( !crtemu->CreateProgram ) crtemu->CreateProgram = ( CRTEMU_GLuint (CRTEMU_GLCALLTYPE*) (void) ) (uintptr_t) crtemu->wglGetProcAddress( "glCreateProgram" ); 448 | if( !crtemu->AttachShader ) crtemu->AttachShader = ( void (CRTEMU_GLCALLTYPE*) (CRTEMU_GLuint, CRTEMU_GLuint) ) (uintptr_t) crtemu->wglGetProcAddress( "glAttachShader" ); 449 | if( !crtemu->BindAttribLocation ) crtemu->BindAttribLocation = ( void (CRTEMU_GLCALLTYPE*) (CRTEMU_GLuint, CRTEMU_GLuint, CRTEMU_GLchar const*) ) (uintptr_t) crtemu->wglGetProcAddress( "glBindAttribLocation" ); 450 | if( !crtemu->LinkProgram ) crtemu->LinkProgram = ( void (CRTEMU_GLCALLTYPE*) (CRTEMU_GLuint) ) (uintptr_t) crtemu->wglGetProcAddress( "glLinkProgram" ); 451 | if( !crtemu->GetProgramiv ) crtemu->GetProgramiv = ( void (CRTEMU_GLCALLTYPE*) (CRTEMU_GLuint, CRTEMU_GLenum, CRTEMU_GLint*) ) (uintptr_t) crtemu->wglGetProcAddress( "glGetProgramiv" ); 452 | if( !crtemu->GenBuffers ) crtemu->GenBuffers = ( void (CRTEMU_GLCALLTYPE*) (CRTEMU_GLsizei, CRTEMU_GLuint*) ) (uintptr_t) crtemu->wglGetProcAddress( "glGenBuffers" ); 453 | if( !crtemu->BindBuffer ) crtemu->BindBuffer = ( void (CRTEMU_GLCALLTYPE*) (CRTEMU_GLenum, CRTEMU_GLuint) ) (uintptr_t) crtemu->wglGetProcAddress( "glBindBuffer" ); 454 | if( !crtemu->EnableVertexAttribArray ) crtemu->EnableVertexAttribArray = ( void (CRTEMU_GLCALLTYPE*) (CRTEMU_GLuint) ) (uintptr_t) crtemu->wglGetProcAddress( "glEnableVertexAttribArray" ); 455 | if( !crtemu->VertexAttribPointer ) crtemu->VertexAttribPointer = ( void (CRTEMU_GLCALLTYPE*) (CRTEMU_GLuint, CRTEMU_GLint, CRTEMU_GLenum, CRTEMU_GLboolean, CRTEMU_GLsizei, void const*) ) (uintptr_t) crtemu->wglGetProcAddress( "glVertexAttribPointer" ); 456 | if( !crtemu->GenTextures ) crtemu->GenTextures = ( void (CRTEMU_GLCALLTYPE*) (CRTEMU_GLsizei, CRTEMU_GLuint*) ) (uintptr_t) crtemu->wglGetProcAddress( "glGenTextures" ); 457 | if( !crtemu->Enable ) crtemu->Enable = ( void (CRTEMU_GLCALLTYPE*) (CRTEMU_GLenum) ) (uintptr_t) crtemu->wglGetProcAddress( "glEnable" ); 458 | if( !crtemu->ActiveTexture ) crtemu->ActiveTexture = ( void (CRTEMU_GLCALLTYPE*) (CRTEMU_GLenum) ) (uintptr_t) crtemu->wglGetProcAddress( "glActiveTexture" ); 459 | if( !crtemu->BindTexture ) crtemu->BindTexture = ( void (CRTEMU_GLCALLTYPE*) (CRTEMU_GLenum, CRTEMU_GLuint) ) (uintptr_t) crtemu->wglGetProcAddress( "glBindTexture" ); 460 | if( !crtemu->TexParameteri ) crtemu->TexParameteri = ( void (CRTEMU_GLCALLTYPE*) (CRTEMU_GLenum, CRTEMU_GLenum, CRTEMU_GLint) ) (uintptr_t) crtemu->wglGetProcAddress( "glTexParameteri" ); 461 | if( !crtemu->DeleteBuffers ) crtemu->DeleteBuffers = ( void (CRTEMU_GLCALLTYPE*) (CRTEMU_GLsizei, CRTEMU_GLuint const*) ) (uintptr_t) crtemu->wglGetProcAddress( "glDeleteBuffers" ); 462 | if( !crtemu->DeleteTextures ) crtemu->DeleteTextures = ( void (CRTEMU_GLCALLTYPE*) (CRTEMU_GLsizei, CRTEMU_GLuint const*) ) (uintptr_t) crtemu->wglGetProcAddress( "glDeleteTextures" ); 463 | if( !crtemu->BufferData ) crtemu->BufferData = ( void (CRTEMU_GLCALLTYPE*) (CRTEMU_GLenum, CRTEMU_GLsizeiptr, void const *, CRTEMU_GLenum) ) (uintptr_t) crtemu->wglGetProcAddress( "glBufferData" ); 464 | if( !crtemu->UseProgram ) crtemu->UseProgram = ( void (CRTEMU_GLCALLTYPE*) (CRTEMU_GLuint) ) (uintptr_t) crtemu->wglGetProcAddress( "glUseProgram" ); 465 | if( !crtemu->Uniform1i ) crtemu->Uniform1i = ( void (CRTEMU_GLCALLTYPE*) (CRTEMU_GLint, CRTEMU_GLint) ) (uintptr_t) crtemu->wglGetProcAddress( "glUniform1i" ); 466 | if( !crtemu->Uniform3f ) crtemu->Uniform3f = ( void (CRTEMU_GLCALLTYPE*) (CRTEMU_GLint, CRTEMU_GLfloat, CRTEMU_GLfloat, CRTEMU_GLfloat) ) (uintptr_t) crtemu->wglGetProcAddress( "glUniform3f" ); 467 | if( !crtemu->GetUniformLocation ) crtemu->GetUniformLocation = ( CRTEMU_GLint (CRTEMU_GLCALLTYPE*) (CRTEMU_GLuint, CRTEMU_GLchar const*) ) (uintptr_t) crtemu->wglGetProcAddress( "glGetUniformLocation" ); 468 | if( !crtemu->TexImage2D ) crtemu->TexImage2D = ( void (CRTEMU_GLCALLTYPE*) (CRTEMU_GLenum, CRTEMU_GLint, CRTEMU_GLint, CRTEMU_GLsizei, CRTEMU_GLsizei, CRTEMU_GLint, CRTEMU_GLenum, CRTEMU_GLenum, void const*) ) (uintptr_t) crtemu->wglGetProcAddress( "glTexImage2D" ); 469 | if( !crtemu->ClearColor ) crtemu->ClearColor = ( void (CRTEMU_GLCALLTYPE*) (CRTEMU_GLfloat, CRTEMU_GLfloat, CRTEMU_GLfloat, CRTEMU_GLfloat) ) (uintptr_t) crtemu->wglGetProcAddress( "glClearColor" ); 470 | if( !crtemu->Clear ) crtemu->Clear = ( void (CRTEMU_GLCALLTYPE*) (CRTEMU_GLbitfield) ) (uintptr_t) crtemu->wglGetProcAddress( "glClear" ); 471 | if( !crtemu->DrawArrays ) crtemu->DrawArrays = ( void (CRTEMU_GLCALLTYPE*) (CRTEMU_GLenum, CRTEMU_GLint, CRTEMU_GLsizei) ) (uintptr_t) crtemu->wglGetProcAddress( "glDrawArrays" ); 472 | if( !crtemu->Viewport ) crtemu->Viewport = ( void (CRTEMU_GLCALLTYPE*) (CRTEMU_GLint, CRTEMU_GLint, CRTEMU_GLsizei, CRTEMU_GLsizei) ) (uintptr_t) crtemu->wglGetProcAddress( "glViewport" ); 473 | if( !crtemu->DeleteShader ) crtemu->DeleteShader = ( void (CRTEMU_GLCALLTYPE*) (CRTEMU_GLuint) ) (uintptr_t) crtemu->wglGetProcAddress( "glDeleteShader" ); 474 | if( !crtemu->DeleteProgram ) crtemu->DeleteProgram = ( void (CRTEMU_GLCALLTYPE*) (CRTEMU_GLuint) ) (uintptr_t) crtemu->wglGetProcAddress( "glDeleteProgram" ); 475 | #ifdef CRTEMU_REPORT_SHADER_ERRORS 476 | if( !crtemu->GetShaderInfoLog ) crtemu->GetShaderInfoLog = ( void (CRTEMU_GLCALLTYPE*) (CRTEMU_GLuint, CRTEMU_GLsizei, CRTEMU_GLsizei*, CRTEMU_GLchar*) ) (uintptr_t) crtemu->wglGetProcAddress( "glGetShaderInfoLog" ); 477 | #endif 478 | 479 | #else 480 | 481 | crtemu->TexParameterfv = glTexParameterfv; 482 | crtemu->DeleteFramebuffers = glDeleteFramebuffers; 483 | crtemu->GetIntegerv = glGetIntegerv; 484 | crtemu->GenFramebuffers = glGenFramebuffers; 485 | crtemu->BindFramebuffer = glBindFramebuffer; 486 | crtemu->Uniform1f = glUniform1f; 487 | crtemu->Uniform2f = glUniform2f; 488 | crtemu->FramebufferTexture2D = glFramebufferTexture2D; 489 | crtemu->CreateShader = glCreateShader; 490 | crtemu->ShaderSource = glShaderSource; 491 | crtemu->CompileShader = glCompileShader; 492 | crtemu->GetShaderiv = glGetShaderiv; 493 | crtemu->CreateProgram = glCreateProgram; 494 | crtemu->AttachShader = glAttachShader; 495 | crtemu->BindAttribLocation = glBindAttribLocation; 496 | crtemu->LinkProgram = glLinkProgram; 497 | crtemu->GetProgramiv = glGetProgramiv; 498 | crtemu->GenBuffers = glGenBuffers; 499 | crtemu->BindBuffer = glBindBuffer; 500 | crtemu->EnableVertexAttribArray = glEnableVertexAttribArray; 501 | crtemu->VertexAttribPointer = glVertexAttribPointer; 502 | crtemu->GenTextures = glGenTextures; 503 | crtemu->Enable = glEnable; 504 | crtemu->ActiveTexture = glActiveTexture; 505 | crtemu->BindTexture = glBindTexture; 506 | crtemu->TexParameteri = glTexParameteri; 507 | crtemu->DeleteBuffers = glDeleteBuffers; 508 | crtemu->DeleteTextures = glDeleteTextures; 509 | crtemu->BufferData = glBufferData; 510 | crtemu->UseProgram = glUseProgram; 511 | crtemu->Uniform1i = glUniform1i; 512 | crtemu->Uniform3f = glUniform3f; 513 | crtemu->GetUniformLocation = glGetUniformLocation; 514 | crtemu->TexImage2D = glTexImage2D; 515 | crtemu->ClearColor = glClearColor; 516 | crtemu->Clear = glClear; 517 | crtemu->DrawArrays = glDrawArrays; 518 | crtemu->Viewport = glViewport; 519 | crtemu->DeleteShader = glDeleteShader; 520 | crtemu->DeleteProgram = glDeleteProgram; 521 | #ifdef CRTEMU_REPORT_SHADER_ERRORS 522 | crtemu->GetShaderInfoLog = glGetShaderInfoLog; 523 | #endif 524 | 525 | #endif 526 | 527 | // Report error if any gl function was not found. 528 | if( !crtemu->TexParameterfv ) goto failed; 529 | if( !crtemu->DeleteFramebuffers ) goto failed; 530 | if( !crtemu->GetIntegerv ) goto failed; 531 | if( !crtemu->GenFramebuffers ) goto failed; 532 | if( !crtemu->BindFramebuffer ) goto failed; 533 | if( !crtemu->Uniform1f ) goto failed; 534 | if( !crtemu->Uniform2f ) goto failed; 535 | if( !crtemu->FramebufferTexture2D ) goto failed; 536 | if( !crtemu->CreateShader ) goto failed; 537 | if( !crtemu->ShaderSource ) goto failed; 538 | if( !crtemu->CompileShader ) goto failed; 539 | if( !crtemu->GetShaderiv ) goto failed; 540 | if( !crtemu->CreateProgram ) goto failed; 541 | if( !crtemu->AttachShader ) goto failed; 542 | if( !crtemu->BindAttribLocation ) goto failed; 543 | if( !crtemu->LinkProgram ) goto failed; 544 | if( !crtemu->GetProgramiv ) goto failed; 545 | if( !crtemu->GenBuffers ) goto failed; 546 | if( !crtemu->BindBuffer ) goto failed; 547 | if( !crtemu->EnableVertexAttribArray ) goto failed; 548 | if( !crtemu->VertexAttribPointer ) goto failed; 549 | if( !crtemu->GenTextures ) goto failed; 550 | if( !crtemu->Enable ) goto failed; 551 | if( !crtemu->ActiveTexture ) goto failed; 552 | if( !crtemu->BindTexture ) goto failed; 553 | if( !crtemu->TexParameteri ) goto failed; 554 | if( !crtemu->DeleteBuffers ) goto failed; 555 | if( !crtemu->DeleteTextures ) goto failed; 556 | if( !crtemu->BufferData ) goto failed; 557 | if( !crtemu->UseProgram ) goto failed; 558 | if( !crtemu->Uniform1i ) goto failed; 559 | if( !crtemu->Uniform3f ) goto failed; 560 | if( !crtemu->GetUniformLocation ) goto failed; 561 | if( !crtemu->TexImage2D ) goto failed; 562 | if( !crtemu->ClearColor ) goto failed; 563 | if( !crtemu->Clear ) goto failed; 564 | if( !crtemu->DrawArrays ) goto failed; 565 | if( !crtemu->Viewport ) goto failed; 566 | if( !crtemu->DeleteShader ) goto failed; 567 | if( !crtemu->DeleteProgram ) goto failed; 568 | #ifdef CRTEMU_REPORT_SHADER_ERRORS 569 | if( !crtemu->GetShaderInfoLog ) goto failed; 570 | #endif 571 | 572 | char const* vs_source = 573 | "#version 120\n\n" 574 | "" 575 | "attribute vec4 pos;" 576 | "varying vec2 uv;" 577 | "" 578 | "void main( void )" 579 | " {" 580 | " gl_Position = vec4( pos.xy, 0.0, 1.0 );" 581 | " uv = pos.zw;" 582 | " }"; 583 | 584 | char const* crt_fs_source = 585 | "#version 120\n\n" 586 | "" 587 | "varying vec2 uv;" 588 | "" 589 | "uniform vec3 modulate;" 590 | "uniform vec2 resolution;" 591 | "uniform float time;" 592 | "uniform sampler2D backbuffer;" 593 | "uniform sampler2D blurbuffer;" 594 | "uniform sampler2D frametexture;" 595 | "uniform float use_frame;" 596 | "" 597 | "uniform float cfg_curvature;" 598 | "uniform float cfg_scanlines;" 599 | "uniform float cfg_shadow_mask; " 600 | "uniform float cfg_separation; " 601 | "uniform float cfg_ghosting; " 602 | "uniform float cfg_noise; " 603 | "uniform float cfg_flicker;" 604 | "uniform float cfg_vignette;" 605 | "uniform float cfg_distortion; " 606 | "uniform float cfg_aspect_lock; " 607 | "uniform float cfg_hpos; " 608 | "uniform float cfg_vpos; " 609 | "uniform float cfg_hsize; " 610 | "uniform float cfg_vsize; " 611 | "uniform float cfg_contrast; " 612 | "uniform float cfg_brightness; " 613 | "uniform float cfg_saturation;" 614 | "uniform float cfg_degauss;" 615 | "" 616 | "vec3 tsample( sampler2D samp, vec2 tc, float offs, vec2 resolution )" 617 | " {" 618 | " vec3 s = pow( abs( texture2D( samp, vec2( tc.x, 1.0-tc.y ) ).rgb), vec3( 2.2 ) );" 619 | " return s*vec3(1.25);" 620 | " }" 621 | "" 622 | "vec3 filmic( vec3 LinearColor )" 623 | " {" 624 | " vec3 x = max( vec3(0.0), LinearColor-vec3(0.004));" 625 | " return (x*(6.2*x+0.5))/(x*(6.2*x+1.7)+0.06);" 626 | " }" 627 | "" 628 | "vec2 curve( vec2 uv )" 629 | " {" 630 | " uv = (uv - 0.5) * 2.0;" 631 | " uv *= vec2( 1.049, 1.042); " 632 | " uv -= vec2( -0.008, 0.008 );" 633 | " uv.x *= 1.0 + pow((abs(uv.y) / 5.0), 2.0);" 634 | " uv.y *= 1.0 + pow((abs(uv.x) / 4.0), 2.0);" 635 | " uv = (uv / 2.0) + 0.5;" 636 | " return uv;" 637 | " }" 638 | "" 639 | "float rand(vec2 co)" 640 | " {" 641 | " return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);" 642 | " }" 643 | " " 644 | "void main(void)" 645 | " {" 646 | " /* Curve */" 647 | " vec2 curved_uv = mix( curve( uv ), uv, 0.4 );" 648 | " float scale = 0.04;" 649 | " vec2 scuv = curved_uv*(1.0-scale)+scale/2.0+vec2(0.003, -0.001);" 650 | " scuv = curved_uv;" 651 | " /* Main color, Bleed */" 652 | " vec3 col;" 653 | " float x = sin(0.1*time+curved_uv.y*13.0)*sin(0.23*time+curved_uv.y*19.0)*sin(0.3+0.11*time+curved_uv.y*23.0)*0.0012;" 654 | " float o =sin(gl_FragCoord.y/1.5)/resolution.x;" 655 | " x+=o*0.25;" 656 | " x*=0.2f;" 657 | " col.r = tsample(backbuffer,vec2(x+scuv.x+0.0009,scuv.y+0.0009),resolution.y/800.0, resolution ).x+0.02;" 658 | " col.g = tsample(backbuffer,vec2(x+scuv.x+0.0000,scuv.y-0.0011),resolution.y/800.0, resolution ).y+0.02;" 659 | " col.b = tsample(backbuffer,vec2(x+scuv.x-0.0015,scuv.y+0.0000),resolution.y/800.0, resolution ).z+0.02;" 660 | " float i = clamp(col.r*0.299 + col.g*0.587 + col.b*0.114, 0.0, 1.0 ); " 661 | " i = pow( 1.0 - pow(i,2.0), 1.0 );" 662 | " i = (1.0-i) * 0.85 + 0.15; " 663 | "" 664 | " /* Ghosting */" 665 | " float ghs = 0.15;" 666 | " vec3 r = tsample(blurbuffer, vec2(x-0.014*1.0, -0.027)*0.85+0.007*vec2( 0.35*sin(1.0/7.0 + 15.0*curved_uv.y + 0.9*time), " 667 | " 0.35*sin( 2.0/7.0 + 10.0*curved_uv.y + 1.37*time) )+vec2(scuv.x+0.001,scuv.y+0.001)," 668 | " 5.5+1.3*sin( 3.0/9.0 + 31.0*curved_uv.x + 1.70*time),resolution).xyz*vec3(0.5,0.25,0.25);" 669 | " vec3 g = tsample(blurbuffer, vec2(x-0.019*1.0, -0.020)*0.85+0.007*vec2( 0.35*cos(1.0/9.0 + 15.0*curved_uv.y + 0.5*time), " 670 | " 0.35*sin( 2.0/9.0 + 10.0*curved_uv.y + 1.50*time) )+vec2(scuv.x+0.000,scuv.y-0.002)," 671 | " 5.4+1.3*sin( 3.0/3.0 + 71.0*curved_uv.x + 1.90*time),resolution).xyz*vec3(0.25,0.5,0.25);" 672 | " vec3 b = tsample(blurbuffer, vec2(x-0.017*1.0, -0.003)*0.85+0.007*vec2( 0.35*sin(2.0/3.0 + 15.0*curved_uv.y + 0.7*time), " 673 | " 0.35*cos( 2.0/3.0 + 10.0*curved_uv.y + 1.63*time) )+vec2(scuv.x-0.002,scuv.y+0.000)," 674 | " 5.3+1.3*sin( 3.0/7.0 + 91.0*curved_uv.x + 1.65*time),resolution).xyz*vec3(0.25,0.25,0.5);" 675 | "" 676 | " col += vec3(ghs*(1.0-0.299))*pow(clamp(vec3(3.0)*r,vec3(0.0),vec3(1.0)),vec3(2.0))*vec3(i);" 677 | " col += vec3(ghs*(1.0-0.587))*pow(clamp(vec3(3.0)*g,vec3(0.0),vec3(1.0)),vec3(2.0))*vec3(i);" 678 | " col += vec3(ghs*(1.0-0.114))*pow(clamp(vec3(3.0)*b,vec3(0.0),vec3(1.0)),vec3(2.0))*vec3(i);" 679 | "" 680 | " /* Level adjustment (curves) */" 681 | " col *= vec3(0.95,1.05,0.95);" 682 | " col = clamp(col*1.3 + 0.75*col*col + 1.25*col*col*col*col*col,vec3(0.0),vec3(10.0));" 683 | "" 684 | " /* Vignette */" 685 | " float vig = (0.1 + 1.0*16.0*curved_uv.x*curved_uv.y*(1.0-curved_uv.x)*(1.0-curved_uv.y));" 686 | " vig = 1.3*pow(vig,0.5);" 687 | " col *= vig;" 688 | "" 689 | " /* Scanlines */" 690 | " float scans = clamp( 0.35+0.18*sin(6.0*time+curved_uv.y*resolution.y*1.5), 0.0, 1.0);" 691 | " float s = pow(scans,0.9);" 692 | " col = col * vec3(s);" 693 | "" 694 | " /* Vertical lines (shadow mask) */" 695 | " col*=1.0-0.23*(clamp((mod(gl_FragCoord.xy.x, 3.0))/2.0,0.0,1.0));" 696 | "" 697 | " /* Tone map */" 698 | " col = filmic( col );" 699 | "" 700 | " /* Noise */" 701 | " /*vec2 seed = floor(curved_uv*resolution.xy*vec2(0.5))/resolution.xy;*/" 702 | " vec2 seed = curved_uv*resolution.xy;;" 703 | " /* seed = curved_uv; */" 704 | " col -= 0.015*pow(vec3(rand( seed +time ), rand( seed +time*2.0 ), rand( seed +time * 3.0 ) ), vec3(1.5) );" 705 | "" 706 | " /* Flicker */" 707 | " col *= (1.0-0.004*(sin(50.0*time+curved_uv.y*2.0)*0.5+0.5));" 708 | "" 709 | " /* Clamp */" 710 | " if (curved_uv.x < 0.0 || curved_uv.x > 1.0)" 711 | " col *= 0.0;" 712 | " if (curved_uv.y < 0.0 || curved_uv.y > 1.0)" 713 | " col *= 0.0;" 714 | "" 715 | " /* Frame */" 716 | " vec2 fscale = vec2( -0.019, -0.018 );" 717 | " vec4 f=texture2D(frametexture,uv*((1.0)+2.0*fscale)-fscale-vec2(-0.0, 0.005));" 718 | " f.xyz = mix( f.xyz, vec3(0.5,0.5,0.5), 0.5 );" 719 | " float fvig = clamp( -0.00+512.0*uv.x*uv.y*(1.0-uv.x)*(1.0-uv.y), 0.2, 0.8 );" 720 | " col = mix( col, mix( max( col, 0.0), pow( abs( f.xyz ), vec3( 1.4 ) ) * fvig, f.w * f.w), vec3( use_frame ) );" 721 | " " 722 | " gl_FragColor = vec4( col, 1.0 )* vec4( modulate, 1.0 );" 723 | " }" 724 | ""; 725 | 726 | crtemu->crt_shader = crtemu_internal_build_shader( crtemu, vs_source, crt_fs_source ); 727 | if( crtemu->crt_shader == 0 ) goto failed; 728 | 729 | char const* blur_fs_source = 730 | "#version 120\n\n" 731 | "" 732 | "varying vec2 uv;" 733 | "" 734 | "uniform vec2 blur;" 735 | "uniform sampler2D texture;" 736 | "" 737 | "void main( void )" 738 | " {" 739 | " vec4 sum = texture2D( texture, uv ) * 0.2270270270;" 740 | " sum += texture2D(texture, vec2( uv.x - 4.0 * blur.x, uv.y - 4.0 * blur.y ) ) * 0.0162162162;" 741 | " sum += texture2D(texture, vec2( uv.x - 3.0 * blur.x, uv.y - 3.0 * blur.y ) ) * 0.0540540541;" 742 | " sum += texture2D(texture, vec2( uv.x - 2.0 * blur.x, uv.y - 2.0 * blur.y ) ) * 0.1216216216;" 743 | " sum += texture2D(texture, vec2( uv.x - 1.0 * blur.x, uv.y - 1.0 * blur.y ) ) * 0.1945945946;" 744 | " sum += texture2D(texture, vec2( uv.x + 1.0 * blur.x, uv.y + 1.0 * blur.y ) ) * 0.1945945946;" 745 | " sum += texture2D(texture, vec2( uv.x + 2.0 * blur.x, uv.y + 2.0 * blur.y ) ) * 0.1216216216;" 746 | " sum += texture2D(texture, vec2( uv.x + 3.0 * blur.x, uv.y + 3.0 * blur.y ) ) * 0.0540540541;" 747 | " sum += texture2D(texture, vec2( uv.x + 4.0 * blur.x, uv.y + 4.0 * blur.y ) ) * 0.0162162162;" 748 | " gl_FragColor = sum;" 749 | " } " 750 | ""; 751 | 752 | crtemu->blur_shader = crtemu_internal_build_shader( crtemu, vs_source, blur_fs_source ); 753 | if( crtemu->blur_shader == 0 ) goto failed; 754 | 755 | char const* accumulate_fs_source = 756 | "#version 120\n\n" 757 | "" 758 | "varying vec2 uv;" 759 | "" 760 | "uniform sampler2D tex0;" 761 | "uniform sampler2D tex1;" 762 | "uniform float modulate;" 763 | "" 764 | "void main( void )" 765 | " {" 766 | " vec4 a = texture2D( tex0, uv ) * vec4( modulate );" 767 | " vec4 b = texture2D( tex1, uv );" 768 | "" 769 | " gl_FragColor = max( a, b * 0.96 );" 770 | " } " 771 | ""; 772 | 773 | crtemu->accumulate_shader = crtemu_internal_build_shader( crtemu, vs_source, accumulate_fs_source ); 774 | if( crtemu->accumulate_shader == 0 ) goto failed; 775 | 776 | char const* blend_fs_source = 777 | "#version 120\n\n" 778 | "" 779 | "varying vec2 uv;" 780 | "" 781 | "uniform sampler2D tex0;" 782 | "uniform sampler2D tex1;" 783 | "uniform float modulate;" 784 | "" 785 | "void main( void )" 786 | " {" 787 | " vec4 a = texture2D( tex0, uv ) * vec4( modulate );" 788 | " vec4 b = texture2D( tex1, uv );" 789 | "" 790 | " gl_FragColor = max( a, b * 0.32 );" 791 | " } " 792 | ""; 793 | 794 | crtemu->blend_shader = crtemu_internal_build_shader( crtemu, vs_source, blend_fs_source ); 795 | if( crtemu->blend_shader == 0 ) goto failed; 796 | 797 | char const* copy_fs_source = 798 | "#version 120\n\n" 799 | "" 800 | "varying vec2 uv;" 801 | "" 802 | "uniform sampler2D tex0;" 803 | "" 804 | "void main( void )" 805 | " {" 806 | " gl_FragColor = texture2D( tex0, uv );" 807 | " } " 808 | ""; 809 | 810 | crtemu->copy_shader = crtemu_internal_build_shader( crtemu, vs_source, copy_fs_source ); 811 | if( crtemu->copy_shader == 0 ) goto failed; 812 | 813 | 814 | crtemu->GenTextures( 1, &crtemu->accumulatetexture_a ); 815 | crtemu->GenFramebuffers( 1, &crtemu->accumulatebuffer_a ); 816 | 817 | crtemu->GenTextures( 1, &crtemu->accumulatetexture_b ); 818 | crtemu->GenFramebuffers( 1, &crtemu->accumulatebuffer_b ); 819 | 820 | crtemu->GenTextures( 1, &crtemu->blurtexture_a ); 821 | crtemu->GenFramebuffers( 1, &crtemu->blurbuffer_a ); 822 | 823 | crtemu->GenTextures( 1, &crtemu->blurtexture_b ); 824 | crtemu->GenFramebuffers( 1, &crtemu->blurbuffer_b ); 825 | 826 | crtemu->BindFramebuffer( CRTEMU_GL_FRAMEBUFFER, 0 ); 827 | 828 | crtemu->GenTextures( 1, &crtemu->frametexture ); 829 | crtemu->Enable( CRTEMU_GL_TEXTURE_2D ); 830 | crtemu->ActiveTexture( CRTEMU_GL_TEXTURE2 ); 831 | crtemu->BindTexture( CRTEMU_GL_TEXTURE_2D, crtemu->frametexture ); 832 | crtemu->TexParameteri( CRTEMU_GL_TEXTURE_2D, CRTEMU_GL_TEXTURE_MIN_FILTER, CRTEMU_GL_LINEAR ); 833 | crtemu->TexParameteri( CRTEMU_GL_TEXTURE_2D, CRTEMU_GL_TEXTURE_MAG_FILTER, CRTEMU_GL_LINEAR ); 834 | 835 | crtemu->GenTextures( 1, &crtemu->backbuffer ); 836 | crtemu->Enable( CRTEMU_GL_TEXTURE_2D ); 837 | crtemu->ActiveTexture( CRTEMU_GL_TEXTURE0 ); 838 | crtemu->BindTexture( CRTEMU_GL_TEXTURE_2D, crtemu->backbuffer ); 839 | crtemu->TexParameteri( CRTEMU_GL_TEXTURE_2D, CRTEMU_GL_TEXTURE_MIN_FILTER, CRTEMU_GL_NEAREST ); 840 | crtemu->TexParameteri( CRTEMU_GL_TEXTURE_2D, CRTEMU_GL_TEXTURE_MAG_FILTER, CRTEMU_GL_NEAREST ); 841 | 842 | crtemu->GenBuffers( 1, &crtemu->vertexbuffer ); 843 | crtemu->BindBuffer( CRTEMU_GL_ARRAY_BUFFER, crtemu->vertexbuffer ); 844 | crtemu->EnableVertexAttribArray( 0 ); 845 | crtemu->VertexAttribPointer( 0, 4, CRTEMU_GL_FLOAT, CRTEMU_GL_FALSE, 4 * sizeof( CRTEMU_GLfloat ), 0 ); 846 | 847 | return crtemu; 848 | 849 | failed: 850 | if( crtemu->accumulatetexture_a ) crtemu->DeleteTextures( 1, &crtemu->accumulatetexture_a ); 851 | if( crtemu->accumulatebuffer_a ) crtemu->DeleteFramebuffers( 1, &crtemu->accumulatebuffer_a ); 852 | if( crtemu->accumulatetexture_b ) crtemu->DeleteTextures( 1, &crtemu->accumulatetexture_b ); 853 | if( crtemu->accumulatebuffer_b ) crtemu->DeleteFramebuffers( 1, &crtemu->accumulatebuffer_b ); 854 | if( crtemu->blurtexture_a ) crtemu->DeleteTextures( 1, &crtemu->blurtexture_a ); 855 | if( crtemu->blurbuffer_a ) crtemu->DeleteFramebuffers( 1, &crtemu->blurbuffer_a ); 856 | if( crtemu->blurtexture_b ) crtemu->DeleteTextures( 1, &crtemu->blurtexture_b ); 857 | if( crtemu->blurbuffer_b ) crtemu->DeleteFramebuffers( 1, &crtemu->blurbuffer_b ); 858 | if( crtemu->frametexture ) crtemu->DeleteTextures( 1, &crtemu->frametexture ); 859 | if( crtemu->backbuffer ) crtemu->DeleteTextures( 1, &crtemu->backbuffer ); 860 | if( crtemu->vertexbuffer ) crtemu->DeleteBuffers( 1, &crtemu->vertexbuffer ); 861 | 862 | #ifndef CRTEMU_SDL 863 | if( crtemu->gl_dll ) FreeLibrary( crtemu->gl_dll ); 864 | #endif 865 | CRTEMU_FREE( crtemu->memctx, crtemu ); 866 | return 0; 867 | } 868 | 869 | 870 | void crtemu_destroy( crtemu_t* crtemu ) 871 | { 872 | crtemu->DeleteTextures( 1, &crtemu->accumulatetexture_a ); 873 | crtemu->DeleteFramebuffers( 1, &crtemu->accumulatebuffer_a ); 874 | crtemu->DeleteTextures( 1, &crtemu->accumulatetexture_b ); 875 | crtemu->DeleteFramebuffers( 1, &crtemu->accumulatebuffer_b ); 876 | crtemu->DeleteTextures( 1, &crtemu->blurtexture_a ); 877 | crtemu->DeleteFramebuffers( 1, &crtemu->blurbuffer_a ); 878 | crtemu->DeleteTextures( 1, &crtemu->blurtexture_b ); 879 | crtemu->DeleteFramebuffers( 1, &crtemu->blurbuffer_b ); 880 | crtemu->DeleteTextures( 1, &crtemu->frametexture ); 881 | crtemu->DeleteTextures( 1, &crtemu->backbuffer ); 882 | crtemu->DeleteBuffers( 1, &crtemu->vertexbuffer ); 883 | #ifndef CRTEMU_SDL 884 | FreeLibrary( crtemu->gl_dll ); 885 | #endif 886 | CRTEMU_FREE( crtemu->memctx, crtemu ); 887 | } 888 | 889 | 890 | void crtemu_config(crtemu_t* crtemu, crtemu_config_t const* config) 891 | { 892 | crtemu->config = *config; 893 | } 894 | 895 | 896 | void crtemu_frame( crtemu_t* crtemu, CRTEMU_U32* frame_abgr, int frame_width, int frame_height ) 897 | { 898 | crtemu->ActiveTexture( CRTEMU_GL_TEXTURE3 ); 899 | crtemu->BindTexture( CRTEMU_GL_TEXTURE_2D, crtemu->frametexture ); 900 | crtemu->TexImage2D( CRTEMU_GL_TEXTURE_2D, 0, CRTEMU_GL_RGBA, frame_width, frame_height, 0, CRTEMU_GL_RGBA, CRTEMU_GL_UNSIGNED_BYTE, frame_abgr ); 901 | if( frame_abgr ) 902 | crtemu->use_frame = 1.0f; 903 | else 904 | crtemu->use_frame = 0.0f; 905 | } 906 | 907 | 908 | static void crtemu_internal_blur( crtemu_t* crtemu, CRTEMU_GLuint source, CRTEMU_GLuint blurbuffer_a, CRTEMU_GLuint blurbuffer_b, 909 | CRTEMU_GLuint blurtexture_b, float r, int width, int height ) 910 | { 911 | crtemu->BindFramebuffer( CRTEMU_GL_FRAMEBUFFER, blurbuffer_b ); 912 | crtemu->UseProgram( crtemu->blur_shader ); 913 | crtemu->Uniform2f( crtemu->GetUniformLocation( crtemu->blur_shader, "blur" ), r / (float) width, 0 ); 914 | crtemu->Uniform1i( crtemu->GetUniformLocation( crtemu->blur_shader, "texture" ), 0 ); 915 | crtemu->ActiveTexture( CRTEMU_GL_TEXTURE0 ); 916 | crtemu->BindTexture( CRTEMU_GL_TEXTURE_2D, source ); 917 | crtemu->TexParameteri( CRTEMU_GL_TEXTURE_2D, CRTEMU_GL_TEXTURE_MIN_FILTER, CRTEMU_GL_LINEAR ); 918 | crtemu->TexParameteri( CRTEMU_GL_TEXTURE_2D, CRTEMU_GL_TEXTURE_MAG_FILTER, CRTEMU_GL_LINEAR ); 919 | crtemu->DrawArrays( CRTEMU_GL_TRIANGLE_FAN, 0, 4 ); 920 | crtemu->ActiveTexture( CRTEMU_GL_TEXTURE0 ); 921 | crtemu->BindTexture( CRTEMU_GL_TEXTURE_2D, 0 ); 922 | crtemu->BindFramebuffer( CRTEMU_GL_FRAMEBUFFER, 0 ); 923 | 924 | crtemu->BindFramebuffer( CRTEMU_GL_FRAMEBUFFER, blurbuffer_a ); 925 | crtemu->UseProgram( crtemu->blur_shader ); 926 | crtemu->Uniform2f( crtemu->GetUniformLocation( crtemu->blur_shader, "blur" ), 0, r / (float) height ); 927 | crtemu->Uniform1i( crtemu->GetUniformLocation( crtemu->blur_shader, "texture" ), 0 ); 928 | crtemu->ActiveTexture( CRTEMU_GL_TEXTURE0 ); 929 | crtemu->BindTexture( CRTEMU_GL_TEXTURE_2D, blurtexture_b ); 930 | crtemu->TexParameteri( CRTEMU_GL_TEXTURE_2D, CRTEMU_GL_TEXTURE_MIN_FILTER, CRTEMU_GL_LINEAR ); 931 | crtemu->TexParameteri( CRTEMU_GL_TEXTURE_2D, CRTEMU_GL_TEXTURE_MAG_FILTER, CRTEMU_GL_LINEAR ); 932 | crtemu->DrawArrays( CRTEMU_GL_TRIANGLE_FAN, 0, 4 ); 933 | crtemu->ActiveTexture( CRTEMU_GL_TEXTURE0 ); 934 | crtemu->BindTexture( CRTEMU_GL_TEXTURE_2D, 0 ); 935 | crtemu->BindFramebuffer( CRTEMU_GL_FRAMEBUFFER, 0 ); 936 | } 937 | 938 | 939 | void crtemu_present( crtemu_t* crtemu, CRTEMU_U64 time_us, CRTEMU_U32 const* pixels_xbgr, int width, int height, 940 | CRTEMU_U32 mod_xbgr, CRTEMU_U32 border_xbgr ) 941 | { 942 | int viewport[ 4 ]; 943 | crtemu->GetIntegerv( CRTEMU_GL_VIEWPORT, viewport ); 944 | 945 | if( width != crtemu->last_present_width || height != crtemu->last_present_height ) 946 | { 947 | crtemu->ActiveTexture( CRTEMU_GL_TEXTURE0 ); 948 | 949 | crtemu->BindTexture( CRTEMU_GL_TEXTURE_2D, crtemu->accumulatetexture_a ); 950 | crtemu->TexImage2D( CRTEMU_GL_TEXTURE_2D, 0, CRTEMU_GL_RGB, width, height, 0, CRTEMU_GL_RGB, CRTEMU_GL_UNSIGNED_BYTE, 0 ); 951 | crtemu->BindFramebuffer( CRTEMU_GL_FRAMEBUFFER, crtemu->accumulatebuffer_a ); 952 | crtemu->FramebufferTexture2D( CRTEMU_GL_FRAMEBUFFER, CRTEMU_GL_COLOR_ATTACHMENT0, CRTEMU_GL_TEXTURE_2D, crtemu->accumulatetexture_a, 0 ); 953 | crtemu->BindFramebuffer( CRTEMU_GL_FRAMEBUFFER, 0 ); 954 | 955 | crtemu->BindTexture( CRTEMU_GL_TEXTURE_2D, crtemu->accumulatetexture_b ); 956 | crtemu->TexImage2D( CRTEMU_GL_TEXTURE_2D, 0, CRTEMU_GL_RGB, width, height, 0, CRTEMU_GL_RGB, CRTEMU_GL_UNSIGNED_BYTE, 0 ); 957 | crtemu->BindFramebuffer( CRTEMU_GL_FRAMEBUFFER, crtemu->accumulatebuffer_b ); 958 | crtemu->FramebufferTexture2D( CRTEMU_GL_FRAMEBUFFER, CRTEMU_GL_COLOR_ATTACHMENT0, CRTEMU_GL_TEXTURE_2D, crtemu->accumulatetexture_b, 0 ); 959 | crtemu->BindFramebuffer( CRTEMU_GL_FRAMEBUFFER, 0 ); 960 | 961 | crtemu->BindTexture( CRTEMU_GL_TEXTURE_2D, crtemu->blurtexture_a ); 962 | crtemu->TexImage2D( CRTEMU_GL_TEXTURE_2D, 0, CRTEMU_GL_RGB, width, height, 0, CRTEMU_GL_RGB, CRTEMU_GL_UNSIGNED_BYTE, 0 ); 963 | crtemu->BindFramebuffer( CRTEMU_GL_FRAMEBUFFER, crtemu->blurbuffer_a ); 964 | crtemu->FramebufferTexture2D( CRTEMU_GL_FRAMEBUFFER, CRTEMU_GL_COLOR_ATTACHMENT0, CRTEMU_GL_TEXTURE_2D, crtemu->blurtexture_a, 0 ); 965 | crtemu->BindFramebuffer( CRTEMU_GL_FRAMEBUFFER, 0 ); 966 | 967 | crtemu->BindTexture( CRTEMU_GL_TEXTURE_2D, crtemu->blurtexture_b ); 968 | crtemu->TexImage2D( CRTEMU_GL_TEXTURE_2D, 0, CRTEMU_GL_RGB, width, height, 0, CRTEMU_GL_RGB, CRTEMU_GL_UNSIGNED_BYTE, 0 ); 969 | crtemu->BindFramebuffer( CRTEMU_GL_FRAMEBUFFER, crtemu->blurbuffer_b ); 970 | crtemu->FramebufferTexture2D( CRTEMU_GL_FRAMEBUFFER, CRTEMU_GL_COLOR_ATTACHMENT0, CRTEMU_GL_TEXTURE_2D, crtemu->blurtexture_b, 0 ); 971 | crtemu->BindFramebuffer( CRTEMU_GL_FRAMEBUFFER, 0 ); 972 | } 973 | 974 | 975 | crtemu->last_present_width = width; 976 | crtemu->last_present_height = height; 977 | 978 | CRTEMU_GLfloat vertices[] = 979 | { 980 | -1.0f, -1.0f, 0.0f, 0.0f, 981 | 1.0f, -1.0f, 1.0f, 0.0f, 982 | 1.0f, 1.0f, 1.0f, 1.0f, 983 | -1.0f, 1.0f, 0.0f, 1.0f, 984 | }; 985 | crtemu->BufferData( CRTEMU_GL_ARRAY_BUFFER, 4 * 4 * sizeof( CRTEMU_GLfloat ), vertices, CRTEMU_GL_STATIC_DRAW ); 986 | crtemu->BindBuffer( CRTEMU_GL_ARRAY_BUFFER, crtemu->vertexbuffer ); 987 | 988 | // Copy to backbuffer 989 | crtemu->ActiveTexture( CRTEMU_GL_TEXTURE0 ); 990 | crtemu->BindTexture( CRTEMU_GL_TEXTURE_2D, crtemu->backbuffer ); 991 | crtemu->TexImage2D( CRTEMU_GL_TEXTURE_2D, 0, CRTEMU_GL_RGBA, width, height, 0, CRTEMU_GL_RGBA, CRTEMU_GL_UNSIGNED_BYTE, pixels_xbgr ); 992 | crtemu->BindTexture( CRTEMU_GL_TEXTURE_2D, 0 ); 993 | 994 | crtemu->Viewport( 0, 0, width, height ); 995 | 996 | // Blur the previous accumulation buffer 997 | crtemu_internal_blur( crtemu, crtemu->accumulatetexture_b, crtemu->blurbuffer_a, crtemu->blurbuffer_b, crtemu->blurtexture_b, 1.0f, width, height ); 998 | 999 | // Update accumulation buffer 1000 | crtemu->BindFramebuffer( CRTEMU_GL_FRAMEBUFFER, crtemu->accumulatebuffer_a ); 1001 | crtemu->UseProgram( crtemu->accumulate_shader ); 1002 | crtemu->Uniform1i( crtemu->GetUniformLocation( crtemu->accumulate_shader, "tex0" ), 0 ); 1003 | crtemu->Uniform1i( crtemu->GetUniformLocation( crtemu->accumulate_shader, "tex1" ), 1 ); 1004 | crtemu->Uniform1f( crtemu->GetUniformLocation( crtemu->accumulate_shader, "modulate" ), 1.0f ); 1005 | crtemu->ActiveTexture( CRTEMU_GL_TEXTURE0 ); 1006 | crtemu->BindTexture( CRTEMU_GL_TEXTURE_2D, crtemu->backbuffer ); 1007 | crtemu->TexParameteri( CRTEMU_GL_TEXTURE_2D, CRTEMU_GL_TEXTURE_MIN_FILTER, CRTEMU_GL_LINEAR ); 1008 | crtemu->TexParameteri( CRTEMU_GL_TEXTURE_2D, CRTEMU_GL_TEXTURE_MAG_FILTER, CRTEMU_GL_LINEAR ); 1009 | crtemu->ActiveTexture( CRTEMU_GL_TEXTURE1 ); 1010 | crtemu->BindTexture( CRTEMU_GL_TEXTURE_2D, crtemu->blurtexture_a ); 1011 | crtemu->TexParameteri( CRTEMU_GL_TEXTURE_2D, CRTEMU_GL_TEXTURE_MIN_FILTER, CRTEMU_GL_LINEAR ); 1012 | crtemu->TexParameteri( CRTEMU_GL_TEXTURE_2D, CRTEMU_GL_TEXTURE_MAG_FILTER, CRTEMU_GL_LINEAR ); 1013 | crtemu->DrawArrays( CRTEMU_GL_TRIANGLE_FAN, 0, 4 ); 1014 | crtemu->ActiveTexture( CRTEMU_GL_TEXTURE0 ); 1015 | crtemu->BindTexture( CRTEMU_GL_TEXTURE_2D, 0 ); 1016 | crtemu->ActiveTexture( CRTEMU_GL_TEXTURE1 ); 1017 | crtemu->BindTexture( CRTEMU_GL_TEXTURE_2D, 0 ); 1018 | crtemu->BindFramebuffer( CRTEMU_GL_FRAMEBUFFER, 0 ); 1019 | 1020 | 1021 | // Store a copy of the accumulation buffer 1022 | crtemu->BindFramebuffer( CRTEMU_GL_FRAMEBUFFER, crtemu->accumulatebuffer_b ); 1023 | crtemu->UseProgram( crtemu->copy_shader ); 1024 | crtemu->Uniform1i( crtemu->GetUniformLocation( crtemu->copy_shader, "tex0" ), 0 ); 1025 | crtemu->ActiveTexture( CRTEMU_GL_TEXTURE0 ); 1026 | crtemu->BindTexture( CRTEMU_GL_TEXTURE_2D, crtemu->accumulatetexture_a ); 1027 | crtemu->TexParameteri( CRTEMU_GL_TEXTURE_2D, CRTEMU_GL_TEXTURE_MIN_FILTER, CRTEMU_GL_LINEAR ); 1028 | crtemu->TexParameteri( CRTEMU_GL_TEXTURE_2D, CRTEMU_GL_TEXTURE_MAG_FILTER, CRTEMU_GL_LINEAR ); 1029 | crtemu->DrawArrays( CRTEMU_GL_TRIANGLE_FAN, 0, 4 ); 1030 | crtemu->ActiveTexture( CRTEMU_GL_TEXTURE0 ); 1031 | crtemu->BindTexture( CRTEMU_GL_TEXTURE_2D, 0 ); 1032 | crtemu->BindFramebuffer( CRTEMU_GL_FRAMEBUFFER, 0 ); 1033 | 1034 | // Blend accumulation and backbuffer 1035 | crtemu->BindFramebuffer( CRTEMU_GL_FRAMEBUFFER, crtemu->accumulatebuffer_a ); 1036 | crtemu->UseProgram( crtemu->blend_shader ); 1037 | crtemu->Uniform1i( crtemu->GetUniformLocation( crtemu->blend_shader, "tex0" ), 0 ); 1038 | crtemu->Uniform1i( crtemu->GetUniformLocation( crtemu->blend_shader, "tex1" ), 1 ); 1039 | crtemu->Uniform1f( crtemu->GetUniformLocation( crtemu->blend_shader, "modulate" ), 1.0f ); 1040 | crtemu->ActiveTexture( CRTEMU_GL_TEXTURE0 ); 1041 | crtemu->BindTexture( CRTEMU_GL_TEXTURE_2D, crtemu->backbuffer ); 1042 | crtemu->ActiveTexture( CRTEMU_GL_TEXTURE1 ); 1043 | crtemu->BindTexture( CRTEMU_GL_TEXTURE_2D, crtemu->accumulatetexture_b ); 1044 | crtemu->DrawArrays( CRTEMU_GL_TRIANGLE_FAN, 0, 4 ); 1045 | crtemu->ActiveTexture( CRTEMU_GL_TEXTURE0 ); 1046 | crtemu->BindTexture( CRTEMU_GL_TEXTURE_2D, 0 ); 1047 | crtemu->ActiveTexture( CRTEMU_GL_TEXTURE1 ); 1048 | crtemu->BindTexture( CRTEMU_GL_TEXTURE_2D, 0 ); 1049 | crtemu->BindFramebuffer( CRTEMU_GL_FRAMEBUFFER, 0 ); 1050 | 1051 | 1052 | // Add slight blur to backbuffer 1053 | crtemu_internal_blur( crtemu, crtemu->accumulatetexture_a, crtemu->accumulatebuffer_a, crtemu->blurbuffer_b, crtemu->blurtexture_b, 0.17f, width, height ); 1054 | 1055 | // Create fully blurred version of backbuffer 1056 | crtemu_internal_blur( crtemu, crtemu->accumulatetexture_a, crtemu->blurbuffer_a, crtemu->blurbuffer_b, crtemu->blurtexture_b, 1.0f, width, height ); 1057 | 1058 | 1059 | // Present to screen with CRT shader 1060 | crtemu->BindFramebuffer( CRTEMU_GL_FRAMEBUFFER, 0 ); 1061 | 1062 | crtemu->Viewport( viewport[ 0 ], viewport[ 1 ], viewport[ 2 ], viewport[ 3 ] ); 1063 | 1064 | int window_width = viewport[ 2 ] - viewport[ 0 ]; 1065 | int window_height = viewport[ 3 ] - viewport[ 1 ]; 1066 | 1067 | float hscale = window_width / (float) width; 1068 | float vscale = window_height / ( (float) height * 1.1f ); 1069 | float pixel_scale = hscale < vscale ? hscale : vscale; 1070 | 1071 | float hborder = ( window_width - pixel_scale * width ) / 2.0f; 1072 | float vborder = ( window_height - pixel_scale * height * 1.1f ) / 2.0f; 1073 | float x1 = hborder; 1074 | float y1 = vborder; 1075 | float x2 = x1 + pixel_scale * width; 1076 | float y2 = y1 + pixel_scale * height * 1.1f; 1077 | 1078 | x1 = ( x1 / window_width ) * 2.0f - 1.0f; 1079 | x2 = ( x2 / window_width ) * 2.0f - 1.0f; 1080 | y1 = ( y1 / window_height ) * 2.0f - 1.0f; 1081 | y2 = ( y2 / window_height ) * 2.0f - 1.0f; 1082 | 1083 | CRTEMU_GLfloat screen_vertices[] = 1084 | { 1085 | 0.0f, 0.0f, 0.0f, 0.0f, 1086 | 0.0f, 0.0f, 1.0f, 0.0f, 1087 | 0.0f, 0.0f, 1.0f, 1.0f, 1088 | 0.0f, 0.0f, 0.0f, 1.0f, 1089 | }; 1090 | screen_vertices[ 0 ] = x1; 1091 | screen_vertices[ 1 ] = y1; 1092 | screen_vertices[ 4 ] = x2; 1093 | screen_vertices[ 5 ] = y1; 1094 | screen_vertices[ 8 ] = x2; 1095 | screen_vertices[ 9 ] = y2; 1096 | screen_vertices[ 12 ] = x1; 1097 | screen_vertices[ 13 ] = y2; 1098 | 1099 | crtemu->BufferData( CRTEMU_GL_ARRAY_BUFFER, 4 * 4 * sizeof( CRTEMU_GLfloat ), screen_vertices, CRTEMU_GL_STATIC_DRAW ); 1100 | crtemu->BindBuffer( CRTEMU_GL_ARRAY_BUFFER, crtemu->vertexbuffer ); 1101 | 1102 | float r = ( ( border_xbgr >> 16 ) & 0xff ) / 255.0f; 1103 | float g = ( ( border_xbgr >> 8 ) & 0xff ) / 255.0f; 1104 | float b = ( ( border_xbgr ) & 0xff ) / 255.0f; 1105 | crtemu->ClearColor( r, g, b, 1.0f ); 1106 | crtemu->Clear( CRTEMU_GL_COLOR_BUFFER_BIT ); 1107 | 1108 | crtemu->UseProgram( crtemu->crt_shader ); 1109 | 1110 | crtemu->Uniform1i( crtemu->GetUniformLocation( crtemu->crt_shader, "backbuffer" ), 0 ); 1111 | crtemu->Uniform1i( crtemu->GetUniformLocation( crtemu->crt_shader, "blurbuffer" ), 1 ); 1112 | crtemu->Uniform1i( crtemu->GetUniformLocation( crtemu->crt_shader, "frametexture" ), 2 ); 1113 | crtemu->Uniform1f( crtemu->GetUniformLocation( crtemu->crt_shader, "use_frame" ), crtemu->use_frame ); 1114 | crtemu->Uniform1f( crtemu->GetUniformLocation( crtemu->crt_shader, "time" ), 1.5f *(CRTEMU_GLfloat)( ( (double) time_us ) / 1000000.0 ) ); 1115 | crtemu->Uniform2f( crtemu->GetUniformLocation( crtemu->crt_shader, "resolution" ), (float) window_width, (float) window_height ); 1116 | 1117 | float mod_r = ( ( mod_xbgr >> 16 ) & 0xff ) / 255.0f; 1118 | float mod_g = ( ( mod_xbgr >> 8 ) & 0xff ) / 255.0f; 1119 | float mod_b = ( ( mod_xbgr ) & 0xff ) / 255.0f; 1120 | crtemu->Uniform3f( crtemu->GetUniformLocation( crtemu->crt_shader, "modulate" ), mod_r, mod_g, mod_b ); 1121 | 1122 | crtemu->Uniform1f( crtemu->GetUniformLocation( crtemu->crt_shader, "cfg_curvature" ), crtemu->config.curvature ); 1123 | crtemu->Uniform1f( crtemu->GetUniformLocation( crtemu->crt_shader, "cfg_scanlines" ), crtemu->config.scanlines ); 1124 | crtemu->Uniform1f( crtemu->GetUniformLocation( crtemu->crt_shader, "cfg_shadow_mask" ), crtemu->config.shadow_mask ); 1125 | crtemu->Uniform1f( crtemu->GetUniformLocation( crtemu->crt_shader, "cfg_separation" ), crtemu->config.separation ); 1126 | crtemu->Uniform1f( crtemu->GetUniformLocation( crtemu->crt_shader, "cfg_ghosting" ), crtemu->config.ghosting ); 1127 | crtemu->Uniform1f( crtemu->GetUniformLocation( crtemu->crt_shader, "cfg_noise" ), crtemu->config.noise ); 1128 | crtemu->Uniform1f( crtemu->GetUniformLocation( crtemu->crt_shader, "cfg_flicker" ), crtemu->config.flicker ); 1129 | crtemu->Uniform1f( crtemu->GetUniformLocation( crtemu->crt_shader, "cfg_vignette" ), crtemu->config.vignette ); 1130 | crtemu->Uniform1f( crtemu->GetUniformLocation( crtemu->crt_shader, "cfg_distortion" ), crtemu->config.distortion ); 1131 | crtemu->Uniform1f( crtemu->GetUniformLocation( crtemu->crt_shader, "cfg_aspect_lock" ), crtemu->config.aspect_lock ); 1132 | crtemu->Uniform1f( crtemu->GetUniformLocation( crtemu->crt_shader, "cfg_hpos" ), crtemu->config.hpos ); 1133 | crtemu->Uniform1f( crtemu->GetUniformLocation( crtemu->crt_shader, "cfg_vpos" ), crtemu->config.vpos ); 1134 | crtemu->Uniform1f( crtemu->GetUniformLocation( crtemu->crt_shader, "cfg_hsize" ), crtemu->config.hsize ); 1135 | crtemu->Uniform1f( crtemu->GetUniformLocation( crtemu->crt_shader, "cfg_vsize" ), crtemu->config.vsize ); 1136 | crtemu->Uniform1f( crtemu->GetUniformLocation( crtemu->crt_shader, "cfg_contrast" ), crtemu->config.contrast ); 1137 | crtemu->Uniform1f( crtemu->GetUniformLocation( crtemu->crt_shader, "cfg_brightness" ), crtemu->config.brightness ); 1138 | crtemu->Uniform1f( crtemu->GetUniformLocation( crtemu->crt_shader, "cfg_saturation" ), crtemu->config.saturation ); 1139 | crtemu->Uniform1f( crtemu->GetUniformLocation( crtemu->crt_shader, "cfg_degauss" ), crtemu->config.degauss ); 1140 | 1141 | float color[] = { 0.0f, 0.0f, 0.0f, 0.0f }; 1142 | 1143 | crtemu->ActiveTexture( CRTEMU_GL_TEXTURE0 ); 1144 | crtemu->BindTexture( CRTEMU_GL_TEXTURE_2D, crtemu->accumulatetexture_a ); 1145 | crtemu->TexParameteri( CRTEMU_GL_TEXTURE_2D, CRTEMU_GL_TEXTURE_MIN_FILTER, CRTEMU_GL_LINEAR ); 1146 | crtemu->TexParameteri( CRTEMU_GL_TEXTURE_2D, CRTEMU_GL_TEXTURE_MAG_FILTER, CRTEMU_GL_LINEAR ); 1147 | crtemu->TexParameteri( CRTEMU_GL_TEXTURE_2D, CRTEMU_GL_TEXTURE_WRAP_S, CRTEMU_GL_CLAMP_TO_BORDER ); 1148 | crtemu->TexParameteri( CRTEMU_GL_TEXTURE_2D, CRTEMU_GL_TEXTURE_WRAP_T, CRTEMU_GL_CLAMP_TO_BORDER ); 1149 | crtemu->TexParameterfv( CRTEMU_GL_TEXTURE_2D, CRTEMU_GL_TEXTURE_BORDER_COLOR, color ); 1150 | 1151 | crtemu->ActiveTexture( CRTEMU_GL_TEXTURE1 ); 1152 | crtemu->BindTexture( CRTEMU_GL_TEXTURE_2D, crtemu->blurtexture_a ); 1153 | crtemu->TexParameteri( CRTEMU_GL_TEXTURE_2D, CRTEMU_GL_TEXTURE_MIN_FILTER, CRTEMU_GL_LINEAR ); 1154 | crtemu->TexParameteri( CRTEMU_GL_TEXTURE_2D, CRTEMU_GL_TEXTURE_MAG_FILTER, CRTEMU_GL_LINEAR ); 1155 | crtemu->TexParameteri( CRTEMU_GL_TEXTURE_2D, CRTEMU_GL_TEXTURE_WRAP_S, CRTEMU_GL_CLAMP_TO_BORDER ); 1156 | crtemu->TexParameteri( CRTEMU_GL_TEXTURE_2D, CRTEMU_GL_TEXTURE_WRAP_T, CRTEMU_GL_CLAMP_TO_BORDER ); 1157 | crtemu->TexParameterfv( CRTEMU_GL_TEXTURE_2D, CRTEMU_GL_TEXTURE_BORDER_COLOR, color ); 1158 | 1159 | crtemu->ActiveTexture( CRTEMU_GL_TEXTURE3 ); 1160 | crtemu->BindTexture( CRTEMU_GL_TEXTURE_2D, crtemu->frametexture ); 1161 | crtemu->TexParameteri( CRTEMU_GL_TEXTURE_2D, CRTEMU_GL_TEXTURE_MIN_FILTER, CRTEMU_GL_LINEAR ); 1162 | crtemu->TexParameteri( CRTEMU_GL_TEXTURE_2D, CRTEMU_GL_TEXTURE_MAG_FILTER, CRTEMU_GL_LINEAR ); 1163 | crtemu->TexParameteri( CRTEMU_GL_TEXTURE_2D, CRTEMU_GL_TEXTURE_WRAP_S, CRTEMU_GL_CLAMP_TO_BORDER ); 1164 | crtemu->TexParameteri( CRTEMU_GL_TEXTURE_2D, CRTEMU_GL_TEXTURE_WRAP_T, CRTEMU_GL_CLAMP_TO_BORDER ); 1165 | crtemu->TexParameterfv( CRTEMU_GL_TEXTURE_2D, CRTEMU_GL_TEXTURE_BORDER_COLOR, color ); 1166 | 1167 | crtemu->DrawArrays( CRTEMU_GL_TRIANGLE_FAN, 0, 4 ); 1168 | 1169 | crtemu->ActiveTexture( CRTEMU_GL_TEXTURE0 ); 1170 | crtemu->BindTexture( CRTEMU_GL_TEXTURE_2D, 0 ); 1171 | crtemu->BindTexture( CRTEMU_GL_TEXTURE_2D, 1 ); 1172 | crtemu->BindTexture( CRTEMU_GL_TEXTURE_2D, 2 ); 1173 | crtemu->BindFramebuffer( CRTEMU_GL_FRAMEBUFFER, 0 ); 1174 | } 1175 | 1176 | 1177 | void crtemu_coordinates_window_to_bitmap( crtemu_t* crtemu, int width, int height, int* x, int* y ) 1178 | { 1179 | CRTEMU_GLint viewport[ 4 ]; 1180 | crtemu->GetIntegerv( CRTEMU_GL_VIEWPORT, viewport ); 1181 | 1182 | int window_width = viewport[ 2 ] - viewport[ 0 ]; 1183 | int window_height = viewport[ 3 ] - viewport[ 1 ]; 1184 | 1185 | float hscale = window_width / (float) width; 1186 | float vscale = window_height / (float) height; 1187 | float pixel_scale = hscale < vscale ? hscale : vscale; 1188 | 1189 | float hborder = ( window_width - pixel_scale * width ) / 2.0f; 1190 | float vborder = ( window_height - pixel_scale * height ) / 2.0f; 1191 | 1192 | float xp = ( ( *x - hborder ) / pixel_scale ) / (float) width; 1193 | float yp = ( ( *y - vborder ) / pixel_scale ) / (float) height; 1194 | 1195 | /* TODO: Common params for shader and this */ 1196 | float xc = ( xp - 0.5f ) * 2.0f; 1197 | float yc = ( yp - 0.5f ) * 2.0f; 1198 | xc *= 1.1f; 1199 | yc *= 1.1f; 1200 | //xc *= 1.0f + powf( ( fabsf( yc ) / 5.0f ), 2.0f); 1201 | //yc *= 1.0f + powf( ( fabsf( xc ) / 4.0f ), 2.0f); 1202 | float yt = ( yc >= 0.0f ? yc : -yc ) / 5.0f; 1203 | float xt = ( xc >= 0.0f ? xc : -xc ) / 4.0f; 1204 | xc *= 1.0f + ( yt * yt ); 1205 | yc *= 1.0f + ( xt * xt ); 1206 | xc = ( xc / 2.0f ) + 0.5f; 1207 | yc = ( yc / 2.0f ) + 0.5f; 1208 | xc = xc * 0.92f + 0.04f; 1209 | yc = yc * 0.92f + 0.04f; 1210 | xp = xc * 0.6f + xp * 0.4f; 1211 | yp = yc * 0.6f + yp * 0.4f; 1212 | 1213 | xp = xp * ( 1.0f - 0.04f ) + 0.04f / 2.0f + 0.003f; 1214 | yp = yp * ( 1.0f - 0.04f ) + 0.04f / 2.0f - 0.001f; 1215 | 1216 | xp = xp * 1.025f - 0.0125f; 1217 | yp = yp * 0.92f + 0.04f; 1218 | 1219 | xp *= width; 1220 | yp *= height; 1221 | 1222 | *x = (int) ( xp ); 1223 | *y = (int) ( yp ); 1224 | } 1225 | 1226 | 1227 | void crtemu_coordinates_bitmap_to_window( crtemu_t* crtemu, int width, int height, int* x, int* y ) 1228 | { 1229 | (void) crtemu, (void) width, (void) height, (void) x, (void) y; // TODO: implement 1230 | } 1231 | 1232 | 1233 | #endif /* CRTEMU_IMPLEMENTATION */ 1234 | 1235 | /* 1236 | ------------------------------------------------------------------------------ 1237 | 1238 | This software is available under 2 licenses - you may choose the one you like. 1239 | 1240 | ------------------------------------------------------------------------------ 1241 | 1242 | ALTERNATIVE A - MIT License 1243 | 1244 | Copyright (c) 2016 Mattias Gustavsson 1245 | 1246 | Permission is hereby granted, free of charge, to any person obtaining a copy of 1247 | this software and associated documentation files (the "Software"), to deal in 1248 | the Software without restriction, including without limitation the rights to 1249 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 1250 | of the Software, and to permit persons to whom the Software is furnished to do 1251 | so, subject to the following conditions: 1252 | 1253 | The above copyright notice and this permission notice shall be included in all 1254 | copies or substantial portions of the Software. 1255 | 1256 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 1257 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 1258 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 1259 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 1260 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 1261 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 1262 | SOFTWARE. 1263 | 1264 | ------------------------------------------------------------------------------ 1265 | 1266 | ALTERNATIVE B - Public Domain (www.unlicense.org) 1267 | 1268 | This is free and unencumbered software released into the public domain. 1269 | 1270 | Anyone is free to copy, modify, publish, use, compile, sell, or distribute this 1271 | software, either in source code form or as a compiled binary, for any purpose, 1272 | commercial or non-commercial, and by any means. 1273 | 1274 | In jurisdictions that recognize copyright laws, the author or authors of this 1275 | software dedicate any and all copyright interest in the software to the public 1276 | domain. We make this dedication for the benefit of the public at large and to 1277 | the detriment of our heirs and successors. We intend this dedication to be an 1278 | overt act of relinquishment in perpetuity of all present and future rights to 1279 | this software under copyright law. 1280 | 1281 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 1282 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 1283 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 1284 | AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 1285 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 1286 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 1287 | 1288 | ------------------------------------------------------------------------------ 1289 | */ 1290 | -------------------------------------------------------------------------------- /source/crtemu_pc.h: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------------------------------------------ 3 | Licensing information can be found at the end of the file. 4 | ------------------------------------------------------------------------------ 5 | 6 | crtemu_pc.h - v0.1 - Cathode ray tube emulation shader for C/C++. 7 | 8 | Do this: 9 | #define CRTEMU_PC_IMPLEMENTATION 10 | before you include this file in *one* C/C++ file to create the implementation. 11 | */ 12 | 13 | 14 | #ifndef crtemu_pc_h 15 | #define crtemu_pc_h 16 | 17 | #ifndef CRTEMU_PC_U32 18 | #define CRTEMU_PC_U32 unsigned int 19 | #endif 20 | #ifndef CRTEMU_PC_U64 21 | #define CRTEMU_PC_U64 unsigned long long 22 | #endif 23 | 24 | typedef struct crtemu_pc_t crtemu_pc_t; 25 | 26 | crtemu_pc_t* crtemu_pc_create( void* memctx ); 27 | 28 | void crtemu_pc_destroy( crtemu_pc_t* crtemu_pc ); 29 | 30 | typedef struct crtemu_pc_config_t 31 | { 32 | float curvature, scanlines, shadow_mask, separation, ghosting, noise, flicker, vignette, distortion, aspect_lock, 33 | hpos, vpos, hsize, vsize, contrast, brightness, saturation, blur, degauss; // range -1.0f to 1.0f, default=0.0f 34 | } crtemu_pc_config_t; 35 | 36 | void crtemu_pc_config(crtemu_pc_t* crtemu_pc, crtemu_pc_config_t const* config); 37 | 38 | void crtemu_pc_frame( crtemu_pc_t* crtemu_pc, CRTEMU_PC_U32* frame_abgr, int frame_width, int frame_height ); 39 | 40 | void crtemu_pc_present( crtemu_pc_t* crtemu_pc, CRTEMU_PC_U64 time_us, CRTEMU_PC_U32 const* pixels_xbgr, int width, int height, 41 | CRTEMU_PC_U32 mod_xbgr, CRTEMU_PC_U32 border_xbgr ); 42 | 43 | void crtemu_pc_coordinates_window_to_bitmap( crtemu_pc_t* crtemu_pc, int width, int height, int* x, int* y ); 44 | void crtemu_pc_coordinates_bitmap_to_window( crtemu_pc_t* crtemu_pc, int width, int height, int* x, int* y ); 45 | 46 | #endif /* crtemu_pc_h */ 47 | 48 | /* 49 | ---------------------- 50 | IMPLEMENTATION 51 | ---------------------- 52 | */ 53 | #ifdef CRTEMU_PC_IMPLEMENTATION 54 | #undef CRTEMU_PC_IMPLEMENTATION 55 | 56 | #define _CRT_NONSTDC_NO_DEPRECATE 57 | #define _CRT_SECURE_NO_WARNINGS 58 | #include 59 | #include 60 | 61 | #ifndef CRTEMU_PC_MALLOC 62 | #include 63 | #if defined(__cplusplus) 64 | #define CRTEMU_PC_MALLOC( ctx, size ) ( ::malloc( size ) ) 65 | #define CRTEMU_PC_FREE( ctx, ptr ) ( ::free( ptr ) ) 66 | #else 67 | #define CRTEMU_PC_MALLOC( ctx, size ) ( malloc( size ) ) 68 | #define CRTEMU_PC_FREE( ctx, ptr ) ( free( ptr ) ) 69 | #endif 70 | #endif 71 | 72 | #ifdef CRTEMU_PC_REPORT_SHADER_ERRORS 73 | #ifndef CRTEMU_PC_REPORT_ERROR 74 | #define _CRT_NONSTDC_NO_DEPRECATE 75 | #define _CRT_SECURE_NO_WARNINGS 76 | #include 77 | #define CRTEMU_PC_REPORT_ERROR( str ) printf( "%s", str ) 78 | #endif 79 | #endif 80 | 81 | #ifndef _WIN32 82 | #define CRTEMU_PC_SDL 83 | #endif 84 | 85 | 86 | #ifndef CRTEMU_PC_SDL 87 | 88 | #ifdef __cplusplus 89 | extern "C" { 90 | #endif 91 | 92 | __declspec(dllimport) struct HINSTANCE__* __stdcall LoadLibraryA( char const* lpLibFileName ); 93 | __declspec(dllimport) int __stdcall FreeLibrary( struct HINSTANCE__* hModule ); 94 | #if defined(_WIN64) 95 | typedef __int64 (__stdcall* CRTEMU_PC_PROC)( void ); 96 | __declspec(dllimport) CRTEMU_PC_PROC __stdcall GetProcAddress( struct HINSTANCE__* hModule, char const* lpLibFileName ); 97 | #else 98 | typedef __int32 (__stdcall* CRTEMU_PC_PROC)( void ); 99 | __declspec(dllimport) CRTEMU_PC_PROC __stdcall GetProcAddress( struct HINSTANCE__* hModule, char const* lpLibFileName ); 100 | #endif 101 | 102 | #ifdef __cplusplus 103 | } 104 | #endif 105 | 106 | #define CRTEMU_PC_GLCALLTYPE __stdcall 107 | typedef unsigned int CRTEMU_PC_GLuint; 108 | typedef int CRTEMU_PC_GLsizei; 109 | typedef unsigned int CRTEMU_PC_GLenum; 110 | typedef int CRTEMU_PC_GLint; 111 | typedef float CRTEMU_PC_GLfloat; 112 | typedef char CRTEMU_PC_GLchar; 113 | typedef unsigned char CRTEMU_PC_GLboolean; 114 | typedef size_t CRTEMU_PC_GLsizeiptr; 115 | typedef unsigned int CRTEMU_PC_GLbitfield; 116 | 117 | #define CRTEMU_PC_GL_FLOAT 0x1406 118 | #define CRTEMU_PC_GL_FALSE 0 119 | #define CRTEMU_PC_GL_FRAGMENT_SHADER 0x8b30 120 | #define CRTEMU_PC_GL_VERTEX_SHADER 0x8b31 121 | #define CRTEMU_PC_GL_COMPILE_STATUS 0x8b81 122 | #define CRTEMU_PC_GL_LINK_STATUS 0x8b82 123 | #define CRTEMU_PC_GL_INFO_LOG_LENGTH 0x8b84 124 | #define CRTEMU_PC_GL_ARRAY_BUFFER 0x8892 125 | #define CRTEMU_PC_GL_TEXTURE_2D 0x0de1 126 | #define CRTEMU_PC_GL_TEXTURE0 0x84c0 127 | #define CRTEMU_PC_GL_TEXTURE1 0x84c1 128 | #define CRTEMU_PC_GL_TEXTURE2 0x84c2 129 | #define CRTEMU_PC_GL_TEXTURE3 0x84c3 130 | #define CRTEMU_PC_GL_TEXTURE_MIN_FILTER 0x2801 131 | #define CRTEMU_PC_GL_TEXTURE_MAG_FILTER 0x2800 132 | #define CRTEMU_PC_GL_NEAREST 0x2600 133 | #define CRTEMU_PC_GL_LINEAR 0x2601 134 | #define CRTEMU_PC_GL_STATIC_DRAW 0x88e4 135 | #define CRTEMU_PC_GL_RGBA 0x1908 136 | #define CRTEMU_PC_GL_UNSIGNED_BYTE 0x1401 137 | #define CRTEMU_PC_GL_COLOR_BUFFER_BIT 0x00004000 138 | #define CRTEMU_PC_GL_TRIANGLE_FAN 0x0006 139 | #define CRTEMU_PC_GL_FRAMEBUFFER 0x8d40 140 | #define CRTEMU_PC_GL_VIEWPORT 0x0ba2 141 | #define CRTEMU_PC_GL_RGB 0x1907 142 | #define CRTEMU_PC_GL_COLOR_ATTACHMENT0 0x8ce0 143 | #define CRTEMU_PC_GL_TEXTURE_WRAP_S 0x2802 144 | #define CRTEMU_PC_GL_TEXTURE_WRAP_T 0x2803 145 | #define CRTEMU_PC_GL_CLAMP_TO_BORDER 0x812D 146 | #define CRTEMU_PC_GL_TEXTURE_BORDER_COLOR 0x1004 147 | 148 | #else 149 | 150 | #include 151 | #include 152 | #define CRTEMU_PC_GLCALLTYPE GLAPIENTRY 153 | 154 | typedef GLuint CRTEMU_PC_GLuint; 155 | typedef GLsizei CRTEMU_PC_GLsizei; 156 | typedef GLenum CRTEMU_PC_GLenum; 157 | typedef GLint CRTEMU_PC_GLint; 158 | typedef GLfloat CRTEMU_PC_GLfloat; 159 | typedef GLchar CRTEMU_PC_GLchar; 160 | typedef GLboolean CRTEMU_PC_GLboolean; 161 | typedef GLsizeiptr CRTEMU_PC_GLsizeiptr; 162 | typedef GLbitfield CRTEMU_PC_GLbitfield; 163 | 164 | #define CRTEMU_PC_GL_FLOAT GL_FLOAT 165 | #define CRTEMU_PC_GL_FALSE GL_FALSE 166 | #define CRTEMU_PC_GL_FRAGMENT_SHADER GL_FRAGMENT_SHADER 167 | #define CRTEMU_PC_GL_VERTEX_SHADER GL_VERTEX_SHADER 168 | #define CRTEMU_PC_GL_COMPILE_STATUS GL_COMPILE_STATUS 169 | #define CRTEMU_PC_GL_LINK_STATUS GL_LINK_STATUS 170 | #define CRTEMU_PC_GL_INFO_LOG_LENGTH GL_INFO_LOG_LENGTH 171 | #define CRTEMU_PC_GL_ARRAY_BUFFER GL_ARRAY_BUFFER 172 | #define CRTEMU_PC_GL_TEXTURE_2D GL_TEXTURE_2D 173 | #define CRTEMU_PC_GL_TEXTURE0 GL_TEXTURE0 174 | #define CRTEMU_PC_GL_TEXTURE1 GL_TEXTURE1 175 | #define CRTEMU_PC_GL_TEXTURE2 GL_TEXTURE2 176 | #define CRTEMU_PC_GL_TEXTURE3 GL_TEXTURE3 177 | #define CRTEMU_PC_GL_TEXTURE_MIN_FILTER GL_TEXTURE_MIN_FILTER 178 | #define CRTEMU_PC_GL_TEXTURE_MAG_FILTER GL_TEXTURE_MAG_FILTER 179 | #define CRTEMU_PC_GL_NEAREST GL_NEAREST 180 | #define CRTEMU_PC_GL_LINEAR GL_LINEAR 181 | #define CRTEMU_PC_GL_STATIC_DRAW GL_STATIC_DRAW 182 | #define CRTEMU_PC_GL_RGBA GL_RGBA 183 | #define CRTEMU_PC_GL_UNSIGNED_BYTE GL_UNSIGNED_BYTE 184 | #define CRTEMU_PC_GL_COLOR_BUFFER_BIT GL_COLOR_BUFFER_BIT 185 | #define CRTEMU_PC_GL_TRIANGLE_FAN GL_TRIANGLE_FAN 186 | #define CRTEMU_PC_GL_FRAMEBUFFER GL_FRAMEBUFFER 187 | #define CRTEMU_PC_GL_VIEWPORT GL_VIEWPORT 188 | #define CRTEMU_PC_GL_RGB GL_RGB 189 | #define CRTEMU_PC_GL_COLOR_ATTACHMENT0 GL_COLOR_ATTACHMENT0 190 | #define CRTEMU_PC_GL_TEXTURE_WRAP_S GL_TEXTURE_WRAP_S 191 | #define CRTEMU_PC_GL_TEXTURE_WRAP_T GL_TEXTURE_WRAP_T 192 | #define CRTEMU_PC_GL_CLAMP_TO_BORDER GL_CLAMP_TO_BORDER 193 | #define CRTEMU_PC_GL_TEXTURE_BORDER_COLOR GL_TEXTURE_BORDER_COLOR 194 | #endif 195 | 196 | 197 | struct crtemu_pc_t 198 | { 199 | void* memctx; 200 | crtemu_pc_config_t config; 201 | 202 | CRTEMU_PC_GLuint vertexbuffer; 203 | CRTEMU_PC_GLuint backbuffer; 204 | 205 | CRTEMU_PC_GLuint accumulatetexture_a; 206 | CRTEMU_PC_GLuint accumulatetexture_b; 207 | CRTEMU_PC_GLuint accumulatebuffer_a; 208 | CRTEMU_PC_GLuint accumulatebuffer_b; 209 | 210 | CRTEMU_PC_GLuint blurtexture_a; 211 | CRTEMU_PC_GLuint blurtexture_b; 212 | CRTEMU_PC_GLuint blurbuffer_a; 213 | CRTEMU_PC_GLuint blurbuffer_b; 214 | 215 | CRTEMU_PC_GLuint frametexture; 216 | float use_frame; 217 | 218 | CRTEMU_PC_GLuint crt_shader; 219 | CRTEMU_PC_GLuint blur_shader; 220 | CRTEMU_PC_GLuint accumulate_shader; 221 | CRTEMU_PC_GLuint blend_shader; 222 | CRTEMU_PC_GLuint copy_shader; 223 | 224 | int last_present_width; 225 | int last_present_height; 226 | 227 | 228 | #ifndef CRTEMU_PC_SDL 229 | struct HINSTANCE__* gl_dll; 230 | CRTEMU_PC_PROC (CRTEMU_PC_GLCALLTYPE *wglGetProcAddress) (char const* ); 231 | #endif 232 | 233 | void (CRTEMU_PC_GLCALLTYPE* TexParameterfv) (CRTEMU_PC_GLenum target, CRTEMU_PC_GLenum pname, CRTEMU_PC_GLfloat const* params); 234 | void (CRTEMU_PC_GLCALLTYPE* DeleteFramebuffers) (CRTEMU_PC_GLsizei n, CRTEMU_PC_GLuint const* framebuffers); 235 | void (CRTEMU_PC_GLCALLTYPE* GetIntegerv) (CRTEMU_PC_GLenum pname, CRTEMU_PC_GLint *data); 236 | void (CRTEMU_PC_GLCALLTYPE* GenFramebuffers) (CRTEMU_PC_GLsizei n, CRTEMU_PC_GLuint *framebuffers); 237 | void (CRTEMU_PC_GLCALLTYPE* BindFramebuffer) (CRTEMU_PC_GLenum target, CRTEMU_PC_GLuint framebuffer); 238 | void (CRTEMU_PC_GLCALLTYPE* Uniform1f) (CRTEMU_PC_GLint location, CRTEMU_PC_GLfloat v0); 239 | void (CRTEMU_PC_GLCALLTYPE* Uniform2f) (CRTEMU_PC_GLint location, CRTEMU_PC_GLfloat v0, CRTEMU_PC_GLfloat v1); 240 | void (CRTEMU_PC_GLCALLTYPE* FramebufferTexture2D) (CRTEMU_PC_GLenum target, CRTEMU_PC_GLenum attachment, CRTEMU_PC_GLenum textarget, CRTEMU_PC_GLuint texture, CRTEMU_PC_GLint level); 241 | CRTEMU_PC_GLuint (CRTEMU_PC_GLCALLTYPE* CreateShader) (CRTEMU_PC_GLenum type); 242 | void (CRTEMU_PC_GLCALLTYPE* ShaderSource) (CRTEMU_PC_GLuint shader, CRTEMU_PC_GLsizei count, CRTEMU_PC_GLchar const* const* string, CRTEMU_PC_GLint const* length); 243 | void (CRTEMU_PC_GLCALLTYPE* CompileShader) (CRTEMU_PC_GLuint shader); 244 | void (CRTEMU_PC_GLCALLTYPE* GetShaderiv) (CRTEMU_PC_GLuint shader, CRTEMU_PC_GLenum pname, CRTEMU_PC_GLint *params); 245 | CRTEMU_PC_GLuint (CRTEMU_PC_GLCALLTYPE* CreateProgram) (void); 246 | void (CRTEMU_PC_GLCALLTYPE* AttachShader) (CRTEMU_PC_GLuint program, CRTEMU_PC_GLuint shader); 247 | void (CRTEMU_PC_GLCALLTYPE* BindAttribLocation) (CRTEMU_PC_GLuint program, CRTEMU_PC_GLuint index, CRTEMU_PC_GLchar const* name); 248 | void (CRTEMU_PC_GLCALLTYPE* LinkProgram) (CRTEMU_PC_GLuint program); 249 | void (CRTEMU_PC_GLCALLTYPE* GetProgramiv) (CRTEMU_PC_GLuint program, CRTEMU_PC_GLenum pname, CRTEMU_PC_GLint *params); 250 | void (CRTEMU_PC_GLCALLTYPE* GenBuffers) (CRTEMU_PC_GLsizei n, CRTEMU_PC_GLuint *buffers); 251 | void (CRTEMU_PC_GLCALLTYPE* BindBuffer) (CRTEMU_PC_GLenum target, CRTEMU_PC_GLuint buffer); 252 | void (CRTEMU_PC_GLCALLTYPE* EnableVertexAttribArray) (CRTEMU_PC_GLuint index); 253 | void (CRTEMU_PC_GLCALLTYPE* VertexAttribPointer) (CRTEMU_PC_GLuint index, CRTEMU_PC_GLint size, CRTEMU_PC_GLenum type, CRTEMU_PC_GLboolean normalized, CRTEMU_PC_GLsizei stride, void const* pointer); 254 | void (CRTEMU_PC_GLCALLTYPE* GenTextures) (CRTEMU_PC_GLsizei n, CRTEMU_PC_GLuint* textures); 255 | void (CRTEMU_PC_GLCALLTYPE* Enable) (CRTEMU_PC_GLenum cap); 256 | void (CRTEMU_PC_GLCALLTYPE* ActiveTexture) (CRTEMU_PC_GLenum texture); 257 | void (CRTEMU_PC_GLCALLTYPE* BindTexture) (CRTEMU_PC_GLenum target, CRTEMU_PC_GLuint texture); 258 | void (CRTEMU_PC_GLCALLTYPE* TexParameteri) (CRTEMU_PC_GLenum target, CRTEMU_PC_GLenum pname, CRTEMU_PC_GLint param); 259 | void (CRTEMU_PC_GLCALLTYPE* DeleteBuffers) (CRTEMU_PC_GLsizei n, CRTEMU_PC_GLuint const* buffers); 260 | void (CRTEMU_PC_GLCALLTYPE* DeleteTextures) (CRTEMU_PC_GLsizei n, CRTEMU_PC_GLuint const* textures); 261 | void (CRTEMU_PC_GLCALLTYPE* BufferData) (CRTEMU_PC_GLenum target, CRTEMU_PC_GLsizeiptr size, void const *data, CRTEMU_PC_GLenum usage); 262 | void (CRTEMU_PC_GLCALLTYPE* UseProgram) (CRTEMU_PC_GLuint program); 263 | void (CRTEMU_PC_GLCALLTYPE* Uniform1i) (CRTEMU_PC_GLint location, CRTEMU_PC_GLint v0); 264 | void (CRTEMU_PC_GLCALLTYPE* Uniform3f) (CRTEMU_PC_GLint location, CRTEMU_PC_GLfloat v0, CRTEMU_PC_GLfloat v1, CRTEMU_PC_GLfloat v2); 265 | CRTEMU_PC_GLint (CRTEMU_PC_GLCALLTYPE* GetUniformLocation) (CRTEMU_PC_GLuint program, CRTEMU_PC_GLchar const* name); 266 | void (CRTEMU_PC_GLCALLTYPE* TexImage2D) (CRTEMU_PC_GLenum target, CRTEMU_PC_GLint level, CRTEMU_PC_GLint internalformat, CRTEMU_PC_GLsizei width, CRTEMU_PC_GLsizei height, CRTEMU_PC_GLint border, CRTEMU_PC_GLenum format, CRTEMU_PC_GLenum type, void const* pixels); 267 | void (CRTEMU_PC_GLCALLTYPE* ClearColor) (CRTEMU_PC_GLfloat red, CRTEMU_PC_GLfloat green, CRTEMU_PC_GLfloat blue, CRTEMU_PC_GLfloat alpha); 268 | void (CRTEMU_PC_GLCALLTYPE* Clear) (CRTEMU_PC_GLbitfield mask); 269 | void (CRTEMU_PC_GLCALLTYPE* DrawArrays) (CRTEMU_PC_GLenum mode, CRTEMU_PC_GLint first, CRTEMU_PC_GLsizei count); 270 | void (CRTEMU_PC_GLCALLTYPE* Viewport) (CRTEMU_PC_GLint x, CRTEMU_PC_GLint y, CRTEMU_PC_GLsizei width, CRTEMU_PC_GLsizei height); 271 | void (CRTEMU_PC_GLCALLTYPE* DeleteShader) (CRTEMU_PC_GLuint shader); 272 | void (CRTEMU_PC_GLCALLTYPE* DeleteProgram) (CRTEMU_PC_GLuint program); 273 | #ifdef CRTEMU_PC_REPORT_SHADER_ERRORS 274 | void (CRTEMU_PC_GLCALLTYPE* GetShaderInfoLog) (CRTEMU_PC_GLuint shader, CRTEMU_PC_GLsizei bufSize, CRTEMU_PC_GLsizei *length, CRTEMU_PC_GLchar *infoLog); 275 | #endif 276 | }; 277 | 278 | 279 | static CRTEMU_PC_GLuint crtemu_pc_internal_build_shader( crtemu_pc_t* crtemu_pc, char const* vs_source, char const* fs_source ) 280 | { 281 | #ifdef CRTEMU_PC_REPORT_SHADER_ERRORS 282 | char error_message[ 1024 ]; 283 | #endif 284 | 285 | CRTEMU_PC_GLuint vs = crtemu_pc->CreateShader( CRTEMU_PC_GL_VERTEX_SHADER ); 286 | crtemu_pc->ShaderSource( vs, 1, (char const**) &vs_source, NULL ); 287 | crtemu_pc->CompileShader( vs ); 288 | CRTEMU_PC_GLint vs_compiled; 289 | crtemu_pc->GetShaderiv( vs, CRTEMU_PC_GL_COMPILE_STATUS, &vs_compiled ); 290 | if( !vs_compiled ) 291 | { 292 | #ifdef CRTEMU_PC_REPORT_SHADER_ERRORS 293 | char const* prefix = "Vertex Shader Error: "; 294 | strcpy( error_message, prefix ); 295 | int len = 0, written = 0; 296 | crtemu_pc->GetShaderiv( vs, CRTEMU_PC_GL_INFO_LOG_LENGTH, &len ); 297 | crtemu_pc->GetShaderInfoLog( vs, (CRTEMU_PC_GLsizei)( sizeof( error_message ) - strlen( prefix ) ), &written, 298 | error_message + strlen( prefix ) ); 299 | CRTEMU_PC_REPORT_ERROR( error_message ); 300 | #endif 301 | return 0; 302 | } 303 | 304 | CRTEMU_PC_GLuint fs = crtemu_pc->CreateShader( CRTEMU_PC_GL_FRAGMENT_SHADER ); 305 | crtemu_pc->ShaderSource( fs, 1, (char const**) &fs_source, NULL ); 306 | crtemu_pc->CompileShader( fs ); 307 | CRTEMU_PC_GLint fs_compiled; 308 | crtemu_pc->GetShaderiv( fs, CRTEMU_PC_GL_COMPILE_STATUS, &fs_compiled ); 309 | if( !fs_compiled ) 310 | { 311 | #ifdef CRTEMU_PC_REPORT_SHADER_ERRORS 312 | char const* prefix = "Fragment Shader Error: "; 313 | strcpy( error_message, prefix ); 314 | int len = 0, written = 0; 315 | crtemu_pc->GetShaderiv( vs, CRTEMU_PC_GL_INFO_LOG_LENGTH, &len ); 316 | crtemu_pc->GetShaderInfoLog( fs, (CRTEMU_PC_GLsizei)( sizeof( error_message ) - strlen( prefix ) ), &written, 317 | error_message + strlen( prefix ) ); 318 | CRTEMU_PC_REPORT_ERROR( error_message ); 319 | #endif 320 | return 0; 321 | } 322 | 323 | 324 | CRTEMU_PC_GLuint prg = crtemu_pc->CreateProgram(); 325 | crtemu_pc->AttachShader( prg, fs ); 326 | crtemu_pc->AttachShader( prg, vs ); 327 | crtemu_pc->BindAttribLocation( prg, 0, "pos" ); 328 | crtemu_pc->LinkProgram( prg ); 329 | 330 | CRTEMU_PC_GLint linked; 331 | crtemu_pc->GetProgramiv( prg, CRTEMU_PC_GL_LINK_STATUS, &linked ); 332 | if( !linked ) 333 | { 334 | #ifdef CRTEMU_PC_REPORT_SHADER_ERRORS 335 | char const* prefix = "Shader Link Error: "; 336 | strcpy( error_message, prefix ); 337 | int len = 0, written = 0; 338 | crtemu_pc->GetShaderiv( vs, CRTEMU_PC_GL_INFO_LOG_LENGTH, &len ); 339 | crtemu_pc->GetShaderInfoLog( prg, (CRTEMU_PC_GLsizei)( sizeof( error_message ) - strlen( prefix ) ), &written, 340 | error_message + strlen( prefix ) ); 341 | CRTEMU_PC_REPORT_ERROR( error_message ); 342 | #endif 343 | return 0; 344 | } 345 | 346 | return prg; 347 | } 348 | 349 | 350 | crtemu_pc_t* crtemu_pc_create( void* memctx ) 351 | { 352 | crtemu_pc_t* crtemu_pc = (crtemu_pc_t*) CRTEMU_PC_MALLOC( memctx, sizeof( crtemu_pc_t ) ); 353 | memset( crtemu_pc, 0, sizeof( crtemu_pc_t ) ); 354 | crtemu_pc->memctx = memctx; 355 | 356 | crtemu_pc->config.curvature = 0.0f; 357 | crtemu_pc->config.scanlines = 0.0f; 358 | crtemu_pc->config.shadow_mask = 0.0f; 359 | crtemu_pc->config.separation = 0.0f; 360 | crtemu_pc->config.ghosting = 0.0f; 361 | crtemu_pc->config.noise = 0.0f; 362 | crtemu_pc->config.flicker = 0.0f; 363 | crtemu_pc->config.vignette = 0.0f; 364 | crtemu_pc->config.distortion = 0.0f; 365 | crtemu_pc->config.aspect_lock = 0.0f; 366 | crtemu_pc->config.hpos = 0.0f; 367 | crtemu_pc->config.vpos = 0.0f; 368 | crtemu_pc->config.hsize = 0.0f; 369 | crtemu_pc->config.vsize = 0.0f; 370 | crtemu_pc->config.contrast = 0.0f; 371 | crtemu_pc->config.brightness = 0.0f; 372 | crtemu_pc->config.saturation = 0.0f; 373 | crtemu_pc->config.blur = 0.0f; 374 | crtemu_pc->config.degauss = 0.0f; 375 | 376 | crtemu_pc->use_frame = 0.0f; 377 | 378 | crtemu_pc->last_present_width = 0; 379 | crtemu_pc->last_present_height = 0; 380 | 381 | #ifndef CRTEMU_PC_SDL 382 | 383 | crtemu_pc->gl_dll = LoadLibraryA( "opengl32.dll" ); 384 | if( !crtemu_pc->gl_dll ) goto failed; 385 | 386 | crtemu_pc->wglGetProcAddress = (CRTEMU_PC_PROC (CRTEMU_PC_GLCALLTYPE*)(char const*)) (uintptr_t) GetProcAddress( crtemu_pc->gl_dll, "wglGetProcAddress" ); 387 | if( !crtemu_pc->gl_dll ) goto failed; 388 | 389 | // Attempt to bind opengl functions using GetProcAddress 390 | crtemu_pc->TexParameterfv = ( void (CRTEMU_PC_GLCALLTYPE*) (CRTEMU_PC_GLenum, CRTEMU_PC_GLenum, CRTEMU_PC_GLfloat const*) ) (uintptr_t) GetProcAddress( crtemu_pc->gl_dll, "glTexParameterfv" ); 391 | crtemu_pc->DeleteFramebuffers = ( void (CRTEMU_PC_GLCALLTYPE*) (CRTEMU_PC_GLsizei, CRTEMU_PC_GLuint const*) ) (uintptr_t) GetProcAddress( crtemu_pc->gl_dll, "glDeleteFramebuffers" ); 392 | crtemu_pc->GetIntegerv = ( void (CRTEMU_PC_GLCALLTYPE*) (CRTEMU_PC_GLenum, CRTEMU_PC_GLint *) ) (uintptr_t) GetProcAddress( crtemu_pc->gl_dll, "glGetIntegerv" ); 393 | crtemu_pc->GenFramebuffers = ( void (CRTEMU_PC_GLCALLTYPE*) (CRTEMU_PC_GLsizei, CRTEMU_PC_GLuint *) ) (uintptr_t) GetProcAddress( crtemu_pc->gl_dll, "glGenFramebuffers" ); 394 | crtemu_pc->BindFramebuffer = ( void (CRTEMU_PC_GLCALLTYPE*) (CRTEMU_PC_GLenum, CRTEMU_PC_GLuint) ) (uintptr_t) GetProcAddress( crtemu_pc->gl_dll, "glBindFramebuffer" ); 395 | crtemu_pc->Uniform1f = ( void (CRTEMU_PC_GLCALLTYPE*) (CRTEMU_PC_GLint, CRTEMU_PC_GLfloat) ) (uintptr_t) GetProcAddress( crtemu_pc->gl_dll, "glUniform1f" ); 396 | crtemu_pc->Uniform2f = ( void (CRTEMU_PC_GLCALLTYPE*) (CRTEMU_PC_GLint, CRTEMU_PC_GLfloat, CRTEMU_PC_GLfloat) ) (uintptr_t) GetProcAddress( crtemu_pc->gl_dll, "glUniform2f" ); 397 | crtemu_pc->FramebufferTexture2D = ( void (CRTEMU_PC_GLCALLTYPE*) (CRTEMU_PC_GLenum, CRTEMU_PC_GLenum, CRTEMU_PC_GLenum, CRTEMU_PC_GLuint, CRTEMU_PC_GLint) ) (uintptr_t) GetProcAddress( crtemu_pc->gl_dll, "glFramebufferTexture2D" ); 398 | crtemu_pc->CreateShader = ( CRTEMU_PC_GLuint (CRTEMU_PC_GLCALLTYPE*) (CRTEMU_PC_GLenum) ) (uintptr_t) GetProcAddress( crtemu_pc->gl_dll, "glCreateShader" ); 399 | crtemu_pc->ShaderSource = ( void (CRTEMU_PC_GLCALLTYPE*) (CRTEMU_PC_GLuint, CRTEMU_PC_GLsizei, CRTEMU_PC_GLchar const* const*, CRTEMU_PC_GLint const*) ) (uintptr_t) GetProcAddress( crtemu_pc->gl_dll, "glShaderSource" ); 400 | crtemu_pc->CompileShader = ( void (CRTEMU_PC_GLCALLTYPE*) (CRTEMU_PC_GLuint) ) (uintptr_t) GetProcAddress( crtemu_pc->gl_dll, "glCompileShader" ); 401 | crtemu_pc->GetShaderiv = ( void (CRTEMU_PC_GLCALLTYPE*) (CRTEMU_PC_GLuint, CRTEMU_PC_GLenum, CRTEMU_PC_GLint*) ) (uintptr_t) GetProcAddress( crtemu_pc->gl_dll, "glGetShaderiv" ); 402 | crtemu_pc->CreateProgram = ( CRTEMU_PC_GLuint (CRTEMU_PC_GLCALLTYPE*) (void) ) (uintptr_t) GetProcAddress( crtemu_pc->gl_dll, "glCreateProgram" ); 403 | crtemu_pc->AttachShader = ( void (CRTEMU_PC_GLCALLTYPE*) (CRTEMU_PC_GLuint, CRTEMU_PC_GLuint) ) (uintptr_t) GetProcAddress( crtemu_pc->gl_dll, "glAttachShader" ); 404 | crtemu_pc->BindAttribLocation = ( void (CRTEMU_PC_GLCALLTYPE*) (CRTEMU_PC_GLuint, CRTEMU_PC_GLuint, CRTEMU_PC_GLchar const*) ) (uintptr_t) GetProcAddress( crtemu_pc->gl_dll, "glBindAttribLocation" ); 405 | crtemu_pc->LinkProgram = ( void (CRTEMU_PC_GLCALLTYPE*) (CRTEMU_PC_GLuint) ) (uintptr_t) GetProcAddress( crtemu_pc->gl_dll, "glLinkProgram" ); 406 | crtemu_pc->GetProgramiv = ( void (CRTEMU_PC_GLCALLTYPE*) (CRTEMU_PC_GLuint, CRTEMU_PC_GLenum, CRTEMU_PC_GLint*) ) (uintptr_t) GetProcAddress( crtemu_pc->gl_dll, "glGetProgramiv" ); 407 | crtemu_pc->GenBuffers = ( void (CRTEMU_PC_GLCALLTYPE*) (CRTEMU_PC_GLsizei, CRTEMU_PC_GLuint*) ) (uintptr_t) GetProcAddress( crtemu_pc->gl_dll, "glGenBuffers" ); 408 | crtemu_pc->BindBuffer = ( void (CRTEMU_PC_GLCALLTYPE*) (CRTEMU_PC_GLenum, CRTEMU_PC_GLuint) ) (uintptr_t) GetProcAddress( crtemu_pc->gl_dll, "glBindBuffer" ); 409 | crtemu_pc->EnableVertexAttribArray = ( void (CRTEMU_PC_GLCALLTYPE*) (CRTEMU_PC_GLuint) ) (uintptr_t) GetProcAddress( crtemu_pc->gl_dll, "glEnableVertexAttribArray" ); 410 | crtemu_pc->VertexAttribPointer = ( void (CRTEMU_PC_GLCALLTYPE*) (CRTEMU_PC_GLuint, CRTEMU_PC_GLint, CRTEMU_PC_GLenum, CRTEMU_PC_GLboolean, CRTEMU_PC_GLsizei, void const*) ) (uintptr_t) GetProcAddress( crtemu_pc->gl_dll, "glVertexAttribPointer" ); 411 | crtemu_pc->GenTextures = ( void (CRTEMU_PC_GLCALLTYPE*) (CRTEMU_PC_GLsizei, CRTEMU_PC_GLuint*) ) (uintptr_t) GetProcAddress( crtemu_pc->gl_dll, "glGenTextures" ); 412 | crtemu_pc->Enable = ( void (CRTEMU_PC_GLCALLTYPE*) (CRTEMU_PC_GLenum) ) (uintptr_t) GetProcAddress( crtemu_pc->gl_dll, "glEnable" ); 413 | crtemu_pc->ActiveTexture = ( void (CRTEMU_PC_GLCALLTYPE*) (CRTEMU_PC_GLenum) ) (uintptr_t) GetProcAddress( crtemu_pc->gl_dll, "glActiveTexture" ); 414 | crtemu_pc->BindTexture = ( void (CRTEMU_PC_GLCALLTYPE*) (CRTEMU_PC_GLenum, CRTEMU_PC_GLuint) ) (uintptr_t) GetProcAddress( crtemu_pc->gl_dll, "glBindTexture" ); 415 | crtemu_pc->TexParameteri = ( void (CRTEMU_PC_GLCALLTYPE*) (CRTEMU_PC_GLenum, CRTEMU_PC_GLenum, CRTEMU_PC_GLint) ) (uintptr_t) GetProcAddress( crtemu_pc->gl_dll, "glTexParameteri" ); 416 | crtemu_pc->DeleteBuffers = ( void (CRTEMU_PC_GLCALLTYPE*) (CRTEMU_PC_GLsizei, CRTEMU_PC_GLuint const*) ) (uintptr_t) GetProcAddress( crtemu_pc->gl_dll, "glDeleteBuffers" ); 417 | crtemu_pc->DeleteTextures = ( void (CRTEMU_PC_GLCALLTYPE*) (CRTEMU_PC_GLsizei, CRTEMU_PC_GLuint const*) ) (uintptr_t) GetProcAddress( crtemu_pc->gl_dll, "glDeleteTextures" ); 418 | crtemu_pc->BufferData = ( void (CRTEMU_PC_GLCALLTYPE*) (CRTEMU_PC_GLenum, CRTEMU_PC_GLsizeiptr, void const *, CRTEMU_PC_GLenum) ) (uintptr_t) GetProcAddress( crtemu_pc->gl_dll, "glBufferData" ); 419 | crtemu_pc->UseProgram = ( void (CRTEMU_PC_GLCALLTYPE*) (CRTEMU_PC_GLuint) ) (uintptr_t) GetProcAddress( crtemu_pc->gl_dll, "glUseProgram" ); 420 | crtemu_pc->Uniform1i = ( void (CRTEMU_PC_GLCALLTYPE*) (CRTEMU_PC_GLint, CRTEMU_PC_GLint) ) (uintptr_t) GetProcAddress( crtemu_pc->gl_dll, "glUniform1i" ); 421 | crtemu_pc->Uniform3f = ( void (CRTEMU_PC_GLCALLTYPE*) (CRTEMU_PC_GLint, CRTEMU_PC_GLfloat, CRTEMU_PC_GLfloat, CRTEMU_PC_GLfloat) ) (uintptr_t) GetProcAddress( crtemu_pc->gl_dll, "glUniform3f" ); 422 | crtemu_pc->GetUniformLocation = ( CRTEMU_PC_GLint (CRTEMU_PC_GLCALLTYPE*) (CRTEMU_PC_GLuint, CRTEMU_PC_GLchar const*) ) (uintptr_t) GetProcAddress( crtemu_pc->gl_dll, "glGetUniformLocation" ); 423 | crtemu_pc->TexImage2D = ( void (CRTEMU_PC_GLCALLTYPE*) (CRTEMU_PC_GLenum, CRTEMU_PC_GLint, CRTEMU_PC_GLint, CRTEMU_PC_GLsizei, CRTEMU_PC_GLsizei, CRTEMU_PC_GLint, CRTEMU_PC_GLenum, CRTEMU_PC_GLenum, void const*) ) (uintptr_t) GetProcAddress( crtemu_pc->gl_dll, "glTexImage2D" ); 424 | crtemu_pc->ClearColor = ( void (CRTEMU_PC_GLCALLTYPE*) (CRTEMU_PC_GLfloat, CRTEMU_PC_GLfloat, CRTEMU_PC_GLfloat, CRTEMU_PC_GLfloat) ) (uintptr_t) GetProcAddress( crtemu_pc->gl_dll, "glClearColor" ); 425 | crtemu_pc->Clear = ( void (CRTEMU_PC_GLCALLTYPE*) (CRTEMU_PC_GLbitfield) ) (uintptr_t) GetProcAddress( crtemu_pc->gl_dll, "glClear" ); 426 | crtemu_pc->DrawArrays = ( void (CRTEMU_PC_GLCALLTYPE*) (CRTEMU_PC_GLenum, CRTEMU_PC_GLint, CRTEMU_PC_GLsizei) ) (uintptr_t) GetProcAddress( crtemu_pc->gl_dll, "glDrawArrays" ); 427 | crtemu_pc->Viewport = ( void (CRTEMU_PC_GLCALLTYPE*) (CRTEMU_PC_GLint, CRTEMU_PC_GLint, CRTEMU_PC_GLsizei, CRTEMU_PC_GLsizei) ) (uintptr_t) GetProcAddress( crtemu_pc->gl_dll, "glViewport" ); 428 | crtemu_pc->DeleteShader = ( void (CRTEMU_PC_GLCALLTYPE*) (CRTEMU_PC_GLuint) ) (uintptr_t) GetProcAddress( crtemu_pc->gl_dll, "glDeleteShader" ); 429 | crtemu_pc->DeleteProgram = ( void (CRTEMU_PC_GLCALLTYPE*) (CRTEMU_PC_GLuint) ) (uintptr_t) GetProcAddress( crtemu_pc->gl_dll, "glDeleteProgram" ); 430 | #ifdef CRTEMU_PC_REPORT_SHADER_ERRORS 431 | crtemu_pc->GetShaderInfoLog = ( void (CRTEMU_PC_GLCALLTYPE*) (CRTEMU_PC_GLuint, CRTEMU_PC_GLsizei, CRTEMU_PC_GLsizei*, CRTEMU_PC_GLchar*) ) (uintptr_t) GetProcAddress( crtemu_pc->gl_dll, "glGetShaderInfoLog" ); 432 | #endif 433 | 434 | // Any opengl functions which didn't bind, try binding them using wglGetProcAddrss 435 | if( !crtemu_pc->TexParameterfv ) crtemu_pc->TexParameterfv = ( void (CRTEMU_PC_GLCALLTYPE*) (CRTEMU_PC_GLenum, CRTEMU_PC_GLenum, CRTEMU_PC_GLfloat const*) ) (uintptr_t) crtemu_pc->wglGetProcAddress( "glTexParameterfv" ); 436 | if( !crtemu_pc->DeleteFramebuffers ) crtemu_pc->DeleteFramebuffers = ( void (CRTEMU_PC_GLCALLTYPE*) (CRTEMU_PC_GLsizei, CRTEMU_PC_GLuint const*) ) (uintptr_t) crtemu_pc->wglGetProcAddress( "glDeleteFramebuffers" ); 437 | if( !crtemu_pc->GetIntegerv ) crtemu_pc->GetIntegerv = ( void (CRTEMU_PC_GLCALLTYPE*) (CRTEMU_PC_GLenum, CRTEMU_PC_GLint *) ) (uintptr_t) crtemu_pc->wglGetProcAddress( "glGetIntegerv" ); 438 | if( !crtemu_pc->GenFramebuffers ) crtemu_pc->GenFramebuffers = ( void (CRTEMU_PC_GLCALLTYPE*) (CRTEMU_PC_GLsizei, CRTEMU_PC_GLuint *) ) (uintptr_t) crtemu_pc->wglGetProcAddress( "glGenFramebuffers" ); 439 | if( !crtemu_pc->BindFramebuffer ) crtemu_pc->BindFramebuffer = ( void (CRTEMU_PC_GLCALLTYPE*) (CRTEMU_PC_GLenum, CRTEMU_PC_GLuint) ) (uintptr_t) crtemu_pc->wglGetProcAddress( "glBindFramebuffer" ); 440 | if( !crtemu_pc->Uniform1f ) crtemu_pc->Uniform1f = ( void (CRTEMU_PC_GLCALLTYPE*) (CRTEMU_PC_GLint, CRTEMU_PC_GLfloat) ) (uintptr_t) crtemu_pc->wglGetProcAddress( "glUniform1f" ); 441 | if( !crtemu_pc->Uniform2f ) crtemu_pc->Uniform2f = ( void (CRTEMU_PC_GLCALLTYPE*) (CRTEMU_PC_GLint, CRTEMU_PC_GLfloat, CRTEMU_PC_GLfloat) ) (uintptr_t) crtemu_pc->wglGetProcAddress( "glUniform2f" ); 442 | if( !crtemu_pc->FramebufferTexture2D ) crtemu_pc->FramebufferTexture2D = ( void (CRTEMU_PC_GLCALLTYPE*) (CRTEMU_PC_GLenum, CRTEMU_PC_GLenum, CRTEMU_PC_GLenum, CRTEMU_PC_GLuint, CRTEMU_PC_GLint) ) (uintptr_t) crtemu_pc->wglGetProcAddress( "glFramebufferTexture2D" ); 443 | if( !crtemu_pc->CreateShader ) crtemu_pc->CreateShader = ( CRTEMU_PC_GLuint (CRTEMU_PC_GLCALLTYPE*) (CRTEMU_PC_GLenum) ) (uintptr_t) crtemu_pc->wglGetProcAddress( "glCreateShader" ); 444 | if( !crtemu_pc->ShaderSource ) crtemu_pc->ShaderSource = ( void (CRTEMU_PC_GLCALLTYPE*) (CRTEMU_PC_GLuint, CRTEMU_PC_GLsizei, CRTEMU_PC_GLchar const* const*, CRTEMU_PC_GLint const*) ) (uintptr_t) crtemu_pc->wglGetProcAddress( "glShaderSource" ); 445 | if( !crtemu_pc->CompileShader ) crtemu_pc->CompileShader = ( void (CRTEMU_PC_GLCALLTYPE*) (CRTEMU_PC_GLuint) ) (uintptr_t) crtemu_pc->wglGetProcAddress( "glCompileShader" ); 446 | if( !crtemu_pc->GetShaderiv ) crtemu_pc->GetShaderiv = ( void (CRTEMU_PC_GLCALLTYPE*) (CRTEMU_PC_GLuint, CRTEMU_PC_GLenum, CRTEMU_PC_GLint*) ) (uintptr_t) crtemu_pc->wglGetProcAddress( "glGetShaderiv" ); 447 | if( !crtemu_pc->CreateProgram ) crtemu_pc->CreateProgram = ( CRTEMU_PC_GLuint (CRTEMU_PC_GLCALLTYPE*) (void) ) (uintptr_t) crtemu_pc->wglGetProcAddress( "glCreateProgram" ); 448 | if( !crtemu_pc->AttachShader ) crtemu_pc->AttachShader = ( void (CRTEMU_PC_GLCALLTYPE*) (CRTEMU_PC_GLuint, CRTEMU_PC_GLuint) ) (uintptr_t) crtemu_pc->wglGetProcAddress( "glAttachShader" ); 449 | if( !crtemu_pc->BindAttribLocation ) crtemu_pc->BindAttribLocation = ( void (CRTEMU_PC_GLCALLTYPE*) (CRTEMU_PC_GLuint, CRTEMU_PC_GLuint, CRTEMU_PC_GLchar const*) ) (uintptr_t) crtemu_pc->wglGetProcAddress( "glBindAttribLocation" ); 450 | if( !crtemu_pc->LinkProgram ) crtemu_pc->LinkProgram = ( void (CRTEMU_PC_GLCALLTYPE*) (CRTEMU_PC_GLuint) ) (uintptr_t) crtemu_pc->wglGetProcAddress( "glLinkProgram" ); 451 | if( !crtemu_pc->GetProgramiv ) crtemu_pc->GetProgramiv = ( void (CRTEMU_PC_GLCALLTYPE*) (CRTEMU_PC_GLuint, CRTEMU_PC_GLenum, CRTEMU_PC_GLint*) ) (uintptr_t) crtemu_pc->wglGetProcAddress( "glGetProgramiv" ); 452 | if( !crtemu_pc->GenBuffers ) crtemu_pc->GenBuffers = ( void (CRTEMU_PC_GLCALLTYPE*) (CRTEMU_PC_GLsizei, CRTEMU_PC_GLuint*) ) (uintptr_t) crtemu_pc->wglGetProcAddress( "glGenBuffers" ); 453 | if( !crtemu_pc->BindBuffer ) crtemu_pc->BindBuffer = ( void (CRTEMU_PC_GLCALLTYPE*) (CRTEMU_PC_GLenum, CRTEMU_PC_GLuint) ) (uintptr_t) crtemu_pc->wglGetProcAddress( "glBindBuffer" ); 454 | if( !crtemu_pc->EnableVertexAttribArray ) crtemu_pc->EnableVertexAttribArray = ( void (CRTEMU_PC_GLCALLTYPE*) (CRTEMU_PC_GLuint) ) (uintptr_t) crtemu_pc->wglGetProcAddress( "glEnableVertexAttribArray" ); 455 | if( !crtemu_pc->VertexAttribPointer ) crtemu_pc->VertexAttribPointer = ( void (CRTEMU_PC_GLCALLTYPE*) (CRTEMU_PC_GLuint, CRTEMU_PC_GLint, CRTEMU_PC_GLenum, CRTEMU_PC_GLboolean, CRTEMU_PC_GLsizei, void const*) ) (uintptr_t) crtemu_pc->wglGetProcAddress( "glVertexAttribPointer" ); 456 | if( !crtemu_pc->GenTextures ) crtemu_pc->GenTextures = ( void (CRTEMU_PC_GLCALLTYPE*) (CRTEMU_PC_GLsizei, CRTEMU_PC_GLuint*) ) (uintptr_t) crtemu_pc->wglGetProcAddress( "glGenTextures" ); 457 | if( !crtemu_pc->Enable ) crtemu_pc->Enable = ( void (CRTEMU_PC_GLCALLTYPE*) (CRTEMU_PC_GLenum) ) (uintptr_t) crtemu_pc->wglGetProcAddress( "glEnable" ); 458 | if( !crtemu_pc->ActiveTexture ) crtemu_pc->ActiveTexture = ( void (CRTEMU_PC_GLCALLTYPE*) (CRTEMU_PC_GLenum) ) (uintptr_t) crtemu_pc->wglGetProcAddress( "glActiveTexture" ); 459 | if( !crtemu_pc->BindTexture ) crtemu_pc->BindTexture = ( void (CRTEMU_PC_GLCALLTYPE*) (CRTEMU_PC_GLenum, CRTEMU_PC_GLuint) ) (uintptr_t) crtemu_pc->wglGetProcAddress( "glBindTexture" ); 460 | if( !crtemu_pc->TexParameteri ) crtemu_pc->TexParameteri = ( void (CRTEMU_PC_GLCALLTYPE*) (CRTEMU_PC_GLenum, CRTEMU_PC_GLenum, CRTEMU_PC_GLint) ) (uintptr_t) crtemu_pc->wglGetProcAddress( "glTexParameteri" ); 461 | if( !crtemu_pc->DeleteBuffers ) crtemu_pc->DeleteBuffers = ( void (CRTEMU_PC_GLCALLTYPE*) (CRTEMU_PC_GLsizei, CRTEMU_PC_GLuint const*) ) (uintptr_t) crtemu_pc->wglGetProcAddress( "glDeleteBuffers" ); 462 | if( !crtemu_pc->DeleteTextures ) crtemu_pc->DeleteTextures = ( void (CRTEMU_PC_GLCALLTYPE*) (CRTEMU_PC_GLsizei, CRTEMU_PC_GLuint const*) ) (uintptr_t) crtemu_pc->wglGetProcAddress( "glDeleteTextures" ); 463 | if( !crtemu_pc->BufferData ) crtemu_pc->BufferData = ( void (CRTEMU_PC_GLCALLTYPE*) (CRTEMU_PC_GLenum, CRTEMU_PC_GLsizeiptr, void const *, CRTEMU_PC_GLenum) ) (uintptr_t) crtemu_pc->wglGetProcAddress( "glBufferData" ); 464 | if( !crtemu_pc->UseProgram ) crtemu_pc->UseProgram = ( void (CRTEMU_PC_GLCALLTYPE*) (CRTEMU_PC_GLuint) ) (uintptr_t) crtemu_pc->wglGetProcAddress( "glUseProgram" ); 465 | if( !crtemu_pc->Uniform1i ) crtemu_pc->Uniform1i = ( void (CRTEMU_PC_GLCALLTYPE*) (CRTEMU_PC_GLint, CRTEMU_PC_GLint) ) (uintptr_t) crtemu_pc->wglGetProcAddress( "glUniform1i" ); 466 | if( !crtemu_pc->Uniform3f ) crtemu_pc->Uniform3f = ( void (CRTEMU_PC_GLCALLTYPE*) (CRTEMU_PC_GLint, CRTEMU_PC_GLfloat, CRTEMU_PC_GLfloat, CRTEMU_PC_GLfloat) ) (uintptr_t) crtemu_pc->wglGetProcAddress( "glUniform3f" ); 467 | if( !crtemu_pc->GetUniformLocation ) crtemu_pc->GetUniformLocation = ( CRTEMU_PC_GLint (CRTEMU_PC_GLCALLTYPE*) (CRTEMU_PC_GLuint, CRTEMU_PC_GLchar const*) ) (uintptr_t) crtemu_pc->wglGetProcAddress( "glGetUniformLocation" ); 468 | if( !crtemu_pc->TexImage2D ) crtemu_pc->TexImage2D = ( void (CRTEMU_PC_GLCALLTYPE*) (CRTEMU_PC_GLenum, CRTEMU_PC_GLint, CRTEMU_PC_GLint, CRTEMU_PC_GLsizei, CRTEMU_PC_GLsizei, CRTEMU_PC_GLint, CRTEMU_PC_GLenum, CRTEMU_PC_GLenum, void const*) ) (uintptr_t) crtemu_pc->wglGetProcAddress( "glTexImage2D" ); 469 | if( !crtemu_pc->ClearColor ) crtemu_pc->ClearColor = ( void (CRTEMU_PC_GLCALLTYPE*) (CRTEMU_PC_GLfloat, CRTEMU_PC_GLfloat, CRTEMU_PC_GLfloat, CRTEMU_PC_GLfloat) ) (uintptr_t) crtemu_pc->wglGetProcAddress( "glClearColor" ); 470 | if( !crtemu_pc->Clear ) crtemu_pc->Clear = ( void (CRTEMU_PC_GLCALLTYPE*) (CRTEMU_PC_GLbitfield) ) (uintptr_t) crtemu_pc->wglGetProcAddress( "glClear" ); 471 | if( !crtemu_pc->DrawArrays ) crtemu_pc->DrawArrays = ( void (CRTEMU_PC_GLCALLTYPE*) (CRTEMU_PC_GLenum, CRTEMU_PC_GLint, CRTEMU_PC_GLsizei) ) (uintptr_t) crtemu_pc->wglGetProcAddress( "glDrawArrays" ); 472 | if( !crtemu_pc->Viewport ) crtemu_pc->Viewport = ( void (CRTEMU_PC_GLCALLTYPE*) (CRTEMU_PC_GLint, CRTEMU_PC_GLint, CRTEMU_PC_GLsizei, CRTEMU_PC_GLsizei) ) (uintptr_t) crtemu_pc->wglGetProcAddress( "glViewport" ); 473 | if( !crtemu_pc->DeleteShader ) crtemu_pc->DeleteShader = ( void (CRTEMU_PC_GLCALLTYPE*) (CRTEMU_PC_GLuint) ) (uintptr_t) crtemu_pc->wglGetProcAddress( "glDeleteShader" ); 474 | if( !crtemu_pc->DeleteProgram ) crtemu_pc->DeleteProgram = ( void (CRTEMU_PC_GLCALLTYPE*) (CRTEMU_PC_GLuint) ) (uintptr_t) crtemu_pc->wglGetProcAddress( "glDeleteProgram" ); 475 | #ifdef CRTEMU_PC_REPORT_SHADER_ERRORS 476 | if( !crtemu_pc->GetShaderInfoLog ) crtemu_pc->GetShaderInfoLog = ( void (CRTEMU_PC_GLCALLTYPE*) (CRTEMU_PC_GLuint, CRTEMU_PC_GLsizei, CRTEMU_PC_GLsizei*, CRTEMU_PC_GLchar*) ) (uintptr_t) crtemu_pc->wglGetProcAddress( "glGetShaderInfoLog" ); 477 | #endif 478 | 479 | #else 480 | 481 | crtemu_pc->TexParameterfv = glTexParameterfv; 482 | crtemu_pc->DeleteFramebuffers = glDeleteFramebuffers; 483 | crtemu_pc->GetIntegerv = glGetIntegerv; 484 | crtemu_pc->GenFramebuffers = glGenFramebuffers; 485 | crtemu_pc->BindFramebuffer = glBindFramebuffer; 486 | crtemu_pc->Uniform1f = glUniform1f; 487 | crtemu_pc->Uniform2f = glUniform2f; 488 | crtemu_pc->FramebufferTexture2D = glFramebufferTexture2D; 489 | crtemu_pc->CreateShader = glCreateShader; 490 | crtemu_pc->ShaderSource = glShaderSource; 491 | crtemu_pc->CompileShader = glCompileShader; 492 | crtemu_pc->GetShaderiv = glGetShaderiv; 493 | crtemu_pc->CreateProgram = glCreateProgram; 494 | crtemu_pc->AttachShader = glAttachShader; 495 | crtemu_pc->BindAttribLocation = glBindAttribLocation; 496 | crtemu_pc->LinkProgram = glLinkProgram; 497 | crtemu_pc->GetProgramiv = glGetProgramiv; 498 | crtemu_pc->GenBuffers = glGenBuffers; 499 | crtemu_pc->BindBuffer = glBindBuffer; 500 | crtemu_pc->EnableVertexAttribArray = glEnableVertexAttribArray; 501 | crtemu_pc->VertexAttribPointer = glVertexAttribPointer; 502 | crtemu_pc->GenTextures = glGenTextures; 503 | crtemu_pc->Enable = glEnable; 504 | crtemu_pc->ActiveTexture = glActiveTexture; 505 | crtemu_pc->BindTexture = glBindTexture; 506 | crtemu_pc->TexParameteri = glTexParameteri; 507 | crtemu_pc->DeleteBuffers = glDeleteBuffers; 508 | crtemu_pc->DeleteTextures = glDeleteTextures; 509 | crtemu_pc->BufferData = glBufferData; 510 | crtemu_pc->UseProgram = glUseProgram; 511 | crtemu_pc->Uniform1i = glUniform1i; 512 | crtemu_pc->Uniform3f = glUniform3f; 513 | crtemu_pc->GetUniformLocation = glGetUniformLocation; 514 | crtemu_pc->TexImage2D = glTexImage2D; 515 | crtemu_pc->ClearColor = glClearColor; 516 | crtemu_pc->Clear = glClear; 517 | crtemu_pc->DrawArrays = glDrawArrays; 518 | crtemu_pc->Viewport = glViewport; 519 | crtemu_pc->DeleteShader = glDeleteShader; 520 | crtemu_pc->DeleteProgram = glDeleteProgram; 521 | #ifdef CRTEMU_PC_REPORT_SHADER_ERRORS 522 | crtemu_pc->GetShaderInfoLog = glGetShaderInfoLog; 523 | #endif 524 | 525 | #endif 526 | 527 | // Report error if any gl function was not found. 528 | if( !crtemu_pc->TexParameterfv ) goto failed; 529 | if( !crtemu_pc->DeleteFramebuffers ) goto failed; 530 | if( !crtemu_pc->GetIntegerv ) goto failed; 531 | if( !crtemu_pc->GenFramebuffers ) goto failed; 532 | if( !crtemu_pc->BindFramebuffer ) goto failed; 533 | if( !crtemu_pc->Uniform1f ) goto failed; 534 | if( !crtemu_pc->Uniform2f ) goto failed; 535 | if( !crtemu_pc->FramebufferTexture2D ) goto failed; 536 | if( !crtemu_pc->CreateShader ) goto failed; 537 | if( !crtemu_pc->ShaderSource ) goto failed; 538 | if( !crtemu_pc->CompileShader ) goto failed; 539 | if( !crtemu_pc->GetShaderiv ) goto failed; 540 | if( !crtemu_pc->CreateProgram ) goto failed; 541 | if( !crtemu_pc->AttachShader ) goto failed; 542 | if( !crtemu_pc->BindAttribLocation ) goto failed; 543 | if( !crtemu_pc->LinkProgram ) goto failed; 544 | if( !crtemu_pc->GetProgramiv ) goto failed; 545 | if( !crtemu_pc->GenBuffers ) goto failed; 546 | if( !crtemu_pc->BindBuffer ) goto failed; 547 | if( !crtemu_pc->EnableVertexAttribArray ) goto failed; 548 | if( !crtemu_pc->VertexAttribPointer ) goto failed; 549 | if( !crtemu_pc->GenTextures ) goto failed; 550 | if( !crtemu_pc->Enable ) goto failed; 551 | if( !crtemu_pc->ActiveTexture ) goto failed; 552 | if( !crtemu_pc->BindTexture ) goto failed; 553 | if( !crtemu_pc->TexParameteri ) goto failed; 554 | if( !crtemu_pc->DeleteBuffers ) goto failed; 555 | if( !crtemu_pc->DeleteTextures ) goto failed; 556 | if( !crtemu_pc->BufferData ) goto failed; 557 | if( !crtemu_pc->UseProgram ) goto failed; 558 | if( !crtemu_pc->Uniform1i ) goto failed; 559 | if( !crtemu_pc->Uniform3f ) goto failed; 560 | if( !crtemu_pc->GetUniformLocation ) goto failed; 561 | if( !crtemu_pc->TexImage2D ) goto failed; 562 | if( !crtemu_pc->ClearColor ) goto failed; 563 | if( !crtemu_pc->Clear ) goto failed; 564 | if( !crtemu_pc->DrawArrays ) goto failed; 565 | if( !crtemu_pc->Viewport ) goto failed; 566 | if( !crtemu_pc->DeleteShader ) goto failed; 567 | if( !crtemu_pc->DeleteProgram ) goto failed; 568 | #ifdef CRTEMU_PC_REPORT_SHADER_ERRORS 569 | if( !crtemu_pc->GetShaderInfoLog ) goto failed; 570 | #endif 571 | 572 | char const* vs_source = 573 | "#version 120\n\n" 574 | "" 575 | "attribute vec4 pos;" 576 | "varying vec2 uv;" 577 | "" 578 | "void main( void )" 579 | " {" 580 | " gl_Position = vec4( pos.xy, 0.0, 1.0 );" 581 | " uv = pos.zw;" 582 | " }"; 583 | 584 | char const* crt_fs_source = 585 | "#version 120\n\n\n" 586 | "\n" 587 | "varying vec2 uv;\n" 588 | "\n" 589 | "uniform vec3 modulate;\n" 590 | "uniform vec2 resolution;\n" 591 | "uniform float time;\n" 592 | "uniform sampler2D backbuffer;\n" 593 | "uniform sampler2D blurbuffer;\n" 594 | "uniform sampler2D frametexture;\n" 595 | "uniform float use_frame;\n" 596 | "\n" 597 | "vec3 tsample( sampler2D samp, vec2 tc, float offs, vec2 resolution )\n" 598 | " {\n" 599 | " tc = tc * vec2(1.035, 0.96) + vec2(-0.0125*0.75, 0.02);\n" 600 | " tc = tc * 1.2 - 0.1;\n" 601 | " vec3 s = pow( abs( texture2D( samp, vec2( tc.x, 1.0-tc.y ) ).rgb), vec3( 2.2 ) );\n" 602 | " return s*vec3(1.25);\n" 603 | " }\n" 604 | "\n" 605 | "vec3 filmic( vec3 LinearColor )\n" 606 | " {\n" 607 | " vec3 x = max( vec3(0.0), LinearColor-vec3(0.004));\n" 608 | " return (x*(6.2*x+0.5))/(x*(6.2*x+1.7)+0.06);\n" 609 | " }\n" 610 | "\n" 611 | "vec2 curve( vec2 uv )\n" 612 | " {\n" 613 | " uv = (uv - 0.5) * 2.0;\n" 614 | " uv *= 1.1; \n" 615 | " uv.x *= 1.0 + pow((abs(uv.y) / 5.0), 2.0);\n" 616 | " uv.y *= 1.0 + pow((abs(uv.x) / 4.0), 2.0);\n" 617 | " uv = (uv / 2.0) + 0.5;\n" 618 | " uv = uv *0.92 + 0.04;\n" 619 | " return uv;\n" 620 | " }\n" 621 | "\n" 622 | "float rand(vec2 co)\n" 623 | " {\n" 624 | " return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);\n" 625 | " }\n" 626 | " \n" 627 | "void main(void)\n" 628 | " {\n" 629 | " /* Curve */\n" 630 | " vec2 curved_uv = mix( curve( uv ), uv, 0.8 );\n" 631 | " float scale = 0.04;\n" 632 | " vec2 scuv = curved_uv*(1.0-scale)+scale/2.0+vec2(0.003, -0.001);\n" 633 | "\n" 634 | " /* Main color, Bleed */\n" 635 | " vec3 col;\n" 636 | " float x = sin(0.1*time+curved_uv.y*13.0)*sin(0.23*time+curved_uv.y*19.0)*sin(0.3+0.11*time+curved_uv.y*23.0)*0.0012;\n" 637 | " float o =sin(gl_FragCoord.y*1.5)/resolution.x;\n" 638 | " x+=o*0.25;\n" 639 | " x *= 0.2f;\n" 640 | " col.r = tsample(backbuffer,vec2(x+scuv.x+0.0009*0.25,scuv.y+0.0009*0.25),resolution.y/800.0, resolution ).x+0.02;\n" 641 | " col.g = tsample(backbuffer,vec2(x+scuv.x+0.0000*0.25,scuv.y-0.0011*0.25),resolution.y/800.0, resolution ).y+0.02;\n" 642 | " col.b = tsample(backbuffer,vec2(x+scuv.x-0.0015*0.25,scuv.y+0.0000*0.25),resolution.y/800.0, resolution ).z+0.02;\n" 643 | " float i = clamp(col.r*0.299 + col.g*0.587 + col.b*0.114, 0.0, 1.0 ); \n" 644 | " i = pow( 1.0 - pow(i,2.0), 1.0 );\n" 645 | " i = (1.0-i) * 0.85 + 0.15; \n" 646 | "\n" 647 | " /* Ghosting */\n" 648 | " float ghs = 0.05;\n" 649 | " vec3 r = tsample(blurbuffer, vec2(x-0.014*1.0, -0.027)*0.45+0.007*vec2( 0.35*sin(1.0/7.0 + 15.0*curved_uv.y + 0.9*time), \n" 650 | " 0.35*sin( 2.0/7.0 + 10.0*curved_uv.y + 1.37*time) )+vec2(scuv.x+0.001,scuv.y+0.001),\n" 651 | " 5.5+1.3*sin( 3.0/9.0 + 31.0*curved_uv.x + 1.70*time),resolution).xyz*vec3(0.5,0.25,0.25);\n" 652 | " vec3 g = tsample(blurbuffer, vec2(x-0.019*1.0, -0.020)*0.45+0.007*vec2( 0.35*cos(1.0/9.0 + 15.0*curved_uv.y + 0.5*time), \n" 653 | " 0.35*sin( 2.0/9.0 + 10.0*curved_uv.y + 1.50*time) )+vec2(scuv.x+0.000,scuv.y-0.002),\n" 654 | " 5.4+1.3*sin( 3.0/3.0 + 71.0*curved_uv.x + 1.90*time),resolution).xyz*vec3(0.25,0.5,0.25);\n" 655 | " vec3 b = tsample(blurbuffer, vec2(x-0.017*1.0, -0.003)*0.35+0.007*vec2( 0.35*sin(2.0/3.0 + 15.0*curved_uv.y + 0.7*time), \n" 656 | " 0.35*cos( 2.0/3.0 + 10.0*curved_uv.y + 1.63*time) )+vec2(scuv.x-0.002,scuv.y+0.000),\n" 657 | " 5.3+1.3*sin( 3.0/7.0 + 91.0*curved_uv.x + 1.65*time),resolution).xyz*vec3(0.25,0.25,0.5);\n" 658 | "\n" 659 | " col += vec3(ghs*(1.0-0.299))*pow(clamp(vec3(3.0)*r,vec3(0.0),vec3(1.0)),vec3(2.0))*vec3(i);\n" 660 | " col += vec3(ghs*(1.0-0.587))*pow(clamp(vec3(3.0)*g,vec3(0.0),vec3(1.0)),vec3(2.0))*vec3(i);\n" 661 | " col += vec3(ghs*(1.0-0.114))*pow(clamp(vec3(3.0)*b,vec3(0.0),vec3(1.0)),vec3(2.0))*vec3(i);\n" 662 | "\n" 663 | " /* Level adjustment (curves) */\n" 664 | " col *= vec3(0.95,1.05,0.95);\n" 665 | " col = clamp(col*1.3 + 0.75*col*col + 1.25*col*col*col*col*col,vec3(0.0),vec3(10.0));\n" 666 | "\n" 667 | " /* Vignette */\n" 668 | " float vig = (0.1 + 1.0*16.0*curved_uv.x*curved_uv.y*(1.0-curved_uv.x)*(1.0-curved_uv.y));\n" 669 | " vig = 1.3*pow(vig,0.5);\n" 670 | " col *= vig;\n" 671 | "\n" 672 | " /* Scanlines */\n" 673 | " float scans = clamp( 0.35+0.18*sin(0.0*time+curved_uv.y*resolution.y*1.5), 0.0, 1.0);\n" 674 | " float s = pow(scans,0.9);\n" 675 | " col = col * vec3(s);\n" 676 | "\n" 677 | " /* Vertical lines (shadow mask) */\n" 678 | " col*=1.0-0.23*(clamp((mod(gl_FragCoord.xy.x, 3.0))/2.0,0.0,1.0));\n" 679 | "\n" 680 | " /* Tone map */\n" 681 | " col = filmic( col );\n" 682 | "\n" 683 | " /* Noise */\n" 684 | " //vec2 seed = floor(curved_uv*resolution.xy*vec2(0.5))/resolution.xy;\n" 685 | " vec2 seed = curved_uv*resolution.xy;;\n" 686 | " /* seed = curved_uv; */\n" 687 | " col -= 0.015*pow(vec3(rand( seed +time ), rand( seed +time*2.0 ), rand( seed +time * 3.0 ) ), vec3(1.5) );\n" 688 | "\n" 689 | " /* Flicker */\n" 690 | " col *= (1.0-0.004*(sin(50.0*time+curved_uv.y*2.0)*0.5+0.5));\n" 691 | "\n" 692 | " /* Clamp */\n" 693 | " if (curved_uv.x < 0.0 || curved_uv.x > 1.0)\n" 694 | " col *= 0.0;\n" 695 | " if (curved_uv.y < 0.0 || curved_uv.y > 1.0)\n" 696 | " col *= 0.0;\n" 697 | "\n" 698 | " /* Frame */\n" 699 | " vec2 fscale = vec2( -0.019, -0.018 );\n" 700 | " vec2 fuv=vec2( uv.x, 1.0 - uv.y)*((1.0)+2.0*fscale)-fscale-vec2(-0.0, 0.005);\n" 701 | " vec4 f=texture2D(frametexture, fuv * vec2(0.91, 0.8) + vec2( 0.050, 0.093 ));\n" 702 | " f.xyz = mix( f.xyz, vec3(0.5,0.5,0.5), 0.5 );\n" 703 | " float fvig = clamp( -0.00+512.0*uv.x*uv.y*(1.0-uv.x)*(1.0-uv.y), 0.2, 0.85 );\n" 704 | " col *= fvig;\n" 705 | " col = mix( col, mix( max( col, 0.0), pow( abs( f.xyz ), vec3( 1.4 ) ), f.w), vec3( use_frame) );\n" 706 | " \n" 707 | " gl_FragColor = vec4( col, 1.0 );\n" 708 | " }\n" 709 | " \n" 710 | ""; 711 | 712 | crtemu_pc->crt_shader = crtemu_pc_internal_build_shader( crtemu_pc, vs_source, crt_fs_source ); 713 | if( crtemu_pc->crt_shader == 0 ) goto failed; 714 | 715 | char const* blur_fs_source = 716 | "#version 120\n\n" 717 | "" 718 | "varying vec2 uv;" 719 | "" 720 | "uniform vec2 blur;" 721 | "uniform sampler2D texture;" 722 | "" 723 | "void main( void )" 724 | " {" 725 | " vec4 sum = texture2D( texture, uv ) * 0.2270270270;" 726 | " sum += texture2D(texture, vec2( uv.x - 4.0 * blur.x, uv.y - 4.0 * blur.y ) ) * 0.0162162162;" 727 | " sum += texture2D(texture, vec2( uv.x - 3.0 * blur.x, uv.y - 3.0 * blur.y ) ) * 0.0540540541;" 728 | " sum += texture2D(texture, vec2( uv.x - 2.0 * blur.x, uv.y - 2.0 * blur.y ) ) * 0.1216216216;" 729 | " sum += texture2D(texture, vec2( uv.x - 1.0 * blur.x, uv.y - 1.0 * blur.y ) ) * 0.1945945946;" 730 | " sum += texture2D(texture, vec2( uv.x + 1.0 * blur.x, uv.y + 1.0 * blur.y ) ) * 0.1945945946;" 731 | " sum += texture2D(texture, vec2( uv.x + 2.0 * blur.x, uv.y + 2.0 * blur.y ) ) * 0.1216216216;" 732 | " sum += texture2D(texture, vec2( uv.x + 3.0 * blur.x, uv.y + 3.0 * blur.y ) ) * 0.0540540541;" 733 | " sum += texture2D(texture, vec2( uv.x + 4.0 * blur.x, uv.y + 4.0 * blur.y ) ) * 0.0162162162;" 734 | " gl_FragColor = sum;" 735 | " } " 736 | ""; 737 | 738 | crtemu_pc->blur_shader = crtemu_pc_internal_build_shader( crtemu_pc, vs_source, blur_fs_source ); 739 | if( crtemu_pc->blur_shader == 0 ) goto failed; 740 | 741 | char const* accumulate_fs_source = 742 | "#version 120\n\n" 743 | "" 744 | "varying vec2 uv;" 745 | "" 746 | "uniform sampler2D tex0;" 747 | "uniform sampler2D tex1;" 748 | "uniform float modulate;" 749 | "" 750 | "void main( void )" 751 | " {" 752 | " vec4 a = texture2D( tex0, uv ) * vec4( modulate );" 753 | " vec4 b = texture2D( tex1, uv );" 754 | "" 755 | " gl_FragColor = max( a, b * 0.96 );" 756 | " } " 757 | ""; 758 | 759 | crtemu_pc->accumulate_shader = crtemu_pc_internal_build_shader( crtemu_pc, vs_source, accumulate_fs_source ); 760 | if( crtemu_pc->accumulate_shader == 0 ) goto failed; 761 | 762 | char const* blend_fs_source = 763 | "#version 120\n\n" 764 | "" 765 | "varying vec2 uv;" 766 | "" 767 | "uniform sampler2D tex0;" 768 | "uniform sampler2D tex1;" 769 | "uniform float modulate;" 770 | "" 771 | "void main( void )" 772 | " {" 773 | " vec4 a = texture2D( tex0, uv ) * vec4( modulate );" 774 | " vec4 b = texture2D( tex1, uv );" 775 | "" 776 | " gl_FragColor = max( a, b * 0.32 );" 777 | " } " 778 | ""; 779 | 780 | crtemu_pc->blend_shader = crtemu_pc_internal_build_shader( crtemu_pc, vs_source, blend_fs_source ); 781 | if( crtemu_pc->blend_shader == 0 ) goto failed; 782 | 783 | char const* copy_fs_source = 784 | "#version 120\n\n" 785 | "" 786 | "varying vec2 uv;" 787 | "" 788 | "uniform sampler2D tex0;" 789 | "" 790 | "void main( void )" 791 | " {" 792 | " gl_FragColor = texture2D( tex0, uv );" 793 | " } " 794 | ""; 795 | 796 | crtemu_pc->copy_shader = crtemu_pc_internal_build_shader( crtemu_pc, vs_source, copy_fs_source ); 797 | if( crtemu_pc->copy_shader == 0 ) goto failed; 798 | 799 | 800 | crtemu_pc->GenTextures( 1, &crtemu_pc->accumulatetexture_a ); 801 | crtemu_pc->GenFramebuffers( 1, &crtemu_pc->accumulatebuffer_a ); 802 | 803 | crtemu_pc->GenTextures( 1, &crtemu_pc->accumulatetexture_b ); 804 | crtemu_pc->GenFramebuffers( 1, &crtemu_pc->accumulatebuffer_b ); 805 | 806 | crtemu_pc->GenTextures( 1, &crtemu_pc->blurtexture_a ); 807 | crtemu_pc->GenFramebuffers( 1, &crtemu_pc->blurbuffer_a ); 808 | 809 | crtemu_pc->GenTextures( 1, &crtemu_pc->blurtexture_b ); 810 | crtemu_pc->GenFramebuffers( 1, &crtemu_pc->blurbuffer_b ); 811 | 812 | crtemu_pc->BindFramebuffer( CRTEMU_PC_GL_FRAMEBUFFER, 0 ); 813 | 814 | crtemu_pc->GenTextures( 1, &crtemu_pc->frametexture ); 815 | crtemu_pc->Enable( CRTEMU_PC_GL_TEXTURE_2D ); 816 | crtemu_pc->ActiveTexture( CRTEMU_PC_GL_TEXTURE2 ); 817 | crtemu_pc->BindTexture( CRTEMU_PC_GL_TEXTURE_2D, crtemu_pc->frametexture ); 818 | crtemu_pc->TexParameteri( CRTEMU_PC_GL_TEXTURE_2D, CRTEMU_PC_GL_TEXTURE_MIN_FILTER, CRTEMU_PC_GL_LINEAR ); 819 | crtemu_pc->TexParameteri( CRTEMU_PC_GL_TEXTURE_2D, CRTEMU_PC_GL_TEXTURE_MAG_FILTER, CRTEMU_PC_GL_LINEAR ); 820 | 821 | crtemu_pc->GenTextures( 1, &crtemu_pc->backbuffer ); 822 | crtemu_pc->Enable( CRTEMU_PC_GL_TEXTURE_2D ); 823 | crtemu_pc->ActiveTexture( CRTEMU_PC_GL_TEXTURE0 ); 824 | crtemu_pc->BindTexture( CRTEMU_PC_GL_TEXTURE_2D, crtemu_pc->backbuffer ); 825 | crtemu_pc->TexParameteri( CRTEMU_PC_GL_TEXTURE_2D, CRTEMU_PC_GL_TEXTURE_MIN_FILTER, CRTEMU_PC_GL_NEAREST ); 826 | crtemu_pc->TexParameteri( CRTEMU_PC_GL_TEXTURE_2D, CRTEMU_PC_GL_TEXTURE_MAG_FILTER, CRTEMU_PC_GL_NEAREST ); 827 | 828 | crtemu_pc->GenBuffers( 1, &crtemu_pc->vertexbuffer ); 829 | crtemu_pc->BindBuffer( CRTEMU_PC_GL_ARRAY_BUFFER, crtemu_pc->vertexbuffer ); 830 | crtemu_pc->EnableVertexAttribArray( 0 ); 831 | crtemu_pc->VertexAttribPointer( 0, 4, CRTEMU_PC_GL_FLOAT, CRTEMU_PC_GL_FALSE, 4 * sizeof( CRTEMU_PC_GLfloat ), 0 ); 832 | 833 | return crtemu_pc; 834 | 835 | failed: 836 | if( crtemu_pc->accumulatetexture_a ) crtemu_pc->DeleteTextures( 1, &crtemu_pc->accumulatetexture_a ); 837 | if( crtemu_pc->accumulatebuffer_a ) crtemu_pc->DeleteFramebuffers( 1, &crtemu_pc->accumulatebuffer_a ); 838 | if( crtemu_pc->accumulatetexture_b ) crtemu_pc->DeleteTextures( 1, &crtemu_pc->accumulatetexture_b ); 839 | if( crtemu_pc->accumulatebuffer_b ) crtemu_pc->DeleteFramebuffers( 1, &crtemu_pc->accumulatebuffer_b ); 840 | if( crtemu_pc->blurtexture_a ) crtemu_pc->DeleteTextures( 1, &crtemu_pc->blurtexture_a ); 841 | if( crtemu_pc->blurbuffer_a ) crtemu_pc->DeleteFramebuffers( 1, &crtemu_pc->blurbuffer_a ); 842 | if( crtemu_pc->blurtexture_b ) crtemu_pc->DeleteTextures( 1, &crtemu_pc->blurtexture_b ); 843 | if( crtemu_pc->blurbuffer_b ) crtemu_pc->DeleteFramebuffers( 1, &crtemu_pc->blurbuffer_b ); 844 | if( crtemu_pc->frametexture ) crtemu_pc->DeleteTextures( 1, &crtemu_pc->frametexture ); 845 | if( crtemu_pc->backbuffer ) crtemu_pc->DeleteTextures( 1, &crtemu_pc->backbuffer ); 846 | if( crtemu_pc->vertexbuffer ) crtemu_pc->DeleteBuffers( 1, &crtemu_pc->vertexbuffer ); 847 | 848 | #ifndef CRTEMU_PC_SDL 849 | if( crtemu_pc->gl_dll ) FreeLibrary( crtemu_pc->gl_dll ); 850 | #endif 851 | CRTEMU_PC_FREE( crtemu_pc->memctx, crtemu_pc ); 852 | return 0; 853 | } 854 | 855 | 856 | void crtemu_pc_destroy( crtemu_pc_t* crtemu_pc ) 857 | { 858 | crtemu_pc->DeleteTextures( 1, &crtemu_pc->accumulatetexture_a ); 859 | crtemu_pc->DeleteFramebuffers( 1, &crtemu_pc->accumulatebuffer_a ); 860 | crtemu_pc->DeleteTextures( 1, &crtemu_pc->accumulatetexture_b ); 861 | crtemu_pc->DeleteFramebuffers( 1, &crtemu_pc->accumulatebuffer_b ); 862 | crtemu_pc->DeleteTextures( 1, &crtemu_pc->blurtexture_a ); 863 | crtemu_pc->DeleteFramebuffers( 1, &crtemu_pc->blurbuffer_a ); 864 | crtemu_pc->DeleteTextures( 1, &crtemu_pc->blurtexture_b ); 865 | crtemu_pc->DeleteFramebuffers( 1, &crtemu_pc->blurbuffer_b ); 866 | crtemu_pc->DeleteTextures( 1, &crtemu_pc->frametexture ); 867 | crtemu_pc->DeleteTextures( 1, &crtemu_pc->backbuffer ); 868 | crtemu_pc->DeleteBuffers( 1, &crtemu_pc->vertexbuffer ); 869 | #ifndef CRTEMU_PC_SDL 870 | FreeLibrary( crtemu_pc->gl_dll ); 871 | #endif 872 | CRTEMU_PC_FREE( crtemu_pc->memctx, crtemu_pc ); 873 | } 874 | 875 | 876 | void crtemu_pc_config(crtemu_pc_t* crtemu_pc, crtemu_pc_config_t const* config) 877 | { 878 | crtemu_pc->config = *config; 879 | } 880 | 881 | 882 | void crtemu_pc_frame( crtemu_pc_t* crtemu_pc, CRTEMU_PC_U32* frame_abgr, int frame_width, int frame_height ) 883 | { 884 | crtemu_pc->ActiveTexture( CRTEMU_PC_GL_TEXTURE3 ); 885 | crtemu_pc->BindTexture( CRTEMU_PC_GL_TEXTURE_2D, crtemu_pc->frametexture ); 886 | crtemu_pc->TexImage2D( CRTEMU_PC_GL_TEXTURE_2D, 0, CRTEMU_PC_GL_RGBA, frame_width, frame_height, 0, CRTEMU_PC_GL_RGBA, CRTEMU_PC_GL_UNSIGNED_BYTE, frame_abgr ); 887 | if( frame_abgr ) 888 | crtemu_pc->use_frame = 1.0f; 889 | else 890 | crtemu_pc->use_frame = 0.0f; 891 | } 892 | 893 | 894 | static void crtemu_pc_internal_blur( crtemu_pc_t* crtemu_pc, CRTEMU_PC_GLuint source, CRTEMU_PC_GLuint blurbuffer_a, CRTEMU_PC_GLuint blurbuffer_b, 895 | CRTEMU_PC_GLuint blurtexture_b, float r, int width, int height ) 896 | { 897 | crtemu_pc->BindFramebuffer( CRTEMU_PC_GL_FRAMEBUFFER, blurbuffer_b ); 898 | crtemu_pc->UseProgram( crtemu_pc->blur_shader ); 899 | crtemu_pc->Uniform2f( crtemu_pc->GetUniformLocation( crtemu_pc->blur_shader, "blur" ), r / (float) width, 0 ); 900 | crtemu_pc->Uniform1i( crtemu_pc->GetUniformLocation( crtemu_pc->blur_shader, "texture" ), 0 ); 901 | crtemu_pc->ActiveTexture( CRTEMU_PC_GL_TEXTURE0 ); 902 | crtemu_pc->BindTexture( CRTEMU_PC_GL_TEXTURE_2D, source ); 903 | crtemu_pc->TexParameteri( CRTEMU_PC_GL_TEXTURE_2D, CRTEMU_PC_GL_TEXTURE_MIN_FILTER, CRTEMU_PC_GL_LINEAR ); 904 | crtemu_pc->TexParameteri( CRTEMU_PC_GL_TEXTURE_2D, CRTEMU_PC_GL_TEXTURE_MAG_FILTER, CRTEMU_PC_GL_LINEAR ); 905 | crtemu_pc->DrawArrays( CRTEMU_PC_GL_TRIANGLE_FAN, 0, 4 ); 906 | crtemu_pc->ActiveTexture( CRTEMU_PC_GL_TEXTURE0 ); 907 | crtemu_pc->BindTexture( CRTEMU_PC_GL_TEXTURE_2D, 0 ); 908 | crtemu_pc->BindFramebuffer( CRTEMU_PC_GL_FRAMEBUFFER, 0 ); 909 | 910 | crtemu_pc->BindFramebuffer( CRTEMU_PC_GL_FRAMEBUFFER, blurbuffer_a ); 911 | crtemu_pc->UseProgram( crtemu_pc->blur_shader ); 912 | crtemu_pc->Uniform2f( crtemu_pc->GetUniformLocation( crtemu_pc->blur_shader, "blur" ), 0, r / (float) height ); 913 | crtemu_pc->Uniform1i( crtemu_pc->GetUniformLocation( crtemu_pc->blur_shader, "texture" ), 0 ); 914 | crtemu_pc->ActiveTexture( CRTEMU_PC_GL_TEXTURE0 ); 915 | crtemu_pc->BindTexture( CRTEMU_PC_GL_TEXTURE_2D, blurtexture_b ); 916 | crtemu_pc->TexParameteri( CRTEMU_PC_GL_TEXTURE_2D, CRTEMU_PC_GL_TEXTURE_MIN_FILTER, CRTEMU_PC_GL_LINEAR ); 917 | crtemu_pc->TexParameteri( CRTEMU_PC_GL_TEXTURE_2D, CRTEMU_PC_GL_TEXTURE_MAG_FILTER, CRTEMU_PC_GL_LINEAR ); 918 | crtemu_pc->DrawArrays( CRTEMU_PC_GL_TRIANGLE_FAN, 0, 4 ); 919 | crtemu_pc->ActiveTexture( CRTEMU_PC_GL_TEXTURE0 ); 920 | crtemu_pc->BindTexture( CRTEMU_PC_GL_TEXTURE_2D, 0 ); 921 | crtemu_pc->BindFramebuffer( CRTEMU_PC_GL_FRAMEBUFFER, 0 ); 922 | } 923 | 924 | 925 | void crtemu_pc_present( crtemu_pc_t* crtemu_pc, CRTEMU_PC_U64 time_us, CRTEMU_PC_U32 const* pixels_xbgr, int width, int height, 926 | CRTEMU_PC_U32 mod_xbgr, CRTEMU_PC_U32 border_xbgr ) 927 | { 928 | int viewport[ 4 ]; 929 | crtemu_pc->GetIntegerv( CRTEMU_PC_GL_VIEWPORT, viewport ); 930 | 931 | if( width != crtemu_pc->last_present_width || height != crtemu_pc->last_present_height ) 932 | { 933 | crtemu_pc->ActiveTexture( CRTEMU_PC_GL_TEXTURE0 ); 934 | 935 | crtemu_pc->BindTexture( CRTEMU_PC_GL_TEXTURE_2D, crtemu_pc->accumulatetexture_a ); 936 | crtemu_pc->TexImage2D( CRTEMU_PC_GL_TEXTURE_2D, 0, CRTEMU_PC_GL_RGB, width, height, 0, CRTEMU_PC_GL_RGB, CRTEMU_PC_GL_UNSIGNED_BYTE, 0 ); 937 | crtemu_pc->BindFramebuffer( CRTEMU_PC_GL_FRAMEBUFFER, crtemu_pc->accumulatebuffer_a ); 938 | crtemu_pc->FramebufferTexture2D( CRTEMU_PC_GL_FRAMEBUFFER, CRTEMU_PC_GL_COLOR_ATTACHMENT0, CRTEMU_PC_GL_TEXTURE_2D, crtemu_pc->accumulatetexture_a, 0 ); 939 | crtemu_pc->BindFramebuffer( CRTEMU_PC_GL_FRAMEBUFFER, 0 ); 940 | 941 | crtemu_pc->BindTexture( CRTEMU_PC_GL_TEXTURE_2D, crtemu_pc->accumulatetexture_b ); 942 | crtemu_pc->TexImage2D( CRTEMU_PC_GL_TEXTURE_2D, 0, CRTEMU_PC_GL_RGB, width, height, 0, CRTEMU_PC_GL_RGB, CRTEMU_PC_GL_UNSIGNED_BYTE, 0 ); 943 | crtemu_pc->BindFramebuffer( CRTEMU_PC_GL_FRAMEBUFFER, crtemu_pc->accumulatebuffer_b ); 944 | crtemu_pc->FramebufferTexture2D( CRTEMU_PC_GL_FRAMEBUFFER, CRTEMU_PC_GL_COLOR_ATTACHMENT0, CRTEMU_PC_GL_TEXTURE_2D, crtemu_pc->accumulatetexture_b, 0 ); 945 | crtemu_pc->BindFramebuffer( CRTEMU_PC_GL_FRAMEBUFFER, 0 ); 946 | 947 | crtemu_pc->BindTexture( CRTEMU_PC_GL_TEXTURE_2D, crtemu_pc->blurtexture_a ); 948 | crtemu_pc->TexImage2D( CRTEMU_PC_GL_TEXTURE_2D, 0, CRTEMU_PC_GL_RGB, width, height, 0, CRTEMU_PC_GL_RGB, CRTEMU_PC_GL_UNSIGNED_BYTE, 0 ); 949 | crtemu_pc->BindFramebuffer( CRTEMU_PC_GL_FRAMEBUFFER, crtemu_pc->blurbuffer_a ); 950 | crtemu_pc->FramebufferTexture2D( CRTEMU_PC_GL_FRAMEBUFFER, CRTEMU_PC_GL_COLOR_ATTACHMENT0, CRTEMU_PC_GL_TEXTURE_2D, crtemu_pc->blurtexture_a, 0 ); 951 | crtemu_pc->BindFramebuffer( CRTEMU_PC_GL_FRAMEBUFFER, 0 ); 952 | 953 | crtemu_pc->BindTexture( CRTEMU_PC_GL_TEXTURE_2D, crtemu_pc->blurtexture_b ); 954 | crtemu_pc->TexImage2D( CRTEMU_PC_GL_TEXTURE_2D, 0, CRTEMU_PC_GL_RGB, width, height, 0, CRTEMU_PC_GL_RGB, CRTEMU_PC_GL_UNSIGNED_BYTE, 0 ); 955 | crtemu_pc->BindFramebuffer( CRTEMU_PC_GL_FRAMEBUFFER, crtemu_pc->blurbuffer_b ); 956 | crtemu_pc->FramebufferTexture2D( CRTEMU_PC_GL_FRAMEBUFFER, CRTEMU_PC_GL_COLOR_ATTACHMENT0, CRTEMU_PC_GL_TEXTURE_2D, crtemu_pc->blurtexture_b, 0 ); 957 | crtemu_pc->BindFramebuffer( CRTEMU_PC_GL_FRAMEBUFFER, 0 ); 958 | } 959 | 960 | 961 | crtemu_pc->last_present_width = width; 962 | crtemu_pc->last_present_height = height; 963 | 964 | CRTEMU_PC_GLfloat vertices[] = 965 | { 966 | -1.0f, -1.0f, 0.0f, 0.0f, 967 | 1.0f, -1.0f, 1.0f, 0.0f, 968 | 1.0f, 1.0f, 1.0f, 1.0f, 969 | -1.0f, 1.0f, 0.0f, 1.0f, 970 | }; 971 | crtemu_pc->BufferData( CRTEMU_PC_GL_ARRAY_BUFFER, 4 * 4 * sizeof( CRTEMU_PC_GLfloat ), vertices, CRTEMU_PC_GL_STATIC_DRAW ); 972 | crtemu_pc->BindBuffer( CRTEMU_PC_GL_ARRAY_BUFFER, crtemu_pc->vertexbuffer ); 973 | 974 | // Copy to backbuffer 975 | crtemu_pc->ActiveTexture( CRTEMU_PC_GL_TEXTURE0 ); 976 | crtemu_pc->BindTexture( CRTEMU_PC_GL_TEXTURE_2D, crtemu_pc->backbuffer ); 977 | crtemu_pc->TexImage2D( CRTEMU_PC_GL_TEXTURE_2D, 0, CRTEMU_PC_GL_RGBA, width, height, 0, CRTEMU_PC_GL_RGBA, CRTEMU_PC_GL_UNSIGNED_BYTE, pixels_xbgr ); 978 | crtemu_pc->BindTexture( CRTEMU_PC_GL_TEXTURE_2D, 0 ); 979 | 980 | crtemu_pc->Viewport( 0, 0, width, height ); 981 | 982 | // Blur the previous accumulation buffer 983 | crtemu_pc_internal_blur( crtemu_pc, crtemu_pc->accumulatetexture_b, crtemu_pc->blurbuffer_a, crtemu_pc->blurbuffer_b, crtemu_pc->blurtexture_b, 1.0f, width, height ); 984 | 985 | // Update accumulation buffer 986 | crtemu_pc->BindFramebuffer( CRTEMU_PC_GL_FRAMEBUFFER, crtemu_pc->accumulatebuffer_a ); 987 | crtemu_pc->UseProgram( crtemu_pc->accumulate_shader ); 988 | crtemu_pc->Uniform1i( crtemu_pc->GetUniformLocation( crtemu_pc->accumulate_shader, "tex0" ), 0 ); 989 | crtemu_pc->Uniform1i( crtemu_pc->GetUniformLocation( crtemu_pc->accumulate_shader, "tex1" ), 1 ); 990 | crtemu_pc->Uniform1f( crtemu_pc->GetUniformLocation( crtemu_pc->accumulate_shader, "modulate" ), 1.0f ); 991 | crtemu_pc->ActiveTexture( CRTEMU_PC_GL_TEXTURE0 ); 992 | crtemu_pc->BindTexture( CRTEMU_PC_GL_TEXTURE_2D, crtemu_pc->backbuffer ); 993 | crtemu_pc->TexParameteri( CRTEMU_PC_GL_TEXTURE_2D, CRTEMU_PC_GL_TEXTURE_MIN_FILTER, CRTEMU_PC_GL_LINEAR ); 994 | crtemu_pc->TexParameteri( CRTEMU_PC_GL_TEXTURE_2D, CRTEMU_PC_GL_TEXTURE_MAG_FILTER, CRTEMU_PC_GL_LINEAR ); 995 | crtemu_pc->ActiveTexture( CRTEMU_PC_GL_TEXTURE1 ); 996 | crtemu_pc->BindTexture( CRTEMU_PC_GL_TEXTURE_2D, crtemu_pc->blurtexture_a ); 997 | crtemu_pc->TexParameteri( CRTEMU_PC_GL_TEXTURE_2D, CRTEMU_PC_GL_TEXTURE_MIN_FILTER, CRTEMU_PC_GL_LINEAR ); 998 | crtemu_pc->TexParameteri( CRTEMU_PC_GL_TEXTURE_2D, CRTEMU_PC_GL_TEXTURE_MAG_FILTER, CRTEMU_PC_GL_LINEAR ); 999 | crtemu_pc->DrawArrays( CRTEMU_PC_GL_TRIANGLE_FAN, 0, 4 ); 1000 | crtemu_pc->ActiveTexture( CRTEMU_PC_GL_TEXTURE0 ); 1001 | crtemu_pc->BindTexture( CRTEMU_PC_GL_TEXTURE_2D, 0 ); 1002 | crtemu_pc->ActiveTexture( CRTEMU_PC_GL_TEXTURE1 ); 1003 | crtemu_pc->BindTexture( CRTEMU_PC_GL_TEXTURE_2D, 0 ); 1004 | crtemu_pc->BindFramebuffer( CRTEMU_PC_GL_FRAMEBUFFER, 0 ); 1005 | 1006 | 1007 | // Store a copy of the accumulation buffer 1008 | crtemu_pc->BindFramebuffer( CRTEMU_PC_GL_FRAMEBUFFER, crtemu_pc->accumulatebuffer_b ); 1009 | crtemu_pc->UseProgram( crtemu_pc->copy_shader ); 1010 | crtemu_pc->Uniform1i( crtemu_pc->GetUniformLocation( crtemu_pc->copy_shader, "tex0" ), 0 ); 1011 | crtemu_pc->ActiveTexture( CRTEMU_PC_GL_TEXTURE0 ); 1012 | crtemu_pc->BindTexture( CRTEMU_PC_GL_TEXTURE_2D, crtemu_pc->accumulatetexture_a ); 1013 | crtemu_pc->TexParameteri( CRTEMU_PC_GL_TEXTURE_2D, CRTEMU_PC_GL_TEXTURE_MIN_FILTER, CRTEMU_PC_GL_LINEAR ); 1014 | crtemu_pc->TexParameteri( CRTEMU_PC_GL_TEXTURE_2D, CRTEMU_PC_GL_TEXTURE_MAG_FILTER, CRTEMU_PC_GL_LINEAR ); 1015 | crtemu_pc->DrawArrays( CRTEMU_PC_GL_TRIANGLE_FAN, 0, 4 ); 1016 | crtemu_pc->ActiveTexture( CRTEMU_PC_GL_TEXTURE0 ); 1017 | crtemu_pc->BindTexture( CRTEMU_PC_GL_TEXTURE_2D, 0 ); 1018 | crtemu_pc->BindFramebuffer( CRTEMU_PC_GL_FRAMEBUFFER, 0 ); 1019 | 1020 | // Blend accumulation and backbuffer 1021 | crtemu_pc->BindFramebuffer( CRTEMU_PC_GL_FRAMEBUFFER, crtemu_pc->accumulatebuffer_a ); 1022 | crtemu_pc->UseProgram( crtemu_pc->blend_shader ); 1023 | crtemu_pc->Uniform1i( crtemu_pc->GetUniformLocation( crtemu_pc->blend_shader, "tex0" ), 0 ); 1024 | crtemu_pc->Uniform1i( crtemu_pc->GetUniformLocation( crtemu_pc->blend_shader, "tex1" ), 1 ); 1025 | crtemu_pc->Uniform1f( crtemu_pc->GetUniformLocation( crtemu_pc->blend_shader, "modulate" ), 1.0f ); 1026 | crtemu_pc->ActiveTexture( CRTEMU_PC_GL_TEXTURE0 ); 1027 | crtemu_pc->BindTexture( CRTEMU_PC_GL_TEXTURE_2D, crtemu_pc->backbuffer ); 1028 | crtemu_pc->ActiveTexture( CRTEMU_PC_GL_TEXTURE1 ); 1029 | crtemu_pc->BindTexture( CRTEMU_PC_GL_TEXTURE_2D, crtemu_pc->accumulatetexture_b ); 1030 | crtemu_pc->DrawArrays( CRTEMU_PC_GL_TRIANGLE_FAN, 0, 4 ); 1031 | crtemu_pc->ActiveTexture( CRTEMU_PC_GL_TEXTURE0 ); 1032 | crtemu_pc->BindTexture( CRTEMU_PC_GL_TEXTURE_2D, 0 ); 1033 | crtemu_pc->ActiveTexture( CRTEMU_PC_GL_TEXTURE1 ); 1034 | crtemu_pc->BindTexture( CRTEMU_PC_GL_TEXTURE_2D, 0 ); 1035 | crtemu_pc->BindFramebuffer( CRTEMU_PC_GL_FRAMEBUFFER, 0 ); 1036 | 1037 | 1038 | // Add slight blur to backbuffer 1039 | crtemu_pc_internal_blur( crtemu_pc, crtemu_pc->accumulatetexture_a, crtemu_pc->accumulatebuffer_a, crtemu_pc->blurbuffer_b, crtemu_pc->blurtexture_b, 0.17f, width, height ); 1040 | 1041 | // Create fully blurred version of backbuffer 1042 | crtemu_pc_internal_blur( crtemu_pc, crtemu_pc->accumulatetexture_a, crtemu_pc->blurbuffer_a, crtemu_pc->blurbuffer_b, crtemu_pc->blurtexture_b, 1.0f, width, height ); 1043 | 1044 | 1045 | // Present to screen with CRT shader 1046 | crtemu_pc->BindFramebuffer( CRTEMU_PC_GL_FRAMEBUFFER, 0 ); 1047 | 1048 | crtemu_pc->Viewport( viewport[ 0 ], viewport[ 1 ], viewport[ 2 ], viewport[ 3 ] ); 1049 | 1050 | int window_width = viewport[ 2 ] - viewport[ 0 ]; 1051 | int window_height = viewport[ 3 ] - viewport[ 1 ]; 1052 | 1053 | window_width = (int)( window_width / 1.2f ); 1054 | 1055 | float hscale = window_width / (float) width; 1056 | float vscale = window_height / ( (float) height * 1.1f ); 1057 | float pixel_scale = hscale < vscale ? hscale : vscale; 1058 | 1059 | float hborder = ( window_width - pixel_scale * width ) / 2.0f; 1060 | float vborder = ( window_height - pixel_scale * height * 1.1f ) / 2.0f; 1061 | float x1 = hborder; 1062 | float y1 = vborder; 1063 | float x2 = x1 + pixel_scale * width; 1064 | float y2 = y1 + pixel_scale * height * 1.1f; 1065 | 1066 | x1 = ( x1 / window_width ) * 2.0f - 1.0f; 1067 | x2 = ( x2 / window_width ) * 2.0f - 1.0f; 1068 | y1 = ( y1 / window_height ) * 2.0f - 1.0f; 1069 | y2 = ( y2 / window_height ) * 2.0f - 1.0f; 1070 | 1071 | CRTEMU_PC_GLfloat screen_vertices[] = 1072 | { 1073 | 0.0f, 0.0f, 0.0f, 0.0f, 1074 | 0.0f, 0.0f, 1.0f, 0.0f, 1075 | 0.0f, 0.0f, 1.0f, 1.0f, 1076 | 0.0f, 0.0f, 0.0f, 1.0f, 1077 | }; 1078 | screen_vertices[ 0 ] = x1; 1079 | screen_vertices[ 1 ] = y1; 1080 | screen_vertices[ 4 ] = x2; 1081 | screen_vertices[ 5 ] = y1; 1082 | screen_vertices[ 8 ] = x2; 1083 | screen_vertices[ 9 ] = y2; 1084 | screen_vertices[ 12 ] = x1; 1085 | screen_vertices[ 13 ] = y2; 1086 | 1087 | crtemu_pc->BufferData( CRTEMU_PC_GL_ARRAY_BUFFER, 4 * 4 * sizeof( CRTEMU_PC_GLfloat ), screen_vertices, CRTEMU_PC_GL_STATIC_DRAW ); 1088 | crtemu_pc->BindBuffer( CRTEMU_PC_GL_ARRAY_BUFFER, crtemu_pc->vertexbuffer ); 1089 | 1090 | float r = ( ( border_xbgr >> 16 ) & 0xff ) / 255.0f; 1091 | float g = ( ( border_xbgr >> 8 ) & 0xff ) / 255.0f; 1092 | float b = ( ( border_xbgr ) & 0xff ) / 255.0f; 1093 | crtemu_pc->ClearColor( r, g, b, 1.0f ); 1094 | crtemu_pc->Clear( CRTEMU_PC_GL_COLOR_BUFFER_BIT ); 1095 | 1096 | crtemu_pc->UseProgram( crtemu_pc->crt_shader ); 1097 | 1098 | crtemu_pc->Uniform1i( crtemu_pc->GetUniformLocation( crtemu_pc->crt_shader, "backbuffer" ), 0 ); 1099 | crtemu_pc->Uniform1i( crtemu_pc->GetUniformLocation( crtemu_pc->crt_shader, "blurbuffer" ), 1 ); 1100 | crtemu_pc->Uniform1i( crtemu_pc->GetUniformLocation( crtemu_pc->crt_shader, "frametexture" ), 2 ); 1101 | crtemu_pc->Uniform1f( crtemu_pc->GetUniformLocation( crtemu_pc->crt_shader, "use_frame" ), crtemu_pc->use_frame ); 1102 | crtemu_pc->Uniform1f( crtemu_pc->GetUniformLocation( crtemu_pc->crt_shader, "time" ), 1.5f * (CRTEMU_PC_GLfloat)( ( (double) time_us ) / 1000000.0 ) ); 1103 | crtemu_pc->Uniform2f( crtemu_pc->GetUniformLocation( crtemu_pc->crt_shader, "resolution" ), (float) window_width, (float) window_height ); 1104 | 1105 | float mod_r = ( ( mod_xbgr >> 16 ) & 0xff ) / 255.0f; 1106 | float mod_g = ( ( mod_xbgr >> 8 ) & 0xff ) / 255.0f; 1107 | float mod_b = ( ( mod_xbgr ) & 0xff ) / 255.0f; 1108 | crtemu_pc->Uniform3f( crtemu_pc->GetUniformLocation( crtemu_pc->crt_shader, "modulate" ), mod_r, mod_g, mod_b ); 1109 | 1110 | crtemu_pc->Uniform1f( crtemu_pc->GetUniformLocation( crtemu_pc->crt_shader, "cfg_curvature" ), crtemu_pc->config.curvature ); 1111 | crtemu_pc->Uniform1f( crtemu_pc->GetUniformLocation( crtemu_pc->crt_shader, "cfg_scanlines" ), crtemu_pc->config.scanlines ); 1112 | crtemu_pc->Uniform1f( crtemu_pc->GetUniformLocation( crtemu_pc->crt_shader, "cfg_shadow_mask" ), crtemu_pc->config.shadow_mask ); 1113 | crtemu_pc->Uniform1f( crtemu_pc->GetUniformLocation( crtemu_pc->crt_shader, "cfg_separation" ), crtemu_pc->config.separation ); 1114 | crtemu_pc->Uniform1f( crtemu_pc->GetUniformLocation( crtemu_pc->crt_shader, "cfg_ghosting" ), crtemu_pc->config.ghosting ); 1115 | crtemu_pc->Uniform1f( crtemu_pc->GetUniformLocation( crtemu_pc->crt_shader, "cfg_noise" ), crtemu_pc->config.noise ); 1116 | crtemu_pc->Uniform1f( crtemu_pc->GetUniformLocation( crtemu_pc->crt_shader, "cfg_flicker" ), crtemu_pc->config.flicker ); 1117 | crtemu_pc->Uniform1f( crtemu_pc->GetUniformLocation( crtemu_pc->crt_shader, "cfg_vignette" ), crtemu_pc->config.vignette ); 1118 | crtemu_pc->Uniform1f( crtemu_pc->GetUniformLocation( crtemu_pc->crt_shader, "cfg_distortion" ), crtemu_pc->config.distortion ); 1119 | crtemu_pc->Uniform1f( crtemu_pc->GetUniformLocation( crtemu_pc->crt_shader, "cfg_aspect_lock" ), crtemu_pc->config.aspect_lock ); 1120 | crtemu_pc->Uniform1f( crtemu_pc->GetUniformLocation( crtemu_pc->crt_shader, "cfg_hpos" ), crtemu_pc->config.hpos ); 1121 | crtemu_pc->Uniform1f( crtemu_pc->GetUniformLocation( crtemu_pc->crt_shader, "cfg_vpos" ), crtemu_pc->config.vpos ); 1122 | crtemu_pc->Uniform1f( crtemu_pc->GetUniformLocation( crtemu_pc->crt_shader, "cfg_hsize" ), crtemu_pc->config.hsize ); 1123 | crtemu_pc->Uniform1f( crtemu_pc->GetUniformLocation( crtemu_pc->crt_shader, "cfg_vsize" ), crtemu_pc->config.vsize ); 1124 | crtemu_pc->Uniform1f( crtemu_pc->GetUniformLocation( crtemu_pc->crt_shader, "cfg_contrast" ), crtemu_pc->config.contrast ); 1125 | crtemu_pc->Uniform1f( crtemu_pc->GetUniformLocation( crtemu_pc->crt_shader, "cfg_brightness" ), crtemu_pc->config.brightness ); 1126 | crtemu_pc->Uniform1f( crtemu_pc->GetUniformLocation( crtemu_pc->crt_shader, "cfg_saturation" ), crtemu_pc->config.saturation ); 1127 | crtemu_pc->Uniform1f( crtemu_pc->GetUniformLocation( crtemu_pc->crt_shader, "cfg_degauss" ), crtemu_pc->config.degauss ); 1128 | 1129 | float color[] = { 0.0f, 0.0f, 0.0f, 0.0f }; 1130 | 1131 | crtemu_pc->ActiveTexture( CRTEMU_PC_GL_TEXTURE0 ); 1132 | crtemu_pc->BindTexture( CRTEMU_PC_GL_TEXTURE_2D, crtemu_pc->accumulatetexture_a ); 1133 | crtemu_pc->TexParameteri( CRTEMU_PC_GL_TEXTURE_2D, CRTEMU_PC_GL_TEXTURE_MIN_FILTER, CRTEMU_PC_GL_LINEAR ); 1134 | crtemu_pc->TexParameteri( CRTEMU_PC_GL_TEXTURE_2D, CRTEMU_PC_GL_TEXTURE_MAG_FILTER, CRTEMU_PC_GL_LINEAR ); 1135 | crtemu_pc->TexParameteri( CRTEMU_PC_GL_TEXTURE_2D, CRTEMU_PC_GL_TEXTURE_WRAP_S, CRTEMU_PC_GL_CLAMP_TO_BORDER ); 1136 | crtemu_pc->TexParameteri( CRTEMU_PC_GL_TEXTURE_2D, CRTEMU_PC_GL_TEXTURE_WRAP_T, CRTEMU_PC_GL_CLAMP_TO_BORDER ); 1137 | crtemu_pc->TexParameterfv( CRTEMU_PC_GL_TEXTURE_2D, CRTEMU_PC_GL_TEXTURE_BORDER_COLOR, color ); 1138 | 1139 | crtemu_pc->ActiveTexture( CRTEMU_PC_GL_TEXTURE1 ); 1140 | crtemu_pc->BindTexture( CRTEMU_PC_GL_TEXTURE_2D, crtemu_pc->blurtexture_a ); 1141 | crtemu_pc->TexParameteri( CRTEMU_PC_GL_TEXTURE_2D, CRTEMU_PC_GL_TEXTURE_MIN_FILTER, CRTEMU_PC_GL_LINEAR ); 1142 | crtemu_pc->TexParameteri( CRTEMU_PC_GL_TEXTURE_2D, CRTEMU_PC_GL_TEXTURE_MAG_FILTER, CRTEMU_PC_GL_LINEAR ); 1143 | crtemu_pc->TexParameteri( CRTEMU_PC_GL_TEXTURE_2D, CRTEMU_PC_GL_TEXTURE_WRAP_S, CRTEMU_PC_GL_CLAMP_TO_BORDER ); 1144 | crtemu_pc->TexParameteri( CRTEMU_PC_GL_TEXTURE_2D, CRTEMU_PC_GL_TEXTURE_WRAP_T, CRTEMU_PC_GL_CLAMP_TO_BORDER ); 1145 | crtemu_pc->TexParameterfv( CRTEMU_PC_GL_TEXTURE_2D, CRTEMU_PC_GL_TEXTURE_BORDER_COLOR, color ); 1146 | 1147 | crtemu_pc->ActiveTexture( CRTEMU_PC_GL_TEXTURE3 ); 1148 | crtemu_pc->BindTexture( CRTEMU_PC_GL_TEXTURE_2D, crtemu_pc->frametexture ); 1149 | crtemu_pc->TexParameteri( CRTEMU_PC_GL_TEXTURE_2D, CRTEMU_PC_GL_TEXTURE_MIN_FILTER, CRTEMU_PC_GL_LINEAR ); 1150 | crtemu_pc->TexParameteri( CRTEMU_PC_GL_TEXTURE_2D, CRTEMU_PC_GL_TEXTURE_MAG_FILTER, CRTEMU_PC_GL_LINEAR ); 1151 | crtemu_pc->TexParameteri( CRTEMU_PC_GL_TEXTURE_2D, CRTEMU_PC_GL_TEXTURE_WRAP_S, CRTEMU_PC_GL_CLAMP_TO_BORDER ); 1152 | crtemu_pc->TexParameteri( CRTEMU_PC_GL_TEXTURE_2D, CRTEMU_PC_GL_TEXTURE_WRAP_T, CRTEMU_PC_GL_CLAMP_TO_BORDER ); 1153 | crtemu_pc->TexParameterfv( CRTEMU_PC_GL_TEXTURE_2D, CRTEMU_PC_GL_TEXTURE_BORDER_COLOR, color ); 1154 | 1155 | crtemu_pc->DrawArrays( CRTEMU_PC_GL_TRIANGLE_FAN, 0, 4 ); 1156 | 1157 | crtemu_pc->ActiveTexture( CRTEMU_PC_GL_TEXTURE0 ); 1158 | crtemu_pc->BindTexture( CRTEMU_PC_GL_TEXTURE_2D, 0 ); 1159 | crtemu_pc->BindTexture( CRTEMU_PC_GL_TEXTURE_2D, 1 ); 1160 | crtemu_pc->BindTexture( CRTEMU_PC_GL_TEXTURE_2D, 2 ); 1161 | crtemu_pc->BindFramebuffer( CRTEMU_PC_GL_FRAMEBUFFER, 0 ); 1162 | } 1163 | 1164 | 1165 | void crtemu_pc_coordinates_window_to_bitmap( crtemu_pc_t* crtemu_pc, int width, int height, int* x, int* y ) 1166 | { 1167 | CRTEMU_PC_GLint viewport[ 4 ]; 1168 | crtemu_pc->GetIntegerv( CRTEMU_PC_GL_VIEWPORT, viewport ); 1169 | 1170 | int window_width = viewport[ 2 ] - viewport[ 0 ]; 1171 | int window_height = viewport[ 3 ] - viewport[ 1 ]; 1172 | 1173 | float hscale = window_width / (float) width; 1174 | float vscale = window_height / (float) height; 1175 | float pixel_scale = hscale < vscale ? hscale : vscale; 1176 | 1177 | float hborder = ( window_width - pixel_scale * width ) / 2.0f; 1178 | float vborder = ( window_height - pixel_scale * height ) / 2.0f; 1179 | 1180 | float xp = ( ( *x - hborder ) / pixel_scale ) / (float) width; 1181 | float yp = ( ( *y - vborder ) / pixel_scale ) / (float) height; 1182 | 1183 | /* TODO: Common params for shader and this */ 1184 | float xc = ( xp - 0.5f ) * 2.0f; 1185 | float yc = ( yp - 0.5f ) * 2.0f; 1186 | xc *= 1.1f; 1187 | yc *= 1.1f; 1188 | //xc *= 1.0f + powf( ( fabsf( yc ) / 5.0f ), 2.0f); 1189 | //yc *= 1.0f + powf( ( fabsf( xc ) / 4.0f ), 2.0f); 1190 | float yt = ( yc >= 0.0f ? yc : -yc ) / 5.0f; 1191 | float xt = ( xc >= 0.0f ? xc : -xc ) / 4.0f; 1192 | xc *= 1.0f + ( yt * yt ); 1193 | yc *= 1.0f + ( xt * xt ); 1194 | xc = ( xc / 2.0f ) + 0.5f; 1195 | yc = ( yc / 2.0f ) + 0.5f; 1196 | xc = xc * 0.92f + 0.04f; 1197 | yc = yc * 0.92f + 0.04f; 1198 | xp = xc * 0.6f + xp * 0.4f; 1199 | yp = yc * 0.6f + yp * 0.4f; 1200 | 1201 | xp = xp * ( 1.0f - 0.04f ) + 0.04f / 2.0f + 0.003f; 1202 | yp = yp * ( 1.0f - 0.04f ) + 0.04f / 2.0f - 0.001f; 1203 | 1204 | xp = xp * 1.025f - 0.0125f; 1205 | yp = yp * 0.92f + 0.04f; 1206 | 1207 | xp *= width; 1208 | yp *= height; 1209 | 1210 | *x = (int) ( xp ); 1211 | *y = (int) ( yp ); 1212 | } 1213 | 1214 | 1215 | void crtemu_pc_coordinates_bitmap_to_window( crtemu_pc_t* crtemu_pc, int width, int height, int* x, int* y ) 1216 | { 1217 | (void) crtemu_pc, (void) width, (void) height, (void) x, (void) y; // TODO: implement 1218 | } 1219 | 1220 | 1221 | #endif /* CRTEMU_PC_IMPLEMENTATION */ 1222 | 1223 | /* 1224 | ------------------------------------------------------------------------------ 1225 | 1226 | This software is available under 2 licenses - you may choose the one you like. 1227 | 1228 | ------------------------------------------------------------------------------ 1229 | 1230 | ALTERNATIVE A - MIT License 1231 | 1232 | Copyright (c) 2016 Mattias Gustavsson 1233 | 1234 | Permission is hereby granted, free of charge, to any person obtaining a copy of 1235 | this software and associated documentation files (the "Software"), to deal in 1236 | the Software without restriction, including without limitation the rights to 1237 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 1238 | of the Software, and to permit persons to whom the Software is furnished to do 1239 | so, subject to the following conditions: 1240 | 1241 | The above copyright notice and this permission notice shall be included in all 1242 | copies or substantial portions of the Software. 1243 | 1244 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 1245 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 1246 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 1247 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 1248 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 1249 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 1250 | SOFTWARE. 1251 | 1252 | ------------------------------------------------------------------------------ 1253 | 1254 | ALTERNATIVE B - Public Domain (www.unlicense.org) 1255 | 1256 | This is free and unencumbered software released into the public domain. 1257 | 1258 | Anyone is free to copy, modify, publish, use, compile, sell, or distribute this 1259 | software, either in source code form or as a compiled binary, for any purpose, 1260 | commercial or non-commercial, and by any means. 1261 | 1262 | In jurisdictions that recognize copyright laws, the author or authors of this 1263 | software dedicate any and all copyright interest in the software to the public 1264 | domain. We make this dedication for the benefit of the public at large and to 1265 | the detriment of our heirs and successors. We intend this dedication to be an 1266 | overt act of relinquishment in perpetuity of all present and future rights to 1267 | this software under copyright law. 1268 | 1269 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 1270 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 1271 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 1272 | AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 1273 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 1274 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 1275 | 1276 | ------------------------------------------------------------------------------ 1277 | */ 1278 | -------------------------------------------------------------------------------- /source/frametimer.h: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------------------------------------------ 3 | Licensing information can be found at the end of the file. 4 | ------------------------------------------------------------------------------ 5 | 6 | frametimer.h - v0.1 - Framerate timer functionality. 7 | 8 | Do this: 9 | #define FRAMETIMER_IMPLEMENTATION 10 | before you include this file in *one* C/C++ file to create the implementation. 11 | */ 12 | 13 | #ifndef frametimer_h 14 | #define frametimer_h 15 | 16 | typedef struct frametimer_t frametimer_t; 17 | 18 | frametimer_t* frametimer_create( void* memxtx ); 19 | 20 | void frametimer_destroy( frametimer_t* frametimer ); 21 | 22 | void frametimer_lock_rate( frametimer_t* frametimer, int fps ); 23 | 24 | float frametimer_update( frametimer_t* frametimer ); 25 | 26 | float frametimer_delta_time( frametimer_t* frametimer ); 27 | 28 | int frametimer_frame_counter( frametimer_t* frametimer ); 29 | 30 | #endif /* frametimer_h */ 31 | 32 | /* 33 | ---------------------- 34 | IMPLEMENTATION 35 | ---------------------- 36 | */ 37 | 38 | #ifdef FRAMETIMER_IMPLEMENTATION 39 | #undef FRAMETIMER_IMPLEMENTATION 40 | 41 | #define _CRT_NONSTDC_NO_DEPRECATE 42 | #ifndef _CRT_SECURE_NO_WARNINGS 43 | #define _CRT_SECURE_NO_WARNINGS 44 | #endif 45 | #ifdef _WIN32 46 | #if !defined( _WIN32_WINNT ) || _WIN32_WINNT < 0x0501 47 | #undef _WIN32_WINNT 48 | #define _WIN32_WINNT 0x0501 // requires Windows XP minimum 49 | #endif 50 | // 0x0400=Windows NT 4.0, 0x0500=Windows 2000, 0x0501=Windows XP, 0x0502=Windows Server 2003, 0x0600=Windows Vista, 51 | // 0x0601=Windows 7, 0x0602=Windows 8, 0x0603=Windows 8.1, 0x0A00=Windows 10, 52 | #define _WINSOCKAPI_ 53 | #pragma warning( push ) 54 | #pragma warning( disable: 4619 ) 55 | #pragma warning( disable: 4668 ) // 'symbol' is not defined as a preprocessor macro, replacing with '0' for 'directives' 56 | #pragma warning( disable: 4768 ) // __declspec attributes before linkage specification are ignored 57 | #pragma warning( disable: 4255 ) // 'function' : no function prototype given: converting '()' to '(void)' 58 | #include 59 | #pragma warning( pop ) 60 | #ifndef __TINYC__ 61 | #pragma comment(lib, "winmm.lib") 62 | #endif 63 | #elif defined( __APPLE__ ) 64 | #include 65 | #else 66 | #include 67 | #endif 68 | 69 | #ifndef FRAMETIMER_MALLOC 70 | #include 71 | #if defined(__cplusplus) 72 | #define FRAMETIMER_MALLOC( ctx, size ) ( ::malloc( size ) ) 73 | #define FRAMETIMER_FREE( ctx, ptr ) ( ::free( ptr ) ) 74 | #else 75 | #define FRAMETIMER_MALLOC( ctx, size ) ( malloc( size ) ) 76 | #define FRAMETIMER_FREE( ctx, ptr ) ( free( ptr ) ) 77 | #endif 78 | #endif 79 | 80 | #ifndef FRAMETIMER_U64 81 | #define FRAMETIMER_U64 unsigned long long 82 | #endif 83 | 84 | 85 | struct frametimer_t 86 | { 87 | FRAMETIMER_U64 clock_freq; 88 | FRAMETIMER_U64 prev_clock; 89 | void* memctx; 90 | float delta_time; 91 | int initialized; 92 | int frame_counter; 93 | int frame_rate_lock; 94 | #ifdef _WIN32 95 | HANDLE waitable_timer; 96 | #endif 97 | }; 98 | 99 | 100 | frametimer_t* frametimer_create( void* memctx ) 101 | { 102 | frametimer_t* frametimer = (frametimer_t*) FRAMETIMER_MALLOC( memctx, sizeof( frametimer_t ) ); 103 | #ifdef _WIN32 104 | #ifndef __TINYC__ 105 | TIMECAPS tc; 106 | if( timeGetDevCaps( &tc, sizeof( TIMECAPS ) ) == TIMERR_NOERROR ) 107 | timeBeginPeriod( tc.wPeriodMin ); 108 | #endif 109 | frametimer->waitable_timer = CreateWaitableTimer(NULL, TRUE, NULL); 110 | #endif 111 | 112 | frametimer->memctx = memctx; 113 | frametimer->initialized = 0; 114 | 115 | #ifdef _WIN32 116 | LARGE_INTEGER f; 117 | QueryPerformanceFrequency( &f ); 118 | frametimer->clock_freq = (FRAMETIMER_U64) f.QuadPart; 119 | #else 120 | frametimer->clock_freq = 1000000000ull; 121 | #endif 122 | 123 | frametimer->prev_clock = 0; 124 | frametimer->delta_time = 0.0f; 125 | frametimer->frame_counter = 0; 126 | frametimer->frame_rate_lock = 0; 127 | return frametimer; 128 | } 129 | 130 | 131 | void frametimer_destroy( frametimer_t* frametimer ) 132 | { 133 | #ifdef _WIN32 134 | CloseHandle( frametimer->waitable_timer ); 135 | #ifndef __TINYC__ 136 | TIMECAPS tc; 137 | if( timeGetDevCaps( &tc, sizeof( TIMECAPS ) ) == TIMERR_NOERROR ) 138 | timeEndPeriod( tc.wPeriodMin ); 139 | #endif 140 | #endif 141 | FRAMETIMER_FREE( frametimer->memctx, frametimer ); 142 | } 143 | 144 | 145 | void frametimer_lock_rate( frametimer_t* frametimer, int fps ) 146 | { 147 | frametimer->frame_rate_lock = ( fps > 0 && fps < 5 ) ? 5 : fps < 0 ? 0 : fps; 148 | } 149 | 150 | 151 | float frametimer_update( frametimer_t* frametimer ) 152 | { 153 | if( !frametimer->initialized ) 154 | { 155 | #ifdef _WIN32 156 | LARGE_INTEGER c; 157 | QueryPerformanceCounter( &c ); 158 | frametimer->prev_clock = (FRAMETIMER_U64) c.QuadPart; 159 | #elif __APPLE__ 160 | frametimer->prev_clock = clock_gettime_nsec_np( CLOCK_UPTIME_RAW ); 161 | #else 162 | struct timespec t; 163 | clock_gettime( CLOCK_MONOTONIC_RAW, &t ); 164 | frametimer->prev_clock = (FRAMETIMER_U64)t.tv_sec; 165 | frametimer->prev_clock *= 1000000000ull; 166 | frametimer->prev_clock += (FRAMETIMER_U64)t.tv_nsec; 167 | #endif 168 | frametimer->initialized = 1; 169 | } 170 | 171 | ++frametimer->frame_counter; 172 | 173 | 174 | FRAMETIMER_U64 curr_clock = 0ULL; 175 | #ifdef _WIN32 176 | LARGE_INTEGER c; 177 | QueryPerformanceCounter( &c ); 178 | curr_clock = (FRAMETIMER_U64) c.QuadPart; 179 | #elif __APPLE__ 180 | curr_clock = (FRAMETIMER_U64) clock_gettime_nsec_np( CLOCK_UPTIME_RAW ); 181 | #else 182 | struct timespec t; 183 | clock_gettime( CLOCK_MONOTONIC_RAW, &t ); 184 | curr_clock = (FRAMETIMER_U64)t.tv_sec; 185 | curr_clock *= 1000000000ull; 186 | curr_clock += (FRAMETIMER_U64)t.tv_nsec; 187 | #endif 188 | 189 | if( frametimer->frame_rate_lock > 0 ) 190 | { 191 | FRAMETIMER_U64 delta = 0ULL; 192 | if( curr_clock > frametimer->prev_clock ) 193 | delta = curr_clock - frametimer->prev_clock - 1ULL; 194 | if( delta < frametimer->clock_freq / frametimer->frame_rate_lock ) 195 | { 196 | FRAMETIMER_U64 wait = ( frametimer->clock_freq / frametimer->frame_rate_lock ) - delta; 197 | #ifdef _WIN32 198 | if( wait > 0 ) 199 | { 200 | LARGE_INTEGER due_time; 201 | due_time.QuadPart = - (LONGLONG) ( ( 10000000ULL * wait ) / frametimer->clock_freq ) ; 202 | 203 | SetWaitableTimer( frametimer->waitable_timer, &due_time, 0, 0, 0, FALSE ); 204 | WaitForSingleObject( frametimer->waitable_timer, 200 ); // wait long enough for timer to trigger ( 200ms == 5fps ) 205 | CancelWaitableTimer( frametimer->waitable_timer ); // in case we timed out 206 | } 207 | #else 208 | struct timespec t = { 0, 0 }; 209 | t.tv_nsec = wait; 210 | while( t.tv_nsec > 0 ) 211 | { 212 | struct timespec r = { 0, 0 }; 213 | if( nanosleep( &t, &r ) >= 0 ) break; 214 | t = r; 215 | } 216 | #endif 217 | curr_clock += wait; 218 | } 219 | } 220 | 221 | FRAMETIMER_U64 delta_clock = 0ULL; 222 | if( curr_clock > frametimer->prev_clock ) delta_clock = curr_clock - frametimer->prev_clock; 223 | 224 | // Cap delta time if it is too high (running at less than 5 fps ) or things will jump 225 | // like crazy on occasional long stalls. 226 | if( delta_clock > frametimer->clock_freq / 5ULL ) delta_clock = frametimer->clock_freq / 5ULL; // Cap to 5 fps 227 | 228 | float delta_time = (float) ( ( (double) delta_clock ) / ( (double) frametimer->clock_freq ) ); 229 | 230 | frametimer->delta_time = delta_time; 231 | frametimer->prev_clock = curr_clock; 232 | 233 | return delta_time; 234 | } 235 | 236 | 237 | float frametimer_delta_time( frametimer_t* frametimer ) 238 | { 239 | return frametimer->delta_time; 240 | } 241 | 242 | 243 | int frametimer_frame_counter( frametimer_t* frametimer ) 244 | { 245 | return frametimer->frame_counter; 246 | } 247 | 248 | 249 | #endif /* FRAMETIMER_IMPLEMENTATION */ 250 | 251 | 252 | /* 253 | ------------------------------------------------------------------------------ 254 | 255 | This software is available under 2 licenses - you may choose the one you like. 256 | 257 | ------------------------------------------------------------------------------ 258 | 259 | ALTERNATIVE A - MIT License 260 | 261 | Copyright (c) 2015 Mattias Gustavsson 262 | 263 | Permission is hereby granted, free of charge, to any person obtaining a copy of 264 | this software and associated documentation files (the "Software"), to deal in 265 | the Software without restriction, including without limitation the rights to 266 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 267 | of the Software, and to permit persons to whom the Software is furnished to do 268 | so, subject to the following conditions: 269 | 270 | The above copyright notice and this permission notice shall be included in all 271 | copies or substantial portions of the Software. 272 | 273 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 274 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 275 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 276 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 277 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 278 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 279 | SOFTWARE. 280 | 281 | ------------------------------------------------------------------------------ 282 | 283 | ALTERNATIVE B - Public Domain (www.unlicense.org) 284 | 285 | This is free and unencumbered software released into the public domain. 286 | 287 | Anyone is free to copy, modify, publish, use, compile, sell, or distribute this 288 | software, either in source code form or as a compiled binary, for any purpose, 289 | commercial or non-commercial, and by any means. 290 | 291 | In jurisdictions that recognize copyright laws, the author or authors of this 292 | software dedicate any and all copyright interest in the software to the public 293 | domain. We make this dedication for the benefit of the public at large and to 294 | the detriment of our heirs and successors. We intend this dedication to be an 295 | overt act of relinquishment in perpetuity of all present and future rights to 296 | this software under copyright law. 297 | 298 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 299 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 300 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 301 | AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 302 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 303 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 304 | 305 | ------------------------------------------------------------------------------ 306 | */ 307 | -------------------------------------------------------------------------------- /source/gif_load.h: -------------------------------------------------------------------------------- 1 | #ifndef GIF_LOAD_H 2 | #define GIF_LOAD_H 3 | 4 | /** gif_load: A slim, fast and header-only GIF loader written in C. 5 | Original author: hidefromkgb (hidefromkgb@gmail.com) 6 | _________________________________________________________________________ 7 | 8 | This is free and unencumbered software released into the public domain. 9 | 10 | Anyone is free to copy, modify, publish, use, compile, sell, or 11 | distribute this software, either in source code form or as a compiled 12 | binary, for any purpose, commercial or non-commercial, and by any means. 13 | 14 | In jurisdictions that recognize copyright laws, the author or authors 15 | of this software dedicate any and all copyright interest in the 16 | software to the public domain. We make this dedication for the benefit 17 | of the public at large and to the detriment of our heirs and 18 | successors. We intend this dedication to be an overt act of 19 | relinquishment in perpetuity of all present and future rights to this 20 | software under copyright law. 21 | 22 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 23 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 24 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 25 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 26 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 27 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 28 | OTHER DEALINGS IN THE SOFTWARE. 29 | _________________________________________________________________________ 30 | **/ 31 | 32 | #ifdef __cplusplus 33 | extern "C" { 34 | #endif 35 | #include /** imports uint8_t, uint16_t and uint32_t **/ 36 | #ifndef GIF_MGET 37 | #include 38 | #define GIF_MGET(m,s,a,c) m = (uint8_t*)realloc((c)? 0 : m, (c)? s : 0UL); 39 | #endif 40 | #ifndef GIF_BIGE 41 | #define GIF_BIGE 0 42 | #endif 43 | #ifndef GIF_EXTR 44 | #define GIF_EXTR static 45 | #endif 46 | #define _GIF_SWAP(h) ((GIF_BIGE)? ((uint16_t)(h << 8) | (h >> 8)) : h) 47 | 48 | #pragma pack(push, 1) 49 | struct GIF_WHDR { /** ======== frame writer info: ======== **/ 50 | long xdim, ydim, clrs, /** global dimensions, palette size **/ 51 | bkgd, tran, /** background index, transparent index **/ 52 | intr, mode, /** interlace flag, frame blending mode **/ 53 | frxd, fryd, frxo, fryo, /** current frame dimensions and offset **/ 54 | time, ifrm, nfrm; /** delay, frame number, frame count **/ 55 | uint8_t *bptr; /** frame pixel indices or metadata **/ 56 | struct { /** [==== GIF RGB palette element: ====] **/ 57 | uint8_t R, G, B; /** [color values - red, green, blue ] **/ 58 | } *cpal; /** current palette **/ 59 | }; 60 | #pragma pack(pop) 61 | 62 | enum {GIF_NONE = 0, GIF_CURR = 1, GIF_BKGD = 2, GIF_PREV = 3}; 63 | 64 | /** [ internal function, do not use ] **/ 65 | static long _GIF_SkipChunk(uint8_t **buff, long size) { 66 | long skip; 67 | 68 | for (skip = 2, ++size, ++(*buff); ((size -= skip) > 0) && (skip > 1); 69 | *buff += (skip = 1 + **buff)); 70 | return size; 71 | } 72 | 73 | /** [ internal function, do not use ] **/ 74 | static long _GIF_LoadHeader(unsigned gflg, uint8_t **buff, void **rpal, 75 | unsigned fflg, long *size, long flen) { 76 | if (flen && (!(*buff += flen) || ((*size -= flen) <= 0))) 77 | return -2; /** v--[ 0x80: "palette is present" flag ]--, **/ 78 | if (flen && (fflg & 0x80)) { /** local palette has priority | **/ 79 | *rpal = *buff; /** [ 3L: 3 uint8_t color channels ]--, | **/ 80 | *buff += (flen = 2 << (fflg & 7)) * 3L; /** <--| | **/ 81 | return ((*size -= flen * 3L) > 0)? flen : -1; /** <--' | **/ 82 | } /** no local palette found, checking for the global one | **/ 83 | return (gflg & 0x80)? (2 << (gflg & 7)) : 0; /** <-----' **/ 84 | } 85 | 86 | /** [ internal function, do not use ] **/ 87 | static long _GIF_LoadFrame(uint8_t **buff, long *size, 88 | uint8_t *bptr, uint8_t *blen) { 89 | typedef uint16_t GIF_H; 90 | const long GIF_HLEN = sizeof(GIF_H), /** to rid the scope of sizeof **/ 91 | GIF_CLEN = 1 << 12; /** code table length: 4096 items **/ 92 | GIF_H accu, mask; /** bit accumulator / bit mask **/ 93 | long ctbl, iter, /** last code table index / index string iterator **/ 94 | prev, curr, /** codes from the stream: previous / current **/ 95 | ctsz, ccsz, /** code table bit sizes: min LZW / current **/ 96 | bseq, bszc; /** counters: block sequence / bit size **/ 97 | uint32_t *code = (uint32_t*)bptr - GIF_CLEN; /** code table pointer **/ 98 | 99 | /** preparing initial values **/ 100 | if ((--(*size) <= GIF_HLEN) || !*++(*buff)) 101 | return -4; /** unexpected end of the stream: insufficient size **/ 102 | mask = (GIF_H)((1 << (ccsz = (ctsz = *(*buff - 1)) + 1)) - 1); 103 | if ((ctsz < 2) || (ctsz > 8)) 104 | return -3; /** min LZW size is out of its nominal [2; 8] bounds **/ 105 | if ((ctbl = (1L << ctsz)) != (mask & _GIF_SWAP(*(GIF_H*)(*buff + 1)))) 106 | return -2; /** initial code is not equal to min LZW size **/ 107 | for (curr = ++ctbl; curr; code[--curr] = 0); /** actual color codes **/ 108 | 109 | /** getting codes from stream (--size makes up for end-of-stream mark) **/ 110 | for (--(*size), bszc = -ccsz, prev = curr = 0; 111 | ((*size -= (bseq = *(*buff)++) + 1) >= 0) && bseq; *buff += bseq) 112 | for (; bseq > 0; bseq -= GIF_HLEN, *buff += GIF_HLEN) 113 | for (accu = (GIF_H)(_GIF_SWAP(*(GIF_H*)*buff) 114 | & ((bseq < GIF_HLEN)? ((1U << (8 * bseq)) - 1U) : ~0U)), 115 | curr |= accu << (ccsz + bszc), accu = (GIF_H)(accu >> -bszc), 116 | bszc += 8 * ((bseq < GIF_HLEN)? bseq : GIF_HLEN); 117 | bszc >= 0; bszc -= ccsz, prev = curr, curr = accu, 118 | accu = (GIF_H)(accu >> ccsz)) 119 | if (((curr &= mask) & ~1L) == (1L << ctsz)) { 120 | if (~(ctbl = curr + 1) & 1) /** end-of-data code (ED). **/ 121 | /** -1: no end-of-stream mark after ED; 1: decoded **/ 122 | return (*((*buff += bseq + 1) - 1))? -1 : 1; 123 | mask = (GIF_H)((1 << (ccsz = ctsz + 1)) - 1); 124 | } /** ^- table drop code (TD). TD = 1 << ctsz, ED = TD + 1 **/ 125 | else { /** single-pixel (SP) or multi-pixel (MP) code. **/ 126 | if (ctbl < GIF_CLEN) { /** is the code table full? **/ 127 | if ((ctbl == mask) && (ctbl < GIF_CLEN - 1)) { 128 | mask = (GIF_H)(mask + mask + 1); 129 | ccsz++; /** yes; extending **/ 130 | } /** prev = TD? => curr < ctbl = prev **/ 131 | code[ctbl] = (uint32_t)prev + (code[prev] & 0xFFF000); 132 | } /** appending SP / MP decoded pixels to the frame **/ 133 | prev = (long)code[iter = (ctbl > curr)? curr : prev]; 134 | if ((bptr += (prev = (prev >> 12) & 0xFFF)) > blen) 135 | continue; /** skipping pixels above frame capacity **/ 136 | for (prev++; (iter &= 0xFFF) >> ctsz; 137 | *bptr-- = (uint8_t)((iter = (long)code[iter]) >> 24)); 138 | (bptr += prev)[-prev] = (uint8_t)iter; 139 | if (ctbl < GIF_CLEN) { /** appending the code table **/ 140 | if (ctbl == curr) 141 | *bptr++ = (uint8_t)iter; 142 | else if (ctbl < curr) 143 | return -5; /** wrong code in the stream **/ 144 | code[ctbl++] += ((uint32_t)iter << 24) + 0x1000; 145 | } 146 | } /** 0: no ED before end-of-stream mark; -4: see above **/ 147 | return (++(*size) >= 0)? 0 : -4; /** ^- N.B.: 0 error is recoverable **/ 148 | } 149 | 150 | /** _________________________________________________________________________ 151 | The main loading function. Returns the total number of frames if the data 152 | includes proper GIF ending, and otherwise it returns the number of frames 153 | loaded per current call, multiplied by -1. So, the data may be incomplete 154 | and in this case the function can be called again when more data arrives, 155 | just remember to keep SKIP up to date. 156 | _________________________________________________________________________ 157 | DATA: raw data chunk, may be partial 158 | SIZE: size of the data chunk that`s currently present 159 | GWFR: frame writer function, MANDATORY 160 | EAMF: metadata reader function, set to 0 if not needed 161 | ANIM: implementation-specific data (e.g. a structure or a pointer to it) 162 | SKIP: number of frames to skip before resuming 163 | **/ 164 | GIF_EXTR long GIF_Load(void *data, long size, 165 | void (*gwfr)(void*, struct GIF_WHDR*), 166 | void (*eamf)(void*, struct GIF_WHDR*), 167 | void *anim, long skip) { 168 | const long GIF_BLEN = (1 << 12) * sizeof(uint32_t); 169 | const uint8_t GIF_EHDM = 0x21, /** extension header mark **/ 170 | GIF_FHDM = 0x2C, /** frame header mark **/ 171 | GIF_EOFM = 0x3B, /** end-of-file mark **/ 172 | GIF_EGCM = 0xF9, /** extension: graphics control mark **/ 173 | GIF_EAMM = 0xFF; /** extension: app metadata mark **/ 174 | #pragma pack(push, 1) 175 | struct GIF_GHDR { /** ========== GLOBAL GIF HEADER: ========== **/ 176 | uint8_t head[6]; /** 'GIF87a' / 'GIF89a' header signature **/ 177 | uint16_t xdim, ydim; /** total image width, total image height **/ 178 | uint8_t flgs; /** FLAGS: 179 | GlobalPlt bit 7 1: global palette exists 180 | 0: local in each frame 181 | ClrRes bit 6-4 bits/channel = ClrRes+1 182 | [reserved] bit 3 0 183 | PixelBits bit 2-0 |Plt| = 2 * 2^PixelBits 184 | **/ 185 | uint8_t bkgd, aspr; /** background color index, aspect ratio **/ 186 | } *ghdr = (struct GIF_GHDR*)data; 187 | struct GIF_FHDR { /** ======= GIF FRAME MASTER HEADER: ======= **/ 188 | uint16_t frxo, fryo; /** offset of this frame in a "full" image **/ 189 | uint16_t frxd, fryd; /** frame width, frame height **/ 190 | uint8_t flgs; /** FLAGS: 191 | LocalPlt bit 7 1: local palette exists 192 | 0: global is used 193 | Interlaced bit 6 1: interlaced frame 194 | 0: non-interlaced frame 195 | Sorted bit 5 usually 0 196 | [reserved] bit 4-3 [undefined] 197 | PixelBits bit 2-0 |Plt| = 2 * 2^PixelBits 198 | **/ 199 | } *fhdr; 200 | struct GIF_EGCH { /** ==== [EXT] GRAPHICS CONTROL HEADER: ==== **/ 201 | uint8_t flgs; /** FLAGS: 202 | [reserved] bit 7-5 [undefined] 203 | BlendMode bit 4-2 000: not set; static GIF 204 | 001: leave result as is 205 | 010: restore background 206 | 011: restore previous 207 | 1--: [undefined] 208 | UserInput bit 1 1: show frame till input 209 | 0: default; ~99% of GIFs 210 | TransColor bit 0 1: got transparent color 211 | 0: frame is fully opaque 212 | **/ 213 | uint16_t time; /** delay in GIF time units; 1 unit = 10 ms **/ 214 | uint8_t tran; /** transparent color index **/ 215 | } *egch = 0; 216 | #pragma pack(pop) 217 | struct GIF_WHDR wtmp, whdr = {0}; 218 | long desc, blen; 219 | uint8_t *buff; 220 | 221 | /** checking if the stream is not empty and has a 'GIF8[79]a' signature, 222 | the data has sufficient size and frameskip value is non-negative **/ 223 | if (!ghdr || (size <= (long)sizeof(*ghdr)) || (*(buff = ghdr->head) != 71) 224 | || (buff[1] != 73) || (buff[2] != 70) || (buff[3] != 56) || (skip < 0) 225 | || ((buff[4] != 55) && (buff[4] != 57)) || (buff[5] != 97) || !gwfr) 226 | return 0; 227 | 228 | buff = (uint8_t*)(ghdr + 1) /** skipping the global header and palette **/ 229 | + _GIF_LoadHeader(ghdr->flgs, 0, 0, 0, 0, 0L) * 3L; 230 | if ((size -= buff - (uint8_t*)ghdr) <= 0) 231 | return 0; 232 | 233 | whdr.xdim = _GIF_SWAP(ghdr->xdim); 234 | whdr.ydim = _GIF_SWAP(ghdr->ydim); 235 | for (whdr.bptr = buff, whdr.bkgd = ghdr->bkgd, blen = --size; 236 | (blen >= 0) && ((desc = *whdr.bptr++) != GIF_EOFM); /** sic: '>= 0' **/ 237 | blen = _GIF_SkipChunk(&whdr.bptr, blen) - 1) /** count all frames **/ 238 | if (desc == GIF_FHDM) { 239 | fhdr = (struct GIF_FHDR*)whdr.bptr; 240 | if (_GIF_LoadHeader(ghdr->flgs, &whdr.bptr, (void**)&whdr.cpal, 241 | fhdr->flgs, &blen, sizeof(*fhdr)) <= 0) 242 | break; 243 | whdr.frxd = _GIF_SWAP(fhdr->frxd); 244 | whdr.fryd = _GIF_SWAP(fhdr->fryd); 245 | whdr.frxo = (whdr.frxd > whdr.frxo)? whdr.frxd : whdr.frxo; 246 | whdr.fryo = (whdr.fryd > whdr.fryo)? whdr.fryd : whdr.fryo; 247 | whdr.ifrm++; 248 | } 249 | blen = whdr.frxo * whdr.fryo * (long)sizeof(*whdr.bptr); 250 | GIF_MGET(whdr.bptr, (unsigned long)(blen + GIF_BLEN + 2), anim, 1) 251 | whdr.nfrm = (desc != GIF_EOFM)? -whdr.ifrm : whdr.ifrm; 252 | for (whdr.bptr += GIF_BLEN, whdr.ifrm = -1; blen /** load all frames **/ 253 | && (skip < ((whdr.nfrm < 0)? -whdr.nfrm : whdr.nfrm)) && (size >= 0); 254 | size = (desc != GIF_EOFM)? ((desc != GIF_FHDM) || (skip > whdr.ifrm))? 255 | _GIF_SkipChunk(&buff, size) - 1 : size - 1 : -1) 256 | if ((desc = *buff++) == GIF_FHDM) { /** found a frame **/ 257 | whdr.intr = !!((fhdr = (struct GIF_FHDR*)buff)->flgs & 0x40); 258 | *(void**)&whdr.cpal = (void*)(ghdr + 1); /** interlaced? -^ **/ 259 | whdr.clrs = _GIF_LoadHeader(ghdr->flgs, &buff, (void**)&whdr.cpal, 260 | fhdr->flgs, &size, sizeof(*fhdr)); 261 | if ((skip <= ++whdr.ifrm) && ((whdr.clrs <= 0) 262 | || (_GIF_LoadFrame(&buff, &size, 263 | whdr.bptr, whdr.bptr + blen) < 0))) 264 | size = -(whdr.ifrm--) - 1; /** failed to load the frame **/ 265 | else if (skip <= whdr.ifrm) { 266 | whdr.frxd = _GIF_SWAP(fhdr->frxd); 267 | whdr.fryd = _GIF_SWAP(fhdr->fryd); 268 | whdr.frxo = _GIF_SWAP(fhdr->frxo); 269 | whdr.fryo = _GIF_SWAP(fhdr->fryo); 270 | whdr.time = (egch)? _GIF_SWAP(egch->time) : 0; 271 | whdr.tran = (egch && (egch->flgs & 0x01))? egch->tran : -1; 272 | whdr.time = (egch && (egch->flgs & 0x02))? -whdr.time - 1 273 | : whdr.time; 274 | whdr.mode = (egch && !(egch->flgs & 0x10))? 275 | (egch->flgs & 0x0C) >> 2 : GIF_NONE; 276 | egch = 0; 277 | wtmp = whdr; 278 | gwfr(anim, &wtmp); /** passing the frame to the caller **/ 279 | } 280 | } 281 | else if (desc == GIF_EHDM) { /** found an extension **/ 282 | if (*buff == GIF_EGCM) /** graphics control ext. **/ 283 | egch = (struct GIF_EGCH*)(buff + 1 + 1); 284 | else if ((*buff == GIF_EAMM) && eamf) { /** app metadata ext. **/ 285 | wtmp = whdr; 286 | wtmp.bptr = buff + 1 + 1; /** just passing the raw chunk **/ 287 | eamf(anim, &wtmp); 288 | } 289 | } 290 | whdr.bptr -= GIF_BLEN; /** for excess pixel codes ----v (here & above) **/ 291 | GIF_MGET(whdr.bptr, (unsigned long)(blen + GIF_BLEN + 2), anim, 0) 292 | return (whdr.nfrm < 0)? (skip - whdr.ifrm - 1) : (whdr.ifrm + 1); 293 | } 294 | 295 | #undef _GIF_SWAP 296 | #ifdef __cplusplus 297 | } 298 | #endif 299 | #endif /** GIF_LOAD_H **/ 300 | -------------------------------------------------------------------------------- /source/jo_gif.h: -------------------------------------------------------------------------------- 1 | /* public domain, Simple, Minimalistic GIF writer - http://jonolick.com 2 | * 3 | * Quick Notes: 4 | * Supports only 4 component input, alpha is currently ignored. (RGBX) 5 | * 6 | * Latest revisions: 7 | * 1.00 (2015-11-03) initial release 8 | * 9 | * Basic usage: 10 | * char *frame = new char[128*128*4]; // 4 component. RGBX format, where X is unused 11 | * jo_gif_t gif = jo_gif_start("foo.gif", 128, 128, 0, 32); 12 | * jo_gif_frame(&gif, frame, 4, false); // frame 1 13 | * jo_gif_frame(&gif, frame, 4, false); // frame 2 14 | * jo_gif_frame(&gif, frame, 4, false); // frame 3, ... 15 | * jo_gif_end(&gif); 16 | * */ 17 | 18 | #ifndef JO_INCLUDE_GIF_H 19 | #define JO_INCLUDE_GIF_H 20 | 21 | #include 22 | 23 | // To get a header file for this, either cut and paste the header, 24 | // or create jo_gif.h, #define JO_GIF_HEADER_FILE_ONLY, and 25 | // then include jo_gif.cpp from it. 26 | 27 | typedef struct jo_gif_t { 28 | FILE *fp; 29 | unsigned char palette[0x300]; 30 | short width, height, repeat; 31 | int numColors, palSize; 32 | int frame; 33 | } jo_gif_t; 34 | 35 | // width/height | the same for every frame 36 | // repeat | 0 = loop forever, 1 = loop once, etc... 37 | // palSize | must be power of 2 - 1. so, 255 not 256. 38 | extern jo_gif_t jo_gif_start(const char *filename, short width, short height, short repeat, int palSize); 39 | 40 | // gif | the state (returned from jo_gif_start) 41 | // rgba | the pixels 42 | // delayCsec | amount of time in between frames (in centiseconds) 43 | // localPalette | true if you want a unique palette generated for this frame (does not effect future frames) 44 | extern void jo_gif_frame(jo_gif_t *gif, unsigned char *rgba, short delayCsec, bool localPalette); 45 | 46 | // gif | the state (returned from jo_gif_start) 47 | extern void jo_gif_end(jo_gif_t *gif); 48 | 49 | #endif 50 | 51 | #ifndef JO_GIF_HEADER_FILE_ONLY 52 | 53 | #if defined(_MSC_VER) && _MSC_VER >= 0x1400 54 | #define _CRT_SECURE_NO_WARNINGS // suppress warnings about fopen() 55 | #endif 56 | 57 | #include 58 | #include 59 | #include 60 | 61 | // Based on NeuQuant algorithm 62 | static void jo_gif_quantize(unsigned char *rgba, int rgbaSize, int sample, unsigned char *map, int numColors) { 63 | // defs for freq and bias 64 | const int intbiasshift = 16; /* bias for fractions */ 65 | const int intbias = (((int) 1) << intbiasshift); 66 | const int gammashift = 10; /* gamma = 1024 */ 67 | const int betashift = 10; 68 | const int beta = (intbias >> betashift); /* beta = 1/1024 */ 69 | const int betagamma = (intbias << (gammashift - betashift)); 70 | 71 | // defs for decreasing radius factor 72 | const int radiusbiasshift = 6; /* at 32.0 biased by 6 bits */ 73 | const int radiusbias = (((int) 1) << radiusbiasshift); 74 | const int radiusdec = 30; /* factor of 1/30 each cycle */ 75 | 76 | // defs for decreasing alpha factor 77 | const int alphabiasshift = 10; /* alpha starts at 1.0 */ 78 | const int initalpha = (((int) 1) << alphabiasshift); 79 | 80 | // radbias and alpharadbias used for radpower calculation 81 | const int radbiasshift = 8; 82 | const int radbias = (((int) 1) << radbiasshift); 83 | const int alpharadbshift = (alphabiasshift + radbiasshift); 84 | const int alpharadbias = (((int) 1) << alpharadbshift); 85 | 86 | sample = sample < 1 ? 1 : sample > 30 ? 30 : sample; 87 | int network[256][3]; 88 | int bias[256] = {0}, freq[256]; 89 | for(int i = 0; i < numColors; ++i) { 90 | // Put nurons evenly through the luminance spectrum. 91 | network[i][0] = network[i][1] = network[i][2] = (i << 12) / numColors; 92 | freq[i] = intbias / numColors; 93 | } 94 | // Learn 95 | { 96 | const int primes[5] = {499, 491, 487, 503}; 97 | int step = 4; 98 | for(int i = 0; i < 4; ++i) { 99 | if(rgbaSize > primes[i] * 4 && (rgbaSize % primes[i])) { // TODO/Error? primes[i]*4? 100 | step = primes[i] * 4; 101 | } 102 | } 103 | sample = step == 4 ? 1 : sample; 104 | 105 | int alphadec = 30 + ((sample - 1) / 3); 106 | int samplepixels = rgbaSize / (4 * sample); 107 | int delta = samplepixels / 100; 108 | int alpha = initalpha; 109 | delta = delta == 0 ? 1 : delta; 110 | 111 | int radius = (numColors >> 3) * radiusbias; 112 | int rad = radius >> radiusbiasshift; 113 | rad = rad <= 1 ? 0 : rad; 114 | int radSq = rad*rad; 115 | int radpower[32]; 116 | for (int i = 0; i < rad; i++) { 117 | radpower[i] = alpha * (((radSq - i * i) * radbias) / radSq); 118 | } 119 | 120 | // Randomly walk through the pixels and relax neurons to the "optimal" target. 121 | for(int i = 0, pix = 0; i < samplepixels;) { 122 | int r = rgba[pix + 0] << 4; 123 | int g = rgba[pix + 1] << 4; 124 | int b = rgba[pix + 2] << 4; 125 | int j = -1; 126 | { 127 | // finds closest neuron (min dist) and updates freq 128 | // finds best neuron (min dist-bias) and returns position 129 | // for frequently chosen neurons, freq[k] is high and bias[k] is negative 130 | // bias[k] = gamma*((1/numColors)-freq[k]) 131 | 132 | int bestd = 0x7FFFFFFF, bestbiasd = 0x7FFFFFFF, bestpos = -1; 133 | for (int k = 0; k < numColors; k++) { 134 | int *n = network[k]; 135 | int dist = abs(n[0] - r) + abs(n[1] - g) + abs(n[2] - b); 136 | if (dist < bestd) { 137 | bestd = dist; 138 | bestpos = k; 139 | } 140 | int biasdist = dist - ((bias[k]) >> (intbiasshift - 4)); 141 | if (biasdist < bestbiasd) { 142 | bestbiasd = biasdist; 143 | j = k; 144 | } 145 | int betafreq = freq[k] >> betashift; 146 | freq[k] -= betafreq; 147 | bias[k] += betafreq << gammashift; 148 | } 149 | freq[bestpos] += beta; 150 | bias[bestpos] -= betagamma; 151 | } 152 | 153 | // Move neuron j towards biased (b,g,r) by factor alpha 154 | network[j][0] -= (network[j][0] - r) * alpha / initalpha; 155 | network[j][1] -= (network[j][1] - g) * alpha / initalpha; 156 | network[j][2] -= (network[j][2] - b) * alpha / initalpha; 157 | if (rad != 0) { 158 | // Move adjacent neurons by precomputed alpha*(1-((i-j)^2/[r]^2)) in radpower[|i-j|] 159 | int lo = j - rad; 160 | lo = lo < -1 ? -1 : lo; 161 | int hi = j + rad; 162 | hi = hi > numColors ? numColors : hi; 163 | for(int jj = j+1, m=1; jj < hi; ++jj) { 164 | int a = radpower[m++]; 165 | network[jj][0] -= (network[jj][0] - r) * a / alpharadbias; 166 | network[jj][1] -= (network[jj][1] - g) * a / alpharadbias; 167 | network[jj][2] -= (network[jj][2] - b) * a / alpharadbias; 168 | } 169 | for(int k = j-1, m=1; k > lo; --k) { 170 | int a = radpower[m++]; 171 | network[k][0] -= (network[k][0] - r) * a / alpharadbias; 172 | network[k][1] -= (network[k][1] - g) * a / alpharadbias; 173 | network[k][2] -= (network[k][2] - b) * a / alpharadbias; 174 | } 175 | } 176 | 177 | pix += step; 178 | pix = pix >= rgbaSize ? pix - rgbaSize : pix; 179 | 180 | // every 1% of the image, move less over the following iterations. 181 | if(++i % delta == 0) { 182 | alpha -= alpha / alphadec; 183 | radius -= radius / radiusdec; 184 | rad = radius >> radiusbiasshift; 185 | rad = rad <= 1 ? 0 : rad; 186 | radSq = rad*rad; 187 | for (j = 0; j < rad; j++) { 188 | radpower[j] = alpha * ((radSq - j * j) * radbias / radSq); 189 | } 190 | } 191 | } 192 | } 193 | // Unbias network to give byte values 0..255 194 | for (int i = 0; i < numColors; i++) { 195 | map[i*3+0] = network[i][0] >>= 4; 196 | map[i*3+1] = network[i][1] >>= 4; 197 | map[i*3+2] = network[i][2] >>= 4; 198 | } 199 | } 200 | 201 | typedef struct { 202 | FILE *fp; 203 | int numBits; 204 | unsigned char buf[256]; 205 | unsigned char idx; 206 | unsigned tmp; 207 | int outBits; 208 | int curBits; 209 | } jo_gif_lzw_t; 210 | 211 | static void jo_gif_lzw_write(jo_gif_lzw_t *s, int code) { 212 | s->outBits |= code << s->curBits; 213 | s->curBits += s->numBits; 214 | while(s->curBits >= 8) { 215 | s->buf[s->idx++] = s->outBits & 255; 216 | s->outBits >>= 8; 217 | s->curBits -= 8; 218 | if (s->idx >= 255) { 219 | putc(s->idx, s->fp); 220 | fwrite(s->buf, s->idx, 1, s->fp); 221 | s->idx = 0; 222 | } 223 | } 224 | } 225 | 226 | static void jo_gif_lzw_encode(unsigned char *in, int len, FILE *fp) { 227 | jo_gif_lzw_t state = {fp, 9}; 228 | int maxcode = 511; 229 | 230 | // Note: 30k stack space for dictionary =| 231 | const int hashSize = 5003; 232 | short codetab[5003]; 233 | int hashTbl[5003]; 234 | memset(hashTbl, 0xFF, sizeof(hashTbl)); 235 | 236 | jo_gif_lzw_write(&state, 0x100); 237 | 238 | int free_ent = 0x102; 239 | int ent = *in++; 240 | CONTINUE: 241 | while (--len) { 242 | int c = *in++; 243 | int fcode = (c << 12) + ent; 244 | int key = (c << 4) ^ ent; // xor hashing 245 | while(hashTbl[key] >= 0) { 246 | if(hashTbl[key] == fcode) { 247 | ent = codetab[key]; 248 | goto CONTINUE; 249 | } 250 | ++key; 251 | key = key >= hashSize ? key - hashSize : key; 252 | } 253 | jo_gif_lzw_write(&state, ent); 254 | ent = c; 255 | if(free_ent < 4096) { 256 | if(free_ent > maxcode) { 257 | ++state.numBits; 258 | if(state.numBits == 12) { 259 | maxcode = 4096; 260 | } else { 261 | maxcode = (1< c ? c : a; } 284 | 285 | jo_gif_t jo_gif_start(const char *filename, short width, short height, short repeat, int numColors) { 286 | numColors = numColors > 255 ? 255 : numColors < 2 ? 2 : numColors; 287 | jo_gif_t gif = {0}; 288 | gif.width = width; 289 | gif.height = height; 290 | gif.repeat = repeat; 291 | gif.numColors = numColors; 292 | gif.palSize = log2(numColors); 293 | 294 | gif.fp = fopen(filename, "wb"); 295 | if(!gif.fp) { 296 | printf("Error: Could not WriteGif to %s\n", filename); 297 | return gif; 298 | } 299 | 300 | fwrite("GIF89a", 6, 1, gif.fp); 301 | // Logical Screen Descriptor 302 | fwrite(&gif.width, 2, 1, gif.fp); 303 | fwrite(&gif.height, 2, 1, gif.fp); 304 | putc(0xF0 | gif.palSize, gif.fp); 305 | fwrite("\x00\x00", 2, 1, gif.fp); // bg color index (unused), aspect ratio 306 | return gif; 307 | } 308 | 309 | void jo_gif_frame(jo_gif_t *gif, unsigned char * rgba, short delayCsec, bool localPalette) { 310 | if(!gif->fp) { 311 | return; 312 | } 313 | short width = gif->width; 314 | short height = gif->height; 315 | int size = width * height; 316 | 317 | unsigned char localPalTbl[0x300]; 318 | unsigned char *palette = gif->frame == 0 || !localPalette ? gif->palette : localPalTbl; 319 | if(gif->frame == 0 || localPalette) { 320 | jo_gif_quantize(rgba, size*4, 1, palette, gif->numColors); 321 | } 322 | 323 | unsigned char *indexedPixels = (unsigned char *)malloc(size); 324 | { 325 | unsigned char *ditheredPixels = (unsigned char*)malloc(size*4); 326 | memcpy(ditheredPixels, rgba, size*4); 327 | for(int k = 0; k < size*4; k+=4) { 328 | int rgb[3] = { ditheredPixels[k+0], ditheredPixels[k+1], ditheredPixels[k+2] }; 329 | int bestd = 0x7FFFFFFF, best = -1; 330 | // TODO: exhaustive search. do something better. 331 | for(int i = 0; i < gif->numColors; ++i) { 332 | int bb = palette[i*3+0]-rgb[0]; 333 | int gg = palette[i*3+1]-rgb[1]; 334 | int rr = palette[i*3+2]-rgb[2]; 335 | int d = bb*bb + gg*gg + rr*rr; 336 | if(d < bestd) { 337 | bestd = d; 338 | best = i; 339 | } 340 | } 341 | indexedPixels[k/4] = best; 342 | int diff[3] = { ditheredPixels[k+0] - palette[indexedPixels[k/4]*3+0], ditheredPixels[k+1] - palette[indexedPixels[k/4]*3+1], ditheredPixels[k+2] - palette[indexedPixels[k/4]*3+2] }; 343 | // Floyd-Steinberg Error Diffusion 344 | // TODO: Use something better -- http://caca.zoy.org/study/part3.html 345 | if(k+4 < size*4) { 346 | ditheredPixels[k+4+0] = (unsigned char)jo_gif_clamp(ditheredPixels[k+4+0]+(diff[0]*7/16), 0, 255); 347 | ditheredPixels[k+4+1] = (unsigned char)jo_gif_clamp(ditheredPixels[k+4+1]+(diff[1]*7/16), 0, 255); 348 | ditheredPixels[k+4+2] = (unsigned char)jo_gif_clamp(ditheredPixels[k+4+2]+(diff[2]*7/16), 0, 255); 349 | } 350 | if(k+width*4+4 < size*4) { 351 | for(int i = 0; i < 3; ++i) { 352 | ditheredPixels[k-4+width*4+i] = (unsigned char)jo_gif_clamp(ditheredPixels[k-4+width*4+i]+(diff[i]*3/16), 0, 255); 353 | ditheredPixels[k+width*4+i] = (unsigned char)jo_gif_clamp(ditheredPixels[k+width*4+i]+(diff[i]*5/16), 0, 255); 354 | ditheredPixels[k+width*4+4+i] = (unsigned char)jo_gif_clamp(ditheredPixels[k+width*4+4+i]+(diff[i]*1/16), 0, 255); 355 | } 356 | } 357 | } 358 | free(ditheredPixels); 359 | } 360 | if(gif->frame == 0) { 361 | // Global Color Table 362 | fwrite(palette, 3*(1<<(gif->palSize+1)), 1, gif->fp); 363 | if(gif->repeat >= 0) { 364 | // Netscape Extension 365 | fwrite("\x21\xff\x0bNETSCAPE2.0\x03\x01", 16, 1, gif->fp); 366 | fwrite(&gif->repeat, 2, 1, gif->fp); // loop count (extra iterations, 0=repeat forever) 367 | putc(0, gif->fp); // block terminator 368 | } 369 | } 370 | // Graphic Control Extension 371 | fwrite("\x21\xf9\x04\x00", 4, 1, gif->fp); 372 | fwrite(&delayCsec, 2, 1, gif->fp); // delayCsec x 1/100 sec 373 | fwrite("\x00\x00", 2, 1, gif->fp); // transparent color index (first byte), currently unused 374 | // Image Descriptor 375 | fwrite("\x2c\x00\x00\x00\x00", 5, 1, gif->fp); // header, x,y 376 | fwrite(&width, 2, 1, gif->fp); 377 | fwrite(&height, 2, 1, gif->fp); 378 | if (gif->frame == 0 || !localPalette) { 379 | putc(0, gif->fp); 380 | } else { 381 | putc(0x80|gif->palSize, gif->fp ); 382 | fwrite(palette, 3*(1<<(gif->palSize+1)), 1, gif->fp); 383 | } 384 | putc(8, gif->fp); // block terminator 385 | jo_gif_lzw_encode(indexedPixels, size, gif->fp); 386 | putc(0, gif->fp); // block terminator 387 | ++gif->frame; 388 | free(indexedPixels); 389 | } 390 | 391 | void jo_gif_end(jo_gif_t *gif) { 392 | if(!gif->fp) { 393 | return; 394 | } 395 | putc(0x3b, gif->fp); // gif trailer 396 | fclose(gif->fp); 397 | } 398 | #endif 399 | -------------------------------------------------------------------------------- /source/main.c: -------------------------------------------------------------------------------- 1 | #define _CRT_SECURE_NO_WARNINGS 2 | #include 3 | #include 4 | #include 5 | #include "app.h" 6 | #include "frametimer.h" 7 | #include "crtemu.h" 8 | #include "crt_frame.h" 9 | #include "crtemu_pc.h" 10 | #include "crt_frame_pc.h" 11 | #include "stb_image.h" 12 | #include "gif_load.h" 13 | 14 | typedef struct jo_gif_t jo_gif_t; 15 | 16 | jo_gif_t* export_start( app_t* app ); 17 | 18 | void export_frame( app_t* app, jo_gif_t* gif, int delay ); 19 | void export_end( jo_gif_t* gif ); 20 | 21 | typedef struct gif_t { 22 | uint32_t* pict; 23 | uint32_t* prev; 24 | unsigned long last; 25 | int width; 26 | int height; 27 | uint32_t** frames; 28 | int* times; 29 | int capacity; 30 | int count; 31 | } gif_t; 32 | 33 | 34 | void gif_frame( void *data, struct GIF_WHDR* whdr ) { 35 | uint32_t x, y, yoff, iter, ifin, dsrc; 36 | gif_t* gif = (gif_t*)data; 37 | 38 | #define BGRA(i) ((whdr->bptr[i] == whdr->tran)? 0 : \ 39 | ((uint32_t)(whdr->cpal[whdr->bptr[i]].B << ((GIF_BIGE)? 8 : 16)) \ 40 | | (uint32_t)(whdr->cpal[whdr->bptr[i]].G << ((GIF_BIGE)? 16 : 8)) \ 41 | | (uint32_t)(whdr->cpal[whdr->bptr[i]].R << ((GIF_BIGE)? 24 : 0)) \ 42 | | ((GIF_BIGE)? 0xFF : 0xFF000000))) 43 | if (!whdr->ifrm) { 44 | uint32_t ddst = (uint32_t)(whdr->xdim * whdr->ydim); 45 | gif->pict = (uint32_t*)calloc(sizeof(uint32_t), ddst); 46 | gif->prev = (uint32_t*)calloc(sizeof(uint32_t), ddst); 47 | gif->width = whdr->xdim; 48 | gif->height = whdr->ydim; 49 | gif->capacity = 16; 50 | gif->count = 0; 51 | gif->frames = (uint32_t**) malloc( sizeof( uint32_t* ) * gif->capacity ); 52 | gif->times = (int*) malloc( sizeof( int ) * gif->capacity ); 53 | } 54 | 55 | /** [TODO:] the frame is assumed to be inside global bounds, 56 | however it might exceed them in some GIFs; fix me. **/ 57 | uint32_t* pict = (uint32_t*)gif->pict; 58 | uint32_t ddst = (uint32_t)(whdr->xdim * whdr->fryo + whdr->frxo); 59 | ifin = (!(iter = (whdr->intr)? 0 : 4))? 4 : 5; /** interlacing support **/ 60 | for (dsrc = (uint32_t)-1; iter < ifin; iter++) 61 | for (yoff = 16U >> ((iter > 1)? iter : 1), y = (8 >> iter) & 7; 62 | y < (uint32_t)whdr->fryd; y += yoff) 63 | for (x = 0; x < (uint32_t)whdr->frxd; x++) 64 | if (whdr->tran != (long)whdr->bptr[++dsrc]) 65 | pict[(uint32_t)whdr->xdim * y + x + ddst] = BGRA(dsrc); 66 | 67 | if( gif->count >= gif->capacity ) { 68 | gif->capacity *= 2; 69 | gif->frames = (uint32_t**) realloc( gif->frames, sizeof( uint32_t* ) * gif->capacity ); 70 | gif->times = (int*) realloc( gif->times, sizeof( int ) * gif->capacity ); 71 | } 72 | uint32_t* frame = (uint32_t*) malloc( sizeof( uint32_t ) * whdr->xdim * whdr->ydim ); 73 | memcpy( frame, gif->pict, sizeof( uint32_t ) * whdr->xdim * whdr->ydim ); 74 | gif->times[ gif->count ] = whdr->time; 75 | gif->frames[ gif->count++ ] = frame; 76 | 77 | if ((whdr->mode == GIF_PREV) && !gif->last) { 78 | whdr->frxd = whdr->xdim; 79 | whdr->fryd = whdr->ydim; 80 | whdr->mode = GIF_BKGD; 81 | ddst = 0; 82 | } 83 | else { 84 | gif->last = (whdr->mode == GIF_PREV)? 85 | gif->last : (unsigned long)(whdr->ifrm + 1); 86 | pict = (uint32_t*)((whdr->mode == GIF_PREV)? gif->pict : gif->prev); 87 | uint32_t* prev = (uint32_t*)((whdr->mode == GIF_PREV)? gif->prev : gif->pict); 88 | for (x = (uint32_t)(whdr->xdim * whdr->ydim); --x; 89 | pict[x - 1] = prev[x - 1]); 90 | } 91 | if (whdr->mode == GIF_BKGD) /** cutting a hole for the next frame **/ 92 | for (whdr->bptr[0] = (uint8_t)((whdr->tran >= 0)? 93 | whdr->tran : whdr->bkgd), y = 0, 94 | pict = (uint32_t*)gif->pict; y < (uint32_t)whdr->fryd; y++) 95 | for (x = 0; x < (uint32_t)whdr->frxd; x++) 96 | pict[(uint32_t)whdr->xdim * y + x + ddst] = BGRA(0); 97 | #undef BGRA 98 | } 99 | 100 | 101 | gif_t* load_gif( char const* filename ) { 102 | FILE* fp = fopen( filename, "rb" ); 103 | if( !fp ) { 104 | return NULL; 105 | } 106 | fseek( fp, 0, SEEK_END ); 107 | size_t size = ftell( fp ); 108 | fseek( fp, 0, SEEK_SET ); 109 | void* data = malloc( size ); 110 | fread( data, 1, size, fp ); 111 | fclose( fp ); 112 | gif_t* gif = (gif_t*)malloc( sizeof( gif_t ) ); 113 | long result = GIF_Load( data, (long)size, gif_frame, NULL, (void*)gif, 0L ); 114 | if( result == 0 ) { 115 | free( data ); 116 | free( gif ); 117 | return NULL; 118 | } 119 | 120 | free( gif->pict ); 121 | free( gif->prev ); 122 | return gif; 123 | } 124 | 125 | int app_proc( app_t* app, void* user_data ) { 126 | char const* filename = (char const*) user_data; 127 | app_title( app, "CRTView - UP/DOWN F11 ESC - F8 Export GIF" ); 128 | app_interpolation( app, APP_INTERPOLATION_NONE ); 129 | app_screenmode_t screenmode = APP_SCREENMODE_WINDOW; 130 | app_screenmode( app, screenmode ); 131 | frametimer_t* frametimer = frametimer_create( NULL ); 132 | frametimer_lock_rate( frametimer, 60 ); 133 | 134 | app_yield( app ); 135 | crtemu_pc_t* crtemu_pc = crtemu_pc_create( NULL ); 136 | crtemu_pc_frame( crtemu_pc, (CRTEMU_U32*) a_crt_frame, 1024, 1024 ); 137 | 138 | crtemu_t* crtemu = NULL; 139 | 140 | CRT_FRAME_U32* frame = (CRT_FRAME_U32*) malloc( CRT_FRAME_WIDTH * CRT_FRAME_HEIGHT * sizeof( CRT_FRAME_U32 ) ); 141 | crt_frame( frame ); 142 | 143 | APP_U32 blank = 0; 144 | int w = 1; 145 | int h = 1; 146 | int c; 147 | 148 | 149 | gif_t* gif = filename ? load_gif( filename ) : NULL; 150 | if( gif ) { 151 | w = gif->width; 152 | h = gif->height; 153 | } 154 | APP_U32* xbgr = filename && !gif ? (APP_U32*) stbi_load( filename, &w, &h, &c, 4 ) : NULL; 155 | int delay = gif ? gif->times[ 0 ] * 10 : 0; 156 | 157 | APP_U64 start = app_time_count( app ); 158 | 159 | bool prepare_export = false; 160 | jo_gif_t* exp_gif = NULL; 161 | 162 | int curr_frame = 0; 163 | int mode = 0; 164 | 165 | int exit = 0; 166 | while( !exit && app_yield( app ) != APP_STATE_EXIT_REQUESTED ) { 167 | frametimer_update( frametimer ); 168 | 169 | char const* dropped_file = app_last_dropped_file(app); 170 | if (dropped_file) { 171 | gif = load_gif( dropped_file ); 172 | if( gif ) { 173 | w = gif->width; 174 | h = gif->height; 175 | } 176 | xbgr = !gif ? (APP_U32*) stbi_load( dropped_file, &w, &h, &c, 4 ) : NULL; 177 | delay = gif ? gif->times[ 0 ] : 0; 178 | curr_frame = 0; 179 | } 180 | 181 | if( gif ) { 182 | delay -= 16; 183 | if( delay <= 0 ) { 184 | if( exp_gif && !prepare_export ) { 185 | export_frame( app, exp_gif, gif->times[ curr_frame ] ); 186 | } 187 | curr_frame = curr_frame + 1; 188 | if( curr_frame >= gif->count ) { 189 | if( exp_gif && !prepare_export ) { 190 | export_end( exp_gif ); 191 | exp_gif = NULL; 192 | app_title( app, "CRTView - UP/DOWN F11 ESC - F8 Export GIF" ); 193 | } 194 | curr_frame = 0; 195 | prepare_export = false; 196 | } 197 | delay = gif->times[ curr_frame ] * 10; 198 | } 199 | } 200 | 201 | int newmode = mode; 202 | app_input_t input = app_input( app ); 203 | for( int i = 0; i < input.count; ++i ) { 204 | if( input.events->type == APP_INPUT_KEY_DOWN ) { 205 | if( input.events->data.key == APP_KEY_ESCAPE ) { 206 | exit = 1; 207 | } else if( input.events->data.key == APP_KEY_F11 ) { 208 | screenmode = screenmode == APP_SCREENMODE_WINDOW ? APP_SCREENMODE_FULLSCREEN : APP_SCREENMODE_WINDOW; 209 | app_screenmode( app, screenmode ); 210 | } else if( input.events->data.key == APP_KEY_UP ) { 211 | newmode = ( 3 + mode - 1 ) % 3; 212 | } else if( input.events->data.key == APP_KEY_DOWN ) { 213 | newmode = ( mode + 1 ) % 3; 214 | } else if( input.events->data.key == APP_KEY_F8 ) { 215 | if( gif && !exp_gif ) { 216 | exp_gif = export_start( app ); 217 | curr_frame = 0; 218 | delay = gif->times[ curr_frame ] * 10; 219 | prepare_export = true; 220 | app_title( app, "CRTView - EXPORTING GIF" ); 221 | } 222 | } 223 | } 224 | } 225 | 226 | if( mode != newmode ) { 227 | if( mode == 0 ) { 228 | crtemu_pc_destroy( crtemu_pc ); 229 | crtemu_pc = NULL; 230 | } else if ( mode == 1 ) { 231 | crtemu_destroy( crtemu ); 232 | crtemu = NULL; 233 | } 234 | mode = newmode; 235 | if( mode == 0 ) { 236 | crtemu_pc = crtemu_pc_create( NULL ); 237 | crtemu_pc_frame( crtemu_pc, (CRTEMU_U32*) a_crt_frame, 1024, 1024 ); 238 | } else if ( mode == 1 ) { 239 | crtemu = crtemu_create( NULL ); 240 | crtemu_frame( crtemu, frame, CRT_FRAME_WIDTH, CRT_FRAME_HEIGHT); 241 | } 242 | } 243 | 244 | APP_U64 div = app_time_freq( app ) / 1000000ULL; 245 | APP_U64 t = ( app_time_count( app ) - start ) / div; 246 | if( exp_gif ) t = 0; 247 | 248 | if( mode == 0 ) { 249 | crtemu_pc_present( crtemu_pc, t, xbgr ? xbgr : gif ? gif->frames[ curr_frame ] : &blank, w, h, 0xffffff, 0x181818 ); 250 | app_present( app, NULL, w, h, 0xffffff, 0x000000 ); 251 | } else if( mode == 1 ) { 252 | crtemu_present( crtemu, t, xbgr ? xbgr : gif ? gif->frames[ curr_frame ] : &blank, w, h, 0xffffff, 0x181818 ); 253 | app_present( app, NULL, w, h, 0xffffff, 0x000000 ); 254 | } else { 255 | app_present( app, xbgr ? xbgr : gif ? gif->frames[ curr_frame ] : &blank, w, h, 0xffffff, 0x181818 ); 256 | } 257 | } 258 | 259 | if( mode == 0 ) { 260 | crtemu_pc_destroy( crtemu_pc ); 261 | } else if( mode == 1 ) { 262 | crtemu_destroy( crtemu ); 263 | } 264 | free( frame ); 265 | frametimer_destroy( frametimer ); 266 | if( xbgr != &blank ) { 267 | stbi_image_free( (stbi_uc*) xbgr ); 268 | } 269 | return 0; 270 | } 271 | 272 | 273 | 274 | 275 | /* 276 | ------------------------- 277 | ENTRY POINT (MAIN) 278 | ------------------------- 279 | */ 280 | 281 | #if defined( _WIN32 ) && !defined( __TINYC__ ) 282 | #ifndef NDEBUG 283 | #pragma warning( push ) 284 | #pragma warning( disable: 4619 ) // pragma warning : there is no warning number 'number' 285 | #pragma warning( disable: 4668 ) // 'symbol' is not defined as a preprocessor macro, replacing with '0' for 'directives' 286 | #include 287 | #pragma warning( pop ) 288 | #endif 289 | #endif /* _WIN32 && !__TINYC__ */ 290 | 291 | int main( int argc, char** argv ) { 292 | (void) argc, (void) argv; 293 | #if defined( _WIN32 ) && !defined( __TINYC__ ) 294 | #ifndef NDEBUG 295 | int flag = _CrtSetDbgFlag( _CRTDBG_REPORT_FLAG ); // Get current flag 296 | flag ^= _CRTDBG_LEAK_CHECK_DF; // Turn on leak-checking bit 297 | _CrtSetDbgFlag( flag ); // Set flag to the new value 298 | _CrtSetReportMode( _CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG ); 299 | _CrtSetReportFile( _CRT_WARN, _CRTDBG_FILE_STDOUT ); 300 | //_CrtSetBreakAlloc( 0 ); 301 | #endif 302 | #endif 303 | 304 | char* filename = argc > 1 ? argv[ 1 ] : NULL; 305 | return app_run( app_proc, filename, NULL, NULL, NULL ); 306 | } 307 | 308 | #ifdef _WIN32 309 | // pass-through so the program will build with either /SUBSYSTEM:WINDOWS or /SUBSYSTEM:CONSOLE 310 | struct HINSTANCE__; 311 | int __stdcall WinMain( struct HINSTANCE__* a, struct HINSTANCE__* b, char* c, int d ) { 312 | (void) a, (void) b, (void) c, (void) d; 313 | return main( __argc, __argv ); 314 | } 315 | #endif /* _WIN32 */ 316 | 317 | 318 | /* 319 | --------------------------------- 320 | LIBRARIES IMPLEMENTATIONS 321 | --------------------------------- 322 | */ 323 | 324 | #define APP_IMPLEMENTATION 325 | #ifdef _WIN32 326 | #define APP_WINDOWS 327 | #else 328 | #define APP_SDL 329 | #endif 330 | #define APP_LOG( ctx, level, message ) 331 | #include "app.h" 332 | 333 | #define CRTEMU_IMPLEMENTATION 334 | #include "crtemu.h" 335 | 336 | #define CRT_FRAME_IMPLEMENTATION 337 | #include "crt_frame.h" 338 | 339 | #define CRTEMU_PC_IMPLEMENTATION 340 | #define CRTEMU_PC_REPORT_SHADER_ERRORS 341 | #include "crtemu_pc.h" 342 | 343 | #define FRAMETIMER_IMPLEMENTATION 344 | #include "frametimer.h" 345 | 346 | #define STB_IMAGE_IMPLEMENTATION 347 | #define STBI_NO_SIMD 348 | #pragma warning( push ) 349 | #pragma warning( disable: 4296 ) 350 | #include "stb_image.h" 351 | #pragma warning( pop ) 352 | 353 | 354 | #include "jo_gif.h" 355 | 356 | 357 | jo_gif_t* export_start( app_t* app ) { 358 | APP_GLint dims[4] = {0}; 359 | app->gl.GetIntegerv(APP_GL_VIEWPORT, dims); 360 | int w = dims[2]; 361 | int h = dims[3]; 362 | 363 | jo_gif_t* jo_gif = malloc( sizeof( jo_gif_t ) ); 364 | *jo_gif = jo_gif_start("crtview.gif", w, h, 0, 256); 365 | return jo_gif; 366 | } 367 | 368 | 369 | void export_frame( app_t* app, jo_gif_t* gif, int delay ) { 370 | uint8_t* pixels = malloc( 4 * gif->width * gif->height * 2 ); 371 | 372 | app->gl.ReadBuffer( APP_GL_FRONT ); 373 | app->gl.ReadPixels( 0, 0, gif->width, gif->height, APP_GL_RGBA, APP_GL_UNSIGNED_BYTE, pixels + 4 * gif->width * gif->height ); 374 | for( int y = 0; y < gif->height; ++y ) { 375 | memcpy( pixels + 4 * gif->width * y, pixels + 4 * gif->width * ( gif->height * 2 - 1 - y ), 4 * gif->width ); 376 | } 377 | jo_gif_frame( gif, pixels, delay, true ); 378 | free( pixels ); 379 | } 380 | 381 | 382 | void export_end( jo_gif_t* gif ) { 383 | jo_gif_end(gif); 384 | free( gif ); 385 | } 386 | --------------------------------------------------------------------------------