mirror of https://github.com/GNOME/gimp.git
1676 lines
49 KiB
C
1676 lines
49 KiB
C
/* GIMP - The GNU 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 3 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, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <string.h>
|
|
|
|
#include <gegl.h>
|
|
#include <gtk/gtk.h>
|
|
|
|
#include "libgimpbase/gimpbase.h"
|
|
#include "libgimpmath/gimpmath.h"
|
|
#include "libgimpwidgets/gimpwidgets.h"
|
|
|
|
#include "display-types.h"
|
|
|
|
#include "base/tile-manager.h"
|
|
#include "base/tile.h"
|
|
|
|
#include "config/gimpdisplayconfig.h"
|
|
|
|
#include "core/gimp.h"
|
|
#include "core/gimpdrawable.h"
|
|
#include "core/gimpimage.h"
|
|
#include "core/gimpimage-colormap.h"
|
|
#include "core/gimppickable.h"
|
|
#include "core/gimpprojection.h"
|
|
|
|
#include "gimpcanvas.h"
|
|
#include "gimpdisplay.h"
|
|
#include "gimpdisplayshell.h"
|
|
#include "gimpdisplayshell-filter.h"
|
|
#include "gimpdisplayshell-render.h"
|
|
#include "gimpdisplayshell-scroll.h"
|
|
|
|
#define GIMP_DISPLAY_ZOOM_FAST (1 << 0) /* use the fastest possible code
|
|
path trading quality for speed
|
|
*/
|
|
#define GIMP_DISPLAY_ZOOM_PIXEL_AA (1 << 1) /* provide AA edges when zooming in
|
|
on the actual pixels (in current
|
|
code only enables it between
|
|
100% and 200% zoom)
|
|
*/
|
|
|
|
|
|
typedef struct _RenderInfo RenderInfo;
|
|
|
|
typedef void (* RenderFunc) (RenderInfo *info);
|
|
|
|
struct _RenderInfo
|
|
{
|
|
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;
|
|
|
|
/* Bresenham helpers */
|
|
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 */
|
|
|
|
gint y_dest_inc;
|
|
gint y_src_dec;
|
|
gint64 dy_start;
|
|
|
|
gint footprint_x;
|
|
gint footprint_y;
|
|
gint footshift_x;
|
|
gint footshift_y;
|
|
|
|
gint64 dy;
|
|
};
|
|
|
|
static void gimp_display_shell_render_info_scale (RenderInfo *info,
|
|
GimpDisplayShell *shell,
|
|
TileManager *tiles,
|
|
gint level,
|
|
gboolean is_premult);
|
|
|
|
static void gimp_display_shell_render_setup_notify (GObject *config,
|
|
GParamSpec *param_spec,
|
|
Gimp *gimp);
|
|
|
|
|
|
static guchar *tile_buf = NULL;
|
|
|
|
static guint check_mod = 0;
|
|
static guint check_shift = 0;
|
|
static guchar check_dark = 0;
|
|
static guchar check_light = 0;
|
|
|
|
|
|
void
|
|
gimp_display_shell_render_init (Gimp *gimp)
|
|
{
|
|
g_return_if_fail (GIMP_IS_GIMP (gimp));
|
|
g_return_if_fail (tile_buf == NULL);
|
|
|
|
g_signal_connect (gimp->config, "notify::transparency-size",
|
|
G_CALLBACK (gimp_display_shell_render_setup_notify),
|
|
gimp);
|
|
g_signal_connect (gimp->config, "notify::transparency-type",
|
|
G_CALLBACK (gimp_display_shell_render_setup_notify),
|
|
gimp);
|
|
|
|
/* allocate a buffer for arranging information from a row of tiles */
|
|
tile_buf = g_new (guchar, GIMP_DISPLAY_RENDER_BUF_WIDTH * MAX_CHANNELS);
|
|
|
|
gimp_display_shell_render_setup_notify (G_OBJECT (gimp->config), NULL, gimp);
|
|
}
|
|
|
|
void
|
|
gimp_display_shell_render_exit (Gimp *gimp)
|
|
{
|
|
g_return_if_fail (GIMP_IS_GIMP (gimp));
|
|
|
|
g_signal_handlers_disconnect_by_func (gimp->config,
|
|
gimp_display_shell_render_setup_notify,
|
|
gimp);
|
|
|
|
if (tile_buf)
|
|
{
|
|
g_free (tile_buf);
|
|
tile_buf = NULL;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gimp_display_shell_render_setup_notify (GObject *config,
|
|
GParamSpec *param_spec,
|
|
Gimp *gimp)
|
|
{
|
|
GimpCheckSize check_size;
|
|
GimpCheckType check_type;
|
|
|
|
g_object_get (config,
|
|
"transparency-size", &check_size,
|
|
"transparency-type", &check_type,
|
|
NULL);
|
|
|
|
gimp_checks_get_shades (check_type, &check_light, &check_dark);
|
|
|
|
switch (check_size)
|
|
{
|
|
case GIMP_CHECK_SIZE_SMALL_CHECKS:
|
|
check_mod = 0x3;
|
|
check_shift = 2;
|
|
break;
|
|
|
|
case GIMP_CHECK_SIZE_MEDIUM_CHECKS:
|
|
check_mod = 0x7;
|
|
check_shift = 3;
|
|
break;
|
|
|
|
case GIMP_CHECK_SIZE_LARGE_CHECKS:
|
|
check_mod = 0xf;
|
|
check_shift = 4;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/* Render Image functions */
|
|
|
|
static void render_image_rgb_a (RenderInfo *info);
|
|
static void render_image_gray_a (RenderInfo *info);
|
|
|
|
static const guchar * render_image_tile_fault (RenderInfo *info);
|
|
|
|
|
|
static void gimp_display_shell_render_highlight (GimpDisplayShell *shell,
|
|
gint x,
|
|
gint y,
|
|
gint w,
|
|
gint h,
|
|
const GdkRectangle *highlight);
|
|
static void gimp_display_shell_render_mask (GimpDisplayShell *shell,
|
|
RenderInfo *info);
|
|
|
|
|
|
/*****************************************************************/
|
|
/* This function is the core of the display--it offsets and */
|
|
/* scales the image according to the current parameters in the */
|
|
/* display object. It handles color, grayscale, 8, 15, 16, 24 */
|
|
/* & 32 bit output depths. */
|
|
/*****************************************************************/
|
|
|
|
void
|
|
gimp_display_shell_render (GimpDisplayShell *shell,
|
|
gint x,
|
|
gint y,
|
|
gint w,
|
|
gint h,
|
|
const GdkRectangle *highlight)
|
|
{
|
|
GimpProjection *projection;
|
|
GimpImage *image;
|
|
RenderInfo info;
|
|
GimpImageType type;
|
|
gint offset_x;
|
|
gint offset_y;
|
|
|
|
g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
|
|
g_return_if_fail (w > 0 && h > 0);
|
|
|
|
image = gimp_display_get_image (shell->display);
|
|
|
|
projection = gimp_image_get_projection (image);
|
|
|
|
gimp_display_shell_scroll_get_render_start_offset (shell,
|
|
&offset_x, &offset_y);
|
|
|
|
/* Initialize RenderInfo with values that don't change during the
|
|
* call of this function.
|
|
*/
|
|
info.shell = shell;
|
|
|
|
info.x = x + offset_x;
|
|
info.y = y + offset_y;
|
|
info.w = w;
|
|
info.h = h;
|
|
|
|
info.dest_bpp = 3;
|
|
info.dest_bpl = info.dest_bpp * GIMP_DISPLAY_RENDER_BUF_WIDTH;
|
|
info.dest_width = info.dest_bpp * info.w;
|
|
|
|
switch (shell->display->config->zoom_quality)
|
|
{
|
|
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;
|
|
}
|
|
|
|
/* Setup RenderInfo for rendering a GimpProjection level. */
|
|
{
|
|
TileManager *tiles;
|
|
gint level;
|
|
gboolean premult;
|
|
|
|
level = gimp_projection_get_level (projection,
|
|
shell->scale_x,
|
|
shell->scale_y);
|
|
|
|
tiles = gimp_projection_get_tiles_at_level (projection, level, &premult);
|
|
|
|
gimp_display_shell_render_info_scale (&info, shell, tiles, level, premult);
|
|
}
|
|
|
|
/* Currently, only RGBA and GRAYA projection types are used. */
|
|
type = gimp_pickable_get_image_type (GIMP_PICKABLE (projection));
|
|
|
|
switch (type)
|
|
{
|
|
case GIMP_RGBA_IMAGE:
|
|
render_image_rgb_a (&info);
|
|
break;
|
|
case GIMP_GRAYA_IMAGE:
|
|
render_image_gray_a (&info);
|
|
break;
|
|
default:
|
|
g_warning ("%s: unsupported projection type (%d)", G_STRFUNC, type);
|
|
g_assert_not_reached ();
|
|
}
|
|
|
|
/* apply filters to the rendered projection */
|
|
if (shell->filter_stack)
|
|
gimp_color_display_stack_convert (shell->filter_stack,
|
|
shell->render_buf,
|
|
w, h,
|
|
3,
|
|
3 * GIMP_DISPLAY_RENDER_BUF_WIDTH);
|
|
|
|
/* dim pixels outside the highlighted rectangle */
|
|
if (highlight)
|
|
{
|
|
gimp_display_shell_render_highlight (shell, x, y, w, h, highlight);
|
|
}
|
|
else if (shell->mask)
|
|
{
|
|
TileManager *tiles = gimp_drawable_get_tiles (shell->mask);
|
|
|
|
/* The mask does not (yet) have an image pyramid, use 0 as level, */
|
|
gimp_display_shell_render_info_scale (&info, shell, tiles, 0, FALSE);
|
|
|
|
gimp_display_shell_render_mask (shell, &info);
|
|
}
|
|
|
|
/* put it to the screen */
|
|
{
|
|
gint disp_xoffset, disp_yoffset;
|
|
gint offset_x, offset_y;
|
|
|
|
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);
|
|
|
|
gimp_canvas_draw_rgb (GIMP_CANVAS (shell->canvas),
|
|
GIMP_CANVAS_STYLE_RENDER,
|
|
x + disp_xoffset, y + disp_yoffset,
|
|
w, h,
|
|
shell->render_buf,
|
|
3 * GIMP_DISPLAY_RENDER_BUF_WIDTH,
|
|
offset_x, offset_y);
|
|
}
|
|
}
|
|
|
|
|
|
#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
|
|
gimp_display_shell_render_highlight (GimpDisplayShell *shell,
|
|
gint x,
|
|
gint y,
|
|
gint w,
|
|
gint h,
|
|
const GdkRectangle *highlight)
|
|
{
|
|
guchar *buf = shell->render_buf;
|
|
GdkRectangle rect;
|
|
gint offset_x;
|
|
gint offset_y;
|
|
|
|
gimp_display_shell_scroll_get_render_start_offset (shell,
|
|
&offset_x, &offset_y);
|
|
|
|
rect.x = x + offset_x;
|
|
rect.y = y + offset_y;
|
|
rect.width = w;
|
|
rect.height = h;
|
|
|
|
if (gdk_rectangle_intersect (highlight, &rect, &rect))
|
|
{
|
|
rect.x -= x + offset_x;
|
|
rect.y -= y + offset_y;
|
|
|
|
for (y = 0; y < rect.y; y++)
|
|
{
|
|
for (x = 0; x < w; x++)
|
|
GIMP_DISPLAY_SHELL_DIM_PIXEL (buf, x)
|
|
|
|
buf += 3 * GIMP_DISPLAY_RENDER_BUF_WIDTH;
|
|
}
|
|
|
|
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)
|
|
|
|
buf += 3 * GIMP_DISPLAY_RENDER_BUF_WIDTH;
|
|
}
|
|
|
|
for ( ; y < h; y++)
|
|
{
|
|
for (x = 0; x < w; x++)
|
|
GIMP_DISPLAY_SHELL_DIM_PIXEL (buf, x)
|
|
|
|
buf += 3 * GIMP_DISPLAY_RENDER_BUF_WIDTH;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (y = 0; y < h; y++)
|
|
{
|
|
for (x = 0; x < w; x++)
|
|
GIMP_DISPLAY_SHELL_DIM_PIXEL (buf, x)
|
|
|
|
buf += 3 * GIMP_DISPLAY_RENDER_BUF_WIDTH;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
gimp_display_shell_render_mask (GimpDisplayShell *shell,
|
|
RenderInfo *info)
|
|
{
|
|
gint y, ye;
|
|
gint x, xe;
|
|
|
|
y = info->y;
|
|
ye = info->y + info->h;
|
|
xe = info->x + info->w;
|
|
|
|
info->dy = info->dy_start;
|
|
info->src = render_image_tile_fault (info);
|
|
|
|
while (TRUE)
|
|
{
|
|
const guchar *src = info->src;
|
|
guchar *dest = info->dest;
|
|
|
|
switch (shell->mask_color)
|
|
{
|
|
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;
|
|
|
|
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)
|
|
{
|
|
if (*src & 0x80)
|
|
continue;
|
|
|
|
dest[0] = dest[0] >> 2;
|
|
dest[1] = dest[1] >> 2;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (++y == ye)
|
|
break;
|
|
|
|
info->dest += info->dest_bpl;
|
|
|
|
info->dy += info->y_dest_inc;
|
|
info->src_y += info->dy / info->y_src_dec;
|
|
info->dy = info->dy % info->y_src_dec;
|
|
|
|
info->src = render_image_tile_fault (info);
|
|
}
|
|
}
|
|
|
|
|
|
/*************************/
|
|
/* 8 Bit functions */
|
|
/*************************/
|
|
|
|
static void
|
|
render_image_gray_a (RenderInfo *info)
|
|
{
|
|
gint y, ye;
|
|
gint x, xe;
|
|
|
|
y = info->y;
|
|
ye = info->y + info->h;
|
|
xe = info->x + info->w;
|
|
|
|
info->dy = info->dy_start;
|
|
info->src = render_image_tile_fault (info);
|
|
|
|
while (TRUE)
|
|
{
|
|
const guchar *src = info->src;
|
|
guchar *dest = info->dest;
|
|
guint dark_light;
|
|
|
|
dark_light = (y >> check_shift) + (info->x >> check_shift);
|
|
|
|
for (x = info->x; x < xe; x++, src += 2, dest += 3)
|
|
{
|
|
guint v;
|
|
|
|
if (dark_light & 0x1)
|
|
v = ((src[0] << 8) + check_dark * (256 - src[1])) >> 8;
|
|
else
|
|
v = ((src[0] << 8) + check_light * (256 - src[1])) >> 8;
|
|
|
|
dest[0] = dest[1] = dest[2] = v;
|
|
|
|
if (((x + 1) & check_mod) == 0)
|
|
dark_light += 1;
|
|
}
|
|
|
|
if (++y == ye)
|
|
break;
|
|
|
|
info->dest += info->dest_bpl;
|
|
|
|
info->dy += info->y_dest_inc;
|
|
info->src_y += info->dy / info->y_src_dec;
|
|
info->dy = info->dy % info->y_src_dec;
|
|
|
|
info->src = render_image_tile_fault (info);
|
|
}
|
|
}
|
|
|
|
static void
|
|
render_image_rgb_a (RenderInfo *info)
|
|
{
|
|
gint y, ye;
|
|
gint x, xe;
|
|
|
|
y = info->y;
|
|
ye = info->y + info->h;
|
|
xe = info->x + info->w;
|
|
|
|
info->dy = info->dy_start;
|
|
info->src = render_image_tile_fault (info);
|
|
|
|
while (TRUE)
|
|
{
|
|
const guchar *src = info->src;
|
|
guchar *dest = info->dest;
|
|
guint dark_light;
|
|
|
|
dark_light = (y >> check_shift) + (info->x >> check_shift);
|
|
|
|
for (x = info->x; x < xe; x++, src += 4, dest += 3)
|
|
{
|
|
guint r, g, b;
|
|
|
|
if (dark_light & 0x1)
|
|
{
|
|
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;
|
|
}
|
|
else
|
|
{
|
|
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;
|
|
}
|
|
|
|
dest[0] = r;
|
|
dest[1] = g;
|
|
dest[2] = b;
|
|
|
|
if (((x + 1) & check_mod) == 0)
|
|
dark_light += 1;
|
|
}
|
|
|
|
if (++y == ye)
|
|
break;
|
|
|
|
info->dest += info->dest_bpl;
|
|
|
|
info->dy += info->y_dest_inc;
|
|
info->src_y += info->dy / info->y_src_dec;
|
|
info->dy = info->dy % info->y_src_dec;
|
|
|
|
if (info->src_y >= 0)
|
|
info->src = render_image_tile_fault (info);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gimp_display_shell_render_info_scale (RenderInfo *info,
|
|
GimpDisplayShell *shell,
|
|
TileManager *tiles,
|
|
gint level,
|
|
gboolean is_premult)
|
|
{
|
|
info->src_tiles = tiles;
|
|
info->src_is_premult = is_premult;
|
|
|
|
/* We must reset info->dest because this member is modified in render
|
|
* functions.
|
|
*/
|
|
info->dest = shell->render_buf;
|
|
|
|
info->scalex = shell->scale_x * (1 << level);
|
|
info->scaley = shell->scale_y * (1 << level);
|
|
|
|
/* use Bresenham like stepping */
|
|
info->x_dest_inc = shell->x_dest_inc;
|
|
info->x_src_dec = shell->x_src_dec << level;
|
|
|
|
info->dx_start = ((gint64) info->x_dest_inc) * info->x + info->x_dest_inc / 2;
|
|
info->src_x = info->dx_start / info->x_src_dec;
|
|
info->dx_start = info->dx_start % info->x_src_dec;
|
|
|
|
/* same for y */
|
|
info->y_dest_inc = shell->y_dest_inc;
|
|
info->y_src_dec = shell->y_src_dec << level;
|
|
|
|
info->dy_start = ((gint64) info->y_dest_inc) * info->y + info->y_dest_inc / 2;
|
|
info->src_y = info->dy_start / info->y_src_dec;
|
|
info->dy_start = info->dy_start % info->y_src_dec;
|
|
|
|
/* 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 ++;
|
|
}
|
|
}
|
|
|
|
/* This version assumes that the src data is already pre-multiplied. */
|
|
static inline void
|
|
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,
|
|
const guchar **src, /* the 9 surrounding source pixels */
|
|
guchar *dest,
|
|
const gint bpp)
|
|
{
|
|
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)
|
|
{
|
|
const guint sum = ((left_weight + center_weight + right_weight) *
|
|
(top_weight + middle_weight + bottom_weight)) >> 4;
|
|
|
|
switch (bpp)
|
|
{
|
|
case 4:
|
|
#define ALPHA 3
|
|
{
|
|
guint factors[9] =
|
|
{
|
|
(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
|
|
};
|
|
|
|
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]));
|
|
guint i;
|
|
|
|
for (i = 0; i < ALPHA; i++)
|
|
{
|
|
dest[i] = ((center_weight * (factors[0] * src[1][i] +
|
|
factors[1] * src[4][i] +
|
|
factors[2] * src[7][i]) +
|
|
|
|
right_weight * (factors[3] * src[2][i] +
|
|
factors[4] * src[5][i] +
|
|
factors[5] * src[8][i]) +
|
|
|
|
left_weight * (factors[6] * src[0][i] +
|
|
factors[7] * src[3][i] +
|
|
factors[8] * src[6][i])) / sum) >> 8;
|
|
}
|
|
|
|
dest[ALPHA] = (a + (sum >> 1)) / sum;
|
|
}
|
|
#undef ALPHA
|
|
break;
|
|
|
|
case 2:
|
|
#define ALPHA 1
|
|
|
|
/* NOTE: this is a copy and paste of the code above, ALPHA changes
|
|
* the behavior in all needed ways.
|
|
*/
|
|
{
|
|
guint factors[9] =
|
|
{
|
|
(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
|
|
};
|
|
|
|
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]));
|
|
guint i;
|
|
|
|
for (i = 0; i < ALPHA; i++)
|
|
{
|
|
dest[i] = ((center_weight * (factors[0] * src[1][i] +
|
|
factors[1] * src[4][i] +
|
|
factors[2] * src[7][i]) +
|
|
|
|
right_weight * (factors[3] * src[2][i] +
|
|
factors[4] * src[5][i] +
|
|
factors[5] * src[8][i]) +
|
|
|
|
left_weight * (factors[6] * src[0][i] +
|
|
factors[7] * src[3][i] +
|
|
factors[8] * src[6][i])) / sum) >> 8;
|
|
}
|
|
|
|
dest[ALPHA] = (a + (sum >> 1)) / sum;
|
|
}
|
|
#undef ALPHA
|
|
break;
|
|
|
|
default:
|
|
g_warning ("bpp=%i not implemented as box filter", bpp);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/* fast paths */
|
|
static const guchar * render_image_tile_fault_one_row (RenderInfo *info);
|
|
static const guchar * render_image_tile_fault_nearest (RenderInfo *info);
|
|
|
|
/* 012 <- this is the order of the numbered source tiles / pixels.
|
|
* 345 for the 3x3 neighbourhoods.
|
|
* 678
|
|
*/
|
|
|
|
/* Function to render a horizontal line of view data. The data
|
|
* returned from this function has the alpha channel pre-multiplied.
|
|
*/
|
|
static const guchar *
|
|
render_image_tile_fault (RenderInfo *info)
|
|
{
|
|
Tile *tile[9];
|
|
const guchar *src[9];
|
|
|
|
guchar *dest;
|
|
gint width;
|
|
gint tilex0; /* the current x-tile indice used for the middle
|
|
sample pair*/
|
|
gint tilex1; /* the current x-tile indice used for the right
|
|
sample pair */
|
|
gint tilexL; /* the current x-tile indice used for the left
|
|
sample pair */
|
|
gint bpp;
|
|
gint dx;
|
|
gint src_x;
|
|
gint skipped;
|
|
|
|
guint left_weight;
|
|
guint center_weight;
|
|
guint right_weight;
|
|
|
|
guint top_weight;
|
|
guint middle_weight;
|
|
guint bottom_weight;
|
|
|
|
guint source_width;
|
|
guint source_height;
|
|
|
|
source_width = tile_manager_width (info->src_tiles);
|
|
source_height = tile_manager_height (info->src_tiles);
|
|
|
|
/* dispatch to fast path functions on special conditions */
|
|
if ((info->zoom_quality & GIMP_DISPLAY_ZOOM_FAST)
|
|
|
|
/* use nearest neighbour for exact levels */
|
|
|| (info->scalex == 1.0 &&
|
|
info->scaley == 1.0)
|
|
|
|
/* 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 &&
|
|
(! (info->zoom_quality & GIMP_DISPLAY_ZOOM_PIXEL_AA)))
|
|
|
|
/* 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 )
|
|
|
|
/* 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)
|
|
)
|
|
{
|
|
return render_image_tile_fault_nearest (info);
|
|
}
|
|
else if (((info->src_y) & ~(TILE_WIDTH -1)) ==
|
|
((info->src_y + 1) & ~(TILE_WIDTH -1)) &&
|
|
((info->src_y) & ~(TILE_WIDTH -1)) ==
|
|
((info->src_y - 1) & ~(TILE_WIDTH -1)) &&
|
|
(info->src_y + 1 < source_height)
|
|
)
|
|
{
|
|
/* all the tiles needed are in a single row, use a tile iterator
|
|
* optimized for this case. */
|
|
return render_image_tile_fault_one_row (info);
|
|
}
|
|
|
|
top_weight = MAX (info->footprint_y / 2,
|
|
info->footshift_y > 0 ? info->dy << info->footshift_y
|
|
: info->dy >> -info->footshift_y)
|
|
- (info->footshift_y > 0 ? info->dy << info->footshift_y
|
|
: info->dy >> -info->footshift_y);
|
|
|
|
bottom_weight = MAX (info->footprint_y / 2,
|
|
info->footshift_y > 0 ? info->dy << info->footshift_y
|
|
: info->dy >> -info->footshift_y)
|
|
- info->footprint_y / 2;
|
|
|
|
middle_weight = info->footprint_y - top_weight - bottom_weight;
|
|
|
|
tile[4] = tile_manager_get_tile (info->src_tiles,
|
|
info->src_x, info->src_y,
|
|
TRUE, FALSE);
|
|
tile[7] = tile_manager_get_tile (info->src_tiles,
|
|
info->src_x, info->src_y + 1,
|
|
TRUE, FALSE);
|
|
tile[1] = tile_manager_get_tile (info->src_tiles,
|
|
info->src_x, info->src_y - 1,
|
|
TRUE, FALSE);
|
|
|
|
tile[5] = tile_manager_get_tile (info->src_tiles,
|
|
info->src_x + 1, info->src_y,
|
|
TRUE, FALSE);
|
|
tile[8] = tile_manager_get_tile (info->src_tiles,
|
|
info->src_x + 1, info->src_y + 1,
|
|
TRUE, FALSE);
|
|
tile[2] = tile_manager_get_tile (info->src_tiles,
|
|
info->src_x + 1, info->src_y - 1,
|
|
TRUE, FALSE);
|
|
|
|
tile[3] = tile_manager_get_tile (info->src_tiles,
|
|
info->src_x - 1, info->src_y,
|
|
TRUE, FALSE);
|
|
tile[6] = tile_manager_get_tile (info->src_tiles,
|
|
info->src_x - 1, info->src_y + 1,
|
|
TRUE, FALSE);
|
|
tile[0] = tile_manager_get_tile (info->src_tiles,
|
|
info->src_x - 1, info->src_y - 1,
|
|
TRUE, FALSE);
|
|
|
|
g_return_val_if_fail (tile[4] != NULL, tile_buf);
|
|
|
|
src[4] = tile_data_pointer (tile[4], info->src_x, info->src_y);
|
|
|
|
if (tile[5])
|
|
{
|
|
src[5] = tile_data_pointer (tile[5], info->src_x + 1, info->src_y);
|
|
}
|
|
else
|
|
{
|
|
src[5] = src[4]; /* reusing existing pixel data */
|
|
}
|
|
|
|
if (tile[7])
|
|
{
|
|
src[7] = tile_data_pointer (tile[7], info->src_x, info->src_y + 1);
|
|
}
|
|
else
|
|
{
|
|
src[7] = src[4]; /* reusing existing pixel data */
|
|
}
|
|
|
|
if (tile[1])
|
|
{
|
|
src[1] = tile_data_pointer (tile[1], info->src_x, info->src_y - 1);
|
|
}
|
|
else
|
|
{
|
|
src[1] = src[4]; /* reusing existing pixel data */
|
|
}
|
|
|
|
if (tile[8])
|
|
{
|
|
src[8] = tile_data_pointer (tile[8], info->src_x + 1, info->src_y + 1);
|
|
}
|
|
else
|
|
{
|
|
src[8] = src[5]; /* reusing existing pixel data */
|
|
}
|
|
|
|
if (tile[0])
|
|
{
|
|
src[0] = tile_data_pointer (tile[0], info->src_x - 1, info->src_y - 1);
|
|
}
|
|
else
|
|
{
|
|
src[0] = src[1]; /* reusing existing pixel data */
|
|
}
|
|
|
|
if (tile[2])
|
|
{
|
|
src[2] = tile_data_pointer (tile[2], info->src_x + 1, info->src_y - 1);
|
|
}
|
|
else
|
|
{
|
|
src[2] = src[4]; /* reusing existing pixel data */
|
|
}
|
|
|
|
if (tile[3])
|
|
{
|
|
src[3] = tile_data_pointer (tile[3], info->src_x - 1, info->src_y);
|
|
}
|
|
else
|
|
{
|
|
src[3] = src[4]; /* reusing existing pixel data */
|
|
}
|
|
|
|
if (tile[6])
|
|
{
|
|
src[6] = tile_data_pointer (tile[6], info->src_x - 1, info->src_y + 1);
|
|
}
|
|
else
|
|
{
|
|
src[6] = src[7]; /* reusing existing pixel data */
|
|
}
|
|
|
|
bpp = tile_manager_bpp (info->src_tiles);
|
|
dest = tile_buf;
|
|
|
|
dx = info->dx_start;
|
|
src_x = info->src_x;
|
|
|
|
width = info->w;
|
|
|
|
tilex0 = info->src_x / TILE_WIDTH;
|
|
tilex1 = (info->src_x + 1) / TILE_WIDTH;
|
|
tilexL = (info->src_x - 1) / TILE_WIDTH;
|
|
|
|
do
|
|
{
|
|
/* we're dealing with unsigneds here, be extra careful */
|
|
left_weight = MAX (info->footprint_x / 2,
|
|
info->footshift_x > 0 ? dx << info->footshift_x
|
|
: dx >> -info->footshift_x)
|
|
- (info->footshift_x > 0 ? dx << info->footshift_x
|
|
: dx >> -info->footshift_x);
|
|
|
|
right_weight = MAX (info->footprint_x / 2,
|
|
info->footshift_x > 0 ? dx << info->footshift_x
|
|
: dx >> -info->footshift_x)
|
|
- info->footprint_x / 2;
|
|
|
|
center_weight = info->footprint_x - left_weight - right_weight;
|
|
|
|
if (src_x + 1 >= source_width)
|
|
{
|
|
src[2] = src[1];
|
|
src[5] = src[4];
|
|
src[8] = src[7];
|
|
}
|
|
|
|
if (info->src_y + 1 >= source_height)
|
|
{
|
|
src[6] = src[3];
|
|
src[7] = src[4];
|
|
src[8] = src[5];
|
|
}
|
|
|
|
if (info->src_is_premult)
|
|
box_filter (left_weight, center_weight, right_weight,
|
|
top_weight, middle_weight, bottom_weight,
|
|
src, dest, bpp);
|
|
else
|
|
box_filter_premult (left_weight, center_weight, right_weight,
|
|
top_weight, middle_weight, bottom_weight,
|
|
src, dest, bpp);
|
|
|
|
dest += bpp;
|
|
|
|
dx += info->x_dest_inc;
|
|
skipped = dx / info->x_src_dec;
|
|
|
|
if (skipped)
|
|
{
|
|
dx -= skipped * info->x_src_dec;
|
|
|
|
/* if we changed integer source pixel coordinates in the source
|
|
* buffer, make sure the src pointers (and their backing tiles) are
|
|
* correct
|
|
*/
|
|
|
|
if (src[0] != src[1])
|
|
{
|
|
src[0] += skipped * bpp;
|
|
}
|
|
else
|
|
{
|
|
tilexL =- 1; /* this forces a refetch of the left most source
|
|
samples */
|
|
}
|
|
|
|
if (src[3] != src[4])
|
|
{
|
|
src[3] += skipped * bpp;
|
|
}
|
|
else
|
|
{
|
|
tilexL = -1; /* this forces a refetch of the left most source
|
|
samples */
|
|
}
|
|
|
|
if (src[6] != src[7])
|
|
{
|
|
src[6] += skipped * bpp;
|
|
}
|
|
else
|
|
{
|
|
tilexL = -1; /* this forces a refetch of the left most source
|
|
samples */
|
|
}
|
|
|
|
src[1] += skipped * bpp;
|
|
src[4] += skipped * bpp;
|
|
src[7] += skipped * bpp;
|
|
|
|
src[5] += skipped * bpp;
|
|
src[8] += skipped * bpp;
|
|
src[2] += skipped * bpp;
|
|
|
|
|
|
src_x += skipped;
|
|
|
|
if ((src_x / TILE_WIDTH) != tilex0)
|
|
{
|
|
tile_release (tile[4], FALSE);
|
|
|
|
if (tile[7])
|
|
tile_release (tile[7], FALSE);
|
|
if (tile[1])
|
|
tile_release (tile[1], FALSE);
|
|
|
|
tilex0 += 1;
|
|
|
|
tile[4] = tile_manager_get_tile (info->src_tiles,
|
|
src_x, info->src_y,
|
|
TRUE, FALSE);
|
|
tile[7] = tile_manager_get_tile (info->src_tiles,
|
|
src_x, info->src_y + 1,
|
|
TRUE, FALSE);
|
|
tile[1] = tile_manager_get_tile (info->src_tiles,
|
|
src_x, info->src_y - 1,
|
|
TRUE, FALSE);
|
|
if (! tile[4])
|
|
goto done;
|
|
|
|
src[4] = tile_data_pointer (tile[4], src_x, info->src_y);
|
|
|
|
if (! tile[7])
|
|
{
|
|
src[7] = src[4];
|
|
}
|
|
else
|
|
{
|
|
src[7] = tile_data_pointer (tile[7],
|
|
src_x, info->src_y + 1);
|
|
}
|
|
|
|
if (! tile[1])
|
|
{
|
|
src[1] = src[4];
|
|
}
|
|
else
|
|
{
|
|
src[1] = tile_data_pointer (tile[1],
|
|
src_x, info->src_y - 1);
|
|
}
|
|
}
|
|
|
|
if (((src_x + 1) / TILE_WIDTH) != tilex1)
|
|
{
|
|
if (tile[5])
|
|
tile_release (tile[5], FALSE);
|
|
if (tile[8])
|
|
tile_release (tile[8], FALSE);
|
|
if (tile[2])
|
|
tile_release (tile[2], FALSE);
|
|
|
|
tilex1 += 1;
|
|
|
|
tile[5] = tile_manager_get_tile (info->src_tiles,
|
|
src_x + 1, info->src_y,
|
|
TRUE, FALSE);
|
|
tile[8] = tile_manager_get_tile (info->src_tiles,
|
|
src_x + 1, info->src_y + 1,
|
|
TRUE, FALSE);
|
|
tile[2] = tile_manager_get_tile (info->src_tiles,
|
|
src_x + 1, info->src_y - 1,
|
|
TRUE, FALSE);
|
|
|
|
if (! tile[5])
|
|
{
|
|
src[5] = src[4];
|
|
}
|
|
else
|
|
{
|
|
src[5] = tile_data_pointer (tile[5],
|
|
src_x + 1, info->src_y);
|
|
}
|
|
|
|
if (! tile[8])
|
|
{
|
|
src[8] = src[7];
|
|
}
|
|
else
|
|
{
|
|
src[8] = tile_data_pointer (tile[8],
|
|
src_x + 1, info->src_y + 1);
|
|
}
|
|
|
|
if (! tile[2])
|
|
{
|
|
src[2] = src[1];
|
|
}
|
|
else
|
|
{
|
|
src[2] = tile_data_pointer (tile[2],
|
|
src_x + 1, info->src_y - 1);
|
|
}
|
|
}
|
|
|
|
if (((src_x - 1) / TILE_WIDTH) != tilexL)
|
|
{
|
|
if (tile[0])
|
|
tile_release (tile[0], FALSE);
|
|
if (tile[3])
|
|
tile_release (tile[3], FALSE);
|
|
if (tile[6])
|
|
tile_release (tile[6], FALSE);
|
|
|
|
tilexL += 1;
|
|
|
|
tile[0] = tile_manager_get_tile (info->src_tiles,
|
|
src_x - 1, info->src_y - 1,
|
|
TRUE, FALSE);
|
|
tile[3] = tile_manager_get_tile (info->src_tiles,
|
|
src_x - 1, info->src_y,
|
|
TRUE, FALSE);
|
|
tile[6] = tile_manager_get_tile (info->src_tiles,
|
|
src_x - 1, info->src_y + 1,
|
|
TRUE, FALSE);
|
|
|
|
if (! tile[3])
|
|
{
|
|
src[3] = src[4];
|
|
}
|
|
else
|
|
{
|
|
src[3] = tile_data_pointer (tile[3],
|
|
src_x - 1, info->src_y);
|
|
}
|
|
|
|
if (! tile[6])
|
|
{
|
|
src[6] = src[7];
|
|
}
|
|
else
|
|
{
|
|
src[6] = tile_data_pointer (tile[6],
|
|
src_x - 1, info->src_y + 1);
|
|
}
|
|
|
|
if (! tile[0])
|
|
{
|
|
src[0] = src[1];
|
|
}
|
|
else
|
|
{
|
|
src[0] = tile_data_pointer (tile[0],
|
|
src_x - 1, info->src_y - 1);
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
while (--width);
|
|
|
|
done:
|
|
for (dx = 0; dx < 9; dx++)
|
|
if (tile[dx])
|
|
tile_release (tile[dx], FALSE);
|
|
|
|
return tile_buf;
|
|
}
|
|
|
|
static const guchar *
|
|
render_image_tile_fault_one_row (RenderInfo *info)
|
|
{
|
|
/* NOTE: there are some additional overhead that can be factored out
|
|
* in the tile administration of this fast path
|
|
*/
|
|
|
|
Tile *tile[3];
|
|
|
|
const guchar *src[9];
|
|
|
|
guchar *dest;
|
|
gint width;
|
|
gint tilex0; /* the current x-tile indice used for the middle
|
|
sample pair*/
|
|
gint tilex1; /* the current x-tile indice used for the right
|
|
sample pair */
|
|
gint tilexL; /* the current x-tile indice used for the left
|
|
sample pair */
|
|
gint bpp;
|
|
gint dx;
|
|
gint src_x;
|
|
gint skipped;
|
|
|
|
guint left_weight;
|
|
guint center_weight;
|
|
guint right_weight;
|
|
|
|
guint top_weight;
|
|
guint middle_weight;
|
|
guint bottom_weight;
|
|
|
|
guint source_width;
|
|
|
|
source_width = tile_manager_width (info->src_tiles);
|
|
|
|
top_weight = MAX (info->footprint_y / 2,
|
|
info->footshift_y > 0 ? info->dy << info->footshift_y
|
|
: info->dy >> -info->footshift_y)
|
|
- (info->footshift_y > 0 ? info->dy << info->footshift_y
|
|
: info->dy >> -info->footshift_y);
|
|
|
|
bottom_weight = MAX (info->footprint_y / 2,
|
|
info->footshift_y > 0 ? info->dy << info->footshift_y
|
|
: info->dy >> -info->footshift_y)
|
|
- info->footprint_y / 2;
|
|
|
|
middle_weight = info->footprint_y - top_weight - bottom_weight;
|
|
|
|
tile[0] = tile_manager_get_tile (info->src_tiles,
|
|
info->src_x, info->src_y, TRUE, FALSE);
|
|
|
|
tile[1] = tile_manager_get_tile (info->src_tiles,
|
|
info->src_x + 1, info->src_y, TRUE, FALSE);
|
|
|
|
tile[2] = tile_manager_get_tile (info->src_tiles,
|
|
info->src_x - 1, info->src_y, TRUE, FALSE);
|
|
|
|
g_return_val_if_fail (tile[0] != NULL, tile_buf);
|
|
|
|
src[4] = tile_data_pointer (tile[0], info->src_x, info->src_y);
|
|
src[7] = tile_data_pointer (tile[0], info->src_x, info->src_y + 1);
|
|
|
|
if (tile[1])
|
|
{
|
|
src[5] = tile_data_pointer (tile[1], info->src_x + 1, info->src_y);
|
|
src[8] = tile_data_pointer (tile[1], info->src_x + 1, info->src_y + 1);
|
|
}
|
|
else
|
|
{
|
|
src[5] = src[4]; /* reusing existing pixel data */
|
|
src[8] = src[5]; /* reusing existing pixel data */
|
|
}
|
|
|
|
if (tile[0])
|
|
{
|
|
src[1] = tile_data_pointer (tile[0], info->src_x, info->src_y - 1);
|
|
}
|
|
else
|
|
{
|
|
src[1] = src[4]; /* reusing existing pixel data */
|
|
}
|
|
|
|
if (tile[2])
|
|
{
|
|
src[0] = tile_data_pointer (tile[2], info->src_x - 1, info->src_y - 1);
|
|
}
|
|
else
|
|
{
|
|
src[0] = src[1]; /* reusing existing pixel data */
|
|
}
|
|
|
|
if (tile[1])
|
|
{
|
|
src[2] = tile_data_pointer (tile[1], info->src_x + 1, info->src_y - 1);
|
|
}
|
|
else
|
|
{
|
|
src[2] = src[4]; /* reusing existing pixel data */
|
|
}
|
|
|
|
if (tile[2])
|
|
{
|
|
src[3] = tile_data_pointer (tile[2], info->src_x - 1, info->src_y);
|
|
src[6] = tile_data_pointer (tile[2], info->src_x - 1, info->src_y + 1);
|
|
}
|
|
else
|
|
{
|
|
src[3] = src[4]; /* reusing existing pixel data */
|
|
src[6] = src[7]; /* reusing existing pixel data */
|
|
}
|
|
|
|
bpp = tile_manager_bpp (info->src_tiles);
|
|
dest = tile_buf;
|
|
|
|
dx = info->dx_start;
|
|
src_x = info->src_x;
|
|
|
|
width = info->w;
|
|
|
|
tilex0 = info->src_x / TILE_WIDTH;
|
|
tilex1 = (info->src_x + 1) / TILE_WIDTH;
|
|
tilexL = (info->src_x - 1) / TILE_WIDTH;
|
|
|
|
do
|
|
{
|
|
left_weight = MAX (info->footprint_x / 2,
|
|
info->footshift_x > 0 ? dx << info->footshift_x
|
|
: dx >> -info->footshift_x)
|
|
- (info->footshift_x > 0 ? dx << info->footshift_x
|
|
: dx >> -info->footshift_x);
|
|
|
|
right_weight = MAX (info->footprint_x / 2,
|
|
info->footshift_x > 0 ? dx << info->footshift_x
|
|
: dx >> -info->footshift_x)
|
|
- info->footprint_x / 2;
|
|
|
|
center_weight = info->footprint_x - left_weight - right_weight;
|
|
|
|
if (src_x + 1 >= source_width)
|
|
{
|
|
src[2] = src[1];
|
|
src[5] = src[4];
|
|
src[8] = src[7];
|
|
}
|
|
|
|
if (info->src_is_premult)
|
|
box_filter (left_weight, center_weight, right_weight,
|
|
top_weight, middle_weight, bottom_weight,
|
|
src, dest, bpp);
|
|
else
|
|
box_filter_premult (left_weight, center_weight, right_weight,
|
|
top_weight, middle_weight, bottom_weight,
|
|
src, dest, bpp);
|
|
|
|
dest += bpp;
|
|
|
|
dx += info->x_dest_inc;
|
|
skipped = dx / info->x_src_dec;
|
|
|
|
if (skipped)
|
|
{
|
|
dx -= skipped * info->x_src_dec;
|
|
|
|
/* if we changed integer source pixel coordinates in the source
|
|
* buffer, make sure the src pointers (and their backing tiles) are
|
|
* correct
|
|
*/
|
|
|
|
if (src[0] != src[1])
|
|
{
|
|
src[0] += skipped * bpp;
|
|
}
|
|
else
|
|
{
|
|
tilexL = -1; /* this forces a refetch of the left most source
|
|
samples */
|
|
}
|
|
|
|
if (src[3] != src[4])
|
|
{
|
|
src[3] += skipped * bpp;
|
|
}
|
|
else
|
|
{
|
|
tilexL = -1; /* this forces a refetch of the left most source
|
|
samples */
|
|
}
|
|
|
|
if (src[6] != src[7])
|
|
{
|
|
src[6] += skipped * bpp;
|
|
}
|
|
else
|
|
{
|
|
tilexL = -1; /* this forces a refetch of the left most source
|
|
samples */
|
|
}
|
|
|
|
src[1] += skipped * bpp;
|
|
src[4] += skipped * bpp;
|
|
src[7] += skipped * bpp;
|
|
|
|
src[5] += skipped * bpp;
|
|
src[8] += skipped * bpp;
|
|
src[2] += skipped * bpp;
|
|
|
|
|
|
src_x += skipped;
|
|
|
|
if ((src_x / TILE_WIDTH) != tilex0)
|
|
{
|
|
tile_release (tile[0], FALSE);
|
|
|
|
tilex0 += 1;
|
|
|
|
tile[0] = tile_manager_get_tile (info->src_tiles,
|
|
src_x, info->src_y, TRUE, FALSE);
|
|
if (! tile[0])
|
|
goto done;
|
|
|
|
src[4] = tile_data_pointer (tile[0], src_x, info->src_y);
|
|
src[7] = tile_data_pointer (tile[0], src_x, info->src_y + 1);
|
|
src[1] = tile_data_pointer (tile[0], src_x, info->src_y - 1);
|
|
}
|
|
|
|
if (((src_x + 1) / TILE_WIDTH) != tilex1)
|
|
{
|
|
if (tile[1])
|
|
tile_release (tile[1], FALSE);
|
|
|
|
tilex1 += 1;
|
|
|
|
tile[1] = tile_manager_get_tile (info->src_tiles,
|
|
src_x + 1, info->src_y,
|
|
TRUE, FALSE);
|
|
|
|
if (! tile[1])
|
|
{
|
|
src[5] = src[4];
|
|
src[8] = src[7];
|
|
src[2] = src[1];
|
|
}
|
|
else
|
|
{
|
|
src[5] = tile_data_pointer (tile[1],
|
|
src_x + 1, info->src_y);
|
|
src[8] = tile_data_pointer (tile[1],
|
|
src_x + 1, info->src_y + 1);
|
|
src[2] = tile_data_pointer (tile[1],
|
|
src_x + 1, info->src_y - 1);
|
|
}
|
|
}
|
|
|
|
if (((src_x - 1) / TILE_WIDTH) != tilexL)
|
|
{
|
|
if (tile[2])
|
|
tile_release (tile[2], FALSE);
|
|
|
|
tilexL += 1;
|
|
|
|
tile[2] = tile_manager_get_tile (info->src_tiles,
|
|
src_x - 1, info->src_y,
|
|
TRUE, FALSE);
|
|
|
|
if (! tile[2])
|
|
{
|
|
src[3] = src[4];
|
|
src[6] = src[7];
|
|
src[0] = src[1];
|
|
}
|
|
else
|
|
{
|
|
src[3] = tile_data_pointer (tile[2],
|
|
src_x - 1, info->src_y);
|
|
src[6] = tile_data_pointer (tile[2],
|
|
src_x - 1, info->src_y + 1);
|
|
src[0] = tile_data_pointer (tile[2],
|
|
src_x - 1, info->src_y - 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
while (--width);
|
|
|
|
done:
|
|
for (dx = 0; dx < 3; dx++)
|
|
if (tile[dx])
|
|
tile_release (tile[dx], FALSE);
|
|
|
|
return tile_buf;
|
|
}
|
|
|
|
/* function to render a horizontal line of view data */
|
|
static const guchar *
|
|
render_image_tile_fault_nearest (RenderInfo *info)
|
|
{
|
|
Tile *tile;
|
|
const guchar *src;
|
|
guchar *d;
|
|
gint width;
|
|
gint tilex;
|
|
gint bpp;
|
|
gint src_x;
|
|
gint64 dx;
|
|
|
|
tile = tile_manager_get_tile (info->src_tiles,
|
|
info->src_x, info->src_y, TRUE, FALSE);
|
|
|
|
g_return_val_if_fail (tile != NULL, tile_buf);
|
|
|
|
src = tile_data_pointer (tile, info->src_x, info->src_y);
|
|
|
|
bpp = tile_manager_bpp (info->src_tiles);
|
|
|
|
dx = info->dx_start;
|
|
|
|
width = info->w;
|
|
|
|
src_x = info->src_x;
|
|
tilex = info->src_x / TILE_WIDTH;
|
|
|
|
d = tile_buf;
|
|
|
|
do
|
|
{
|
|
const guchar *s = src;
|
|
gint skipped;
|
|
|
|
if (info->src_is_premult)
|
|
{
|
|
switch (bpp)
|
|
{
|
|
case 4:
|
|
*d++ = *s++;
|
|
case 3:
|
|
*d++ = *s++;
|
|
case 2:
|
|
*d++ = *s++;
|
|
case 1:
|
|
*d++ = *s++;
|
|
}
|
|
}
|
|
else /* pre-multiply */
|
|
{
|
|
switch (bpp)
|
|
{
|
|
case 4:
|
|
d[0] = (s[0] * (s[3] + 1)) >> 8;
|
|
d[1] = (s[1] * (s[3] + 1)) >> 8;
|
|
d[2] = (s[2] * (s[3] + 1)) >> 8;
|
|
d[3] = s[3];
|
|
|
|
d += 4;
|
|
s += 4;
|
|
break;
|
|
|
|
case 3:
|
|
*d++ = *s++;
|
|
*d++ = *s++;
|
|
*d++ = *s++;
|
|
break;
|
|
|
|
case 2:
|
|
d[0] = (s[0] * (s[1] + 1)) >> 8;
|
|
d[1] = s[1];
|
|
|
|
d += 2;
|
|
s += 2;
|
|
break;
|
|
|
|
case 1:
|
|
*d++ = *s++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
dx += info->x_dest_inc;
|
|
skipped = dx / info->x_src_dec;
|
|
|
|
if (skipped)
|
|
{
|
|
src += skipped * bpp;
|
|
src_x += skipped;
|
|
dx -= skipped * info->x_src_dec;
|
|
|
|
if ((src_x / TILE_WIDTH) != tilex)
|
|
{
|
|
tile_release (tile, FALSE);
|
|
tilex += 1;
|
|
|
|
tile = tile_manager_get_tile (info->src_tiles,
|
|
src_x, info->src_y, TRUE, FALSE);
|
|
if (! tile)
|
|
return tile_buf;
|
|
|
|
src = tile_data_pointer (tile, src_x, info->src_y);
|
|
}
|
|
}
|
|
}
|
|
while (--width);
|
|
|
|
tile_release (tile, FALSE);
|
|
|
|
return tile_buf;
|
|
}
|