void tex_gen_checkerboard(byte *data, int w, int h, uint color1, uint color2) { byte color[8]; color1 = (color1 << 8) | (color1 >> 24); color2 = (color2 << 8) | (color2 >> 24); for(int z = 0; z < 4; z++) { color[z] = (uint)((color1 >> ((3 - z) * 8)) & 0xff); color[z+4] = (uint)((color2 >> ((3 - z) * 8)) & 0xff); } for(int x = 0; x < w; x++) { for(int y = 0; y < h; y++) { memcpy(&data[(y*w+x) << 2], &color[((x & 1) ^ (y & 1)) << 2], 4); } } } float tri(float x, float p, float a) { return ((4.0f * a) / p) * fabsf(fmodf(x - p / 4.0f, p) - p / 2.0f) - a; } void tex_gen_triwave(byte *data, int w, int h, uint color1, uint color2, uint color3, uint color4, uint color5, uint color6) { byte color[16]; color1 = (color1 << 8) | (color1 >> 24); color2 = (color2 << 8) | (color2 >> 24); color3 = (color3 << 8) | (color3 >> 24); color4 = (color4 << 8) | (color4 >> 24); color5 = (color5 << 8) | (color5 >> 24); color6 = (color6 << 8) | (color6 >> 24); for(int z = 0; z < 4; z++) { color[z] = (uint)((color1 >> ((3 - z) * 8)) & 0xff); color[z+4] = (uint)((color2 >> ((3 - z) * 8)) & 0xff); color[z+8] = (uint)((color3 >> ((3 - z) * 8)) & 0xff); color[z+12] = (uint)((color4 >> ((3 - z) * 8)) & 0xff); color[z+16] = (uint)((color5 >> ((3 - z) * 8)) & 0xff); color[z+20] = (uint)((color6 >> ((3 - z) * 8)) & 0xff); } for(int y = 0; y < h; y++) { int offs = ((y / 2 == h / 16) || (y / 2 == (h / 2 - h / 16) - 1)) ? 16 : (((y / 2 == h / 16 + 1) || (y / 2 == (h / 2 - h / 16) - 2)) ? 20 : 0); for(int x = 0; x < w; x++) { memcpy(&data[(y*w+x) << 2], &color[offs], 4); } } for(int x = 0; x < w; x++) { int y = ((x < w / 8) || (x >= w - w / 8)) ? (h / 2) : (h / 2 + (int)tri((float)(w / 4 + x), (float)((w * 3) / 4), (float)(((h * 3) / 4) / 2))); sys_assert(y - 8 >= 0 && y + 7 < h) memcpy(&data[((y-8)*w+x) << 2], &color[0], 4); memcpy(&data[((y-7)*w+x) << 2], &color[0], 4); memcpy(&data[((y-6)*w+x) << 2], &color[0], 4); memcpy(&data[((y-5)*w+x) << 2], &color[4], 4); memcpy(&data[((y-4)*w+x) << 2], &color[4], 4); memcpy(&data[((y-3)*w+x) << 2], &color[0], 4); memcpy(&data[((y-2)*w+x) << 2], &color[0], 4); memcpy(&data[((y-1)*w+x) << 2], &color[8], 4); memcpy(&data[((y+0)*w+x) << 2], &color[8], 4); memcpy(&data[((y+1)*w+x) << 2], &color[0], 4); memcpy(&data[((y+2)*w+x) << 2], &color[0], 4); memcpy(&data[((y+3)*w+x) << 2], &color[12], 4); memcpy(&data[((y+4)*w+x) << 2], &color[12], 4); memcpy(&data[((y+5)*w+x) << 2], &color[0], 4); memcpy(&data[((y+6)*w+x) << 2], &color[0], 4); memcpy(&data[((y+7)*w+x) << 2], &color[0], 4); } } /* void tex_pack_1bit(const byte *data, byte *pack, uint size) { for(int z = 0; z < size; z++) { pack[z >> 3] &= ~(1 << (z & 7)); if(data[(z << 2) | 3]) pack[z >> 3] |= 1 << (z & 7); } } void tex_unpack_1bit(byte *data, const byte *pack, uint size) { for(int z = 0; z < size; z++) { memset(&data[z << 2], (pack[z >> 3] & (1 << (z & 7))) ? 0xff : 0x00, 4); } } */ void tex_param(tex_t *tex) { byte mipmap = (gdr.tex_miptype < tex->mipmap) ? gdr.tex_miptype : tex->mipmap; glBindTexture(GL_TEXTURE_2D, *(tex->id)); // glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); // glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (gdr.tex_filter && tex->filter) ? (mipmap ? ((mipmap == 1) ? GL_LINEAR_MIPMAP_NEAREST : GL_LINEAR_MIPMAP_LINEAR) : GL_LINEAR) : (mipmap ? ((mipmap == 1) ? GL_NEAREST_MIPMAP_NEAREST : GL_NEAREST_MIPMAP_LINEAR) : GL_NEAREST)); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, (gdr.tex_filter && tex->filter) ? GL_LINEAR : GL_NEAREST); // if(gdr.aniso_avail) glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY, tex->filter ? MIN_VALUE(gdr.tex_aniso, gdr.aniso_max) : 1.0f); } int tex_config() { int done = 0; ulong iter = 0; tex_t *tex; while(tex = tbl_iter(&gdr.textures, &iter)) { tex_param(tex); done += 1; } logd(LOG_GFX, STR_TEX_PARAMS, done); return done; } void tex_delete(uint *tex) { // uint *tex; // for(int z = 0; z < gdr.tex_loaded; z++) { // tex = gdr.textures[z]; if(*tex) { glDeleteTextures(1, tex); logd(LOG_GFX, STR_TEX_IUNLOADED, *tex); *tex = 0; } // } } byte tex_unload(tex_t *tex) { if(!(*(tex->id))) { return 0; } glDeleteTextures(1, tex->id); gdr.tex_mem -= tex->width * tex->height * 4; logd(LOG_GFX, STR_TEX_UNLOADED, tex->path ? tex->path : "[built-in]", *(tex->id)); *(tex->id) = 0; return 1; } void tex_remove(tex_t *tex) { if(tex_unload(tex)) { // tbl_pop(&gdr.textures, tex); gdr.tex_loaded -= 1; } } int tex_clear() { return tbl_clear_func(&gdr.textures, (clear_func*)tex_remove); } byte *img_load(const char *filename, int *width, int *height, byte flip, byte ignmiss) { byte *data; int err; FILE *fd; if(!(fd = fopen(filename, "rb"))) { err = errno; if(err != ENOENT || !ignmiss) loge(LOG_GFX, STR_TEX_EOPEN, filename, strerror(err), err); return 0; } data = stbi_load(fd, width, height, flip); fclose(fd); if(!data) { loge(LOG_GFX, STR_TEX_ELOAD, filename, stbi_failure_desc(), stbi_failure_reason()); } return data; } void tex_push(uint *tex, const byte *data, int width, int height) { glGenTextures(1, tex); glBindTexture(GL_TEXTURE_2D, *tex); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); gdr.tex_mem += width * height * 4; gdr.tex_peak = gdr.tex_mem > gdr.tex_peak ? gdr.tex_mem : gdr.tex_peak; glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); } void tex_gen_fallback(uint *id, int size) { byte *data = mem_alloc(size * size * 4, MEM_IMAGE); tex_gen_checkerboard(data, size, size, 0xff000000, 0xffff00ff); tex_push(id, data, size, size); mem_free(data); logd(LOG_GFX, STR_TEX_ILOADED, "fallback", *id); } void tex_push_par(uint *tex, const byte *data, const char *filename, int width, int height, byte filter, byte mipmap) { tex_t *bind; sys_assert(bind = (tex_t*)tbl_push(&gdr.textures)); tex_push(tex, data, width, height); glGenerateMipmap(GL_TEXTURE_2D); // mem_free(data); bind->id = tex; bind->width = width; bind->height = height; bind->filter = filter; bind->mipmap = mipmap; bind->path = filename; tex_param(bind); } /* byte tex_load_sys(uint *tex, const char *filename, byte flip) { byte *data; int width, height; if(!(data = img_load(filename, &width, &height, flip, 0))) { *tex = sys.tex_fallback; return 0; } tex_push(tex, data, width, height); mem_free(data); logd(LOG_GFX, STR_TEX_ILOADED, filename, *tex); return 1; } */ byte tex_load(uint *tex, const char *filename, byte flags) { byte *data; int width, height; if(!(data = img_load(filename, &width, &height, (flags & TEX_FLAG_FLIP) != 0, 0))) { *tex = gdr.tex_fallback; return 0; } tex_push_par(tex, data, filename, width, height, (flags & TEX_FLAG_FILTER) != 0, ((flags & TEX_FLAG_MIPMAP) != 0) ? 2 : ((flags & TEX_FLAG_NMIPMAP) != 0)); gdr.tex_loaded += 1; mem_free(data); logd(LOG_GFX, STR_TEX_LOADED, filename, *tex); return 1; } byte img_save(const char *filename, int x, int y, byte alpha, byte flip, int stride, const byte *data) { int len; byte *png; int err; FILE *fd; if(!(png = stbi_write_png_to_mem(data, stride, x, y, alpha ? 4 : 3, &len, flip))) { loge(LOG_GFX, STR_TEX_ESAVE, filename); return 0; } if(!(fd = fopen(filename, "wb"))) { err = errno; loge(LOG_GFX, STR_TEX_EWOPEN, filename, strerror(err), err); mem_free(png); return 0; } if(fwrite(png, 1, len, fd) != len) { loge(LOG_GFX, STR_TEX_EWRITE, filename); fclose(fd); mem_free(png); return 0; } fclose(fd); mem_free(png); return 1; }