gimp/app/display/gimpdisplayshell-render.c

1481 lines
44 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
1997-11-25 06:05:25 +08:00
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
1997-11-25 06:05:25 +08:00
* (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/>.
1997-11-25 06:05:25 +08:00
*/
#include "config.h"
#include <gegl.h>
#include <gtk/gtk.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/gimpdrawable.h"
#include "core/gimpimage.h"
#include "core/gimppickable.h"
2004-07-14 00:36:29 +08:00
#include "core/gimpprojection.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"
#include "gimpdisplayshell-scroll.h"
2010-09-29 04:39:50 +08:00
#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)
*/
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
{
2010-09-29 05:02:23 +08:00
TileManager *src_tiles;
const guchar *src;
gboolean src_is_premult;
guchar *dest;
gint x, y;
gint w, h;
gdouble scalex; /* scale from (pre-scaled) src to dest */
gdouble scaley;
gdouble full_scalex; /* actual display scale factor */
gdouble full_scaley;
gint src_x;
gint src_y;
gint dest_bpl;
gint zoom_quality;
/* Bresenham helpers */
2010-09-29 05:02:23 +08:00
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 */
2010-09-29 05:02:23 +08:00
gint y_dest_inc;
gint y_src_dec;
gint64 dy_start;
2010-09-29 05:02:23 +08:00
gint footprint_x;
gint footprint_y;
gint footshift_x;
gint footshift_y;
2010-09-29 05:02:23 +08:00
gint64 dy;
1997-11-25 06:05:25 +08:00
};
static guchar tile_buf[GIMP_DISPLAY_RENDER_BUF_WIDTH * MAX_CHANNELS];
2010-09-29 05:02:23 +08:00
static void gimp_display_shell_render_info_init (RenderInfo *info,
GimpDisplayShell *shell,
gint x,
gint y,
gint w,
gint h,
cairo_surface_t *dest,
TileManager *tiles,
gint level,
gboolean is_premult);
1997-11-25 06:05:25 +08:00
/* Render Image functions */
static void render_image_alpha (RenderInfo *info);
static void render_image_gray_a (RenderInfo *info);
static void render_image_rgb_a (RenderInfo *info);
static const guchar * render_image_tile_fault (RenderInfo *info);
1997-11-25 06:05:25 +08:00
/*****************************************************************/
/* This function is the core of the display -- it offsets and */
1997-11-25 06:05:25 +08:00
/* scales the image according to the current parameters in the */
/* display object. It handles RGBA and GRAYA projection tiles */
/* and renders them to an ARGB32 cairo surface. */
1997-11-25 06:05:25 +08:00
/*****************************************************************/
void
gimp_display_shell_render (GimpDisplayShell *shell,
cairo_t *cr,
gint x,
gint y,
gint w,
gint h)
1997-11-25 06:05:25 +08:00
{
GimpProjection *projection;
GimpImage *image;
TileManager *tiles;
RenderInfo info;
GimpImageType type;
2010-09-29 05:02:23 +08:00
gint level;
gboolean premult;
g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
g_return_if_fail (cr != NULL);
g_return_if_fail (w > 0 && h > 0);
image = gimp_display_get_image (shell->display);
projection = gimp_image_get_projection (image);
1997-11-25 06:05:25 +08:00
2010-09-29 05:02:23 +08:00
/* setup RenderInfo for rendering a GimpProjection level. */
level = gimp_projection_get_level (projection,
shell->scale_x, shell->scale_y);
2010-09-29 05:02:23 +08:00
tiles = gimp_projection_get_tiles_at_level (projection, level, &premult);
2010-09-29 05:02:23 +08:00
gimp_display_shell_render_info_init (&info,
shell, x, y, w, h,
shell->render_surface,
tiles, level, premult);
1997-11-25 06:05:25 +08:00
/* 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 0
if (shell->filter_stack)
gimp_color_display_stack_convert (shell->filter_stack,
shell->render_buf,
w, h,
3,
3 * GIMP_DISPLAY_RENDER_BUF_WIDTH);
#endif
cairo_surface_mark_dirty (shell->render_surface);
if (shell->mask)
{
if (! shell->mask_surface)
{
shell->mask_surface =
cairo_image_surface_create (CAIRO_FORMAT_A8,
GIMP_DISPLAY_RENDER_BUF_WIDTH,
GIMP_DISPLAY_RENDER_BUF_HEIGHT);
}
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_init (&info,
shell, x, y, w, h,
shell->mask_surface,
tiles, 0, FALSE);
render_image_alpha (&info);
cairo_surface_mark_dirty (shell->mask_surface);
}
/* put it to the screen */
{
gint disp_xoffset, disp_yoffset;
cairo_save (cr);
gimp_display_shell_scroll_get_disp_offset (shell,
&disp_xoffset, &disp_yoffset);
cairo_rectangle (cr, x + disp_xoffset, y + disp_yoffset, w, h);
cairo_clip (cr);
cairo_set_source_surface (cr, shell->render_surface,
x + disp_xoffset, y + disp_yoffset);
cairo_paint (cr);
if (shell->mask)
{
/* FIXME: mask_color should be GimpRGB */
switch (shell->mask_color)
{
case GIMP_RED_CHANNEL:
cairo_set_source_rgba (cr, 1, 0, 0, 0.5);
break;
case GIMP_GREEN_CHANNEL:
cairo_set_source_rgba (cr, 0, 1, 0, 0.5);
break;
case GIMP_BLUE_CHANNEL:
cairo_set_source_rgba (cr, 0, 0, 1, 0.5);
break;
default:
g_warn_if_reached ();
return;
}
cairo_mask_surface (cr, shell->mask_surface,
x + disp_xoffset, y + disp_yoffset);
}
cairo_restore (cr);
}
1997-11-25 06:05:25 +08:00
}
/* render a GRAY tile to an A8 cairo surface */
static void
render_image_alpha (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;
2010-09-29 05:02:23 +08:00
guchar *dest = info->dest;
for (x = info->x; x < xe; x++, src++, dest++)
{
*dest = *src;
}
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);
}
}
/* render a GRAYA tile to an ARGB32 cairo surface */
1997-11-25 06:05:25 +08:00
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;
guint32 *dest = (guint32 *) info->dest;
for (x = info->x; x < xe; x++, src += 2, dest++)
{
/* data in src is premultiplied already */
*dest = (src[1] << 24) | (src[0] << 16) | (src[0] << 8) | src[0];
}
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
}
}
/* render an RGBA tile to an ARGB32 cairo surface */
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;
guint32 *dest = (guint32 *) info->dest;
1997-11-25 06:05:25 +08:00
for (x = info->x; x < xe; x++, src += 4, dest++)
{
/* data in src is premultiplied already */
*dest = (src[3] << 24) | (src[0] << 16) | (src[1] << 8) | src[2];
}
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
2010-09-29 05:02:23 +08:00
gimp_display_shell_render_info_init (RenderInfo *info,
GimpDisplayShell *shell,
gint x,
gint y,
gint w,
gint h,
cairo_surface_t *dest,
TileManager *tiles,
gint level,
gboolean is_premult)
1997-11-25 06:05:25 +08:00
{
2010-09-29 05:02:23 +08:00
gint offset_x;
gint offset_y;
gimp_display_shell_scroll_get_render_start_offset (shell,
&offset_x, &offset_y);
info->x = x + offset_x;
info->y = y + offset_y;
info->w = w;
info->h = h;
info->dest = cairo_image_surface_get_data (dest);
info->dest_bpl = cairo_image_surface_get_stride (dest);
info->src_tiles = tiles;
info->src_is_premult = is_premult;
1997-11-25 06:05:25 +08:00
2010-09-29 05:02:23 +08:00
info->scalex = shell->scale_x * (1 << level);
info->scaley = shell->scale_y * (1 << level);
2010-09-29 05:02:23 +08:00
info->full_scalex = shell->scale_x;
info->full_scaley = shell->scale_y;
1997-11-25 06:05:25 +08:00
/* use Bresenham like stepping */
2010-09-29 05:02:23 +08:00
info->x_dest_inc = shell->x_dest_inc;
info->x_src_dec = shell->x_src_dec << level;
2010-09-29 05:02:23 +08:00
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 */
2010-09-29 05:02:23 +08:00
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);
2010-09-29 05:02:23 +08:00
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 ++;
}
2010-09-29 05:02:23 +08:00
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;
}
}
/* 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
{
const 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
};
const 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.
*/
{
const 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
};
const 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 */
2010-09-29 05:02:23 +08:00
|| (info->full_scalex > 1.0 &&
info->full_scaley > 1.0 &&
(! (info->zoom_quality & GIMP_DISPLAY_ZOOM_PIXEL_AA)))
/* or at any point when both scale factors are greater or equal to 200% */
2010-09-29 05:02:23 +08:00
|| (info->full_scalex >= 2.0 &&
info->full_scaley >= 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;
}