void gimp_gegl_convolve (GeglBuffer *src_buffer, const GeglRectangle *src_rect, GeglBuffer *dest_buffer, const GeglRectangle *dest_rect, const gfloat *kernel, gint kernel_size, gdouble divisor, GimpConvolutionType mode, gboolean alpha_weighting) { GeglBufferIterator *dest_iter; gfloat *src; gint src_rowstride; const Babl *src_format; const Babl *dest_format; gint src_components; gint dest_components; src_format = gegl_buffer_get_format (src_buffer); if (babl_format_is_palette (src_format)) src_format = gimp_babl_format (GIMP_RGB, GIMP_PRECISION_FLOAT_LINEAR, babl_format_has_alpha (src_format)); else src_format = gimp_babl_format (gimp_babl_format_get_base_type (src_format), GIMP_PRECISION_FLOAT_LINEAR, babl_format_has_alpha (src_format)); dest_format = gegl_buffer_get_format (dest_buffer); if (babl_format_is_palette (dest_format)) dest_format = gimp_babl_format (GIMP_RGB, GIMP_PRECISION_FLOAT_LINEAR, babl_format_has_alpha (dest_format)); else dest_format = gimp_babl_format (gimp_babl_format_get_base_type (dest_format), GIMP_PRECISION_FLOAT_LINEAR, babl_format_has_alpha (dest_format)); src_components = babl_format_get_n_components (src_format); dest_components = babl_format_get_n_components (dest_format); /* Set up dest iterator */ dest_iter = gegl_buffer_iterator_new (dest_buffer, dest_rect, 0, dest_format, GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE); /* Get source pixel data */ src_rowstride = src_components * src_rect->width; src = g_malloc (sizeof(gfloat) * src_rowstride * src_rect->height); gegl_buffer_get (src_buffer, src_rect, 1.0, src_format, src, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); while (gegl_buffer_iterator_next (dest_iter)) { /* Convolve the src image using the convolution kernel, writing * to dest Convolve is not tile-enabled--use accordingly */ gfloat *dest = dest_iter->data[0]; const gint components = src_components; const gint a_component = components - 1; const gint margin = kernel_size / 2; const gint x1 = 0; const gint y1 = 0; const gint x2 = src_rect->width - 1; const gint y2 = src_rect->height - 1; const gint dest_x1 = dest_iter->roi[0].x; const gint dest_y1 = dest_iter->roi[0].y; const gint dest_x2 = dest_iter->roi[0].x + dest_iter->roi[0].width; const gint dest_y2 = dest_iter->roi[0].y + dest_iter->roi[0].height; gint x, y; gfloat offset; /* If the mode is NEGATIVE_CONVOL, the offset should be 128 */ if (mode == GIMP_NEGATIVE_CONVOL) { offset = 0.5; mode = GIMP_NORMAL_CONVOL; } else { offset = 0.0; } for (y = dest_y1; y < dest_y2; y++) { gfloat *d = dest; if (alpha_weighting) { for (x = dest_x1; x < dest_x2; x++) { const gfloat *m = kernel; gdouble total[4] = { 0.0, 0.0, 0.0, 0.0 }; gdouble weighted_divisor = 0.0; gint i, j, b; for (j = y - margin; j <= y + margin; j++) { for (i = x - margin; i <= x + margin; i++, m++) { gint xx = CLAMP (i, x1, x2); gint yy = CLAMP (j, y1, y2); const gfloat *s = src + yy * src_rowstride + xx * components; const gfloat a = s[a_component]; if (a) { gdouble mult_alpha = *m * a; weighted_divisor += mult_alpha; for (b = 0; b < a_component; b++) total[b] += mult_alpha * s[b]; total[a_component] += mult_alpha; } } } if (weighted_divisor == 0.0) weighted_divisor = divisor; for (b = 0; b < a_component; b++) total[b] /= weighted_divisor; total[a_component] /= divisor; for (b = 0; b < components; b++) { total[b] += offset; if (mode != GIMP_NORMAL_CONVOL && total[b] < 0.0) total[b] = - total[b]; *d++ = CLAMP (total[b], 0.0, 1.0); } } } else { for (x = dest_x1; x < dest_x2; x++) { const gfloat *m = kernel; gdouble total[4] = { 0.0, 0.0, 0.0, 0.0 }; gint i, j, b; for (j = y - margin; j <= y + margin; j++) { for (i = x - margin; i <= x + margin; i++, m++) { gint xx = CLAMP (i, x1, x2); gint yy = CLAMP (j, y1, y2); const gfloat *s = src + yy * src_rowstride + xx * components; for (b = 0; b < components; b++) total[b] += *m * s[b]; } } for (b = 0; b < components; b++) { total[b] = total[b] / divisor + offset; if (mode != GIMP_NORMAL_CONVOL && total[b] < 0.0) total[b] = - total[b]; *d++ = CLAMP (total[b], 0.0, 1.0); } } } dest += dest_iter->roi[0].width * dest_components; } } g_free (src); }
GtkWidget * convert_precision_dialog_new (GimpImage *image, GimpContext *context, GtkWidget *parent, GimpComponentType component_type, GimpProgress *progress) { ConvertDialog *dialog; GtkWidget *button; GtkWidget *main_vbox; GtkWidget *vbox; GtkWidget *hbox; GtkWidget *label; GtkWidget *frame; GtkWidget *combo; GtkSizeGroup *size_group; const gchar *enum_desc; gchar *blurb; GType dither_type; const Babl *format; gint bits; gboolean linear; g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); g_return_val_if_fail (GTK_IS_WIDGET (parent), NULL); g_return_val_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress), NULL); dialog = g_slice_new0 (ConvertDialog); /* a random format with precision */ format = gimp_babl_format (GIMP_RGB, gimp_babl_precision (component_type, FALSE), FALSE); bits = (babl_format_get_bytes_per_pixel (format) * 8 / babl_format_get_n_components (format)); linear = gimp_babl_format_get_linear (gimp_image_get_layer_format (image, FALSE)); dialog->image = image; dialog->progress = progress; dialog->component_type = component_type; dialog->linear = linear; dialog->bits = bits; /* gegl:color-reduction only does 16 bits */ if (bits <= 16) { dialog->layer_dither_type = saved_layer_dither_type; dialog->text_layer_dither_type = saved_text_layer_dither_type; dialog->mask_dither_type = saved_mask_dither_type; } gimp_enum_get_value (GIMP_TYPE_COMPONENT_TYPE, component_type, NULL, NULL, &enum_desc, NULL); blurb = g_strdup_printf (_("Convert Image to %s"), enum_desc); dialog->dialog = gimp_viewable_dialog_new (GIMP_VIEWABLE (image), context, _("Precision Conversion"), "gimp-image-convert-precision", GIMP_STOCK_CONVERT_PRECISION, blurb, parent, gimp_standard_help_func, GIMP_HELP_IMAGE_CONVERT_PRECISION, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, NULL); g_free (blurb); button = gtk_dialog_add_button (GTK_DIALOG (dialog->dialog), _("C_onvert"), GTK_RESPONSE_OK); gtk_button_set_image (GTK_BUTTON (button), gtk_image_new_from_icon_name (GIMP_STOCK_CONVERT_PRECISION, GTK_ICON_SIZE_BUTTON)); gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog->dialog), GTK_RESPONSE_OK, GTK_RESPONSE_CANCEL, -1); gtk_window_set_resizable (GTK_WINDOW (dialog->dialog), FALSE); g_object_weak_ref (G_OBJECT (dialog->dialog), (GWeakNotify) convert_precision_dialog_free, dialog); g_signal_connect (dialog->dialog, "response", G_CALLBACK (convert_precision_dialog_response), dialog); main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12); gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12); gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog->dialog))), main_vbox, TRUE, TRUE, 0); gtk_widget_show (main_vbox); /* dithering */ dither_type = gimp_gegl_get_op_enum_type ("gegl:color-reduction", "dither-strategy"); frame = gimp_frame_new (_("Dithering")); gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0); gtk_widget_show (frame); vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6); gtk_container_add (GTK_CONTAINER (frame), vbox); gtk_widget_show (vbox); /* gegl:color-reduction only does 16 bits */ gtk_widget_set_sensitive (vbox, bits <= 16); size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); /* layers */ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); gtk_widget_show (hbox); label = gtk_label_new_with_mnemonic (_("_Layers:")); gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); gtk_size_group_add_widget (size_group, label); gtk_widget_show (label); combo = gimp_enum_combo_box_new (dither_type); gtk_label_set_mnemonic_widget (GTK_LABEL (label), combo); gtk_box_pack_start (GTK_BOX (hbox), combo, TRUE, TRUE, 0); gtk_widget_show (combo); gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (combo), dialog->layer_dither_type, G_CALLBACK (gimp_int_combo_box_get_active), &dialog->layer_dither_type); /* text layers */ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); gtk_widget_show (hbox); label = gtk_label_new_with_mnemonic (_("_Text Layers:")); gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); gtk_size_group_add_widget (size_group, label); gtk_widget_show (label); combo = gimp_enum_combo_box_new (dither_type); gtk_label_set_mnemonic_widget (GTK_LABEL (label), combo); gtk_box_pack_start (GTK_BOX (hbox), combo, TRUE, TRUE, 0); gtk_widget_show (combo); gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (combo), dialog->text_layer_dither_type, G_CALLBACK (gimp_int_combo_box_get_active), &dialog->text_layer_dither_type); gimp_help_set_help_data (combo, _("Dithering text layers will make them uneditable"), NULL); /* channels */ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); gtk_widget_show (hbox); label = gtk_label_new_with_mnemonic (_("_Channels and Masks:")); gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); gtk_size_group_add_widget (size_group, label); gtk_widget_show (label); combo = gimp_enum_combo_box_new (dither_type); gtk_label_set_mnemonic_widget (GTK_LABEL (label), combo); gtk_box_pack_start (GTK_BOX (hbox), combo, TRUE, TRUE, 0); gtk_widget_show (combo); gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (combo), dialog->mask_dither_type, G_CALLBACK (gimp_int_combo_box_get_active), &dialog->mask_dither_type); g_object_unref (size_group); /* gamma */ frame = gimp_frame_new (_("Gamma")); gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0); gtk_widget_show (frame); vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6); gtk_container_add (GTK_CONTAINER (frame), vbox); gtk_widget_show (vbox); hbox = gimp_int_radio_group_new (FALSE, NULL, G_CALLBACK (gimp_radio_button_update), &dialog->linear, linear, _("Perceptual gamma (sRGB)"), FALSE, NULL, _("Linear light"), TRUE, NULL, NULL); gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); gtk_widget_show (hbox); return dialog->dialog; }
gint gegl_buffer_export_png (GeglBuffer *gegl_buffer, const gchar *path, gint compression, gint bd, gint src_x, gint src_y, gint width, gint height) { FILE *fp; gint i; png_struct *png; png_info *info; guchar *pixels; png_color_16 white; int png_color_type; gchar format_string[16]; const Babl *format; gint bit_depth = 8; if (!strcmp (path, "-")) { fp = stdout; } else { fp = fopen (path, "wb"); } if (!fp) { return -1; } { const Babl *babl = gegl_buffer_get_format (gegl_buffer); if (bd == 16) bit_depth = 16; else bit_depth = 8; if (babl_format_has_alpha (babl)) if (babl_format_get_n_components (babl) != 2) { png_color_type = PNG_COLOR_TYPE_RGB_ALPHA; strcpy (format_string, "R'G'B'A "); } else { png_color_type = PNG_COLOR_TYPE_GRAY_ALPHA; strcpy (format_string, "Y'A "); } else if (babl_format_get_n_components (babl) != 1) { png_color_type = PNG_COLOR_TYPE_RGB; strcpy (format_string, "R'G'B' "); } else { png_color_type = PNG_COLOR_TYPE_GRAY; strcpy (format_string, "Y' "); } } if (bit_depth == 16) strcat (format_string, "u16"); else strcat (format_string, "u8"); png = png_create_write_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (png == NULL) { if (stdout != fp) fclose (fp); return -1; } info = png_create_info_struct (png); if (setjmp (png_jmpbuf (png))) { if (stdout != fp) fclose (fp); return -1; } png_set_compression_level (png, compression); png_init_io (png, fp); png_set_IHDR (png, info, width, height, bit_depth, png_color_type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_DEFAULT); if (png_color_type == PNG_COLOR_TYPE_RGB || png_color_type == PNG_COLOR_TYPE_RGB_ALPHA) { white.red = 0xff; white.blue = 0xff; white.green = 0xff; } else white.gray = 0xff; png_set_bKGD (png, info, &white); png_write_info (png, info); #if BYTE_ORDER == LITTLE_ENDIAN if (bit_depth > 8) png_set_swap (png); #endif format = babl_format (format_string); pixels = g_malloc0 (width * babl_format_get_bytes_per_pixel (format)); for (i=0; i< height; i++) { GeglRectangle rect; rect.x = src_x; rect.y = src_y+i; rect.width = width; rect.height = 1; gegl_buffer_get (gegl_buffer, &rect, 1.0, babl_format (format_string), pixels, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); png_write_rows (png, &pixels, 1); } png_write_end (png, info); png_destroy_write_struct (&png, &info); g_free (pixels); if (stdout != fp) fclose (fp); return 0; }
static gboolean process (GeglOperation *operation, void *in_buf, void *out_buf, glong samples, const GeglRectangle *roi, gint level) { GeglProperties *o = GEGL_PROPERTIES (operation); const Babl *format = gegl_operation_get_format (operation, "input"); gfloat *in = in_buf; gfloat *out = out_buf; gint component_index = 0; gint n_components; gdouble min = 0.0; gdouble max = 1.0; n_components = babl_format_get_n_components (format); switch (o->component) { case GEGL_COMPONENT_EXTRACT_RGB_RED: case GEGL_COMPONENT_EXTRACT_HUE: case GEGL_COMPONENT_EXTRACT_CMYK_CYAN: case GEGL_COMPONENT_EXTRACT_YCBCR_Y: case GEGL_COMPONENT_EXTRACT_LAB_L: component_index = 0; if (o->component == GEGL_COMPONENT_EXTRACT_LAB_L) { max = 100.0; } break; case GEGL_COMPONENT_EXTRACT_RGB_GREEN: case GEGL_COMPONENT_EXTRACT_HSV_SATURATION: case GEGL_COMPONENT_EXTRACT_HSL_SATURATION: case GEGL_COMPONENT_EXTRACT_CMYK_MAGENTA: case GEGL_COMPONENT_EXTRACT_YCBCR_CB: case GEGL_COMPONENT_EXTRACT_LAB_A: case GEGL_COMPONENT_EXTRACT_LCH_C: case GEGL_COMPONENT_EXTRACT_ALPHA: component_index = 1; if (o->component == GEGL_COMPONENT_EXTRACT_YCBCR_CB) { min = -0.5; max = 0.5; } else if (o->component == GEGL_COMPONENT_EXTRACT_LAB_A) { min = -127.5; max = 127.5; } else if (o->component == GEGL_COMPONENT_EXTRACT_LCH_C) { max = 200.0; } break; case GEGL_COMPONENT_EXTRACT_RGB_BLUE: case GEGL_COMPONENT_EXTRACT_HSV_VALUE: case GEGL_COMPONENT_EXTRACT_HSL_LIGHTNESS: case GEGL_COMPONENT_EXTRACT_CMYK_YELLOW: case GEGL_COMPONENT_EXTRACT_YCBCR_CR: case GEGL_COMPONENT_EXTRACT_LAB_B: case GEGL_COMPONENT_EXTRACT_LCH_H: component_index = 2; if (o->component == GEGL_COMPONENT_EXTRACT_YCBCR_CR) { min = -0.5; max = 0.5; } else if (o->component == GEGL_COMPONENT_EXTRACT_LAB_B) { min = -127.5; max = 127.5; } else if (o->component == GEGL_COMPONENT_EXTRACT_LCH_H) { min = 0.0; max = 360.0; } break; case GEGL_COMPONENT_EXTRACT_CMYK_KEY: component_index = 3; break; } while (samples--) { gdouble value = in[component_index]; if (min != 0.0 || max != 1.0) { gdouble scale = 1.0 / (max - min); gdouble offset = -min; value = CLAMP ((value + offset) * scale, 0.0, 1.0); } if (o->invert) out[0] = 1.0 - value; else out[0] = value; in += n_components; out += 1; } return TRUE; }