nuclear@1: #include nuclear@1: #include nuclear@1: #include nuclear@1: #include nuclear@11: #include nuclear@1: #include nuclear@1: #include nuclear@1: #include "opengl.h" nuclear@1: #include "thumbs.h" nuclear@11: #include "resman.h" nuclear@1: nuclear@11: static int load_res_texture(const char *fname, int id, void *cls); nuclear@11: static int done_res_texture(int id, void *cls); nuclear@11: static void free_res_texture(int id, void *cls); nuclear@11: nuclear@20: struct resman *texman; nuclear@20: int dbg_load_async = 1; nuclear@11: nuclear@1: struct thumbnail *create_thumbs(const char *dirpath) nuclear@1: { nuclear@1: DIR *dir; nuclear@1: struct dirent *dent; nuclear@13: /* allocate dummy head node */ nuclear@13: struct thumbnail *list = calloc(1, sizeof *list); nuclear@21: char *env; nuclear@21: nuclear@21: if((env = getenv("RESMAN_LOAD_ASYNC"))) { nuclear@21: dbg_load_async = atoi(env); nuclear@21: } nuclear@1: nuclear@11: if(!texman) { nuclear@11: texman = resman_create(); nuclear@11: resman_set_load_func(texman, load_res_texture, 0); nuclear@11: resman_set_done_func(texman, done_res_texture, 0); nuclear@11: resman_set_destroy_func(texman, free_res_texture, 0); nuclear@2: } nuclear@2: nuclear@1: if(!(dir = opendir(dirpath))) { nuclear@1: fprintf(stderr, "failed to open directory: %s: %s\n", dirpath, strerror(errno)); nuclear@1: return 0; nuclear@1: } nuclear@1: nuclear@1: while((dent = readdir(dir))) { nuclear@18: struct img_pixmap img; nuclear@1: struct thumbnail *node; nuclear@1: nuclear@1: if(!(node = malloc(sizeof *node))) { nuclear@1: perror("failed to allocate thumbnail list node"); nuclear@1: continue; nuclear@1: } nuclear@11: memset(node, 0, sizeof *node); nuclear@1: nuclear@1: if(!(node->fname = malloc(strlen(dirpath) + strlen(dent->d_name) + 2))) { nuclear@1: free(node); nuclear@1: continue; nuclear@1: } nuclear@1: strcpy(node->fname, dirpath); nuclear@1: if(dirpath[strlen(dirpath) - 1] != '/') { nuclear@1: strcat(node->fname, "/"); nuclear@1: } nuclear@1: strcat(node->fname, dent->d_name); nuclear@1: nuclear@16: node->aspect = 1.0; nuclear@11: nuclear@20: if(dbg_load_async) { nuclear@20: resman_lookup(texman, node->fname, node); nuclear@20: } else { nuclear@20: img_init(&img); nuclear@20: if(img_load(&img, node->fname) == -1) { nuclear@20: img_destroy(&img); nuclear@20: free(node->fname); nuclear@20: free(node); nuclear@20: continue; nuclear@20: } nuclear@20: nuclear@20: printf("loaded image: %s\n", node->fname); nuclear@20: nuclear@20: node->aspect = (float)img.width / (float)img.height; nuclear@20: nuclear@20: glGenTextures(1, &node->tex); nuclear@20: glBindTexture(GL_TEXTURE_2D, node->tex); nuclear@20: glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); nuclear@20: glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); nuclear@20: glTexImage2D(GL_TEXTURE_2D, 0, img_glintfmt(&img), img.width, img.height, 0, img_glfmt(&img), img_gltype(&img), img.pixels); nuclear@18: img_destroy(&img); nuclear@20: nuclear@20: node->prev = list; nuclear@20: node->next = list->next; nuclear@20: if(list->next) list->next->prev = node; nuclear@20: list->next = node; nuclear@1: } nuclear@20: node->list = list; nuclear@21: node->load_count = 0; nuclear@1: } nuclear@1: closedir(dir); nuclear@1: nuclear@1: return list; nuclear@1: } nuclear@1: nuclear@1: void free_thumbs(struct thumbnail *thumbs) nuclear@1: { nuclear@1: if(!thumbs) return; nuclear@1: nuclear@1: while(thumbs) { nuclear@1: struct thumbnail *tmp = thumbs; nuclear@1: thumbs = thumbs->next; nuclear@1: nuclear@1: free(tmp->fname); nuclear@1: free(tmp); nuclear@1: } nuclear@1: } nuclear@1: nuclear@1: nuclear@11: void update_thumbs(void) nuclear@11: { nuclear@11: resman_poll(texman); nuclear@11: } nuclear@11: nuclear@1: void draw_thumbs(struct thumbnail *thumbs, float thumb_sz, float start_y) nuclear@1: { nuclear@1: int vp[4]; nuclear@1: float gap = thumb_sz / 4.0; nuclear@1: float x = gap; nuclear@1: float y = gap + start_y; nuclear@1: float view_aspect; nuclear@1: nuclear@1: glGetIntegerv(GL_VIEWPORT, vp); nuclear@14: view_aspect = (float)(vp[2] - vp[0]) / (float)(vp[3] - vp[1]); nuclear@1: nuclear@1: glMatrixMode(GL_PROJECTION); nuclear@1: glPushMatrix(); nuclear@1: glLoadIdentity(); nuclear@1: glOrtho(0, 1, 1.0 / view_aspect, 0, -1, 1); nuclear@1: nuclear@1: glMatrixMode(GL_MODELVIEW); nuclear@1: nuclear@13: thumbs = thumbs->next; /* skip dummy node */ nuclear@1: while(thumbs) { nuclear@1: glPushMatrix(); nuclear@1: glTranslatef(x, y, 0); nuclear@1: nuclear@1: glScalef(thumb_sz, thumb_sz, 1); nuclear@1: nuclear@1: glBegin(GL_QUADS); nuclear@1: glColor3f(0.25, 0.25, 0.25); nuclear@1: glTexCoord2f(0, 0); glVertex2f(0, 0); nuclear@1: glTexCoord2f(1, 0); glVertex2f(1, 0); nuclear@1: glTexCoord2f(1, 1); glVertex2f(1, 1); nuclear@1: glTexCoord2f(0, 1); glVertex2f(0, 1); nuclear@1: glEnd(); nuclear@1: nuclear@12: if(thumbs->tex) { nuclear@12: if(thumbs->aspect >= 1.0) { nuclear@12: glTranslatef(0, 0.5 - 0.5 / thumbs->aspect, 0); nuclear@12: glScalef(1, 1.0 / thumbs->aspect, 1); nuclear@12: } else { nuclear@12: glTranslatef(0.5 - thumbs->aspect / 2.0, 0, 0); nuclear@12: glScalef(thumbs->aspect, 1, 1); nuclear@12: } nuclear@12: nuclear@12: if(glIsTexture(thumbs->tex)) { nuclear@12: glEnable(GL_TEXTURE_2D); nuclear@12: glBindTexture(GL_TEXTURE_2D, thumbs->tex); nuclear@12: nuclear@12: glBegin(GL_QUADS); nuclear@12: glColor3f(1, 1, 1); nuclear@12: glTexCoord2f(0, 0); glVertex2f(0, 0); nuclear@12: glTexCoord2f(1, 0); glVertex2f(1, 0); nuclear@12: glTexCoord2f(1, 1); glVertex2f(1, 1); nuclear@12: glTexCoord2f(0, 1); glVertex2f(0, 1); nuclear@12: glEnd(); nuclear@12: } else { nuclear@12: fprintf(stderr, "invalid texture: %u\n", thumbs->tex); nuclear@12: } nuclear@12: nuclear@12: glPopMatrix(); nuclear@12: glDisable(GL_TEXTURE_2D); nuclear@1: } nuclear@1: nuclear@1: thumbs->layout_pos[0] = x; nuclear@1: thumbs->layout_pos[1] = y; nuclear@1: thumbs->layout_size[0] = thumb_sz; nuclear@1: thumbs->layout_size[1] = thumb_sz; nuclear@1: nuclear@1: x += thumb_sz + gap; nuclear@1: if(x >= 1.0 - thumb_sz) { nuclear@1: x = gap; nuclear@1: y += thumb_sz + gap; nuclear@1: } nuclear@1: nuclear@1: thumbs = thumbs->next; nuclear@1: } nuclear@1: nuclear@1: glMatrixMode(GL_PROJECTION); nuclear@1: glPopMatrix(); nuclear@1: glMatrixMode(GL_MODELVIEW); nuclear@1: } nuclear@11: nuclear@11: static int load_res_texture(const char *fname, int id, void *cls) nuclear@11: { nuclear@12: struct thumbnail *rdata = resman_get_res_data(texman, id); nuclear@11: nuclear@11: assert(rdata); nuclear@11: if(!rdata->img) { nuclear@11: if(!(rdata->img = img_create())) { nuclear@11: return -1; nuclear@11: } nuclear@11: } nuclear@11: nuclear@12: if(img_load(rdata->img, fname) == -1) { nuclear@11: img_free(rdata->img); nuclear@13: rdata->img = 0; nuclear@11: return -1; nuclear@11: } nuclear@11: rdata->aspect = (float)rdata->img->width / (float)rdata->img->height; nuclear@11: nuclear@11: /* set the resource's data to the loaded image, so that we can use nuclear@11: * it in the done callback */ nuclear@12: resman_set_res_data(texman, id, rdata); nuclear@11: return 0; nuclear@11: } nuclear@11: nuclear@11: static int done_res_texture(int id, void *cls) nuclear@11: { nuclear@20: struct thumbnail *thumb = resman_get_res_data(texman, id); nuclear@13: int load_result = resman_get_res_result(texman, id); nuclear@11: nuclear@13: if(load_result == -1) { nuclear@13: /* returning -1 will remove this resource, the free_res_texture nuclear@13: * destroy handler will be called, which will remove the node nuclear@13: * from the list nuclear@13: */ nuclear@13: return -1; nuclear@13: } nuclear@11: nuclear@20: if(resman_get_res_result(texman, id) != 0 || !thumb) { nuclear@12: fprintf(stderr, "failed to load resource %d (%s)\n", id, resman_get_res_name(texman, id)); nuclear@11: } else { nuclear@12: printf("done loading resource %d (%s)\n", id, resman_get_res_name(texman, id)); nuclear@11: } nuclear@11: nuclear@20: if(!thumb->tex) { nuclear@20: glGenTextures(1, &thumb->tex); nuclear@11: } nuclear@20: glBindTexture(GL_TEXTURE_2D, thumb->tex); nuclear@11: glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); nuclear@11: glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); nuclear@20: glTexImage2D(GL_TEXTURE_2D, 0, img_glintfmt(thumb->img), nuclear@20: thumb->img->width, thumb->img->height, 0, img_glfmt(thumb->img), nuclear@20: img_gltype(thumb->img), thumb->img->pixels); nuclear@20: nuclear@21: /* and add it to the list of thumbnails (if it's the first loading) */ nuclear@21: if(resman_get_res_load_count(texman, id) == 0) { nuclear@21: thumb->prev = thumb->list; nuclear@21: thumb->next = thumb->list->next; nuclear@21: if(thumb->list->next) thumb->list->next->prev = thumb; nuclear@21: thumb->list->next = thumb; nuclear@21: } nuclear@11: return 0; nuclear@11: } nuclear@11: nuclear@11: static void free_res_texture(int id, void *cls) nuclear@11: { nuclear@13: struct thumbnail *thumb = resman_get_res_data(texman, id); nuclear@11: nuclear@14: printf("deleting thumb %d: %s\n", id, thumb->fname); nuclear@14: nuclear@13: if(thumb) { nuclear@13: if(thumb->tex) { nuclear@13: glDeleteTextures(1, &thumb->tex); nuclear@11: } nuclear@13: if(thumb->img) { nuclear@13: img_free(thumb->img); nuclear@11: } nuclear@11: } nuclear@13: nuclear@13: /* remove from the list */ nuclear@13: if(thumb->prev) { nuclear@13: thumb->prev->next = thumb->next; nuclear@13: } nuclear@13: if(thumb->next) { nuclear@13: thumb->next->prev = thumb->prev; nuclear@13: } nuclear@13: free(thumb); nuclear@11: }