temp-buf.c 13.5 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
20

#include "config.h"

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

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

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

45
#include "libgimp/gimpcolorspace.h"
Elliot Lee's avatar
Elliot Lee committed
46
47
48

static unsigned char *   temp_buf_allocate (unsigned int);
static void              temp_buf_to_color (TempBuf *, TempBuf *);
Sven Neumann's avatar
Sven Neumann committed
49
static void              temp_buf_to_gray  (TempBuf *, TempBuf *);
Elliot Lee's avatar
Elliot Lee committed
50
51
52
53
54


/*  Memory management  */

static unsigned char *
Sven Neumann's avatar
Sven Neumann committed
55
temp_buf_allocate (unsigned int size)
Elliot Lee's avatar
Elliot Lee committed
56
{
Sven Neumann's avatar
Sven Neumann committed
57
  unsigned char *data;
Elliot Lee's avatar
Elliot Lee committed
58
59
60
61
62
63
64
65
66
67

  data = (unsigned char *) g_malloc (size);

  return data;
}


/*  The conversion routines  */

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


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

109
      pix = INTENSITY (*src++, *src++, *src++);
Elliot Lee's avatar
Elliot Lee committed
110
111
112
113
114
115
116

      *dest++ = (unsigned char) pix;
    }
}


TempBuf *
Sven Neumann's avatar
Sven Neumann committed
117
118
119
120
121
122
temp_buf_new (int width, 
	      int height, 
	      int bytes, 
	      int x, 
	      int y, 
	      unsigned char *col)
Elliot Lee's avatar
Elliot Lee committed
123
124
{
  long i;
Sven Neumann's avatar
Sven Neumann committed
125
  int  j;
126
  unsigned char * data;
Elliot Lee's avatar
Elliot Lee committed
127
128
129
130
131
132
133
134
135
136
137
138
  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;

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

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

  return temp;
}


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

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

219
220
221
  /* 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
222
223
224
225
226
227

  return new;
}


TempBuf *
Sven Neumann's avatar
Sven Neumann committed
228
229
230
231
232
233
temp_buf_resize (TempBuf *buf, 
		 int      bytes, 
		 int      x, 
		 int      y, 
		 int      w, 
		 int      h)
Elliot Lee's avatar
Elliot Lee committed
234
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
266
{
  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 *
Sven Neumann's avatar
Sven Neumann committed
267
268
269
270
271
272
273
temp_buf_copy_area (TempBuf *src, 
		    TempBuf *dest, 
		    int      x, 
		    int      y, 
		    int      w, 
		    int      h, 
		    int      border)
Elliot Lee's avatar
Elliot Lee committed
274
275
276
277
278
279
280
281
{
  TempBuf * new;
  PixelRegion srcR, destR;
  unsigned char empty[MAX_CHANNELS] = { 0, 0, 0, 0 };
  int x1, y1, x2, y2;

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

  /*  some bounds checking  */
287
288
289
290
  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
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305

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

  /*  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
330
temp_buf_free (TempBuf *temp_buf)
Elliot Lee's avatar
Elliot Lee committed
331
332
333
334
335
336
337
338
339
340
341
342
{
  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 *
Sven Neumann's avatar
Sven Neumann committed
343
temp_buf_data (TempBuf *temp_buf)
Elliot Lee's avatar
Elliot Lee committed
344
345
346
347
348
349
350
351
352
353
354
355
356
357
{
  if (temp_buf->swapped)
    temp_buf_unswap (temp_buf);

  return temp_buf->data;
}


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


MaskBuf *
Sven Neumann's avatar
Sven Neumann committed
358
359
mask_buf_new (int width, 
	      int height)
Elliot Lee's avatar
Elliot Lee committed
360
361
362
363
364
365
366
367
{
  static unsigned char empty = 0;

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


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


unsigned char *
Sven Neumann's avatar
Sven Neumann committed
375
mask_buf_data (MaskBuf *mask_buf)
Elliot Lee's avatar
Elliot Lee committed
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
402
{
  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
403
404
 *    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
405
406
407
408
409
 *
 *  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
410

Elliot Lee's avatar
Elliot Lee committed
411
412
413
414
415
416
417
418
419
420
421
422
/*  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 ();
423
424
  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
425
426
427
428
}


void
Sven Neumann's avatar
Sven Neumann committed
429
temp_buf_swap (TempBuf *buf)
Elliot Lee's avatar
Elliot Lee committed
430
431
432
433
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
463
{
  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)
	{
464
	  g_message ("Error in temp buf caching: \"%s\" is a directory (cannot overwrite)", filename);
Elliot Lee's avatar
Elliot Lee committed
465
466
467
468
469
470
	  g_free (filename);
	  return;
	}
    }

  /*  Open file for overwrite  */
471
  if ((fp = fopen (filename, "wb")))
Elliot Lee's avatar
Elliot Lee committed
472
    {
473
474
475
476
477
478
479
      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");
480
          g_message ("Cannot write \"%s\"", filename);
481
482
483
          g_free (filename);
          return;
        }
Elliot Lee's avatar
Elliot Lee committed
484
485
486
    }
  else
    {
487
      (void) unlink (filename);
Elliot Lee's avatar
Elliot Lee committed
488
      perror ("Error in temp buf caching");
489
      g_message ("Cannot write \"%s\"", filename);
Elliot Lee's avatar
Elliot Lee committed
490
491
492
493
494
495
496
497
498
499
500
501
      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
502
temp_buf_unswap (TempBuf *buf)
Elliot Lee's avatar
Elliot Lee committed
503
504
505
{
  struct stat stat_buf;
  FILE * fp;
506
  gboolean succ = FALSE;
Elliot Lee's avatar
Elliot Lee committed
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524

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

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

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


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

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