"Fossies" - the Fresh Open Source Software Archive

Member "feh-3.4.1/src/thumbnail.c" (29 May 2020, 23543 Bytes) of package /linux/privat/feh-3.4.1.tar.bz2:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "thumbnail.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 3.4_vs_3.4.1.

    1 /* thumbnail.c
    2 
    3 Copyright (C) 1999-2003 Tom Gilbert.
    4 Copyright (C) 2010-2020 Daniel Friesel.
    5 
    6 Permission is hereby granted, free of charge, to any person obtaining a copy
    7 of this software and associated documentation files (the "Software"), to
    8 deal in the Software without restriction, including without limitation the
    9 rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
   10 sell copies of the Software, and to permit persons to whom the Software is
   11 furnished to do so, subject to the following conditions:
   12 
   13 The above copyright notice and this permission notice shall be included in
   14 all copies of the Software and its documentation and acknowledgment shall be
   15 given in the documentation and software packages that this Software was
   16 used.
   17 
   18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
   21 THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
   22 IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
   23 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
   24 
   25 */
   26 
   27 #include "feh.h"
   28 #include "filelist.h"
   29 #include "winwidget.h"
   30 #include "options.h"
   31 #include "thumbnail.h"
   32 #include "md5.h"
   33 #include "feh_png.h"
   34 #include "index.h"
   35 #include "signals.h"
   36 
   37 static gib_list *thumbnails = NULL;
   38 
   39 static thumbmode_data td;
   40 
   41 /* TODO Break this up a bit ;) */
   42 /* TODO s/bit/lot */
   43 void init_thumbnail_mode(void)
   44 {
   45     /* moved to thumbnail_data:
   46        Imlib_Image im_main;
   47        Imlib_Image bg_im = NULL;
   48        Imlib_Font fn = NULL;
   49        Imlib_Font title_fn = NULL;
   50 
   51        int w = 800, h = 600;
   52        int bg_w = 0, bg_h = 0;
   53 
   54        int text_area_w, text_area_h;
   55        int max_column_w = 0;
   56      */
   57 
   58     Imlib_Load_Error err;
   59     Imlib_Image im_temp;
   60     int ww = 0, hh = 0, www, hhh, xxx, yyy;
   61     int orig_w, orig_h;
   62     int x = 0, y = 0;
   63     winwidget winwid = NULL;
   64     Imlib_Image im_thumb = NULL;
   65     unsigned char trans_bg = 0;
   66     int title_area_h = 0;
   67     int tw = 0, th = 0;
   68     int fw, fh;
   69     int thumbnailcount = 0;
   70     feh_file *file = NULL;
   71     gib_list *l, *last = NULL;
   72     int lineno;
   73     int index_image_width, index_image_height;
   74     unsigned int thumb_counter = 0;
   75     gib_list *line, *lines;
   76 
   77     /* initialize thumbnail mode data */
   78     td.im_main = NULL;
   79     td.im_bg = NULL;
   80     td.font_main = NULL;
   81     td.font_title = NULL;
   82 
   83     td.w = 640;
   84     td.h = 480;
   85     td.bg_w = 0;
   86     td.bg_h = 0;
   87     td.thumb_tot_h = 0;
   88     td.text_area_w = 0;
   89     td.text_area_h = 0;
   90 
   91     td.vertical = 0;
   92     td.max_column_w = 0;
   93 
   94     if (!opt.thumb_title)
   95         opt.thumb_title = "%n";
   96     mode = "thumbnail";
   97 
   98     if (opt.font)
   99         td.font_main = gib_imlib_load_font(opt.font);
  100 
  101     if (!td.font_main)
  102         td.font_main = gib_imlib_load_font(DEFAULT_FONT);
  103 
  104     if (opt.title_font) {
  105         int fh, fw;
  106 
  107         td.font_title = gib_imlib_load_font(opt.title_font);
  108         if (!td.font_title)
  109             td.font_title = gib_imlib_load_font(DEFAULT_FONT_TITLE);
  110 
  111         gib_imlib_get_text_size(td.font_title, "W", NULL, &fw, &fh,
  112                 IMLIB_TEXT_TO_RIGHT);
  113         title_area_h = fh + 4;
  114     } else
  115         td.font_title = imlib_load_font(DEFAULT_FONT_TITLE);
  116 
  117     if ((!td.font_main) || (!td.font_title))
  118         eprintf("Error loading fonts");
  119 
  120     /* Work out how tall the font is */
  121     gib_imlib_get_text_size(td.font_main, "W", NULL, &tw, &th,
  122             IMLIB_TEXT_TO_RIGHT);
  123     get_index_string_dim(NULL, td.font_main, &fw, &fh);
  124     td.text_area_h = fh + 5;
  125 
  126     /* This includes the text area for index data */
  127     td.thumb_tot_h = opt.thumb_h + td.text_area_h;
  128 
  129     /* Use bg image dimensions for default size */
  130     if (opt.bg && opt.bg_file) {
  131         if (!strcmp(opt.bg_file, "trans"))
  132             trans_bg = 1;
  133         else {
  134 
  135             D(("Time to apply a background to blend onto\n"));
  136             if (feh_load_image_char(&td.im_bg, opt.bg_file) != 0) {
  137                 td.bg_w = gib_imlib_image_get_width(td.im_bg);
  138                 td.bg_h = gib_imlib_image_get_height(td.im_bg);
  139             }
  140         }
  141     }
  142 
  143     /* figure out geometry for the main window and entries */
  144     feh_thumbnail_calculate_geometry();
  145 
  146     index_image_width = td.w;
  147     index_image_height = td.h + title_area_h;
  148     D(("imlib_create_image(%d, %d)\n", index_image_width, index_image_height));
  149     td.im_main = imlib_create_image(index_image_width, index_image_height);
  150 
  151     if (!td.im_main) {
  152         if (index_image_height >= 32768 || index_image_width >= 32768) {
  153             eprintf("Failed to create %dx%d pixels (%d MB) index image.\n"
  154                     "This is probably due to Imlib2 issues when dealing with images larger than 32k x 32k pixels.",
  155                     index_image_width, index_image_height, index_image_width * index_image_height * 4 / (1024*1024));
  156         } else {
  157             eprintf("Failed to create %dx%d pixels (%d MB) index image. Do you have enough RAM?",
  158                     index_image_width, index_image_height, index_image_width * index_image_height * 4 / (1024*1024));
  159         }
  160     }
  161 
  162     gib_imlib_image_set_has_alpha(td.im_main, 1);
  163 
  164     if (td.im_bg)
  165         gib_imlib_blend_image_onto_image(td.im_main, td.im_bg,
  166                          gib_imlib_image_has_alpha
  167                          (td.im_bg), 0, 0, td.bg_w, td.bg_h, 0, 0,
  168                          td.w, td.h, 1, 0, 0);
  169     else if (trans_bg) {
  170         gib_imlib_image_fill_rectangle(td.im_main, 0, 0, td.w,
  171                 td.h + title_area_h, 0, 0, 0, 0);
  172         gib_imlib_image_set_has_alpha(td.im_main, 1);
  173     } else {
  174         /* Colour the background */
  175         gib_imlib_image_fill_rectangle(td.im_main, 0, 0, td.w,
  176                 td.h + title_area_h, 0, 0, 0, 255);
  177     }
  178 
  179 
  180     if (opt.display) {
  181         winwid = winwidget_create_from_image(td.im_main, WIN_TYPE_THUMBNAIL);
  182         winwidget_rename(winwid, PACKAGE " [thumbnail mode]");
  183         winwidget_show(winwid);
  184     }
  185 
  186     /* make sure we have an ~/.thumbnails/normal directory for storing
  187        permanent thumbnails */
  188     td.cache_thumbnails = opt.cache_thumbnails;
  189 
  190     if (td.cache_thumbnails) {
  191         if (opt.thumb_w > opt.thumb_h)
  192             td.cache_dim = opt.thumb_w;
  193         else
  194             td.cache_dim = opt.thumb_h;
  195 
  196         if (td.cache_dim > 256) {
  197             /* No caching as specified by standard. Sort of. */
  198             td.cache_thumbnails = 0;
  199         } else if (td.cache_dim > 128) {
  200             td.cache_dim = 256;
  201             td.cache_dir = estrdup("large");
  202         } else {
  203             td.cache_dim = 128;
  204             td.cache_dir = estrdup("normal");
  205         }
  206         feh_thumbnail_setup_thumbnail_dir();
  207     }
  208 
  209     for (l = filelist; l; l = l->next) {
  210         file = FEH_FILE(l->data);
  211         if (last) {
  212             filelist = feh_file_remove_from_list(filelist, last);
  213             last = NULL;
  214         }
  215         D(("About to load image %s\n", file->filename));
  216         /*      if (feh_load_image(&im_temp, file) != 0) */
  217         if (feh_thumbnail_get_thumbnail(&im_temp, file, &orig_w, &orig_h)
  218                 != 0) {
  219             if (opt.verbose)
  220                 feh_display_status('.');
  221             D(("Successfully loaded %s\n", file->filename));
  222             www = opt.thumb_w;
  223             hhh = opt.thumb_h;
  224             ww = gib_imlib_image_get_width(im_temp);
  225             hh = gib_imlib_image_get_height(im_temp);
  226 
  227             if (!orig_w) {
  228                 orig_w = ww;
  229                 orig_h = hh;
  230             }
  231 
  232             thumbnailcount++;
  233             if (gib_imlib_image_has_alpha(im_temp))
  234                 imlib_context_set_blend(1);
  235             else
  236                 imlib_context_set_blend(0);
  237 
  238             if (opt.aspect) {
  239                 double ratio = 0.0;
  240 
  241                 /* Keep the aspect ratio for the thumbnail */
  242                 ratio = ((double) ww / hh) / ((double) www / hhh);
  243 
  244                 if (ratio > 1.0)
  245                     hhh = opt.thumb_h / ratio;
  246                 else if (ratio != 1.0)
  247                     www = opt.thumb_w * ratio;
  248             }
  249 
  250             if ((!opt.stretch) && ((www > ww) || (hhh > hh))) {
  251                 /* Don't make the image larger unless stretch is specified */
  252                 www = ww;
  253                 hhh = hh;
  254             }
  255 
  256             im_thumb = gib_imlib_create_cropped_scaled_image(im_temp, 0, 0,
  257                     ww, hh, www, hhh, 1);
  258             gib_imlib_free_image_and_decache(im_temp);
  259 
  260             if (opt.alpha) {
  261                 DATA8 atab[256];
  262 
  263                 D(("Applying alpha options\n"));
  264                 gib_imlib_image_set_has_alpha(im_thumb, 1);
  265                 memset(atab, opt.alpha_level, sizeof(atab));
  266                 gib_imlib_apply_color_modifier_to_rectangle
  267                     (im_thumb, 0, 0, www, hhh, NULL, NULL, NULL, atab);
  268             }
  269 
  270             td.text_area_w = opt.thumb_w;
  271             /* Now draw on the info text */
  272             if (opt.index_info) {
  273                 get_index_string_dim(file, td.font_main, &fw, &fh);
  274                 if (fw > td.text_area_w)
  275                     td.text_area_w = fw;
  276                 if (fh > td.text_area_h) {
  277                     td.text_area_h = fh + 5;
  278                     td.thumb_tot_h = opt.thumb_h + td.text_area_h;
  279                 }
  280             }
  281             if (td.text_area_w > opt.thumb_w)
  282                 td.text_area_w += 5;
  283 
  284             if (td.vertical) {
  285                 if (td.text_area_w > td.max_column_w)
  286                     td.max_column_w = td.text_area_w;
  287                 if (y > td.h - td.thumb_tot_h) {
  288                     y = 0;
  289                     x += td.max_column_w;
  290                     td.max_column_w = 0;
  291                 }
  292                 if (x > td.w - td.text_area_w)
  293                     break;
  294             } else {
  295                 if (x > td.w - td.text_area_w) {
  296                     x = 0;
  297                     y += td.thumb_tot_h;
  298                 }
  299                 if (y > td.h - td.thumb_tot_h)
  300                     break;
  301             }
  302 
  303             /* center image relative to the text below it (if any) */
  304             xxx = x + ((td.text_area_w - www) / 2);
  305             yyy = y;
  306 
  307             if (opt.aspect)
  308                 yyy += (opt.thumb_h - hhh) / 2;
  309 
  310             /* Draw now */
  311             gib_imlib_blend_image_onto_image(td.im_main,
  312                              im_thumb,
  313                              gib_imlib_image_has_alpha
  314                              (im_thumb), 0, 0,
  315                              www, hhh, xxx,
  316                              yyy, www, hhh, 1,
  317                              gib_imlib_image_has_alpha(im_thumb), 0);
  318 
  319             thumbnails = gib_list_add_front(thumbnails,
  320                     feh_thumbnail_new(file, xxx, yyy, www, hhh));
  321 
  322             gib_imlib_free_image_and_decache(im_thumb);
  323 
  324             lineno = 0;
  325             if (opt.index_info) {
  326                 line = lines = feh_wrap_string(create_index_string(file),
  327                         opt.thumb_w * 3, td.font_main, NULL);
  328 
  329                 while (line) {
  330                     gib_imlib_get_text_size(td.font_main, (char *) line -> data,
  331                             NULL, &fw, &fh, IMLIB_TEXT_TO_RIGHT);
  332                     gib_imlib_text_draw(td.im_main, td.font_main, NULL,
  333                             x + ((td.text_area_w - fw) >> 1),
  334                             y + opt.thumb_h + (lineno++ * (th + 2)) + 2,
  335                             (char *) line->data,
  336                             IMLIB_TEXT_TO_RIGHT, 255, 255, 255, 255);
  337                     line = line->next;
  338                 }
  339                 gib_list_free_and_data(lines);
  340             }
  341 
  342             if (td.vertical)
  343                 y += td.thumb_tot_h;
  344             else
  345                 x += td.text_area_w;
  346         } else {
  347             if (opt.verbose)
  348                 feh_display_status('x');
  349             last = l;
  350         }
  351         if (opt.display) {
  352             /* thumb_counter is unsigned, so no need to catch overflows */
  353             if (++thumb_counter == opt.thumb_redraw) {
  354                 winwidget_render_image(winwid, 0, 1);
  355                 thumb_counter = 0;
  356             }
  357             if (!feh_main_iteration(0))
  358                 exit(0);
  359         }
  360     }
  361 
  362     if (thumb_counter != 0)
  363         winwidget_render_image(winwid, 0, 1);
  364 
  365     if (opt.verbose)
  366         putc('\n', stderr);
  367 
  368     if (opt.title_font) {
  369         int fw, fh, fx, fy;
  370         char *s;
  371 
  372         s = create_index_title_string(thumbnailcount, td.w, td.h);
  373         gib_imlib_get_text_size(td.font_title, s, NULL, &fw, &fh,
  374                 IMLIB_TEXT_TO_RIGHT);
  375         fx = (index_image_width - fw) >> 1;
  376         fy = index_image_height - fh - 2;
  377         gib_imlib_text_draw(td.im_main, td.font_title, NULL, fx,
  378                 fy, s, IMLIB_TEXT_TO_RIGHT, 255, 255, 255, 255);
  379 
  380         if (opt.display)
  381             winwidget_render_image(winwid, 0, 1);
  382     }
  383 
  384     if (opt.output && opt.output_file) {
  385         char output_buf[1024];
  386 
  387         if (opt.output_dir)
  388             snprintf(output_buf, 1024, "%s/%s", opt.output_dir, opt.output_file);
  389         else {
  390             strncpy(output_buf, opt.output_file, 1023);
  391             output_buf[1023] = '\0';
  392         }
  393         gib_imlib_save_image_with_error_return(td.im_main, output_buf, &err);
  394         if (err) {
  395             feh_imlib_print_load_error(output_buf, td.im_main, err);
  396         }
  397         else if (opt.verbose) {
  398             int tw, th;
  399 
  400             tw = gib_imlib_image_get_width(td.im_main);
  401             th = gib_imlib_image_get_height(td.im_main);
  402             fprintf(stderr, PACKAGE " - File saved as %s\n", output_buf);
  403             fprintf(stderr,
  404                     "    - Image is %dx%d pixels and contains %d thumbnails\n",
  405                     tw, th, thumbnailcount);
  406         }
  407     }
  408 
  409     if (!opt.display)
  410         gib_imlib_free_image_and_decache(td.im_main);
  411     else if (opt.start_list_at) {
  412         for (l = thumbnails; l; l = l->next) {
  413             if (!strcmp(opt.start_list_at, FEH_THUMB(l->data)->file->filename)) {
  414                 opt.start_list_at = NULL;
  415                 feh_thumbnail_select(winwid, FEH_THUMB(l->data));
  416                 break;
  417             }
  418         }
  419     }
  420 
  421 
  422     return;
  423 }
  424 
  425 feh_thumbnail *feh_thumbnail_new(feh_file * file, int x, int y, int w, int h)
  426 {
  427     feh_thumbnail *thumb;
  428 
  429     thumb = (feh_thumbnail *) emalloc(sizeof(feh_thumbnail));
  430     thumb->x = x;
  431     thumb->y = y;
  432     thumb->w = w;
  433     thumb->h = h;
  434     thumb->file = file;
  435     thumb->exists = 1;
  436 
  437     return(thumb);
  438 }
  439 
  440 feh_file *feh_thumbnail_get_file_from_coords(int x, int y)
  441 {
  442     gib_list *l;
  443     feh_thumbnail *thumb;
  444 
  445     for (l = thumbnails; l; l = l->next) {
  446         thumb = FEH_THUMB(l->data);
  447         if (XY_IN_RECT(x, y, thumb->x, thumb->y, thumb->w, thumb->h)) {
  448             if (thumb->exists) {
  449                 return(thumb->file);
  450             }
  451         }
  452     }
  453     D(("No matching %d %d\n", x, y));
  454     return(NULL);
  455 }
  456 
  457 feh_thumbnail *feh_thumbnail_get_thumbnail_from_coords(int x, int y)
  458 {
  459     gib_list *l;
  460     feh_thumbnail *thumb;
  461 
  462     for (l = thumbnails; l; l = l->next) {
  463         thumb = FEH_THUMB(l->data);
  464         if (XY_IN_RECT(x, y, thumb->x, thumb->y, thumb->w, thumb->h)) {
  465             if (thumb->exists) {
  466                 return(thumb);
  467             }
  468         }
  469     }
  470     D(("No matching %d %d\n", x, y));
  471     return(NULL);
  472 }
  473 
  474 feh_thumbnail *feh_thumbnail_get_from_file(feh_file * file)
  475 {
  476     gib_list *l;
  477     feh_thumbnail *thumb;
  478 
  479     for (l = thumbnails; l; l = l->next) {
  480         thumb = FEH_THUMB(l->data);
  481         if (thumb->file == file) {
  482             if (thumb->exists) {
  483                 return(thumb);
  484             }
  485         }
  486     }
  487     D(("No match\n"));
  488     return(NULL);
  489 }
  490 
  491 void feh_thumbnail_mark_removed(feh_file * file, int deleted)
  492 {
  493     feh_thumbnail *thumb;
  494     winwidget w;
  495 
  496     thumb = feh_thumbnail_get_from_file(file);
  497     if (thumb) {
  498         w = winwidget_get_first_window_of_type(WIN_TYPE_THUMBNAIL);
  499         if (w) {
  500             int tw, th;
  501             if (deleted)
  502                 gib_imlib_image_fill_rectangle(w->im, thumb->x, thumb->y,
  503                         thumb->w, thumb->h, 255, 0, 0, 150);
  504             else
  505                 gib_imlib_image_fill_rectangle(w->im, thumb->x, thumb->y,
  506                         thumb->w, thumb->h, 0, 0, 255, 150);
  507 
  508             gib_imlib_get_text_size(td.font_main, "X", NULL, &tw, &th,
  509                     IMLIB_TEXT_TO_RIGHT);
  510             gib_imlib_text_draw(w->im, td.font_main, NULL,
  511                     thumb->x + ((thumb->w - tw) / 2),
  512                     thumb->y + ((thumb->h - th) / 2), "X",
  513                     IMLIB_TEXT_TO_RIGHT, 205, 205, 50, 255);
  514             winwidget_render_image(w, 0, 1);
  515         }
  516         thumb->exists = 0;
  517     }
  518     return;
  519 }
  520 
  521 void feh_thumbnail_calculate_geometry(void)
  522 {
  523     if (!opt.limit_w && !opt.limit_h) {
  524         if (td.im_bg) {
  525             opt.limit_w = td.bg_w;
  526             opt.limit_h = td.bg_h;
  527         } else
  528             opt.limit_w = 800;
  529     }
  530 
  531     /* Here we need to whiz through the files, and look at the filenames and
  532        info in the selected font, work out how much space we need, and
  533        calculate the size of the image we will require */
  534 
  535     if (opt.limit_w) {
  536         td.w = opt.limit_w;
  537 
  538         index_calculate_height(td.font_main, td.w, &td.h, &td.thumb_tot_h);
  539 
  540         if (opt.limit_h) {
  541             if (td.h> opt.limit_h)
  542                 weprintf(
  543                     "The image size you specified (%dx%d) is not large\n"
  544                     "enough to hold all %d thumbnails. To fit all\n"
  545                     "the thumnails, either decrease their size, choose a smaller font,\n"
  546                     "or use a larger image (like %dx%d)",
  547                     opt.limit_w, opt.limit_h, filelist_len, opt.limit_w, td.h);
  548             td.h = opt.limit_h;
  549         }
  550     } else if (opt.limit_h) {
  551         td.vertical = 1;
  552         td.h = opt.limit_h;
  553 
  554         index_calculate_width(td.font_main, &td.w, td.h, &td.thumb_tot_h);
  555     }
  556 }
  557 
  558 int feh_thumbnail_get_thumbnail(Imlib_Image * image, feh_file * file,
  559     int * orig_w, int * orig_h)
  560 {
  561     int status = 0;
  562     char *thumb_file = NULL, *uri = NULL;
  563 
  564     *orig_w = 0;
  565     *orig_h = 0;
  566 
  567     if (!file || !file->filename)
  568         return (0);
  569 
  570     if (td.cache_thumbnails) {
  571         uri = feh_thumbnail_get_name_uri(file->filename);
  572         thumb_file = feh_thumbnail_get_name(uri);
  573 
  574         if (thumb_file == NULL) {
  575             free(uri);
  576             return feh_load_image(image, file);
  577         }
  578 
  579         status = feh_thumbnail_get_generated(image, file, thumb_file,
  580             orig_w, orig_h);
  581 
  582         if (!status)
  583             status = feh_thumbnail_generate(image, file, thumb_file, uri,
  584                 orig_w, orig_h);
  585 
  586         D(("uri is %s, thumb_file is %s\n", uri, thumb_file));
  587         free(uri);
  588         free(thumb_file);
  589     } else
  590         status = feh_load_image(image, file);
  591 
  592     return status;
  593 }
  594 
  595 static char *feh_thumbnail_get_prefix()
  596 {
  597     char *dir = NULL, *home, *xdg_cache_home;
  598 
  599     // TODO: perhaps make sure that either of those paths aren't /-terminated
  600 
  601     xdg_cache_home = getenv("XDG_CACHE_HOME");
  602     if (xdg_cache_home && xdg_cache_home[0] == '/') {
  603         dir = estrjoin("/", xdg_cache_home, "thumbnails", td.cache_dir, NULL);
  604     } else {
  605         home = getenv("HOME");
  606         if (home && home[0] == '/') {
  607             dir = estrjoin("/", home, ".cache/thumbnails", td.cache_dir, NULL);
  608         }
  609     }
  610 
  611     return dir;
  612 }
  613 
  614 char *feh_thumbnail_get_name(char *uri)
  615 {
  616     char *prefix, *thumb_file = NULL, *md5_name;
  617 
  618     /* FIXME: make sure original file isn't under ~/.thumbnails */
  619 
  620     prefix = feh_thumbnail_get_prefix();
  621     if (prefix) {
  622         md5_name = feh_thumbnail_get_name_md5(uri);
  623         thumb_file = estrjoin("/", prefix, md5_name, NULL);
  624         free(md5_name);
  625         free(prefix);
  626     }
  627 
  628     return thumb_file;
  629 }
  630 
  631 char *feh_thumbnail_get_name_uri(char *name)
  632 {
  633     char *cwd, *uri = NULL;
  634 
  635     /* FIXME: what happens with http, https, and ftp? MTime etc */
  636     if (!path_is_url(name)) {
  637 
  638         /* make sure it's an absoulte path */
  639         /* FIXME: add support for ~, need to investigate if it's expanded
  640            somewhere else before adding (unecessary) code */
  641         if (name[0] != '/') {
  642             /* work around /some/path/./image.ext */
  643             if ((strncmp(name, "./", 2)) == 0)
  644                 name += 2;
  645             cwd = getcwd(NULL, 0);
  646             uri = estrjoin("/", "file:/", cwd, name, NULL);
  647             free(cwd);
  648         } else {
  649             uri = estrjoin(NULL, "file://", name, NULL);
  650         }
  651     } else
  652         uri = estrdup(name);
  653 
  654     return uri;
  655 }
  656 
  657 char *feh_thumbnail_get_name_md5(char *uri)
  658 {
  659     int i;
  660     char *pos, *md5_name;
  661     md5_state_t pms;
  662     md5_byte_t digest[16];
  663 
  664     /* generate the md5 sum */
  665     md5_init(&pms);
  666     md5_append(&pms, (unsigned char *)uri, strlen(uri));
  667     md5_finish(&pms, digest);
  668 
  669     /* print the md5 as hex to a string */
  670     md5_name = emalloc(32 + 4 + 1); /* md5 + .png + '\0' */
  671     for (i = 0, pos = md5_name; i < 16; i++, pos += 2) {
  672         sprintf(pos, "%02x", digest[i]);
  673     }
  674     sprintf(pos, ".png");
  675 
  676     return md5_name;
  677 }
  678 
  679 int feh_thumbnail_generate(Imlib_Image * image, feh_file * file,
  680         char *thumb_file, char *uri, int * orig_w, int * orig_h)
  681 {
  682     int w, h, thumb_w, thumb_h;
  683     Imlib_Image im_temp;
  684     struct stat sb;
  685     char c_width[8], c_height[8];
  686     char *tmp_thumb_file, *prefix;
  687     int tmp_fd;
  688 
  689     if (feh_load_image(&im_temp, file) != 0) {
  690         *orig_w = w = gib_imlib_image_get_width(im_temp);
  691         *orig_h = h = gib_imlib_image_get_height(im_temp);
  692         thumb_w = td.cache_dim;
  693         thumb_h = td.cache_dim;
  694 
  695         if ((w > td.cache_dim) || (h > td.cache_dim)) {
  696             double ratio = (double) w / h;
  697             if (ratio > 1.0)
  698                 thumb_h = td.cache_dim / ratio;
  699             else if (ratio != 1.0)
  700                 thumb_w = td.cache_dim * ratio;
  701         }
  702 
  703         *image = gib_imlib_create_cropped_scaled_image(im_temp, 0, 0, w, h,
  704                 thumb_w, thumb_h, 1);
  705 
  706         if (!stat(file->filename, &sb)) {
  707             char c_mtime[128];
  708             sprintf(c_mtime, "%d", (int)sb.st_mtime);
  709             snprintf(c_width, 8, "%d", w);
  710             snprintf(c_height, 8, "%d", h);
  711             prefix = feh_thumbnail_get_prefix();
  712             if (prefix == NULL) {
  713                 gib_imlib_free_image_and_decache(im_temp);
  714                 return 0;
  715             }
  716             tmp_thumb_file = estrjoin("/", prefix, ".feh_thumbnail_XXXXXX", NULL);
  717             free(prefix);
  718             tmp_fd = mkstemp(tmp_thumb_file);
  719             if (!feh_png_write_png_fd(*image, tmp_fd, "Thumb::URI", uri,
  720                     "Thumb::MTime", c_mtime,
  721                     "Thumb::Image::Width", c_width,
  722                     "Thumb::Image::Height", c_height)) {
  723                 rename(tmp_thumb_file, thumb_file);
  724             } else {
  725                 unlink(tmp_thumb_file);
  726             }
  727             close(tmp_fd);
  728             free(tmp_thumb_file);
  729         }
  730 
  731         gib_imlib_free_image_and_decache(im_temp);
  732 
  733         return 1;
  734     }
  735 
  736     return 0;
  737 }
  738 
  739 int feh_thumbnail_get_generated(Imlib_Image * image, feh_file * file,
  740     char *thumb_file, int * orig_w, int * orig_h)
  741 {
  742     struct stat sb;
  743     char *c_mtime;
  744     char *c_width, *c_height;
  745     time_t mtime = 0;
  746     gib_hash *hash;
  747 
  748     if (!stat(file->filename, &sb)) {
  749         hash = feh_png_read_comments(thumb_file);
  750         if (hash != NULL) {
  751             c_mtime  = (char *) gib_hash_get(hash, "Thumb::MTime");
  752             c_width  = (char *) gib_hash_get(hash, "Thumb::Image::Width");
  753             c_height = (char *) gib_hash_get(hash, "Thumb::Image::Height");
  754             if (c_mtime != NULL)
  755                 mtime = (time_t) strtol(c_mtime, NULL, 10);
  756             if (c_width != NULL)
  757                 *orig_w = atoi(c_width);
  758             if (c_height != NULL)
  759                 *orig_h = atoi(c_height);
  760             gib_hash_free_and_data(hash);
  761         }
  762 
  763         /* FIXME: should we bother about Thumb::URI? */
  764         if (mtime == sb.st_mtime) {
  765             feh_load_image_char(image, thumb_file);
  766 
  767             return (1);
  768         }
  769     }
  770 
  771     return (0);
  772 }
  773 
  774 void feh_thumbnail_show_fullsize(feh_file *thumbfile)
  775 {
  776     winwidget thumbwin = NULL;
  777     gib_list *l;
  778 
  779     for (l = filelist; l; l = l->next) {
  780         if (FEH_FILE(l->data) == thumbfile) {
  781             break;
  782         }
  783     }
  784     if (!l) {
  785         eprintf("Cannot find %s in filelist, wtf", thumbfile->filename);
  786     }
  787     thumbwin = winwidget_get_first_window_of_type(WIN_TYPE_THUMBNAIL_VIEWER);
  788     if (!thumbwin) {
  789         thumbwin = winwidget_create_from_file(
  790                 l,
  791                 WIN_TYPE_THUMBNAIL_VIEWER);
  792         if (thumbwin)
  793             winwidget_show(thumbwin);
  794     } else if (FEH_FILE(thumbwin->file->data) != thumbfile) {
  795         thumbwin->file = l;
  796 #ifdef HAVE_INOTIFY
  797         winwidget_inotify_remove(thumbwin);
  798 #endif
  799         feh_reload_image(thumbwin, 1, 0);
  800 #ifdef HAVE_INOTIFY
  801         winwidget_inotify_add(thumbwin, thumbfile);
  802 #endif
  803     }
  804 }
  805 
  806 void feh_thumbnail_select(winwidget winwid, feh_thumbnail *thumbnail)
  807 {
  808     Imlib_Image origwin;
  809 
  810     if (thumbnail == td.selected)
  811         return;
  812 
  813     if (thumbnail) {
  814         origwin = winwid->im;
  815         winwid->im = gib_imlib_clone_image(origwin);
  816         gib_imlib_image_fill_rectangle(winwid->im,
  817                 thumbnail->x, thumbnail->y, thumbnail->w,
  818                 thumbnail->h, 50, 50, 255, 100);
  819         gib_imlib_image_draw_rectangle(winwid->im,
  820                 thumbnail->x, thumbnail->y, thumbnail->w,
  821                 thumbnail->h, 255, 255, 255, 255);
  822         gib_imlib_image_draw_rectangle(winwid->im,
  823                 thumbnail->x + 1, thumbnail->y + 1,
  824                 thumbnail->w - 2, thumbnail->h - 2,
  825                 0, 0, 0, 255);
  826         gib_imlib_image_draw_rectangle(winwid->im,
  827                 thumbnail->x + 2, thumbnail->y + 2,
  828                 thumbnail->w - 4, thumbnail->h - 4,
  829                 255, 255, 255, 255);
  830         winwidget_render_image(winwid, 0, 0);
  831         gib_imlib_free_image_and_decache(winwid->im);
  832         winwid->im = origwin;
  833     } else
  834         winwidget_render_image(winwid, 0, 0);
  835 
  836     td.selected = thumbnail;
  837 }
  838 
  839 void feh_thumbnail_select_next(winwidget winwid, int jump)
  840 {
  841     gib_list *l;
  842     feh_thumbnail *thumb;
  843     int len = 0, cur = 0, target = 0;
  844 
  845     for (l = thumbnails; l; l = l->next) {
  846         thumb = FEH_THUMB(l->data);
  847         if (thumb == td.selected)
  848             cur = len;
  849         len++;
  850     }
  851 
  852     target = (cur + len - jump) % len;
  853 
  854     for (l = thumbnails; l; l = l->next) {
  855         if (target-- == 0) {
  856             feh_thumbnail_select(winwid, FEH_THUMB(l->data));
  857         }
  858     }
  859 }
  860 
  861 void feh_thumbnail_select_prev(winwidget winwid, int jump)
  862 {
  863     gib_list *l;
  864     feh_thumbnail *thumb;
  865     int len = 0, cur = 0, target = 0;
  866 
  867     for (l = thumbnails; l; l = l->next) {
  868         thumb = FEH_THUMB(l->data);
  869         if (thumb == td.selected)
  870             cur = len;
  871         len++;
  872     }
  873 
  874     target = (cur + jump) % len;
  875 
  876     for (l = thumbnails; l; l = l->next) {
  877         if (target-- == 0) {
  878             feh_thumbnail_select(winwid, FEH_THUMB(l->data));
  879             break;
  880         }
  881     }
  882 }
  883 
  884 void feh_thumbnail_show_selected()
  885 {
  886     if (td.selected && td.selected->file)
  887         feh_thumbnail_show_fullsize(td.selected->file);
  888 }
  889 
  890 feh_file* feh_thumbnail_get_selected_file()
  891 {
  892     if (td.selected)
  893         return td.selected->file;
  894     return NULL;
  895 }
  896 
  897 int feh_thumbnail_setup_thumbnail_dir(void)
  898 {
  899     int status = 0;
  900     struct stat sb;
  901     char *dir, *p;
  902 
  903     dir = feh_thumbnail_get_prefix();
  904 
  905     if (dir) {
  906         if (!stat(dir, &sb)) {
  907             if (S_ISDIR(sb.st_mode))
  908                 status = 1;
  909             else
  910                 weprintf("%s should be a directory", dir);
  911         } else {
  912             for (p = dir + 1; *p; p++) {
  913                 if (*p != '/') {
  914                     continue;
  915                 }
  916 
  917                 *p = 0;
  918                 if (stat(dir, &sb) != 0) {
  919                     if (mkdir(dir, 0700) == -1) {
  920                         weprintf("unable to create directory %s", dir);
  921                     }
  922                 }
  923                 *p = '/';
  924             }
  925 
  926             if (stat(dir, &sb) != 0) {
  927                 if (mkdir(dir, 0700) == -1) {
  928                     weprintf("unable to create directory %s", dir);
  929                 }
  930             }
  931         }
  932         free(dir);
  933     }
  934 
  935     return status;
  936 }