gimpdisplayshell-render.c 49.7 KB
Newer Older
1
/* GIMP - The GNU Image Manipulation Program
Elliot Lee's avatar
Elliot Lee committed
2 3 4 5 6 7 8 9 10 11 12 13 14 15
 * 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
 */
Sven Neumann's avatar
Sven Neumann committed
18 19 20

#include "config.h"

Elliot Lee's avatar
Elliot Lee committed
21
#include <string.h>
22

Sven Neumann's avatar
Sven Neumann committed
23 24
#include <gtk/gtk.h>

25
#include "libgimpbase/gimpbase.h"
26
#include "libgimpmath/gimpmath.h"
27 28
#include "libgimpwidgets/gimpwidgets.h"

29
#include "display-types.h"
Sven Neumann's avatar
Sven Neumann committed
30

Michael Natterer's avatar
Michael Natterer committed
31 32 33
#include "base/tile-manager.h"
#include "base/tile.h"

34 35
#include "config/gimpdisplayconfig.h"

36
#include "core/gimp.h"
37
#include "core/gimpdrawable.h"
38
#include "core/gimpimage.h"
39
#include "core/gimpimage-colormap.h"
Michael Natterer's avatar
Michael Natterer committed
40
#include "core/gimpprojection.h"
41

42
#include "gimpcanvas.h"
43
#include "gimpdisplay.h"
Michael Natterer's avatar
Michael Natterer committed
44
#include "gimpdisplayshell.h"
45
#include "gimpdisplayshell-filter.h"
46
#include "gimpdisplayshell-render.h"
47
#include "gimpdisplayshell-scroll.h"
48

49
#define GIMP_DISPLAY_ZOOM_FAST     (1 << 0) /* use the fastest possible code
50 51
                                               path trading quality for speed
                                             */
52
#define GIMP_DISPLAY_ZOOM_PIXEL_AA (1 << 1) /* provide AA edges when zooming in
53
                                               on the actual pixels (in current
Sven Neumann's avatar
Sven Neumann committed
54 55 56
                                               code only enables it between
                                               100% and 200% zoom)
                                             */
57

58 59 60
#define GIMP_DISPLAY_RENDER_BUF_WIDTH  256


Elliot Lee's avatar
Elliot Lee committed
61
typedef struct _RenderInfo  RenderInfo;
62 63

typedef void (* RenderFunc) (RenderInfo *info);
Elliot Lee's avatar
Elliot Lee committed
64 65 66

struct _RenderInfo
{
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
  const GimpDisplayShell *shell;
  TileManager            *src_tiles;
  const guchar           *src;
  guchar                 *dest;
  gboolean                src_is_premult;
  gint                    x, y;
  gint                    w, h;
  gdouble                 scalex;
  gdouble                 scaley;
  gint                    src_x;
  gint                    src_y;
  gint                    dest_bpp;
  gint                    dest_bpl;
  gint                    dest_width;

  gint                    zoom_quality;
83

84
  /* Bresenham helpers */
85 86 87
  gint                    x_dest_inc; /* amount to increment for each dest. pixel  */
  gint                    x_src_dec;  /* amount to decrement for each source pixel */
  gint64                  dx_start;   /* pixel fraction for first pixel            */
88

89 90 91
  gint                    y_dest_inc;
  gint                    y_src_dec;
  gint64                  dy_start;
92

93 94 95 96
  gint                    footprint_x;
  gint                    footprint_y;
  gint                    footshift_x;
  gint                    footshift_y;
97

98
  gint64                  dy;
Elliot Lee's avatar
Elliot Lee committed
99 100
};

101 102 103 104 105
static void  gimp_display_shell_render_info_scale   (RenderInfo             *info,
                                                     const GimpDisplayShell *shell,
                                                     TileManager            *tiles,
                                                     gint                    level,
                                                     gboolean                is_premult);
Elliot Lee's avatar
Elliot Lee committed
106

107 108 109
static void  gimp_display_shell_render_setup_notify (GObject                *config,
                                                     GParamSpec             *param_spec,
                                                     Gimp                   *gimp);
110 111


112
static guchar *tile_buf    = NULL;
113

114 115
static guint   check_mod   = 0;
static guint   check_shift = 0;
116 117
static guchar  check_dark  = 0;
static guchar  check_light = 0;
118

Elliot Lee's avatar
Elliot Lee committed
119 120

void
121
gimp_display_shell_render_init (Gimp *gimp)
122 123
{
  g_return_if_fail (GIMP_IS_GIMP (gimp));
124
  g_return_if_fail (tile_buf == NULL);
125

126
  g_signal_connect (gimp->config, "notify::transparency-size",
127
                    G_CALLBACK (gimp_display_shell_render_setup_notify),
128
                    gimp);
129
  g_signal_connect (gimp->config, "notify::transparency-type",
130
                    G_CALLBACK (gimp_display_shell_render_setup_notify),
131 132
                    gimp);

133
  /*  allocate a buffer for arranging information from a row of tiles  */
134
  tile_buf = g_new (guchar, GIMP_DISPLAY_RENDER_BUF_WIDTH * MAX_CHANNELS);
135

136
  gimp_display_shell_render_setup_notify (G_OBJECT (gimp->config), NULL, gimp);
137 138 139
}

void
140
gimp_display_shell_render_exit (Gimp *gimp)
141 142 143
{
  g_return_if_fail (GIMP_IS_GIMP (gimp));

144
  g_signal_handlers_disconnect_by_func (gimp->config,
145
                                        gimp_display_shell_render_setup_notify,
146 147 148 149 150 151 152 153 154 155
                                        gimp);

  if (tile_buf)
    {
      g_free (tile_buf);
      tile_buf = NULL;
    }
}

static void
156 157 158
gimp_display_shell_render_setup_notify (GObject    *config,
                                        GParamSpec *param_spec,
                                        Gimp       *gimp)
Elliot Lee's avatar
Elliot Lee committed
159
{
160
  GimpCheckSize check_size;
161
  GimpCheckType check_type;
162

163 164
  g_object_get (config,
                "transparency-size", &check_size,
165
                "transparency-type", &check_type,
166
                NULL);
Elliot Lee's avatar
Elliot Lee committed
167

168 169
  gimp_checks_get_shades (check_type, &check_light, &check_dark);

Elliot Lee's avatar
Elliot Lee committed
170 171
  switch (check_size)
    {
172
    case GIMP_CHECK_SIZE_SMALL_CHECKS:
173
      check_mod   = 0x3;
Elliot Lee's avatar
Elliot Lee committed
174 175
      check_shift = 2;
      break;
176

177
    case GIMP_CHECK_SIZE_MEDIUM_CHECKS:
178
      check_mod   = 0x7;
Elliot Lee's avatar
Elliot Lee committed
179 180
      check_shift = 3;
      break;
181

182
    case GIMP_CHECK_SIZE_LARGE_CHECKS:
183
      check_mod   = 0xf;
Elliot Lee's avatar
Elliot Lee committed
184 185 186 187 188
      check_shift = 4;
      break;
    }
}

189

Elliot Lee's avatar
Elliot Lee committed
190 191
/*  Render Image functions  */

192 193
static void           render_image_rgb_a         (RenderInfo             *info);
static void           render_image_gray_a        (RenderInfo             *info);
194

195
static const guchar * render_image_tile_fault    (RenderInfo             *info);
Elliot Lee's avatar
Elliot Lee committed
196 197


198 199 200 201 202 203 204 205
static void  gimp_display_shell_render_highlight (const GimpDisplayShell *shell,
                                                  gint                    x,
                                                  gint                    y,
                                                  gint                    w,
                                                  gint                    h,
                                                  GdkRectangle           *highlight);
static void  gimp_display_shell_render_mask      (const GimpDisplayShell *shell,
                                                  RenderInfo             *info);
206 207


Elliot Lee's avatar
Elliot Lee committed
208 209 210
/*****************************************************************/
/*  This function is the core of the display--it offsets and     */
/*  scales the image according to the current parameters in the  */
211
/*  display object.  It handles color, grayscale, 8, 15, 16, 24  */
Elliot Lee's avatar
Elliot Lee committed
212 213 214 215
/*  & 32 bit output depths.                                      */
/*****************************************************************/

void
216 217 218 219 220 221
gimp_display_shell_render (const GimpDisplayShell *shell,
                           gint                    x,
                           gint                    y,
                           gint                    w,
                           gint                    h,
                           GdkRectangle           *highlight)
Elliot Lee's avatar
Elliot Lee committed
222
{
223
  GimpProjection *projection;
224
  GimpImage      *image;
225 226
  RenderInfo      info;
  GimpImageType   type;
227 228
  gint            offset_x;
  gint            offset_y;
229

230
  g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
Sven Neumann's avatar
Sven Neumann committed
231 232
  g_return_if_fail (w > 0 && h > 0);

233
  image = shell->display->image;
234 235

  projection = gimp_image_get_projection (image);
Elliot Lee's avatar
Elliot Lee committed
236

237
  gimp_display_shell_scroll_get_render_start_offset (shell, &offset_x, &offset_y);
238

239 240
  /* Initialize RenderInfo with values that don't change during the
   * call of this function.
241 242
   */
  info.shell      = shell;
243

244 245
  info.x          = x + offset_x;
  info.y          = y + offset_y;
246 247 248 249
  info.w          = w;
  info.h          = h;

  info.dest_bpp   = 3;
250
  info.dest_bpl   = info.dest_bpp * GIMP_DISPLAY_RENDER_BUF_WIDTH;
251 252
  info.dest_width = info.dest_bpp * info.w;

253
  switch (shell->display->config->zoom_quality)
254 255 256 257 258 259 260 261 262 263
    {
    case GIMP_ZOOM_QUALITY_LOW:
      info.zoom_quality = GIMP_DISPLAY_ZOOM_FAST;
      break;

    case GIMP_ZOOM_QUALITY_HIGH:
      info.zoom_quality = GIMP_DISPLAY_ZOOM_PIXEL_AA;
      break;
    }

264 265
  /* Setup RenderInfo for rendering a GimpProjection level. */
  {
266
    TileManager *tiles;
267
    gint         level;
268
    gboolean     premult;
269 270 271 272 273

    level = gimp_projection_get_level (projection,
                                       shell->scale_x,
                                       shell->scale_y);

274
    tiles = gimp_projection_get_tiles_at_level (projection, level, &premult);
275

276
    gimp_display_shell_render_info_scale (&info, shell, tiles, level, premult);
277
  }
Elliot Lee's avatar
Elliot Lee committed
278

279
  /* Currently, only RGBA and GRAYA projection types are used. */
280 281
  type = gimp_projection_get_image_type (projection);

282 283 284 285 286 287 288 289 290
  switch (type)
    {
      case GIMP_RGBA_IMAGE:
        render_image_rgb_a (&info);
        break;
      case GIMP_GRAYA_IMAGE:
        render_image_gray_a (&info);
        break;
      default:
291
        g_warning ("%s: unsupported projection type (%d)", G_STRFUNC, type);
292 293
        g_assert_not_reached ();
    }
294 295

  /*  apply filters to the rendered projection  */
296 297 298 299 300
  if (shell->filter_stack)
    gimp_color_display_stack_convert (shell->filter_stack,
                                      shell->render_buf,
                                      w, h,
                                      3,
301
                                      3 * GIMP_DISPLAY_RENDER_BUF_WIDTH);
302

303 304
  /*  dim pixels outside the highlighted rectangle  */
  if (highlight)
305 306 307
    {
      gimp_display_shell_render_highlight (shell, x, y, w, h, highlight);
    }
308
  else if (shell->mask)
309
    {
310
      TileManager *tiles = gimp_drawable_get_tiles (shell->mask);
311

312
      /* The mask does not (yet) have an image pyramid, use 0 as level, */
313
      gimp_display_shell_render_info_scale (&info, shell, tiles, 0, FALSE);
314

315
      gimp_display_shell_render_mask (shell, &info);
316
    }
317

318
  /*  put it to the screen  */
319 320 321 322
  {
    gint disp_xoffset, disp_yoffset;
    gint offset_x, offset_y;

323 324
    gimp_display_shell_scroll_get_disp_offset (shell, &disp_xoffset, &disp_yoffset);
    gimp_display_shell_scroll_get_render_start_offset (shell, &offset_x, &offset_y);
325 326 327

    gimp_canvas_draw_rgb (GIMP_CANVAS (shell->canvas), GIMP_CANVAS_STYLE_RENDER,
                        x + disp_xoffset, y + disp_yoffset,
328 329
                        w, h,
                        shell->render_buf,
330
                        3 * GIMP_DISPLAY_RENDER_BUF_WIDTH,
331 332
                        offset_x, offset_y);
  }
Elliot Lee's avatar
Elliot Lee committed
333 334 335
}


336 337 338 339 340 341 342 343 344 345
#define GIMP_DISPLAY_SHELL_DIM_PIXEL(buf,x) \
{ \
  buf[3 * (x) + 0] >>= 1; \
  buf[3 * (x) + 1] >>= 1; \
  buf[3 * (x) + 2] >>= 1; \
}

/*  This function highlights the given area by dimming all pixels outside. */

static void
346 347 348 349 350 351
gimp_display_shell_render_highlight (const GimpDisplayShell *shell,
                                     gint                    x,
                                     gint                    y,
                                     gint                    w,
                                     gint                    h,
                                     GdkRectangle           *highlight)
352 353 354
{
  guchar       *buf  = shell->render_buf;
  GdkRectangle  rect;
355 356 357
  gint          offset_x;
  gint          offset_y;

358
  gimp_display_shell_scroll_get_render_start_offset (shell, &offset_x, &offset_y);
359

360 361
  rect.x      = x + offset_x;
  rect.y      = y + offset_y;
362 363 364 365 366
  rect.width  = w;
  rect.height = h;

  if (gdk_rectangle_intersect (highlight, &rect, &rect))
    {
367 368
      rect.x -= x + offset_x;
      rect.y -= y + offset_y;
369 370 371 372 373 374

      for (y = 0; y < rect.y; y++)
        {
          for (x = 0; x < w; x++)
            GIMP_DISPLAY_SHELL_DIM_PIXEL (buf, x)

375
          buf += 3 * GIMP_DISPLAY_RENDER_BUF_WIDTH;
376 377 378 379 380 381 382 383 384 385
        }

      for ( ; y < rect.y + rect.height; y++)
        {
          for (x = 0; x < rect.x; x++)
            GIMP_DISPLAY_SHELL_DIM_PIXEL (buf, x)

          for (x += rect.width; x < w; x++)
            GIMP_DISPLAY_SHELL_DIM_PIXEL (buf, x)

386
          buf += 3 * GIMP_DISPLAY_RENDER_BUF_WIDTH;
387 388 389 390 391 392 393
        }

      for ( ; y < h; y++)
        {
          for (x = 0; x < w; x++)
            GIMP_DISPLAY_SHELL_DIM_PIXEL (buf, x)

394
          buf += 3 * GIMP_DISPLAY_RENDER_BUF_WIDTH;
395 396 397 398 399 400 401 402 403
        }
    }
  else
    {
      for (y = 0; y < h; y++)
        {
          for (x = 0; x < w; x++)
            GIMP_DISPLAY_SHELL_DIM_PIXEL (buf, x)

404
          buf += 3 * GIMP_DISPLAY_RENDER_BUF_WIDTH;
405 406 407 408
        }
    }
}

409
static void
410 411
gimp_display_shell_render_mask (const GimpDisplayShell *shell,
                                RenderInfo             *info)
412
{
413 414
  gint y, ye;
  gint x, xe;
415 416 417 418 419

  y  = info->y;
  ye = info->y + info->h;
  xe = info->x + info->w;

420
  info->dy  = info->dy_start;
421 422 423 424
  info->src = render_image_tile_fault (info);

  while (TRUE)
    {
425 426
      const guchar *src  = info->src;
      guchar       *dest = info->dest;
427

428
      switch (shell->mask_color)
429
        {
430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445
        case GIMP_RED_CHANNEL:
          for (x = info->x; x < xe; x++, src++, dest += 3)
            {
              if (*src & 0x80)
                continue;

              dest[1] = dest[1] >> 2;
              dest[2] = dest[2] >> 2;
            }
          break;

        case GIMP_GREEN_CHANNEL:
          for (x = info->x; x < xe; x++, src++, dest += 3)
            {
              if (*src & 0x80)
                continue;
446

447 448 449 450 451 452 453
              dest[0] = dest[0] >> 2;
              dest[2] = dest[2] >> 2;
            }
          break;

        case GIMP_BLUE_CHANNEL:
          for (x = info->x; x < xe; x++, src++, dest += 3)
454
            {
455 456 457 458 459
              if (*src & 0x80)
                continue;

              dest[0] = dest[0] >> 2;
              dest[1] = dest[1] >> 2;
460
            }
461 462 463 464
          break;

        default:
          break;
465
        }
466 467 468 469

      if (++y == ye)
        break;

470
      info->dest  += info->dest_bpl;
471

472 473 474
      info->dy    += info->y_dest_inc;
      info->src_y += info->dy / info->y_src_dec;
      info->dy     = info->dy % info->y_src_dec;
475

476
      info->src = render_image_tile_fault (info);
477 478 479
    }
}

480

Elliot Lee's avatar
Elliot Lee committed
481 482 483 484 485
/*************************/
/*  8 Bit functions      */
/*************************/

static void
486
render_image_gray_a (RenderInfo *info)
Elliot Lee's avatar
Elliot Lee committed
487
{
488 489
  gint  y, ye;
  gint  x, xe;
Elliot Lee's avatar
Elliot Lee committed
490

491
  y  = info->y;
Elliot Lee's avatar
Elliot Lee committed
492 493 494
  ye = info->y + info->h;
  xe = info->x + info->w;

495
  info->dy = info->dy_start;
Elliot Lee's avatar
Elliot Lee committed
496 497
  info->src = render_image_tile_fault (info);

Sven Neumann's avatar
Sven Neumann committed
498
  while (TRUE)
Elliot Lee's avatar
Elliot Lee committed
499
    {
500 501 502
      const guchar *src  = info->src;
      guchar       *dest = info->dest;
      guint         dark_light;
503

504
      dark_light = (y >> check_shift) + (info->x >> check_shift);
Sven Neumann's avatar
Sven Neumann committed
505

506
      for (x = info->x; x < xe; x++, src += 2, dest += 3)
Sven Neumann's avatar
Sven Neumann committed
507
        {
508
          guint v;
509

510
          if (dark_light & 0x1)
511
            v = ((src[0] << 8) + check_dark  * (256 - src[1])) >> 8;
512
          else
513
            v = ((src[0] << 8) + check_light * (256 - src[1])) >> 8;
Daniel Egger's avatar
Daniel Egger committed
514

515
          dest[0] = dest[1] = dest[2] = v;
516

517 518
          if (((x + 1) & check_mod) == 0)
            dark_light += 1;
519
        }
Elliot Lee's avatar
Elliot Lee committed
520

Sven Neumann's avatar
Sven Neumann committed
521 522
      if (++y == ye)
        break;
Elliot Lee's avatar
Elliot Lee committed
523

524
      info->dest  += info->dest_bpl;
525

526 527 528
      info->dy    += info->y_dest_inc;
      info->src_y += info->dy / info->y_src_dec;
      info->dy     = info->dy % info->y_src_dec;
Sven Neumann's avatar
Sven Neumann committed
529

530
      info->src = render_image_tile_fault (info);
Elliot Lee's avatar
Elliot Lee committed
531 532 533 534
    }
}

static void
535
render_image_rgb_a (RenderInfo *info)
Elliot Lee's avatar
Elliot Lee committed
536
{
537 538
  gint  y, ye;
  gint  x, xe;
Elliot Lee's avatar
Elliot Lee committed
539

540
  y  = info->y;
Daniel Egger's avatar
Daniel Egger committed
541 542 543
  ye = info->y + info->h;
  xe = info->x + info->w;

544
  info->dy = info->dy_start;
Elliot Lee's avatar
Elliot Lee committed
545 546
  info->src = render_image_tile_fault (info);

Sven Neumann's avatar
Sven Neumann committed
547
  while (TRUE)
Elliot Lee's avatar
Elliot Lee committed
548
    {
549 550 551
      const guchar *src  = info->src;
      guchar       *dest = info->dest;
      guint         dark_light;
Elliot Lee's avatar
Elliot Lee committed
552

553
      dark_light = (y >> check_shift) + (info->x >> check_shift);
Elliot Lee's avatar
Elliot Lee committed
554

555
      for (x = info->x; x < xe; x++, src += 4, dest += 3)
556
        {
557
          guint r, g, b;
558

559 560
          if (dark_light & 0x1)
            {
561 562 563
              r = ((src[0] << 8) + check_dark  * (256 - src[3])) >> 8;
              g = ((src[1] << 8) + check_dark  * (256 - src[3])) >> 8;
              b = ((src[2] << 8) + check_dark  * (256 - src[3])) >> 8;
564 565 566
            }
          else
            {
567 568 569
              r = ((src[0] << 8) + check_light * (256 - src[3])) >> 8;
              g = ((src[1] << 8) + check_light * (256 - src[3])) >> 8;
              b = ((src[2] << 8) + check_light * (256 - src[3])) >> 8;
570
            }
571

572 573 574
          dest[0] = r;
          dest[1] = g;
          dest[2] = b;
575

576 577
          if (((x + 1) & check_mod) == 0)
            dark_light += 1;
578
        }
Elliot Lee's avatar
Elliot Lee committed
579

Sven Neumann's avatar
Sven Neumann committed
580 581
      if (++y == ye)
        break;
Elliot Lee's avatar
Elliot Lee committed
582

583
      info->dest  += info->dest_bpl;
584

585 586 587 588
      info->dy    += info->y_dest_inc;
      info->src_y += info->dy / info->y_src_dec;
      info->dy     = info->dy % info->y_src_dec;

589 590
      if (info->src_y >= 0)
        info->src = render_image_tile_fault (info);
Elliot Lee's avatar
Elliot Lee committed
591 592 593 594
    }
}

static void
595 596 597 598 599
gimp_display_shell_render_info_scale (RenderInfo             *info,
                                      const GimpDisplayShell *shell,
                                      TileManager            *tiles,
                                      gint                    level,
                                      gboolean                is_premult)
Elliot Lee's avatar
Elliot Lee committed
600
{
601 602
  info->src_tiles      = tiles;
  info->src_is_premult = is_premult;
Elliot Lee's avatar
Elliot Lee committed
603

604 605 606
  /* We must reset info->dest because this member is modified in render
   * functions.
   */
607
  info->dest     = shell->render_buf;
608

609 610
  info->scalex   = shell->scale_x * (1 << level);
  info->scaley   = shell->scale_y * (1 << level);
Elliot Lee's avatar
Elliot Lee committed
611

612
  /* use Bresenham like stepping */
613
  info->x_dest_inc = shell->x_dest_inc;
614
  info->x_src_dec  = shell->x_src_dec << level;
615

616
  info->dx_start   = ((gint64) info->x_dest_inc) * info->x + info->x_dest_inc / 2;
617 618
  info->src_x      = info->dx_start / info->x_src_dec;
  info->dx_start   = info->dx_start % info->x_src_dec;
619 620

  /* same for y */
621
  info->y_dest_inc = shell->y_dest_inc;
622
  info->y_src_dec  = shell->y_src_dec << level;
623

624
  info->dy_start   = ((gint64) info->y_dest_inc) * info->y + info->y_dest_inc / 2;
625 626
  info->src_y      = info->dy_start / info->y_src_dec;
  info->dy_start   = info->dy_start % info->y_src_dec;
627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653

  /* make sure that the footprint is in the range 256..512 */
  info->footprint_x = info->x_src_dec;
  info->footshift_x = 0;
  while (info->footprint_x > 512)
    {
      info->footprint_x >>= 1;
      info->footshift_x --;
    }
  while (info->footprint_x < 256)
    {
      info->footprint_x <<= 1;
      info->footshift_x ++;
    }

  info->footprint_y = info->y_src_dec;
  info->footshift_y = 0;
  while (info->footprint_y > 512)
    {
      info->footprint_y >>= 1;
      info->footshift_y --;
    }
  while (info->footprint_y < 256)
    {
      info->footprint_y <<= 1;
      info->footshift_y ++;
    }
654 655
}

656
/* This version assumes that the src data is already pre-multiplied. */
657
static inline void
658 659 660 661 662 663
box_filter (const guint    left_weight,
            const guint    center_weight,
            const guint    right_weight,
            const guint    top_weight,
            const guint    middle_weight,
            const guint    bottom_weight,
Øyvind Kolås's avatar
Øyvind Kolås committed
664 665
            const guchar **src,   /* the 9 surrounding source pixels */
            guchar        *dest,
666
            const gint     bpp)
667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698
{
  const guint sum = ((left_weight + center_weight + right_weight) *
                     (top_weight + middle_weight + bottom_weight));
  gint i;

  for (i = 0; i < bpp; i++)
    {
      dest[i] = ( left_weight   * ((src[0][i] * top_weight) +
                                   (src[3][i] * middle_weight) +
                                   (src[6][i] * bottom_weight))
                + center_weight * ((src[1][i] * top_weight) +
                                   (src[4][i] * middle_weight) +
                                   (src[7][i] * bottom_weight))
                + right_weight  * ((src[2][i] * top_weight) +
                                   (src[5][i] * middle_weight) +
                                   (src[8][i] * bottom_weight))) / sum;
    }
}

/* This version assumes that the src data is not pre-multipled.
 * It creates pre-multiplied output though.
 */
static inline void
box_filter_premult (const guint    left_weight,
                    const guint    center_weight,
                    const guint    right_weight,
                    const guint    top_weight,
                    const guint    middle_weight,
                    const guint    bottom_weight,
                    const guchar **src,   /* the 9 surrounding source pixels */
                    guchar        *dest,
                    const gint     bpp)
699
{
700
  const guint sum = ((left_weight + center_weight + right_weight) *
701
                     (top_weight + middle_weight + bottom_weight)) >> 4;
702

703
  switch (bpp)
704 705 706
    {
      case 4:
#define ALPHA 3
707
        {
708
          guint factors[9] =
Sven Neumann's avatar
Sven Neumann committed
709
            {
710 711 712 713 714 715 716 717 718
              (src[1][ALPHA] * top_weight)    >> 4,
              (src[4][ALPHA] * middle_weight) >> 4,
              (src[7][ALPHA] * bottom_weight) >> 4,
              (src[2][ALPHA] * top_weight)    >> 4,
              (src[5][ALPHA] * middle_weight) >> 4,
              (src[8][ALPHA] * bottom_weight) >> 4,
              (src[0][ALPHA] * top_weight)    >> 4,
              (src[3][ALPHA] * middle_weight) >> 4,
              (src[6][ALPHA] * bottom_weight) >> 4
Sven Neumann's avatar
Sven Neumann committed
719
            };
720

721 722 723
          guint a = (center_weight * (factors[0] + factors[1] + factors[2]) +
                     right_weight  * (factors[3] + factors[4] + factors[5]) +
                     left_weight   * (factors[6] + factors[7] + factors[8]));
724
          guint i;
725

726
          for (i = 0; i < ALPHA; i++)
727
            {
728 729 730
              dest[i] = ((center_weight * (factors[0] * src[1][i] +
                                           factors[1] * src[4][i] +
                                           factors[2] * src[7][i]) +
731

732 733 734
                          right_weight  * (factors[3] * src[2][i] +
                                           factors[4] * src[5][i] +
                                           factors[5] * src[8][i]) +
735

736 737 738
                          left_weight   * (factors[6] * src[0][i] +
                                           factors[7] * src[3][i] +
                                           factors[8] * src[6][i])) / sum) >> 8;
739
            }
740 741

          dest[ALPHA] = (a + (sum >> 1)) / sum;
742
        }
743 744
#undef ALPHA
        break;
Sven Neumann's avatar
Sven Neumann committed
745

746 747
      case 2:
#define ALPHA 1
748

749 750 751
        /* NOTE: this is a copy and paste of the code above, ALPHA changes
         *       the behavior in all needed ways.
         */
752
        {
753
          guint factors[9] =
Sven Neumann's avatar
Sven Neumann committed
754
            {
755 756 757 758 759 760 761 762 763
              (src[1][ALPHA] * top_weight)    >> 4,
              (src[4][ALPHA] * middle_weight) >> 4,
              (src[7][ALPHA] * bottom_weight) >> 4,
              (src[2][ALPHA] * top_weight)    >> 4,
              (src[5][ALPHA] * middle_weight) >> 4,
              (src[8][ALPHA] * bottom_weight) >> 4,
              (src[0][ALPHA] * top_weight)    >> 4,
              (src[3][ALPHA] * middle_weight) >> 4,
              (src[6][ALPHA] * bottom_weight) >> 4
Sven Neumann's avatar
Sven Neumann committed
764
            };
765

766 767 768
          guint a = (center_weight * (factors[0] + factors[1] + factors[2]) +
                     right_weight  * (factors[3] + factors[4] + factors[5]) +
                     left_weight   * (factors[6] + factors[7] + factors[8]));
769
          guint i;
770

771
          for (i = 0; i < ALPHA; i++)
772
            {
773 774 775
              dest[i] = ((center_weight * (factors[0] * src[1][i] +
                                           factors[1] * src[4][i] +
                                           factors[2] * src[7][i]) +
776

777 778 779
                          right_weight  * (factors[3] * src[2][i] +
                                           factors[4] * src[5][i] +
                                           factors[5] * src[8][i]) +
780

781 782 783
                          left_weight   * (factors[6] * src[0][i] +
                                           factors[7] * src[3][i] +
                                           factors[8] * src[6][i])) / sum) >> 8;
784
            }
785 786

          dest[ALPHA] = (a + (sum >> 1)) / sum;
787
        }
788 789
#undef ALPHA
        break;
Sven Neumann's avatar
Sven Neumann committed
790

791
      default:
Øyvind Kolås's avatar
Øyvind Kolås committed
792
        g_warning ("bpp=%i not implemented as box filter", bpp);
Sven Neumann's avatar
Sven Neumann committed
793
        break;
794 795 796
    }
}

797

798 799 800 801
/* fast paths */
static const guchar * render_image_tile_fault_one_row  (RenderInfo *info);
static const guchar * render_image_tile_fault_nearest  (RenderInfo *info);

802 803 804
/*  012 <- this is the order of the numbered source tiles / pixels.
 *  345    for the 3x3 neighbourhoods.
 *  678
805 806
 */

807 808 809
/* Function to render a horizontal line of view data.  The data
 * returned from this function has the alpha channel pre-multiplied.
 */
810
static const guchar *
Elliot Lee's avatar
Elliot Lee committed
811
render_image_tile_fault (RenderInfo *info)
812
{
813 814
  Tile         *tile[9];
  const guchar *src[9];
815 816 817

  guchar       *dest;
  gint          width;
818
  gint          tilex0;   /* the current x-tile indice used for the middle
819 820 821
                             sample pair*/
  gint          tilex1;   /* the current x-tile indice used for the right
                             sample pair */
822 823
  gint          tilexL;   /* the current x-tile indice used for the left
                             sample pair */
824
  gint          bpp;
825 826 827
  gint          dx;
  gint          src_x;
  gint          skipped;
828

829
  guint         left_weight;
830
  guint         center_weight;
831
  guint         right_weight;
832

833
  guint         top_weight;
834
  guint         middle_weight;
835
  guint         bottom_weight;
836

837 838 839
  guint         source_width;
  guint         source_height;

840
  source_width  = tile_manager_width (info->src_tiles);
841
  source_height = tile_manager_height (info->src_tiles);
842

843
  /* dispatch to fast path functions on special conditions */
844
  if ((info->zoom_quality & GIMP_DISPLAY_ZOOM_FAST)
845

846
      /* use nearest neighbour for exact levels */
847
      || (info->scalex == 1.0 &&
Sven Neumann's avatar
Sven Neumann committed
848
          info->scaley == 1.0)
849

850 851 852
      /* or when we're larger than 1.0 and not using any AA */
      || (info->shell->scale_x > 1.0 &&
          info->shell->scale_y > 1.0 &&
853
          (! (info->zoom_quality & GIMP_DISPLAY_ZOOM_PIXEL_AA)))
854

855 856 857
      /* or at any point when both scale factors are greater or equal to 200% */
      || (info->shell->scale_x >= 2.0 &&
          info->shell->scale_y >= 2.0 )
Sven Neumann's avatar
Sven Neumann committed
858

859 860 861 862
      /* or when we're scaling a 1bpp texture, this code-path seems to be
       * invoked when interacting with SIOX which uses a palletized drawable
       */
      || (tile_manager_bpp (info->src_tiles)==1)
863
      )
864
    {
865 866 867
      return render_image_tile_fault_nearest (info);
    }
  else if (((info->src_y)     & ~(TILE_WIDTH -1)) ==
868 869
           ((info->src_y + 1) & ~(TILE_WIDTH -1)) &&
           ((info->src_y)     & ~(TILE_WIDTH -1)) ==
870 871
           ((info->src_y - 1) & ~(TILE_WIDTH -1)) &&
           (info->src_y + 1 < source_height)
872
          )
873
    {
Øyvind Kolås's avatar
Øyvind Kolås committed
874 875
      /* all the tiles needed are in a single row, use a tile iterator
       * optimized for this case. */
876 877 878
      return render_image_tile_fault_one_row (info);
    }