diff options
-rw-r--r-- | config.c | 32 | ||||
-rw-r--r-- | gl.c | 54 | ||||
-rw-r--r-- | main.c | 10 | ||||
-rw-r--r-- | ted.cfg | 4 | ||||
-rw-r--r-- | ted.h | 17 | ||||
-rw-r--r-- | util.c | 1 |
6 files changed, 111 insertions, 7 deletions
@@ -45,6 +45,7 @@ static void settings_copy(Settings *dest, const Settings *src) { *dest = *src; gl_rc_sab_incref(dest->bg_shader); + gl_rc_texture_incref(dest->bg_texture); context_copy(&dest->context, &src->context); for (u32 i = 0; i < LANG_COUNT; ++i) { @@ -258,7 +259,7 @@ static OptionU8 const options_u8[] = { {"tags-max-depth", &options_zero.tags_max_depth, 1, 100, false}, }; static OptionU16 const options_u16[] = { - {"text-size", &options_zero.text_size, TEXT_SIZE_MIN, TEXT_SIZE_MAX, true}, + {"text-size", &options_zero.text_size, TEXT_SIZE_MIN, TEXT_SIZE_MAX, false}, {"max-menu-width", &options_zero.max_menu_width, 10, U16_MAX, false}, {"error-display-time", &options_zero.error_display_time, 0, U16_MAX, false}, }; @@ -573,10 +574,11 @@ void main() { \n\ uniform float t_time;\n\ uniform float t_save_time;\n\ uniform vec2 t_aspect;\n\ +uniform sampler2D t_texture;\n\ #line 1\n\ %s", s->bg_shader_text); - if (s->bg_shader) gl_rc_sab_decref(&s->bg_shader); + gl_rc_sab_decref(&s->bg_shader); GLuint shader = gl_compile_and_link_shaders(ted->error, vshader, fshader); if (shader) { @@ -607,6 +609,26 @@ uniform vec2 t_aspect;\n\ } } +static void settings_load_bg_texture(Ted *ted, Settings *s) { + gl_rc_texture_decref(&s->bg_texture); + + const char *path = s->bg_shader_image; + char expanded[TED_PATH_MAX]; + expanded[0] = '\0'; + if (path[0] == '~') { + strbuf_cpy(expanded, ted->home); + ++path; + } + strbuf_cat(expanded, path); + + GLuint texture = gl_load_texture_from_image(expanded); + if (texture) { + s->bg_texture = gl_rc_texture_new(texture); + } else { + ted_seterr(ted, "Couldn't load image %s", path); + } +} + // reads a single "line" of the config file, but it may include a multiline string, // so it may read multiple lines. static void config_parse_line(ConfigReader *cfg, Settings *settings, const ConfigPart *part, char **pline) { @@ -832,9 +854,10 @@ static void config_parse_line(ConfigReader *cfg, Settings *settings, const Confi } } - if (streq(key, "bg-shader")) { + if (streq(key, "bg-shader")) settings_load_bg_shader(ted, settings); - } + if (streq(key, "bg-texture")) + settings_load_bg_texture(ted, settings); // this is probably a bad idea: //if (!recognized) @@ -952,6 +975,7 @@ void config_free(Ted *ted) { for (u32 i = 0; i < LANG_COUNT; ++i) free(settings->language_extensions[i]); gl_rc_sab_decref(&settings->bg_shader); + gl_rc_texture_decref(&settings->bg_texture); } @@ -5,6 +5,7 @@ do(DRAWARRAYS, DrawArrays)\ do(GENTEXTURES, GenTextures)\ do(DELETETEXTURES, DeleteTextures)\ + do(GENERATEMIPMAP, GenerateMipmap)\ do(TEXIMAGE2D, TexImage2D)\ do(BINDTEXTURE, BindTexture)\ do(TEXPARAMETERI, TexParameteri)\ @@ -87,6 +88,29 @@ void gl_rc_sab_decref(GlRcSAB **ps) { *ps = NULL; } +GlRcTexture *gl_rc_texture_new(GLuint texture) { + GlRcTexture *t = calloc(1, sizeof *t); + t->ref_count = 1; + t->texture = texture; + return t; +} + +void gl_rc_texture_incref(GlRcTexture *t) { + if (!t) return; + ++t->ref_count; +} + +void gl_rc_texture_decref(GlRcTexture **pt) { + GlRcTexture *t = *pt; + if (!t) return; + if (--t->ref_count == 0) { + debug_println("Delete texture %u", t->texture); + glDeleteTextures(1, &t->texture); + free(t); + } + *pt = NULL; +} + // set by main() static int gl_version_major; static int gl_version_minor; @@ -305,3 +329,33 @@ static void gl_geometry_draw(void) { arr_clear(gl_geometry_triangles); } + +static GLuint gl_load_texture_from_image(const char *path) { + GLuint texture = 0; + int w=0, h=0, n=0; + unsigned char *data = stbi_load(path, &w, &h, &n, 4); + if (data && w > 0 && h > 0) { + size_t bytes_per_row = (size_t)w * 4; + unsigned char *tempbuf = calloc(1, bytes_per_row); + // flip image vertically + for (int y = 0; y < h/2; ++y) { + int y2 = h-1-y; + unsigned char *row1 = &data[y*w*4]; + unsigned char *row2 = &data[y2*w*4]; + memcpy(tempbuf, row1, bytes_per_row); + memcpy(row1, row2, bytes_per_row); + memcpy(row2, tempbuf, bytes_per_row); + } + free(tempbuf); + + glGenTextures(1, &texture); + glBindTexture(GL_TEXTURE_2D, texture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + free(data); + } + return texture; +} @@ -1,6 +1,5 @@ /* FUTURE FEATURES: -- custom shaders texture - config variables - plugins? */ @@ -811,6 +810,15 @@ int main(int argc, char **argv) { glUniform1f(glGetUniformLocation(shader, "t_time"), (float)fmod(t - start_time, 3600)); glUniform2f(glGetUniformLocation(shader, "t_aspect"), (float)window_width / (float)window_height, 1); glUniform1f(glGetUniformLocation(shader, "t_save_time"), (float)(t - ted->last_save_time)); + if (s->bg_texture) { + GLuint texture = s->bg_texture->texture; + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, texture); + glUniform1i(glGetUniformLocation(shader, "t_texture"), 0); + } else { + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, 0); + } glBindBuffer(GL_ARRAY_BUFFER, buffer); if (!array) { GLuint v_pos = (GLuint)glGetAttribLocation(shader, "v_pos"); @@ -42,6 +42,7 @@ regenerate-tags-if-not-found = yes # t_aspect - (window_width / window_height, 1) # t_time - current time in seconds, modulo 3600 # t_save_time - time since last save, in seconds. +# t_texture - a texture, made with your choice of image # if you want your shader supported no matter what, only use stuff from GLSL version 110. # on non-ancient computers, you should be able to use GLSL version 130. # bg-shader = "void main() { @@ -51,6 +52,9 @@ regenerate-tags-if-not-found = yes # vec4 color = vec4(r, g, 1.0 - t_save_time, 0.2); # gl_FragColor = clamp(color, 0.0, 1.0); # }" +# here's an example using a texture: +# bg-texture = "/path/to/my/cool/picture.jpg" (or .png, .gif, .bmp) +# bg-shader = "void main() { gl_FragColor = texture2D(t_texture, t_pos) * vec4(1.0,1.0,1.0,0.2); }" [keyboard] # motion and selection @@ -143,9 +143,21 @@ typedef struct { char *path; // these settings apply to all paths which start with this string, or all paths if path=NULL } SettingsContext; -// shader-array-buffer combo. // need to use refcounting for this because of Settings. -// (we copy parent settings to children) +// => we copy parent settings to children +// e.g. +// [core] +// bg-texture = "blablabla.png" +// [Javascript.core] +// some random shit +// the main Settings' bg_texture will get copied to javascript's Settings, +// so we need to be extra careful about when we delete textures. +typedef struct { + u32 ref_count; + u32 texture; +} GlRcTexture; + +// shader-array-buffer combo. typedef struct { u32 ref_count; u32 shader; @@ -177,6 +189,7 @@ typedef struct { u8 scrolloff; u8 tags_max_depth; GlRcSAB *bg_shader; + GlRcTexture *bg_texture; char bg_shader_text[4096]; char bg_shader_image[TED_PATH_MAX]; char build_default_command[256]; @@ -150,6 +150,7 @@ static void str_cpy(char *dst, size_t dst_sz, char const *src) { } #define strbuf_cpy(dst, src) str_cpy(dst, sizeof dst, src) +#define strbuf_cat(dst, src) str_cat(dst, sizeof dst, src) // advances str to the start of the next UTF8 character static void utf8_next_char_const(char const **str) { |