/* 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 #include #include #include #ifdef HAVE_UNISTD_H #include #endif #include #include #include #ifdef G_OS_WIN32 #include /* For _getpid() */ #endif #include "appenv.h" #include "drawable.h" #include "gdisplay.h" #include "general.h" #include "gimprc.h" #include "paint_funcs.h" #include "temp_buf.h" #include "libgimp/gimpcolorspace.h" static unsigned char * temp_buf_allocate (unsigned int); static void temp_buf_to_color (TempBuf *, TempBuf *); static void temp_buf_to_gray (TempBuf *, TempBuf *); /* Memory management */ static unsigned char * temp_buf_allocate (unsigned int size) { unsigned char *data; data = (unsigned char *) g_malloc (size); return data; } /* The conversion routines */ static void temp_buf_to_color (TempBuf *src_buf, TempBuf *dest_buf) { unsigned char *src; unsigned char *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--) { unsigned char tmpch; *dest++ = *src++; /* alpha channel */ *dest++ = tmpch = *src++; *dest++ = tmpch; *dest++ = tmpch; } } static void temp_buf_to_gray (TempBuf *src_buf, TempBuf *dest_buf) { unsigned char *src; unsigned char *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++ = (unsigned char) pix; } } TempBuf * temp_buf_new (int width, int height, int bytes, int x, int y, unsigned char *col) { long i; int j; unsigned char * data; TempBuf * temp; temp = (TempBuf *) g_malloc (sizeof (TempBuf)); 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 */ unsigned char * dptr; /* Fill the first row */ dptr = data; for (i = width - 1; i >= 0; --i) { unsigned char * 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, int bytes, int x, int y, int w, int h) { int 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, int x, int y, int w, int h, int border) { TempBuf * new; PixelRegion srcR, destR; unsigned char empty[MAX_CHANNELS] = { 0, 0, 0, 0 }; int 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); } unsigned char * 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 (int width, int height) { static unsigned char empty = 0; return (temp_buf_new (width, height, 1, 0, 0, &empty)); } void mask_buf_free (MaskBuf *mask) { temp_buf_free ((TempBuf *) mask); } unsigned char * 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 int swap_index = 0; /* a static pointer which keeps track of the last request for a swapped buffer */ static TempBuf * cached_in_memory = NULL; static char * 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; char * filename; struct stat stat_buf; int 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); }