home *** CD-ROM | disk | FTP | other *** search
- /* The GIMP -- an image manipulation program
- * Copyright (C) 1995 Spencer Kimball and Peter Mattis
- *
- * 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.
- */
-
- #include "config.h"
-
- #include <glib.h>
-
- #include <errno.h>
- #include <stdlib.h>
- #include <stdio.h>
- #ifdef HAVE_UNISTD_H
- #include <unistd.h>
- #endif
- #include <string.h>
- #include <sys/types.h>
- #include <sys/stat.h>
-
- #ifdef G_OS_WIN32
- #include <process.h> /* For _getpid() */
- #endif
-
- #include "apptypes.h"
-
- #include "appenv.h"
- #include "drawable.h"
- #include "gdisplay.h"
- #include "gimprc.h"
- #include "paint_funcs.h"
- #include "temp_buf.h"
-
- #include "libgimp/gimpcolorspace.h"
-
-
- static guchar * temp_buf_allocate (guint);
- static void temp_buf_to_color (TempBuf *, TempBuf *);
- static void temp_buf_to_gray (TempBuf *, TempBuf *);
-
-
- /* Memory management */
-
- static guchar *
- temp_buf_allocate (guint size)
- {
- guchar *data;
-
- data = g_new (guchar, size);
-
- return data;
- }
-
-
- /* The conversion routines */
-
- static void
- temp_buf_to_color (TempBuf *src_buf,
- TempBuf *dest_buf)
- {
- guchar *src;
- guchar *dest;
- long num_bytes;
-
- src = temp_buf_data (src_buf);
- dest = temp_buf_data (dest_buf);
-
- num_bytes = src_buf->width * src_buf->height;
-
- while (num_bytes--)
- {
- guchar tmpch;
- *dest++ = *src++; /* alpha channel */
- *dest++ = tmpch = *src++;
- *dest++ = tmpch;
- *dest++ = tmpch;
- }
- }
-
-
- static void
- temp_buf_to_gray (TempBuf *src_buf,
- TempBuf *dest_buf)
- {
- guchar *src;
- guchar *dest;
- long num_bytes;
- float pix;
-
- src = temp_buf_data (src_buf);
- dest = temp_buf_data (dest_buf);
-
- num_bytes = src_buf->width * src_buf->height;
-
- while (num_bytes--)
- {
- *dest++ = *src++; /* alpha channel */
-
- pix = INTENSITY (*src++, *src++, *src++);
-
- *dest++ = (guchar) pix;
- }
- }
-
-
- TempBuf *
- temp_buf_new (gint width,
- gint height,
- gint bytes,
- gint x,
- gint y,
- guchar *col)
- {
- long i;
- int j;
- guchar *data;
- TempBuf *temp;
-
- temp = g_new (TempBuf, 1);
-
- temp->width = width;
- temp->height = height;
- temp->bytes = bytes;
- temp->x = x;
- temp->y = y;
- temp->swapped = FALSE;
- temp->filename = NULL;
-
- temp->data = data = temp_buf_allocate (width * height * bytes);
-
- /* initialize the data */
- if (col)
- {
- /* First check if we can save a lot of work */
- if (bytes == 1)
- {
- memset (data, *col, width * height);
- }
- else if ((bytes == 3) && (col[1] == *col) && (*col == col[2]))
- {
- memset (data, *col, width * height * 3);
- }
- else if ((bytes == 4) && (col[1] == *col) && (*col == col[2]) && (col[2] == col[3]))
- {
- memset (data, *col, (width * height) << 2);
- }
- else
- {
- /* No, we cannot */
- guchar * dptr;
- /* Fill the first row */
- dptr = data;
- for (i = width - 1; i >= 0; --i)
- {
- guchar * init;
- j = bytes;
- init = col;
- while (j--)
- *dptr++ = *init++;
- }
- /* Now copy from it (we set bytes to bytesperrow now) */
- bytes *= width;
- while (--height)
- {
- memcpy (dptr, data, bytes);
- dptr += bytes;
- }
- }
- }
-
- return temp;
- }
-
-
- TempBuf *
- temp_buf_copy (TempBuf *src,
- TempBuf *dest)
- {
- TempBuf * new;
- long length;
-
- if (!src)
- {
- g_message ("trying to copy a temp buf which is NULL.");
- return dest;
- }
-
- if (!dest)
- new = temp_buf_new (src->width, src->height, src->bytes, 0, 0, NULL);
- else
- {
- new = dest;
- if (dest->width != src->width || dest->height != src->height)
- g_message ("In temp_buf_copy, the widths or heights don't match.");
- /* The temp buf is smart, and can translate between color and gray */
- /* (only necessary if not we allocated it */
- if (src->bytes != new->bytes)
- {
- if (src->bytes == 4) /* RGB color */
- temp_buf_to_gray (src, new);
- else if (src->bytes == 2) /* grayscale */
- temp_buf_to_color (src, new);
- else
- g_message ("Cannot convert from indexed color.");
- return new;
- }
- }
-
- /* make the copy */
- length = src->width * src->height * src->bytes;
- memcpy (temp_buf_data (new), temp_buf_data (src), length);
-
- return new;
- }
-
-
- TempBuf *
- temp_buf_resize (TempBuf *buf,
- gint bytes,
- gint x,
- gint y,
- gint w,
- gint h)
- {
- gint size;
-
- /* calculate the requested size */
- size = w * h * bytes;
-
- /* First, configure the canvas buffer */
- if (!buf)
- buf = temp_buf_new (w, h, bytes, x, y, NULL);
- else
- {
- if (size != (buf->width * buf->height * buf->bytes))
- {
- /* Make sure the temp buf is unswapped */
- temp_buf_unswap (buf);
-
- /* Reallocate the data for it */
- buf->data = g_realloc (buf->data, size);
- }
-
- /* Make sure the temp buf fields are valid */
- buf->x = x;
- buf->y = y;
- buf->width = w;
- buf->height = h;
- buf->bytes = bytes;
- }
-
- return buf;
- }
-
-
- TempBuf *
- temp_buf_copy_area (TempBuf *src,
- TempBuf *dest,
- gint x,
- gint y,
- gint w,
- gint h,
- gint border)
- {
- TempBuf * new;
- PixelRegion srcR, destR;
- guchar empty[MAX_CHANNELS] = { 0, 0, 0, 0 };
- gint x1, y1, x2, y2;
-
- if (!src)
- {
- g_message ("trying to copy a temp buf which is NULL.");
- return dest;
- }
-
- /* some bounds checking */
- x1 = CLAMP (x, 0, src->width);
- y1 = CLAMP (y, 0, src->height);
- x2 = CLAMP (x + w, 0, src->width);
- y2 = CLAMP (y + h, 0, src->height);
-
- if (!(x2 - x1) || !(y2 - y1))
- return dest;
-
- x = x1 - border;
- y = y1 - border;
- w = (x2 - x1) + border * 2;
- h = (y2 - y1) + border * 2;
-
- if (!dest)
- new = temp_buf_new (w, h, src->bytes, x, y, empty);
- else
- {
- new = dest;
- if (dest->bytes != src->bytes)
- g_message ("In temp_buf_copy_area, the widths or heights or bytes don't match.");
- }
-
- /* Set the offsets for the dest */
- new->x = src->x + x;
- new->y = src->y + y;
-
- /* Copy the region */
- srcR.bytes = src->bytes;
- srcR.w = (x2 - x1);
- srcR.h = (y2 - y1);
- srcR.rowstride = src->bytes * src->width;
- srcR.data = temp_buf_data (src) + y1 * srcR.rowstride + x1 * srcR.bytes;
-
- destR.rowstride = new->bytes * new->width;
- destR.data = temp_buf_data (new) + (y1 - y) * destR.rowstride + (x1 - x) * srcR.bytes;
-
- copy_region (&srcR, &destR);
-
- return new;
- }
-
-
- void
- temp_buf_free (TempBuf *temp_buf)
- {
- if (temp_buf->data)
- g_free (temp_buf->data);
-
- if (temp_buf->swapped)
- temp_buf_swap_free (temp_buf);
-
- g_free (temp_buf);
- }
-
-
- guchar *
- temp_buf_data (TempBuf *temp_buf)
- {
- if (temp_buf->swapped)
- temp_buf_unswap (temp_buf);
-
- return temp_buf->data;
- }
-
-
- /******************************************************************
- * Mask buffer functions *
- ******************************************************************/
-
-
- MaskBuf *
- mask_buf_new (gint width,
- gint height)
- {
- static guchar empty = 0;
-
- return (temp_buf_new (width, height, 1, 0, 0, &empty));
- }
-
-
- void
- mask_buf_free (MaskBuf *mask)
- {
- temp_buf_free ((TempBuf *) mask);
- }
-
-
- guchar *
- mask_buf_data (MaskBuf *mask_buf)
- {
- if (mask_buf->swapped)
- temp_buf_unswap (mask_buf);
-
- return mask_buf->data;
- }
-
-
- /******************************************************************
- * temp buffer disk caching functions *
- ******************************************************************/
-
- /* NOTES:
- * Disk caching is setup as follows:
- * On a call to temp_buf_swap, the TempBuf parameter is stored
- * in a temporary variable called cached_in_memory.
- * On the next call to temp_buf_swap, if cached_in_memory is non-null,
- * cached_in_memory is moved to disk, and the latest TempBuf parameter
- * is stored in cached_in_memory. This method keeps the latest TempBuf
- * structure in memory instead of moving it directly to disk as requested.
- * On a call to temp_buf_unswap, if cached_in_memory is non-null, it is
- * compared against the requested TempBuf. If they are the same, nothing
- * must be moved in from disk since it still resides in memory. However,
- * if the two pointers are different, the requested TempBuf is retrieved
- * from disk. In the former case, cached_in_memory is set to NULL;
- * in the latter case, cached_in_memory is left unchanged.
- * If temp_buf_swap_free is called, cached_in_memory must be checked
- * against the temp buf being freed. If they are the same, then
- * cached_in_memory must be set to NULL;
- *
- * In the case where memory usage is set to "stingy":
- * temp bufs are not cached in memory at all, they go right to disk.
- */
-
-
- /* a static counter for generating unique filenames */
- static gint swap_index = 0;
-
- /* a static pointer which keeps track of the last request for a swapped buffer */
- static TempBuf * cached_in_memory = NULL;
-
-
- static gchar *
- generate_unique_filename (void)
- {
- pid_t pid;
- pid = getpid ();
- return g_strdup_printf ("%s" G_DIR_SEPARATOR_S "gimp%d.%d",
- temp_path, (int) pid, swap_index++);
- }
-
-
- void
- temp_buf_swap (TempBuf *buf)
- {
- TempBuf * swap;
- gchar * filename;
- struct stat stat_buf;
- gint err;
- FILE * fp;
-
- if (!buf || buf->swapped)
- return;
-
- /* Set the swapped flag */
- buf->swapped = TRUE;
-
- if (stingy_memory_use)
- swap = buf;
- else
- {
- swap = cached_in_memory;
- cached_in_memory = buf;
- }
-
- /* For the case where there is no temp buf ready to be moved to disk, return */
- if (!swap)
- return;
-
- /* Get a unique filename for caching the data to a UNIX file */
- filename = generate_unique_filename ();
-
- /* Check if generated filename is valid */
- err = stat (filename, &stat_buf);
- if (!err)
- {
- if (stat_buf.st_mode & S_IFDIR)
- {
- g_message ("Error in temp buf caching: \"%s\" is a directory (cannot overwrite)", filename);
- g_free (filename);
- return;
- }
- }
-
- /* Open file for overwrite */
- if ((fp = fopen (filename, "wb")))
- {
- size_t blocks_written;
- blocks_written = fwrite (swap->data, swap->width * swap->height * swap->bytes, 1, fp);
- /* Check whether all bytes were written and fclose() was able to flush its buffers */
- if ((0 != fclose (fp)) || (1 != blocks_written))
- {
- (void) unlink (filename);
- perror ("Write error on temp buf");
- g_message ("Cannot write \"%s\"", filename);
- g_free (filename);
- return;
- }
- }
- else
- {
- (void) unlink (filename);
- perror ("Error in temp buf caching");
- g_message ("Cannot write \"%s\"", filename);
- g_free (filename);
- return;
- }
- /* Finally, free the buffer's data */
- g_free (swap->data);
- swap->data = NULL;
-
- swap->filename = filename;
- }
-
-
- void
- temp_buf_unswap (TempBuf *buf)
- {
- struct stat stat_buf;
- FILE * fp;
- gboolean succ = FALSE;
-
- if (!buf || !buf->swapped)
- return;
-
- /* Set the swapped flag */
- buf->swapped = FALSE;
-
- /* If the requested temp buf is still in memory, simply return */
- if (cached_in_memory == buf)
- {
- cached_in_memory = NULL;
- return;
- }
-
- /* Allocate memory for the buffer's data */
- buf->data = temp_buf_allocate (buf->width * buf->height * buf->bytes);
-
- /* Find out if the filename of the swapped data is an existing file... */
- /* (buf->filname HAS to be != 0 */
- if (!stat (buf->filename, &stat_buf))
- {
- if ((fp = fopen (buf->filename, "rb")))
- {
- size_t blocks_read;
- blocks_read = fread (buf->data, buf->width * buf->height * buf->bytes, 1, fp);
- (void) fclose (fp);
- if (blocks_read != 1)
- perror ("Read error on temp buf");
- else
- succ = TRUE;
- }
- else
- perror ("Error in temp buf caching");
-
- /* Delete the swap file */
- unlink (buf->filename);
- }
- if (!succ)
- g_message ("Error in temp buf caching: information swapped to disk was lost!");
-
- g_free (buf->filename); /* free filename */
- buf->filename = NULL;
- }
-
-
- void
- temp_buf_swap_free (TempBuf *buf)
- {
- struct stat stat_buf;
-
- if (!buf->swapped)
- return;
-
- /* Set the swapped flag */
- buf->swapped = FALSE;
-
- /* If the requested temp buf is cached in memory... */
- if (cached_in_memory == buf)
- {
- cached_in_memory = NULL;
- return;
- }
-
- /* Find out if the filename of the swapped data is an existing file... */
- if (!stat (buf->filename, &stat_buf))
- {
- /* Delete the swap file */
- unlink (buf->filename);
- }
- else
- g_message ("Error in temp buf disk swapping: information swapped to disk was lost!");
-
- if (buf->filename)
- g_free (buf->filename); /* free filename */
- buf->filename = NULL;
- }
-
-
- void
- swapping_free (void)
- {
- if (cached_in_memory)
- temp_buf_free (cached_in_memory);
- }
-