static void inline get_rgba_pixel (void *data, int img_no, int x, int y, lua_Number pixel[4]) { Priv *p; gfloat buf[4]; p = data; if (img_no == 0) { gint i; if (!p->in_drawable) return; gegl_buffer_sample (p->in_drawable, x, y, NULL, buf, p->rgba_float, GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE); for (i = 0; i < 4; i++) pixel[i] = buf[i]; } else if (img_no == 1) { gint i; if (!p->aux_drawable) return; gegl_buffer_sample (p->aux_drawable, x, y, NULL, buf, p->rgba_float, GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE); for (i = 0; i < 4; i++) pixel[i] = buf[i]; } }
/* Apply the actual transform */ static void apply_spread (gint x_amount, gint y_amount, gint img_width, gint img_height, const Babl *format, GeglBuffer *src, GeglBuffer *dst, const GeglRectangle *roi) { gfloat *dst_buf; gint x1, y1; // Noise image gdouble x, y; // Source Image GRand *gr = g_rand_new (); /* Get buffer in which to place dst pixels. */ dst_buf = g_new0 (gfloat, roi->width * roi->height * 4); for (y1 = 0; y1 < roi->height; y1++) { for (x1 = 0; x1 < roi->width; x1++) { calc_sample_coords (x1, y1, x_amount, y_amount, gr, &x, &y); /* Only displace the pixel if it's within the bounds of the image. */ if (x >= 0 && x < img_width && y >= 0 && y < img_height) gegl_buffer_sample (src, x, y, NULL, &dst_buf[(y1 * roi->width + x1) * 4], format, GEGL_SAMPLER_LINEAR, GEGL_ABYSS_NONE); else /* Else just copy it */ gegl_buffer_sample (src, x1, y1, NULL, &dst_buf[(y1 * roi->width + x1) * 4], format, GEGL_SAMPLER_LINEAR, GEGL_ABYSS_NONE); } /* for */ } /* for */ gegl_buffer_sample_cleanup (src); /* Store dst pixels. */ gegl_buffer_set (dst, roi, 0, format, dst_buf, GEGL_AUTO_ROWSTRIDE); gegl_buffer_flush(dst); g_free (dst_buf); g_rand_free (gr); }
static void apply_whirl_pinch (gdouble whirl, gdouble pinch, gdouble radius, gdouble cen_x, gdouble cen_y, Babl *format, GeglBuffer *src, GeglRectangle *in_boundary, GeglBuffer *dst, GeglRectangle *boundary, const GeglRectangle *roi) { gfloat *dst_buf; gint row, col; gdouble scale_x, scale_y; gdouble cx, cy; /* Get buffer in which to place dst pixels. */ dst_buf = g_new0 (gfloat, roi->width * roi->height * 4); whirl = whirl * G_PI / 180; scale_x = 1.0; scale_y = roi->width / (gdouble) roi->height; for (row = 0; row < roi->height; row++) { for (col = 0; col < roi->width; col++) { calc_undistorted_coords (roi->x + col, roi->y + row, cen_x, cen_y, scale_x, scale_y, whirl, pinch, radius, &cx, &cy); gegl_buffer_sample (src, cx, cy, 1.0, &dst_buf[(row * roi->width + col) * 4], format, GEGL_INTERPOLATION_LINEAR); } /* for */ } /* for */ gegl_buffer_sample_cleanup (src); /* Store dst pixels. */ gegl_buffer_set (dst, roi, format, dst_buf, GEGL_AUTO_ROWSTRIDE); gegl_buffer_flush(dst); g_free (dst_buf); }
guchar sel_pixel_value (gint row, gint col) { guchar ret; if (col > sel_width || row > sel_height) { g_warning ("sel_pixel_value [%d,%d] out of bounds", col, row); return 0; } gegl_buffer_sample (sel_buffer, col + sel_x1, row + sel_y1, NULL, &ret, babl_format ("Y u8"), GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE); return ret; }
static GimpVector2 gimp_cage_transform_compute_destination (GimpCageConfig *config, gfloat *coef, Babl *format_coef, GeglBuffer *coef_buf, GimpVector2 coords) { gdouble pos_x, pos_y; GimpVector2 result; gint cvn = config->n_cage_vertices; gint i; /* When Gegl bug #645810 will be solved, this should be a good optimisation */ #ifdef GEGL_BUG_645810_SOLVED gegl_buffer_sample (coef_buf, coords.x, coords.y, 1.0, coef, format_coef, GEGL_INTERPOLATION_LANCZOS); #else GeglRectangle rect; rect.height = 1; rect.width = 1; rect.x = coords.x; rect.y = coords.y; gegl_buffer_get (coef_buf, 1, &rect, format_coef, coef, GEGL_AUTO_ROWSTRIDE); #endif pos_x = 0; pos_y = 0; for (i = 0; i < cvn; i++) { pos_x += coef[i] * config->cage_points[i].dest_point.x; pos_y += coef[i] * config->cage_points[i].dest_point.y; pos_x += coef[i + cvn] * config->cage_points[i].edge_scaling_factor * config->cage_points[i].edge_normal.x; pos_y += coef[i + cvn] * config->cage_points[i].edge_scaling_factor * config->cage_points[i].edge_normal.y; } result.x = pos_x; result.y = pos_y; return result; }
static gboolean gimp_projection_get_pixel_at (GimpPickable *pickable, gint x, gint y, const Babl *format, gpointer pixel) { GeglBuffer *buffer = gimp_projection_get_buffer (pickable); if (x < 0 || y < 0 || x >= gegl_buffer_get_width (buffer) || y >= gegl_buffer_get_height (buffer)) return FALSE; gegl_buffer_sample (buffer, x, y, NULL, pixel, format, GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE); return TRUE; }
static GimpVector2 gimp_cage_transform_compute_destination (GimpCageConfig *config, gfloat *coef, const Babl *format_coef, GeglBuffer *coef_buf, GimpVector2 coords) { GimpVector2 result = {0, 0}; gint n_cage_vertices = gimp_cage_config_get_n_points (config); gint i; GimpCagePoint *point; /* When Gegl bug #645810 will be solved, this should be a good optimisation */ #ifdef GEGL_BUG_645810_SOLVED gegl_buffer_sample (coef_buf, coords.x, coords.y, 1.0, coef, format_coef, GEGL_INTERPOLATION_NEAREST); #else GeglRectangle rect; rect.height = 1; rect.width = 1; rect.x = coords.x; rect.y = coords.y; gegl_buffer_get (coef_buf, &rect, 1.0, format_coef, coef, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); #endif for (i = 0; i < n_cage_vertices; i++) { point = &g_array_index (config->cage_points, GimpCagePoint, i); result.x += coef[i] * point->dest_point.x; result.y += coef[i] * point->dest_point.y; result.x += coef[i + n_cage_vertices] * point->edge_scaling_factor * point->edge_normal.x; result.y += coef[i + n_cage_vertices] * point->edge_scaling_factor * point->edge_normal.y; } return result; }
static gboolean do_plasma (PlasmaContext *context, gint x1, gint y1, gint x2, gint y2, gint plasma_depth, gint recursion_depth) { gfloat tl[3], ml[3], bl[3], mt[3], mm[3], mb[3], tr[3], mr[3], br[3]; gfloat tmp[3]; gint xm, ym; gfloat ran; if (G_UNLIKELY ((!context->using_buffer) && ((x2 - x1 + 1) <= TILE_SIZE) && ((y2 - y1 + 1) <= TILE_SIZE))) { gboolean ret; GeglRectangle rect; rect.x = x1; rect.y = y1; rect.width = x2 - x1 + 1; rect.height = y2 - y1 + 1; gegl_buffer_get (context->output, &rect, 1.0, babl_format ("R'G'B' float"), context->buffer, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); context->using_buffer = TRUE; context->buffer_x = x1; context->buffer_y = y1; context->buffer_width = x2 - x1 + 1; ret = do_plasma (context, x1, y1, x2, y2, plasma_depth, recursion_depth); context->using_buffer = FALSE; gegl_buffer_set (context->output, &rect, 0, babl_format ("R'G'B' float"), context->buffer, GEGL_AUTO_ROWSTRIDE); return ret; } xm = (x1 + x2) / 2; ym = (y1 + y2) / 2; if (plasma_depth == -1) { random_rgba (context->gr, tl); put_pixel (context, tl, x1, y1); random_rgba (context->gr, tr); put_pixel (context, tr, x2, y1); random_rgba (context->gr, bl); put_pixel (context, bl, x1, y2); random_rgba (context->gr, br); put_pixel (context, br, x2, y2); random_rgba (context->gr, mm); put_pixel (context, mm, xm, ym); random_rgba (context->gr, ml); put_pixel (context, ml, x1, ym); random_rgba (context->gr, mr); put_pixel (context, mr, x2, ym); random_rgba (context->gr, mt); put_pixel (context, mt, xm, y1); random_rgba (context->gr, mb); put_pixel (context, mb, xm, y2); return FALSE; } if (!plasma_depth) { if (x1 == x2 && y1 == y2) return FALSE; gegl_buffer_sample (context->output, x1, y1, NULL, tl, babl_format ("R'G'B' float"), GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE); gegl_buffer_sample (context->output, x1, y2, NULL, bl, babl_format ("R'G'B' float"), GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE); gegl_buffer_sample (context->output, x2, y1, NULL, tr, babl_format ("R'G'B' float"), GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE); gegl_buffer_sample (context->output, x2, y2, NULL, br, babl_format ("R'G'B' float"), GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE); ran = context->o->turbulence / (2.0 * recursion_depth); if (xm != x1 || xm != x2) { /* Left. */ average_pixel (ml, tl, bl); add_random (context->gr, ml, ran); put_pixel (context, ml, x1, ym); /* Right. */ if (x1 != x2) { average_pixel (mr, tr, br); add_random (context->gr, mr, ran); put_pixel (context, mr, x2, ym); } } if (ym != y1 || ym != x2) { /* Bottom. */ if (x1 != xm || ym != y2) { average_pixel (mb, bl, br); add_random (context->gr, mb, ran); put_pixel (context, mb, xm, y2); } if (y1 != y2) { /* Top. */ average_pixel (mt, tl, tr); add_random (context->gr, mt, ran); put_pixel (context, mt, xm, y1); } } if (y1 != y2 || x1 != x2) { /* Middle pixel. */ average_pixel (mm, tl, br); average_pixel (tmp, bl, tr); average_pixel (mm, mm, tmp); add_random (context->gr, mm, ran); put_pixel (context, mm, xm, ym); } return x2 - x1 < 3 && y2 - y1 < 3; } if (x1 < x2 || y1 < y2) { /* Top left. */ do_plasma (context, x1, y1, xm, ym, plasma_depth - 1, recursion_depth + 1); /* Bottom left. */ do_plasma (context, x1, ym, xm, y2, plasma_depth - 1, recursion_depth + 1); /* Top right. */ do_plasma (context, xm, y1, x2, ym, plasma_depth - 1, recursion_depth + 1); /* Bottom right. */ return do_plasma (context, xm, ym, x2, y2, plasma_depth - 1, recursion_depth + 1); } return TRUE; }
static gboolean find_contiguous_segment (const gfloat *col, GeglBuffer *src_buffer, GeglBuffer *mask_buffer, const Babl *format, gint n_components, gboolean has_alpha, gint width, gboolean select_transparent, GimpSelectCriterion select_criterion, gboolean antialias, gfloat threshold, gint initial_x, gint initial_y, gint *start, gint *end, gfloat *row) { gfloat *s; gfloat mask_row[width]; gfloat diff; #ifdef FETCH_ROW gegl_buffer_get (src_buffer, GEGL_RECTANGLE (0, initial_y, width, 1), 1.0, format, row, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); s = row + initial_x * n_components; #else s = g_alloca (n_components * sizeof (gfloat)); gegl_buffer_sample (src_buffer, initial_x, initial_y, NULL, s, format, GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE); #endif diff = pixel_difference (col, s, antialias, threshold, n_components, has_alpha, select_transparent, select_criterion); /* check the starting pixel */ if (! diff) return FALSE; mask_row[initial_x] = diff; *start = initial_x - 1; #ifdef FETCH_ROW s = row + *start * n_components; #endif while (*start >= 0 && diff) { #ifndef FETCH_ROW gegl_buffer_sample (src_buffer, *start, initial_y, NULL, s, format, GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE); #endif diff = pixel_difference (col, s, antialias, threshold, n_components, has_alpha, select_transparent, select_criterion); mask_row[*start] = diff; if (diff) { (*start)--; #ifdef FETCH_ROW s -= n_components; #endif } } diff = 1; *end = initial_x + 1; #ifdef FETCH_ROW s = row + *end * n_components; #endif while (*end < width && diff) { #ifndef FETCH_ROW gegl_buffer_sample (src_buffer, *end, initial_y, NULL, s, format, GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE); #endif diff = pixel_difference (col, s, antialias, threshold, n_components, has_alpha, select_transparent, select_criterion); mask_row[*end] = diff; if (diff) { (*end)++; #ifdef FETCH_ROW s += n_components; #endif } } gegl_buffer_set (mask_buffer, GEGL_RECTANGLE (*start, initial_y, *end - *start, 1), 0, babl_format ("Y float"), &mask_row[*start], GEGL_AUTO_ROWSTRIDE); return TRUE; }
/** * gimp_drawable_get_line_art_fill_buffer: * @drawable: the #GimpDrawable to edit. * @line_art: the #GimpLineArt computed as fill source. * @options: the #GimpFillOptions. * @sample_merged: * @seed_x: X coordinate to start the fill. * @seed_y: Y coordinate to start the fill. * @mask_buffer: mask of the fill in-progress when in an interactive * filling process. Set to NULL if you need a one-time * fill. * @mask_x: returned x bound of @mask_buffer. * @mask_y: returned x bound of @mask_buffer. * @mask_width: returned width bound of @mask_buffer. * @mask_height: returned height bound of @mask_buffer. * * Creates the fill buffer for a bucket fill operation on @drawable * based on @line_art and @options, without actually applying it. * If @mask_buffer is not NULL, the intermediate fill mask will also be * returned. This fill mask can later be reused in successive calls to * gimp_drawable_get_bucket_fill_buffer() for interactive filling. * * Returns: a fill buffer which can be directly applied to @drawable, or * used in a drawable filter as preview. */ GeglBuffer * gimp_drawable_get_line_art_fill_buffer (GimpDrawable *drawable, GimpLineArt *line_art, GimpFillOptions *options, gboolean sample_merged, gdouble seed_x, gdouble seed_y, GeglBuffer **mask_buffer, gdouble *mask_x, gdouble *mask_y, gint *mask_width, gint *mask_height) { GimpImage *image; GeglBuffer *buffer; GeglBuffer *new_mask; gint x, y, width, height; gint mask_offset_x = 0; gint mask_offset_y = 0; gint sel_x, sel_y, sel_width, sel_height; g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL); g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)), NULL); g_return_val_if_fail (GIMP_IS_FILL_OPTIONS (options), NULL); image = gimp_item_get_image (GIMP_ITEM (drawable)); if (! gimp_item_mask_intersect (GIMP_ITEM (drawable), &sel_x, &sel_y, &sel_width, &sel_height)) return NULL; if (mask_buffer && *mask_buffer) { gfloat pixel; gegl_buffer_sample (*mask_buffer, seed_x, seed_y, NULL, &pixel, babl_format ("Y float"), GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE); if (pixel != 0.0) /* Already selected. This seed won't change the selection. */ return NULL; } gimp_set_busy (image->gimp); /* Do a seed bucket fill...To do this, calculate a new * contiguous region. */ new_mask = gimp_pickable_contiguous_region_by_line_art (NULL, line_art, (gint) seed_x, (gint) seed_y); if (mask_buffer && *mask_buffer) { gimp_gegl_mask_combine_buffer (new_mask, *mask_buffer, GIMP_CHANNEL_OP_ADD, 0, 0); g_object_unref (*mask_buffer); } if (mask_buffer) *mask_buffer = new_mask; gimp_gegl_mask_bounds (new_mask, &x, &y, &width, &height); width -= x; height -= y; /* If there is a selection, intersect the region bounds * with the selection bounds, to avoid processing areas * that are going to be masked out anyway. The actual * intersection of the fill region with the mask data * happens when combining the fill buffer, in * gimp_drawable_apply_buffer(). */ if (! gimp_channel_is_empty (gimp_image_get_mask (image))) { gint off_x = 0; gint off_y = 0; if (sample_merged) gimp_item_get_offset (GIMP_ITEM (drawable), &off_x, &off_y); if (! gimp_rectangle_intersect (x, y, width, height, sel_x + off_x, sel_y + off_y, sel_width, sel_height, &x, &y, &width, &height)) { if (! mask_buffer) g_object_unref (new_mask); /* The fill region and the selection are disjoint; bail. */ gimp_unset_busy (image->gimp); return NULL; } } /* make sure we handle the mask correctly if it was sample-merged */ if (sample_merged) { GimpItem *item = GIMP_ITEM (drawable); gint off_x, off_y; /* Limit the channel bounds to the drawable's extents */ gimp_item_get_offset (item, &off_x, &off_y); gimp_rectangle_intersect (x, y, width, height, off_x, off_y, gimp_item_get_width (item), gimp_item_get_height (item), &x, &y, &width, &height); mask_offset_x = x; mask_offset_y = y; /* translate mask bounds to drawable coords */ x -= off_x; y -= off_y; } else { mask_offset_x = x; mask_offset_y = y; } buffer = gimp_fill_options_create_buffer (options, drawable, GEGL_RECTANGLE (0, 0, width, height), -x, -y); gimp_gegl_apply_opacity (buffer, NULL, NULL, buffer, new_mask, -mask_offset_x, -mask_offset_y, 1.0); if (gimp_fill_options_get_antialias (options)) { /* Antialias for the line art algorithm is not applied during mask * creation because it is not based on individual pixel colors. * Instead we just want to apply it on the borders of the mask at * the end (since the mask can evolve, we don't want to actually * touch it, but only the intermediate results). */ GeglNode *graph; GeglNode *input; GeglNode *op; graph = gegl_node_new (); input = gegl_node_new_child (graph, "operation", "gegl:buffer-source", "buffer", buffer, NULL); op = gegl_node_new_child (graph, "operation", "gegl:gaussian-blur", "std-dev-x", 0.5, "std-dev-y", 0.5, NULL); gegl_node_connect_to (input, "output", op, "input"); gegl_node_blit_buffer (op, buffer, NULL, 0, GEGL_ABYSS_NONE); g_object_unref (graph); } if (mask_x) *mask_x = x; if (mask_y) *mask_y = y; if (mask_width) *mask_width = width; if (mask_height) *mask_height = height; if (! mask_buffer) g_object_unref (new_mask); gimp_unset_busy (image->gimp); return buffer; }
static void convolve_pixel(gfloat *src_buf, gfloat *dst_buf, const GeglRectangle *result, const GeglRectangle *extended, const GeglRectangle *boundary, gdouble **matrix, GeglChantO *o, GeglBuffer *input, gint xx, gint yy, gdouble matrixsum) { gint i, x, y, temp, s_x, s_y; gdouble sum; gfloat color[4]; gint d_offset, s_offset; gint half; gdouble alphasum = 0.0; s_x = 0; s_y = 0; half = (MATRIX_SIZE / 2) + (MATRIX_SIZE % 2); d_offset = ((yy - result->y)*result->width * 4) + (xx - result->x) * 4; s_offset = (yy - result->y + HALF_WINDOW) * extended->width * 4 + (xx - result->x + HALF_WINDOW) * 4; for (i=0; i < 4; i++) { sum = 0.0; if ((i==0 && o->red) || (i==1 && o->blue) || (i==2 && o->green) || (i==3 && o->alpha)) { for (x=0;x < MATRIX_SIZE; x++) for (y=0; y < MATRIX_SIZE; y++) { if (!strcmp(o->border,"wrap")) { s_x = fmod (x+xx, boundary->width); while (s_x < 0) s_x +=boundary->width; s_y = fmod (y+yy, boundary->height); while (s_y < 0) s_y +=boundary->width; } else if (!strcmp(o->border,"extend")) { s_x = CLAMP (x+xx, 0, boundary->width); s_y = CLAMP (y+yy, 0, boundary->height); } temp = (s_y - extended->y) * extended->width * 4 + (s_x - extended->x) * 4; if ((s_x >= extended->x && (s_x < extended->x + extended->width)) && (s_y >=extended->y && (s_y < extended->y + extended->height))) { if (i!=3 && o->weight) sum += matrix[x][y] * src_buf[temp + i] * src_buf[temp + 3]; else sum += matrix[x][y] * src_buf[temp + i]; if (i==3) alphasum += fabs (matrix[x][y] * src_buf[temp+i]); } else { gfloat temp_color[4]; gegl_buffer_sample (input, s_x, s_y, NULL, temp_color, babl_format ("RGBA float"), GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE); if (i!=3 && o->weight) sum += matrix[x][y] * temp_color[i] * temp_color[3]; else sum += matrix[x][y] * temp_color[i]; if (i==3) alphasum += fabs (matrix[x][y] * temp_color[i]); } } sum = sum / o->div; if (i==3 && o->weight) { if (alphasum != 0) sum = sum * matrixsum / alphasum; else sum = 0.0; } sum += o->off; color[i] = sum; } else color[i] = src_buf[s_offset + i]; } for (i=0; i < 4; i++) dst_buf[d_offset + i] = color[i]; }
static void lens_distort_func (gfloat *src_buf, gfloat *dst_buf, const GeglRectangle *extended, const GeglRectangle *result, const GeglRectangle *boundary, LensValues *lens, gint xx, gint yy, GeglBuffer *input, gfloat *background) { gdouble sx, sy, mag; gdouble brighten; gfloat pixel_buffer [16 * 4], temp[4]; gdouble dx, dy; gint x_int, y_int, x = 0, y = 0, offset = 0; temp[0] = temp[1] = temp[2] = temp[3] = 0.0; lens_get_source_coord ((gdouble) xx, (gdouble) yy, &sx, &sy, &mag, lens); /* pseudo gamma transformation, since the input is scRGB */ brighten = pow (MAX (1.0 + mag * lens->brighten, 0.0), 2.4); x_int = floor (sx); dx = sx - x_int; y_int = floor (sy); dy = sy - y_int; for (y = y_int - 1; y <= y_int + 2; y++) { for (x = x_int - 1; x <= x_int + 2; x++) { gint b; if (x < boundary->x || x >= (boundary->x + boundary->width) || y < boundary->y || y >= (boundary->y + boundary->height)) { for (b = 0; b < 4; b++) pixel_buffer[offset++] = background[b]; } else { if (x >= extended->x && x < (extended->x + extended->width) && y >= extended->y && y < (extended->y + extended->height)) { gint src_off; src_off = (y - extended->y) * extended->width * 4 + (x - extended->x) * 4; for (b = 0; b < 4; b++) temp[b] = src_buf[src_off++]; } else { gegl_buffer_sample (input, x, y, NULL, temp, babl_format ("RGBA float"), GEGL_SAMPLER_LINEAR, GEGL_ABYSS_CLAMP); } for (b = 0; b < 4; b++) pixel_buffer[offset++] = temp[b]; } } } lens_cubic_interpolate (pixel_buffer, temp, dx, dy, brighten); offset = (yy - result->y) * result->width * 4 + (xx - result->x) * 4; for (x = 0; x < 4; x++) dst_buf[offset++] = temp[x]; }
/** * gimp_drawable_get_bucket_fill_buffer: * @drawable: the #GimpDrawable to edit. * @options: * @fill_transparent: * @fill_criterion: * @threshold: * @sample_merged: * @diagonal_neighbors: * @seed_x: X coordinate to start the fill. * @seed_y: Y coordinate to start the fill. * @mask_buffer: mask of the fill in-progress when in an interactive * filling process. Set to NULL if you need a one-time * fill. * @mask_x: returned x bound of @mask_buffer. * @mask_y: returned x bound of @mask_buffer. * @mask_width: returned width bound of @mask_buffer. * @mask_height: returned height bound of @mask_buffer. * * Creates the fill buffer for a bucket fill operation on @drawable, * without actually applying it (if you want to apply it directly as a * one-time operation, use gimp_drawable_bucket_fill() instead). If * @mask_buffer is not NULL, the intermediate fill mask will also be * returned. This fill mask can later be reused in successive calls to * gimp_drawable_get_bucket_fill_buffer() for interactive filling. * * Returns: a fill buffer which can be directly applied to @drawable, or * used in a drawable filter as preview. */ GeglBuffer * gimp_drawable_get_bucket_fill_buffer (GimpDrawable *drawable, GimpFillOptions *options, gboolean fill_transparent, GimpSelectCriterion fill_criterion, gdouble threshold, gboolean sample_merged, gboolean diagonal_neighbors, gdouble seed_x, gdouble seed_y, GeglBuffer **mask_buffer, gdouble *mask_x, gdouble *mask_y, gint *mask_width, gint *mask_height) { GimpImage *image; GimpPickable *pickable; GeglBuffer *buffer; GeglBuffer *new_mask; gboolean antialias; gint x, y, width, height; gint mask_offset_x = 0; gint mask_offset_y = 0; gint sel_x, sel_y, sel_width, sel_height; g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL); g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)), NULL); g_return_val_if_fail (GIMP_IS_FILL_OPTIONS (options), NULL); image = gimp_item_get_image (GIMP_ITEM (drawable)); if (! gimp_item_mask_intersect (GIMP_ITEM (drawable), &sel_x, &sel_y, &sel_width, &sel_height)) return NULL; if (mask_buffer && *mask_buffer && threshold == 0.0) { gfloat pixel; gegl_buffer_sample (*mask_buffer, seed_x, seed_y, NULL, &pixel, babl_format ("Y float"), GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE); if (pixel != 0.0) /* Already selected. This seed won't change the selection. */ return NULL; } gimp_set_busy (image->gimp); if (sample_merged) pickable = GIMP_PICKABLE (image); else pickable = GIMP_PICKABLE (drawable); antialias = gimp_fill_options_get_antialias (options); /* Do a seed bucket fill...To do this, calculate a new * contiguous region. */ new_mask = gimp_pickable_contiguous_region_by_seed (pickable, antialias, threshold, fill_transparent, fill_criterion, diagonal_neighbors, (gint) seed_x, (gint) seed_y); if (mask_buffer && *mask_buffer) { gimp_gegl_mask_combine_buffer (new_mask, *mask_buffer, GIMP_CHANNEL_OP_ADD, 0, 0); g_object_unref (*mask_buffer); } if (mask_buffer) *mask_buffer = new_mask; gimp_gegl_mask_bounds (new_mask, &x, &y, &width, &height); width -= x; height -= y; /* If there is a selection, intersect the region bounds * with the selection bounds, to avoid processing areas * that are going to be masked out anyway. The actual * intersection of the fill region with the mask data * happens when combining the fill buffer, in * gimp_drawable_apply_buffer(). */ if (! gimp_channel_is_empty (gimp_image_get_mask (image))) { gint off_x = 0; gint off_y = 0; if (sample_merged) gimp_item_get_offset (GIMP_ITEM (drawable), &off_x, &off_y); if (! gimp_rectangle_intersect (x, y, width, height, sel_x + off_x, sel_y + off_y, sel_width, sel_height, &x, &y, &width, &height)) { if (! mask_buffer) g_object_unref (new_mask); /* The fill region and the selection are disjoint; bail. */ gimp_unset_busy (image->gimp); return NULL; } } /* make sure we handle the mask correctly if it was sample-merged */ if (sample_merged) { GimpItem *item = GIMP_ITEM (drawable); gint off_x, off_y; /* Limit the channel bounds to the drawable's extents */ gimp_item_get_offset (item, &off_x, &off_y); gimp_rectangle_intersect (x, y, width, height, off_x, off_y, gimp_item_get_width (item), gimp_item_get_height (item), &x, &y, &width, &height); mask_offset_x = x; mask_offset_y = y; /* translate mask bounds to drawable coords */ x -= off_x; y -= off_y; } else { mask_offset_x = x; mask_offset_y = y; } buffer = gimp_fill_options_create_buffer (options, drawable, GEGL_RECTANGLE (0, 0, width, height), -x, -y); gimp_gegl_apply_opacity (buffer, NULL, NULL, buffer, new_mask, -mask_offset_x, -mask_offset_y, 1.0); if (mask_x) *mask_x = x; if (mask_y) *mask_y = y; if (mask_width) *mask_width = width; if (mask_height) *mask_height = height; if (! mask_buffer) g_object_unref (new_mask); gimp_unset_busy (image->gimp); return buffer; }
static void fractaltrace (GeglBuffer *input, const GeglRectangle *picture, gfloat *dst_buf, const GeglRectangle *roi, GeglChantO *o, gint y, GeglFractalTraceType fractal_type, const Babl *format) { GeglMatrix2 scale; /* a matrix indicating scaling factors around the current center pixel. */ gint x, i, offset; gdouble scale_x, scale_y; gdouble bailout2; gfloat dest[4]; scale_x = (o->X2 - o->X1) / picture->width; scale_y = (o->Y2 - o->Y1) / picture->height; bailout2 = o->bailout * o->bailout; offset = (y - roi->y) * roi->width * 4; for (x = roi->x; x < roi->x + roi->width; x++) { gdouble cx, cy; gdouble px, py; dest[1] = dest[2] = dest[3] = dest[0] = 0.0; switch (fractal_type) { case GEGL_FRACTAL_TRACE_TYPE_JULIA: #define gegl_unmap(u,v,ud,vd) { \ gdouble rx, ry; \ cx = o->X1 + ((u) - picture->x) * scale_x; \ cy = o->Y1 + ((v) - picture->y) * scale_y; \ julia (cx, cy, o->JX, o->JY, &rx, &ry, o->depth, bailout2); \ ud = (rx - o->X1) / scale_x + picture->x; \ vd = (ry - o->Y1) / scale_y + picture->y; \ } gegl_sampler_compute_scale (scale, x, y); gegl_unmap(x,y,px,py); #undef gegl_unmap break; case GEGL_FRACTAL_TRACE_TYPE_MANDELBROT: #define gegl_unmap(u,v,ud,vd) { \ gdouble rx, ry; \ cx = o->X1 + ((u) - picture->x) * scale_x; \ cy = o->Y1 + ((v) - picture->y) * scale_y; \ julia (cx, cy, cx, cy, &rx, &ry, o->depth, bailout2); \ ud = (rx - o->X1) / scale_x + picture->x; \ vd = (ry - o->Y1) / scale_y + picture->y; \ } gegl_sampler_compute_scale (scale, x, y); gegl_unmap(x,y,px,py); #undef gegl_unmap break; default: g_error (_("Unsupported fractal type")); } gegl_buffer_sample (input, px, py, &scale, dest, format, GEGL_SAMPLER_NOHALO, o->abyss_policy); for (i = 0; i < 4; i++) dst_buf[offset++] = dest[i]; } }
static void lens_distort_func (gfloat *src_buf, gfloat *dst_buf, const GeglRectangle *extended, const GeglRectangle *result, const GeglRectangle *boundary, LensDistortion old, gint xx, gint yy, GeglBuffer *input) { gdouble sx, sy, mag; gdouble brighten; gfloat pixel_buffer [16 * 4], temp[4]; gdouble dx, dy; gint x_int, y_int, x = 0, y = 0, offset = 0; temp[0] = temp[1] = temp[2] = temp[3] = 0.0; lens_get_source_coord ((gdouble)xx, (gdouble)yy, &sx, &sy, &mag, &old); brighten = 1.0 + mag * old.brighten; x_int = floor (sx); dx = sx - x_int; y_int = floor (sy); dy = sy - y_int; for (y = y_int - 1; y <= y_int + 2; y++) { for (x = x_int - 1; x <= x_int + 2; x++) { gint b; if (x >= extended->x && x<(extended->x + extended->width) && y >= extended->y && y < (extended->y + extended->height)) { gint src_off; src_off = (y - extended->y) * extended->width * 4 + (x - extended->x) * 4; for (b=0; b<4; b++) temp[b] = src_buf[src_off++]; } else if (x >= boundary->x && x < boundary->x + boundary->width && y >= boundary->y && y < boundary->y + boundary->height) { gegl_buffer_sample (input, x, y, NULL, temp, babl_format ("RGBA float"), GEGL_SAMPLER_CUBIC, GEGL_ABYSS_NONE); } else { for (b=0; b<4; b++) temp[b] = 0.0; } for (b=0; b<4; b++) pixel_buffer[offset++] = temp[b]; } } lens_cubic_interpolate (pixel_buffer, temp, dx, dy, brighten); offset = (yy - result->y) * result->width * 4 + (xx - result->x) * 4; for (x=0; x<4; x++) dst_buf[offset++] = temp[x]; }
static gboolean save_image (const gchar *filename, GeglBuffer *buffer, GError **error) { const Babl *format = babl_format ("R'G'B'A u8"); gint row, col, cols, rows, x, y; gint colcount, colspan, rowspan; gint *palloc; guchar *buf, *buf2; gchar *width, *height; FILE *fp; cols = gegl_buffer_get_width (buffer); rows = gegl_buffer_get_height (buffer); fp = g_fopen (filename, "w"); if (! fp) { g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno), _("Could not open '%s' for writing: %s"), gimp_filename_to_utf8 (filename), g_strerror (errno)); return FALSE; } palloc = g_new (int, rows * cols); if (gtmvals.fulldoc) { fprintf (fp, "<HTML>\n<HEAD><TITLE>%s</TITLE></HEAD>\n<BODY>\n", filename); fprintf (fp, "<H1>%s</H1>\n", filename); } fprintf (fp, "<TABLE BORDER=%d CELLPADDING=%d CELLSPACING=%d>\n", gtmvals.border, gtmvals.cellpadding, gtmvals.cellspacing); if (gtmvals.caption) fprintf (fp, "<CAPTION>%s</CAPTION>\n", gtmvals.captiontxt); gimp_progress_init_printf (_("Saving '%s'"), gimp_filename_to_utf8 (filename)); buf = g_new (guchar, babl_format_get_bytes_per_pixel (format)); buf2 = g_new (guchar, babl_format_get_bytes_per_pixel (format)); width = height = NULL; if (strcmp (gtmvals.clwidth, "") != 0) { width = g_strdup_printf (" WIDTH=\"%s\"", gtmvals.clwidth); } if (strcmp (gtmvals.clheight, "") != 0) { height = g_strdup_printf (" HEIGHT=\"%s\" ", gtmvals.clheight); } if (! width) width = g_strdup (" "); if (! height) height = g_strdup (" "); /* Initialize array to hold ROWSPAN and COLSPAN cell allocation table */ for (row = 0; row < rows; row++) for (col = 0; col < cols; col++) palloc[cols * row + col] = 1; colspan = 0; rowspan = 0; for (y = 0; y < rows; y++) { fprintf (fp," <TR>\n"); for (x = 0; x < cols; x++) { gegl_buffer_sample (buffer, x, y, NULL, buf, format, GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE); /* Determine ROWSPAN and COLSPAN */ if (gtmvals.spantags) { col = x; row = y; colcount = 0; colspan = 0; rowspan = 0; gegl_buffer_sample (buffer, col, row, NULL, buf2, format, GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE); while (color_comp (buf, buf2) && palloc[cols * row + col] == 1 && row < rows) { while (color_comp (buf, buf2) && palloc[cols * row + col] == 1 && col < cols) { colcount++; col++; gegl_buffer_sample (buffer, col, row, NULL, buf2, format, GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE); } if (colcount != 0) { row++; rowspan++; } if (colcount < colspan || colspan == 0) colspan = colcount; col = x; colcount = 0; gegl_buffer_sample (buffer, col, row, NULL, buf2, format, GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE); } if (colspan > 1 || rowspan > 1) { for (row = 0; row < rowspan; row++) for (col = 0; col < colspan; col++) palloc[cols * (row + y) + (col + x)] = 0; palloc[cols * y + x] = 2; } } if (palloc[cols * y + x] == 1) fprintf (fp, " <TD%s%sBGCOLOR=#%02x%02x%02x>", width, height, buf[0], buf[1], buf[2]); if (palloc[cols * y + x] == 2) fprintf (fp," <TD ROWSPAN=\"%d\" COLSPAN=\"%d\"%s%sBGCOLOR=#%02x%02x%02x>", rowspan, colspan, width, height, buf[0], buf[1], buf[2]); if (palloc[cols * y + x] != 0) { if (gtmvals.tdcomp) fprintf (fp, "%s</TD>\n", gtmvals.cellcontent); else fprintf (fp, "\n %s\n </TD>\n", gtmvals.cellcontent); } } fprintf (fp," </TR>\n"); gimp_progress_update ((double) y / (double) rows); } gimp_progress_update (1.0); if (gtmvals.fulldoc) fprintf (fp, "</TABLE></BODY></HTML>\n"); else fprintf (fp, "</TABLE>\n"); fclose (fp); g_free (width); g_free (height); g_free (palloc); return TRUE; }
static gboolean process (GeglOperation *operation, GeglBuffer *input, GeglBuffer *output, const GeglRectangle *result, gint level) { GeglChantO *o = GEGL_CHANT_PROPERTIES (operation); GeglOperationAreaFilter *op_area = GEGL_OPERATION_AREA_FILTER (operation); GeglRectangle boundary = get_effective_area (operation); GeglRectangle extended; const Babl *format = babl_format ("RGBA float"); GRand *gr = g_rand_new_with_seed (o->seed); gfloat color[4]; gint cols, rows, num_tiles, count; gint *random_indices; gfloat *dst_buf; Polygon poly; gint i; extended.x = CLAMP (result->x - op_area->left, boundary.x, boundary.x + boundary.width); extended.width = CLAMP (result->width + op_area->left + op_area->right, 0, boundary.width); extended.y = CLAMP (result->y - op_area->top, boundary.y, boundary.y + boundary.width); extended.height = CLAMP (result->height + op_area->top + op_area->bottom, 0, boundary.height); dst_buf = g_new0 (gfloat, extended.width * extended.height * 4); cols = (result->width + o->tile_size - 1) / o->tile_size; rows = (result->height + o->tile_size - 1) / o->tile_size; num_tiles = (rows + 1) * (cols + 1); random_indices = g_new0 (gint, num_tiles); for (i = 0; i < num_tiles; i++) random_indices[i] = i; randomize_indices (num_tiles, random_indices, gr); for (count = 0; count < num_tiles; count++) { gint i, j, ix, iy; gdouble x, y, width, height, theta; i = random_indices[count] / (cols + 1); j = random_indices[count] % (cols + 1); x = j * o->tile_size + (o->tile_size / 4.0) - g_rand_double_range (gr, 0, (o->tile_size /2.0)) + result->x; y = i * o->tile_size + (o->tile_size / 4.0) - g_rand_double_range (gr, 0, (o->tile_size /2.0)) + result->y; width = (o->tile_size + g_rand_double_range (gr, -o->tile_size / 8.0, o->tile_size / 8.0)) * o->tile_saturation; height = (o->tile_size + g_rand_double_range (gr, -o->tile_size / 8.0, o->tile_size / 8.0)) * o->tile_saturation; theta = g_rand_double_range (gr, 0, 2 * G_PI); polygon_reset (&poly); polygon_add_point (&poly, -width / 2.0, -height / 2.0); polygon_add_point (&poly, width / 2.0, -height / 2.0); polygon_add_point (&poly, width / 2.0, height / 2.0); polygon_add_point (&poly, -width / 2.0, height / 2.0); polygon_rotate (&poly, theta); polygon_translate (&poly, x, y); ix = CLAMP (x, boundary.x, boundary.x + boundary.width - 1); iy = CLAMP (y, boundary.y, boundary.y + boundary.height - 1); gegl_buffer_sample (input, ix, iy, NULL, color, format, GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE); fill_poly_color (&poly, &extended, &boundary, dst_buf, color); } gegl_buffer_set (output, &extended, 0, format, dst_buf, GEGL_AUTO_ROWSTRIDE); g_free (dst_buf); g_free (random_indices); g_free (gr); return TRUE; }
GeglBuffer * gimp_pickable_contiguous_region_by_seed (GimpPickable *pickable, gboolean antialias, gfloat threshold, gboolean select_transparent, GimpSelectCriterion select_criterion, gboolean diagonal_neighbors, gint x, gint y) { GeglBuffer *src_buffer; GeglBuffer *mask_buffer; const Babl *format; GeglRectangle extent; gint n_components; gboolean has_alpha; gfloat start_col[MAX_CHANNELS]; g_return_val_if_fail (GIMP_IS_PICKABLE (pickable), NULL); gimp_pickable_flush (pickable); src_buffer = gimp_pickable_get_buffer (pickable); format = choose_format (src_buffer, select_criterion, &n_components, &has_alpha); gegl_buffer_sample (src_buffer, x, y, NULL, start_col, format, GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE); if (has_alpha) { if (select_transparent) { /* don't select transparent regions if the start pixel isn't * fully transparent */ if (start_col[n_components - 1] > 0) select_transparent = FALSE; } } else { select_transparent = FALSE; } extent = *gegl_buffer_get_extent (src_buffer); mask_buffer = gegl_buffer_new (&extent, babl_format ("Y float")); if (x >= extent.x && x < (extent.x + extent.width) && y >= extent.y && y < (extent.y + extent.height)) { GIMP_TIMER_START(); find_contiguous_region (src_buffer, mask_buffer, format, n_components, has_alpha, select_transparent, select_criterion, antialias, threshold, diagonal_neighbors, x, y, start_col); GIMP_TIMER_END("foo"); } return mask_buffer; }
GeglBuffer * gimp_image_contiguous_region_by_seed (GimpImage *image, GimpDrawable *drawable, gboolean sample_merged, gboolean antialias, gfloat threshold, gboolean select_transparent, GimpSelectCriterion select_criterion, gint x, gint y) { GimpPickable *pickable; GeglBuffer *src_buffer; GeglBuffer *mask_buffer; const Babl *format; gint n_components; gboolean has_alpha; gfloat start_col[MAX_CHANNELS]; g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL); if (sample_merged) pickable = GIMP_PICKABLE (image); else pickable = GIMP_PICKABLE (drawable); gimp_pickable_flush (pickable); src_buffer = gimp_pickable_get_buffer (pickable); format = choose_format (src_buffer, select_criterion, &n_components, &has_alpha); gegl_buffer_sample (src_buffer, x, y, NULL, start_col, format, GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE); if (has_alpha) { if (select_transparent) { /* don't select transparent regions if the start pixel isn't * fully transparent */ if (start_col[n_components - 1] > 0) select_transparent = FALSE; } } else { select_transparent = FALSE; } mask_buffer = gegl_buffer_new (gegl_buffer_get_extent (src_buffer), babl_format ("Y float")); find_contiguous_region_helper (src_buffer, mask_buffer, format, n_components, has_alpha, select_transparent, select_criterion, antialias, threshold, x, y, start_col); return mask_buffer; }
static void find_contiguous_region_helper (GeglBuffer *src_buffer, GeglBuffer *mask_buffer, const Babl *format, gint n_components, gboolean has_alpha, gboolean select_transparent, GimpSelectCriterion select_criterion, gboolean antialias, gfloat threshold, gint x, gint y, const gfloat *col) { gint start, end; gint new_start, new_end; GQueue *coord_stack; coord_stack = g_queue_new (); /* To avoid excessive memory allocation (y, start, end) tuples are * stored in interleaved format: * * [y1] [start1] [end1] [y2] [start2] [end2] */ g_queue_push_tail (coord_stack, GINT_TO_POINTER (y)); g_queue_push_tail (coord_stack, GINT_TO_POINTER (x - 1)); g_queue_push_tail (coord_stack, GINT_TO_POINTER (x + 1)); do { y = GPOINTER_TO_INT (g_queue_pop_head (coord_stack)); start = GPOINTER_TO_INT (g_queue_pop_head (coord_stack)); end = GPOINTER_TO_INT (g_queue_pop_head (coord_stack)); for (x = start + 1; x < end; x++) { gfloat val; gegl_buffer_sample (mask_buffer, x, y, NULL, &val, babl_format ("Y float"), GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE); if (val != 0.0) continue; if (! find_contiguous_segment (col, src_buffer, mask_buffer, format, n_components, has_alpha, gegl_buffer_get_width (src_buffer), select_transparent, select_criterion, antialias, threshold, x, y, &new_start, &new_end)) continue; if (y + 1 < gegl_buffer_get_height (src_buffer)) { g_queue_push_tail (coord_stack, GINT_TO_POINTER (y + 1)); g_queue_push_tail (coord_stack, GINT_TO_POINTER (new_start)); g_queue_push_tail (coord_stack, GINT_TO_POINTER (new_end)); } if (y - 1 >= 0) { g_queue_push_tail (coord_stack, GINT_TO_POINTER (y - 1)); g_queue_push_tail (coord_stack, GINT_TO_POINTER (new_start)); g_queue_push_tail (coord_stack, GINT_TO_POINTER (new_end)); } } } while (! g_queue_is_empty (coord_stack)); g_queue_free (coord_stack); }
static gboolean find_contiguous_segment (const gfloat *col, GeglBuffer *src_buffer, GeglBuffer *mask_buffer, const Babl *format, gint n_components, gboolean has_alpha, gint width, gboolean select_transparent, GimpSelectCriterion select_criterion, gboolean antialias, gfloat threshold, gint initial_x, gint initial_y, gint *start, gint *end) { gfloat s[MAX_CHANNELS]; gfloat mask_row[width]; gfloat diff; gegl_buffer_sample (src_buffer, initial_x, initial_y, NULL, s, format, GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE); diff = pixel_difference (col, s, antialias, threshold, n_components, has_alpha, select_transparent, select_criterion); /* check the starting pixel */ if (! diff) return FALSE; mask_row[initial_x] = diff; *start = initial_x - 1; while (*start >= 0 && diff) { gegl_buffer_sample (src_buffer, *start, initial_y, NULL, s, format, GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE); diff = pixel_difference (col, s, antialias, threshold, n_components, has_alpha, select_transparent, select_criterion); mask_row[*start] = diff; if (diff) (*start)--; } diff = 1; *end = initial_x + 1; while (*end < width && diff) { gegl_buffer_sample (src_buffer, *end, initial_y, NULL, s, format, GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE); diff = pixel_difference (col, s, antialias, threshold, n_components, has_alpha, select_transparent, select_criterion); mask_row[*end] = diff; if (diff) (*end)++; } gegl_buffer_set (mask_buffer, GEGL_RECTANGLE (*start, initial_y, *end - *start, 1), 0, babl_format ("Y float"), &mask_row[*start], GEGL_AUTO_ROWSTRIDE); return TRUE; }
static gboolean process (GeglOperation *operation, GeglBuffer *input, GeglBuffer *output, const GeglRectangle *result, gint level) { GeglChantO *o = GEGL_CHANT_PROPERTIES (operation); GeglRectangle boundary = get_effective_area (operation); const Babl *format = babl_format ("RGBA float"); gint x,y; gfloat *src_buf, *dst_buf; gfloat dest[4]; gint i, offset = 0; gboolean inside; gdouble px, py; GeglMatrix2 scale; /* a matrix indicating scaling factors around the current center pixel. */ src_buf = g_new0 (gfloat, result->width * result->height * 4); dst_buf = g_new0 (gfloat, result->width * result->height * 4); gegl_buffer_get (input, result, 1.0, format, src_buf, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); if (o->middle) { o->pole_x = boundary.width / 2; o->pole_y = boundary.height / 2; } for (y = result->y; y < result->y + result->height; y++) for (x = result->x; x < result->x + result->width; x++) { #define gegl_unmap(u,v,ud,vd) { \ gdouble rx, ry; \ inside = calc_undistorted_coords ((gdouble)x, (gdouble)y, \ &rx, &ry, o, boundary); \ ud = rx; \ vd = ry; \ } gegl_sampler_compute_scale (scale, x, y); gegl_unmap(x,y,px,py); #undef gegl_unmap if (inside) gegl_buffer_sample (input, px, py, &scale, dest, format, GEGL_SAMPLER_NOHALO, GEGL_ABYSS_NONE); else for (i=0; i<4; i++) dest[i] = 0.0; for (i=0; i<4; i++) dst_buf[offset++] = dest[i]; } gegl_buffer_set (output, result, 0, format, dst_buf, GEGL_AUTO_ROWSTRIDE); g_free (src_buf); g_free (dst_buf); return TRUE; }