Changeset View
Changeset View
Standalone View
Standalone View
source/blender/datatoc/datatoc_icon.c
| Show All 13 Lines | |||||
| * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||||
| */ | */ | ||||
| /** \file | /** \file | ||||
| * \ingroup datatoc | * \ingroup datatoc | ||||
| */ | */ | ||||
| #include <assert.h> | #include <assert.h> | ||||
| #include <errno.h> | |||||
| #include <stdio.h> | #include <stdio.h> | ||||
| #include <stdlib.h> | #include <stdlib.h> | ||||
| #include <string.h> | #include <string.h> | ||||
| /* for bool */ | /* for bool */ | ||||
| #include "../blenlib/BLI_sys_types.h" | #include "../blenlib/BLI_sys_types.h" | ||||
| /* for DIR */ | |||||
| #if !defined(WIN32) || defined(FREEWINDOWS) | |||||
| # include <dirent.h> | |||||
| #endif | |||||
| #include <png.h> | #include <png.h> | ||||
| /* for Win32 DIR functions */ | |||||
| #ifdef WIN32 | |||||
| # include "../blenlib/BLI_winstuff.h" | |||||
| #endif | |||||
| #ifdef WIN32 | #ifdef WIN32 | ||||
| # define SEP '\\' | # define SEP '\\' | ||||
| #else | #else | ||||
| # define SEP '/' | # define SEP '/' | ||||
| #endif | #endif | ||||
| #define ICON_GRID_COLS 26 | |||||
| #define ICON_GRID_ROWS 30 | |||||
| #define ICON_GRID_MARGIN 10 | |||||
| #define ICON_GRID_W 32 | |||||
| #define ICON_GRID_H 32 | |||||
| #define ICON_TEXTURE_W (ICON_GRID_COLS * (ICON_GRID_W + ICON_GRID_MARGIN) + ICON_GRID_MARGIN) | |||||
| #define ICON_TEXTURE_H (ICON_GRID_ROWS * (ICON_GRID_H + ICON_GRID_MARGIN) + ICON_GRID_MARGIN) | |||||
| const char *icon_names[] = { | |||||
| # define DEF_ICON(name) #name, | |||||
| # define DEF_ICON_COLOR(name) #name, | |||||
| # define DEF_ICON_BLANK(name) "", | |||||
| # define DEF_ICON_VECTOR(name) | |||||
| # include "../editors/include/UI_icons.h" | |||||
| }; | |||||
| /* -------------------------------------------------------------------- */ | /* -------------------------------------------------------------------- */ | ||||
| /* Utility functions */ | /* Utility functions */ | ||||
| static int path_ensure_slash(char *string) | static int path_ensure_slash(char *string) | ||||
| { | { | ||||
| int len = strlen(string); | int len = strlen(string); | ||||
| if (len == 0 || string[len - 1] != SEP) { | if (len == 0 || string[len - 1] != SEP) { | ||||
| string[len] = SEP; | string[len] = SEP; | ||||
| string[len + 1] = '\0'; | string[len + 1] = '\0'; | ||||
| return len + 1; | return len + 1; | ||||
| } | } | ||||
| return len; | return len; | ||||
| } | } | ||||
| static bool path_test_extension(const char *str, const char *ext) | |||||
| { | |||||
| const size_t a = strlen(str); | |||||
| const size_t b = strlen(ext); | |||||
| return !(a == 0 || b == 0 || b >= a) && (strcmp(ext, str + a - b) == 0); | |||||
| } | |||||
| static void endian_switch_uint32(uint *val) | static void endian_switch_uint32(uint *val) | ||||
| { | { | ||||
| uint tval = *val; | uint tval = *val; | ||||
| *val = ((tval >> 24)) | ((tval << 8) & 0x00ff0000) | ((tval >> 8) & 0x0000ff00) | ((tval << 24)); | *val = ((tval >> 24)) | ((tval << 8) & 0x00ff0000) | ((tval >> 8) & 0x0000ff00) | ((tval << 24)); | ||||
| } | } | ||||
| static const char *path_slash_rfind(const char *string) | |||||
| { | |||||
| const char *const lfslash = strrchr(string, '/'); | |||||
| const char *const lbslash = strrchr(string, '\\'); | |||||
| if (!lfslash) { | |||||
| return lbslash; | |||||
| } | |||||
| if (!lbslash) { | |||||
| return lfslash; | |||||
| } | |||||
| return (lfslash > lbslash) ? lfslash : lbslash; | |||||
| } | |||||
| static const char *path_basename(const char *path) | |||||
| { | |||||
| const char *const filename = path_slash_rfind(path); | |||||
| return filename ? filename + 1 : path; | |||||
| } | |||||
| /* -------------------------------------------------------------------- */ | /* -------------------------------------------------------------------- */ | ||||
| /* Write a PNG from RGBA pixels */ | /* Write a PNG from RGBA pixels */ | ||||
| static bool write_png(const char *name, const uint *pixels, const int width, const int height) | static bool write_png(const char *name, const uint *pixels, const int width, const int height) | ||||
| { | { | ||||
| png_structp png_ptr; | png_structp png_ptr; | ||||
| png_infop info_ptr; | png_infop info_ptr; | ||||
| png_bytepp row_pointers = NULL; | png_bytepp row_pointers = NULL; | ||||
| ▲ Show 20 Lines • Show All 89 Lines • ▼ Show 20 Lines | #endif | ||||
| return true; | return true; | ||||
| } | } | ||||
| /* -------------------------------------------------------------------- */ | /* -------------------------------------------------------------------- */ | ||||
| /* Merge icon-data from files */ | /* Merge icon-data from files */ | ||||
| struct IconHead { | struct IconHead { | ||||
| uint icon_w, icon_h; | uint icon_w, icon_h; | ||||
| uint orig_x, orig_y; | char hash[16]; | ||||
| uint canvas_w, canvas_h; | |||||
| }; | |||||
| struct IconInfo { | |||||
| struct IconHead head; | |||||
| char *file_name; | |||||
| }; | |||||
| struct IconMergeContext { | |||||
| /* Information about all icons read from disk. | |||||
| * Is used for sanity checks like prevention of two files defining icon for | |||||
| * the same position on canvas. */ | |||||
| int num_read_icons; | |||||
| struct IconInfo *read_icons; | |||||
| }; | }; | ||||
| static void icon_merge_context_init(struct IconMergeContext *context) | |||||
| { | |||||
| context->num_read_icons = 0; | |||||
| context->read_icons = NULL; | |||||
| } | |||||
| /* Get icon information from the context which matches given icon head. | |||||
| * Is used to check whether icon is re-defined, and to provide useful information about which | |||||
| * files are conflicting. */ | |||||
| static struct IconInfo *icon_merge_context_info_for_icon_head(struct IconMergeContext *context, | |||||
| struct IconHead *icon_head) | |||||
| { | |||||
| if (context->read_icons == NULL) { | |||||
| return NULL; | |||||
| } | |||||
| for (int i = 0; i < context->num_read_icons; i++) { | |||||
| struct IconInfo *read_icon_info = &context->read_icons[i]; | |||||
| const struct IconHead *read_icon_head = &read_icon_info->head; | |||||
| if (read_icon_head->orig_x == icon_head->orig_x && | |||||
| read_icon_head->orig_y == icon_head->orig_y) { | |||||
| return read_icon_info; | |||||
| } | |||||
| } | |||||
| return NULL; | |||||
| } | |||||
| static void icon_merge_context_register_icon(struct IconMergeContext *context, | |||||
| const char *file_name, | |||||
| const struct IconHead *icon_head) | |||||
| { | |||||
| context->read_icons = realloc(context->read_icons, | |||||
| sizeof(struct IconInfo) * (context->num_read_icons + 1)); | |||||
| struct IconInfo *icon_info = &context->read_icons[context->num_read_icons]; | |||||
| icon_info->head = *icon_head; | |||||
| icon_info->file_name = strdup(path_basename(file_name)); | |||||
| context->num_read_icons++; | |||||
| } | |||||
| static void icon_merge_context_free(struct IconMergeContext *context) | |||||
| { | |||||
| if (context->read_icons != NULL) { | |||||
| for (int i = 0; i < context->num_read_icons; i++) { | |||||
| free(context->read_icons[i].file_name); | |||||
| } | |||||
| free(context->read_icons); | |||||
| } | |||||
| } | |||||
| static bool icon_decode_head(FILE *f_src, struct IconHead *r_head) | static bool icon_decode_head(FILE *f_src, struct IconHead *r_head) | ||||
| { | { | ||||
| if (fread(r_head, 1, sizeof(*r_head), f_src) == sizeof(*r_head)) { | if (fread(r_head, 1, sizeof(*r_head), f_src) == sizeof(*r_head)) { | ||||
| #ifndef __LITTLE_ENDIAN__ | #ifndef __LITTLE_ENDIAN__ | ||||
| endian_switch_uint32(&r_head->icon_w); | endian_switch_uint32(&r_head->icon_w); | ||||
| endian_switch_uint32(&r_head->icon_h); | endian_switch_uint32(&r_head->icon_h); | ||||
| endian_switch_uint32(&r_head->orig_x); | endian_switch_uint32(&r_head->hash); | ||||
| endian_switch_uint32(&r_head->orig_y); | |||||
| endian_switch_uint32(&r_head->canvas_w); | |||||
| endian_switch_uint32(&r_head->canvas_h); | |||||
| #endif | #endif | ||||
| return true; | return true; | ||||
| } | } | ||||
| /* quiet warning */ | /* quiet warning */ | ||||
| (void)endian_switch_uint32; | (void)endian_switch_uint32; | ||||
| return false; | return false; | ||||
| Show All 38 Lines | static bool icon_read(const char *file_src, struct IconHead *r_head, uint **r_pixels) | ||||
| } | } | ||||
| success = icon_decode(f_src, r_head, r_pixels); | success = icon_decode(f_src, r_head, r_pixels); | ||||
| fclose(f_src); | fclose(f_src); | ||||
| return success; | return success; | ||||
| } | } | ||||
| static bool icon_merge(struct IconMergeContext *context, | static bool icon_merge(const char *file_src, | ||||
| const char *file_src, | |||||
| uint32_t **r_pixels_canvas, | uint32_t **r_pixels_canvas, | ||||
| uint *r_canvas_w, | uint *r_canvas_w, | ||||
| uint *r_canvas_h) | uint *r_canvas_h, | ||||
| uint orig_x, | |||||
| uint orig_y) | |||||
| { | { | ||||
| struct IconHead head; | struct IconHead head; | ||||
| uint *pixels; | uint *pixels; | ||||
| uint x, y; | uint x, y; | ||||
| /* canvas */ | /* canvas */ | ||||
| uint32_t *pixels_canvas; | uint32_t *pixels_canvas; | ||||
| uint canvas_w, canvas_h; | uint canvas_w, canvas_h; | ||||
| if (!icon_read(file_src, &head, &pixels)) { | if (!icon_read(file_src, &head, &pixels)) { | ||||
| return false; | return false; | ||||
| } | } | ||||
| struct IconInfo *read_icon_info = icon_merge_context_info_for_icon_head(context, &head); | const uint resolution_divider = ICON_GRID_W / head.icon_w; | ||||
| if (read_icon_info != NULL) { | const uint init_canvas_w = ICON_TEXTURE_W / resolution_divider; | ||||
| printf( | const uint init_canvas_h = ICON_TEXTURE_H / resolution_divider; | ||||
| "Conflicting icon files %s and %s\n", path_basename(file_src), read_icon_info->file_name); | |||||
| free(pixels); | |||||
| return false; | |||||
| } | |||||
| icon_merge_context_register_icon(context, file_src, &head); | |||||
| if (*r_canvas_w == 0) { | if (*r_canvas_w == 0) { | ||||
| /* init once */ | /* init once */ | ||||
| *r_canvas_w = head.canvas_w; | *r_canvas_w = init_canvas_w; | ||||
| *r_canvas_h = head.canvas_h; | *r_canvas_h = init_canvas_h; | ||||
| *r_pixels_canvas = calloc(1, (head.canvas_w * head.canvas_h) * sizeof(uint32_t)); | *r_pixels_canvas = calloc(1, (init_canvas_w * init_canvas_h) * sizeof(uint32_t)); | ||||
| } | } | ||||
| canvas_w = *r_canvas_w; | canvas_w = *r_canvas_w; | ||||
| canvas_h = *r_canvas_h; | canvas_h = *r_canvas_h; | ||||
| pixels_canvas = *r_pixels_canvas; | pixels_canvas = *r_pixels_canvas; | ||||
| assert(head.canvas_w == canvas_w); | assert(init_canvas_w == canvas_w); | ||||
| assert(head.canvas_h == canvas_h); | assert(init_canvas_h == canvas_h); | ||||
| orig_x /= resolution_divider; | |||||
| orig_y /= resolution_divider; | |||||
| for (x = 0; x < head.icon_w; x++) { | for (x = 0; x < head.icon_w; x++) { | ||||
| for (y = 0; y < head.icon_h; y++) { | for (y = 0; y < head.icon_h; y++) { | ||||
| uint pixel; | uint pixel; | ||||
| uint dst_x, dst_y; | uint dst_x, dst_y; | ||||
| uint pixel_xy_dst; | uint pixel_xy_dst; | ||||
| /* get pixel */ | /* get pixel */ | ||||
| pixel = pixels[(y * head.icon_w) + x]; | pixel = pixels[(y * head.icon_w) + x]; | ||||
| /* set pixel */ | /* set pixel */ | ||||
| dst_x = head.orig_x + x; | dst_x = orig_x + x; | ||||
| dst_y = head.orig_y + y; | dst_y = orig_y + y; | ||||
| pixel_xy_dst = (dst_y * canvas_w) + dst_x; | pixel_xy_dst = (dst_y * canvas_w) + dst_x; | ||||
| assert(pixel_xy_dst < (canvas_w * canvas_h)); | assert(pixel_xy_dst < (canvas_w * canvas_h)); | ||||
| pixels_canvas[pixel_xy_dst] = pixel; | pixels_canvas[pixel_xy_dst] = pixel; | ||||
| } | } | ||||
| } | } | ||||
| free(pixels); | free(pixels); | ||||
| /* only for bounds check */ | /* only for bounds check */ | ||||
| (void)canvas_h; | (void)canvas_h; | ||||
| return true; | return true; | ||||
| } | } | ||||
| static bool icondir_to_png(const char *path_src, const char *file_dst) | static bool icondir_to_png(const char *path_src, const char *file_dst, const char *file_prefix) | ||||
| { | { | ||||
| /* Takes a path full of 'dat' files and writes out */ | |||||
| DIR *dir; | |||||
| const struct dirent *fname; | |||||
| char filepath[1024]; | char filepath[1024]; | ||||
| char *filename; | char *filename; | ||||
| int path_str_len; | int path_str_len; | ||||
| int found = 0, fail = 0; | int found = 0, fail = 0; | ||||
| struct IconMergeContext context; | |||||
| uint32_t *pixels_canvas = NULL; | uint32_t *pixels_canvas = NULL; | ||||
| uint canvas_w = 0, canvas_h = 0; | uint canvas_w = 0, canvas_h = 0; | ||||
| icon_merge_context_init(&context); | |||||
| errno = 0; | |||||
| dir = opendir(path_src); | |||||
| if (dir == NULL) { | |||||
| printf( | |||||
| "%s: failed to dir '%s', (%s)\n", __func__, path_src, errno ? strerror(errno) : "unknown"); | |||||
| return false; | |||||
| } | |||||
| strcpy(filepath, path_src); | strcpy(filepath, path_src); | ||||
| path_str_len = path_ensure_slash(filepath); | path_str_len = path_ensure_slash(filepath); | ||||
| filename = &filepath[path_str_len]; | filename = &filepath[path_str_len]; | ||||
| while ((fname = readdir(dir)) != NULL) { | strcpy(filename, file_prefix); | ||||
| if (path_test_extension(fname->d_name, ".dat")) { | filename += strlen(file_prefix); | ||||
| strcpy(filename, fname->d_name); | for (int y = 0; y < ICON_GRID_ROWS; y++) { | ||||
| for (int x = 0; x < ICON_GRID_COLS; x++) { | |||||
| const int pos_x = x * (ICON_GRID_W + ICON_GRID_MARGIN) + ICON_GRID_MARGIN; | |||||
| const int pos_y = y * (ICON_GRID_H + ICON_GRID_MARGIN) + ICON_GRID_MARGIN; | |||||
| if (icon_merge(&context, filepath, &pixels_canvas, &canvas_w, &canvas_h)) { | const char *icon_name = icon_names[y * ICON_GRID_COLS + x]; | ||||
| if (icon_name[0] == '\0') { | |||||
| continue; | |||||
| } | |||||
| if (strcmp(icon_name, "NONE") == 0) { | |||||
| continue; | |||||
| } | |||||
| if (strcmp(icon_name, "BLANK1") == 0) { | |||||
| continue; | |||||
| } | |||||
| char name[strlen(icon_name)]; | |||||
| strcpy(name, icon_name); | |||||
| for (int i = 0; name[i]; i++) { | |||||
| if (name[i] >= 'A' && name[i] <= 'Z') { | |||||
| name[i] += 'a' - 'A'; | |||||
| } | |||||
| } | |||||
| strcpy(filename, name); | |||||
| strcat(filepath, ".dat"); | |||||
| if (icon_merge(filepath, &pixels_canvas, &canvas_w, &canvas_h, pos_x, pos_y)) { | |||||
| found++; | found++; | ||||
| } | } | ||||
| else { | else { | ||||
| fail++; | fail++; | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| icon_merge_context_free(&context); | |||||
| closedir(dir); | |||||
| if (found == 0) { | if (found == 0) { | ||||
| printf("%s: dir '%s' has no icons\n", __func__, path_src); | printf("%s: dir '%s' has no icons\n", __func__, path_src); | ||||
| } | } | ||||
| if (fail != 0) { | if (fail != 0) { | ||||
| printf("%s: dir '%s' failed %d icons\n", __func__, path_src, fail); | printf("%s: dir '%s' failed %d icons\n", __func__, path_src, fail); | ||||
| } | } | ||||
| /* Write pixels. */ | /* Write pixels. */ | ||||
| write_png(file_dst, pixels_canvas, canvas_w, canvas_h); | write_png(file_dst, pixels_canvas, canvas_w, canvas_h); | ||||
| free(pixels_canvas); | free(pixels_canvas); | ||||
| return (fail == 0); | return (fail == 0); | ||||
| } | } | ||||
| /* -------------------------------------------------------------------- */ | /* -------------------------------------------------------------------- */ | ||||
| /* Main and parse args */ | /* Main and parse args */ | ||||
| int main(int argc, char **argv) | int main(int argc, char **argv) | ||||
| { | { | ||||
| const char *path_src; | const char *path_src; | ||||
| const char *file_dst; | const char *file_dst; | ||||
| const char *file_prefix; | |||||
| if (argc < 3) { | if (argc < 4) { | ||||
| printf("Usage: datatoc_icon <dir_icons> <data_icon_to.png>\n"); | printf("Usage: datatoc_icon <dir_icons> <data_icon_to.png> <icon_>\n"); | ||||
| exit(1); | exit(1); | ||||
| } | } | ||||
| path_src = argv[1]; | path_src = argv[1]; | ||||
| file_dst = argv[2]; | file_dst = argv[2]; | ||||
| file_prefix = argv[3]; | |||||
| return (icondir_to_png(path_src, file_dst) == true) ? 0 : 1; | return (icondir_to_png(path_src, file_dst, file_prefix) == true) ? 0 : 1; | ||||
| } | } | ||||