gimpregioniterator.c 9.61 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
/* LIBGIMP - The GIMP Library
 * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
 *
 * gimpmisc.c
 * Contains all kinds of miscellaneous routines factored out from different
 * plug-ins. They stay here until their API has crystalized a bit and we can
 * put them into the file where they belong (Maurits Rijk 
 * <lpeek.mrijk@consunet.nl> if you want to blame someone for this mess)
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

26 27
#include "config.h"

28
#include <stdio.h>
Hans Breuer's avatar
Hans Breuer committed
29 30

#include <glib.h>
31

32
#include "gimp.h"
33
#include "gimpmisc.h"
34

35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52

struct _GimpPixelFetcher
{
  gint          col, row;
  gint          img_width;
  gint	        img_height;
  gint 	        sel_x1, sel_y1, sel_x2, sel_y2;
  gint	        img_bpp;
  gint	        img_has_alpha;
  gint          tile_width, tile_height;
  guchar        bg_color[4];
  GimpDrawable *drawable;
  GimpTile     *tile;
  gboolean      tile_dirty;
  gboolean      shadow;
};


53 54 55 56 57 58 59 60 61 62 63 64 65
GimpPixelFetcher *
gimp_pixel_fetcher_new (GimpDrawable *drawable)
{
  GimpPixelFetcher *pf;

  pf = g_new (GimpPixelFetcher, 1);

  gimp_drawable_mask_bounds (drawable->drawable_id,
			     &pf->sel_x1, &pf->sel_y1, 
			     &pf->sel_x2, &pf->sel_y2);

  pf->col           = -1;
  pf->row           = -1;
66 67 68
  pf->img_width     = gimp_drawable_width     (drawable->drawable_id);
  pf->img_height    = gimp_drawable_height    (drawable->drawable_id);
  pf->img_bpp       = gimp_drawable_bpp       (drawable->drawable_id);
69 70 71 72 73 74
  pf->img_has_alpha = gimp_drawable_has_alpha (drawable->drawable_id);
  pf->tile_width    = gimp_tile_width ();
  pf->tile_height   = gimp_tile_height ();
  pf->bg_color[0]   = 0;
  pf->bg_color[1]   = 0;
  pf->bg_color[2]   = 0;
75
  pf->bg_color[3]   = 255;
76 77 78 79
  pf->drawable      = drawable;
  pf->tile          = NULL;
  pf->tile_dirty    = FALSE;
  pf->shadow	    = FALSE;
80

81 82
  /* this allows us to use (slightly faster) do-while loops */
  g_assert (pf->img_bpp > 0);
83 84 85 86 87 88 89

  return pf;
}

void
gimp_pixel_fetcher_set_bg_color (GimpPixelFetcher *pf)
{
90
  GimpRGB background;
91 92 93 94 95

  gimp_palette_get_background (&background);

  switch (pf->img_bpp)
    {
96
    case 2: pf->bg_color[1] = 255;
97 98 99 100
    case 1:
      pf->bg_color[0] = gimp_rgb_intensity_uchar (&background);
      break;

101
    case 4: pf->bg_color[3] = 255;
102 103 104 105 106 107 108
    case 3:
      gimp_rgb_get_uchar (&background,
			  pf->bg_color, pf->bg_color + 1, pf->bg_color + 2);
      break;
    }
}

109 110
void		 
gimp_pixel_fetcher_set_shadow (GimpPixelFetcher *pf,
111
			       gboolean          shadow)
112 113 114 115
{
  pf->shadow = shadow;
}

116
static guchar *
117
gimp_pixel_fetcher_provide_tile (GimpPixelFetcher *pf,
118 119
				 gint              x,
				 gint              y)
120
{
121 122
  gint col, row;
  gint coloff, rowoff;
123 124 125 126 127 128

  col    = x / pf->tile_width;
  coloff = x % pf->tile_width;
  row    = y / pf->tile_height;
  rowoff = y % pf->tile_height;

129 130 131
  if ((col != pf->col) || (row != pf->row) || (pf->tile == NULL))
    {
      if (pf->tile != NULL)
132
	gimp_tile_unref (pf->tile, pf->tile_dirty);
133

134 135
      pf->tile = gimp_drawable_get_tile (pf->drawable, pf->shadow, row, col);
      pf->tile_dirty = FALSE;
136 137 138 139 140
      gimp_tile_ref (pf->tile);

      pf->col = col;
      pf->row = row;
    }
141 142 143 144 145 146
  
  return pf->tile->data + pf->img_bpp * (pf->tile->ewidth * rowoff + coloff);
}

void
gimp_pixel_fetcher_get_pixel (GimpPixelFetcher *pf,
147 148 149
			      gint              x,
			      gint              y,
			      guchar           *pixel)
150 151 152 153 154 155 156 157 158
{
  guchar *p;
  gint    i;

  if (x < pf->sel_x1 || x >= pf->sel_x2 ||
      y < pf->sel_y1 || y >= pf->sel_y2)
    {
      return;
    }
159

160
  p = gimp_pixel_fetcher_provide_tile (pf, x, y);
161

162 163
  i = pf->img_bpp;
  do
164
    *pixel++ = *p++;
165
  while (--i);
166 167
}

168 169
void
gimp_pixel_fetcher_put_pixel (GimpPixelFetcher *pf,
170 171 172
			      gint              x,
			      gint              y,
			      guchar           *pixel)
173 174 175 176 177 178 179 180 181 182 183 184
{
  guchar *p;
  gint    i;

  if (x < pf->sel_x1 || x >= pf->sel_x2 ||
      y < pf->sel_y1 || y >= pf->sel_y2)
    {
      return;
    }

  p = gimp_pixel_fetcher_provide_tile (pf, x, y);

185 186
  i = pf->img_bpp;
  do
187
    *p++ = *pixel++;
188
  while (--i);
189 190 191 192

  pf->tile_dirty = TRUE;
}

193 194
void
gimp_pixel_fetcher_get_pixel2 (GimpPixelFetcher *pf,
195 196 197 198
			       gint              x,
			       gint              y,
			       gint		 wrapmode,
			       guchar           *pixel)
199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220
{
  guchar *p;
  gint    i;

  if (x < 0 || x >= pf->img_width ||
      y < 0 || y >= pf->img_height)
    switch (wrapmode)
      {
      case PIXEL_WRAP:
	if (x < 0 || x >= pf->img_width)
	  {
	    x %= pf->img_width;
	    if (x < 0)
	      x += pf->img_width;
	  }
	if (y < 0 || y >= pf->img_height)
	  {
	    y %= pf->img_height;
	    if (y < 0)
	      y += pf->img_height;
	  }
	break;
221

222
      case PIXEL_SMEAR:
223 224
	x = CLAMP (x, 0, pf->img_width - 1);
	y = CLAMP (y, 0, pf->img_height - 1);
225
	break;
226

227 228 229 230
      case PIXEL_BLACK:
	if (x < 0 || x >= pf->img_width || 
	    y < 0 || y >= pf->img_height)
	  {
231 232
	    i = pf->img_bpp;
	    do
233
	      pixel[i] = 0;
234 235
	    while (--i);

236 237 238
	    return;
	  }
	break;
239

240 241 242 243
      default:
	return;
      }

244
  p = gimp_pixel_fetcher_provide_tile (pf, x, y);
245

246 247
  i = pf->img_bpp;
  do
248
    *pixel++ = *p++;
249
  while (--i);
250 251 252 253 254
}

void
gimp_pixel_fetcher_destroy (GimpPixelFetcher *pf)
{
255
  if (pf->tile)
256
    gimp_tile_unref (pf->tile, pf->tile_dirty);
257 258 259

  g_free (pf);
}
260 261 262

void		 
gimp_get_bg_guchar (GimpDrawable *drawable,
263 264
		    gboolean      transparent,
		    guchar       *bg)
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
{
  GimpRGB  background;

  gimp_palette_get_background (&background);

  switch (gimp_drawable_type (drawable->drawable_id))
    {
    case GIMP_RGB_IMAGE :
      gimp_rgb_get_uchar (&background, &bg[0], &bg[1], &bg[2]);
      bg[3] = 255;
      break;

    case GIMP_RGBA_IMAGE:
      gimp_rgb_get_uchar (&background, &bg[0], &bg[1], &bg[2]);
      bg[3] = transparent ? 0 : 255;
      break;

    case GIMP_GRAY_IMAGE:
      bg[0] = gimp_rgb_intensity_uchar (&background);
      bg[1] = 255;
      break;

    case GIMP_GRAYA_IMAGE:
      bg[0] = gimp_rgb_intensity_uchar (&background);
      bg[1] = transparent ? 0 : 255;
      break;

    default:
      break;
    }
}
296 297

static void
298 299 300 301 302 303
gimp_rgn_render_row (guchar       *src,
		     guchar       *dest,
		     gint          col,    /* row width in pixels */
		     gint 	   bpp,
		     GimpRgnFunc2  func,
		     gpointer      data)
304 305 306 307 308 309 310 311 312 313 314 315
{
  while (col--)
    {
      func (src, dest, bpp, data);
      src += bpp;
      dest += bpp;
    }
}

static void
gimp_rgn_render_region (const GimpPixelRgn *srcPR,
		        const GimpPixelRgn *destPR,
316 317
		        GimpRgnFunc2        func,
			gpointer            data)
318
{
319
  gint    row;
320 321 322 323 324 325 326 327 328 329 330 331 332
  guchar* src  = srcPR->data;
  guchar* dest = destPR->data;
  
  for (row = 0; row < srcPR->h; row++)
    {
      gimp_rgn_render_row (src, dest, srcPR->w, srcPR->bpp, func, data);

      src  += srcPR->rowstride;
      dest += destPR->rowstride;
    }
}

void
333 334 335 336
gimp_rgn_iterate1 (GimpDrawable *drawable,
		   GimpRunMode   run_mode,
		   GimpRgnFunc1  func,
		   gpointer      data)
337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358
{
  GimpPixelRgn srcPR;
  gint      x1, y1, x2, y2;
  gpointer  pr;
  gint      total_area, area_so_far;
  gint      progress_skip;

  gimp_drawable_mask_bounds (drawable->drawable_id, &x1, &y1, &x2, &y2);

  total_area = (x2 - x1) * (y2 - y1);

  area_so_far   = 0;
  progress_skip = 0;

  gimp_pixel_rgn_init (&srcPR, drawable,
		       x1, y1, (x2 - x1), (y2 - y1), FALSE, FALSE);

  for (pr = gimp_pixel_rgns_register (1, &srcPR);
       pr != NULL;
       pr = gimp_pixel_rgns_process (pr))
    {
      guchar *src = srcPR.data;
359
      gint    y;
360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378

      for (y = 0; y < srcPR.h; y++)
	{
	  guchar *s = src;
	  gint x;

	  for (x = 0; x < srcPR.w; x++)
	    {
              func (s, srcPR.bpp, data);
	      s += srcPR.bpp;
	    }

	  src += srcPR.rowstride;
	}

      if (run_mode != GIMP_RUN_NONINTERACTIVE)
	{
	  area_so_far += srcPR.w * srcPR.h;
	  if (((progress_skip++) % 10) == 0)
379 380
	    gimp_progress_update ((gdouble) area_so_far /
				  (gdouble) total_area);
381 382 383 384 385
	}
    }
}

void
386 387 388 389
gimp_rgn_iterate2 (GimpDrawable *drawable,
		   GimpRunMode   run_mode, 
		   GimpRgnFunc2  func,
		   gpointer      data)
390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419
{
  GimpPixelRgn srcPR, destPR;
  gint      x1, y1, x2, y2;
  gpointer  pr;
  gint      total_area, area_so_far;
  gint      progress_skip;

  gimp_drawable_mask_bounds (drawable->drawable_id, &x1, &y1, &x2, &y2);

  total_area = (x2 - x1) * (y2 - y1);

  area_so_far   = 0;
  progress_skip = 0;

  /* Initialize the pixel regions. */
  gimp_pixel_rgn_init (&srcPR, drawable, x1, y1, (x2 - x1), (y2 - y1),
		       FALSE, FALSE);
  gimp_pixel_rgn_init (&destPR, drawable, x1, y1, (x2 - x1), (y2 - y1),
		       TRUE, TRUE);
  
  for (pr = gimp_pixel_rgns_register (2, &srcPR, &destPR);
       pr != NULL;
       pr = gimp_pixel_rgns_process (pr))
    {
      gimp_rgn_render_region (&srcPR, &destPR, func, data);

      if (run_mode != GIMP_RUN_NONINTERACTIVE)
	{
	  area_so_far += srcPR.w * srcPR.h;
	  if (((progress_skip++) % 10) == 0)
420 421
	    gimp_progress_update ((gdouble) area_so_far /
				  (gdouble) total_area);
422 423 424 425 426 427 428 429
	}
    }

  /*  update the processed region  */
  gimp_drawable_flush (drawable);
  gimp_drawable_merge_shadow (drawable->drawable_id, TRUE);
  gimp_drawable_update (drawable->drawable_id, x1, y1, (x2 - x1), (y2 - y1));
}