gimppixelfetcher.c 15.7 KB
Newer Older
1 2 3 4 5 6
/* 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
7
 * put them into the file where they belong (Maurits Rijk
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
 * <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

struct _GimpPixelFetcher
{
38 39 40 41 42 43 44 45 46 47 48 49
  gint                      col, row;
  gint                      img_width;
  gint                      img_height;
  gint                      sel_x1, sel_y1, sel_x2, sel_y2;
  gint                      img_bpp;
  gint                      tile_width, tile_height;
  guchar                    bg_color[4];
  GimpPixelFetcherEdgeMode  mode;
  GimpDrawable             *drawable;
  GimpTile                 *tile;
  gboolean                  tile_dirty;
  gboolean                  shadow;
50 51
};

52
struct _GimpRgnIterator
53 54
{
  GimpDrawable *drawable;
55
  gint                 x1, y1, x2, y2;
56 57
  GimpRunMode   run_mode;
};
58

59 60 61 62 63 64 65 66
GimpPixelFetcher *
gimp_pixel_fetcher_new (GimpDrawable *drawable)
{
  GimpPixelFetcher *pf;

  pf = g_new (GimpPixelFetcher, 1);

  gimp_drawable_mask_bounds (drawable->drawable_id,
67 68
                             &pf->sel_x1, &pf->sel_y1,
                             &pf->sel_x2, &pf->sel_y2);
69 70 71

  pf->col           = -1;
  pf->row           = -1;
72 73 74
  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);
75 76 77 78 79
  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;
80
  pf->bg_color[3]   = 255;
81
  pf->mode            = GIMP_PIXEL_FETCHER_EDGE_NONE;
82 83 84
  pf->drawable      = drawable;
  pf->tile          = NULL;
  pf->tile_dirty    = FALSE;
85
  pf->shadow            = FALSE;
86

87 88
  /* this allows us to use (slightly faster) do-while loops */
  g_assert (pf->img_bpp > 0);
89 90 91 92

  return pf;
}

93 94 95 96 97 98 99
void
gimp_pixel_fetcher_set_edge_mode (GimpPixelFetcher         *pf,
                                  GimpPixelFetcherEdgeMode  mode)
{
  pf->mode = mode;
}

100 101 102
void
gimp_pixel_fetcher_set_bg_color (GimpPixelFetcher *pf)
{
103
  GimpRGB background;
104 105 106 107 108

  gimp_palette_get_background (&background);

  switch (pf->img_bpp)
    {
109
    case 2: pf->bg_color[1] = 255;
110 111 112 113
    case 1:
      pf->bg_color[0] = gimp_rgb_intensity_uchar (&background);
      break;

114
    case 4: pf->bg_color[3] = 255;
115 116
    case 3:
      gimp_rgb_get_uchar (&background,
117
                          pf->bg_color, pf->bg_color + 1, pf->bg_color + 2);
118 119 120 121
      break;
    }
}

122
void
123
gimp_pixel_fetcher_set_shadow (GimpPixelFetcher *pf,
124
                               gboolean          shadow)
125 126 127 128
{
  pf->shadow = shadow;
}

129
static guchar *
130
gimp_pixel_fetcher_provide_tile (GimpPixelFetcher *pf,
131 132
                                 gint              x,
                                 gint              y)
133
{
134 135
  gint col, row;
  gint coloff, rowoff;
136 137 138 139 140 141

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

142 143 144
  if ((col != pf->col) || (row != pf->row) || (pf->tile == NULL))
    {
      if (pf->tile != NULL)
145
        gimp_tile_unref (pf->tile, pf->tile_dirty);
146

147 148
      pf->tile = gimp_drawable_get_tile (pf->drawable, pf->shadow, row, col);
      pf->tile_dirty = FALSE;
149 150 151 152 153
      gimp_tile_ref (pf->tile);

      pf->col = col;
      pf->row = row;
    }
154

155 156 157 158 159
  return pf->tile->data + pf->img_bpp * (pf->tile->ewidth * rowoff + coloff);
}

void
gimp_pixel_fetcher_put_pixel (GimpPixelFetcher *pf,
160 161 162
                              gint              x,
                              gint              y,
                              const guchar     *pixel)
163 164 165 166 167 168 169 170 171 172 173 174
{
  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);

175 176
  i = pf->img_bpp;
  do
177
    *p++ = *pixel++;
178
  while (--i);
179 180 181 182

  pf->tile_dirty = TRUE;
}

183
void
184 185 186 187
gimp_pixel_fetcher_get_pixel (GimpPixelFetcher *pf,
                              gint              x,
                              gint              y,
                              guchar           *pixel)
188 189 190 191
{
  guchar *p;
  gint    i;

192 193 194 195 196 197 198 199 200
  if (pf->mode == GIMP_PIXEL_FETCHER_EDGE_NONE &&
      (x < pf->sel_x1 || x >= pf->sel_x2 ||
       y < pf->sel_y1 || y >= pf->sel_y2))
    {
      return;
    }
  else if (x < 0 || x >= pf->img_width ||
           y < 0 || y >= pf->img_height)
    switch (pf->mode)
201
      {
202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233
      case GIMP_PIXEL_FETCHER_EDGE_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;

      case GIMP_PIXEL_FETCHER_EDGE_SMEAR:
        x = CLAMP (x, 0, pf->img_width - 1);
        y = CLAMP (y, 0, pf->img_height - 1);
        break;

      case GIMP_PIXEL_FETCHER_EDGE_BLACK:
        if (x < 0 || x >= pf->img_width ||
            y < 0 || y >= pf->img_height)
          {
            i = pf->img_bpp;
            do
              pixel[i] = 0;
            while (--i);

            return;
          }
        break;
234

235
      default:
236
        return;
237 238
      }

239
  p = gimp_pixel_fetcher_provide_tile (pf, x, y);
240

241 242
  i = pf->img_bpp;
  do
243
    *pixel++ = *p++;
244
  while (--i);
245 246 247 248 249
}

void
gimp_pixel_fetcher_destroy (GimpPixelFetcher *pf)
{
250
  if (pf->tile)
251
    gimp_tile_unref (pf->tile, pf->tile_dirty);
252 253 254

  g_free (pf);
}
255

256 257
static void
gimp_get_color_guchar (GimpDrawable *drawable,
258 259 260
                       GimpRGB            *color,
                       gboolean      transparent,
                       guchar       *bg)
261 262 263 264
{
  switch (gimp_drawable_type (drawable->drawable_id))
    {
    case GIMP_RGB_IMAGE :
265
      gimp_rgb_get_uchar (color, &bg[0], &bg[1], &bg[2]);
266 267 268 269
      bg[3] = 255;
      break;

    case GIMP_RGBA_IMAGE:
270
      gimp_rgb_get_uchar (color, &bg[0], &bg[1], &bg[2]);
271 272 273 274
      bg[3] = transparent ? 0 : 255;
      break;

    case GIMP_GRAY_IMAGE:
275
      bg[0] = gimp_rgb_intensity_uchar (color);
276 277 278 279
      bg[1] = 255;
      break;

    case GIMP_GRAYA_IMAGE:
280
      bg[0] = gimp_rgb_intensity_uchar (color);
281 282 283 284 285 286 287
      bg[1] = transparent ? 0 : 255;
      break;

    default:
      break;
    }
}
288

289
void
290
gimp_get_bg_guchar (GimpDrawable *drawable,
291 292
                    gboolean      transparent,
                    guchar       *bg)
293 294 295 296 297 298 299
{
  GimpRGB  background;

  gimp_palette_get_background (&background);
  gimp_get_color_guchar (drawable, &background, transparent, bg);
}

300
void
301
gimp_get_fg_guchar (GimpDrawable *drawable,
302 303
                    gboolean      transparent,
                    guchar       *fg)
304 305 306 307 308 309 310 311
{
  GimpRGB  foreground;

  gimp_palette_get_foreground (&foreground);
  gimp_get_color_guchar (drawable, &foreground, transparent, fg);
}

GimpRgnIterator*
312 313
gimp_rgn_iterator_new (GimpDrawable *drawable,
                       GimpRunMode   run_mode)
314 315
{
  GimpRgnIterator *iter = g_new (GimpRgnIterator, 1);
316

317 318
  iter->drawable = drawable;
  iter->run_mode = run_mode;
319 320 321
  gimp_drawable_mask_bounds (drawable->drawable_id,
                             &iter->x1, &iter->y1,
                             &iter->x2, &iter->y2);
322 323 324 325 326 327 328 329 330 331 332

  return iter;
}

void
gimp_rgn_iterator_free (GimpRgnIterator *iter)
{
  g_free (iter);
}

static void
333 334 335 336
gimp_rgn_iterator_iter_single (GimpRgnIterator *iter,
                               GimpPixelRgn    *srcPR,
                               GimpRgnFuncSrc   func,
                               gpointer         data)
337 338
{
  gpointer  pr;
339 340
  gint      total_area;
  gint      area_so_far;
341 342 343

  total_area = (iter->x2 - iter->x1) * (iter->y2 - iter->y1);
  area_so_far   = 0;
344

345 346 347 348 349 350
  for (pr = gimp_pixel_rgns_register (1, srcPR);
       pr != NULL;
       pr = gimp_pixel_rgns_process (pr))
    {
      guchar *src = srcPR->data;
      gint    y;
351

352
      for (y = srcPR->y; y < srcPR->y + srcPR->h; y++)
353 354 355
        {
          guchar *s = src;
          gint x;
356

357 358
          for (x = srcPR->x; x < srcPR->x + srcPR->w; x++)
            {
359
              func (x, y, s, srcPR->bpp, data);
360 361
              s += srcPR->bpp;
            }
362

363 364
          src += srcPR->rowstride;
        }
365

366
      if (iter->run_mode != GIMP_RUN_NONINTERACTIVE)
367 368 369 370
        {
          area_so_far += srcPR->w * srcPR->h;
          gimp_progress_update ((gdouble) area_so_far /        (gdouble) total_area);
        }
371 372 373 374
    }
}

void
375
gimp_rgn_iterator_src (GimpRgnIterator *iter,
376 377
                       GimpRgnFuncSrc   func,
                       gpointer         data)
378 379
{
  GimpPixelRgn srcPR;
380

381 382 383 384
  gimp_pixel_rgn_init (&srcPR, iter->drawable,
                       iter->x1, iter->y1,
                       iter->x2 - iter->x1, iter->y2 - iter->y1,
                       FALSE, FALSE);
385 386 387
  gimp_rgn_iterator_iter_single (iter, &srcPR, func, data);
}

388
void
389 390 391
gimp_rgn_iterator_src_dest (GimpRgnIterator    *iter,
                            GimpRgnFuncSrcDest  func,
                            gpointer            data)
392
{
393 394 395 396 397 398
  GimpPixelRgn  srcPR, destPR;
  gint          x1, y1, x2, y2;
  gint                bpp;
  gpointer      pr;
  gint          total_area;
  gint          area_so_far;
399 400 401 402 403 404 405 406 407 408

  x1 = iter->x1;
  y1 = iter->y1;
  x2 = iter->x2;
  y2 = iter->y2;

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

  gimp_pixel_rgn_init (&srcPR, iter->drawable, x1, y1, x2 - x1, y2 - y1,
409
                       FALSE, FALSE);
410
  gimp_pixel_rgn_init (&destPR, iter->drawable, x1, y1, x2 - x1, y2 - y1,
411
                       TRUE, TRUE);
412 413

  bpp = srcPR.bpp;
414

415 416 417 418 419 420 421
  for (pr = gimp_pixel_rgns_register (2, &srcPR, &destPR);
       pr != NULL;
       pr = gimp_pixel_rgns_process (pr))
    {
      gint    y;
      guchar* src  = srcPR.data;
      guchar* dest = destPR.data;
422

423
      for (y = srcPR.y; y < srcPR.y + srcPR.h; y++)
424 425 426 427 428 429 430 431 432 433 434 435 436 437 438
        {
          gint x;
          guchar *s = src;
          guchar *d = dest;

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

          src  += srcPR.rowstride;
          dest += destPR.rowstride;
        }
439 440

      if (iter->run_mode != GIMP_RUN_NONINTERACTIVE)
441 442 443 444 445
        {
          area_so_far += srcPR.w * srcPR.h;
          gimp_progress_update ((gdouble) area_so_far /
                                (gdouble) total_area);
        }
446 447 448 449
    }

  gimp_drawable_flush (iter->drawable);
  gimp_drawable_merge_shadow (iter->drawable->drawable_id, TRUE);
450 451
  gimp_drawable_update (iter->drawable->drawable_id,
                        x1, y1, x2 - x1, y2 - y1);
452 453
}

454
void
455
gimp_rgn_iterator_dest (GimpRgnIterator *iter,
456 457
                        GimpRgnFuncDest  func,
                        gpointer         data)
458 459
{
  GimpPixelRgn destPR;
460

461 462 463 464
  gimp_pixel_rgn_init (&destPR, iter->drawable,
                       iter->x1, iter->y1,
                       iter->x2 - iter->x1, iter->y2 - iter->y1,
                       TRUE, TRUE);
465
  gimp_rgn_iterator_iter_single (iter, &destPR, (GimpRgnFuncSrc) func, data);
466

467 468 469
  /*  update the processed region  */
  gimp_drawable_flush (iter->drawable);
  gimp_drawable_merge_shadow (iter->drawable->drawable_id, TRUE);
470 471 472
  gimp_drawable_update (iter->drawable->drawable_id,
                        iter->x1, iter->y1,
                        iter->x2 - iter->x1, iter->y2 - iter->y1);
473 474
}

475
static void
476
gimp_rgn_render_row (guchar       *src,
477 478 479 480 481
                     guchar       *dest,
                     gint          col,    /* row width in pixels */
                     gint            bpp,
                     GimpRgnFunc2  func,
                     gpointer      data)
482 483 484 485 486 487 488 489 490 491 492
{
  while (col--)
    {
      func (src, dest, bpp, data);
      src += bpp;
      dest += bpp;
    }
}

static void
gimp_rgn_render_region (const GimpPixelRgn *srcPR,
493 494 495
                        const GimpPixelRgn *destPR,
                        GimpRgnFunc2        func,
                        gpointer            data)
496
{
497
  gint    row;
498 499
  guchar *src  = srcPR->data;
  guchar *dest = destPR->data;
500

501 502 503 504 505 506 507 508 509 510
  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
511
gimp_rgn_iterate1 (GimpDrawable *drawable,
512 513 514
                   GimpRunMode   run_mode,
                   GimpRgnFunc1  func,
                   gpointer      data)
515
{
516 517 518 519 520 521
  GimpPixelRgn  srcPR;
  gint          x1, y1, x2, y2;
  gpointer      pr;
  gint          total_area;
  gint          area_so_far;
  gint          progress_skip;
522 523 524 525 526 527 528 529 530

  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,
531
                       x1, y1, (x2 - x1), (y2 - y1), FALSE, FALSE);
532 533 534 535 536 537

  for (pr = gimp_pixel_rgns_register (1, &srcPR);
       pr != NULL;
       pr = gimp_pixel_rgns_process (pr))
    {
      guchar *src = srcPR.data;
538
      gint    y;
539 540

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

545 546
          for (x = 0; x < srcPR.w; x++)
            {
547
              func (s, srcPR.bpp, data);
548 549
              s += srcPR.bpp;
            }
550

551 552
          src += srcPR.rowstride;
        }
553 554

      if (run_mode != GIMP_RUN_NONINTERACTIVE)
555 556 557 558 559 560 561
        {
          area_so_far += srcPR.w * srcPR.h;

          if (((progress_skip++) % 10) == 0)
            gimp_progress_update ((gdouble) area_so_far /
                                  (gdouble) total_area);
        }
562 563 564 565
    }
}

void
566
gimp_rgn_iterate2 (GimpDrawable *drawable,
567 568 569
                   GimpRunMode   run_mode,
                   GimpRgnFunc2  func,
                   gpointer      data)
570
{
571 572 573 574 575 576
  GimpPixelRgn  srcPR, destPR;
  gint          x1, y1, x2, y2;
  gpointer      pr;
  gint          total_area;
  gint          area_so_far;
  gint          progress_skip;
577 578 579 580 581 582 583 584 585 586

  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),
587
                       FALSE, FALSE);
588
  gimp_pixel_rgn_init (&destPR, drawable, x1, y1, (x2 - x1), (y2 - y1),
589
                       TRUE, TRUE);
590

591 592 593 594 595 596 597
  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)
598 599 600 601 602 603 604
        {
          area_so_far += srcPR.w * srcPR.h;

          if (((progress_skip++) % 10) == 0)
            gimp_progress_update ((gdouble) area_so_far /
                                  (gdouble) total_area);
        }
605 606 607 608 609 610 611
    }

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