temp-buf.c 13.3 KB
Newer Older
Elliot Lee's avatar
Elliot Lee committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/* 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
16
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
Elliot Lee's avatar
Elliot Lee committed
17
 */
18
19
#include "config.h"

Tor Lillqvist's avatar
Tor Lillqvist committed
20
21
#include <glib.h>

Elliot Lee's avatar
Elliot Lee committed
22
23
24
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
25
#ifdef HAVE_UNISTD_H
Elliot Lee's avatar
Elliot Lee committed
26
#include <unistd.h>
27
#endif
Elliot Lee's avatar
Elliot Lee committed
28
#include <string.h>
Asbjørn Pettersen's avatar
Asbjørn Pettersen committed
29
#include <sys/types.h>
Elliot Lee's avatar
Elliot Lee committed
30
#include <sys/stat.h>
31

32
#ifdef G_OS_WIN32
33
34
35
#include <process.h>		/* For _getpid() */
#endif
 
Elliot Lee's avatar
Elliot Lee committed
36
37
38
39
40
41
42
#include "appenv.h"
#include "drawable.h"
#include "gdisplay.h"
#include "gimprc.h"
#include "paint_funcs.h"
#include "temp_buf.h"

43
#include "libgimp/gimpcolorspace.h"
Elliot Lee's avatar
Elliot Lee committed
44

45
46
47
48

static guchar * temp_buf_allocate (guint);
static void     temp_buf_to_color (TempBuf *, TempBuf *);
static void     temp_buf_to_gray  (TempBuf *, TempBuf *);
Elliot Lee's avatar
Elliot Lee committed
49
50
51
52


/*  Memory management  */

53
54
static guchar *
temp_buf_allocate (guint size)
Elliot Lee's avatar
Elliot Lee committed
55
{
56
  guchar *data;
Elliot Lee's avatar
Elliot Lee committed
57

58
  data = g_new (guchar, size);
Elliot Lee's avatar
Elliot Lee committed
59
60
61
62
63
64
65
66

  return data;
}


/*  The conversion routines  */

static void
Sven Neumann's avatar
Sven Neumann committed
67
68
temp_buf_to_color (TempBuf *src_buf, 
		   TempBuf *dest_buf)
Elliot Lee's avatar
Elliot Lee committed
69
{
70
71
  guchar *src;
  guchar *dest;
Elliot Lee's avatar
Elliot Lee committed
72
73
74
75
76
77
78
79
80
  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--)
    {
81
      guchar tmpch;
Elliot Lee's avatar
Elliot Lee committed
82
      *dest++ = *src++;  /* alpha channel */
83
84
85
      *dest++ = tmpch = *src++;
      *dest++ = tmpch;
      *dest++ = tmpch;
Elliot Lee's avatar
Elliot Lee committed
86
87
88
89
90
    }
}


static void
Sven Neumann's avatar
Sven Neumann committed
91
92
temp_buf_to_gray (TempBuf *src_buf, 
		  TempBuf *dest_buf)
Elliot Lee's avatar
Elliot Lee committed
93
{
94
95
  guchar *src;
  guchar *dest;
Sven Neumann's avatar
Sven Neumann committed
96
  long  num_bytes;
Elliot Lee's avatar
Elliot Lee committed
97
98
99
100
101
102
103
104
105
106
107
  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 */

108
      pix = INTENSITY (*src++, *src++, *src++);
Elliot Lee's avatar
Elliot Lee committed
109

110
      *dest++ = (guchar) pix;
Elliot Lee's avatar
Elliot Lee committed
111
112
113
114
115
    }
}


TempBuf *
116
117
118
119
120
121
temp_buf_new (gint    width, 
	      gint    height, 
	      gint    bytes, 
	      gint    x, 
	      gint    y, 
	      guchar *col)
Elliot Lee's avatar
Elliot Lee committed
122
123
{
  long i;
Sven Neumann's avatar
Sven Neumann committed
124
  int  j;
125
126
  guchar  *data;
  TempBuf *temp;
Elliot Lee's avatar
Elliot Lee committed
127

128
  temp = g_new (TempBuf, 1);
Elliot Lee's avatar
Elliot Lee committed
129
130
131
132
133
134
135
136
137

  temp->width  = width;
  temp->height = height;
  temp->bytes  = bytes;
  temp->x      = x;
  temp->y      = y;
  temp->swapped = FALSE;
  temp->filename = NULL;

138
  temp->data = data = temp_buf_allocate (width * height * bytes);
Elliot Lee's avatar
Elliot Lee committed
139
140
141
142

  /*  initialize the data  */
  if (col)
    {
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
      /* 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 */
159
          guchar * dptr;
160
161
162
163
          /* Fill the first row */
          dptr = data;
          for (i = width - 1; i >= 0; --i)
            {
164
              guchar * init;
165
166
167
168
169
170
171
172
173
174
175
176
177
              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;
            }
        }
Elliot Lee's avatar
Elliot Lee committed
178
179
180
181
182
183
184
    }

  return temp;
}


TempBuf *
Sven Neumann's avatar
Sven Neumann committed
185
186
temp_buf_copy (TempBuf *src, 
	       TempBuf *dest)
Elliot Lee's avatar
Elliot Lee committed
187
188
189
190
191
192
{
  TempBuf * new;
  long length;

  if (!src)
    {
193
      g_message ("trying to copy a temp buf which is NULL.");
Elliot Lee's avatar
Elliot Lee committed
194
195
196
197
198
199
200
201
202
      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)
203
	g_message ("In temp_buf_copy, the widths or heights don't match.");
204
205
206
207
208
209
210
211
212
      /*  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
213
	    g_message ("Cannot convert from indexed color.");
214
215
	  return new;
        }
Elliot Lee's avatar
Elliot Lee committed
216
217
    }

218
219
220
  /* make the copy */
  length = src->width * src->height * src->bytes;
  memcpy (temp_buf_data (new), temp_buf_data (src), length);
Elliot Lee's avatar
Elliot Lee committed
221
222
223
224
225
226

  return new;
}


TempBuf *
Sven Neumann's avatar
Sven Neumann committed
227
temp_buf_resize (TempBuf *buf, 
228
229
230
231
232
		 gint     bytes, 
		 gint     x, 
		 gint     y, 
		 gint     w, 
		 gint     h)
Elliot Lee's avatar
Elliot Lee committed
233
{
234
  gint size;
Elliot Lee's avatar
Elliot Lee committed
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265

  /*  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 *
Sven Neumann's avatar
Sven Neumann committed
266
267
temp_buf_copy_area (TempBuf *src, 
		    TempBuf *dest, 
268
269
270
271
272
		    gint     x, 
		    gint     y, 
		    gint     w, 
		    gint     h, 
		    gint     border)
Elliot Lee's avatar
Elliot Lee committed
273
274
275
{
  TempBuf * new;
  PixelRegion srcR, destR;
276
277
  guchar empty[MAX_CHANNELS] = { 0, 0, 0, 0 };
  gint x1, y1, x2, y2;
Elliot Lee's avatar
Elliot Lee committed
278
279
280

  if (!src)
    {
281
      g_message ("trying to copy a temp buf which is NULL.");
Elliot Lee's avatar
Elliot Lee committed
282
283
284
285
      return dest;
    }

  /*  some bounds checking  */
286
287
288
289
  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);
Elliot Lee's avatar
Elliot Lee committed
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304

  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)
305
	g_message ("In temp_buf_copy_area, the widths or heights or bytes don't match.");
Elliot Lee's avatar
Elliot Lee committed
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
    }

  /*  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
Sven Neumann's avatar
Sven Neumann committed
329
temp_buf_free (TempBuf *temp_buf)
Elliot Lee's avatar
Elliot Lee committed
330
331
332
333
334
335
336
337
338
339
340
{
  if (temp_buf->data)
    g_free (temp_buf->data);

  if (temp_buf->swapped)
    temp_buf_swap_free (temp_buf);

  g_free (temp_buf);
}


341
guchar *
Sven Neumann's avatar
Sven Neumann committed
342
temp_buf_data (TempBuf *temp_buf)
Elliot Lee's avatar
Elliot Lee committed
343
344
345
346
347
348
349
350
351
352
353
354
355
356
{
  if (temp_buf->swapped)
    temp_buf_unswap (temp_buf);

  return temp_buf->data;
}


/******************************************************************
 *  Mask buffer functions                                         *
 ******************************************************************/


MaskBuf *
357
358
mask_buf_new (gint width, 
	      gint height)
Elliot Lee's avatar
Elliot Lee committed
359
{
360
  static guchar empty = 0;
Elliot Lee's avatar
Elliot Lee committed
361
362
363
364
365
366

  return (temp_buf_new (width, height, 1, 0, 0, &empty));
}


void
Sven Neumann's avatar
Sven Neumann committed
367
mask_buf_free (MaskBuf *mask)
Elliot Lee's avatar
Elliot Lee committed
368
369
370
371
372
{
  temp_buf_free ((TempBuf *) mask);
}


373
guchar *
Sven Neumann's avatar
Sven Neumann committed
374
mask_buf_data (MaskBuf *mask_buf)
Elliot Lee's avatar
Elliot Lee committed
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
{
  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
Sven Neumann's avatar
Sven Neumann committed
402
403
 *    against the temp buf being freed.  If they are the same, then 
 *    cached_in_memory must be set to NULL;
Elliot Lee's avatar
Elliot Lee committed
404
405
406
407
408
 *
 *  In the case where memory usage is set to "stingy":
 *    temp bufs are not cached in memory at all, they go right to disk.
 */

Sven Neumann's avatar
Sven Neumann committed
409

Elliot Lee's avatar
Elliot Lee committed
410
/*  a static counter for generating unique filenames  */
411
static gint swap_index = 0;
Elliot Lee's avatar
Elliot Lee committed
412
413
414
415
416

/*  a static pointer which keeps track of the last request for a swapped buffer  */
static TempBuf * cached_in_memory = NULL;


417
static gchar *
Elliot Lee's avatar
Elliot Lee committed
418
419
420
421
generate_unique_filename (void)
{
  pid_t pid;
  pid = getpid ();
422
423
  return g_strdup_printf ("%s" G_DIR_SEPARATOR_S "gimp%d.%d",
			  temp_path, (int) pid, swap_index++);
Elliot Lee's avatar
Elliot Lee committed
424
425
426
427
}


void
Sven Neumann's avatar
Sven Neumann committed
428
temp_buf_swap (TempBuf *buf)
Elliot Lee's avatar
Elliot Lee committed
429
430
{
  TempBuf * swap;
431
  gchar * filename;
Elliot Lee's avatar
Elliot Lee committed
432
  struct stat stat_buf;
433
  gint err;
Elliot Lee's avatar
Elliot Lee committed
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
  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)
	{
463
	  g_message ("Error in temp buf caching: \"%s\" is a directory (cannot overwrite)", filename);
Elliot Lee's avatar
Elliot Lee committed
464
465
466
467
468
469
	  g_free (filename);
	  return;
	}
    }

  /*  Open file for overwrite  */
470
  if ((fp = fopen (filename, "wb")))
Elliot Lee's avatar
Elliot Lee committed
471
    {
472
473
474
475
476
477
478
      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");
479
          g_message ("Cannot write \"%s\"", filename);
480
481
482
          g_free (filename);
          return;
        }
Elliot Lee's avatar
Elliot Lee committed
483
484
485
    }
  else
    {
486
      (void) unlink (filename);
Elliot Lee's avatar
Elliot Lee committed
487
      perror ("Error in temp buf caching");
488
      g_message ("Cannot write \"%s\"", filename);
Elliot Lee's avatar
Elliot Lee committed
489
490
491
492
493
494
495
496
497
498
499
500
      g_free (filename);
      return;
    }
  /*  Finally, free the buffer's data  */
  g_free (swap->data);
  swap->data = NULL;

  swap->filename = filename;
}


void
Sven Neumann's avatar
Sven Neumann committed
501
temp_buf_unswap (TempBuf *buf)
Elliot Lee's avatar
Elliot Lee committed
502
503
504
{
  struct stat stat_buf;
  FILE * fp;
505
  gboolean succ = FALSE;
Elliot Lee's avatar
Elliot Lee committed
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523

  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... */
524
  /*  (buf->filname HAS to be != 0 */
Elliot Lee's avatar
Elliot Lee committed
525
526
  if (!stat (buf->filename, &stat_buf))
    {
527
      if ((fp = fopen (buf->filename, "rb")))
Elliot Lee's avatar
Elliot Lee committed
528
	{
529
530
531
532
533
	  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");
534
535
	  else
	    succ = TRUE;
Elliot Lee's avatar
Elliot Lee committed
536
537
538
539
540
541
542
	}
      else
	perror ("Error in temp buf caching");

      /*  Delete the swap file  */
      unlink (buf->filename);
    }
543
  if (!succ)
544
    g_message ("Error in temp buf caching: information swapped to disk was lost!");
Elliot Lee's avatar
Elliot Lee committed
545

546
  g_free (buf->filename);   /*  free filename  */
Elliot Lee's avatar
Elliot Lee committed
547
548
549
550
551
  buf->filename = NULL;
}


void
Sven Neumann's avatar
Sven Neumann committed
552
temp_buf_swap_free (TempBuf *buf)
Elliot Lee's avatar
Elliot Lee committed
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
{
  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
576
    g_message ("Error in temp buf disk swapping: information swapped to disk was lost!");
Elliot Lee's avatar
Elliot Lee committed
577
578
579
580
581
582
583
584
585
586
587
588
589

  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);
}