static gboolean gimp_operation_tile_source_process (GeglOperation *operation, GeglBuffer *output, const GeglRectangle *result) { GimpOperationTileSource *self = GIMP_OPERATION_TILE_SOURCE (operation); const Babl *format; PixelRegion srcPR; gpointer pr; if (! self->tile_manager) return FALSE; format = gegl_operation_get_format (operation, "output"); pixel_region_init (&srcPR, self->tile_manager, result->x, result->y, result->width, result->height, FALSE); for (pr = pixel_regions_register (1, &srcPR); pr; pr = pixel_regions_process (pr)) { GeglRectangle rect = { srcPR.x, srcPR.y, srcPR.w, srcPR.h }; gegl_buffer_set (output, &rect, 1, format, srcPR.data, srcPR.rowstride); } return TRUE; }
static void render_blob (GimpBlob *blob, PixelRegion *dest) { gpointer pr; for (pr = pixel_regions_register (1, dest); pr != NULL; pr = pixel_regions_process (pr)) { guchar *d = dest->data; gint h = dest->h; gint y; for (y = 0; y < h; y++, d += dest->rowstride) { render_blob_line (blob, d, dest->x, dest->y + y, dest->w); } } }
static void gimp_clone_motion (GimpSourceCore *source_core, GimpDrawable *drawable, GimpPaintOptions *paint_options, const GimpCoords *coords, gdouble opacity, GimpPickable *src_pickable, PixelRegion *srcPR, gint src_offset_x, gint src_offset_y, TempBuf *paint_area, gint paint_area_offset_x, gint paint_area_offset_y, gint paint_area_width, gint paint_area_height) { GimpPaintCore *paint_core = GIMP_PAINT_CORE (source_core); GimpCloneOptions *options = GIMP_CLONE_OPTIONS (paint_options); GimpSourceOptions *source_options = GIMP_SOURCE_OPTIONS (paint_options); GimpContext *context = GIMP_CONTEXT (paint_options); GimpImage *image = gimp_item_get_image (GIMP_ITEM (drawable)); GimpImage *src_image = NULL; GimpDynamicsOutput *force_output; GimpImageType src_type = 0; GimpImageType dest_type; gpointer pr = NULL; gint y; PixelRegion destPR; GimpPattern *pattern = NULL; gdouble fade_point; gdouble force; switch (options->clone_type) { case GIMP_IMAGE_CLONE: src_image = gimp_pickable_get_image (src_pickable); src_type = gimp_pickable_get_image_type (src_pickable); if (gimp_pickable_get_bytes (src_pickable) < srcPR->bytes) src_type = GIMP_IMAGE_TYPE_WITH_ALPHA (src_type); pixel_region_init_temp_buf (&destPR, paint_area, paint_area_offset_x, paint_area_offset_y, paint_area_width, paint_area_height); pr = pixel_regions_register (2, srcPR, &destPR); break; case GIMP_PATTERN_CLONE: pattern = gimp_context_get_pattern (context); pixel_region_init_temp_buf (&destPR, paint_area, 0, 0, paint_area->width, paint_area->height); pr = pixel_regions_register (1, &destPR); break; } dest_type = gimp_drawable_type (drawable); for (; pr != NULL; pr = pixel_regions_process (pr)) { guchar *s = srcPR->data; guchar *d = destPR.data; for (y = 0; y < destPR.h; y++) { switch (options->clone_type) { case GIMP_IMAGE_CLONE: gimp_clone_line_image (image, dest_type, src_image, src_type, s, d, srcPR->bytes, destPR.bytes, destPR.w); s += srcPR->rowstride; break; case GIMP_PATTERN_CLONE: gimp_clone_line_pattern (image, dest_type, pattern, d, paint_area->x + src_offset_x, paint_area->y + y + src_offset_y, destPR.bytes, destPR.w); break; } d += destPR.rowstride; } } force_output = gimp_dynamics_get_output (GIMP_BRUSH_CORE (paint_core)->dynamics, GIMP_DYNAMICS_OUTPUT_FORCE); fade_point = gimp_paint_options_get_fade (paint_options, image, paint_core->pixel_dist); force = gimp_dynamics_output_get_linear_value (force_output, coords, paint_options, fade_point); gimp_brush_core_paste_canvas (GIMP_BRUSH_CORE (paint_core), drawable, coords, MIN (opacity, GIMP_OPACITY_OPAQUE), gimp_context_get_opacity (context), gimp_context_get_paint_mode (context), gimp_paint_options_get_brush_mode (paint_options), force, /* In fixed mode, paint incremental so the * individual brushes are properly applied * on top of each other. * Otherwise the stuff we paint is seamless * and we don't need intermediate masking. */ source_options->align_mode == GIMP_SOURCE_ALIGN_FIXED ? GIMP_PAINT_INCREMENTAL : GIMP_PAINT_CONSTANT); }
static void gimp_text_layer_render_layout (GimpTextLayer *layer, GimpTextLayout *layout) { GimpDrawable *drawable = GIMP_DRAWABLE (layer); GimpItem *item = GIMP_ITEM (layer); GimpImage *image = gimp_item_get_image (item); cairo_t *cr; cairo_surface_t *surface; PixelRegion layerPR; const guchar *data; GimpImageType layer_type; gint layer_alpha_byte; gint rowstride; gint width; gint height; gpointer pr; g_return_if_fail (gimp_drawable_has_alpha (drawable)); width = gimp_item_get_width (item); height = gimp_item_get_height (item); surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height); cr = cairo_create (surface); gimp_text_layout_render (layout, cr, layer->text->base_dir, FALSE); cairo_destroy (cr); pixel_region_init (&layerPR, gimp_drawable_get_tiles (drawable), 0, 0, width, height, TRUE); layer_type = gimp_drawable_type (drawable); layer_alpha_byte = layerPR.bytes - 1; cairo_surface_flush (surface); data = cairo_image_surface_get_data (surface); rowstride = cairo_image_surface_get_stride (surface); for (pr = pixel_regions_register (1, &layerPR); pr != NULL; pr = pixel_regions_process (pr)) { const guchar *src = data + layerPR.y * rowstride + layerPR.x * 4; guchar *dest = layerPR.data; gint rows = layerPR.h; while (rows--) { const guchar *s = src; guchar *d = dest; gint w = layerPR.w; while (w--) { guchar color[4]; GIMP_CAIRO_ARGB32_GET_PIXEL (s, color[0], color[1], color[2], color[3]); gimp_image_transform_color (image, layer_type, d, GIMP_RGB, color); d[layer_alpha_byte] = color[3]; s += 4; d += layerPR.bytes; } src += rowstride; dest += layerPR.rowstride; } } cairo_surface_destroy (surface); gimp_drawable_update (drawable, 0, 0, width, height); }
/** * gimp_channel_combine_ellipse_rect: * @mask: the channel with which to combine the elliptic rect * @op: whether to replace, add to, or subtract from the current * contents * @x: x coordinate of upper left corner of bounding rect * @y: y coordinate of upper left corner of bounding rect * @w: width of bounding rect * @h: height of bounding rect * @a: elliptic a-constant applied to corners * @b: elliptic b-constant applied to corners * @antialias: if %TRUE, antialias the elliptic corners * * Used for rounded cornered rectangles and ellipses. If @op is * %GIMP_CHANNEL_OP_REPLACE or %GIMP_CHANNEL_OP_ADD, sets pixels * within the ellipse to 255. If @op is %GIMP_CHANNEL_OP_SUBTRACT, * sets pixels within to zero. If @antialias is %TRUE, pixels that * impinge on the edge of the ellipse are set to intermediate values, * depending on how much they overlap. **/ void gimp_channel_combine_ellipse_rect (GimpChannel *mask, GimpChannelOps op, gint x, gint y, gint w, gint h, gdouble a, gdouble b, gboolean antialias) { PixelRegion maskPR; gdouble a_sqr; gdouble b_sqr; gdouble ellipse_center_x; gint x0, y0; gint width, height; gpointer pr; g_return_if_fail (GIMP_IS_CHANNEL (mask)); g_return_if_fail (a >= 0.0 && b >= 0.0); g_return_if_fail (op != GIMP_CHANNEL_OP_INTERSECT); /* Make sure the elliptic corners fit into the rect */ a = MIN (a, w / 2.0); b = MIN (b, h / 2.0); a_sqr = SQR (a); b_sqr = SQR (b); if (! gimp_rectangle_intersect (x, y, w, h, 0, 0, gimp_item_get_width (GIMP_ITEM (mask)), gimp_item_get_height (GIMP_ITEM (mask)), &x0, &y0, &width, &height)) return; ellipse_center_x = x + a; pixel_region_init (&maskPR, gimp_drawable_get_tiles (GIMP_DRAWABLE (mask)), x0, y0, width, height, TRUE); for (pr = pixel_regions_register (1, &maskPR); pr != NULL; pr = pixel_regions_process (pr)) { guchar *data = maskPR.data; gint py; for (py = maskPR.y; py < maskPR.y + maskPR.h; py++, data += maskPR.rowstride) { const gint px = maskPR.x; gdouble ellipse_center_y; if (py >= y + b && py < y + h - b) { /* we are on a row without rounded corners */ gimp_channel_combine_span (data, op, 0, maskPR.w, 255); continue; } /* Match the ellipse center y with our current y */ if (py < y + b) { ellipse_center_y = y + b; } else { ellipse_center_y = y + h - b; } /* For a non-antialiased ellipse, use the normal equation * for an ellipse with an arbitrary center * (ellipse_center_x, ellipse_center_y). */ if (! antialias) { gdouble half_ellipse_width_at_y; gint x_start; gint x_end; half_ellipse_width_at_y = sqrt (a_sqr - a_sqr * SQR (py + 0.5f - ellipse_center_y) / b_sqr); x_start = ROUND (ellipse_center_x - half_ellipse_width_at_y); x_end = ROUND (ellipse_center_x + w - 2 * a + half_ellipse_width_at_y); gimp_channel_combine_span (data, op, MAX (x_start - px, 0), MIN (x_end - px, maskPR.w), 255); } else /* use antialiasing */ { /* algorithm changed 7-18-04, because the previous one * did not work well for eccentric ellipses. The new * algorithm measures the distance to the ellipse in the * X and Y directions, and uses trigonometry to * approximate the distance to the ellipse as the * distance to the hypotenuse of a right triangle whose * legs are the X and Y distances. (WES) */ const gfloat yi = ABS (py + 0.5 - ellipse_center_y); gint last_val = -1; gint x_start = px; gint cur_x; for (cur_x = px; cur_x < (px + maskPR.w); cur_x++) { gfloat xj; gfloat xdist; gfloat ydist; gfloat r; gfloat dist; gint val; if (cur_x < x + w / 2) { ellipse_center_x = x + a; } else { ellipse_center_x = x + w - a; } xj = ABS (cur_x + 0.5 - ellipse_center_x); if (yi < b) xdist = xj - a * sqrt (1 - SQR (yi) / b_sqr); else xdist = 1000.0; /* anything large will work */ if (xj < a) ydist = yi - b * sqrt (1 - SQR (xj) / a_sqr); else ydist = 1000.0; /* anything large will work */ r = hypot (xdist, ydist); if (r < 0.001) dist = 0.; else dist = xdist * ydist / r; /* trig formula for distance to * hypotenuse */ if (xdist < 0.0) dist *= -1; if (dist < -0.5) val = 255; else if (dist < 0.5) val = (gint) (255 * (1 - (dist + 0.5))); else val = 0; if (last_val != val) { if (last_val != -1) gimp_channel_combine_span (data, op, MAX (x_start - px, 0), MIN (cur_x - px, maskPR.w), last_val); x_start = cur_x; last_val = val; } /* skip ahead if we are on the straight segment * between rounded corners */ if (cur_x >= x + a && cur_x < x + w - a) { gimp_channel_combine_span (data, op, MAX (x_start - px, 0), MIN (cur_x - px, maskPR.w), last_val); x_start = cur_x; cur_x = x + w - a; last_val = val = 255; } /* Time to change center? */ if (cur_x >= x + w / 2) { ellipse_center_x = x + w - a; } } gimp_channel_combine_span (data, op, MAX (x_start - px, 0), MIN (cur_x - px, maskPR.w), last_val); } } } /* use the intersected values for the boundary calculation */ x = x0; y = y0; w = width; h = height; /* determine new boundary */ if (mask->bounds_known && (op == GIMP_CHANNEL_OP_ADD) && ! mask->empty) { if (x < mask->x1) mask->x1 = x; if (y < mask->y1) mask->y1 = y; if ((x + w) > mask->x2) mask->x2 = (x + w); if ((y + h) > mask->y2) mask->y2 = (y + h); } else if (op == GIMP_CHANNEL_OP_REPLACE || mask->empty) { mask->empty = FALSE; mask->x1 = x; mask->y1 = y; mask->x2 = x + w; mask->y2 = y + h; } else { mask->bounds_known = FALSE; } gimp_drawable_update (GIMP_DRAWABLE (mask), x, y, w, h); }
TempBuf * gimp_paint_core_get_orig_proj (GimpPaintCore *core, GimpPickable *pickable, gint x, gint y, gint width, gint height) { TileManager *src_tiles; PixelRegion srcPR; PixelRegion destPR; Tile *saved_tile; gboolean release_tile; gint h; gint pixelwidth; gint pickable_width; gint pickable_height; const guchar *s; guchar *d; gpointer pr; g_return_val_if_fail (GIMP_IS_PAINT_CORE (core), NULL); g_return_val_if_fail (GIMP_IS_PICKABLE (pickable), NULL); g_return_val_if_fail (core->saved_proj_tiles != NULL, NULL); core->orig_proj_buf = temp_buf_resize (core->orig_proj_buf, gimp_pickable_get_bytes (pickable), x, y, width, height); src_tiles = gimp_pickable_get_tiles (pickable); pickable_width = tile_manager_width (src_tiles); pickable_height = tile_manager_height (src_tiles); gimp_rectangle_intersect (x, y, width, height, 0, 0, pickable_width, pickable_height, &x, &y, &width, &height); /* configure the pixel regions */ pixel_region_init (&srcPR, src_tiles, x, y, width, height, FALSE); pixel_region_init_temp_buf (&destPR, core->orig_proj_buf, x - core->orig_proj_buf->x, y - core->orig_proj_buf->y, width, height); for (pr = pixel_regions_register (2, &srcPR, &destPR); pr != NULL; pr = pixel_regions_process (pr)) { /* If the saved tile corresponding to this location is valid, use it */ saved_tile = tile_manager_get_tile (core->saved_proj_tiles, srcPR.x, srcPR.y, FALSE, FALSE); if (tile_is_valid (saved_tile)) { release_tile = TRUE; saved_tile = tile_manager_get_tile (core->saved_proj_tiles, srcPR.x, srcPR.y, TRUE, FALSE); s = tile_data_pointer (saved_tile, srcPR.x, srcPR.y); } else { release_tile = FALSE; s = srcPR.data; } d = destPR.data; pixelwidth = srcPR.w * srcPR.bytes; h = srcPR.h; while (h --) { memcpy (d, s, pixelwidth); s += srcPR.rowstride; d += destPR.rowstride; } if (release_tile) tile_release (saved_tile, FALSE); } return core->orig_proj_buf; }