gimp/app/base/temp-buf.c
Sven Neumann 023c76978f configure.in etc/Makefile.am etc/gimprc.in removed templates for gimprc
2002-12-29  Sven Neumann  <sven@gimp.org>

	* configure.in
	* etc/Makefile.am
	* etc/gimprc.in
	* etc/gimprc_user.in: removed templates for gimprc files.

	* etc/gimprc: added this file as generated by gimp-config-dump.

	* app/gui/user-install-dialog.c
	* data/misc/user_install: don't install an empty user gimprc.

	* app/config/Makefile.am
	* app/config/gimpconfig-substitute.[ch]: removed these files.
	* app/config/gimpconfig-path.[ch]: and added them again with
	reduced functionality. Paths found in config files are now
	basically handled like standard strings by the config system.
	Users of the GimpConfig path variables need to expand the path
	themselves.

	* app/config/gimpbaseconfig.c
	* app/config/gimpconfig-deserialize.c
	* app/config/gimpconfig-dump.c
	* app/config/gimpconfig-utils.c
	* app/config/gimpconfig.c
	* app/config/gimpcoreconfig.c
	* app/config/gimprc.c:
	* app/base/base.c
	* app/base/temp-buf.c
	* app/core/gimp.c
	* app/core/gimpdatafactory.c
	* app/core/gimpmodules.c
	* app/gui/user-install-dialog.c
	* app/plug-in/plug-in.c
	* app/tools/tools.c
	* app/widgets/gimppropwidgets.c: changed accordingly.
2002-12-29 18:58:24 +00:00

810 lines
18 KiB
C

/* 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 <errno.h>
#include <stdlib.h>
#include <stdio.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <string.h>
#include <glib-object.h>
#ifdef G_OS_WIN32
#include <process.h> /* For _getpid() */
#endif
#include "libgimpcolor/gimpcolor.h"
#include "base-types.h"
#include "config/gimpbaseconfig.h"
#include "config/gimpconfig-path.h"
#include "pixel-region.h"
#include "temp-buf.h"
#include "paint-funcs/paint-funcs.h"
static guchar * temp_buf_allocate (guint);
static void temp_buf_to_color (TempBuf *src_buf,
TempBuf *dest_buf);
static void temp_buf_to_gray (TempBuf *src_buf,
TempBuf *dest_buf);
#ifdef __GNUC__
#warning FIXME: extern GimpBaseConfig *base_config;
#endif
extern GimpBaseConfig *base_config;
/* 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;
glong num_pixels;
src = temp_buf_data (src_buf);
dest = temp_buf_data (dest_buf);
num_pixels = src_buf->width * src_buf->height;
if (dest_buf->bytes == 4)
{
while (num_pixels--)
{
guchar tmpch;
*dest++ = tmpch = *src++;
*dest++ = tmpch;
*dest++ = tmpch;
*dest++ = *src++; /* alpha channel */
}
}
else
{
while (num_pixels--)
{
guchar tmpch;
*dest++ = tmpch = *src++;
*dest++ = tmpch;
*dest++ = tmpch;
}
}
}
static void
temp_buf_to_gray (TempBuf *src_buf,
TempBuf *dest_buf)
{
guchar *src;
guchar *dest;
glong num_pixels;
gfloat pix;
src = temp_buf_data (src_buf);
dest = temp_buf_data (dest_buf);
num_pixels = src_buf->width * src_buf->height;
if (dest_buf->bytes == 2)
{
while (num_pixels--)
{
pix = INTENSITY (src[0], src[1], src[2]);
*dest++ = (guchar) pix;
*dest++ = src[3]; /* alpha channel */
src += 4;
}
}
else
{
while (num_pixels--)
{
pix = INTENSITY (src[0], src[1], src[2]);
*dest++ = (guchar) pix;
src += 3;
}
}
}
TempBuf *
temp_buf_new (gint width,
gint height,
gint bytes,
gint x,
gint y,
guchar *col)
{
glong i;
gint j;
guchar *data;
TempBuf *temp;
g_return_val_if_fail (width > 0 && height > 0, NULL);
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;
}
/* This function simply renders a checkerboard with the given
parameters into a newly allocated tempbuf */
TempBuf *
temp_buf_new_check (gint width,
gint height,
GimpCheckType check_type,
GimpCheckSize check_size)
{
TempBuf *newbuf;
guchar *data;
guchar check_shift = 0;
guchar fg_color = 0;
guchar bg_color = 0;
gint i, j;
g_return_val_if_fail (width > 0 && height > 0, NULL);
switch (check_size)
{
case GIMP_SMALL_CHECKS:
check_shift = 2;
break;
case GIMP_MEDIUM_CHECKS:
check_shift = 4;
break;
case GIMP_LARGE_CHECKS:
check_shift = 6;
break;
}
switch (check_type)
{
case GIMP_LIGHT_CHECKS:
fg_color = 204;
bg_color = 255;
break;
case GIMP_GRAY_CHECKS:
fg_color = 102;
bg_color = 153;
break;
case GIMP_DARK_CHECKS:
fg_color = 0;
bg_color = 51;
break;
case GIMP_WHITE_ONLY:
fg_color = 255;
bg_color = 255;
break;
case GIMP_GRAY_ONLY:
fg_color = 127;
bg_color = 127;
break;
case GIMP_BLACK_ONLY:
fg_color = 0;
bg_color = 0;
}
newbuf = temp_buf_new (width, height, 3, 0, 0, NULL);
data = temp_buf_data (newbuf);
for (i = 0, j = 1; i <= height; j++)
{
for (i = 1; i <= width; i++)
{
*(data + i - 1) = ((j & check_shift) && (i & check_shift))
? fg_color : bg_color;
}
}
return newbuf;
}
TempBuf *
temp_buf_copy (TempBuf *src,
TempBuf *dest)
{
glong length;
g_return_val_if_fail (src != NULL, NULL);
g_return_val_if_fail (! dest || (dest->width == src->width &&
dest->height == src->height), NULL);
if (! dest)
{
dest = temp_buf_new (src->width, src->height, src->bytes, 0, 0, NULL);
}
if (src->bytes != dest->bytes)
{
if (src->bytes == 4 && dest->bytes == 2) /* RGBA -> GRAYA */
temp_buf_to_gray (src, dest);
else if (src->bytes == 3 && dest->bytes == 1) /* RGB -> GRAY */
temp_buf_to_gray (src, dest);
else if (src->bytes == 2 && dest->bytes == 4) /* GRAYA -> RGBA */
temp_buf_to_color (src, dest);
else if (src->bytes == 1 && dest->bytes == 3) /* GRAYA -> RGBA */
temp_buf_to_color (src, dest);
else
g_warning ("temp_buf_copy(): unimplemented color conversion");
}
else
{
/* make the copy */
length = src->width * src->height * src->bytes;
memcpy (temp_buf_data (dest), temp_buf_data (src), length);
}
return dest;
}
TempBuf *
temp_buf_resize (TempBuf *buf,
gint bytes,
gint x,
gint y,
gint width,
gint height)
{
gint size;
g_return_val_if_fail (width > 0 && height > 0, NULL);
/* calculate the requested size */
size = width * height * bytes;
/* First, configure the canvas buffer */
if (!buf)
{
buf = temp_buf_new (width, height, 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_renew (guchar, buf->data, size);
}
/* Make sure the temp buf fields are valid */
buf->x = x;
buf->y = y;
buf->width = width;
buf->height = height;
buf->bytes = bytes;
}
return buf;
}
TempBuf *
temp_buf_scale (TempBuf *src,
gint new_width,
gint new_height)
{
gint loop1;
gint loop2;
gdouble x_ratio;
gdouble y_ratio;
guchar *src_data;
guchar *dest_data;
TempBuf *dest;
g_return_val_if_fail (src != NULL, NULL);
g_return_val_if_fail (new_width > 0 && new_height > 0, NULL);
dest = temp_buf_new (new_width,
new_height,
src->bytes,
0, 0, NULL);
src_data = temp_buf_data (src);
dest_data = temp_buf_data (dest);
x_ratio = (gdouble) src->width / (gdouble) new_width;
y_ratio = (gdouble) src->height / (gdouble) new_height;
for (loop1 = 0 ; loop1 < new_height ; loop1++)
{
for (loop2 = 0 ; loop2 < new_width ; loop2++)
{
gint i;
guchar *src_pixel;
guchar *dest_pixel;
src_pixel = src_data +
(gint) (loop2 * x_ratio) * src->bytes +
(gint) (loop1 * y_ratio) * src->bytes * src->width;
dest_pixel = dest_data +
(loop2 + loop1 * new_width) * src->bytes;
for (i = 0 ; i < src->bytes; i++)
*dest_pixel++ = *src_pixel++;
}
}
return dest;
}
TempBuf *
temp_buf_copy_area (TempBuf *src,
TempBuf *dest,
gint x,
gint y,
gint width,
gint height,
gint dest_x,
gint dest_y)
{
TempBuf *new;
PixelRegion srcPR = { 0 };
PixelRegion destPR = { 0 };
guchar empty[MAX_CHANNELS] = { 0, 0, 0, 0 };
gint x1, y1, x2, y2;
g_return_val_if_fail (src != NULL, dest);
g_return_val_if_fail (!dest || dest->bytes == src->bytes, dest);
g_return_val_if_fail (width + dest_x > 0, dest);
g_return_val_if_fail (height + dest_y > 0, dest);
g_return_val_if_fail (!dest || dest->width >= width + dest_x, dest);
g_return_val_if_fail (!dest || dest->height >= height + dest_y, dest);
/* some bounds checking */
x1 = CLAMP (x, 0, src->width - 1);
y1 = CLAMP (y, 0, src->height - 1);
x2 = CLAMP (x + width - 1, 0, src->width - 1);
y2 = CLAMP (y + height - 1, 0, src->height - 1);
if (!(x2 - x1) || !(y2 - y1))
return dest;
width = x2 - x1 + 1;
height = y2 - y1 + 1;
if (! dest)
{
new = temp_buf_new (width + dest_x,
height + dest_y,
src->bytes,
0, 0,
empty);
}
else
{
new = dest;
}
/* Copy the region */
srcPR.bytes = src->bytes;
srcPR.w = width;
srcPR.h = height;
srcPR.rowstride = src->bytes * src->width;
srcPR.data = (temp_buf_data (src) +
y1 * srcPR.rowstride +
x1 * srcPR.bytes);
destPR.bytes = dest->bytes;
destPR.rowstride = new->bytes * new->width;
destPR.data = (temp_buf_data (new) +
dest_y * destPR.rowstride +
dest_x * destPR.bytes);
copy_region (&srcPR, &destPR);
return new;
}
void
temp_buf_free (TempBuf *temp_buf)
{
g_return_if_fail (temp_buf != NULL);
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;
}
guchar *
temp_buf_data_clear (TempBuf *temp_buf)
{
g_return_val_if_fail (temp_buf != NULL, NULL);
if (temp_buf->swapped)
temp_buf_unswap (temp_buf);
memset (temp_buf->data, 0,
temp_buf->height * temp_buf->width * temp_buf->bytes);
return temp_buf->data;
}
gsize
temp_buf_get_memsize (TempBuf *temp_buf)
{
gsize memsize = 0;
g_return_val_if_fail (temp_buf != NULL, 0);
memsize += sizeof (TempBuf);
if (temp_buf->swapped)
{
memsize += strlen (temp_buf->filename) + 1;
}
else
{
memsize += (temp_buf->bytes *
temp_buf->width *
temp_buf->height);
}
return memsize;
}
/******************************************************************
* 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)
{
return temp_buf_data ((TempBuf *) mask_buf);
}
guchar *
mask_buf_data_clear (MaskBuf *mask_buf)
{
return temp_buf_data_clear ((TempBuf *) mask_buf);
}
/******************************************************************
* 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 tmp_file_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_tmp_filename (GimpBaseConfig *config)
{
pid_t pid;
gchar *tmpdir;
gchar *tmpfile;
gchar *path;
tmpdir = gimp_config_path_expand (config->temp_path, TRUE, NULL);
pid = getpid ();
tmpfile = g_strdup_printf ("gimp%d.%d",
(gint) pid,
tmp_file_index++);
path = g_build_filename (tmpdir, tmpfile, NULL);
g_free (tmpfile);
g_free (tmpdir);
return path;
}
void
temp_buf_swap (TempBuf *buf)
{
TempBuf *swap;
gchar *filename;
FILE *fp;
if (!buf || buf->swapped)
return;
/* Set the swapped flag */
buf->swapped = TRUE;
if (base_config->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_tmp_filename (base_config);
/* Check if generated filename is valid */
if (g_file_test (filename, G_FILE_TEST_IS_DIR))
{
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)
{
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);
if (g_file_test (buf->filename, G_FILE_TEST_IS_REGULAR))
{
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)
{
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 (g_file_test (buf->filename, G_FILE_TEST_IS_REGULAR))
{
/* 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);
}