static gboolean gimp_crop_tool_execute (GimpRectangleTool *rectangle, gint x, gint y, gint w, gint h) { GimpTool *tool = GIMP_TOOL (rectangle); GimpCropOptions *options = GIMP_CROP_TOOL_GET_OPTIONS (tool); GimpImage *image = gimp_display_get_image (tool->display); gimp_tool_pop_status (tool, tool->display); /* if rectangle exists, crop it */ if (w > 0 && h > 0) { if (options->layer_only) { GimpLayer *layer = gimp_image_get_active_layer (image); gint off_x, off_y; if (! layer) { gimp_tool_message_literal (tool, tool->display, _("There is no active layer to crop.")); return FALSE; } if (gimp_item_is_content_locked (GIMP_ITEM (layer))) { gimp_tool_message_literal (tool, tool->display, _("The active layer's pixels are locked.")); return FALSE; } gimp_item_get_offset (GIMP_ITEM (layer), &off_x, &off_y); off_x -= x; off_y -= y; gimp_item_resize (GIMP_ITEM (layer), GIMP_CONTEXT (options), w, h, off_x, off_y); } else { gimp_image_crop (image, GIMP_CONTEXT (options), x, y, w + x, h + y, TRUE); } gimp_image_flush (image); return TRUE; } return TRUE; }
/* -------------------------- * p_create_corpus_layer * -------------------------- * create the corpus layer that builds the reference pattern * for the resynthesizer call. The reference pattern is built * as duplicate of the original image reduced to the area around the current selection. * (grown by corpus_border_radius) * Note that the duplicate image has a selection, that includes the gorwn area * around the orignal selection, but EXCLUDES the original selection * (that is the area holding the object that has to be replaced * by pattern of the surrounding area) * returns the layer_id of the reference pattern. */ static gint32 p_create_corpus_layer(gint32 image_id, gint32 drawable_id, TransValues *val_ptr) { gint32 dup_image_id; gint32 channel_id; gint32 channel_2_id; GimpRGB bck_color; GimpRGB white_opaque_color; /* gboolean has_selection; */ gboolean non_empty; gint x1, y1, x2, y2; gint32 active_layer_stackposition; gint32 active_dup_layer_id; active_layer_stackposition = gap_layer_get_stackposition(image_id, drawable_id); dup_image_id = gimp_image_duplicate(image_id); channel_id = gimp_selection_save(dup_image_id); gimp_selection_grow(dup_image_id, val_ptr->corpus_border_radius); gimp_selection_invert(dup_image_id); gimp_context_get_background(&bck_color); channel_2_id = gimp_selection_save(dup_image_id); gimp_image_select_item(dup_image_id, GIMP_CHANNEL_OP_REPLACE, channel_id); gimp_rgba_set_uchar (&white_opaque_color, 255, 255, 255, 255); gimp_context_set_background(&white_opaque_color); gimp_edit_clear(channel_2_id); gimp_context_set_background(&bck_color); /* restore original background color */ gimp_selection_load(channel_2_id); gimp_selection_invert(dup_image_id); /* has_selection = */ gimp_selection_bounds(dup_image_id, &non_empty, &x1, &y1, &x2, &y2); gimp_image_crop(dup_image_id, (x2 - x1), (y2 - y1), x1, y1); gimp_selection_invert(dup_image_id); active_dup_layer_id = gap_layer_get_id_by_stackposition(dup_image_id, active_layer_stackposition); if (1==0) { /* debug code shows the duplicate image by adding a display */ gimp_display_new(dup_image_id); } return (active_dup_layer_id); } /* end p_create_corpus_layer */
static gboolean apply_crop(crop_settings settings, image_output out) { gboolean success = TRUE; gint newWidth, newHeight, oldWidth, oldHeight, posX, posY; oldWidth = gimp_image_width(out->image_id); oldHeight = gimp_image_height(out->image_id); if (settings->manual) { newWidth = settings->new_w; newHeight = settings->new_h; posX = (oldWidth - newWidth) / 2; posY = (oldHeight - newHeight) / 2; } else { float ratio1, ratio2; if (settings->ratio == CROP_PRESET_CUSTOM) { ratio1 = settings->custom_ratio1; ratio2 = settings->custom_ratio2; } else { ratio1 = (float)crop_preset_ratio[settings->ratio][0]; ratio2 = (float)crop_preset_ratio[settings->ratio][1]; } if (( (float)oldWidth / oldHeight ) > ( ratio1 / ratio2) ) { /* crop along the width */ newHeight = oldHeight; newWidth = round(( ratio1 * (float)newHeight ) / ratio2); posX = (oldWidth - newWidth) / 2; posY = 0; } else { /* crop along the height */ newWidth = oldWidth; newHeight = round(( ratio2 * (float)newWidth) / ratio1); posX = 0; posY = (oldHeight - newHeight) / 2; } } success = gimp_image_crop ( out->image_id, newWidth, newHeight, posX, posY ); return success; }
void image_crop_cmd_callback (GtkAction *action, gpointer data) { GimpImage *image; GtkWidget *widget; gint x1, y1, x2, y2; return_if_no_image (image, data); return_if_no_widget (widget, data); if (! gimp_channel_bounds (gimp_image_get_mask (image), &x1, &y1, &x2, &y2)) { gimp_message (image->gimp, G_OBJECT (widget), GIMP_MESSAGE_WARNING, _("Cannot crop because the current selection is empty.")); return; } gimp_image_crop (image, action_data_get_context (data), x1, y1, x2, y2, FALSE, TRUE); gimp_image_flush (image); }
static GimpValueArray * image_crop_invoker (GimpProcedure *procedure, Gimp *gimp, GimpContext *context, GimpProgress *progress, const GimpValueArray *args, GError **error) { gboolean success = TRUE; GimpImage *image; gint32 new_width; gint32 new_height; gint32 offx; gint32 offy; image = gimp_value_get_image (gimp_value_array_index (args, 0), gimp); new_width = g_value_get_int (gimp_value_array_index (args, 1)); new_height = g_value_get_int (gimp_value_array_index (args, 2)); offx = g_value_get_int (gimp_value_array_index (args, 3)); offy = g_value_get_int (gimp_value_array_index (args, 4)); if (success) { if (new_width > gimp_image_get_width (image) || new_height > gimp_image_get_height (image) || offx > (gimp_image_get_width (image) - new_width) || offy > (gimp_image_get_height (image) - new_height)) success = FALSE; else gimp_image_crop (image, context, offx, offy, offx + new_width, offy + new_height, TRUE); } return gimp_procedure_get_return_values (procedure, success, error ? *error : NULL); }
static void guillotine (gint32 image_ID) { gint guide; gint image_width; gint image_height; gboolean guides_found = FALSE; GList *hguides, *hg; GList *vguides, *vg; image_width = gimp_image_width (image_ID); image_height = gimp_image_height (image_ID); hguides = g_list_append (NULL, GINT_TO_POINTER (0)); hguides = g_list_append (hguides, GINT_TO_POINTER (image_height)); vguides = g_list_append (NULL, GINT_TO_POINTER (0)); vguides = g_list_append (vguides, GINT_TO_POINTER (image_width)); for (guide = gimp_image_find_next_guide (image_ID, 0); guide > 0; guide = gimp_image_find_next_guide (image_ID, guide)) { gint position = gimp_image_get_guide_position (image_ID, guide); switch (gimp_image_get_guide_orientation (image_ID, guide)) { case GIMP_ORIENTATION_HORIZONTAL: if (! g_list_find (hguides, GINT_TO_POINTER (position))) { hguides = g_list_insert_sorted (hguides, GINT_TO_POINTER (position), guide_sort_func); guides_found = TRUE; } break; case GIMP_ORIENTATION_VERTICAL: if (! g_list_find (vguides, GINT_TO_POINTER (position))) { vguides = g_list_insert_sorted (vguides, GINT_TO_POINTER (position), guide_sort_func); guides_found = TRUE; } break; case GIMP_ORIENTATION_UNKNOWN: g_assert_not_reached (); break; } } if (guides_found) { gchar *filename; gint h, v, hpad, vpad; gint x, y; gchar *hformat; gchar *format; filename = gimp_image_get_filename (image_ID); if (!filename) filename = g_strdup (_("Untitled")); /* get the number horizontal and vertical slices */ h = g_list_length (hguides); v = g_list_length (vguides); /* need the number of digits of h and v for the padding */ hpad = log10(h) + 1; vpad = log10(v) + 1; /* format for the x-y coordinates in the filename */ hformat = g_strdup_printf ("%%0%i", MAX (hpad, vpad)); format = g_strdup_printf ("-%si-%si", hformat, hformat); /* Do the actual dup'ing and cropping... this isn't a too naive a * way to do this since we got copy-on-write tiles, either. */ for (y = 0, hg = hguides; hg && hg->next; y++, hg = hg->next) { for (x = 0, vg = vguides; vg && vg->next; x++, vg = vg->next) { gint32 new_image = gimp_image_duplicate (image_ID); GString *new_filename; gchar *fileextension; gchar *fileindex; gint pos; if (new_image == -1) { g_warning ("Couldn't create new image."); return; } gimp_image_undo_disable (new_image); gimp_image_crop (new_image, GPOINTER_TO_INT (vg->next->data) - GPOINTER_TO_INT (vg->data), GPOINTER_TO_INT (hg->next->data) - GPOINTER_TO_INT (hg->data), GPOINTER_TO_INT (vg->data), GPOINTER_TO_INT (hg->data)); new_filename = g_string_new (filename); /* show the rough coordinates of the image in the title */ fileindex = g_strdup_printf (format, x, y); /* get the position of the file extension - last . in filename */ fileextension = strrchr (new_filename->str, '.'); pos = fileextension - new_filename->str; /* insert the coordinates before the extension */ g_string_insert (new_filename, pos, fileindex); g_free (fileindex); gimp_image_set_filename (new_image, new_filename->str); g_string_free (new_filename, TRUE); while ((guide = gimp_image_find_next_guide (new_image, 0))) gimp_image_delete_guide (new_image, guide); gimp_image_undo_enable (new_image); gimp_display_new (new_image); } } g_free (filename); g_free (hformat); g_free (format); } g_list_free (hguides); g_list_free (vguides); }
static gboolean apply_crop(crop_settings settings, image_output out) { gboolean success = TRUE; gint newWidth, newHeight, oldWidth, oldHeight, posX = 0, posY = 0; gboolean keepX = FALSE, keepY = FALSE; oldWidth = gimp_image_width(out->image_id); oldHeight = gimp_image_height(out->image_id); if (settings->manual) { newWidth = min(oldWidth, settings->new_w); newHeight = min(oldHeight, settings->new_h); } else { float ratio1, ratio2; if (settings->ratio == CROP_PRESET_CUSTOM) { ratio1 = settings->custom_ratio1; ratio2 = settings->custom_ratio2; } else { ratio1 = (float)crop_preset_ratio[settings->ratio][0]; ratio2 = (float)crop_preset_ratio[settings->ratio][1]; } if (( (float)oldWidth / oldHeight ) > ( ratio1 / ratio2) ) { // crop along the width newHeight = oldHeight; newWidth = round(( ratio1 * (float)newHeight ) / ratio2); keepY = TRUE; } else { // crop along the height newWidth = oldWidth; newHeight = round(( ratio2 * (float)newWidth) / ratio1); keepX = TRUE; } } switch (settings->start_pos) { case CROP_START_TL: posX = 0; posY = 0; break; case CROP_START_TR: posX = (oldWidth - newWidth); posY = 0; break; case CROP_START_BL: posX = 0; posY = (oldHeight - newHeight); break; case CROP_START_BR: posX = (oldWidth - newWidth); posY = (oldHeight - newHeight); break; default: if (!keepX) posX = (oldWidth - newWidth) / 2; if (!keepY) posY = (oldHeight - newHeight) / 2; break; } success = gimp_image_crop ( out->image_id, newWidth, newHeight, posX, posY ); return success; }
static gboolean webx_pipeline_check_update (WebxPipeline *pipeline) { gint *layers; gint num_layers; gint i; g_return_val_if_fail (WEBX_IS_PIPELINE (pipeline), FALSE); if (pipeline->rgb_image != -1) { gimp_image_delete (pipeline->rgb_image); pipeline->rgb_image = -1; } if (pipeline->indexed_image != -1) { gimp_image_delete (pipeline->indexed_image); pipeline->indexed_image = -1; } if (pipeline->background) { g_object_unref (pipeline->background); pipeline->background = NULL; } pipeline->rgb_image = gimp_image_duplicate (pipeline->user_image); gimp_image_undo_disable (pipeline->rgb_image); pipeline->rgb_layer = gimp_image_merge_visible_layers (pipeline->rgb_image, GIMP_CLIP_TO_IMAGE); /* make sure there is only one layer, where all visible layers were merged */ layers = gimp_image_get_layers (pipeline->rgb_image, &num_layers); for (i = 0; i < num_layers; i++) { if (layers[i] != pipeline->rgb_layer) gimp_image_remove_layer (pipeline->rgb_image, layers[i]); } g_free (layers); /* we don't want layer to be smaller than image */ gimp_layer_resize_to_image_size (pipeline->rgb_layer); gimp_image_scale (pipeline->rgb_image, pipeline->resize_width, pipeline->resize_height); webx_pipeline_create_background (pipeline); pipeline->crop_offsx *= pipeline->crop_scale_x; pipeline->crop_offsy *= pipeline->crop_scale_y; pipeline->crop_width *= pipeline->crop_scale_x; pipeline->crop_height *= pipeline->crop_scale_y; pipeline->crop_scale_x = 1.0; pipeline->crop_scale_y = 1.0; webx_pipeline_crop_clip (pipeline); if (pipeline->crop_width != pipeline->resize_width || pipeline->crop_height != pipeline->resize_height ) { gimp_image_crop (pipeline->rgb_image, pipeline->crop_width, pipeline->crop_height, pipeline->crop_offsx, pipeline->crop_offsy); } if (gimp_drawable_is_indexed (pipeline->rgb_layer)) { pipeline->indexed_image = gimp_image_duplicate (pipeline->rgb_image); gimp_image_undo_disable (pipeline->indexed_image); pipeline->indexed_layer = gimp_image_merge_visible_layers (pipeline->indexed_image, GIMP_CLIP_TO_IMAGE); } else { pipeline->indexed_image = -1; pipeline->indexed_layer = -1; } if ( ! gimp_drawable_is_rgb (pipeline->rgb_layer)) gimp_image_convert_rgb (pipeline->rgb_image); return TRUE; }
static void do_acrop (GimpDrawable *drawable, gint32 image_id) { GimpPixelRgn srcPR; gint iwidth, iheight; gint x, y, l; guchar *buffer; gint xmin, xmax, ymin, ymax; gint *layers = NULL; gint numlayers; iwidth = gimp_image_width(image_id); iheight = gimp_image_height(image_id); /* Set each edge to the opposite side -- i.e. xmin (the left edge * of the final cropped image) starts at the right edge. */ xmin = iwidth - 1; xmax = 1; ymin = iheight - 1; ymax = 1; buffer = g_malloc ((iwidth > iheight ? iwidth : iheight) * drawable->bpp); layers = gimp_image_get_layers (image_id, &numlayers); for (l=0; l<numlayers; ++l) { gint dwidth, dheight; gint layerOffsetX, layerOffsetY; gint bytes; gint start; drawable = gimp_drawable_get(layers[l]); dwidth = drawable->width; dheight = drawable->height; bytes = drawable->bpp; /* Relate the layer coordinates to the image coordinates */ gimp_drawable_offsets (layers[l], &layerOffsetX, &layerOffsetY); /* initialize the pixel region to this layer */ gimp_pixel_rgn_init (&srcPR, drawable, 0, 0, dwidth, dheight, FALSE, FALSE); /* Update ymin. If the layer offset is greater than ymin, * then the region to be cropped already contains the whole layer * and we don't have to look any farther. */ start = 0; if (layerOffsetY < 0) start = -layerOffsetY; for (y = start; y < dheight && layerOffsetY + y < ymin; y++) { gimp_pixel_rgn_get_row (&srcPR, buffer, 0, y, dwidth); for (x = 0; x < dwidth * bytes; x += bytes) { if (!colours_equal (buffer, &buffer[x], bytes)) { /* convert this layer coordinate back to image coord */ ymin = y + layerOffsetY; break; } } } /* Update ymax. If the layer's offset plus height is less than * ymax, then the region to be cropped already contains the * whole layer and we don't have to look any farther. */ start = dheight - 1; if (layerOffsetY + dheight > iheight) start = iheight - layerOffsetY - 1; for (y = start; y > 0 && layerOffsetY + y > ymax; y--) { gimp_pixel_rgn_get_row (&srcPR, buffer, 0, y, dwidth); for (x = 0; x < dwidth * bytes; x += bytes) { if (!colours_equal (buffer, &buffer[x], bytes)) { /* convert this layer coordinate back to image coord */ ymax = y + layerOffsetY + 1; break; } } } /* Update xmin. If the layer offset is greater than ymin, * then the region to be cropped already contains the whole layer * and we don't have to look any farther. */ start = 0; if (layerOffsetX < 0) start = -layerOffsetX; for (x = start; x < dwidth && layerOffsetX + x < xmin; x++) { gimp_pixel_rgn_get_col (&srcPR, buffer, x, 0, dheight); for (y = 0; y < dheight * bytes; y += bytes) { if (!colours_equal (buffer, &buffer[y], bytes)) { /* convert this layer coordinate back to image coord */ xmin = x + layerOffsetX; break; } } } /* Update xmax. If the layer's offset plus width is less than * xmax, then the region to be cropped already contains the * whole layer and we don't have to look any farther. */ start = dwidth - 1; if (layerOffsetX + dwidth > iwidth) start = iwidth - layerOffsetX - 1; for (x = start; x > 0 && layerOffsetX + x > xmax; x--) { gimp_pixel_rgn_get_col (&srcPR, buffer, x, 0, dheight); for (y = 0; y < dheight * bytes; y += bytes) { if (!colours_equal (buffer, &buffer[y], bytes)) { /* convert this layer coordinate back to image coord */ xmax = x + layerOffsetX + 1; break; } } } gimp_progress_update ((gdouble)l / numlayers); } if (xmin == 0 && xmax == iwidth && ymin == 0 && ymax == iheight) { g_message ("Nothing to crop."); return; } gimp_image_undo_group_start (image_id); gimp_image_crop(image_id, xmax - xmin, ymax - ymin, xmin, ymin); g_free (layers); g_free (buffer); gimp_progress_update (1.00); gimp_image_undo_group_end (image_id); }
static void do_zcrop (GimpDrawable *drawable, gint32 image_id) { GimpPixelRgn srcPR, destPR; gint width, height, x, y; gint bytes; guchar *buffer; gint8 *killrows; gint8 *killcols; gint32 livingrows, livingcols, destrow, destcol; gint total_area, area; gboolean has_alpha; width = drawable->width; height = drawable->height; bytes = drawable->bpp; total_area = width * height * 4; area = 0; killrows = g_new (gint8, height); killcols = g_new (gint8, width); buffer = g_malloc ((width > height ? width : height) * bytes); /* initialize the pixel regions */ gimp_pixel_rgn_init (&srcPR, drawable, 0, 0, width, height, FALSE, FALSE); gimp_pixel_rgn_init (&destPR, drawable, 0, 0, width, height, TRUE, TRUE); has_alpha = gimp_drawable_has_alpha (drawable->drawable_id); livingrows = 0; for (y = 0; y < height; y++) { gimp_pixel_rgn_get_row (&srcPR, buffer, 0, y, width); killrows[y] = TRUE; for (x = 0; x < width * bytes; x += bytes) { if (! colors_equal (buffer, &buffer[x], bytes, has_alpha)) { livingrows++; killrows[y] = FALSE; break; } } area += width; if (y % 20 == 0) gimp_progress_update ((double) area / (double) total_area); } livingcols = 0; for (x = 0; x < width; x++) { gimp_pixel_rgn_get_col (&srcPR, buffer, x, 0, height); killcols[x] = TRUE; for (y = 0; y < height * bytes; y += bytes) { if (! colors_equal (buffer, &buffer[y], bytes, has_alpha)) { livingcols++; killcols[x] = FALSE; break; } } area += height; if (x % 20 == 0) gimp_progress_update ((double) area / (double) total_area); } if ((livingcols == 0 || livingrows==0) || (livingcols == width && livingrows == height)) { g_message (_("Nothing to crop.")); g_free (killrows); g_free (killcols); return; } destrow = 0; for (y = 0; y < height; y++) { if (!killrows[y]) { gimp_pixel_rgn_get_row (&srcPR, buffer, 0, y, width); gimp_pixel_rgn_set_row (&destPR, buffer, 0, destrow, width); destrow++; } area += width; if (y % 20 == 0) gimp_progress_update ((double) area / (double) total_area); } destcol = 0; gimp_pixel_rgn_init(&srcPR, drawable, 0, 0, width, height, FALSE, TRUE); for (x = 0; x < width; x++) { if (!killcols[x]) { gimp_pixel_rgn_get_col (&srcPR, buffer, x, 0, height); gimp_pixel_rgn_set_col (&destPR, buffer, destcol, 0, height); destcol++; } area += height; if (x % 20 == 0) gimp_progress_update ((double) area / (double) total_area); } g_free (buffer); g_free (killrows); g_free (killcols); gimp_progress_update (1.00); gimp_image_undo_group_start (image_id); gimp_drawable_flush (drawable); gimp_drawable_merge_shadow (drawable->drawable_id, TRUE); gimp_image_crop (image_id, livingcols, livingrows, 0, 0); gimp_image_undo_group_end (image_id); }
static void do_zcrop (gint32 drawable_id, gint32 image_id) { GeglBuffer *drawable_buffer; GeglBuffer *shadow_buffer; gfloat *linear_buf; const Babl *format; gint x, y, width, height; gint components; gint8 *killrows; gint8 *killcols; gint32 livingrows, livingcols, destrow, destcol; gint32 selection_copy_id; gboolean has_alpha; drawable_buffer = gimp_drawable_get_buffer (drawable_id); shadow_buffer = gimp_drawable_get_shadow_buffer (drawable_id); width = gegl_buffer_get_width (drawable_buffer); height = gegl_buffer_get_height (drawable_buffer); has_alpha = gimp_drawable_has_alpha (drawable_id); if (has_alpha) format = babl_format ("R'G'B'A float"); else format = babl_format ("R'G'B' float"); components = babl_format_get_n_components (format); killrows = g_new (gint8, height); killcols = g_new (gint8, width); linear_buf = g_new (gfloat, (width > height ? width : height) * components); /* search which rows to remove */ livingrows = 0; for (y = 0; y < height; y++) { gegl_buffer_get (drawable_buffer, GEGL_RECTANGLE (0, y, width, 1), 1.0, format, linear_buf, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); killrows[y] = TRUE; for (x = components; x < width * components; x += components) { if (! colors_equal (linear_buf, &linear_buf[x], components, has_alpha)) { livingrows++; killrows[y] = FALSE; break; } } } gimp_progress_update (0.25); /* search which columns to remove */ livingcols = 0; for (x = 0; x < width; x++) { gegl_buffer_get (drawable_buffer, GEGL_RECTANGLE (x, 0, 1, height), 1.0, format, linear_buf, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); killcols[x] = TRUE; for (y = components; y < height * components; y += components) { if (! colors_equal (linear_buf, &linear_buf[y], components, has_alpha)) { livingcols++; killcols[x] = FALSE; break; } } } gimp_progress_update (0.5); if ((livingcols == 0 || livingrows == 0) || (livingcols == width && livingrows == height)) { g_message (_("Nothing to crop.")); g_free (linear_buf); g_free (killrows); g_free (killcols); return; } /* restitute living rows */ destrow = 0; for (y = 0; y < height; y++) { if (!killrows[y]) { gegl_buffer_copy (drawable_buffer, GEGL_RECTANGLE (0, y, width, 1), GEGL_ABYSS_NONE, shadow_buffer, GEGL_RECTANGLE (0, destrow, width, 1)); destrow++; } } gimp_progress_update (0.75); /* restitute living columns */ destcol = 0; for (x = 0; x < width; x++) { if (!killcols[x]) { gegl_buffer_copy (shadow_buffer, GEGL_RECTANGLE (x, 0, 1, height), GEGL_ABYSS_NONE, shadow_buffer, GEGL_RECTANGLE (destcol, 0, 1, height)); destcol++; } } gimp_progress_update (1.00); gimp_image_undo_group_start (image_id); selection_copy_id = gimp_selection_save (image_id); gimp_selection_none (image_id); gegl_buffer_flush (shadow_buffer); gimp_drawable_merge_shadow (drawable_id, TRUE); gegl_buffer_flush (drawable_buffer); gimp_image_select_item (image_id, GIMP_CHANNEL_OP_REPLACE, selection_copy_id); gimp_image_remove_channel (image_id, selection_copy_id); gimp_image_crop (image_id, livingcols, livingrows, 0, 0); gimp_image_undo_group_end (image_id); g_object_unref (shadow_buffer); g_object_unref (drawable_buffer); g_free (linear_buf); g_free (killrows); g_free (killcols); }