gimptile.c 9.47 KB
Newer Older
1 2 3 4
/* LIBGIMP - The GIMP Library
 * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
 *
 * gimptile.c
Manish Singh's avatar
Manish Singh committed
5 6
 *
 * This library is free software; you can redistribute it and/or
Marc Lehmann's avatar
Marc Lehmann committed
7
 * modify it under the terms of the GNU Lesser General Public
Manish Singh's avatar
Manish Singh committed
8
 * License as published by the Free Software Foundation; either
9 10 11 12 13
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
Manish Singh's avatar
Manish Singh committed
14 15
 * Library General Public License for more details.
 *
Marc Lehmann's avatar
Marc Lehmann committed
16
 * You should have received a copy of the GNU Lesser General Public
17 18 19
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
20 21
 */

Sven Neumann's avatar
Sven Neumann committed
22 23
#include "config.h"

Elliot Lee's avatar
Elliot Lee committed
24
#include <string.h>
25

26 27 28 29 30 31
#include <glib.h>

#include "libgimpbase/gimpbase.h"
#include "libgimpbase/gimpprotocol.h"
#include "libgimpbase/gimpwire.h"

Elliot Lee's avatar
Elliot Lee committed
32 33 34
#include "gimp.h"


35 36 37
/*  This is the percentage of the maximum cache size that 
 *  should be cleared from the cache when an eviction is 
 *  necessary.
Elliot Lee's avatar
Elliot Lee committed
38 39 40
 */
#define FREE_QUANTUM 0.1

41 42
void         gimp_read_expect_msg   (WireMessage *msg,
				     gint         type);
Elliot Lee's avatar
Elliot Lee committed
43

44 45 46 47
static void  gimp_tile_get          (GimpTile    *tile);
static void  gimp_tile_put          (GimpTile    *tile);
static void  gimp_tile_cache_insert (GimpTile    *tile);
static void  gimp_tile_cache_flush  (GimpTile    *tile);
Elliot Lee's avatar
Elliot Lee committed
48
static void  gimp_tile_cache_zorch  (void);
49
static guint gimp_tile_hash         (GimpTile    *tile);
Elliot Lee's avatar
Elliot Lee committed
50 51


52
gint _gimp_tile_width  = -1;
Elliot Lee's avatar
Elliot Lee committed
53 54 55
gint _gimp_tile_height = -1;

static GHashTable *tile_hash_table = NULL;
56 57 58 59 60
static GList      *tile_list_head  = NULL;
static GList      *tile_list_tail  = NULL;
static gulong      max_tile_size   = 0;
static gulong      cur_cache_size  = 0;
static gulong      max_cache_size  = 0;
Elliot Lee's avatar
Elliot Lee committed
61 62 63


void
64
gimp_tile_ref (GimpTile *tile)
Elliot Lee's avatar
Elliot Lee committed
65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
{
  if (tile)
    {
      tile->ref_count += 1;

      if (tile->ref_count == 1)
	{
	  gimp_tile_get (tile);
	  tile->dirty = FALSE;
	}

      gimp_tile_cache_insert (tile);
    }
}

void
81
gimp_tile_ref_zero (GimpTile *tile)
Elliot Lee's avatar
Elliot Lee committed
82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
{
  if (tile)
    {
      tile->ref_count += 1;

      if (tile->ref_count == 1)
	{
	  tile->data = g_new (guchar, tile->ewidth * tile->eheight * tile->bpp);
	  memset (tile->data, 0, tile->ewidth * tile->eheight * tile->bpp);
	}

      gimp_tile_cache_insert (tile);
    }
}

void
98 99
gimp_tile_unref (GimpTile *tile,
		 gboolean  dirty)
Elliot Lee's avatar
Elliot Lee committed
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
{
  if (tile)
    {
      tile->ref_count -= 1;
      tile->dirty |= dirty;

      if (tile->ref_count == 0)
	{
	  gimp_tile_flush (tile);
	  g_free (tile->data);
	  tile->data = NULL;
	}
    }
}

void
116
gimp_tile_flush (GimpTile *tile)
Elliot Lee's avatar
Elliot Lee committed
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
{
  if (tile && tile->data && tile->dirty)
    {
      gimp_tile_put (tile);
      tile->dirty = FALSE;
    }
}

void
gimp_tile_cache_size (gulong kilobytes)
{
  max_cache_size = kilobytes * 1024;
}

void
gimp_tile_cache_ntiles (gulong ntiles)
{
Marc Lehmann's avatar
Marc Lehmann committed
134
  gimp_tile_cache_size ((gulong)(ntiles * _gimp_tile_width * _gimp_tile_height * 4 + 1023) / 1024);
Elliot Lee's avatar
Elliot Lee committed
135 136 137
}

guint
138
gimp_tile_width (void)
Elliot Lee's avatar
Elliot Lee committed
139 140 141 142 143
{
  return _gimp_tile_width;
}

guint
144
gimp_tile_height (void)
Elliot Lee's avatar
Elliot Lee committed
145 146 147 148 149 150
{
  return _gimp_tile_height;
}


static void
151
gimp_tile_get (GimpTile *tile)
Elliot Lee's avatar
Elliot Lee committed
152
{
153
  extern GIOChannel *_writechannel;
Elliot Lee's avatar
Elliot Lee committed
154 155 156 157 158 159
  extern guchar* _shm_addr;

  GPTileReq tile_req;
  GPTileData *tile_data;
  WireMessage msg;

160
  tile_req.drawable_ID = tile->drawable->drawable_id;
Elliot Lee's avatar
Elliot Lee committed
161 162
  tile_req.tile_num = tile->tile_num;
  tile_req.shadow = tile->shadow;
163
  if (! gp_tile_req_write (_writechannel, &tile_req, NULL))
Elliot Lee's avatar
Elliot Lee committed
164 165
    gimp_quit ();

166
  gimp_read_expect_msg (&msg, GP_TILE_DATA);
Elliot Lee's avatar
Elliot Lee committed
167 168

  tile_data = msg.data;
169
  if ((tile_data->drawable_ID != tile->drawable->drawable_id) ||
Elliot Lee's avatar
Elliot Lee committed
170 171 172 173 174 175
      (tile_data->tile_num != tile->tile_num) ||
      (tile_data->shadow != tile->shadow) ||
      (tile_data->width != tile->ewidth) ||
      (tile_data->height != tile->eheight) ||
      (tile_data->bpp != tile->bpp))
    {
Manish Singh's avatar
Manish Singh committed
176
      g_message ("received tile info did not match computed tile info\n");
Elliot Lee's avatar
Elliot Lee committed
177 178 179 180 181 182 183 184 185 186 187 188 189 190
      gimp_quit ();
    }

  if (tile_data->use_shm)
    {
      tile->data = g_new (guchar, tile->ewidth * tile->eheight * tile->bpp);
      memcpy (tile->data, _shm_addr, tile->ewidth * tile->eheight * tile->bpp);
    }
  else
    {
      tile->data = tile_data->data;
      tile_data->data = NULL;
    }

191
  if (! gp_tile_ack_write (_writechannel, NULL))
Elliot Lee's avatar
Elliot Lee committed
192
    gimp_quit ();
Manish Singh's avatar
Manish Singh committed
193 194

  wire_destroy (&msg);
Elliot Lee's avatar
Elliot Lee committed
195 196 197
}

static void
198
gimp_tile_put (GimpTile *tile)
Elliot Lee's avatar
Elliot Lee committed
199
{
200
  extern GIOChannel *_writechannel;
201
  extern guchar *_shm_addr;
Elliot Lee's avatar
Elliot Lee committed
202 203 204 205 206 207 208 209 210

  GPTileReq tile_req;
  GPTileData tile_data;
  GPTileData *tile_info;
  WireMessage msg;

  tile_req.drawable_ID = -1;
  tile_req.tile_num = 0;
  tile_req.shadow = 0;
211
  if (! gp_tile_req_write (_writechannel, &tile_req, NULL))
Elliot Lee's avatar
Elliot Lee committed
212 213
    gimp_quit ();

214
  gimp_read_expect_msg (&msg, GP_TILE_DATA);
Elliot Lee's avatar
Elliot Lee committed
215 216 217

  tile_info = msg.data;

218
  tile_data.drawable_ID = tile->drawable->drawable_id;
219 220 221 222 223 224 225
  tile_data.tile_num    = tile->tile_num;
  tile_data.shadow      = tile->shadow;
  tile_data.bpp         = tile->bpp;
  tile_data.width       = tile->ewidth;
  tile_data.height      = tile->eheight;
  tile_data.use_shm     = tile_info->use_shm;
  tile_data.data        = NULL;
Elliot Lee's avatar
Elliot Lee committed
226 227 228 229 230 231

  if (tile_info->use_shm)
    memcpy (_shm_addr, tile->data, tile->ewidth * tile->eheight * tile->bpp);
  else
    tile_data.data = tile->data;

232
  if (! gp_tile_data_write (_writechannel, &tile_data, NULL))
Elliot Lee's avatar
Elliot Lee committed
233 234
    gimp_quit ();

235
  gimp_read_expect_msg (&msg, GP_TILE_ACK);
Manish Singh's avatar
Manish Singh committed
236 237

  wire_destroy (&msg);
Elliot Lee's avatar
Elliot Lee committed
238 239 240 241 242 243
}

/* This function is nearly identical to the function 'tile_cache_insert'
 *  in the file 'tile_cache.c' which is part of the main gimp application.
 */
static void
244
gimp_tile_cache_insert (GimpTile *tile)
Elliot Lee's avatar
Elliot Lee committed
245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334
{
  GList *tmp;

  if (!tile_hash_table)
    {
      tile_hash_table = g_hash_table_new ((GHashFunc) gimp_tile_hash, NULL);
      max_tile_size = gimp_tile_width () * gimp_tile_height () * 4;
    }

  /* First check and see if the tile is already
   *  in the cache. In that case we will simply place
   *  it at the end of the tile list to indicate that
   *  it was the most recently accessed tile.
   */
  tmp = g_hash_table_lookup (tile_hash_table, tile);

  if (tmp)
    {
      /* The tile was already in the cache. Place it at
       *  the end of the tile list.
       */
      if (tmp == tile_list_tail)
	tile_list_tail = tile_list_tail->prev;
      tile_list_head = g_list_remove_link (tile_list_head, tmp);
      if (!tile_list_head)
	tile_list_tail = NULL;
      g_list_free (tmp);

      /* Remove the old reference to the tiles list node
       *  in the tile hash table.
       */
      g_hash_table_remove (tile_hash_table, tile);

      tile_list_tail = g_list_append (tile_list_tail, tile);
      if (!tile_list_head)
	tile_list_head = tile_list_tail;
      tile_list_tail = g_list_last (tile_list_tail);

      /* Add the tiles list node to the tile hash table. The
       *  list node is indexed by the tile itself. This makes
       *  for a quick lookup of which list node the tile is in.
       */
      g_hash_table_insert (tile_hash_table, tile, tile_list_tail);
    }
  else
    {
      /* The tile was not in the cache. First check and see
       *  if there is room in the cache. If not then we'll have
       *  to make room first. Note: it might be the case that the
       *  cache is smaller than the size of a tile in which case
       *  it won't be possible to put it in the cache.
       */
      if ((cur_cache_size + max_tile_size) > max_cache_size)
	{
	  while (tile_list_head && (cur_cache_size + max_cache_size * FREE_QUANTUM) > max_cache_size)
	    gimp_tile_cache_zorch ();

	  if ((cur_cache_size + max_tile_size) > max_cache_size)
	    return;
	}

      /* Place the tile at the end of the tile list.
       */
      tile_list_tail = g_list_append (tile_list_tail, tile);
      if (!tile_list_head)
	tile_list_head = tile_list_tail;
      tile_list_tail = g_list_last (tile_list_tail);

      /* Add the tiles list node to the tile hash table.
       */
      g_hash_table_insert (tile_hash_table, tile, tile_list_tail);

      /* Note the increase in the number of bytes the cache
       *  is referencing.
       */
      cur_cache_size += max_tile_size;

      /* Reference the tile so that it won't be returned to
       *  the main gimp application immediately.
       */
      tile->ref_count += 1;
      if (tile->ref_count == 1)
	{
	  gimp_tile_get (tile);
	  tile->dirty = FALSE;
	}
    }
}

static void
335
gimp_tile_cache_flush (GimpTile *tile)
Elliot Lee's avatar
Elliot Lee committed
336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376
{
  GList *tmp;

  if (!tile_hash_table)
    {
      tile_hash_table = g_hash_table_new ((GHashFunc) gimp_tile_hash, NULL);
      max_tile_size = gimp_tile_width () * gimp_tile_height () * 4;
    }

  /* Find where the tile is in the cache.
   */
  tmp = g_hash_table_lookup (tile_hash_table, tile);

  if (tmp)
    {
      /* If the tile is in the cache, then remove it from the
       *  tile list.
       */
      if (tmp == tile_list_tail)
        tile_list_tail = tile_list_tail->prev;
      tile_list_head = g_list_remove_link (tile_list_head, tmp);
      if (!tile_list_head)
        tile_list_tail = NULL;
      g_list_free (tmp);

      /* Remove the tile from the tile hash table.
       */
      g_hash_table_remove (tile_hash_table, tile);

      /* Note the decrease in the number of bytes the cache
       *  is referencing.
       */
      cur_cache_size -= max_tile_size;

      /* Unreference the tile.
       */
      gimp_tile_unref (tile, FALSE);
    }
}

static void
377
gimp_tile_cache_zorch (void)
Elliot Lee's avatar
Elliot Lee committed
378 379
{
  if (tile_list_head)
380
    gimp_tile_cache_flush ((GimpTile*) tile_list_head->data);
Elliot Lee's avatar
Elliot Lee committed
381 382 383
}

static guint
384
gimp_tile_hash (GimpTile *tile)
Elliot Lee's avatar
Elliot Lee committed
385 386 387
{
  return (gulong) tile;
}