From 8b527949b0a74a37079210a6fc8fabbd576432eb Mon Sep 17 00:00:00 2001 From: Harry Jeffery Date: Wed, 11 Nov 2015 15:22:41 +0000 Subject: Update makefile to use a build directory --- Makefile | 14 ++- image.c | 245 ------------------------------------------------ image.h | 49 ---------- main.c | 284 -------------------------------------------------------- navigator.c | 188 ------------------------------------- navigator.h | 45 --------- src/image.c | 245 ++++++++++++++++++++++++++++++++++++++++++++++++ src/image.h | 49 ++++++++++ src/main.c | 284 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/navigator.c | 188 +++++++++++++++++++++++++++++++++++++ src/navigator.h | 45 +++++++++ src/texture.c | 146 +++++++++++++++++++++++++++++ src/texture.h | 46 +++++++++ src/viewport.c | 119 ++++++++++++++++++++++++ src/viewport.h | 48 ++++++++++ texture.c | 146 ----------------------------- texture.h | 46 --------- viewport.c | 119 ------------------------ viewport.h | 48 ---------- 19 files changed, 1182 insertions(+), 1172 deletions(-) delete mode 100644 image.c delete mode 100644 image.h delete mode 100644 main.c delete mode 100644 navigator.c delete mode 100644 navigator.h create mode 100644 src/image.c create mode 100644 src/image.h create mode 100644 src/main.c create mode 100644 src/navigator.c create mode 100644 src/navigator.h create mode 100644 src/texture.c create mode 100644 src/texture.h create mode 100644 src/viewport.c create mode 100644 src/viewport.h delete mode 100644 texture.c delete mode 100644 texture.h delete mode 100644 viewport.c delete mode 100644 viewport.h diff --git a/Makefile b/Makefile index edfbdc9..b05d4e4 100644 --- a/Makefile +++ b/Makefile @@ -6,16 +6,26 @@ CFLAGS = -W -Wall -std=gnu11 `sdl2-config --cflags` LDFLAGS = `sdl2-config --libs` -lfreeimage TARGET = imv -OBJECTS = main.o image.o texture.o navigator.o viewport.o +BUILDDIR = build + +SOURCES = $(wildcard src/*.c) +OBJECTS = $(patsubst src/%.c,$(BUILDDIR)/%.o,$(SOURCES)) $(TARGET): $(OBJECTS) $(CC) $(LDFLAGS) -o $@ $^ $(LDLIBS) -debug: CFLAGS += -DDEBUG -g +debug: CFLAGS += -DDEBUG -g -pg debug: $(TARGET) +$(BUILDDIR)/%.o: src/%.c $(BUILDDIR) + $(CC) -c $(CFLAGS) -o $@ $< + +$(BUILDDIR): + mkdir -p $(BUILDDIR) + clean: $(RM) $(TARGET) $(OBJECTS) + rmdir $(BUILDDIR) install: $(TARGET) install -m 0755 $(TARGET) $(prefix)/bin diff --git a/image.c b/image.c deleted file mode 100644 index 8516bf5..0000000 --- a/image.c +++ /dev/null @@ -1,245 +0,0 @@ -/* Copyright (c) 2015 Harry Jeffery - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - -#include "image.h" - -void imv_init_image(struct imv_image *img) -{ - img->mbmp = NULL; - img->cur_bmp = NULL; - img->width = 0; - img->height = 0; - img->cur_frame = 0; - img->next_frame = 0; - img->num_frames = 0; - img->frame_time = 0; - img->changed = 0; -} - -void imv_destroy_image(struct imv_image *img) -{ - if(img->cur_bmp) { - FreeImage_Unload(img->cur_bmp); - img->cur_bmp = NULL; - } - if(img->mbmp) { - FreeImage_CloseMultiBitmap(img->mbmp, 0); - img->mbmp = NULL; - } -} - -int imv_can_load_image(const char* path) -{ - if(FreeImage_GetFileType(path, 0) == FIF_UNKNOWN) { - return 0; - } else { - return 1; - } -} - -int imv_image_load(struct imv_image *img, const char* path) -{ - if(img->mbmp) { - FreeImage_CloseMultiBitmap(img->mbmp, 0); - img->mbmp = NULL; - } - - if(img->cur_bmp) { - FreeImage_Unload(img->cur_bmp); - img->cur_bmp = NULL; - } - - - FREE_IMAGE_FORMAT fmt = FreeImage_GetFileType(path,0); - - if(fmt == FIF_UNKNOWN) { - return 1; - } - - img->num_frames = 0; - img->cur_frame = 0; - img->next_frame = 0; - img->frame_time = 0; - - if(fmt == FIF_GIF) { - img->mbmp = FreeImage_OpenMultiBitmap(FIF_GIF, path, - /* don't create file */ 0, - /* read only */ 1, - /* keep in memory */ 1, - /* flags */ GIF_LOAD256); - if(!img->mbmp) { - return 1; - } - img->num_frames = FreeImage_GetPageCount(img->mbmp); - - /* get the dimensions from the first frame */ - FIBITMAP *frame = FreeImage_LockPage(img->mbmp, 0); - img->width = FreeImage_GetWidth(frame); - img->height = FreeImage_GetHeight(frame); - FreeImage_UnlockPage(img->mbmp, frame, 0); - - /* load a frame */ - imv_image_load_next_frame(img); - } else { - FIBITMAP *image = FreeImage_Load(fmt, path, 0); - if(!image) { - return 1; - } - FreeImage_FlipVertical(image); - img->cur_bmp = FreeImage_ConvertTo32Bits(image); - img->width = FreeImage_GetWidth(img->cur_bmp); - img->height = FreeImage_GetHeight(img->cur_bmp); - } - - img->changed = 1; - return 0; -} - -void imv_image_load_next_frame(struct imv_image *img) -{ - if(!imv_image_is_animated(img)) { - return; - } - - FITAG *tag = NULL; - char disposal_method = 0; - int frame_time = 0; - short top = 0; - short left = 0; - - img->cur_frame = img->next_frame; - img->next_frame = (img->cur_frame + 1) % img->num_frames; - FIBITMAP *frame = FreeImage_LockPage(img->mbmp, img->cur_frame); - FIBITMAP *frame32 = FreeImage_ConvertTo32Bits(frame); - FreeImage_FlipVertical(frame32); - - /* First frame is always going to use the raw frame */ - if(img->cur_frame > 0) { - FreeImage_GetMetadata(FIMD_ANIMATION, frame, "DisposalMethod", &tag); - if(FreeImage_GetTagValue(tag)) { - disposal_method = *(char*)FreeImage_GetTagValue(tag); - } - } - - FreeImage_GetMetadata(FIMD_ANIMATION, frame, "FrameLeft", &tag); - if(FreeImage_GetTagValue(tag)) { - left = *(short*)FreeImage_GetTagValue(tag); - } - - FreeImage_GetMetadata(FIMD_ANIMATION, frame, "FrameTop", &tag); - if(FreeImage_GetTagValue(tag)) { - top = *(short*)FreeImage_GetTagValue(tag); - } - - FreeImage_GetMetadata(FIMD_ANIMATION, frame, "FrameTime", &tag); - if(FreeImage_GetTagValue(tag)) { - frame_time = *(int*)FreeImage_GetTagValue(tag); - } - - img->frame_time += frame_time * 0.001; - - FreeImage_UnlockPage(img->mbmp, frame, 0); - - /* If this frame is inset, we need to expand it for compositing */ - if(left != 0 || top != 0) { - RGBQUAD color = {0,0,0,0}; - FIBITMAP *expanded = FreeImage_EnlargeCanvas(frame32, - left, - img->height - FreeImage_GetHeight(frame32) - top, - img->width - FreeImage_GetWidth(frame32) - left, - top, - &color, - 0); - FreeImage_Unload(frame32); - frame32 = expanded; - } - - /* If the frame is still too small, enlarge it to fit */ - if(img->width != (int)FreeImage_GetWidth(frame32) || - img->height != (int)FreeImage_GetHeight(frame32)) { - RGBQUAD color = {0,0,0,0}; - FIBITMAP *expanded = FreeImage_EnlargeCanvas(frame32, - 0, - img->height - FreeImage_GetHeight(frame32), - img->width - FreeImage_GetWidth(frame32), - 0, - &color, - 0); - FreeImage_Unload(frame32); - frame32 = expanded; - } - - switch(disposal_method) { - case 0: /* nothing specified, just use the raw frame */ - if(img->cur_bmp) { - FreeImage_Unload(img->cur_bmp); - } - img->cur_bmp = frame32; - break; - case 1: /* composite over previous frame */ - if(img->cur_bmp && img->cur_frame > 0) { - FIBITMAP *bg_frame = FreeImage_ConvertTo24Bits(img->cur_bmp); - FreeImage_Unload(img->cur_bmp); - FIBITMAP *comp = FreeImage_Composite(frame32, 1, NULL, bg_frame); - FreeImage_Unload(bg_frame); - FreeImage_Unload(frame32); - img->cur_bmp = comp; - } else { - /* No previous frame, just render directly */ - if(img->cur_bmp) { - FreeImage_Unload(img->cur_bmp); - } - img->cur_bmp = frame32; - } - break; - case 2: /* TODO - set to background, composite over that */ - break; - case 3: /* TODO - restore to previous content */ - break; - } - img->changed = 1; -} - -int imv_image_is_animated(struct imv_image *img) -{ - return img->num_frames > 1; -} - -void imv_image_play(struct imv_image *img, double time) -{ - if(!imv_image_is_animated(img)) { - return; - } - - img->frame_time -= time; - if(img->frame_time < 0) { - img->frame_time = 0; - imv_image_load_next_frame(img); - } -} - -int imv_image_has_changed(struct imv_image *img) -{ - if(img->changed) { - img->changed = 0; - return 1; - } else { - return 0; - } -} diff --git a/image.h b/image.h deleted file mode 100644 index d36dab4..0000000 --- a/image.h +++ /dev/null @@ -1,49 +0,0 @@ -#ifndef IMV_IMAGE_H -#define IMV_IMAGE_H - -/* Copyright (c) 2015 Harry Jeffery - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - -#include - -struct imv_image { - FIMULTIBITMAP *mbmp; - FIBITMAP *cur_bmp; - int width; - int height; - int cur_frame; - int next_frame; - int num_frames; - int changed; - double frame_time; -}; - -void imv_init_image(struct imv_image *img); -void imv_destroy_image(struct imv_image *img); - -int imv_can_load_image(const char* path); -int imv_image_load(struct imv_image *img, const char* path); -void imv_image_load_next_frame(struct imv_image *img); - -int imv_image_is_animated(struct imv_image *img); -void imv_image_play(struct imv_image *img, double time); - -int imv_image_has_changed(struct imv_image *img); - -#endif diff --git a/main.c b/main.c deleted file mode 100644 index 2efeec3..0000000 --- a/main.c +++ /dev/null @@ -1,284 +0,0 @@ -/* Copyright (c) 2015 Harry Jeffery - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - -#include -#include -#include -#include - -#include "image.h" -#include "texture.h" -#include "navigator.h" -#include "viewport.h" - -struct { - int autoscale; - int fullscreen; - int stdin; - int center; - int recursive; -} g_options = {0,0,0,0,0}; - -void print_usage(const char* name) -{ - fprintf(stdout, - "Usage: %s [-ifsh] [images...]\n" - "\n" - "Flags:\n" - " -i: Read paths from stdin. One path per line.\n" - " -r: Recursively search input paths.\n" - " -f: Start in fullscreen mode\n" - " -s: Auto scale images to fit window\n" - " -c: Center images in the window\n" - " -h: Print this help\n" - "\n" - "Mouse:\n" - " Click+Drag to Pan\n" - " MouseWheel to Zoom\n" - "\n" - "Hotkeys:\n" - " 'q': Quit\n" - " '[',LArrow: Previous image\n" - " ']',RArrow: Next image\n" - " 'i','+': Zoom in\n" - " 'o','=': Zoom out\n" - " 'h': Pan left\n" - " 'j': Pan down\n" - " 'k': Pan up\n" - " 'l': Pan right\n" - " 'r': Reset view\n" - " 'c': Center view\n" - " 's': Scale image to fit window\n" - " 'x': Close current image\n" - " 'f': Toggle fullscreen\n" - " ' ': Toggle gif playback\n" - " '.': Step a frame of gif playback\n" - ,name); -} - -void parse_arg(const char* name, const char* arg) -{ - for(const char *o = arg; *o != 0; ++o) { - switch(*o) { - case 'f': g_options.fullscreen = 1; break; - case 's': g_options.autoscale = 1; break; - case 'c': g_options.center = 1; break; - case 'i': g_options.stdin = 1; break; - case 'r': g_options.recursive = 1; break; - case 'h': print_usage(name); exit(0); break; - default: - fprintf(stderr, "Unknown argument '%c'. Aborting.\n", *o); - exit(1); - } - } -} - -int main(int argc, char** argv) -{ - if(argc < 2) { - print_usage(argv[0]); - exit(1); - } - - struct imv_navigator nav; - imv_init_navigator(&nav); - - for(int i = 1; i < argc; ++i) { - if(argv[i][0] == '-') { - parse_arg(argv[0], &argv[i][1]); - } else { - if(g_options.recursive) { - imv_navigator_add_path_recursive(&nav, argv[i]); - } else { - imv_navigator_add_path(&nav, argv[i]); - } - } - } - - if(g_options.stdin) { - char buf[512]; - while(fgets(buf, sizeof(buf), stdin)) { - size_t len = strlen(buf); - if(buf[len-1] == '\n') { - buf[--len] = 0; - } - if(len > 0) { - if(g_options.recursive) { - imv_navigator_add_path_recursive(&nav, buf); - } else { - imv_navigator_add_path(&nav, buf); - } - } - } - } - - if(!imv_navigator_get_current_path(&nav)) { - fprintf(stderr, "No input files. Exiting.\n"); - exit(1); - } - - if(SDL_Init(SDL_INIT_VIDEO) != 0) { - fprintf(stderr, "SDL Failed to Init: %s\n", SDL_GetError()); - exit(1); - } - - const int width = 1280; - const int height = 720; - - SDL_Window *window = SDL_CreateWindow( - "imv", - SDL_WINDOWPOS_CENTERED, - SDL_WINDOWPOS_CENTERED, - width, height, - SDL_WINDOW_RESIZABLE); - - SDL_Renderer *renderer = - SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); - - /* Use linear sampling for scaling */ - SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1"); - - struct imv_image img; - imv_init_image(&img); - - struct imv_texture tex; - imv_init_texture(&tex, renderer); - - struct imv_viewport view; - imv_init_viewport(&view, window); - - /* Put us in fullscren by default if requested */ - if(g_options.fullscreen) { - imv_viewport_toggle_fullscreen(&view); - } - - double last_time = SDL_GetTicks() / 1000.0; - - int quit = 0; - while(!quit) { - SDL_Event e; - while(!quit && SDL_PollEvent(&e)) { - switch(e.type) { - case SDL_QUIT: - quit = 1; - break; - case SDL_KEYDOWN: - switch (e.key.keysym.sym) { - case SDLK_q: quit = 1; break; - case SDLK_LEFTBRACKET: - case SDLK_LEFT: imv_navigator_prev_path(&nav); break; - case SDLK_RIGHTBRACKET: - case SDLK_RIGHT: imv_navigator_next_path(&nav); break; - case SDLK_EQUALS: - case SDLK_i: - case SDLK_UP: imv_viewport_zoom(&view, 1); break; - case SDLK_MINUS: - case SDLK_o: - case SDLK_DOWN: imv_viewport_zoom(&view, -1); break; - case SDLK_r: imv_viewport_reset(&view); break; - case SDLK_c: imv_viewport_center(&view, &img); break; - case SDLK_j: imv_viewport_move(&view, 0, -50); break; - case SDLK_k: imv_viewport_move(&view, 0, 50); break; - case SDLK_h: imv_viewport_move(&view, 50, 0); break; - case SDLK_l: imv_viewport_move(&view, -50, 0); break; - case SDLK_x: imv_navigator_remove_current_path(&nav); break; - case SDLK_f: imv_viewport_toggle_fullscreen(&view); break; - case SDLK_PERIOD: imv_image_load_next_frame(&img); break; - case SDLK_SPACE: imv_viewport_toggle_playing(&view, &img);break; - case SDLK_s: imv_viewport_scale_to_window(&view, &img);break; - } - break; - case SDL_MOUSEWHEEL: - imv_viewport_zoom(&view, e.wheel.y); - break; - case SDL_MOUSEMOTION: - if(e.motion.state & SDL_BUTTON_LMASK) { - imv_viewport_move(&view, e.motion.xrel, e.motion.yrel); - } - break; - case SDL_WINDOWEVENT: - imv_viewport_set_redraw(&view); - break; - } - } - - if(quit) { - break; - } - - while(imv_navigator_has_changed(&nav)) { - const char* current_path = imv_navigator_get_current_path(&nav); - char title[256]; - snprintf(&title[0], sizeof(title), "imv - [%i/%i] [LOADING] %s", - nav.cur_path + 1, nav.num_paths, current_path); - imv_viewport_set_title(&view, title); - - if(!current_path) { - fprintf(stderr, "No input files left. Exiting.\n"); - exit(1); - } - - if(imv_image_load(&img, current_path) != 0) { - imv_navigator_remove_current_path(&nav); - } else { - snprintf(&title[0], sizeof(title), "imv - [%i/%i] [%ix%i] %s", - nav.cur_path + 1, nav.num_paths, - img.width, img.height, current_path); - imv_viewport_set_title(&view, title); - imv_viewport_reset(&view); - } - /* Autoscale if requested */ - if(g_options.autoscale) { - imv_viewport_scale_to_window(&view, &img); - } - if(g_options.center) { - imv_viewport_center(&view, &img); - } - } - - if(view.playing) { - double cur_time = SDL_GetTicks() / 1000.0; - double dt = cur_time - last_time; - imv_image_play(&img, dt); - } - - if(imv_image_has_changed(&img)) { - imv_texture_set_image(&tex, img.cur_bmp); - imv_viewport_set_redraw(&view); - } - - if(view.redraw) { - imv_texture_draw(&tex, view.x, view.y, view.scale); - view.redraw = 0; - } - last_time = SDL_GetTicks() / 1000.0; - SDL_Delay(10); - } - - imv_destroy_image(&img); - imv_destroy_texture(&tex); - imv_destroy_navigator(&nav); - imv_destroy_viewport(&view); - - SDL_DestroyRenderer(renderer); - SDL_DestroyWindow(window); - SDL_Quit(); - - return 0; -} diff --git a/navigator.c b/navigator.c deleted file mode 100644 index 408c53c..0000000 --- a/navigator.c +++ /dev/null @@ -1,188 +0,0 @@ -/* Copyright (c) 2015 Harry Jeffery - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - -#include "navigator.h" - -#include -#include -#include -#include -#include - -void imv_init_navigator(struct imv_navigator *nav) -{ - nav->buf_size = 512; - nav->paths = (char **)malloc(sizeof(char*) * nav->buf_size); - nav->num_paths = 0; - nav->cur_path = 0; - nav->last_move_direction = 1; - nav->changed = 0; -} - -void imv_destroy_navigator(struct imv_navigator *nav) -{ - if(nav->buf_size > 0) { - free(nav->paths); - nav->paths = NULL; - nav->buf_size = 0; - } - nav->num_paths = 0; -} - -int imv_can_load_image(const char* path); - -static void add_item(struct imv_navigator *nav, const char *path) -{ - if(!imv_can_load_image(path)) { - return; - } - - if(nav->buf_size == nav->num_paths) { - int new_buf_size = nav->buf_size * 2; - char **new_paths = malloc(sizeof(char*) * new_buf_size); - memcpy(new_paths, nav->paths, sizeof(char*) * nav->buf_size); - free(nav->paths); - nav->paths = new_paths; - nav->buf_size = new_buf_size; - } - nav->paths[nav->num_paths] = strdup(path); - nav->num_paths += 1; - if(nav->num_paths == 1) { - nav->changed = 1; - } -} - -void imv_navigator_add_path(struct imv_navigator *nav, const char *path) -{ - char path_buf[512]; - struct stat path_info; - stat(path, &path_info); - if(S_ISDIR(path_info.st_mode)) { - DIR *d = opendir(path); - if(d) { - struct dirent *dir; - while((dir = readdir(d)) != NULL) { - if(strcmp(dir->d_name, "..") == 0 || - strcmp(dir->d_name, ".") == 0) { - continue; - } - snprintf(path_buf, sizeof(path_buf), "%s/%s", path, dir->d_name); - add_item(nav, path_buf); - } - closedir(d); - } - } else { - add_item(nav, path); - } -} - -void imv_navigator_add_path_recursive(struct imv_navigator *nav, const char *path) -{ - char path_buf[512]; - struct stat path_info; - stat(path, &path_info); - if(S_ISDIR(path_info.st_mode)) { - DIR *d = opendir(path); - if(d) { - struct dirent *dir; - while((dir = readdir(d)) != NULL) { - if(strcmp(dir->d_name, "..") == 0 || - strcmp(dir->d_name, ".") == 0) { - continue; - } - snprintf(path_buf, sizeof(path_buf), "%s/%s", path, dir->d_name); - imv_navigator_add_path_recursive(nav, path_buf); - } - closedir(d); - } - } else { - add_item(nav, path); - } -} - -const char *imv_navigator_get_current_path(struct imv_navigator *nav) -{ - if(nav->num_paths == 0) { - return NULL; - } - return nav->paths[nav->cur_path]; -} - -void imv_navigator_next_path(struct imv_navigator *nav) -{ - int prev_path = nav->cur_path; - if(nav->num_paths == 0) { - return; - } - nav->cur_path += 1; - if(nav->cur_path == nav->num_paths) { - nav->cur_path = 0; - } - nav->last_move_direction = 1; - nav->changed = prev_path != nav->cur_path; -} - -void imv_navigator_prev_path(struct imv_navigator *nav) -{ - int prev_path = nav->cur_path; - if(nav->num_paths == 0) { - return; - } - nav->cur_path -= 1; - if(nav->cur_path < 0) { - nav->cur_path = nav->num_paths - 1; - } - nav->last_move_direction = -1; - nav->changed = prev_path != nav->cur_path; -} - -void imv_navigator_remove_current_path(struct imv_navigator *nav) -{ - if(nav->num_paths == 0) { - return; - } - - free(nav->paths[nav->cur_path]); - for(int i = nav->cur_path; i < nav->num_paths - 1; ++i) { - nav->paths[i] = nav->paths[i+1]; - } - nav->num_paths -= 1; - - if(nav->last_move_direction < 0) { - /* Move left */ - imv_navigator_prev_path(nav); - } else { - /* Try to stay where we are, unless we ran out of room */ - if(nav->cur_path == nav->num_paths) { - nav->cur_path = 0; - } - } - - nav->changed = 1; -} - -int imv_navigator_has_changed(struct imv_navigator *nav) -{ - if(nav->changed) { - nav->changed = 0; - return 1; - } else { - return 0; - } -} diff --git a/navigator.h b/navigator.h deleted file mode 100644 index 1bccff6..0000000 --- a/navigator.h +++ /dev/null @@ -1,45 +0,0 @@ -#ifndef IMV_NAVIGATOR_H -#define IMV_NAVIGATOR_H - -/* Copyright (c) 2015 Harry Jeffery - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - -struct imv_navigator { - int num_paths; - int buf_size; - int cur_path; - char **paths; - int last_move_direction; - int changed; -}; - -void imv_init_navigator(struct imv_navigator *nav); -void imv_destroy_navigator(struct imv_navigator *nav); - -void imv_navigator_add_path(struct imv_navigator *nav, const char *path); -void imv_navigator_add_path_recursive(struct imv_navigator *nav, const char *path); - -const char *imv_navigator_get_current_path(struct imv_navigator *nav); -void imv_navigator_next_path(struct imv_navigator *nav); -void imv_navigator_prev_path(struct imv_navigator *nav); -void imv_navigator_remove_current_path(struct imv_navigator *nav); - -int imv_navigator_has_changed(struct imv_navigator *nav); - -#endif diff --git a/src/image.c b/src/image.c new file mode 100644 index 0000000..8516bf5 --- /dev/null +++ b/src/image.c @@ -0,0 +1,245 @@ +/* Copyright (c) 2015 Harry Jeffery + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +#include "image.h" + +void imv_init_image(struct imv_image *img) +{ + img->mbmp = NULL; + img->cur_bmp = NULL; + img->width = 0; + img->height = 0; + img->cur_frame = 0; + img->next_frame = 0; + img->num_frames = 0; + img->frame_time = 0; + img->changed = 0; +} + +void imv_destroy_image(struct imv_image *img) +{ + if(img->cur_bmp) { + FreeImage_Unload(img->cur_bmp); + img->cur_bmp = NULL; + } + if(img->mbmp) { + FreeImage_CloseMultiBitmap(img->mbmp, 0); + img->mbmp = NULL; + } +} + +int imv_can_load_image(const char* path) +{ + if(FreeImage_GetFileType(path, 0) == FIF_UNKNOWN) { + return 0; + } else { + return 1; + } +} + +int imv_image_load(struct imv_image *img, const char* path) +{ + if(img->mbmp) { + FreeImage_CloseMultiBitmap(img->mbmp, 0); + img->mbmp = NULL; + } + + if(img->cur_bmp) { + FreeImage_Unload(img->cur_bmp); + img->cur_bmp = NULL; + } + + + FREE_IMAGE_FORMAT fmt = FreeImage_GetFileType(path,0); + + if(fmt == FIF_UNKNOWN) { + return 1; + } + + img->num_frames = 0; + img->cur_frame = 0; + img->next_frame = 0; + img->frame_time = 0; + + if(fmt == FIF_GIF) { + img->mbmp = FreeImage_OpenMultiBitmap(FIF_GIF, path, + /* don't create file */ 0, + /* read only */ 1, + /* keep in memory */ 1, + /* flags */ GIF_LOAD256); + if(!img->mbmp) { + return 1; + } + img->num_frames = FreeImage_GetPageCount(img->mbmp); + + /* get the dimensions from the first frame */ + FIBITMAP *frame = FreeImage_LockPage(img->mbmp, 0); + img->width = FreeImage_GetWidth(frame); + img->height = FreeImage_GetHeight(frame); + FreeImage_UnlockPage(img->mbmp, frame, 0); + + /* load a frame */ + imv_image_load_next_frame(img); + } else { + FIBITMAP *image = FreeImage_Load(fmt, path, 0); + if(!image) { + return 1; + } + FreeImage_FlipVertical(image); + img->cur_bmp = FreeImage_ConvertTo32Bits(image); + img->width = FreeImage_GetWidth(img->cur_bmp); + img->height = FreeImage_GetHeight(img->cur_bmp); + } + + img->changed = 1; + return 0; +} + +void imv_image_load_next_frame(struct imv_image *img) +{ + if(!imv_image_is_animated(img)) { + return; + } + + FITAG *tag = NULL; + char disposal_method = 0; + int frame_time = 0; + short top = 0; + short left = 0; + + img->cur_frame = img->next_frame; + img->next_frame = (img->cur_frame + 1) % img->num_frames; + FIBITMAP *frame = FreeImage_LockPage(img->mbmp, img->cur_frame); + FIBITMAP *frame32 = FreeImage_ConvertTo32Bits(frame); + FreeImage_FlipVertical(frame32); + + /* First frame is always going to use the raw frame */ + if(img->cur_frame > 0) { + FreeImage_GetMetadata(FIMD_ANIMATION, frame, "DisposalMethod", &tag); + if(FreeImage_GetTagValue(tag)) { + disposal_method = *(char*)FreeImage_GetTagValue(tag); + } + } + + FreeImage_GetMetadata(FIMD_ANIMATION, frame, "FrameLeft", &tag); + if(FreeImage_GetTagValue(tag)) { + left = *(short*)FreeImage_GetTagValue(tag); + } + + FreeImage_GetMetadata(FIMD_ANIMATION, frame, "FrameTop", &tag); + if(FreeImage_GetTagValue(tag)) { + top = *(short*)FreeImage_GetTagValue(tag); + } + + FreeImage_GetMetadata(FIMD_ANIMATION, frame, "FrameTime", &tag); + if(FreeImage_GetTagValue(tag)) { + frame_time = *(int*)FreeImage_GetTagValue(tag); + } + + img->frame_time += frame_time * 0.001; + + FreeImage_UnlockPage(img->mbmp, frame, 0); + + /* If this frame is inset, we need to expand it for compositing */ + if(left != 0 || top != 0) { + RGBQUAD color = {0,0,0,0}; + FIBITMAP *expanded = FreeImage_EnlargeCanvas(frame32, + left, + img->height - FreeImage_GetHeight(frame32) - top, + img->width - FreeImage_GetWidth(frame32) - left, + top, + &color, + 0); + FreeImage_Unload(frame32); + frame32 = expanded; + } + + /* If the frame is still too small, enlarge it to fit */ + if(img->width != (int)FreeImage_GetWidth(frame32) || + img->height != (int)FreeImage_GetHeight(frame32)) { + RGBQUAD color = {0,0,0,0}; + FIBITMAP *expanded = FreeImage_EnlargeCanvas(frame32, + 0, + img->height - FreeImage_GetHeight(frame32), + img->width - FreeImage_GetWidth(frame32), + 0, + &color, + 0); + FreeImage_Unload(frame32); + frame32 = expanded; + } + + switch(disposal_method) { + case 0: /* nothing specified, just use the raw frame */ + if(img->cur_bmp) { + FreeImage_Unload(img->cur_bmp); + } + img->cur_bmp = frame32; + break; + case 1: /* composite over previous frame */ + if(img->cur_bmp && img->cur_frame > 0) { + FIBITMAP *bg_frame = FreeImage_ConvertTo24Bits(img->cur_bmp); + FreeImage_Unload(img->cur_bmp); + FIBITMAP *comp = FreeImage_Composite(frame32, 1, NULL, bg_frame); + FreeImage_Unload(bg_frame); + FreeImage_Unload(frame32); + img->cur_bmp = comp; + } else { + /* No previous frame, just render directly */ + if(img->cur_bmp) { + FreeImage_Unload(img->cur_bmp); + } + img->cur_bmp = frame32; + } + break; + case 2: /* TODO - set to background, composite over that */ + break; + case 3: /* TODO - restore to previous content */ + break; + } + img->changed = 1; +} + +int imv_image_is_animated(struct imv_image *img) +{ + return img->num_frames > 1; +} + +void imv_image_play(struct imv_image *img, double time) +{ + if(!imv_image_is_animated(img)) { + return; + } + + img->frame_time -= time; + if(img->frame_time < 0) { + img->frame_time = 0; + imv_image_load_next_frame(img); + } +} + +int imv_image_has_changed(struct imv_image *img) +{ + if(img->changed) { + img->changed = 0; + return 1; + } else { + return 0; + } +} diff --git a/src/image.h b/src/image.h new file mode 100644 index 0000000..d36dab4 --- /dev/null +++ b/src/image.h @@ -0,0 +1,49 @@ +#ifndef IMV_IMAGE_H +#define IMV_IMAGE_H + +/* Copyright (c) 2015 Harry Jeffery + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +#include + +struct imv_image { + FIMULTIBITMAP *mbmp; + FIBITMAP *cur_bmp; + int width; + int height; + int cur_frame; + int next_frame; + int num_frames; + int changed; + double frame_time; +}; + +void imv_init_image(struct imv_image *img); +void imv_destroy_image(struct imv_image *img); + +int imv_can_load_image(const char* path); +int imv_image_load(struct imv_image *img, const char* path); +void imv_image_load_next_frame(struct imv_image *img); + +int imv_image_is_animated(struct imv_image *img); +void imv_image_play(struct imv_image *img, double time); + +int imv_image_has_changed(struct imv_image *img); + +#endif diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..2efeec3 --- /dev/null +++ b/src/main.c @@ -0,0 +1,284 @@ +/* Copyright (c) 2015 Harry Jeffery + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +#include +#include +#include +#include + +#include "image.h" +#include "texture.h" +#include "navigator.h" +#include "viewport.h" + +struct { + int autoscale; + int fullscreen; + int stdin; + int center; + int recursive; +} g_options = {0,0,0,0,0}; + +void print_usage(const char* name) +{ + fprintf(stdout, + "Usage: %s [-ifsh] [images...]\n" + "\n" + "Flags:\n" + " -i: Read paths from stdin. One path per line.\n" + " -r: Recursively search input paths.\n" + " -f: Start in fullscreen mode\n" + " -s: Auto scale images to fit window\n" + " -c: Center images in the window\n" + " -h: Print this help\n" + "\n" + "Mouse:\n" + " Click+Drag to Pan\n" + " MouseWheel to Zoom\n" + "\n" + "Hotkeys:\n" + " 'q': Quit\n" + " '[',LArrow: Previous image\n" + " ']',RArrow: Next image\n" + " 'i','+': Zoom in\n" + " 'o','=': Zoom out\n" + " 'h': Pan left\n" + " 'j': Pan down\n" + " 'k': Pan up\n" + " 'l': Pan right\n" + " 'r': Reset view\n" + " 'c': Center view\n" + " 's': Scale image to fit window\n" + " 'x': Close current image\n" + " 'f': Toggle fullscreen\n" + " ' ': Toggle gif playback\n" + " '.': Step a frame of gif playback\n" + ,name); +} + +void parse_arg(const char* name, const char* arg) +{ + for(const char *o = arg; *o != 0; ++o) { + switch(*o) { + case 'f': g_options.fullscreen = 1; break; + case 's': g_options.autoscale = 1; break; + case 'c': g_options.center = 1; break; + case 'i': g_options.stdin = 1; break; + case 'r': g_options.recursive = 1; break; + case 'h': print_usage(name); exit(0); break; + default: + fprintf(stderr, "Unknown argument '%c'. Aborting.\n", *o); + exit(1); + } + } +} + +int main(int argc, char** argv) +{ + if(argc < 2) { + print_usage(argv[0]); + exit(1); + } + + struct imv_navigator nav; + imv_init_navigator(&nav); + + for(int i = 1; i < argc; ++i) { + if(argv[i][0] == '-') { + parse_arg(argv[0], &argv[i][1]); + } else { + if(g_options.recursive) { + imv_navigator_add_path_recursive(&nav, argv[i]); + } else { + imv_navigator_add_path(&nav, argv[i]); + } + } + } + + if(g_options.stdin) { + char buf[512]; + while(fgets(buf, sizeof(buf), stdin)) { + size_t len = strlen(buf); + if(buf[len-1] == '\n') { + buf[--len] = 0; + } + if(len > 0) { + if(g_options.recursive) { + imv_navigator_add_path_recursive(&nav, buf); + } else { + imv_navigator_add_path(&nav, buf); + } + } + } + } + + if(!imv_navigator_get_current_path(&nav)) { + fprintf(stderr, "No input files. Exiting.\n"); + exit(1); + } + + if(SDL_Init(SDL_INIT_VIDEO) != 0) { + fprintf(stderr, "SDL Failed to Init: %s\n", SDL_GetError()); + exit(1); + } + + const int width = 1280; + const int height = 720; + + SDL_Window *window = SDL_CreateWindow( + "imv", + SDL_WINDOWPOS_CENTERED, + SDL_WINDOWPOS_CENTERED, + width, height, + SDL_WINDOW_RESIZABLE); + + SDL_Renderer *renderer = + SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); + + /* Use linear sampling for scaling */ + SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1"); + + struct imv_image img; + imv_init_image(&img); + + struct imv_texture tex; + imv_init_texture(&tex, renderer); + + struct imv_viewport view; + imv_init_viewport(&view, window); + + /* Put us in fullscren by default if requested */ + if(g_options.fullscreen) { + imv_viewport_toggle_fullscreen(&view); + } + + double last_time = SDL_GetTicks() / 1000.0; + + int quit = 0; + while(!quit) { + SDL_Event e; + while(!quit && SDL_PollEvent(&e)) { + switch(e.type) { + case SDL_QUIT: + quit = 1; + break; + case SDL_KEYDOWN: + switch (e.key.keysym.sym) { + case SDLK_q: quit = 1; break; + case SDLK_LEFTBRACKET: + case SDLK_LEFT: imv_navigator_prev_path(&nav); break; + case SDLK_RIGHTBRACKET: + case SDLK_RIGHT: imv_navigator_next_path(&nav); break; + case SDLK_EQUALS: + case SDLK_i: + case SDLK_UP: imv_viewport_zoom(&view, 1); break; + case SDLK_MINUS: + case SDLK_o: + case SDLK_DOWN: imv_viewport_zoom(&view, -1); break; + case SDLK_r: imv_viewport_reset(&view); break; + case SDLK_c: imv_viewport_center(&view, &img); break; + case SDLK_j: imv_viewport_move(&view, 0, -50); break; + case SDLK_k: imv_viewport_move(&view, 0, 50); break; + case SDLK_h: imv_viewport_move(&view, 50, 0); break; + case SDLK_l: imv_viewport_move(&view, -50, 0); break; + case SDLK_x: imv_navigator_remove_current_path(&nav); break; + case SDLK_f: imv_viewport_toggle_fullscreen(&view); break; + case SDLK_PERIOD: imv_image_load_next_frame(&img); break; + case SDLK_SPACE: imv_viewport_toggle_playing(&view, &img);break; + case SDLK_s: imv_viewport_scale_to_window(&view, &img);break; + } + break; + case SDL_MOUSEWHEEL: + imv_viewport_zoom(&view, e.wheel.y); + break; + case SDL_MOUSEMOTION: + if(e.motion.state & SDL_BUTTON_LMASK) { + imv_viewport_move(&view, e.motion.xrel, e.motion.yrel); + } + break; + case SDL_WINDOWEVENT: + imv_viewport_set_redraw(&view); + break; + } + } + + if(quit) { + break; + } + + while(imv_navigator_has_changed(&nav)) { + const char* current_path = imv_navigator_get_current_path(&nav); + char title[256]; + snprintf(&title[0], sizeof(title), "imv - [%i/%i] [LOADING] %s", + nav.cur_path + 1, nav.num_paths, current_path); + imv_viewport_set_title(&view, title); + + if(!current_path) { + fprintf(stderr, "No input files left. Exiting.\n"); + exit(1); + } + + if(imv_image_load(&img, current_path) != 0) { + imv_navigator_remove_current_path(&nav); + } else { + snprintf(&title[0], sizeof(title), "imv - [%i/%i] [%ix%i] %s", + nav.cur_path + 1, nav.num_paths, + img.width, img.height, current_path); + imv_viewport_set_title(&view, title); + imv_viewport_reset(&view); + } + /* Autoscale if requested */ + if(g_options.autoscale) { + imv_viewport_scale_to_window(&view, &img); + } + if(g_options.center) { + imv_viewport_center(&view, &img); + } + } + + if(view.playing) { + double cur_time = SDL_GetTicks() / 1000.0; + double dt = cur_time - last_time; + imv_image_play(&img, dt); + } + + if(imv_image_has_changed(&img)) { + imv_texture_set_image(&tex, img.cur_bmp); + imv_viewport_set_redraw(&view); + } + + if(view.redraw) { + imv_texture_draw(&tex, view.x, view.y, view.scale); + view.redraw = 0; + } + last_time = SDL_GetTicks() / 1000.0; + SDL_Delay(10); + } + + imv_destroy_image(&img); + imv_destroy_texture(&tex); + imv_destroy_navigator(&nav); + imv_destroy_viewport(&view); + + SDL_DestroyRenderer(renderer); + SDL_DestroyWindow(window); + SDL_Quit(); + + return 0; +} diff --git a/src/navigator.c b/src/navigator.c new file mode 100644 index 0000000..408c53c --- /dev/null +++ b/src/navigator.c @@ -0,0 +1,188 @@ +/* Copyright (c) 2015 Harry Jeffery + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +#include "navigator.h" + +#include +#include +#include +#include +#include + +void imv_init_navigator(struct imv_navigator *nav) +{ + nav->buf_size = 512; + nav->paths = (char **)malloc(sizeof(char*) * nav->buf_size); + nav->num_paths = 0; + nav->cur_path = 0; + nav->last_move_direction = 1; + nav->changed = 0; +} + +void imv_destroy_navigator(struct imv_navigator *nav) +{ + if(nav->buf_size > 0) { + free(nav->paths); + nav->paths = NULL; + nav->buf_size = 0; + } + nav->num_paths = 0; +} + +int imv_can_load_image(const char* path); + +static void add_item(struct imv_navigator *nav, const char *path) +{ + if(!imv_can_load_image(path)) { + return; + } + + if(nav->buf_size == nav->num_paths) { + int new_buf_size = nav->buf_size * 2; + char **new_paths = malloc(sizeof(char*) * new_buf_size); + memcpy(new_paths, nav->paths, sizeof(char*) * nav->buf_size); + free(nav->paths); + nav->paths = new_paths; + nav->buf_size = new_buf_size; + } + nav->paths[nav->num_paths] = strdup(path); + nav->num_paths += 1; + if(nav->num_paths == 1) { + nav->changed = 1; + } +} + +void imv_navigator_add_path(struct imv_navigator *nav, const char *path) +{ + char path_buf[512]; + struct stat path_info; + stat(path, &path_info); + if(S_ISDIR(path_info.st_mode)) { + DIR *d = opendir(path); + if(d) { + struct dirent *dir; + while((dir = readdir(d)) != NULL) { + if(strcmp(dir->d_name, "..") == 0 || + strcmp(dir->d_name, ".") == 0) { + continue; + } + snprintf(path_buf, sizeof(path_buf), "%s/%s", path, dir->d_name); + add_item(nav, path_buf); + } + closedir(d); + } + } else { + add_item(nav, path); + } +} + +void imv_navigator_add_path_recursive(struct imv_navigator *nav, const char *path) +{ + char path_buf[512]; + struct stat path_info; + stat(path, &path_info); + if(S_ISDIR(path_info.st_mode)) { + DIR *d = opendir(path); + if(d) { + struct dirent *dir; + while((dir = readdir(d)) != NULL) { + if(strcmp(dir->d_name, "..") == 0 || + strcmp(dir->d_name, ".") == 0) { + continue; + } + snprintf(path_buf, sizeof(path_buf), "%s/%s", path, dir->d_name); + imv_navigator_add_path_recursive(nav, path_buf); + } + closedir(d); + } + } else { + add_item(nav, path); + } +} + +const char *imv_navigator_get_current_path(struct imv_navigator *nav) +{ + if(nav->num_paths == 0) { + return NULL; + } + return nav->paths[nav->cur_path]; +} + +void imv_navigator_next_path(struct imv_navigator *nav) +{ + int prev_path = nav->cur_path; + if(nav->num_paths == 0) { + return; + } + nav->cur_path += 1; + if(nav->cur_path == nav->num_paths) { + nav->cur_path = 0; + } + nav->last_move_direction = 1; + nav->changed = prev_path != nav->cur_path; +} + +void imv_navigator_prev_path(struct imv_navigator *nav) +{ + int prev_path = nav->cur_path; + if(nav->num_paths == 0) { + return; + } + nav->cur_path -= 1; + if(nav->cur_path < 0) { + nav->cur_path = nav->num_paths - 1; + } + nav->last_move_direction = -1; + nav->changed = prev_path != nav->cur_path; +} + +void imv_navigator_remove_current_path(struct imv_navigator *nav) +{ + if(nav->num_paths == 0) { + return; + } + + free(nav->paths[nav->cur_path]); + for(int i = nav->cur_path; i < nav->num_paths - 1; ++i) { + nav->paths[i] = nav->paths[i+1]; + } + nav->num_paths -= 1; + + if(nav->last_move_direction < 0) { + /* Move left */ + imv_navigator_prev_path(nav); + } else { + /* Try to stay where we are, unless we ran out of room */ + if(nav->cur_path == nav->num_paths) { + nav->cur_path = 0; + } + } + + nav->changed = 1; +} + +int imv_navigator_has_changed(struct imv_navigator *nav) +{ + if(nav->changed) { + nav->changed = 0; + return 1; + } else { + return 0; + } +} diff --git a/src/navigator.h b/src/navigator.h new file mode 100644 index 0000000..1bccff6 --- /dev/null +++ b/src/navigator.h @@ -0,0 +1,45 @@ +#ifndef IMV_NAVIGATOR_H +#define IMV_NAVIGATOR_H + +/* Copyright (c) 2015 Harry Jeffery + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +struct imv_navigator { + int num_paths; + int buf_size; + int cur_path; + char **paths; + int last_move_direction; + int changed; +}; + +void imv_init_navigator(struct imv_navigator *nav); +void imv_destroy_navigator(struct imv_navigator *nav); + +void imv_navigator_add_path(struct imv_navigator *nav, const char *path); +void imv_navigator_add_path_recursive(struct imv_navigator *nav, const char *path); + +const char *imv_navigator_get_current_path(struct imv_navigator *nav); +void imv_navigator_next_path(struct imv_navigator *nav); +void imv_navigator_prev_path(struct imv_navigator *nav); +void imv_navigator_remove_current_path(struct imv_navigator *nav); + +int imv_navigator_has_changed(struct imv_navigator *nav); + +#endif diff --git a/src/texture.c b/src/texture.c new file mode 100644 index 0000000..7dd6157 --- /dev/null +++ b/src/texture.c @@ -0,0 +1,146 @@ +/* Copyright (c) 2015 Harry Jeffery + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +#include "texture.h" + +void imv_init_texture(struct imv_texture *tex, SDL_Renderer *r) +{ + tex->renderer = r; + tex->num_chunks = 0; + tex->chunks = NULL; + + SDL_RendererInfo ri; + SDL_GetRendererInfo(r, &ri); + tex->chunk_width = ri.max_texture_width; + tex->chunk_height = ri.max_texture_height; +} + +void imv_destroy_texture(struct imv_texture *tex) +{ + if(tex->num_chunks > 0) { + for(int i = 0; i < tex->num_chunks; ++i) { + SDL_DestroyTexture(tex->chunks[i]); + } + free(tex->chunks); + tex->num_chunks = 0; + tex->chunks = NULL; + tex->renderer = NULL; + } +} + +int imv_texture_set_image(struct imv_texture *tex, FIBITMAP *image) +{ + FIBITMAP *frame = FreeImage_ConvertTo32Bits(image); + tex->width = FreeImage_GetWidth(frame); + tex->height = FreeImage_GetHeight(frame); + + char* pixels = (char*)FreeImage_GetBits(frame); + + /* figure out how many chunks are needed, and create them */ + if(tex->num_chunks > 0) { + for(int i = 0; i < tex->num_chunks; ++i) { + SDL_DestroyTexture(tex->chunks[i]); + } + free(tex->chunks); + } + + tex->num_chunks_wide = 1 + tex->width / tex->chunk_width; + tex->num_chunks_tall = 1 + tex->height / tex->chunk_height; + + tex->last_chunk_width = tex->width % tex->chunk_width; + tex->last_chunk_height = tex->height % tex->chunk_height; + + if(tex->last_chunk_width == 0) { + tex->last_chunk_width = tex->chunk_width; + } + if(tex->last_chunk_height == 0) { + tex->last_chunk_height = tex->chunk_height; + } + + tex->num_chunks = tex->num_chunks_wide * tex->num_chunks_tall; + tex->chunks = (SDL_Texture**)malloc(sizeof(SDL_Texture*) * tex->num_chunks); + + int failed_at = -1; + for(int y = 0; y < tex->num_chunks_tall; ++y) { + for(int x = 0; x < tex->num_chunks_wide; ++x) { + const int is_last_h_chunk = (x == tex->num_chunks_wide - 1); + const int is_last_v_chunk = (y == tex->num_chunks_tall - 1); + tex->chunks[x + y * tex->num_chunks_wide] = + SDL_CreateTexture(tex->renderer, + SDL_PIXELFORMAT_RGB888, + SDL_TEXTUREACCESS_STATIC, + is_last_h_chunk ? tex->last_chunk_width : tex->chunk_width, + is_last_v_chunk ? tex->last_chunk_height : tex->chunk_height); + if(tex->chunks[x + y * tex->num_chunks_wide] == NULL) { + failed_at = x + y * tex->num_chunks_wide; + break; + } + } + } + + if(failed_at != -1) { + for(int i = 0; i <= failed_at; ++i) { + SDL_DestroyTexture(tex->chunks[i]); + } + free(tex->chunks); + tex->num_chunks = 0; + tex->chunks = NULL; + return 1; + } + + for(int y = 0; y < tex->num_chunks_tall; ++y) { + for(int x = 0; x < tex->num_chunks_wide; ++x) { + ptrdiff_t offset = 4 * x * tex->chunk_width + + y * 4 * tex->width * tex->chunk_height; + char* addr = pixels + offset; + SDL_UpdateTexture(tex->chunks[x + y * tex->num_chunks_wide], + NULL, addr, 4 * tex->width); + } + } + + return 0; +} + +void imv_texture_draw(struct imv_texture *tex, int bx, int by, double scale) +{ + int offset_x = 0; + int offset_y = 0; + + SDL_RenderClear(tex->renderer); + for(int y = 0; y < tex->num_chunks_tall; ++y) { + for(int x = 0; x < tex->num_chunks_wide; ++x) { + int img_w, img_h, img_access; + unsigned int img_format; + SDL_QueryTexture(tex->chunks[x + y * tex->num_chunks_wide], + &img_format, &img_access, &img_w, &img_h); + SDL_Rect view_area = { + bx + offset_x, + by + offset_y, + img_w * scale, + img_h * scale + }; + SDL_RenderCopy(tex->renderer, + tex->chunks[x + y * tex->num_chunks_wide], NULL, &view_area); + offset_x += tex->chunk_width * scale; + } + offset_x = 0; + offset_y += tex->chunk_height * scale; + } + SDL_RenderPresent(tex->renderer); +} diff --git a/src/texture.h b/src/texture.h new file mode 100644 index 0000000..4f805a2 --- /dev/null +++ b/src/texture.h @@ -0,0 +1,46 @@ +#ifndef IMV_TEXTURE_H +#define IMV_TEXTURE_H + +/* Copyright (c) 2015 Harry Jeffery + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +#include +#include + +struct imv_texture { + int width; /* width of the texture overall */ + int height; /* height of the texture overall */ + int num_chunks; /* number of chunks allocated */ + SDL_Texture **chunks; /* array of chunks */ + int num_chunks_wide; /* number of chunks per row of the image */ + int num_chunks_tall; /* number of chunks per column of the image */ + int chunk_width; /* chunk width */ + int chunk_height; /* chunk height */ + int last_chunk_width; /* width of rightmost chunk */ + int last_chunk_height; /* height of bottommost chunk */ + SDL_Renderer *renderer; /* SDL renderer to draw to */ +}; + +void imv_init_texture(struct imv_texture *tex, SDL_Renderer *r); +void imv_destroy_texture(struct imv_texture *tex); + +int imv_texture_set_image(struct imv_texture *tex, FIBITMAP *image); +void imv_texture_draw(struct imv_texture *tex, int x, int y, double scale); + +#endif diff --git a/src/viewport.c b/src/viewport.c new file mode 100644 index 0000000..1630604 --- /dev/null +++ b/src/viewport.c @@ -0,0 +1,119 @@ +/* Copyright (c) 2015 Harry Jeffery + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +#include "viewport.h" +#include "image.h" + +void imv_init_viewport(struct imv_viewport *view, SDL_Window *window) +{ + view->window = window; + view->scale = 1; + view->x = view->y = view->fullscreen = view->redraw = 0; + view->playing = 1; +} + +void imv_destroy_viewport(struct imv_viewport *view) +{ + view->window = NULL; + return; +} + +void imv_viewport_toggle_fullscreen(struct imv_viewport *view) +{ + if(view->fullscreen) { + SDL_SetWindowFullscreen(view->window, 0); + view->fullscreen = 0; + } else { + SDL_SetWindowFullscreen(view->window, SDL_WINDOW_FULLSCREEN_DESKTOP); + view->fullscreen = 1; + } +} + +void imv_viewport_toggle_playing(struct imv_viewport *view, struct imv_image *img) +{ + if(view->playing) { + view->playing = 0; + } else if(imv_image_is_animated(img)) { + view->playing = 1; + } +} + +void imv_viewport_reset(struct imv_viewport *view) +{ + view->scale = 1; + view->x = view->y = 0; + view->redraw = 1; +} + +void imv_viewport_move(struct imv_viewport *view, int x, int y) +{ + view->x += x; + view->y += y; + view->redraw = 1; +} + +void imv_viewport_zoom(struct imv_viewport *view, int amount) +{ + view->scale += amount * 0.1; + if(view->scale > 100) + view->scale = 10; + else if (view->scale < 0.01) + view->scale = 0.1; + view->redraw = 1; +} + +void imv_viewport_center(struct imv_viewport *view, const struct imv_image* img) +{ + int ww, wh; + SDL_GetWindowSize(view->window, &ww, &wh); + + view->x = (ww - img->width * view->scale) / 2; + view->y = (wh - img->height * view->scale) / 2; + + view->redraw = 1; +} + +void imv_viewport_scale_to_window(struct imv_viewport *view, const struct imv_image* img) +{ + int ww, wh; + SDL_GetWindowSize(view->window, &ww, &wh); + + double window_aspect = (double)ww / (double)wh; + double image_aspect = (double)img->width / (double)img->height; + + if(window_aspect > image_aspect) { + /* Image will become too tall before it becomes too wide */ + view->scale = (double)wh / (double)img->height; + } else { + /* Image will become too wide before it becomes too tall */ + view->scale = (double)ww / (double)img->width; + } + + imv_viewport_center(view, img); +} + +void imv_viewport_set_redraw(struct imv_viewport *view) +{ + view->redraw = 1; +} + +void imv_viewport_set_title(struct imv_viewport *view, char* title) +{ + SDL_SetWindowTitle(view->window, title); +} diff --git a/src/viewport.h b/src/viewport.h new file mode 100644 index 0000000..2cff15c --- /dev/null +++ b/src/viewport.h @@ -0,0 +1,48 @@ +#ifndef IMV_VIEWPORT_H +#define IMV_VIEWPORT_H + +/* Copyright (c) 2015 Harry Jeffery + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +#include +#include "image.h" + +struct imv_viewport { + SDL_Window *window; + double scale; + int x, y; + int fullscreen; + int redraw; + int playing; +}; + +void imv_init_viewport(struct imv_viewport *view, SDL_Window *window); +void imv_destroy_viewport(struct imv_viewport *view); + +void imv_viewport_toggle_fullscreen(struct imv_viewport*); +void imv_viewport_toggle_playing(struct imv_viewport*, struct imv_image*); +void imv_viewport_reset(struct imv_viewport*); +void imv_viewport_move(struct imv_viewport*, int, int); +void imv_viewport_zoom(struct imv_viewport*, int); +void imv_viewport_center(struct imv_viewport*, const struct imv_image*); +void imv_viewport_scale_to_window(struct imv_viewport*, const struct imv_image*); +void imv_viewport_set_redraw(struct imv_viewport*); +void imv_viewport_set_title(struct imv_viewport*, char*); + +#endif diff --git a/texture.c b/texture.c deleted file mode 100644 index 7dd6157..0000000 --- a/texture.c +++ /dev/null @@ -1,146 +0,0 @@ -/* Copyright (c) 2015 Harry Jeffery - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - -#include "texture.h" - -void imv_init_texture(struct imv_texture *tex, SDL_Renderer *r) -{ - tex->renderer = r; - tex->num_chunks = 0; - tex->chunks = NULL; - - SDL_RendererInfo ri; - SDL_GetRendererInfo(r, &ri); - tex->chunk_width = ri.max_texture_width; - tex->chunk_height = ri.max_texture_height; -} - -void imv_destroy_texture(struct imv_texture *tex) -{ - if(tex->num_chunks > 0) { - for(int i = 0; i < tex->num_chunks; ++i) { - SDL_DestroyTexture(tex->chunks[i]); - } - free(tex->chunks); - tex->num_chunks = 0; - tex->chunks = NULL; - tex->renderer = NULL; - } -} - -int imv_texture_set_image(struct imv_texture *tex, FIBITMAP *image) -{ - FIBITMAP *frame = FreeImage_ConvertTo32Bits(image); - tex->width = FreeImage_GetWidth(frame); - tex->height = FreeImage_GetHeight(frame); - - char* pixels = (char*)FreeImage_GetBits(frame); - - /* figure out how many chunks are needed, and create them */ - if(tex->num_chunks > 0) { - for(int i = 0; i < tex->num_chunks; ++i) { - SDL_DestroyTexture(tex->chunks[i]); - } - free(tex->chunks); - } - - tex->num_chunks_wide = 1 + tex->width / tex->chunk_width; - tex->num_chunks_tall = 1 + tex->height / tex->chunk_height; - - tex->last_chunk_width = tex->width % tex->chunk_width; - tex->last_chunk_height = tex->height % tex->chunk_height; - - if(tex->last_chunk_width == 0) { - tex->last_chunk_width = tex->chunk_width; - } - if(tex->last_chunk_height == 0) { - tex->last_chunk_height = tex->chunk_height; - } - - tex->num_chunks = tex->num_chunks_wide * tex->num_chunks_tall; - tex->chunks = (SDL_Texture**)malloc(sizeof(SDL_Texture*) * tex->num_chunks); - - int failed_at = -1; - for(int y = 0; y < tex->num_chunks_tall; ++y) { - for(int x = 0; x < tex->num_chunks_wide; ++x) { - const int is_last_h_chunk = (x == tex->num_chunks_wide - 1); - const int is_last_v_chunk = (y == tex->num_chunks_tall - 1); - tex->chunks[x + y * tex->num_chunks_wide] = - SDL_CreateTexture(tex->renderer, - SDL_PIXELFORMAT_RGB888, - SDL_TEXTUREACCESS_STATIC, - is_last_h_chunk ? tex->last_chunk_width : tex->chunk_width, - is_last_v_chunk ? tex->last_chunk_height : tex->chunk_height); - if(tex->chunks[x + y * tex->num_chunks_wide] == NULL) { - failed_at = x + y * tex->num_chunks_wide; - break; - } - } - } - - if(failed_at != -1) { - for(int i = 0; i <= failed_at; ++i) { - SDL_DestroyTexture(tex->chunks[i]); - } - free(tex->chunks); - tex->num_chunks = 0; - tex->chunks = NULL; - return 1; - } - - for(int y = 0; y < tex->num_chunks_tall; ++y) { - for(int x = 0; x < tex->num_chunks_wide; ++x) { - ptrdiff_t offset = 4 * x * tex->chunk_width + - y * 4 * tex->width * tex->chunk_height; - char* addr = pixels + offset; - SDL_UpdateTexture(tex->chunks[x + y * tex->num_chunks_wide], - NULL, addr, 4 * tex->width); - } - } - - return 0; -} - -void imv_texture_draw(struct imv_texture *tex, int bx, int by, double scale) -{ - int offset_x = 0; - int offset_y = 0; - - SDL_RenderClear(tex->renderer); - for(int y = 0; y < tex->num_chunks_tall; ++y) { - for(int x = 0; x < tex->num_chunks_wide; ++x) { - int img_w, img_h, img_access; - unsigned int img_format; - SDL_QueryTexture(tex->chunks[x + y * tex->num_chunks_wide], - &img_format, &img_access, &img_w, &img_h); - SDL_Rect view_area = { - bx + offset_x, - by + offset_y, - img_w * scale, - img_h * scale - }; - SDL_RenderCopy(tex->renderer, - tex->chunks[x + y * tex->num_chunks_wide], NULL, &view_area); - offset_x += tex->chunk_width * scale; - } - offset_x = 0; - offset_y += tex->chunk_height * scale; - } - SDL_RenderPresent(tex->renderer); -} diff --git a/texture.h b/texture.h deleted file mode 100644 index 4f805a2..0000000 --- a/texture.h +++ /dev/null @@ -1,46 +0,0 @@ -#ifndef IMV_TEXTURE_H -#define IMV_TEXTURE_H - -/* Copyright (c) 2015 Harry Jeffery - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - -#include -#include - -struct imv_texture { - int width; /* width of the texture overall */ - int height; /* height of the texture overall */ - int num_chunks; /* number of chunks allocated */ - SDL_Texture **chunks; /* array of chunks */ - int num_chunks_wide; /* number of chunks per row of the image */ - int num_chunks_tall; /* number of chunks per column of the image */ - int chunk_width; /* chunk width */ - int chunk_height; /* chunk height */ - int last_chunk_width; /* width of rightmost chunk */ - int last_chunk_height; /* height of bottommost chunk */ - SDL_Renderer *renderer; /* SDL renderer to draw to */ -}; - -void imv_init_texture(struct imv_texture *tex, SDL_Renderer *r); -void imv_destroy_texture(struct imv_texture *tex); - -int imv_texture_set_image(struct imv_texture *tex, FIBITMAP *image); -void imv_texture_draw(struct imv_texture *tex, int x, int y, double scale); - -#endif diff --git a/viewport.c b/viewport.c deleted file mode 100644 index 1630604..0000000 --- a/viewport.c +++ /dev/null @@ -1,119 +0,0 @@ -/* Copyright (c) 2015 Harry Jeffery - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - -#include "viewport.h" -#include "image.h" - -void imv_init_viewport(struct imv_viewport *view, SDL_Window *window) -{ - view->window = window; - view->scale = 1; - view->x = view->y = view->fullscreen = view->redraw = 0; - view->playing = 1; -} - -void imv_destroy_viewport(struct imv_viewport *view) -{ - view->window = NULL; - return; -} - -void imv_viewport_toggle_fullscreen(struct imv_viewport *view) -{ - if(view->fullscreen) { - SDL_SetWindowFullscreen(view->window, 0); - view->fullscreen = 0; - } else { - SDL_SetWindowFullscreen(view->window, SDL_WINDOW_FULLSCREEN_DESKTOP); - view->fullscreen = 1; - } -} - -void imv_viewport_toggle_playing(struct imv_viewport *view, struct imv_image *img) -{ - if(view->playing) { - view->playing = 0; - } else if(imv_image_is_animated(img)) { - view->playing = 1; - } -} - -void imv_viewport_reset(struct imv_viewport *view) -{ - view->scale = 1; - view->x = view->y = 0; - view->redraw = 1; -} - -void imv_viewport_move(struct imv_viewport *view, int x, int y) -{ - view->x += x; - view->y += y; - view->redraw = 1; -} - -void imv_viewport_zoom(struct imv_viewport *view, int amount) -{ - view->scale += amount * 0.1; - if(view->scale > 100) - view->scale = 10; - else if (view->scale < 0.01) - view->scale = 0.1; - view->redraw = 1; -} - -void imv_viewport_center(struct imv_viewport *view, const struct imv_image* img) -{ - int ww, wh; - SDL_GetWindowSize(view->window, &ww, &wh); - - view->x = (ww - img->width * view->scale) / 2; - view->y = (wh - img->height * view->scale) / 2; - - view->redraw = 1; -} - -void imv_viewport_scale_to_window(struct imv_viewport *view, const struct imv_image* img) -{ - int ww, wh; - SDL_GetWindowSize(view->window, &ww, &wh); - - double window_aspect = (double)ww / (double)wh; - double image_aspect = (double)img->width / (double)img->height; - - if(window_aspect > image_aspect) { - /* Image will become too tall before it becomes too wide */ - view->scale = (double)wh / (double)img->height; - } else { - /* Image will become too wide before it becomes too tall */ - view->scale = (double)ww / (double)img->width; - } - - imv_viewport_center(view, img); -} - -void imv_viewport_set_redraw(struct imv_viewport *view) -{ - view->redraw = 1; -} - -void imv_viewport_set_title(struct imv_viewport *view, char* title) -{ - SDL_SetWindowTitle(view->window, title); -} diff --git a/viewport.h b/viewport.h deleted file mode 100644 index 2cff15c..0000000 --- a/viewport.h +++ /dev/null @@ -1,48 +0,0 @@ -#ifndef IMV_VIEWPORT_H -#define IMV_VIEWPORT_H - -/* Copyright (c) 2015 Harry Jeffery - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - -#include -#include "image.h" - -struct imv_viewport { - SDL_Window *window; - double scale; - int x, y; - int fullscreen; - int redraw; - int playing; -}; - -void imv_init_viewport(struct imv_viewport *view, SDL_Window *window); -void imv_destroy_viewport(struct imv_viewport *view); - -void imv_viewport_toggle_fullscreen(struct imv_viewport*); -void imv_viewport_toggle_playing(struct imv_viewport*, struct imv_image*); -void imv_viewport_reset(struct imv_viewport*); -void imv_viewport_move(struct imv_viewport*, int, int); -void imv_viewport_zoom(struct imv_viewport*, int); -void imv_viewport_center(struct imv_viewport*, const struct imv_image*); -void imv_viewport_scale_to_window(struct imv_viewport*, const struct imv_image*); -void imv_viewport_set_redraw(struct imv_viewport*); -void imv_viewport_set_title(struct imv_viewport*, char*); - -#endif -- cgit v1.2.3