diff --git a/src/osdep/amiberry_gfx.cpp b/src/osdep/amiberry_gfx.cpp index 98887f4ae..407c8e1e8 100644 --- a/src/osdep/amiberry_gfx.cpp +++ b/src/osdep/amiberry_gfx.cpp @@ -270,7 +270,9 @@ static float SDL2_getrefreshrate(const int monid) #ifdef USE_OPENGL static GLuint osd_texture = 0; static GLuint osd_program = 0; +static GLint osd_tex_loc = -1; static GLuint osd_vbo = 0; +static GLuint vbo_uploaded = 0; static const char* osd_vs_source = "#version 120\n" @@ -343,6 +345,8 @@ static bool init_osd_shader() glDeleteShader(vsh); glDeleteShader(fsh); + osd_tex_loc = glGetUniformLocation(osd_program, "tex0"); + glGenBuffers(1, &osd_vbo); return true; @@ -395,8 +399,6 @@ static bool SDL2_alloctexture(int monid, int w, int h) const int crt_type = get_crtemu_type(shader_name); crtemu_shader = crtemu_create(static_cast(crt_type), nullptr); } - if (crtemu_shader) - crtemu_frame(crtemu_shader, (CRTEMU_U32*)amiga_surface->pixels, w, h); return crtemu_shader != nullptr || external_shader != nullptr; #else if (w < 0 || h < 0) @@ -457,7 +459,9 @@ static void update_leds(const int monid) const amigadisplay* ad = &adisplays[monid]; const int m = statusline_get_multiplier(monid) / 100; const int led_height = TD_TOTAL_HEIGHT * m; - const int led_width = ad->picasso_on ? mon->currentmode.native_width : crop_rect.w; + int led_width = ad->picasso_on ? mon->currentmode.native_width : 640; + if (led_width <= 0) + led_width = 640; // (Re)allocate OSD surface and texture if dimensions changed if (!mon->statusline_surface || mon->statusline_surface->w != led_width || mon->statusline_surface->h != led_height) { @@ -1608,15 +1612,6 @@ static void render_with_external_shader(ExternalShader* shader, const int monid, // Use the shader shader->use(); - // Verify shader is active - GLint current_program = 0; - glGetIntegerv(GL_CURRENT_PROGRAM, ¤t_program); - - if (current_program == 0) { - write_log("ERROR: No shader program is active!\n"); - return; - } - // Set uniforms shader->set_texture_size(static_cast(width), static_cast(height)); shader->set_input_size(static_cast(width), static_cast(height)); @@ -1639,19 +1634,16 @@ static void render_with_external_shader(ExternalShader* shader, const int monid, // The shader expects: attribute vec4 VertexCoord (position as vec2 in xy) // attribute vec2 TexCoord // We'll use interleaved format: x, y, u, v - float vertices[] = { - -1.0f, -1.0f, 0.0f, 1.0f, // Bottom-left - 1.0f, -1.0f, 1.0f, 1.0f, // Bottom-right - 1.0f, 1.0f, 1.0f, 0.0f, // Top-right - -1.0f, 1.0f, 0.0f, 0.0f // Top-left - }; - glBindBuffer(GL_ARRAY_BUFFER, vbo); - glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); - - GLenum err = glGetError(); - if (err != GL_NO_ERROR) { - write_log("GL error after buffer data: 0x%x\n", err); + if (vbo_uploaded == 0) { + float vertices[] = { + -1.0f, -1.0f, 0.0f, 1.0f, // Bottom-left + 1.0f, -1.0f, 1.0f, 1.0f, // Bottom-right + 1.0f, 1.0f, 1.0f, 0.0f, // Top-right + -1.0f, 1.0f, 0.0f, 0.0f // Top-left + }; + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); + vbo_uploaded = vbo; } // Set up vertex attributes to match shader bindings @@ -1675,12 +1667,6 @@ static void render_with_external_shader(ExternalShader* shader, const int monid, // Draw fullscreen quad glDrawArrays(GL_TRIANGLE_FAN, 0, 4); - // Check for GL errors - err = glGetError(); - if (err != GL_NO_ERROR) { - write_log("OpenGL error after draw: 0x%x\n", err); - } - // Cleanup glDisableVertexAttribArray(0); glDisableVertexAttribArray(2); @@ -1694,20 +1680,24 @@ static void render_osd(const int monid, int drawableWidth, int drawableHeight) { const AmigaMonitor* mon = &AMonitors[monid]; const amigadisplay* ad = &adisplays[monid]; + static int last_osd_w = 0; + static int last_osd_h = 0; if (((currprefs.leds_on_screen & STATUSLINE_CHIPSET) && !ad->picasso_on) || ((currprefs.leds_on_screen & STATUSLINE_RTG) && ad->picasso_on)) { - update_leds(monid); if (mon->statusline_surface) { if (osd_texture != 0 && !glIsTexture(osd_texture)) { osd_texture = 0; } if (osd_texture == 0) { glGenTextures(1, &osd_texture); + glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, osd_texture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + last_osd_w = 0; + last_osd_h = 0; } if (!init_osd_shader()) return; @@ -1715,7 +1705,14 @@ static void render_osd(const int monid, int drawableWidth, int drawableHeight) glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, osd_texture); glPixelStorei(GL_UNPACK_ROW_LENGTH, mon->statusline_surface->pitch / 4); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, mon->statusline_surface->w, mon->statusline_surface->h, 0, GL_RGBA, GL_UNSIGNED_BYTE, mon->statusline_surface->pixels); + if (mon->statusline_surface->w != last_osd_w || mon->statusline_surface->h != last_osd_h) { + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, mon->statusline_surface->w, mon->statusline_surface->h, 0, GL_RGBA, GL_UNSIGNED_BYTE, mon->statusline_surface->pixels); + last_osd_w = mon->statusline_surface->w; + last_osd_h = mon->statusline_surface->h; + } + else { + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, mon->statusline_surface->w, mon->statusline_surface->h, GL_RGBA, GL_UNSIGNED_BYTE, mon->statusline_surface->pixels); + } glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); glEnable(GL_BLEND); @@ -1724,8 +1721,7 @@ static void render_osd(const int monid, int drawableWidth, int drawableHeight) glViewport(0, 0, drawableWidth, drawableHeight); glUseProgram(osd_program); - GLint tex_loc = glGetUniformLocation(osd_program, "tex0"); - if (tex_loc != -1) glUniform1i(tex_loc, 0); + if (osd_tex_loc != -1) glUniform1i(osd_tex_loc, 0); // Ensure only attribute 0 is enabled for OSD glEnableVertexAttribArray(0); @@ -1874,18 +1870,20 @@ void show_screen(const int monid, int mode) // Check if cropping is active const bool is_cropped = (crop_rect.x != 0 || crop_rect.y != 0 || crop_rect.w != (amiga_surface ? amiga_surface->w : 0) || - crop_rect.h != (amiga_surface ? amiga_surface->h : 0)); + crop_rect.h != (amiga_surface ? amiga_surface->h : 0)) && + (crop_rect.w > 0 && crop_rect.h > 0); if (is_cropped && amiga_surface) { - // SLOW PATH: Cropping is active - SDL_Rect corrected_crop_rect; - uae_u8* packed_pixel_buffer = create_packed_pixel_buffer(amiga_surface, crop_rect, corrected_crop_rect); - - if (packed_pixel_buffer) { - render_with_external_shader(external_shader, monid, packed_pixel_buffer, - corrected_crop_rect.w, corrected_crop_rect.h, corrected_crop_rect.w * 4, destW, destH); - delete[] packed_pixel_buffer; - } + // Fast path for cropping using GL_UNPACK_ROW_LENGTH + const int bpp = 4; + int x = std::max(0, crop_rect.x); + int y = std::max(0, crop_rect.y); + int w = std::min(crop_rect.w, amiga_surface->w - x); + int h = std::min(crop_rect.h, amiga_surface->h - y); + uae_u8* crop_ptr = static_cast(amiga_surface->pixels) + (y * amiga_surface->pitch) + (x * bpp); + + render_with_external_shader(external_shader, monid, crop_ptr, + w, h, amiga_surface->pitch, destW, destH); } else if (amiga_surface) { // FAST PATH: No cropping render_with_external_shader(external_shader, monid, @@ -1922,30 +1920,29 @@ void show_screen(const int monid, int mode) // Check if any cropping is actually being applied. // If crop_rect covers the entire surface, we can take a much faster path. const bool is_cropped = (crop_rect.x != 0 || crop_rect.y != 0 || - crop_rect.w != amiga_surface->w || - crop_rect.h != amiga_surface->h); + crop_rect.w != (amiga_surface ? amiga_surface->w : 0) || + crop_rect.h != (amiga_surface ? amiga_surface->h : 0)) && + (crop_rect.w > 0 && crop_rect.h > 0); - if (is_cropped) + if (is_cropped && amiga_surface) { - // SLOW PATH: Cropping is active. - // We must create a temporary packed buffer for the cropped region. - SDL_Rect corrected_crop_rect; - uae_u8* packed_pixel_buffer = create_packed_pixel_buffer(amiga_surface, crop_rect, corrected_crop_rect); - - if (packed_pixel_buffer) - { - crtemu_present(crtemu_shader, time * 1000, reinterpret_cast(packed_pixel_buffer), - corrected_crop_rect.w, corrected_crop_rect.h, 0xffffffff, 0x000000); - - delete[] packed_pixel_buffer; - } + // Fast path for cropping using GL_UNPACK_ROW_LENGTH + const int bpp = 4; + int x = std::max(0, crop_rect.x); + int y = std::max(0, crop_rect.y); + int w = std::min(crop_rect.w, amiga_surface->w - x); + int h = std::min(crop_rect.h, amiga_surface->h - y); + uae_u8* crop_ptr = static_cast(amiga_surface->pixels) + (y * amiga_surface->pitch) + (x * bpp); + + crtemu_present(crtemu_shader, time * 1000, reinterpret_cast(crop_ptr), + w, h, amiga_surface->pitch, 0xffffffff, 0x000000); } else { // FAST PATH: No cropping. // Render the full surface directly without any expensive memory allocation or copying. crtemu_present(crtemu_shader, time * 1000, (CRTEMU_U32 const*)amiga_surface->pixels, - amiga_surface->w, amiga_surface->h, 0xffffffff, 0x000000); + amiga_surface->w, amiga_surface->h, amiga_surface->pitch, 0xffffffff, 0x000000); } render_osd(monid, drawableWidth, drawableHeight); @@ -4358,6 +4355,8 @@ bool target_graphics_buffer_update(const int monid, const bool force) { render_quad = { dx, dy, scaled_width, scaled_height }; crop_rect = { currprefs.gfx_horizontal_offset, currprefs.gfx_vertical_offset, currprefs.gfx_manual_crop_width, currprefs.gfx_manual_crop_height }; + if (crop_rect.w <= 0) crop_rect.w = w; + if (crop_rect.h <= 0) crop_rect.h = h; } set_scaling_option(mon->monitor_id, &currprefs, scaled_width, scaled_height); #else @@ -4373,6 +4372,8 @@ bool target_graphics_buffer_update(const int monid, const bool force) { render_quad = { dx, dy, scaled_width, scaled_height }; crop_rect = { currprefs.gfx_horizontal_offset, currprefs.gfx_vertical_offset, currprefs.gfx_manual_crop_width, currprefs.gfx_manual_crop_height }; + if (crop_rect.w <= 0) crop_rect.w = w; + if (crop_rect.h <= 0) crop_rect.h = h; } } else @@ -4690,6 +4691,8 @@ void auto_crop_image() #ifdef USE_OPENGL render_quad = { dx, dy, width, height }; crop_rect = { cx, cy, cw, ch }; + if (crop_rect.w <= 0 && amiga_surface) crop_rect.w = amiga_surface->w; + if (crop_rect.h <= 0 && amiga_surface) crop_rect.h = amiga_surface->h; #else if (amiberry_options.rotation_angle == 0 || amiberry_options.rotation_angle == 180) @@ -4697,6 +4700,8 @@ void auto_crop_image() SDL_RenderSetLogicalSize(mon->amiga_renderer, width, height); render_quad = { dx, dy, width, height }; crop_rect = { cx, cy, cw, ch }; + if (crop_rect.w <= 0 && amiga_surface) crop_rect.w = amiga_surface->w; + if (crop_rect.h <= 0 && amiga_surface) crop_rect.h = amiga_surface->h; } else { diff --git a/src/osdep/crtemu.h b/src/osdep/crtemu.h index e818a677e..e8d024178 100644 --- a/src/osdep/crtemu.h +++ b/src/osdep/crtemu.h @@ -37,7 +37,7 @@ void crtemu_destroy( crtemu_t* crtemu ); void crtemu_frame( crtemu_t* crtemu, CRTEMU_U32* frame_abgr, int frame_width, int frame_height ); -void crtemu_present( crtemu_t* crtemu, CRTEMU_U64 time_us, CRTEMU_U32 const* pixels_xbgr, int width, int height, +void crtemu_present( crtemu_t* crtemu, CRTEMU_U64 time_us, CRTEMU_U32 const* pixels_xbgr, int width, int height, int pitch, CRTEMU_U32 mod_xbgr, CRTEMU_U32 border_xbgr ); void crtemu_coordinates_window_to_bitmap( crtemu_t* crtemu, int width, int height, int* x, int* y ); @@ -141,6 +141,7 @@ void crtemu_coordinates_window_to_bitmap( crtemu_t* crtemu, int width, int heigh #define CRTEMU_GL_FRAMEBUFFER 0x8d40 #define CRTEMU_GL_VIEWPORT 0x0ba2 #define CRTEMU_GL_RGB 0x1907 + #define CRTEMU_GL_UNPACK_ROW_LENGTH 0x0CF2 #define CRTEMU_GL_COLOR_ATTACHMENT0 0x8ce0 #define CRTEMU_GL_TEXTURE_WRAP_S 0x2802 #define CRTEMU_GL_TEXTURE_WRAP_T 0x2803 @@ -192,6 +193,7 @@ typedef GLbitfield CRTEMU_GLbitfield; #define CRTEMU_GL_FRAMEBUFFER GL_FRAMEBUFFER #define CRTEMU_GL_VIEWPORT GL_VIEWPORT #define CRTEMU_GL_RGB GL_RGB +#define CRTEMU_GL_UNPACK_ROW_LENGTH GL_UNPACK_ROW_LENGTH #define CRTEMU_GL_COLOR_ATTACHMENT0 GL_COLOR_ATTACHMENT0 #define CRTEMU_GL_TEXTURE_WRAP_S GL_TEXTURE_WRAP_S #define CRTEMU_GL_TEXTURE_WRAP_T GL_TEXTURE_WRAP_T @@ -237,6 +239,24 @@ struct crtemu_t { int last_present_width; int last_present_height; + CRTEMU_GLint loc_blur_blur; + CRTEMU_GLint loc_blur_texture; + CRTEMU_GLint loc_copy_tex0; + CRTEMU_GLint loc_accumulate_tex0; + CRTEMU_GLint loc_accumulate_tex1; + CRTEMU_GLint loc_accumulate_modulate; + CRTEMU_GLint loc_blend_tex0; + CRTEMU_GLint loc_blend_tex1; + CRTEMU_GLint loc_blend_modulate; + CRTEMU_GLint loc_crt_backbuffer; + CRTEMU_GLint loc_crt_blurbuffer; + CRTEMU_GLint loc_crt_frametexture; + CRTEMU_GLint loc_crt_use_frame; + CRTEMU_GLint loc_crt_time; + CRTEMU_GLint loc_crt_resolution; + CRTEMU_GLint loc_crt_size; + CRTEMU_GLint loc_crt_modulate; + #ifndef CRTEMU_SDL struct HINSTANCE__* gl_dll; @@ -278,6 +298,7 @@ struct crtemu_t { CRTEMU_GLint (CRTEMU_GLCALLTYPE* GetUniformLocation) (CRTEMU_GLuint program, CRTEMU_GLchar const* name); 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); void (CRTEMU_GLCALLTYPE* TexSubImage2D) (CRTEMU_GLenum target, CRTEMU_GLint level, CRTEMU_GLint xoffset, CRTEMU_GLint yoffset, CRTEMU_GLsizei width, CRTEMU_GLsizei height, CRTEMU_GLenum format, CRTEMU_GLenum type, void const* pixels); + void (CRTEMU_GLCALLTYPE* PixelStorei) (CRTEMU_GLenum pname, CRTEMU_GLint param); void (CRTEMU_GLCALLTYPE* ClearColor) (CRTEMU_GLfloat red, CRTEMU_GLfloat green, CRTEMU_GLfloat blue, CRTEMU_GLfloat alpha); void (CRTEMU_GLCALLTYPE* Clear) (CRTEMU_GLbitfield mask); void (CRTEMU_GLCALLTYPE* DrawArrays) (CRTEMU_GLenum mode, CRTEMU_GLint first, CRTEMU_GLsizei count); @@ -1143,8 +1164,8 @@ bool crtemu_shaders_1084( crtemu_t* crtemu ) { " {\n" " uv = (uv - 0.5) * 2.0;\n" " uv *= 1.1; \n" - " uv.x *= 1.0 + pow((abs(uv.y) / 4.5), 2.0);\n" - " uv.y *= 1.0 + pow((abs(uv.x) / 3.5), 2.0);\n" + " uv.x *= 1.0 + pow((abs(uv.y) / 6.0), 2.0);\n" + " uv.y *= 1.0 + pow((abs(uv.x) / 5.0), 2.0);\n" " uv = (uv / 2.0) + 0.5;\n" " uv = uv *0.92 + 0.04;\n" " return uv;\n" @@ -1158,7 +1179,7 @@ bool crtemu_shaders_1084( crtemu_t* crtemu ) { "void main(void)\n" " {\n" " /* Curve */\n" - " vec2 curved_uv = mix( curve( uv ), uv, 0.5 );\n" + " vec2 curved_uv = mix( curve( uv ), uv, 0.65 );\n" " float scale = 0.04;\n" " vec2 scuv = curved_uv;\n" "\n" @@ -1195,7 +1216,7 @@ bool crtemu_shaders_1084( crtemu_t* crtemu ) { " col = clamp(col*1.3 + 0.75*col*col + 1.25*col*col*col*col*col,vec3(0.0),vec3(10.0));\n" "\n" " /* Vignette */\n" - " float vig = (0.0 + 1.0*16.0*curved_uv.x*curved_uv.y*(1.0-curved_uv.x)*(1.0-curved_uv.y));\n" + " float vig = (0.2 + 1.0*16.0*curved_uv.x*curved_uv.y*(1.0-curved_uv.x)*(1.0-curved_uv.y));\n" " vig = 1.3*pow(vig,0.5);\n" " col *= vig;\n" "\n" @@ -1384,6 +1405,36 @@ bool crtemu_shaders_none( crtemu_t* crtemu ) { } +static void crtemu_init_uniform_locations(crtemu_t* crtemu) { + if (crtemu->blur_shader) { + crtemu->loc_blur_blur = crtemu->GetUniformLocation(crtemu->blur_shader, "blur"); + crtemu->loc_blur_texture = crtemu->GetUniformLocation(crtemu->blur_shader, "texture"); + } + if (crtemu->copy_shader) { + crtemu->loc_copy_tex0 = crtemu->GetUniformLocation(crtemu->copy_shader, "tex0"); + } + if (crtemu->accumulate_shader) { + crtemu->loc_accumulate_tex0 = crtemu->GetUniformLocation(crtemu->accumulate_shader, "tex0"); + crtemu->loc_accumulate_tex1 = crtemu->GetUniformLocation(crtemu->accumulate_shader, "tex1"); + crtemu->loc_accumulate_modulate = crtemu->GetUniformLocation(crtemu->accumulate_shader, "modulate"); + } + if (crtemu->blend_shader) { + crtemu->loc_blend_tex0 = crtemu->GetUniformLocation(crtemu->blend_shader, "tex0"); + crtemu->loc_blend_tex1 = crtemu->GetUniformLocation(crtemu->blend_shader, "tex1"); + crtemu->loc_blend_modulate = crtemu->GetUniformLocation(crtemu->blend_shader, "modulate"); + } + if (crtemu->crt_shader) { + crtemu->loc_crt_backbuffer = crtemu->GetUniformLocation(crtemu->crt_shader, "backbuffer"); + crtemu->loc_crt_blurbuffer = crtemu->GetUniformLocation(crtemu->crt_shader, "blurbuffer"); + crtemu->loc_crt_frametexture = crtemu->GetUniformLocation(crtemu->crt_shader, "frametexture"); + crtemu->loc_crt_use_frame = crtemu->GetUniformLocation(crtemu->crt_shader, "use_frame"); + crtemu->loc_crt_time = crtemu->GetUniformLocation(crtemu->crt_shader, "time"); + crtemu->loc_crt_resolution = crtemu->GetUniformLocation(crtemu->crt_shader, "resolution"); + crtemu->loc_crt_size = crtemu->GetUniformLocation(crtemu->crt_shader, "size"); + crtemu->loc_crt_modulate = crtemu->GetUniformLocation(crtemu->crt_shader, "modulate"); + } +} + crtemu_t* crtemu_create( crtemu_type_t type, void* memctx ) { crtemu_t* crtemu = (crtemu_t*) CRTEMU_MALLOC( memctx, sizeof( crtemu_t ) ); memset( crtemu, 0, sizeof( crtemu_t ) ); @@ -1439,6 +1490,7 @@ crtemu_t* crtemu_create( crtemu_type_t type, void* memctx ) { crtemu->GetUniformLocation = ( CRTEMU_GLint (CRTEMU_GLCALLTYPE*) (CRTEMU_GLuint, CRTEMU_GLchar const*) ) (uintptr_t) GetProcAddress( crtemu->gl_dll, "glGetUniformLocation" ); 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" ); crtemu->TexSubImage2D = ( void (CRTEMU_GLCALLTYPE*) (CRTEMU_GLenum, CRTEMU_GLint, CRTEMU_GLint, CRTEMU_GLint, CRTEMU_GLsizei, CRTEMU_GLsizei, CRTEMU_GLenum, CRTEMU_GLenum, void const*) ) (uintptr_t) GetProcAddress( crtemu->gl_dll, "glTexSubImage2D" ); + crtemu->PixelStorei = ( void (CRTEMU_GLCALLTYPE*) (CRTEMU_GLenum, CRTEMU_GLint) ) (uintptr_t) GetProcAddress( crtemu->gl_dll, "glPixelStorei" ); crtemu->ClearColor = ( void (CRTEMU_GLCALLTYPE*) (CRTEMU_GLfloat, CRTEMU_GLfloat, CRTEMU_GLfloat, CRTEMU_GLfloat) ) (uintptr_t) GetProcAddress( crtemu->gl_dll, "glClearColor" ); crtemu->Clear = ( void (CRTEMU_GLCALLTYPE*) (CRTEMU_GLbitfield) ) (uintptr_t) GetProcAddress( crtemu->gl_dll, "glClear" ); crtemu->DrawArrays = ( void (CRTEMU_GLCALLTYPE*) (CRTEMU_GLenum, CRTEMU_GLint, CRTEMU_GLsizei) ) (uintptr_t) GetProcAddress( crtemu->gl_dll, "glDrawArrays" ); @@ -1485,6 +1537,7 @@ crtemu_t* crtemu_create( crtemu_type_t type, void* memctx ) { if( !crtemu->GetUniformLocation ) crtemu->GetUniformLocation = ( CRTEMU_GLint (CRTEMU_GLCALLTYPE*) (CRTEMU_GLuint, CRTEMU_GLchar const*) ) (uintptr_t) crtemu->wglGetProcAddress( "glGetUniformLocation" ); 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" ); if( !crtemu->TexSubImage2D ) crtemu->TexSubImage2D = ( void (CRTEMU_GLCALLTYPE*) (CRTEMU_GLenum, CRTEMU_GLint, CRTEMU_GLint, CRTEMU_GLint, CRTEMU_GLsizei, CRTEMU_GLsizei, CRTEMU_GLenum, CRTEMU_GLenum, void const*) ) (uintptr_t) crtemu->wglGetProcAddress( "glTexSubImage2D" ); + if( !crtemu->PixelStorei ) crtemu->PixelStorei = ( void (CRTEMU_GLCALLTYPE*) (CRTEMU_GLenum, CRTEMU_GLint) ) (uintptr_t) crtemu->wglGetProcAddress( "glPixelStorei" ); if( !crtemu->ClearColor ) crtemu->ClearColor = ( void (CRTEMU_GLCALLTYPE*) (CRTEMU_GLfloat, CRTEMU_GLfloat, CRTEMU_GLfloat, CRTEMU_GLfloat) ) (uintptr_t) crtemu->wglGetProcAddress( "glClearColor" ); if( !crtemu->Clear ) crtemu->Clear = ( void (CRTEMU_GLCALLTYPE*) (CRTEMU_GLbitfield) ) (uintptr_t) crtemu->wglGetProcAddress( "glClear" ); if( !crtemu->DrawArrays ) crtemu->DrawArrays = ( void (CRTEMU_GLCALLTYPE*) (CRTEMU_GLenum, CRTEMU_GLint, CRTEMU_GLsizei) ) (uintptr_t) crtemu->wglGetProcAddress( "glDrawArrays" ); @@ -1538,6 +1591,7 @@ crtemu_t* crtemu_create( crtemu_type_t type, void* memctx ) { crtemu->DeleteShader = glDeleteShader; crtemu->DeleteProgram = glDeleteProgram; crtemu->TexSubImage2D = glTexSubImage2D; + crtemu->PixelStorei = glPixelStorei; #ifdef CRTEMU_REPORT_SHADER_ERRORS crtemu->GetShaderInfoLog = glGetShaderInfoLog; #endif @@ -1586,6 +1640,7 @@ crtemu_t* crtemu_create( crtemu_type_t type, void* memctx ) { if( !crtemu->DeleteShader ) goto failed; if( !crtemu->DeleteProgram ) goto failed; if( !crtemu->TexSubImage2D ) goto failed; + if( !crtemu->PixelStorei ) goto failed; #ifdef CRTEMU_REPORT_SHADER_ERRORS if( !crtemu->GetShaderInfoLog ) goto failed; #endif @@ -1608,6 +1663,8 @@ crtemu_t* crtemu_create( crtemu_type_t type, void* memctx ) { } break; } + crtemu_init_uniform_locations(crtemu); + crtemu->GenTextures( 1, &crtemu->accumulatetexture_a ); crtemu->GenFramebuffers( 1, &crtemu->accumulatebuffer_a ); @@ -1747,8 +1804,8 @@ static void crtemu_internal_blur( crtemu_t* crtemu, CRTEMU_GLuint source, CRTEMU crtemu->BindFramebuffer( CRTEMU_GL_FRAMEBUFFER, blurbuffer_b ); crtemu->UseProgram( crtemu->blur_shader ); - crtemu->Uniform2f( crtemu->GetUniformLocation( crtemu->blur_shader, "blur" ), r / (float) width, 0 ); - crtemu->Uniform1i( crtemu->GetUniformLocation( crtemu->blur_shader, "texture" ), 0 ); + crtemu->Uniform2f( crtemu->loc_blur_blur, r / (float) width, 0 ); + crtemu->Uniform1i( crtemu->loc_blur_texture, 0 ); crtemu->ActiveTexture( CRTEMU_GL_TEXTURE0 ); crtemu->BindTexture( CRTEMU_GL_TEXTURE_2D, source ); crtemu->TexParameteri( CRTEMU_GL_TEXTURE_2D, CRTEMU_GL_TEXTURE_MIN_FILTER, CRTEMU_GL_LINEAR ); @@ -1760,8 +1817,8 @@ static void crtemu_internal_blur( crtemu_t* crtemu, CRTEMU_GLuint source, CRTEMU crtemu->BindFramebuffer( CRTEMU_GL_FRAMEBUFFER, blurbuffer_a ); crtemu->UseProgram( crtemu->blur_shader ); - crtemu->Uniform2f( crtemu->GetUniformLocation( crtemu->blur_shader, "blur" ), 0, r / (float) height ); - crtemu->Uniform1i( crtemu->GetUniformLocation( crtemu->blur_shader, "texture" ), 0 ); + crtemu->Uniform2f( crtemu->loc_blur_blur, 0, r / (float) height ); + crtemu->Uniform1i( crtemu->loc_blur_texture, 0 ); crtemu->ActiveTexture( CRTEMU_GL_TEXTURE0 ); crtemu->BindTexture( CRTEMU_GL_TEXTURE_2D, blurtexture_b ); crtemu->TexParameteri( CRTEMU_GL_TEXTURE_2D, CRTEMU_GL_TEXTURE_MIN_FILTER, CRTEMU_GL_LINEAR ); @@ -1773,7 +1830,7 @@ static void crtemu_internal_blur( crtemu_t* crtemu, CRTEMU_GLuint source, CRTEMU } -void crtemu_present( crtemu_t* crtemu, CRTEMU_U64 time_us, CRTEMU_U32 const* pixels_xbgr, int width, int height, +void crtemu_present( crtemu_t* crtemu, CRTEMU_U64 time_us, CRTEMU_U32 const* pixels_xbgr, int width, int height, int pitch, CRTEMU_U32 mod_xbgr, CRTEMU_U32 border_xbgr ) { int viewport[ 4 ]; @@ -1784,28 +1841,23 @@ void crtemu_present( crtemu_t* crtemu, CRTEMU_U64 time_us, CRTEMU_U32 const* pix if( pixels_xbgr ) { crtemu->ActiveTexture( CRTEMU_GL_TEXTURE0 ); crtemu->BindTexture( CRTEMU_GL_TEXTURE_2D, crtemu->backbuffer ); + crtemu->PixelStorei(CRTEMU_GL_UNPACK_ROW_LENGTH, pitch / 4); crtemu->TexImage2D( CRTEMU_GL_TEXTURE_2D, 0, CRTEMU_GL_RGBA, width, height, 0, CRTEMU_GL_RGBA, CRTEMU_GL_UNSIGNED_BYTE, pixels_xbgr ); + crtemu->PixelStorei(CRTEMU_GL_UNPACK_ROW_LENGTH, 0); crtemu->BindTexture( CRTEMU_GL_TEXTURE_2D, 0 ); } crtemu->BindFramebuffer( CRTEMU_GL_FRAMEBUFFER, 0 ); crtemu->Viewport( viewport[ 0 ], viewport[ 1 ], viewport[ 2 ], viewport[ 3 ] ); crtemu->UseProgram( crtemu->copy_shader ); - crtemu->Uniform1i( crtemu->GetUniformLocation( crtemu->copy_shader, "tex0" ), 0 ); + crtemu->Uniform1i( crtemu->loc_copy_tex0, 0 ); crtemu->ActiveTexture( CRTEMU_GL_TEXTURE0 ); crtemu->BindTexture( CRTEMU_GL_TEXTURE_2D, crtemu->backbuffer ); crtemu->TexParameteri( CRTEMU_GL_TEXTURE_2D, CRTEMU_GL_TEXTURE_MIN_FILTER, CRTEMU_GL_NEAREST ); crtemu->TexParameteri( CRTEMU_GL_TEXTURE_2D, CRTEMU_GL_TEXTURE_MAG_FILTER, CRTEMU_GL_NEAREST ); - CRTEMU_GLfloat flipped_vertices[] = { - -1.0f, -1.0f, 0.0f, 1.0f, - 1.0f, -1.0f, 1.0f, 1.0f, - 1.0f, 1.0f, 1.0f, 0.0f, - -1.0f, 1.0f, 0.0f, 0.0f, - }; - crtemu->BindBuffer( CRTEMU_GL_ARRAY_BUFFER, crtemu->vertexbuffer ); - crtemu->BufferData( CRTEMU_GL_ARRAY_BUFFER, 4 * 4 * sizeof( CRTEMU_GLfloat ), flipped_vertices, CRTEMU_GL_STATIC_DRAW ); + crtemu->BindBuffer( CRTEMU_GL_ARRAY_BUFFER, crtemu->vertexbuffer_static ); crtemu->VertexAttribPointer( 0, 4, CRTEMU_GL_FLOAT, CRTEMU_GL_FALSE, 4 * sizeof( CRTEMU_GLfloat ), 0 ); crtemu->DrawArrays( CRTEMU_GL_TRIANGLE_FAN, 0, 4 ); @@ -1819,11 +1871,13 @@ void crtemu_present( crtemu_t* crtemu, CRTEMU_U64 time_us, CRTEMU_U32 const* pix if( pixels_xbgr ) { crtemu->ActiveTexture( CRTEMU_GL_TEXTURE0 ); crtemu->BindTexture( CRTEMU_GL_TEXTURE_2D, crtemu->backbuffer ); + crtemu->PixelStorei(CRTEMU_GL_UNPACK_ROW_LENGTH, pitch / 4); if (width != crtemu->last_present_width || height != crtemu->last_present_height) { crtemu->TexImage2D( CRTEMU_GL_TEXTURE_2D, 0, CRTEMU_GL_RGBA, width, height, 0, CRTEMU_GL_RGBA, CRTEMU_GL_UNSIGNED_BYTE, pixels_xbgr ); } else { crtemu->TexSubImage2D( CRTEMU_GL_TEXTURE_2D, 0, 0, 0, width, height, CRTEMU_GL_RGBA, CRTEMU_GL_UNSIGNED_BYTE, pixels_xbgr ); } + crtemu->PixelStorei(CRTEMU_GL_UNPACK_ROW_LENGTH, 0); crtemu->BindTexture( CRTEMU_GL_TEXTURE_2D, 0 ); } else { if( width != crtemu->last_present_width || height != crtemu->last_present_height ) { @@ -1837,7 +1891,7 @@ void crtemu_present( crtemu_t* crtemu, CRTEMU_U64 time_us, CRTEMU_U32 const* pix crtemu->BindFramebuffer( CRTEMU_GL_FRAMEBUFFER, crtemu->fbo_backbuffer ); crtemu->Viewport( 0, 0, width, height ); crtemu->UseProgram( crtemu->copy_shader ); - crtemu->Uniform1i( crtemu->GetUniformLocation( crtemu->copy_shader, "tex0" ), 0 ); + crtemu->Uniform1i( crtemu->loc_copy_tex0, 0 ); crtemu->BindBuffer( CRTEMU_GL_ARRAY_BUFFER, crtemu->vertexbuffer_static ); crtemu->VertexAttribPointer( 0, 4, CRTEMU_GL_FLOAT, CRTEMU_GL_FALSE, 4 * sizeof( CRTEMU_GLfloat ), 0 ); crtemu->DrawArrays( CRTEMU_GL_TRIANGLE_FAN, 0, 4 ); @@ -1854,24 +1908,27 @@ void crtemu_present( crtemu_t* crtemu, CRTEMU_U64 time_us, CRTEMU_U32 const* pix crtemu->TexImage2D( CRTEMU_GL_TEXTURE_2D, 0, CRTEMU_GL_RGB, width, height, 0, CRTEMU_GL_RGB, CRTEMU_GL_UNSIGNED_BYTE, 0 ); crtemu->BindFramebuffer( CRTEMU_GL_FRAMEBUFFER, crtemu->accumulatebuffer_a ); crtemu->FramebufferTexture2D( CRTEMU_GL_FRAMEBUFFER, CRTEMU_GL_COLOR_ATTACHMENT0, CRTEMU_GL_TEXTURE_2D, crtemu->accumulatetexture_a, 0 ); - crtemu->BindFramebuffer( CRTEMU_GL_FRAMEBUFFER, 0 ); + crtemu->ClearColor( 0, 0, 0, 0 ); + crtemu->Clear( CRTEMU_GL_COLOR_BUFFER_BIT ); crtemu->BindTexture( CRTEMU_GL_TEXTURE_2D, crtemu->accumulatetexture_b ); crtemu->TexImage2D( CRTEMU_GL_TEXTURE_2D, 0, CRTEMU_GL_RGB, width, height, 0, CRTEMU_GL_RGB, CRTEMU_GL_UNSIGNED_BYTE, 0 ); crtemu->BindFramebuffer( CRTEMU_GL_FRAMEBUFFER, crtemu->accumulatebuffer_b ); crtemu->FramebufferTexture2D( CRTEMU_GL_FRAMEBUFFER, CRTEMU_GL_COLOR_ATTACHMENT0, CRTEMU_GL_TEXTURE_2D, crtemu->accumulatetexture_b, 0 ); - crtemu->BindFramebuffer( CRTEMU_GL_FRAMEBUFFER, 0 ); + crtemu->Clear( CRTEMU_GL_COLOR_BUFFER_BIT ); crtemu->BindTexture( CRTEMU_GL_TEXTURE_2D, crtemu->blurtexture_a ); crtemu->TexImage2D( CRTEMU_GL_TEXTURE_2D, 0, CRTEMU_GL_RGB, width, height, 0, CRTEMU_GL_RGB, CRTEMU_GL_UNSIGNED_BYTE, 0 ); crtemu->BindFramebuffer( CRTEMU_GL_FRAMEBUFFER, crtemu->blurbuffer_a ); crtemu->FramebufferTexture2D( CRTEMU_GL_FRAMEBUFFER, CRTEMU_GL_COLOR_ATTACHMENT0, CRTEMU_GL_TEXTURE_2D, crtemu->blurtexture_a, 0 ); - crtemu->BindFramebuffer( CRTEMU_GL_FRAMEBUFFER, 0 ); + crtemu->Clear( CRTEMU_GL_COLOR_BUFFER_BIT ); crtemu->BindTexture( CRTEMU_GL_TEXTURE_2D, crtemu->blurtexture_b ); crtemu->TexImage2D( CRTEMU_GL_TEXTURE_2D, 0, CRTEMU_GL_RGB, width, height, 0, CRTEMU_GL_RGB, CRTEMU_GL_UNSIGNED_BYTE, 0 ); crtemu->BindFramebuffer( CRTEMU_GL_FRAMEBUFFER, crtemu->blurbuffer_b ); crtemu->FramebufferTexture2D( CRTEMU_GL_FRAMEBUFFER, CRTEMU_GL_COLOR_ATTACHMENT0, CRTEMU_GL_TEXTURE_2D, crtemu->blurtexture_b, 0 ); + crtemu->Clear( CRTEMU_GL_COLOR_BUFFER_BIT ); + crtemu->BindFramebuffer( CRTEMU_GL_FRAMEBUFFER, 0 ); } @@ -1893,9 +1950,9 @@ void crtemu_present( crtemu_t* crtemu, CRTEMU_U64 time_us, CRTEMU_U32 const* pix // Update accumulation buffer crtemu->BindFramebuffer( CRTEMU_GL_FRAMEBUFFER, crtemu->accumulatebuffer_a ); crtemu->UseProgram( crtemu->accumulate_shader ); - crtemu->Uniform1i( crtemu->GetUniformLocation( crtemu->accumulate_shader, "tex0" ), 0 ); - crtemu->Uniform1i( crtemu->GetUniformLocation( crtemu->accumulate_shader, "tex1" ), 1 ); - crtemu->Uniform1f( crtemu->GetUniformLocation( crtemu->accumulate_shader, "modulate" ), 1.0f ); + crtemu->Uniform1i( crtemu->loc_accumulate_tex0, 0 ); + crtemu->Uniform1i( crtemu->loc_accumulate_tex1, 1 ); + crtemu->Uniform1f( crtemu->loc_accumulate_modulate, 1.0f ); crtemu->ActiveTexture( CRTEMU_GL_TEXTURE0 ); crtemu->BindTexture( CRTEMU_GL_TEXTURE_2D, crtemu->backbuffer ); crtemu->TexParameteri( CRTEMU_GL_TEXTURE_2D, CRTEMU_GL_TEXTURE_MIN_FILTER, CRTEMU_GL_LINEAR ); @@ -1915,7 +1972,7 @@ void crtemu_present( crtemu_t* crtemu, CRTEMU_U64 time_us, CRTEMU_U32 const* pix // Store a copy of the accumulation buffer crtemu->BindFramebuffer( CRTEMU_GL_FRAMEBUFFER, crtemu->accumulatebuffer_b ); crtemu->UseProgram( crtemu->copy_shader ); - crtemu->Uniform1i( crtemu->GetUniformLocation( crtemu->copy_shader, "tex0" ), 0 ); + crtemu->Uniform1i( crtemu->loc_copy_tex0, 0 ); crtemu->ActiveTexture( CRTEMU_GL_TEXTURE0 ); crtemu->BindTexture( CRTEMU_GL_TEXTURE_2D, crtemu->accumulatetexture_a ); crtemu->TexParameteri( CRTEMU_GL_TEXTURE_2D, CRTEMU_GL_TEXTURE_MIN_FILTER, CRTEMU_GL_LINEAR ); @@ -1928,9 +1985,9 @@ void crtemu_present( crtemu_t* crtemu, CRTEMU_U64 time_us, CRTEMU_U32 const* pix // Blend accumulation and backbuffer crtemu->BindFramebuffer( CRTEMU_GL_FRAMEBUFFER, crtemu->accumulatebuffer_a ); crtemu->UseProgram( crtemu->blend_shader ); - crtemu->Uniform1i( crtemu->GetUniformLocation( crtemu->blend_shader, "tex0" ), 0 ); - crtemu->Uniform1i( crtemu->GetUniformLocation( crtemu->blend_shader, "tex1" ), 1 ); - crtemu->Uniform1f( crtemu->GetUniformLocation( crtemu->blend_shader, "modulate" ), 1.0f ); + crtemu->Uniform1i( crtemu->loc_blend_tex0, 0 ); + crtemu->Uniform1i( crtemu->loc_blend_tex1, 1 ); + crtemu->Uniform1f( crtemu->loc_blend_modulate, 1.0f ); crtemu->ActiveTexture( CRTEMU_GL_TEXTURE0 ); crtemu->BindTexture( CRTEMU_GL_TEXTURE_2D, crtemu->backbuffer ); crtemu->ActiveTexture( CRTEMU_GL_TEXTURE1 ); @@ -2004,30 +2061,30 @@ void crtemu_present( crtemu_t* crtemu, CRTEMU_U64 time_us, CRTEMU_U32 const* pix crtemu->VertexAttribPointer( 0, 4, CRTEMU_GL_FLOAT, CRTEMU_GL_FALSE, 4 * sizeof( CRTEMU_GLfloat ), 0 ); crtemu->BufferData( CRTEMU_GL_ARRAY_BUFFER, 4 * 4 * sizeof( CRTEMU_GLfloat ), screen_vertices, CRTEMU_GL_STATIC_DRAW ); - float b = ( ( border_xbgr >> 16 ) & 0xff ) / 255.0f; - float g = ( ( border_xbgr >> 8 ) & 0xff ) / 255.0f; float r = ( ( border_xbgr ) & 0xff ) / 255.0f; + float g = ( ( border_xbgr >> 8 ) & 0xff ) / 255.0f; + float b = ( ( border_xbgr >> 16 ) & 0xff ) / 255.0f; crtemu->ClearColor( r, g, b, 1.0f ); crtemu->Clear( CRTEMU_GL_COLOR_BUFFER_BIT ); crtemu->UseProgram( crtemu->crt_shader ); + crtemu->Uniform1i( crtemu->loc_crt_backbuffer, 0 ); + crtemu->Uniform1i( crtemu->loc_crt_blurbuffer, 1 ); + crtemu->Uniform1i( crtemu->loc_crt_frametexture, 2 ); + crtemu->Uniform1f( crtemu->loc_crt_use_frame, crtemu->use_frame ); + crtemu->Uniform1f( crtemu->loc_crt_time, 1.5f * (CRTEMU_GLfloat)( ( (double) time_us ) / 1000000.0 ) ); + crtemu->Uniform2f( crtemu->loc_crt_resolution, (float) window_width, (float) window_height ); - crtemu->Uniform1i( crtemu->GetUniformLocation( crtemu->crt_shader, "backbuffer" ), 0 ); - crtemu->Uniform1i( crtemu->GetUniformLocation( crtemu->crt_shader, "blurbuffer" ), 1 ); - crtemu->Uniform1i( crtemu->GetUniformLocation( crtemu->crt_shader, "frametexture" ), 2 ); - crtemu->Uniform1f( crtemu->GetUniformLocation( crtemu->crt_shader, "use_frame" ), crtemu->use_frame ); - crtemu->Uniform1f( crtemu->GetUniformLocation( crtemu->crt_shader, "time" ), 1.5f * (CRTEMU_GLfloat)( ( (double) time_us ) / 1000000.0 ) ); - crtemu->Uniform2f( crtemu->GetUniformLocation( crtemu->crt_shader, "resolution" ), (float) window_width, (float) window_height ); if( crtemu->type == CRTEMU_TYPE_LITE ) { - crtemu->Uniform2f( crtemu->GetUniformLocation( crtemu->crt_shader, "size" ), (float)( target_width / 2 >= width ? width : target_width / 2 ), (float) ( target_height / 2 >= height ? height : target_height / 2 ) ); + crtemu->Uniform2f( crtemu->loc_crt_size, (float)( target_width / 2 >= width ? width : target_width / 2 ), (float) ( target_height / 2 >= height ? height : target_height / 2 ) ); } else { - crtemu->Uniform2f( crtemu->GetUniformLocation( crtemu->crt_shader, "size" ), (float) target_width, (float) target_height ); + crtemu->Uniform2f( crtemu->loc_crt_size, (float) target_width, (float) target_height ); } - float mod_r = ( ( mod_xbgr >> 16 ) & 0xff ) / 255.0f; + float mod_r = ( ( mod_xbgr ) & 0xff ) / 255.0f; float mod_g = ( ( mod_xbgr >> 8 ) & 0xff ) / 255.0f; - float mod_b = ( ( mod_xbgr ) & 0xff ) / 255.0f; - crtemu->Uniform3f( crtemu->GetUniformLocation( crtemu->crt_shader, "modulate" ), mod_r, mod_g, mod_b ); + float mod_b = ( ( mod_xbgr >> 16 ) & 0xff ) / 255.0f; + crtemu->Uniform3f( crtemu->loc_crt_modulate, mod_r, mod_g, mod_b ); float color[] = { 0.0f, 0.0f, 0.0f, 0.0f }; @@ -2197,6 +2254,55 @@ void crtemu_coordinates_window_to_bitmap( crtemu_t* crtemu, int width, int heigh *x = (int) ( xp ); *y = (int) ( yp ); } break; + case CRTEMU_TYPE_1084: { + CRTEMU_GLint viewport[ 4 ]; + crtemu->GetIntegerv( CRTEMU_GL_VIEWPORT, viewport ); + + int window_width = viewport[ 2 ] - viewport[ 0 ]; + int window_height = viewport[ 3 ] - viewport[ 1 ]; + + int aspect_width = (int)( ( window_height * 4 ) / 3 ); + int aspect_height= (int)( ( window_width * 3 ) / 4 ); + int target_width, target_height; + if( aspect_height <= window_height ) { + target_width = window_width; + target_height = aspect_height; + } else { + target_width = aspect_width; + target_height = window_height; + } + + float hscale = target_width / (float) width; + float vscale = target_height / (float) height; + + float hborder = ( window_width - hscale * width ) / 2.0f; + float vborder = ( window_height - vscale * height ) / 2.0f; + + float xp = ( ( *x - hborder ) / hscale ) / (float) width; + float yp = ( ( *y - vborder ) / vscale ) / (float) height; + + /* TODO: Common params for shader and this */ + float xc = ( xp - 0.5f ) * 2.0f; + float yc = ( yp - 0.5f ) * 2.0f; + xc *= 1.1f; + yc *= 1.1f; + float yt = ( yc >= 0.0f ? yc : -yc ) / 6.0f; + float xt = ( xc >= 0.0f ? xc : -xc ) / 5.0f; + xc *= 1.0f + ( yt * yt ); + yc *= 1.0f + ( xt * xt ); + xc = ( xc / 2.0f ) + 0.5f; + yc = ( yc / 2.0f ) + 0.5f; + xc = xc * 0.92f + 0.04f; + yc = yc * 0.92f + 0.04f; + xp = xc * 0.35f + xp * 0.65f; + yp = yc * 0.35f + yp * 0.65f; + + xp *= width; + yp *= height; + + *x = (int) ( xp ); + *y = (int) ( yp ); + } break; case CRTEMU_TYPE_LITE: { CRTEMU_GLint viewport[ 4 ]; crtemu->GetIntegerv( CRTEMU_GL_VIEWPORT, viewport );