Logo Search packages:      
Sourcecode: danpei version File versions  Download package

image_cache.c

/*
 * Danpei -- a GTK+ based Image Viewer
 * Copyright (C) 2001-2003 Shinji Moiino
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */
/* image_cache.c */

#include <errno.h>
#include <fcntl.h>
#include <png.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <gdk/gdkx.h>
#include <gdk-pixbuf/gdk-pixbuf.h>

#include "config.h"
#include "intl.h"
#include "image_cache.h"
#include "main.h"
#include "version.h"

/* Static function declarations. */
static void image_cache_add_a_new_object             (ManageCache*);

static void image_cache_cache_list_structure_init    (CacheList*         );

static void image_cache_cache_list_structure_destroy (CacheList*         );

static void image_cache_cache_list_add               (gchar*           ,
                                                      CacheList*       ,
                                                      GdkPixbuf*       ,
                                                      gint             ,
                                                      gint             ,
                                                      gchar*             );

static void image_cache_write_pixmap                 (GdkPixbuf*       ,
                                                      gint             ,
                                                      gint             ,
                                                      gchar*             );

static gint image_cache_saver_png                    (GdkPixbuf*       ,
                                                      gchar*             );

/* Function definitions. */
/*
 * @image_cache_manage_cache_structure_init
 *
 *  Initialize the ManageCache structure object.
 *
 */
void image_cache_manage_cache_structure_init(ManageCache *mp) {
  mp->using_disk = 0;
  mp->cache_dir  = NULL;
  mp->top        = NULL;
  mp->last       = NULL;

  return;
}

/*
 * @image_cache_manage_cache_structure_destroy
 *
 *  Destroy the ManageCache structure object.
 *
 */
void image_cache_manage_cache_structure_destroy(ManageCache *mp,
                                                gboolean    is_all) {
  if (mp == NULL) { return; }

  mp->using_disk = 0;
  if ((is_all == TRUE) && (mp->cache_dir != NULL)) { 
    free(mp->cache_dir); 
    mp->cache_dir = NULL;
  }
  if (mp->top  != NULL) {
    image_cache_cache_list_structure_destroy(mp->top);
    mp->top = mp->last = NULL;
  }

  return;
}

/*
 * @image_cache_add
 *
 *  Add the image to the ManageCache structure object.
 *
 */
void image_cache_add(ManageCache *mp      ,
                     GdkPixbuf   *image   ,
                     gint        width    ,
                     gint        height   ,
                     gchar       *filename  ) {
  size_t    size;

  /* Initialize the local variables. */
  size = 0;

  image_cache_add_a_new_object(mp);

  image_cache_cache_list_add(mp->cache_dir, mp->last, image, 
                             width, height, filename);

  mp->using_disk += (mp->last)->size;

  return;
}

/*
 * @image_cache_delete
 *
 *  Delete the image from the ManageCache structure object.
 *
 */
void image_cache_delete(ManageCache   *mp      ,
                        gchar         *filename  ) {
  CacheList *cl1, *cl2;

  cl1 = cl2 = mp->top;

  while (cl1 != NULL) {
    if (strcmp(cl1->pixmap_name, filename) == 0) { break; }
    cl2 = cl1;         /* cl2 = last cl1 value. */
    cl1 = cl1->next;
  }

  if (cl1 != NULL) {
    if (cl1 == cl2) {
      if (cl1->next == NULL) 
        mp->top = mp->last = NULL;
      else
        mp->top = cl1->next;
    }
    else {
      cl2->next = cl1->next;
      if (cl2->next == NULL) { mp->last = cl2; }
    }
    FREE_CACHELIST_MEMBER_AND_CACHE(cl1);
    free(cl1);
  }

  return;
}

/*
 * @image_cache_serch
 *
 *  Serch the pixmap into the cache_dir.
 *
 */
void image_cache_serch(TopLevel  *tp      ,
                       Thumbnail *thumb   ,
                       gint      icon_size,
                       gchar     *filename  ) {
  ManageCache *mp;
  CacheList   *cl;
  GdkPixbuf   *im, *im2;
  gint        wk_size, image_width, image_height;

  /* Initialize the local variables. */
  mp = &(tp->cache);
  cl = NULL;
  im = im2 = NULL;

  /* Serch in the cache files. */
  cl = mp->top;
  while (cl != NULL) {
    if (strcmp(cl->pixmap_name, filename) == 0) {
      im = gdk_pixbuf_new_from_file(cl->cache_name);
      if (im != NULL) {
        image_width  = gdk_pixbuf_get_width (im);
        image_height = gdk_pixbuf_get_height(im);
        if (image_width < image_height) {
          wk_size = (icon_size * image_width) / image_height;
          thumb->icon_width  = wk_size;
          thumb->icon_height = icon_size;
          thumb->pixmap = gdk_pixmap_new(tp->window->window,
                                         thumb->icon_width , 
                                         thumb->icon_height, -1);
          im2 = gdk_pixbuf_scale_simple(im, 
                                        thumb->icon_width , 
                                        thumb->icon_height,
                                        GDK_INTERP_TILES  ); 
          gdk_pixbuf_render_to_drawable_alpha(im2, thumb->pixmap,
                                              0, 0, 0, 0,
                                              thumb->icon_width, 
                                              thumb->icon_height,
                                              GDK_PIXBUF_ALPHA_BILEVEL,
                                              0, GDK_RGB_DITHER_NORMAL, 0, 0);
        }
        else {
          wk_size = (icon_size * image_height) / image_width;
          thumb->icon_width  = icon_size;
          thumb->icon_height = wk_size;
          thumb->pixmap = gdk_pixmap_new(tp->window->window,
                                         thumb->icon_width , 
                                         thumb->icon_height, -1);
          im2 = gdk_pixbuf_scale_simple(im, 
                                        thumb->icon_width , 
                                        thumb->icon_height,
                                        GDK_INTERP_TILES  ); 
          gdk_pixbuf_render_to_drawable_alpha(im2, thumb->pixmap,
                                              0, 0, 0, 0,
                                              thumb->icon_width, 
                                              thumb->icon_height,
                                              GDK_PIXBUF_ALPHA_BILEVEL,
                                              0, GDK_RGB_DITHER_NORMAL, 0, 0);
        }
        gdk_pixbuf_unref(im);
        gdk_pixbuf_unref(im2);
      }
      break;
    }
    cl = cl->next;
  }

  return;
}

/*
 * @image_cache_rename
 *
 *  Rename the pixmap's filename.
 *
 */
void image_cache_rename(ManageCache *mp  ,
                        gchar       *src ,
                        gchar       *dist  ) {
  CacheList     *cl;

  /* Initialize the local variables. */
  cl      = NULL;

  /* Serch in the cache files. */
  cl = mp->top;
  while (cl != NULL) {
    if (strcmp(cl->pixmap_name, src) == 0) {
      free(cl->pixmap_name);
      cl->pixmap_name = strdup(dist);
      if (cl->pixmap_name == NULL) {
        /* Out of memory. */
        fprintf(stderr, "danpei: Out of memory.\n");
        fprintf(stderr, "        image_cache.c: error -- 01.\n");
        gtk_exit(-1);
      }
      break;
    }
    cl = cl->next;
  }

  return;
}

/*
 * @image_cache_copy
 *
 *  Dupricate the cache data and add to cache list.
 *
 */
void image_cache_copy(ManageCache *mp              ,
                      gchar       *src_pixmap_name ,
                      gchar       *dest_pixmap_name  ) {
  CacheList *cl;
  GdkPixbuf *im;

  /* Initialize the local variables. */
  cl      = NULL;
  im      = NULL;

  /* Serch in the cache files. */
  cl = mp->top;
  while (cl != NULL) {
    if (strcmp(cl->pixmap_name, src_pixmap_name) == 0) {
      im = gdk_pixbuf_new_from_file(cl->cache_name);
      break;
    }
    cl = cl->next;
  }

  if (im != NULL) {
    image_cache_add(mp, im, 
                    gdk_pixbuf_get_width (im),
                    gdk_pixbuf_get_height(im),
                    dest_pixmap_name              );
    gdk_pixbuf_unref(im);
  }

  return;
}

/*
 * @image_cache_check_using_disk
 *
 *  Check cache size, and if it is over, remove the top of the cache 
 *  files.
 *
 */
void image_cache_check_using_disk(TopLevel *tp) {
  
  CacheList *cl1, *cl2;

  /* Initialize the local variables. */
  cl1 = cl2 = NULL;

  if (tp->cache.using_disk > 
      tp->app_option.image_cache.cache_size * 1024 * 100) {
    cl1 = tp->cache.top;
    while (cl1 != NULL) {
      cl2 = cl1->next;
      tp->cache.using_disk -= cl1->size;
      FREE_CACHELIST_MEMBER_AND_CACHE(cl1);
      free(cl1);
      if (tp->cache.using_disk <
          tp->app_option.image_cache.cache_size * 1024 * 100) {
        break;
      }
      cl1 = cl2;
    }
    tp->cache.top = cl2;
  }

  return;
}

/*
 * @image_cache_write_info_to_disk
 *
 *  Write the information of cache files into the file '.danpei.cache'.
 *  If success, return TRUE. Otherwise return FALSE.
 *
 */
gboolean image_cache_write_info_to_disk(TopLevel *tp) {
  FILE      *fp;
  CacheList *cl;
  gchar     *save_file;

  /* Initialize the local variables. */
  fp        = NULL;
  save_file = NULL;
  cl        = NULL;

  if (tp->cache.cache_dir == NULL) { return; }

  save_file = (gchar*)malloc(sizeof(gchar) * 
                             (strlen(tp->cache.cache_dir) +
                              strlen("/.danpei.cache")    + 1));
  if (save_file == NULL) {
    /* Out of memory. */
    fprintf(stderr, "danpei: Out of memory.\n");
    fprintf(stderr, "        image_cache.c: error -- 02.\n");
    gtk_exit(-1);
  }
  sprintf(save_file, "%s/.danpei.cache", tp->cache.cache_dir);

  fp = fopen(save_file, "w");
  if (fp == NULL) {
    free(save_file);
    return FALSE;
  }

  cl = tp->cache.top;
  while (cl != NULL) {
    if ((strlen(cl->pixmap_name) < 1023) &&
        (strlen(cl->cache_name ) < 1023)    ) {
      if ((fputs(cl->pixmap_name, fp) == EOF)) {
        /* Write error. */
        fclose(fp);
        remove(save_file);
        free(save_file);
        return FALSE;
      }
      if ((fputs("\n", fp) == EOF)) {
        /* Write error. */
        fclose(fp);
        remove(save_file);
        free(save_file);
        return FALSE;
      }
      if ((fputs(cl->cache_name, fp) == EOF)) {
        /* Write error. */
        fclose(fp);
        remove(save_file);
        free(save_file);
        return FALSE;
      }
      if ((fputs("\n", fp) == EOF)) {
        /* Write error. */
        fclose(fp);
        remove(save_file);
        free(save_file);
        return FALSE;
      }
    }
    cl = cl->next;
  }

  free(save_file);
  fclose(fp);

  return TRUE;
}

/*
 * @image_cache_read_info_from_disk
 *
 *  Read the information of cache files from the file '.danpei.cache'.
 *  If success, return TRUE. Otherwise return FALSE.
 *
 */
gboolean image_cache_read_info_from_disk(TopLevel *tp) {
  FILE        *fp;
  struct stat stat_buf;
  gchar       *load_file, *wk_p;
  char        *ret;
  gchar       buf1[1024], buf2[1024];
  gboolean    error_flag;

  /* Initialize the local variables. */
  fp         = NULL;
  load_file  = wk_p = NULL;
  ret        = NULL;
  error_flag = FALSE;

  load_file = (gchar*)malloc(sizeof(gchar) * 
                             (strlen(tp->cache.cache_dir) +
                              strlen("/.danpei.cache")    + 1));
  if (load_file == NULL) {
    /* Out of memory. */
    fprintf(stderr, "danpei: Out of memory.\n");
    fprintf(stderr, "        image_cache.c: error -- 03.\n");
    gtk_exit(-1);
  }
  sprintf(load_file, "%s/.danpei.cache", tp->cache.cache_dir);

  fp = fopen(load_file, "r");
  if (fp == NULL) {
    free(load_file);
    return FALSE;
  }

  while (1) {
    error_flag = FALSE;

    /* Get image file name. */
    ret = fgets(buf1, 1023, fp);
    if (ret == NULL) { 
      break; 
    }
    else {
      wk_p = strrchr(buf1, '\n');
      if (wk_p == NULL) {
        error_flag = TRUE;
      }
      else { 
        *wk_p = '\0'; 
      }
    }

    /* Get cache file name. */
    ret = fgets(buf2, 1023, fp);
    if (ret == NULL) { 
      break; 
    }
    else {
      wk_p = strrchr(buf2, '\n');
      if (wk_p == NULL) { 
        error_flag = TRUE;
      }
      else {
        *wk_p = '\0'; 
      }
      wk_p = strrchr(buf2, '/');
      if (wk_p == NULL) {
        error_flag = TRUE;
      }
      else {
        wk_p++;
        if ((*(wk_p + 0) != 'd') || (*(wk_p + 1) != 'a') ||
            (*(wk_p + 2) != 'n') || (*(wk_p + 3) != 'p') ||
            (*(wk_p + 4) != 'e') || (*(wk_p + 5) != 'i')    ) {
          error_flag = TRUE;
        }
      }
    }

    if (error_flag == FALSE) {
      /* Check existense of both image file and cache file. */
      if ((access(buf1, F_OK) == 0) &&
          (access(buf2, F_OK) == 0)     ) {
        image_cache_add_a_new_object(&(tp->cache));
        ((tp->cache).last)->pixmap_name = strdup(buf1);
        if (((tp->cache).last)->pixmap_name == NULL) {
          /* Out of memory. */
          fprintf(stderr, "danpei: Out of memory.\n");
          fprintf(stderr, "        image_cache.c: error -- 04.\n");
          gtk_exit(-1);
        }

        (tp->cache.last)->cache_name = strdup(buf2);
        if ((tp->cache.last)->cache_name == NULL) {
          /* Out of memory. */
          fprintf(stderr, "danpei: Out of memory.\n");
          fprintf(stderr, "        image_cache.c: error -- 05.\n");
          gtk_exit(-1);
        }

        /* Get the cache file size and check whether disk size is over. */
        if (stat((tp->cache.last)->cache_name, &stat_buf) == 0) {
          ((tp->cache).last)->size = stat_buf.st_size;
          (tp->cache).using_disk += ((tp->cache).last)->size;
          image_cache_check_using_disk(tp);
        }
        else {
          /* Open error. */
          fprintf(stderr, "danpei: File open error.\n");
          fprintf(stderr, "        filename = %s.\n", 
                                   (tp->cache.last)->cache_name);
          fprintf(stderr, "        image_cache.c: error -- 06.\n");
          fprintf(stderr, "        <errno.h> - errno = %d.\n", errno);
          free(load_file);
          gtk_exit(-1);
        }
      }
    }
  }
  if (ferror(fp) != 0) {
    /* Read error. */
    image_cache_manage_cache_structure_destroy(&(tp->cache), FALSE);
    /* Delete .danpei.cache and all cache files(danpei*.png). */
    toplevel_clean_cache_files(tp); remove(load_file);
    free(load_file);
    return FALSE;
  }
  else {
    if (tp->cache.last != NULL) {
      if (((tp->cache.last)->pixmap_name == NULL) ||
          ((tp->cache.last)->cache_name  == NULL)    ) {
        /* .danpei.cache has broken.                              */
        /* Delete .danpei.cache and all cache files(danpei*.png). */
        image_cache_manage_cache_structure_destroy(&(tp->cache), FALSE);
        toplevel_clean_cache_files(tp); remove(load_file);
        free(load_file);
        fclose(fp);
        return FALSE;
      }
    }
    else {
      /* .danpei.cache has no data.                             */
      /* Delete .danpei.cache and all cache files(danpei*.png). */
      image_cache_manage_cache_structure_destroy(&(tp->cache), FALSE);
      toplevel_clean_cache_files(tp);
      free(load_file);
      fclose(fp);
      return FALSE;
    }
  }

  free(load_file);
  fclose(fp);

  return TRUE;
}


/* Static function definitions. */
/*
 * @image_cache_add_a_new_object
 *
 *  Add a new object to the tail of the ManageCache structure object.
 *
 */
static void image_cache_add_a_new_object(ManageCache *mp) {
  CacheList *cl;

  /* Initialize the local variables. */
  cl   = NULL;

  cl = (CacheList*)malloc(sizeof(CacheList));
  if (cl == NULL) {
    /* Out of memory. */
    fprintf(stderr, "danpei: Out of memory.\n");
    fprintf(stderr, "        image_cache.c: error -- 07.\n");
    gtk_exit(-1);
  }

  if (mp->top == NULL) 
    mp->top = mp->last = cl;
  else {
    mp->last->next = cl;
    mp->last       = cl;
  }

  image_cache_cache_list_structure_init(cl);

  return;
}

/*
 * @image_cache_cache_list_structure_init
 *
 *  Initialize the CacheList structre objeckt.
 *
 */
static void image_cache_cache_list_structure_init(CacheList *cp) {
  cp->cache_name  = NULL;
  cp->pixmap_name = NULL;
  cp->next        = NULL;

  return;
}

/*
 * @image_cache_cache_list_structure_destroy
 *
 *  Destroy the CacheList structre objects.
 *  This function does NOT REMEOVE cache files.
 *
 */
static void image_cache_cache_list_structure_destroy(CacheList *cp) {
  CacheList *cp1, *cp2;

  if (cp == NULL) { return; }

  cp1 = cp2 =cp;
  while (cp1 != NULL) {
    FREE_CACHELIST_MEMBER_ONLY(cp1);
    cp2 = cp1->next;
    free(cp1);
    cp1 = cp2;
  }
  cp = NULL;

  return;
}

/*
 * @image_cache_cache_list_add
 *
 *  Destroy the CacheList structre objeckts.
 *
 */
static void image_cache_cache_list_add (gchar     *cache_dir,
                                        CacheList *cl       ,
                                        GdkPixbuf *image    ,
                                        gint      width     ,
                                        gint      height    ,
                                        gchar     *filename   ) {
  int         fd;
  gchar       *wk_name;
  struct stat stat_buf;

  wk_name = (gchar*)malloc(sizeof(gchar)      * 
                           (strlen(cache_dir) + 
                            strlen("/danpeiXXXXXX.png") + 1));
  if (wk_name == NULL) {
    /* Out of memory. */
    fprintf(stderr, "danpei: Out of memory.\n");
    fprintf(stderr, "        image_cache.c: error -- 08.\n");
    gtk_exit(-1);
  }

  sprintf(wk_name, "%s/danpeiXXXXXX", cache_dir);

  if((fd = mkstemp(wk_name)) == -1) {
    /* Open error. */
    fprintf(stderr, "danpei: Out of memory.\n");
    fprintf(stderr, "        image_cache.c: error -- 09.\n");
    gtk_exit(-1);
  }
  else {
    close(fd);
    remove(wk_name);
    strcat(wk_name, ".png");
  }

  image_cache_write_pixmap(image, width, height, wk_name);

  /* cache_name has FULL PATH, so if cache_dir is changed, 
   * all cache files will be removed.
   */
  cl->cache_name  = strdup(wk_name);
  cl->pixmap_name = strdup(filename);
  if ((cl->cache_name == NULL) || (cl->pixmap_name == NULL)) {
    /* Out of memory */
    fprintf(stderr, "danpei: Out of memory.\n");
    fprintf(stderr, "        image_cache.c: error -- 10.\n");
    gtk_exit(-1);
  }

  if (stat(wk_name, &stat_buf) == 0) {
    cl->size = stat_buf.st_size;
  }
  else {
    /* Open error. */
    fprintf(stderr, "danpei: File open error.\n");
    fprintf(stderr, "        filename = %s.\n", filename);
    fprintf(stderr, "        image_cache.c: error -- 11.\n");
    fprintf(stderr, "        <errno.h> - errno = %d.\n", errno);
    gtk_exit(-1);
  }

  free(wk_name);

  return;
}

/*
 * @image_cache_write_pixmap
 *
 *
 *
 */
static void image_cache_write_pixmap(GdkPixbuf *image   ,
                                     gint      width    ,
                                     gint      height   ,
                                     gchar     *filename  ) {
  GdkPixbuf *tmp_img;

  tmp_img = gdk_pixbuf_scale_simple(image, width, height, GDK_INTERP_HYPER);
  image_cache_saver_png(tmp_img, filename);
  gdk_pixbuf_unref(tmp_img);

  return;
}

/*
 * @image_cache_saver_png
 *
 * This code was mostly taken from gimageview-0.1.9.
 *
 */
static gboolean image_cache_saver_png(GdkPixbuf *pixbuf  ,
                                      gchar     *filename   ) {
   FILE        *fp;
   gchar       *png_text_filename, *buffer;
   gboolean    has_alpha;
   int         width, height, depth, rowstride;
   guchar      *pixels;
   png_structp png_ptr;
   png_infop   info_ptr;
   png_text    text[2];
   int i;

   /* Initialize the local variables. */
   png_text_filename = buffer = NULL;

   if (pixbuf == NULL)   { return; }
   if (filename == NULL) { return; }
   if (strcmp(filename, "") == 0) { return; }

   fp = fopen (filename, "wb");
   if (fp == NULL) { return FALSE; }

   png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, 
                                     NULL, NULL, NULL       );
   if (png_ptr == NULL) {
      fclose (fp);
      return FALSE;
   }

   info_ptr = png_create_info_struct (png_ptr);
   if (info_ptr == NULL) {
      png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
      fclose (fp);
      return FALSE;
   }

   if (setjmp (png_ptr->jmpbuf)) {
      png_destroy_write_struct(&png_ptr, &info_ptr);
      fclose (fp);
      return FALSE;
   }

   png_init_io(png_ptr, fp);

   has_alpha = gdk_pixbuf_get_has_alpha(pixbuf);
   width  = gdk_pixbuf_get_width (pixbuf);
   height = gdk_pixbuf_get_height(pixbuf);
   depth  = gdk_pixbuf_get_bits_per_sample (pixbuf);
   pixels = gdk_pixbuf_get_pixels(pixbuf);
   rowstride = gdk_pixbuf_get_rowstride(pixbuf);

   png_set_IHDR (png_ptr, info_ptr, width, height,
                 depth, PNG_COLOR_TYPE_RGB_ALPHA,
                 PNG_INTERLACE_NONE,
                 PNG_COMPRESSION_TYPE_DEFAULT,
                 PNG_FILTER_TYPE_DEFAULT);

   /* Some text to go with the png image */
   png_text_filename = strrchr(filename, '/');
   if (png_text_filename == NULL) {
     png_text_filename = filename;
   }
   else {
     png_text_filename++;
   }
   text[0].key  = "Title";
   text[0].text = png_text_filename;
   text[0].compression = PNG_TEXT_COMPRESSION_NONE;
   text[1].key  = "Software";
   text[1].text = "Danepi version 2.7.0";
   text[1].compression = PNG_TEXT_COMPRESSION_NONE;
   png_set_text(png_ptr, info_ptr, text, 2);

   /* Write header data */
   png_write_info (png_ptr, info_ptr);

   /* if there is no alpha in the data, allocate buffer to expand into */
   if (has_alpha) {
      buffer = NULL;
   }
   else {
      buffer = (gchar*)malloc(4 * width);
   }

   /* pump the raster data into libpng, one scan line at a time */
   for (i = 0; i < height; i++) {
      if (has_alpha) {
         png_bytep row_pointer = pixels;
         png_write_row (png_ptr, row_pointer);
      } else {
         /* expand RGB to RGBA using an opaque alpha value */
         gint x;
         gchar *buffer_ptr = buffer;
         gchar *source_ptr = pixels;
         for (x = 0; x < width; x++) {
            *buffer_ptr++ = *source_ptr++;
            *buffer_ptr++ = *source_ptr++;
            *buffer_ptr++ = *source_ptr++;
            *buffer_ptr++ = 255;
         }
         png_write_row (png_ptr, (png_bytep) buffer);
      }
      pixels += rowstride;
   }

   png_write_end (png_ptr, info_ptr);
   png_destroy_write_struct (&png_ptr, &info_ptr);

   free (buffer);

   fclose (fp);
   return TRUE;
}


Generated by  Doxygen 1.6.0   Back to index