emote2ss

Animated webp to spritesheets converting tool
git clone git://bsandro.tech/emote2ss
Log | Files | Refs | README | LICENSE

ui.c (11765B)


      1 #define _DEFAULT_SOURCE
      2 
      3 #include <dirent.h>
      4 #include "fileops.h"
      5 #include "animation.h"
      6 #include "ui.h"
      7 #include "spritesheet.h"
      8 
      9 static int cols = 1;
     10 static volatile UIWindow *modal_win = NULL;
     11 static UIImageDisplay *img_disp = NULL;
     12 static UILabel *lbl_header = NULL;
     13 static Animation *img = NULL;
     14 
     15 static int FilelistFilter(const struct dirent *f) {
     16 	if (strncmp(f->d_name, ".", 255)==0) return 0;
     17 	if (f->d_type==DT_DIR) return 1;
     18 	//@todo not very efficient but dirent doesn't have the length :c
     19 	size_t len = strlen(f->d_name);
     20 	// checking for the ".webp" at the very end
     21 	if (len<6) return 0;
     22 	return strncmp(f->d_name+len-5, ".webp", 255)==0;
     23 }
     24 
     25 static int FilelistCompare(const struct dirent **a, const struct dirent **b) {
     26 	if ((*a)->d_type==DT_DIR && (*b)->d_type==DT_DIR) {
     27 		if (strncmp((*a)->d_name, "..", 255)==0) return -1;
     28 		else if (strncmp((*b)->d_name, "..", 255)==0) return 1;
     29 		else return alphasort(a, b);
     30 	}
     31 	if ((*a)->d_type==DT_DIR) return -1;
     32 	if ((*b)->d_type==DT_DIR) return 1;
     33 	return alphasort(a, b);
     34 }
     35 
     36 UIWindow * MainWindowCreate(const char *wname, int w, int h) {
     37 	UIWindow *win = UIWindowCreate(0, 0, wname, w, h);
     38 	win->e.messageUser = WinMainEvent;
     39 	UIPanel *panelv = UIPanelCreate(&win->e, UI_PANEL_GRAY|UI_PANEL_MEDIUM_SPACING);
     40 	lbl_header = UILabelCreate(&panelv->e, 0, "webp 2 spritesheet converter", -1);
     41 
     42 	UIPanel *panelh = UIPanelCreate(&panelv->e, UI_PANEL_GRAY|UI_PANEL_MEDIUM_SPACING|UI_PANEL_HORIZONTAL);
     43 	UILabel *label = UILabelCreate(&panelh->e, 0, "Size: 0x0px", -1);
     44 	UISlider *slider = UISliderCreate(&panelh->e, 0);
     45 	UIButton *btnopen = UIButtonCreate(&panelh->e, 0, "Open", -1);
     46 	btnopen->e.messageUser = ButtonOpenEvent;
     47 	UIButton *btnsave = UIButtonCreate(&panelh->e, 0, "Save", -1);
     48 	btnsave->e.messageUser = ButtonSaveEvent;
     49 	btnsave->e.cp = win;
     50 	UIButton *btn_exit = UIButtonCreate(&panelh->e, 0, "Close", -1);
     51 	btn_exit->e.messageUser = ButtonCloseEvent;
     52 
     53 	img_disp = UIImageDisplayCreate(&panelv->e, UI_ELEMENT_V_FILL|UI_ELEMENT_H_FILL|UI_IMAGE_DISPLAY_INTERACTIVE|_UI_IMAGE_DISPLAY_ZOOM_FIT, NULL, 0, 0, 0);
     54 	img_disp->e.cp = label;
     55 	slider->e.messageUser = SliderEvent;
     56 	slider->e.cp = img_disp;
     57 	return win;
     58 }
     59 
     60 int WinModalEvent(UIElement *element, UIMessage msg, int di, void *dp) {
     61 	if (msg == UI_MSG_DESTROY) {
     62 		// printf("modal bye\n");
     63 		assert(element==modal_win);
     64 		modal_win = NULL;
     65 	}
     66 	return 0;
     67 }
     68 
     69 int ButtonDialogSaveEvent(UIElement *element, UIMessage msg, int di, void *dp) {
     70 	if (msg == UI_MSG_CLICKED) {
     71 		// get values of path and filename inputs
     72 		UITextbox *path_input = (UITextbox *)element->parent->children;
     73 		UITextbox *filename_input = (UITextbox *)path_input->e.next;
     74 		// printf("path_input: %p\nfilename_input: %p\n", path_input, filename_input);
     75 		// that printf might work incorrectly because path_input->string might not contain valid C string with \0 at the end
     76 		// printf("path_input: %s(%d)\nfilename_input: %s(%d)\n", path_input->string, path_input->bytes, filename_input->string, filename_input->bytes);
     77 		int path_len = path_input->bytes + filename_input->bytes + 2; // DIR_SEPARATOR and '\0'
     78 		// printf("path_len: %d\n", path_len);
     79 		char out_name[path_len];
     80 		out_name[path_input->bytes] = DIR_SEPARATOR;
     81 		out_name[path_len-1] = '\0';
     82 		memcpy(out_name, path_input->string, path_input->bytes);
     83 		memcpy(out_name+path_input->bytes+1, filename_input->string, filename_input->bytes);
     84 		// printf("strlen(out_name): %d\n", strlen(out_name));
     85 		// printf("out_name: %s\n", out_name);
     86 		WebpWrite(out_name, img, cols);
     87 		// printf("save dialog window close\n");
     88 		assert(element->window==modal_win);
     89 		UIElementDestroy(element->window);
     90 	}
     91 	return 0;
     92  }
     93 
     94 int ButtonDialogOpenEvent(UIElement *element, UIMessage msg, int di, void *dp) {
     95 	if (msg == UI_MSG_CLICKED) {
     96 		// printf("open dialog window close\n");
     97 		UITextbox *path_input = (UITextbox *)element->parent->children;
     98 		UITextbox *filename_input = (UITextbox *)path_input->e.next;
     99 		// printf("path_input: %p\nfilename_input: %p\n", path_input, filename_input);
    100 		// printf("path_input: %s(%d)\nfilename_input: %s(%d)\n", path_input->string, strlen(path_input->string), filename_input->string, strlen(filename_input->string));
    101 
    102 		UIElementDestroy(element->window);
    103 
    104 		int path_len = path_input->bytes + filename_input->bytes + 2; // DIR_SEPARATOR and '\0'
    105 		char filepath[path_len];
    106 		filepath[path_input->bytes] = DIR_SEPARATOR;
    107 		filepath[path_len-1] = '\0';
    108 		memcpy(filepath, path_input->string, path_input->bytes);
    109 		memcpy(filepath+path_input->bytes+1, filename_input->string, filename_input->bytes);
    110 
    111 		if (img != NULL) {
    112 			ImageUnload(&img);
    113 			assert(img==NULL);
    114 		}
    115 		img = ImageLoad(filepath);
    116 		assert(img!=NULL);
    117 		PreviewUpdate(img, img_disp);
    118 		UILabelSetContent(lbl_header, img->path, -1);
    119 	}
    120 	return 0;
    121 }
    122 
    123 int TableEvent(UIElement *element, UIMessage msg, int di, void *dp) {
    124 	static int selected = -1;
    125 	static struct dirent **filelist = NULL;
    126 	char *dir_name = element->cp;
    127 	UITable *table = (UITable *)element;
    128 	if (filelist==NULL) {
    129 		table->itemCount = scandir(dir_name, &filelist, FilelistFilter, FilelistCompare);
    130 		// printf("populated dir %s with %d items\n", dir_name, table->itemCount);
    131 	}
    132 	if (msg==UI_MSG_TABLE_GET_ITEM) {
    133 		UITableGetItem *m = (UITableGetItem *)dp;
    134 		m->isSelected = selected==m->index;
    135 		bool is_dir = filelist[m->index]->d_type==DT_DIR;
    136 		//printf("render %s (%d)\n", filelist[m->index]->d_name, m->bufferBytes);
    137 		int ret = snprintf(m->buffer, m->bufferBytes, is_dir?"[%s]":"%s", filelist[m->index]->d_name);
    138 		//printf("%s - %d (%d)\n", filelist[m->index]->d_name, ret, m->bufferBytes);
    139 		return ret;
    140 	} else if (msg==UI_MSG_CLICKED) {
    141 		int hit = UITableHitTest(table, element->window->cursorX, element->window->cursorY);
    142 		if (hit!=-1 && selected==hit) {
    143 			if (filelist[hit]->d_type==DT_DIR) {
    144 				char newpath[PATH_MAX];
    145 				bzero(newpath, PATH_MAX);
    146 				strcpy(newpath, element->cp);
    147 				strcat(newpath, "/");
    148 				strcat(newpath, filelist[hit]->d_name);
    149 				free(element->cp);
    150 				element->cp = realpath(newpath, NULL);
    151 				//free(filelist);
    152 				filelist = NULL;
    153 				selected = -1;
    154 				//@todo duplicated code
    155 				UIPanel *panel_out = (UIPanel *)element->parent;
    156 				UILabel *label = (UILabel *)panel_out->e.children;
    157 				UIPanel *panel_top = (UIPanel *)label->e.next;
    158 				UITextbox *path_input = (UITextbox *)panel_top->e.children;
    159 				UITextboxClear(path_input, false);
    160 				UITextboxReplace(path_input, (char *)element->cp, -1, false);
    161 				UIElementRepaint(path_input, NULL);
    162 				UITableEnsureVisible(table, -1);
    163 			}
    164 		} else {
    165 			selected = hit;
    166 			if (!UITableEnsureVisible(table, selected)) {
    167 				UIElementRepaint(element, NULL);
    168 			}
    169 			//@todo remove copypasta
    170 			if (hit!=-1 && filelist[hit]->d_type!=DT_DIR) {
    171 				UIPanel *panel_out = (UIPanel *)element->parent;
    172 				UILabel *label = (UILabel *)panel_out->e.children;
    173 				UIPanel *panel_top = (UIPanel *)label->e.next;
    174 				UITextbox *path_input = (UITextbox *)panel_top->e.children;
    175 				UITextbox *file_input = (UITextbox *)path_input->e.next;
    176 				UITextboxClear(path_input, false);
    177 				UITextboxClear(file_input, false);
    178 				UITextboxReplace(path_input, (char *)element->cp, -1, false);
    179 				UITextboxReplace(file_input, filelist[hit]->d_name, -1, false);
    180 				UIElementRepaint(path_input, NULL);
    181 				UIElementRepaint(file_input, NULL);
    182 			}
    183 		}
    184 	} else if (msg==UI_MSG_DESTROY) {
    185 		free(filelist);
    186 		filelist = NULL;
    187 		selected = -1;
    188 		UIElement *el = table->e.children;
    189 			printf("child %p\n", el);
    190 		while (el!=NULL) {
    191 			printf("child %p\n", el);
    192 		}
    193 	} else if (msg==UI_MSG_UPDATE) {
    194 		UITableResizeColumns(table);
    195 	}
    196 	return 0;
    197 }
    198 
    199 int ButtonCloseEvent(UIElement *element, UIMessage msg, int di, void *dp) {
    200 	if (msg == UI_MSG_CLICKED) {
    201 		UIElementDestroy(element->window);
    202 	}
    203 	return 0;
    204 }
    205 
    206 void ShowModalWindow(UIWindow *parent, const char *def_dir, const char *def_file, CallbackFn cb) {
    207 	if (modal_win == NULL) {
    208 		// printf("create modal window\n");
    209 		int w = UIElementMessage(parent, UI_MSG_GET_WIDTH, 0, 0);
    210 		int h = UIElementMessage(parent, UI_MSG_GET_HEIGHT, 0, 0);
    211 		modal_win = UIWindowCreate(parent, NULL, def_file?"Save File":"Open File", w, h);
    212 		modal_win->e.messageUser = WinModalEvent;
    213 		UIPanel *panel_out = UIPanelCreate(&modal_win->e, UI_PANEL_GRAY|UI_PANEL_EXPAND);
    214 		UILabel *lbl_title = UILabelCreate(&panel_out->e, 0, def_file?"Save File":"Open File", -1);
    215 		UIPanel *panel_top = UIPanelCreate(&panel_out->e, UI_PANEL_GRAY|UI_PANEL_MEDIUM_SPACING|UI_PANEL_HORIZONTAL);
    216 		UITextbox *path_input = UITextboxCreate(&panel_top->e, UI_ELEMENT_DISABLED|UI_ELEMENT_H_FILL);
    217 		if (def_dir != NULL) {
    218 			UITextboxReplace(path_input, def_dir, -1, false);
    219 		}
    220 
    221 		UITextbox *filename_input = UITextboxCreate(&panel_top->e, UI_ELEMENT_TAB_STOP|UI_ELEMENT_H_FILL);
    222 
    223 		printf("path_input: %p\nfilename_input: %p\n", path_input, filename_input);
    224 		if (def_file != NULL) {
    225 			UITextboxReplace(filename_input, def_file, -1, false);
    226 		}
    227 
    228 		UIButton *btn_save = UIButtonCreate(&panel_top->e, UI_ELEMENT_TAB_STOP, def_file?"Save":"Open", -1);
    229 		btn_save->e.messageUser = cb;
    230 
    231 		UIButton *btn_cancel = UIButtonCreate(&panel_top->e, UI_ELEMENT_TAB_STOP, "Cancel", -1);
    232 		btn_cancel->e.messageUser = ButtonCloseEvent;
    233 
    234 		UITable *table = UITableCreate(&panel_out->e, UI_ELEMENT_V_FILL, "Directory");
    235 		table->itemCount = 1; // at least '..' element
    236 		// @todo setting the initial directory this way is 100% trashcan
    237 		char *init_path = (char *)malloc(2*sizeof(char));
    238 		init_path[0] = '.';
    239 		init_path[1] = '\0';
    240 		table->e.cp = (void *)init_path;
    241 		table->e.messageUser = TableEvent;
    242 		UITableResizeColumns(table);
    243 	} else {
    244 		UIElementFocus(modal_win);
    245 	}
    246 }
    247 
    248 int ButtonSaveEvent(UIElement *element, UIMessage msg, int di, void *dp) {
    249 	if (msg == UI_MSG_CLICKED) {
    250 		// printf("save button clicked\n");
    251 		char fname[strlen(img->filename)+7];
    252 		int n = snprintf(fname, NAME_MAX, "atlas_%s", img->filename);
    253 		assert(n>0);
    254 		ShowModalWindow(element->window, img->dirname, fname, ButtonDialogSaveEvent);
    255 	}
    256 	return 0;
    257 }
    258 
    259 int ButtonOpenEvent(UIElement *element, UIMessage msg, int di, void *dp) {
    260 	if (msg == UI_MSG_CLICKED) {
    261 		// printf("open button clicked\n");
    262 		ShowModalWindow(element->window, getenv("HOME"), NULL, ButtonDialogOpenEvent);
    263 	}
    264 	return 0;
    265 }
    266 
    267 int SliderEvent(UIElement *element, UIMessage msg, int di, void *dp) {
    268 	if (msg == UI_MSG_VALUE_CHANGED) {
    269 		if (img == NULL) return 1;
    270 
    271 		float slider_pos = ((UISlider *)element)->position;
    272 		float step = 1.0f / (float)img->frame_count;
    273 		int new_cols = (int)(slider_pos / step);
    274 		if (new_cols > 0 && cols != new_cols) {
    275 			//printf("new_cols: %d\n", new_cols);
    276 			cols = new_cols;
    277 			PreviewUpdate(img, element->cp);
    278 		}
    279 	}
    280 
    281 	return 0;
    282 }
    283 
    284 int WinMainEvent(UIElement *element, UIMessage msg, int di, void *dp) {
    285 	if (msg == UI_MSG_DESTROY) {
    286 		// printf("bye\n");
    287 		free(img);
    288 		exit(0);
    289 	}
    290 	return 0;
    291 }
    292 
    293 void PreviewUpdate(Animation *img, UIImageDisplay *img_disp) {
    294 	// gen new spritesheet and refresh the preview
    295 	Spritesheet ss = SpritesheetGen(img, cols);
    296 	//printf("spritesheet width: %d, height: %d, stride: %d, len: %zu\n", ss.width, ss.height, ss.stride, ss.len);
    297 
    298 	uint32_t *frame0 = calloc(ss.width*ss.height, sizeof(uint32_t));
    299 	assert(frame0!=NULL);
    300 
    301 	for (uint32_t i=0; i<ss.width*ss.height; ++i) {
    302 		frame0[i] = UI_COLOR_FROM_RGBA(ss.data[i*4+0], ss.data[i*4+1], ss.data[i*4+2], ss.data[i*4+3]);
    303 	}
    304 	UIImageDisplaySetContent(img_disp, frame0, ss.width, ss.height, ss.stride);
    305 	UIElementRefresh(img_disp->e.parent);
    306 	UIElementRefresh(&img_disp->e);
    307 
    308 	free(frame0);
    309 	free(ss.data);
    310 
    311 	UILabel *label = (UILabel *)img_disp->e.cp;
    312 	char label_text[256] = {0};
    313 	snprintf(&label_text, 255, "Size: %4dx%4dpx", ss.width, ss.height);
    314 	UILabelSetContent(label, label_text, -1);
    315 	UIElementRefresh(label->e.parent);
    316 }