August 2005

Using the Epeg API II

In this article, expanding epeg to handle a source and target directory is examined with the bonus of checking to see if a thumbnail file in the target directory is missing and regenerating it on the fly.

For those who wish to skip the diatribe, the full source is available.

The Scenario

There is a source directory called backgrounds/ in the current directory that holds copies of the original (and larg-ish) images. The target directory is aptly named thumbcache. The goal is to first, initialize the thumbcache directory, then update it as needed.

Note: There are some intentional holes left in this program such as getting the full path for example. Call it room for improvement..

What is Needed

Since the make syntax was addressed in the first article, that will be skipped. In a nutshell, directory handling interfaces are all that need to be added.

#include <stddef.h>
#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>

#include <Epeg.h>

#define BACKGROUNDS backgrounds
#define THUMBCACHE  thumbcache
#define LEN 1024

Of course, the directory names are defined, the LEN value is not a standard string length - this can also be grokked from the limits.h file.

Functions

After a little iteration, the functions can be broken down into three basic types:

  1. Main (duh)
  2. Core program
  3. Helper

For simplicity, here are the two helper functions:

Helper Functions

fullpath()

Using the filename and leading directory, develop a full path name, in the case of this program it is relative, however, tacking the dir argument to a getcwd() call can develop an entire path.

char
*fullpath(char *file, char *dir)
{
    static char path[LEN];

    strcpy(path, dir);
    strcat(path, /);
    strcat(path, file);

    return (path);
}
create_thumb()

... is actually the only part of the program that is e17 specific (which is somewhat ironic). In the function, the exact functions defined in Epeg I article are used with one exception, the filenames (in and out) are predefined and the width, height and quality are hard coded.

void
create_thumb(char *src, char *dst)
{
    Epeg_Image * image;

    image = epeg_file_open(src);

    if (!image) {
        printf(Cannot open %s\n, src);
        exit(1);
    }

    epeg_decode_size_set(image, 128, 96);
    epeg_quality_set(image, 75);
    epeg_file_output_set(image, dst);
    epeg_encode(image);
    epeg_close(image);
}

Core Functions

Now it is time for the core functions. These come in two parts, the first is init_thumbcache. In the next function, there is no thumbcache directory at all, it simply has to be generated, so all that has to happen is each background image that is loaded needs to call create_thumb().

init_thumbcache()
void
init_thumbcache(DIR * backgrounds)
{
    int i;
    char dst[LEN];
    char src[LEN];
    DIR  *thumbcache;
    struct dirent *src_dp, dst_dp;

    if (mkdir(THUMBCACHE, 0755)) {
        printf(Could not create directory %s\n, THUMBCACHE);
        exit (1);
    }

    i = 0;

    while (src_dp = readdir(backgrounds)) {
        if (i > 1) {
            strcpy(src, fullpath(src_dp->d_name, BACKGROUNDS));
            strcpy(dst, fullpath(src_dp->d_name, THUMBCACHE));
            create_thumb(src, dst);
        }

        i++;
    }

    (void)closedir(backgrounds);
}

The background directory images are being iterated over and for each one, a new thumbnail is being generated.

update_thumbcache()

Next comes the weird one, in the update_thumbcache() function, there is some definite oddness to deal with.

  1. Load a background image.
  2. Check to see if it has a thumb.
  3. If not, make one, if so - go on.

That certainly sounds neat, but effectively it is a directory search, which is easy enough. Crack open the first directory, and for each item in it, see if there is a matching one in the second.

void
update_thumbcache(DIR * backgrounds, DIR * thumbcache)
{
    int i, j, k;
    char dst[LEN];
    char src[LEN];
    struct dirent *src_dp, *dst_dp;

    i = 0;

    while (src_dp = readdir(backgrounds)) {
        if (i > 1) {
            j = 0;
            k = 0;

            while (dst_dp = readdir(thumbcache)) {
                if (j > 1)
                    if (strcmp(src_dp->d_name, dst_dp->d_name) == 0)
                        k++;

                j++;
            }

        }

        if (k == 0) {
            strcpy(src, fullpath(src_dp->d_name, BACKGROUNDS));
            strcpy(dst, fullpath(src_dp->d_name, THUMBCACHE));
            create_thumb(src, dst);
        }

        i++;
        rewinddir(thumbcache);
    }

    (void)closedir(backgrounds);
    (void)closedir(thumbcache);
}

That is a bit to digest, but it is actually relatively easy. Following is a simple step by step which should be obvious when looking at the code:

  1. Set the background file counter to 0.
  2. Read in the backgrounds directory to a structure (src_dp).
  3. If we have passed the first 2 points in the directory (that would be . & ..) then proceed, otherwise count up.
  4. Set the thumbcache file counter to 0.
  5. Set the count of matches to 0 (actually it is always either 0 or 1).
  6. Read in the thumbcache directory to a structure (dst_dp).
  7. If we have passed the first 2 points in the thumbcache directory then proceed, if not, count up.
  8. Compare the name of what is in backgrounds to the current entry in thumbcache, if there is one, increment the counter.
  9. If there were no matches found, create a new thumb.
  10. Increment the backgrounds file counter and rewind the thumbcache directory.
  11. rinse, repeat as neccessary...

main()

Last and not least, the main function. Basically main does not do a whole lot:

void update_thumbcache  (DIR *, DIR *);
void init_thumbcache    (DIR *);
void create_thumb       (char *, char *);
char *fullpath          (char *, char *);

int
main (void)
{
    DIR * backgrounds, * thumbcache;

    backgrounds = opendir(BACKGROUNDS);
    if (backgrounds == NULL) {
        printf(Cannot find %s - exiting\n, BACKGROUNDS);
        closedir(backgrounds);
        exit (1);
    }

    thumbcache  = opendir(THUMBCACHE);
    if (thumbcache == NULL) {
        closedir(thumbcache);
        init_thumbcache(backgrounds);
        return 0;
    }

    update_thumbcache(backgrounds, thumbcache);

    return 0;
}

In a nutshell:

  1. Check to make sure that backgrounds exists.
  2. Check to see if thumbcache exists, if it does, call update_thumbcache(), if not, go ahead and call init_thumbcache().

Where to go Next

Some obvious, changes should be incorporated into the program, as was noted earlier, there are intentional flaws, the next article will address them:

  • System header files are not used to their fullest (hint, see include files).
  • Safer string manipulation?
  • Absolute paths might be safer.
  • There should not be any globals.
  • Command line arguments for epeg settings, src directory and dst directory.
  • Using more of the Epeg API (for instance, comments).
  • Anything else . . .

As always, reader feedback is welcome.

Next: Enlightenment Thumbnailing utility Previous: Epeg I