gimp/app/display/gimpdisplayshell-render.c

1656 lines
49 KiB
C
Raw Normal View History

/* GIMP - The GNU Image Manipulation Program
1997-11-25 06:05:25 +08:00
* 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
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
1997-11-25 06:05:25 +08:00
*/
#include "config.h"
1997-11-25 06:05:25 +08:00
#include <string.h>
#include <gtk/gtk.h>
#include "libgimpbase/gimpbase.h"
#include "libgimpmath/gimpmath.h"
Ported module loading to GTypeModule, getting rid of all own module 2002-10-20 Michael Natterer <mitch@gimp.org> Ported module loading to GTypeModule, getting rid of all own module registering/bookkeeping stuff for color selectors and display filters. The modules now simply register GimpColorSelector and GimpColorDisplay subclasses, the list of registered subclasses can then be obtained calling g_type_children() on the abstract base classes. This is work in progress and just the first working state after I started breaking everything... * app/gui/color-select.[ch] * libgimp/gimpcolordisplay.h * libgimp/gimpcolorselector.h: removed. * app/gui/Makefile.am * libgimp/Makefile.am: changed accordingly. * libgimp/gimpmodule.h: massively simplified. All voodoo is gone. * libgimpwidgets/gimpcolordisplay.[ch] * libgimpwidgets/gimpcolorselector.[ch]: new abstract base classes which need to be subclassed by modules. * libgimpwidgets/gimpcolorselect.[ch]: the built-in color selector from app/gui/color-select.* ported to be a GimpColorSelector subclass. * libgimpwidgets/Makefile.am * libgimpwidgets/gimpwidgets.h * libgimpwidgets/gimpwidgetsmarshal.list * libgimpwidgets/gimpwidgetstypes.h: changed accordingly. * app/core/gimpmoduleinfo.[ch]: made it a GTypeModule subclass * app/core/gimpmodules.c: changed accordingly. * app/core/gimpcontainer.c * app/core/gimplist.c: HACKED around to allow GimpLists of GObjects (not GimpObjects). This is EEKy, so I will either make gimp->modules a simple GList and revert this bit of change, or allow GObjects all over the place in GimpContainer land... * app/display/gimpdisplayshell-filter.[ch] * app/gui/color-notebook.c: removed all module stuff and use g_type_children() to get the list of available color_selectors and display_filters. * app/display/gimpdisplayshell-filter-dialog.c * app/display/gimpdisplayshell-render.c * app/gui/module-browser.c: changed accordingly. * app/gui/gui.c: ref the built-in color selector's class before the modules are queried so it appears first in the list of GimpColorSelector's children. * modules/Makefile.am: build the water color selector again. * modules/cdisplay_gamma.c * modules/cdisplay_highcontrast.c * modules/colorsel_triangle.c * modules/colorsel_water.c: ported them all to the new API. * modules/gimpmodregister.[ch]: removed the old EMX module hack.
2002-10-20 18:14:17 +08:00
#include "libgimpwidgets/gimpwidgets.h"
#include "display-types.h"
new directory app/base/ 2001-05-15 Michael Natterer <mitch@gimp.org> * configure.in: new directory app/base/ * app/Makefile.am * app/boundary.[ch] * app/brush_scale.[ch] * app/gimpchecks.h * app/gimplut.[ch] * app/pixel_processor.[ch] * app/pixel_region.[ch] * app/pixel_surround.[ch] * app/temp_buf.[ch] * app/tile.[ch] * app/tile_cache.[ch] * app/tile_manager.[ch] * app/tile_manager_pvt.h * app/tile_pvt.h * app/tile_swap.[ch]: moved to base/ * app/base/Makefile.am * app/base/base-types.h * app/base/*: new directory for the sub-object pixel maniplation and storage stuff. Does not include Gtk+ or anything outside base/. Did some cleanup in all files. * app/appenums.h * app/apptypes.h * app/core/gimpimage.h: removed types which are now in base/base-types.h. * app/base/base-config.[ch] * app/gimprc.[ch]: put the config variables for base/ to their own file so base/ doesn not have to include gimprc.h (does not yet work, i.e. the variables are un-configurable right now) * app/main.c: set a log handler for "Gimp-Base". * app/paint-funcs/Makefile.am * app/paint-funcs/paint-funcs.[ch]: removed the color hash which maps RGB to color indices because it's a totally standalone system which has nothing to do with the paint-funcs and introduced a GimpImage dependency. paint-funcs/ should be considered on the same sub-object (glib-only) level as base/, only in a different directory. * app/core/Makefile.am * app/core/gimpimage-colorhash.[ch]: put the color hash here. * app/gimage.c: don't invalidate the color hash here... * app/core/gimpimage.c: ... but in the colormap_changed() default inplementation. Initialize the hash in class_init(). * tools/pdbgen/Makefile.am: scan app/base/base-types.h for enums. * tools/pdbgen/enums.pl: regenerated. * app/[lots] * app/core/[of] * app/gui/[files] * app/pdb/[all] * app/tools/[over] * app/widgets/[the] * tools/pdbgen/pdb/[place]: changed #includes accordingly. And use base_config->value instead of the stuff from gimprc.h.
2001-05-15 19:25:25 +08:00
#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"
2004-07-14 00:36:29 +08:00
#include "core/gimpprojection.h"
#include "gimpcanvas.h"
#include "gimpdisplay.h"
2001-11-01 05:20:09 +08:00
#include "gimpdisplayshell.h"
#include "gimpdisplayshell-filter.h"
app/display/Makefile.am app/display/gimpdisplay-callbacks.[ch] 2001-11-01 Michael Natterer <mitch@gimp.org> * app/display/Makefile.am * app/display/gimpdisplay-callbacks.[ch] * app/display/gimpdisplay-render.[ch] * app/display/gimpdisplay-scale.[ch] * app/display/gimpdisplay-scroll.[ch]: removed and added as gimpdisplayshell-foo.[ch] because they are all methods of the shell. * app/display/gimpdisplay.[ch] * app/display/gimpdisplayshell.[ch]: moved the "offset" and "size" variables from GimpDisplay to GimpDisplayShell. GimpDisplay should know nothing about screen coordinates. The gdisplay_[un]transform_foo() methods are still part of GimpDisplay but will be moved to GimpDisplayShell as soon as the tools' vitrual functions speak in image coordinates instead of GdkEvents. * app/display/gimpdisplayshell-callbacks.[ch]: prefixed all functions with gimp_display_shell_*. Moved some stuff to a "realize" callback File still has to be renamed. * app/display/gimpdisplay-foreach.[ch]: removed gdisplays_shrink_wrap(). * app/gui/menus.c * app/gui/view-commands.[ch] * app/display/gimpdisplayshell-scale.[ch]: implemented "Zoom to Fit Window" function (#57670). * app/nav_window.c * app/display/gimpdisplay-handlers.c * app/display/gimpdisplayshell-render.[ch] * app/display/gimpdisplayshell-scale.[ch] * app/display/gimpdisplayshell-scroll.[ch] * app/gui/colormap-dialog.c * app/gui/gui.c * app/gui/preferences-dialog.c * app/tools/gimpmagnifytool.c * app/tools/gimpmovetool.c * app/widgets/gimppreview.c: changed according to variable and filename changes. * app/tools/tool_manager.c: tool_manager_select_tool(): send the active tool a "HALT" command before selecting the new one. Fixes stale tool dialogs which were there because some other hack was removed (This is IMHO the right place to shut down the active tool). * app/tools/gimpcroptool.c: don't shrink wrap after cropping but let gimprc.allow_resize_windows decide. * app/tools/gimpselectiontool.c: gimage_mask_value() takes image, not screen coordinates. A good example of how braindead it is to pass GdkEvents to tools :-) Fixes incorrect cursor and oper update of the selection tools. * app/tools/gimptransformtool.c * app/undo.c: removed (#if 0 for now) some strange code which did manual exposing of GimpDisplayShell areas. This was definitely a hack and should not be there given the image emits correct "update" signals.
2001-11-02 17:31:21 +08:00
#include "gimpdisplayshell-render.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)
*/
#define GIMP_DISPLAY_RENDER_BUF_WIDTH 256
1997-11-25 06:05:25 +08:00
typedef struct _RenderInfo RenderInfo;
typedef void (* RenderFunc) (RenderInfo *info);
1997-11-25 06:05:25 +08:00
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;
1997-11-25 06:05:25 +08:00
};
static void gimp_display_shell_render_info_scale (RenderInfo *info,
GimpDisplayShell *shell,
TileManager *tiles,
gint level,
gboolean is_premult);
1997-11-25 06:05:25 +08:00
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;
1997-11-25 06:05:25 +08:00
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)
1997-11-25 06:05:25 +08:00
{
GimpCheckSize check_size;
GimpCheckType check_type;
g_object_get (config,
"transparency-size", &check_size,
"transparency-type", &check_type,
NULL);
1997-11-25 06:05:25 +08:00
gimp_checks_get_shades (check_type, &check_light, &check_dark);
1997-11-25 06:05:25 +08:00
switch (check_size)
{
case GIMP_CHECK_SIZE_SMALL_CHECKS:
check_mod = 0x3;
1997-11-25 06:05:25 +08:00
check_shift = 2;
break;
case GIMP_CHECK_SIZE_MEDIUM_CHECKS:
check_mod = 0x7;
1997-11-25 06:05:25 +08:00
check_shift = 3;
break;
case GIMP_CHECK_SIZE_LARGE_CHECKS:
check_mod = 0xf;
1997-11-25 06:05:25 +08:00
check_shift = 4;
break;
}
}
1997-11-25 06:05:25 +08:00
/* 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);
1997-11-25 06:05:25 +08:00
static void gimp_display_shell_render_highlight (GimpDisplayShell *shell,
gint x,
gint y,
gint w,
gint h,
GdkRectangle *highlight);
static void gimp_display_shell_render_mask (GimpDisplayShell *shell,
RenderInfo *info);
1997-11-25 06:05:25 +08:00
/*****************************************************************/
/* 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 */
1997-11-25 06:05:25 +08:00
/* & 32 bit output depths. */
/*****************************************************************/
void
gimp_display_shell_render (GimpDisplayShell *shell,
gint x,
gint y,
gint w,
gint h,
GdkRectangle *highlight)
1997-11-25 06:05:25 +08:00
{
GimpProjection *projection;
GimpImage *image;
RenderInfo info;
GimpImageType type;
g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
g_return_if_fail (w > 0 && h > 0);
image = shell->display->image;
projection = image->projection;
1997-11-25 06:05:25 +08:00
/* Initialize RenderInfo with values that don't change during the
* call of this function.
*/
info.shell = shell;
info.x = x + shell->offset_x;
info.y = y + shell->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 (GIMP_DISPLAY_CONFIG (image->gimp->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);
}
1997-11-25 06:05:25 +08:00
/* Currently, only RGBA and GRAYA projection types are used. */
type = gimp_projection_get_image_type (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 */
gimp_canvas_draw_rgb (GIMP_CANVAS (shell->canvas), GIMP_CANVAS_STYLE_RENDER,
x + shell->disp_xoffset, y + shell->disp_yoffset,
w, h,
shell->render_buf,
3 * GIMP_DISPLAY_RENDER_BUF_WIDTH,
shell->offset_x, shell->offset_y);
1997-11-25 06:05:25 +08:00
}
#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,
GdkRectangle *highlight)
{
guchar *buf = shell->render_buf;
GdkRectangle rect;
rect.x = shell->offset_x + x;
rect.y = shell->offset_y + y;
rect.width = w;
rect.height = h;
if (gdk_rectangle_intersect (highlight, &rect, &rect))
{
rect.x -= shell->offset_x + x;
rect.y -= shell->offset_y + 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);
}
}
1997-11-25 06:05:25 +08:00
/*************************/
/* 8 Bit functions */
/*************************/
static void
render_image_gray_a (RenderInfo *info)
1997-11-25 06:05:25 +08:00
{
gint y, ye;
gint x, xe;
1997-11-25 06:05:25 +08:00
y = info->y;
1997-11-25 06:05:25 +08:00
ye = info->y + info->h;
xe = info->x + info->w;
info->dy = info->dy_start;
1997-11-25 06:05:25 +08:00
info->src = render_image_tile_fault (info);
while (TRUE)
1997-11-25 06:05:25 +08:00
{
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;
2000-10-26 07:14:11 +08:00
dest[0] = dest[1] = dest[2] = v;
if (((x + 1) & check_mod) == 0)
dark_light += 1;
}
1997-11-25 06:05:25 +08:00
if (++y == ye)
break;
1997-11-25 06:05:25 +08:00
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);
1997-11-25 06:05:25 +08:00
}
}
static void
render_image_rgb_a (RenderInfo *info)
1997-11-25 06:05:25 +08:00
{
gint y, ye;
gint x, xe;
1997-11-25 06:05:25 +08:00
y = info->y;
2000-10-26 07:14:11 +08:00
ye = info->y + info->h;
xe = info->x + info->w;
info->dy = info->dy_start;
1997-11-25 06:05:25 +08:00
info->src = render_image_tile_fault (info);
while (TRUE)
1997-11-25 06:05:25 +08:00
{
const guchar *src = info->src;
guchar *dest = info->dest;
guint dark_light;
1997-11-25 06:05:25 +08:00
dark_light = (y >> check_shift) + (info->x >> check_shift);
1997-11-25 06:05:25 +08:00
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;
}
1997-11-25 06:05:25 +08:00
if (++y == ye)
break;
1997-11-25 06:05:25 +08:00
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);
1997-11-25 06:05:25 +08:00
}
}
static void
gimp_display_shell_render_info_scale (RenderInfo *info,
GimpDisplayShell *shell,
TileManager *tiles,
gint level,
gboolean is_premult)
1997-11-25 06:05:25 +08:00
{
info->src_tiles = tiles;
info->src_is_premult = is_premult;
1997-11-25 06:05:25 +08:00
/* 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);
1997-11-25 06:05:25 +08:00
/* 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 *
1997-11-25 06:05:25 +08:00
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;
}