/* 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 . */ #include "config.h" #include #include #include "libgimpmath/gimpmath.h" #include "libgimpwidgets/gimpwidgets.h" #include "display-types.h" #include "tools/tools-types.h" #include "core/gimpchannel.h" #include "core/gimpdrawable.h" #include "core/gimpimage.h" #include "base/tile-manager.h" #include "tools/gimpperspectivetool.h" #include "tools/gimptransformtool.h" #include "tools/tool_manager.h" #include "gimpdisplay.h" #include "gimpdisplayshell.h" #include "gimpdisplayshell-appearance.h" #include "gimpdisplayshell-preview.h" #include "gimpdisplayshell-transform.h" #define INT_MULT(a,b,t) ((t) = (a) * (b) + 0x80, ((((t) >> 8) + (t)) >> 8)) #define INT_MULT3(a,b,c,t) ((t) = (a) * (b) * (c) + 0x7F5B, \ ((((t) >> 7) + (t)) >> 16)) #define MAX_SUB_COLS 6 /* number of columns and */ #define MAX_SUB_ROWS 6 /* rows to use in perspective preview subdivision */ /* local function prototypes */ static void gimp_display_shell_draw_quad (GimpDrawable *texture, cairo_t *cr, GimpChannel *mask, gint mask_offx, gint mask_offy, gint *x, gint *y, gfloat *u, gfloat *v, guchar opacity); static void gimp_display_shell_draw_tri (GimpDrawable *texture, cairo_t *cr, cairo_surface_t *area, gint area_offx, gint area_offy, GimpChannel *mask, gint mask_offx, gint mask_offy, gint *x, gint *y, gfloat *u, gfloat *v, guchar opacity); static void gimp_display_shell_draw_tri_row (GimpDrawable *texture, cairo_t *cr, cairo_surface_t *area, gint area_offx, gint area_offy, gint x1, gfloat u1, gfloat v1, gint x2, gfloat u2, gfloat v2, gint y, guchar opacity); static void gimp_display_shell_draw_tri_row_mask (GimpDrawable *texture, cairo_t *cr, cairo_surface_t *area, gint area_offx, gint area_offy, GimpChannel *mask, gint mask_offx, gint mask_offy, gint x1, gfloat u1, gfloat v1, gint x2, gfloat u2, gfloat v2, gint y, guchar opacity); static void gimp_display_shell_trace_tri_edge (gint *dest, gint x1, gint y1, gint x2, gint y2); /* public functions */ /** * gimp_display_shell_preview_transform: * @shell: the #GimpDisplayShell * * If the active tool as reported by tool_manager_get_active() is a * #GimpTransformTool and the tool has a valid drawable, and the tool * has use_grid true (which, incidentally, is not the same thing as * the preview type), and the area of the transformed preview is * convex, then proceed with drawing the preview. * * The preview area is divided into 1 or more quadrilaterals, and * drawn with gimp_display_shell_draw_quad(), which in turn breaks it * down into 2 triangles, and draws row by row. If the tool is the * Perspective tool, then more small quadrilaterals are used to * compensate for the little rectangles not being the same size. In * other words, all the transform tools are affine transformations * except perspective, so approximate it with a few subdivisions. **/ void gimp_display_shell_preview_transform (GimpDisplayShell *shell, cairo_t *cr) { GimpTool *tool; GimpTransformTool *tr_tool; gdouble z1, z2, z3, z4; GimpChannel *mask; gint mask_x1, mask_y1; gint mask_x2, mask_y2; gint mask_offx, mask_offy; gint columns, rows; gint j, k, sub; /* x and y get filled with the screen coordinates of each corner of * each quadrilateral subdivision of the transformed area. u and v * are the corresponding points in the mask */ gfloat du, dv, dx, dy; gint x[MAX_SUB_COLS * MAX_SUB_ROWS][4], y[MAX_SUB_COLS * MAX_SUB_ROWS][4]; gfloat u[MAX_SUB_COLS * MAX_SUB_ROWS][4], v[MAX_SUB_COLS * MAX_SUB_ROWS][4]; guchar opacity = 255; g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); g_return_if_fail (cr != NULL); if (! gimp_display_shell_get_show_transform (shell) || ! shell->canvas) return; tool = tool_manager_get_active (shell->display->gimp); if (! GIMP_IS_TRANSFORM_TOOL (tool) || ! GIMP_IS_DRAWABLE (tool->drawable)) return; tr_tool = GIMP_TRANSFORM_TOOL (tool); if (! tr_tool->use_grid) return; z1 = ((tr_tool->tx2 - tr_tool->tx1) * (tr_tool->ty4 - tr_tool->ty1) - (tr_tool->tx4 - tr_tool->tx1) * (tr_tool->ty2 - tr_tool->ty1)); z2 = ((tr_tool->tx4 - tr_tool->tx1) * (tr_tool->ty3 - tr_tool->ty1) - (tr_tool->tx3 - tr_tool->tx1) * (tr_tool->ty4 - tr_tool->ty1)); z3 = ((tr_tool->tx4 - tr_tool->tx2) * (tr_tool->ty3 - tr_tool->ty2) - (tr_tool->tx3 - tr_tool->tx2) * (tr_tool->ty4 - tr_tool->ty2)); z4 = ((tr_tool->tx3 - tr_tool->tx2) * (tr_tool->ty1 - tr_tool->ty2) - (tr_tool->tx1 - tr_tool->tx2) * (tr_tool->ty3 - tr_tool->ty2)); /* only draw convex polygons */ if (! ((z1 * z2 > 0) && (z3 * z4 > 0))) return; /* take opacity from the tool options */ { gdouble value; g_object_get (gimp_tool_get_options (tool), "preview-opacity", &value, NULL); opacity = value * 255.999; } mask = NULL; mask_offx = mask_offy = 0; if (gimp_item_mask_bounds (GIMP_ITEM (tool->drawable), &mask_x1, &mask_y1, &mask_x2, &mask_y2)) { mask = gimp_image_get_mask (gimp_display_get_image (shell->display)); gimp_item_get_offset (GIMP_ITEM (tool->drawable), &mask_offx, &mask_offy); } if (GIMP_IS_PERSPECTIVE_TOOL (tr_tool)) { /* approximate perspective transform by subdivision * * increase number of columns/rows to increase precision */ columns = MAX_SUB_COLS; rows = MAX_SUB_ROWS; } else { /* for affine transforms subdivision has no effect */ columns = 1; rows = 1; } dx = (tr_tool->x2 - tr_tool->x1) / ((gfloat) columns); dy = (tr_tool->y2 - tr_tool->y1) / ((gfloat) rows); du = (mask_x2 - mask_x1) / ((gfloat) columns); dv = (mask_y2 - mask_y1) / ((gfloat) rows); #define CALC_VERTEX(col, row, sub, index) \ { \ gdouble tx1, ty1; \ gdouble tx2, ty2; \ \ u[sub][index] = tr_tool->x1 + (dx * (col + (index & 1))); \ v[sub][index] = tr_tool->y1 + (dy * (row + (index >> 1))); \ \ gimp_matrix3_transform_point (&tr_tool->transform, \ u[sub][index], v[sub][index], \ &tx1, &ty1); \ \ gimp_display_shell_transform_xy_f (shell, \ tx1, ty1, \ &tx2, &ty2); \ x[sub][index] = (gint) tx2; \ y[sub][index] = (gint) ty2; \ \ u[sub][index] = mask_x1 + (du * (col + (index & 1))); \ v[sub][index] = mask_y1 + (dv * (row + (index >> 1))); \ } #define COPY_VERTEX(subdest, idest, subsrc, isrc) \ x[subdest][idest] = x[subsrc][isrc]; \ y[subdest][idest] = y[subsrc][isrc]; \ u[subdest][idest] = u[subsrc][isrc]; \ v[subdest][idest] = v[subsrc][isrc]; /* * upper left corner subdivision: calculate all vertices */ for (j = 0; j < 4; j++) { CALC_VERTEX (0, 0, 0, j); } /* * top row subdivisions: calculate only right side vertices */ for (j = 1; j < columns; j++) { COPY_VERTEX (j, 0, j - 1, 1); CALC_VERTEX (j, 0, j, 1); COPY_VERTEX (j, 2, j - 1, 3); CALC_VERTEX (j, 0, j, 3); } /* * left column subdivisions: calculate only bottom side vertices */ for (j = 1, sub = columns; j < rows; j++, sub += columns) { COPY_VERTEX (sub, 0, sub - columns, 2); COPY_VERTEX (sub, 1, sub - columns, 3); CALC_VERTEX (0, j, sub, 2); CALC_VERTEX (0, j, sub, 3); } /* * the rest: calculate only the bottom-right vertex */ for (j = 1, sub = columns; j < rows; j++) { sub++; for (k = 1; k < columns; k++, sub++) { COPY_VERTEX (sub, 0, sub - 1, 1); COPY_VERTEX (sub, 1, sub - columns, 3); COPY_VERTEX (sub, 2, sub - 1, 3); CALC_VERTEX (k, j, sub, 3); } } #undef CALC_VERTEX #undef COPY_VERTEX k = columns * rows; for (j = 0; j < k; j++) gimp_display_shell_draw_quad (tool->drawable, cr, mask, mask_offx, mask_offy, x[j], y[j], u[j], v[j], opacity); } /* private functions */ /** * gimp_display_shell_draw_quad: * @texture: the #GimpDrawable to be previewed * @cr: the #cairo_t to draw to * @mask: a #GimpChannel * @opacity: the opacity of the preview * * Take a quadrilateral, divide it into two triangles, and draw those * with gimp_display_shell_draw_tri(). **/ static void gimp_display_shell_draw_quad (GimpDrawable *texture, cairo_t *cr, GimpChannel *mask, gint mask_offx, gint mask_offy, gint *x, gint *y, gfloat *u, gfloat *v, guchar opacity) { gint x2[3], y2[3]; gfloat u2[3], v2[3]; gint minx, maxx, miny, maxy; /* screen bounds of the quad */ gdouble clip_x1, clip_y1, clip_x2, clip_y2; gint c; x2[0] = x[3]; y2[0] = y[3]; u2[0] = u[3]; v2[0] = v[3]; x2[1] = x[2]; y2[1] = y[2]; u2[1] = u[2]; v2[1] = v[2]; x2[2] = x[1]; y2[2] = y[1]; u2[2] = u[1]; v2[2] = v[1]; /* Allocate a box around the quad to compute preview data into * and fill it with the original window contents. */ cairo_clip_extents (cr, &clip_x1, &clip_y1, &clip_x2, &clip_y2); /* find bounds of quad in order to only grab as much of dest as needed */ minx = maxx = x[0]; miny = maxy = y[0]; for (c = 1; c < 4; c++) { if (x[c] < minx) minx = x[c]; else if (x[c] > maxx) maxx = x[c]; if (y[c] < miny) miny = y[c]; else if (y[c] > maxy) maxy = y[c]; } if (minx < clip_x1) minx = clip_x1; if (miny < clip_y1) miny = clip_y1; if (maxx > clip_x2) maxx = clip_x2; if (maxy > clip_y2) maxy = clip_y2; if (minx <= maxx && miny <= maxy) { cairo_surface_t *area; area = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, maxx - minx + 1, maxy - miny + 1); g_return_if_fail (area != NULL); gimp_display_shell_draw_tri (texture, cr, area, minx, miny, mask, mask_offx, mask_offy, x, y, u, v, opacity); gimp_display_shell_draw_tri (texture, cr, area, minx, miny, mask, mask_offx, mask_offy, x2, y2, u2, v2, opacity); cairo_surface_destroy (area); } } /** * gimp_display_shell_draw_tri: * @texture: the thing being transformed * @cr: the #cairo_t to draw to * @area: has prefetched pixel data of dest * @area_offx: x coordinate of area in dest * @area_offy: y coordinate of area in dest * @x: Array of the three x coords of triangle * @y: Array of the three y coords of triangle * * This draws a triangle onto dest by breaking it down into pixel rows, and * then calling gimp_display_shell_draw_tri_row() and * gimp_display_shell_draw_tri_row_mask() to do the actual pixel changing. **/ static void gimp_display_shell_draw_tri (GimpDrawable *texture, cairo_t *cr, cairo_surface_t *area, gint area_offx, gint area_offy, GimpChannel *mask, gint mask_offx, gint mask_offy, gint *x, gint *y, gfloat *u, /* texture coords */ gfloat *v, /* 0.0 ... tex width, height */ guchar opacity) { gdouble clip_x1, clip_y1, clip_x2, clip_y2; gint j, k; gint ry; gint *l_edge, *r_edge; /* arrays holding x-coords of edge pixels */ gint *left, *right; /* temp pointers into l_edge and r_edge */ gfloat dul, dvl, dur, dvr; /* left and right texture coord deltas */ gfloat u_l, v_l, u_r, v_r; /* left and right texture coord pairs */ g_return_if_fail (GIMP_IS_DRAWABLE (texture)); g_return_if_fail (area != NULL); g_return_if_fail (x != NULL && y != NULL && u != NULL && v != NULL); left = right = NULL; dul = dvl = dur = dvr = 0; u_l = v_l = u_r = v_r = 0; cairo_clip_extents (cr, &clip_x1, &clip_y1, &clip_x2, &clip_y2); /* sort vertices in order of y-coordinate */ for (j = 0; j < 3; j++) for (k = j + 1; k < 3; k++) if (y[k] < y[j]) { gint tmp; gfloat ftmp; tmp = y[k]; y[k] = y[j]; y[j] = tmp; tmp = x[k]; x[k] = x[j]; x[j] = tmp; ftmp = u[k]; u[k] = u[j]; u[j] = ftmp; ftmp = v[k]; v[k] = v[j]; v[j] = ftmp; } if (y[2] == y[0]) return; l_edge = g_new (gint, y[2] - y[0]); r_edge = g_new (gint, y[2] - y[0]); /* draw the triangle */ gimp_display_shell_trace_tri_edge (l_edge, x[0], y[0], x[2], y[2]); left = l_edge; dul = (u[2] - u[0]) / (y[2] - y[0]); dvl = (v[2] - v[0]) / (y[2] - y[0]); u_l = u[0]; v_l = v[0]; if (y[0] != y[1]) { gimp_display_shell_trace_tri_edge (r_edge, x[0], y[0], x[1], y[1]); right = r_edge; dur = (u[1] - u[0]) / (y[1] - y[0]); dvr = (v[1] - v[0]) / (y[1] - y[0]); u_r = u[0]; v_r = v[0]; if (mask) for (ry = y[0]; ry < y[1]; ry++) { if (ry >= clip_y1 && ry < clip_y2) gimp_display_shell_draw_tri_row_mask (texture, cr, area, area_offx, area_offy, mask, mask_offx, mask_offy, *left, u_l, v_l, *right, u_r, v_r, ry, opacity); left ++; right ++; u_l += dul; v_l += dvl; u_r += dur; v_r += dvr; } else for (ry = y[0]; ry < y[1]; ry++) { if (ry >= clip_y1 && ry < clip_y2) gimp_display_shell_draw_tri_row (texture, cr, area, area_offx, area_offy, *left, u_l, v_l, *right, u_r, v_r, ry, opacity); left ++; right ++; u_l += dul; v_l += dvl; u_r += dur; v_r += dvr; } } if (y[1] != y[2]) { gimp_display_shell_trace_tri_edge (r_edge, x[1], y[1], x[2], y[2]); right = r_edge; dur = (u[2] - u[1]) / (y[2] - y[1]); dvr = (v[2] - v[1]) / (y[2] - y[1]); u_r = u[1]; v_r = v[1]; if (mask) for (ry = y[1]; ry < y[2]; ry++) { if (ry >= clip_y1 && ry < clip_y2) gimp_display_shell_draw_tri_row_mask (texture, cr, area, area_offx, area_offy, mask, mask_offx, mask_offy, *left, u_l, v_l, *right, u_r, v_r, ry, opacity); left ++; right ++; u_l += dul; v_l += dvl; u_r += dur; v_r += dvr; } else for (ry = y[1]; ry < y[2]; ry++) { if (ry >= clip_y1 && ry < clip_y2) gimp_display_shell_draw_tri_row (texture, cr, area, area_offx, area_offy, *left, u_l, v_l, *right, u_r, v_r, ry, opacity); left ++; right ++; u_l += dul; v_l += dvl; u_r += dur; v_r += dvr; } } g_free (l_edge); g_free (r_edge); } /** * gimp_display_shell_draw_tri_row: * @texture: the thing being transformed * @cr: the #cairo_t to draw to * @area: has prefetched pixel data of dest * * Called from gimp_display_shell_draw_tri(), this draws a single row of a * triangle onto dest when there is not a mask. The run (x1,y) to (x2,y) in * dest corresponds to the run (u1,v1) to (u2,v2) in texture. **/ static void gimp_display_shell_draw_tri_row (GimpDrawable *texture, cairo_t *cr, cairo_surface_t *area, gint area_offx, gint area_offy, gint x1, gfloat u1, gfloat v1, gint x2, gfloat u2, gfloat v2, gint y, guchar opacity) { TileManager *tiles; /* used to get the source texture colors */ guchar *pptr; /* points into the pixels of a row of area */ gfloat u, v; gfloat du, dv; gint dx; guchar pixel[4]; const guchar *cmap; gint offset; if (x2 == x1) return; g_return_if_fail (GIMP_IS_DRAWABLE (texture)); g_return_if_fail (area != NULL); g_return_if_fail (cairo_image_surface_get_format (area) == CAIRO_FORMAT_ARGB32); /* make sure the pixel run goes in the positive direction */ if (x1 > x2) { gint tmp; gfloat ftmp; tmp = x2; x2 = x1; x1 = tmp; ftmp = u2; u2 = u1; u1 = ftmp; ftmp = v2; v2 = v1; v1 = ftmp; } u = u1; v = v1; du = (u2 - u1) / (x2 - x1); dv = (v2 - v1) / (x2 - x1); /* don't calculate unseen pixels */ if (x1 < area_offx) { u += du * (area_offx - x1); v += dv * (area_offx - x1); x1 = area_offx; } else if (x1 > area_offx + cairo_image_surface_get_width (area) - 1) { return; } if (x2 < area_offx) { return; } else if (x2 > area_offx + cairo_image_surface_get_width (area) - 1) { x2 = area_offx + cairo_image_surface_get_width (area) - 1; } dx = x2 - x1; if (! dx) return; pptr = (cairo_image_surface_get_data (area) + (y - area_offy) * cairo_image_surface_get_stride (area) + (x1 - area_offx) * 4); tiles = gimp_drawable_get_tiles (texture); switch (gimp_drawable_type (texture)) { case GIMP_INDEXED_IMAGE: cmap = gimp_drawable_get_colormap (texture); while (dx--) { read_pixel_data_1 (tiles, (gint) u, (gint) v, pixel); offset = pixel[0] + pixel[0] + pixel[0]; GIMP_CAIRO_ARGB32_SET_PIXEL (pptr, cmap[offset + 0], cmap[offset + 1], cmap[offset + 2], opacity); pptr += 4; u += du; v += dv; } break; case GIMP_INDEXEDA_IMAGE: cmap = gimp_drawable_get_colormap (texture); while (dx--) { register gulong tmp; guchar alpha; read_pixel_data_1 (tiles, (gint) u, (gint) v, pixel); offset = pixel[0] + pixel[0] + pixel[0]; alpha = INT_MULT (opacity, pixel[1], tmp); GIMP_CAIRO_ARGB32_SET_PIXEL (pptr, cmap[offset + 0], cmap[offset + 1], cmap[offset + 2], alpha); pptr += 4; u += du; v += dv; } break; case GIMP_GRAY_IMAGE: while (dx--) { read_pixel_data_1 (tiles, (gint) u, (gint) v, pixel); GIMP_CAIRO_ARGB32_SET_PIXEL (pptr, pixel[0], pixel[0], pixel[0], opacity); pptr += 4; u += du; v += dv; } break; case GIMP_GRAYA_IMAGE: while (dx--) { register gulong tmp; guchar alpha; read_pixel_data_1 (tiles, (gint) u, (gint) v, pixel); alpha = INT_MULT (opacity, pixel[1], tmp); GIMP_CAIRO_ARGB32_SET_PIXEL (pptr, pixel[0], pixel[0], pixel[0], alpha); pptr += 4; u += du; v += dv; } break; case GIMP_RGB_IMAGE: while (dx--) { read_pixel_data_1 (tiles, (gint) u, (gint) v, pixel); GIMP_CAIRO_ARGB32_SET_PIXEL (pptr, pixel[0], pixel[1], pixel[2], opacity); pptr += 4; u += du; v += dv; } break; case GIMP_RGBA_IMAGE: while (dx--) { register gulong tmp; guchar alpha; read_pixel_data_1 (tiles, (gint) u, (gint) v, pixel); alpha = INT_MULT (opacity, pixel[3], tmp); GIMP_CAIRO_ARGB32_SET_PIXEL (pptr, pixel[0], pixel[1], pixel[2], alpha); pptr += 4; u += du; v += dv; } break; default: return; } cairo_surface_mark_dirty (area); cairo_set_source_surface (cr, area, area_offx, area_offy); cairo_rectangle (cr, x1, y, x2 - x1, 1); cairo_fill (cr); } /** * gimp_display_shell_draw_tri_row_mask: * * Called from gimp_display_shell_draw_tri(), this draws a single row of a * triangle onto dest, when there is a mask. **/ static void gimp_display_shell_draw_tri_row_mask (GimpDrawable *texture, cairo_t *cr, cairo_surface_t *area, gint area_offx, gint area_offy, GimpChannel *mask, gint mask_offx, gint mask_offy, gint x1, gfloat u1, gfloat v1, gint x2, gfloat u2, gfloat v2, gint y, guchar opacity) { TileManager *tiles, *masktiles; /* used to get the source texture colors */ guchar *pptr; /* points into the pixels of area */ gfloat u, v; gfloat mu, mv; gfloat du, dv; gint dx; guchar pixel[4]; guchar maskval; const guchar *cmap; gint offset; if (x2 == x1) return; g_return_if_fail (GIMP_IS_DRAWABLE (texture)); g_return_if_fail (GIMP_IS_CHANNEL (mask)); g_return_if_fail (area != NULL); g_return_if_fail (cairo_image_surface_get_format (area) == CAIRO_FORMAT_ARGB32); /* make sure the pixel run goes in the positive direction */ if (x1 > x2) { gint tmp; gfloat ftmp; tmp = x2; x2 = x1; x1 = tmp; ftmp = u2; u2 = u1; u1 = ftmp; ftmp = v2; v2 = v1; v1 = ftmp; } u = u1; v = v1; du = (u2 - u1) / (x2 - x1); dv = (v2 - v1) / (x2 - x1); /* don't calculate unseen pixels */ if (x1 < area_offx) { u += du * (area_offx - x1); v += dv * (area_offx - x1); x1 = area_offx; } else if (x1 > area_offx + cairo_image_surface_get_width (area) - 1) { return; } if (x2 < area_offx) { return; } else if (x2 > area_offx + cairo_image_surface_get_width (area) - 1) { x2 = area_offx + cairo_image_surface_get_width (area) - 1; } dx = x2 - x1; if (! dx) return; mu = u + mask_offx; mv = v + mask_offy; pptr = (cairo_image_surface_get_data (area) + (y - area_offy) * cairo_image_surface_get_stride (area) + (x1 - area_offx) * 4); tiles = gimp_drawable_get_tiles (texture); masktiles = gimp_drawable_get_tiles (GIMP_DRAWABLE (mask)); switch (gimp_drawable_type (texture)) { case GIMP_INDEXED_IMAGE: cmap = gimp_drawable_get_colormap (texture); while (dx--) { register gulong tmp; guchar alpha; read_pixel_data_1 (tiles, (gint) u, (gint) v, pixel); read_pixel_data_1 (masktiles, (gint) mu, (gint) mv, &maskval); offset = pixel[0] + pixel[0] + pixel[0]; alpha = INT_MULT (opacity, maskval, tmp); GIMP_CAIRO_ARGB32_SET_PIXEL (pptr, cmap[offset + 0], cmap[offset + 1], cmap[offset + 2], alpha); pptr += 4; u += du; v += dv; mu += du; mv += dv; } break; case GIMP_INDEXEDA_IMAGE: cmap = gimp_drawable_get_colormap (texture); while (dx--) { register gulong tmp; guchar alpha; read_pixel_data_1 (tiles, (gint) u, (gint) v, pixel); read_pixel_data_1 (masktiles, (gint) mu, (gint) mv, &maskval); offset = pixel[0] + pixel[0] + pixel[0]; alpha = INT_MULT3 (opacity, maskval, pixel[1], tmp); GIMP_CAIRO_ARGB32_SET_PIXEL (pptr, cmap[offset + 0], cmap[offset + 1], cmap[offset + 2], alpha); pptr += 4; u += du; v += dv; mu += du; mv += dv; } break; case GIMP_GRAY_IMAGE: while (dx--) { register gulong tmp; guchar alpha; read_pixel_data_1 (tiles, (gint) u, (gint) v, pixel); read_pixel_data_1 (masktiles, (gint) mu, (gint) mv, &maskval); alpha = INT_MULT (opacity, maskval, tmp); GIMP_CAIRO_ARGB32_SET_PIXEL (pptr, pixel[0], pixel[0], pixel[0], alpha); pptr += 4; u += du; v += dv; mu += du; mv += dv; } break; case GIMP_GRAYA_IMAGE: while (dx--) { register gulong tmp; guchar alpha; read_pixel_data_1 (tiles, (gint) u, (gint) v, pixel); read_pixel_data_1 (masktiles, (gint) mu, (gint) mv, &maskval); alpha = INT_MULT3 (opacity, maskval, pixel[1], tmp); GIMP_CAIRO_ARGB32_SET_PIXEL (pptr, pixel[0], pixel[0], pixel[0], alpha); pptr += 4; u += du; v += dv; mu += du; mv += dv; } break; case GIMP_RGB_IMAGE: while (dx--) { register gulong tmp; guchar alpha; read_pixel_data_1 (tiles, (gint) u, (gint) v, pixel); read_pixel_data_1 (masktiles, (gint) mu, (gint) mv, &maskval); alpha = INT_MULT (opacity, maskval, tmp); GIMP_CAIRO_ARGB32_SET_PIXEL (pptr, pixel[0], pixel[1], pixel[2], alpha); pptr += 4; u += du; v += dv; mu += du; mv += dv; } break; case GIMP_RGBA_IMAGE: while (dx--) { register gulong tmp; guchar alpha; read_pixel_data_1 (tiles, (gint) u, (gint) v, pixel); read_pixel_data_1 (masktiles, (gint) mu, (gint) mv, &maskval); alpha = INT_MULT3 (opacity, maskval, pixel[3], tmp); GIMP_CAIRO_ARGB32_SET_PIXEL (pptr, pixel[0], pixel[1], pixel[2], alpha); pptr += 4; u += du; v += dv; mu += du; mv += dv; } break; default: return; } cairo_surface_mark_dirty (area); cairo_set_source_surface (cr, area, area_offx, area_offy); cairo_rectangle (cr, x1, y, x2 - x1, 1); cairo_fill (cr); } /** * gimp_display_shell_trace_tri_edge: * @dest: x coordinates are placed in this array * * Find the x coordinates for a line that runs from (x1,y1) to (x2,y2), * corresponding to the y coordinates y1 to y2-1. So * dest must be large enough to hold y2-y1 values. **/ static void gimp_display_shell_trace_tri_edge (gint *dest, gint x1, gint y1, gint x2, gint y2) { const gint dy = y2 - y1; gint dx; gint xdir; gint errorterm; gint b; gint *dptr; if (dy <= 0) return; g_return_if_fail (dest != NULL); b = 0; errorterm = 0; dptr = dest; if (x2 < x1) { dx = x1 - x2; xdir = -1; } else { dx = x2 - x1; xdir = 1; } if (dx >= dy) { b = dy; while (b --) { *dptr = x1; errorterm += dx; while (errorterm > dy) { x1 += xdir; errorterm -= dy; } dptr ++; } } else if (dy >= dx) { b = dy; while (b --) { *dptr = x1; errorterm += dx; if (errorterm > dy) { x1 += xdir; errorterm -= dy; } dptr ++; } } else if (dx == 0) { b = dy; while (b --) { *dptr = x1; dptr ++; } } else /* dy == dx */ { b = dy; while (b --) { *dptr = x1; x1 += xdir; dptr ++; } } }