static void bilateral_prepare (GeglOperation *operation) { gegl_operation_set_format (operation, "input", babl_format ("RGBA float")); gegl_operation_set_format (operation, "output", babl_format ("RGBA float")); }
static gboolean process (GeglOperation *operation, GeglBuffer *input, GeglBuffer *output, const GeglRectangle *result, gint level) { GeglOperationAreaFilter *area = GEGL_OPERATION_AREA_FILTER (operation); GeglProperties *o = GEGL_PROPERTIES (operation); GeglBuffer *dest, *dest_tmp; GeglRectangle working_region; GeglRectangle *whole_region; GeglBufferIterator *iter; whole_region = gegl_operation_source_get_bounding_box (operation, "input"); working_region.x = result->x - area->left; working_region.width = result->width + area->left + area->right; working_region.y = result->y - area->top; working_region.height = result->height + area->top + area->bottom; gegl_rectangle_intersect (&working_region, &working_region, whole_region); dest_tmp = gegl_buffer_new (&working_region, babl_format ("Y' float")); iter = gegl_buffer_iterator_new (dest_tmp, &working_region, 0, babl_format ("Y' float"), GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE); gegl_buffer_iterator_add (iter, input, &working_region, 0, babl_format ("Y' float"), GEGL_ACCESS_READ, GEGL_ABYSS_NONE); while (gegl_buffer_iterator_next (iter)) { gint i; gfloat *data_out = iter->data[0]; gfloat *data_in = iter->data[1]; for (i = 0; i < iter->length; i++) { /* compute sigmoidal transfer */ gfloat val = *data_in; val = 1.0 / (1.0 + exp (-(SIGMOIDAL_BASE + (o->sharpness * SIGMOIDAL_RANGE)) * (val - 0.5))); val = val * o->brightness; *data_out = CLAMP (val, 0.0, 1.0); data_out +=1; data_in +=1; } } dest = grey_blur_buffer (dest_tmp, o->glow_radius, result); iter = gegl_buffer_iterator_new (output, result, 0, babl_format ("RGBA float"), GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE); gegl_buffer_iterator_add (iter, input, result, 0, babl_format ("RGBA float"), GEGL_ACCESS_READ, GEGL_ABYSS_NONE); gegl_buffer_iterator_add (iter, dest, result, 0, babl_format ("Y' float"), GEGL_ACCESS_READ, GEGL_ABYSS_NONE); while (gegl_buffer_iterator_next (iter)) { gint i; gfloat *data_out = iter->data[0]; gfloat *data_in = iter->data[1]; gfloat *data_blur = iter->data[2]; for (i = 0; i < iter->length; i++) { gint c; for (c = 0; c < 3; c++) { gfloat tmp = (1.0 - data_in[c]) * (1.0 - *data_blur); data_out[c] = CLAMP (1.0 - tmp, 0.0, 1.0); } data_out[3] = data_in[3]; data_out += 4; data_in += 4; data_blur+= 1; } } g_object_unref (dest); g_object_unref (dest_tmp); return TRUE; }
static void gimp_mypaint_surface_get_color (MyPaintSurface *base_surface, float x, float y, float radius, float *color_r, float *color_g, float *color_b, float *color_a) { GimpMybrushSurface *surface = (GimpMybrushSurface *)base_surface; GeglRectangle dabRect; if (radius < 1.0f) radius = 1.0f; dabRect = calculate_dab_roi (x, y, radius); *color_r = 0.0f; *color_g = 0.0f; *color_b = 0.0f; *color_a = 0.0f; if (dabRect.width > 0 || dabRect.height > 0) { const float one_over_radius2 = 1.0f / (radius * radius); float sum_weight = 0.0f; float sum_r = 0.0f; float sum_g = 0.0f; float sum_b = 0.0f; float sum_a = 0.0f; /* Read in clamp mode to avoid transparency bleeding in at the edges */ GeglBufferIterator *iter = gegl_buffer_iterator_new (surface->buffer, &dabRect, 0, babl_format ("R'aG'aB'aA float"), GEGL_BUFFER_READ, GEGL_ABYSS_CLAMP); if (surface->paint_mask) { GeglRectangle mask_roi = dabRect; mask_roi.x -= surface->paint_mask_x; mask_roi.y -= surface->paint_mask_y; gegl_buffer_iterator_add (iter, surface->paint_mask, &mask_roi, 0, babl_format ("Y float"), GEGL_ACCESS_READ, GEGL_ABYSS_NONE); } while (gegl_buffer_iterator_next (iter)) { float *pixel = (float *)iter->data[0]; float *mask; int iy, ix; if (surface->paint_mask) mask = iter->data[1]; else mask = NULL; for (iy = iter->roi[0].y; iy < iter->roi[0].y + iter->roi[0].height; iy++) { float yy = (iy + 0.5f - y); for (ix = iter->roi[0].x; ix < iter->roi[0].x + iter->roi[0].width; ix++) { /* pixel_weight == a standard dab with hardness = 0.5, aspect_ratio = 1.0, and angle = 0.0 */ float xx = (ix + 0.5f - x); float rr = (yy * yy + xx * xx) * one_over_radius2; float pixel_weight = 0.0f; if (rr <= 1.0f) pixel_weight = 1.0f - rr; if (mask) pixel_weight *= *mask; sum_r += pixel_weight * pixel[RED]; sum_g += pixel_weight * pixel[GREEN]; sum_b += pixel_weight * pixel[BLUE]; sum_a += pixel_weight * pixel[ALPHA]; sum_weight += pixel_weight; pixel += 4; if (mask) mask += 1; } } } if (sum_a > 0.0f && sum_weight > 0.0f) { sum_r /= sum_weight; sum_g /= sum_weight; sum_b /= sum_weight; sum_a /= sum_weight; sum_r /= sum_a; sum_g /= sum_a; sum_b /= sum_a; /* FIXME: Clamping is wrong because GEGL allows alpha > 1, this should probably re-multipy things */ *color_r = CLAMP(sum_r, 0.0f, 1.0f); *color_g = CLAMP(sum_g, 0.0f, 1.0f); *color_b = CLAMP(sum_b, 0.0f, 1.0f); *color_a = CLAMP(sum_a, 0.0f, 1.0f); } } }
static void prepare (GeglOperation *operation) { gegl_operation_set_format (operation, "output", babl_format ("R'G'B'A float")); }
static void prepare (GeglOperation *operation) { gegl_operation_set_format (operation, "input", babl_format ("RGBA u16")); gegl_operation_set_format (operation, "output", babl_format ("RGBA u16")); }
static gboolean process (GeglOperation *operation, GeglBuffer *input, GeglBuffer *output, const GeglRectangle *result, gint level) { GeglProperties *o = GEGL_PROPERTIES (operation); GeglSampler *sampler = gegl_buffer_sampler_new_at_level (input, babl_format ("RGBA float"), o->sampler_type, level); GeglRectangle *in_extent = gegl_operation_source_get_bounding_box (operation, "input"); GeglBufferIterator *iter; GeglAbyssPolicy abyss = o->clamp ? GEGL_ABYSS_CLAMP : GEGL_ABYSS_NONE; gdouble px_x = gegl_coordinate_relative_to_pixel (o->x, in_extent->width); gdouble px_y = gegl_coordinate_relative_to_pixel (o->y, in_extent->height); gdouble scalex; gdouble scaley; if (o->aspect > 1.0) { scalex = 1.0; scaley = o->aspect; } else if (o->aspect < 1.0) { scalex = 1.0 / o->aspect; scaley = 1.0; } else { scalex = 1.0; scaley = 1.0; } iter = gegl_buffer_iterator_new (output, result, 0, babl_format ("RGBA float"), GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE); while (gegl_buffer_iterator_next (iter)) { gint x = result->x; gint y = result->y; gfloat *out_pixel = iter->data[0]; for (y = iter->roi[0].y; y < iter->roi[0].y + iter->roi[0].height; ++y) for (x = iter->roi[0].x; x < iter->roi[0].x + iter->roi[0].width; ++x) { gdouble radius; gdouble shift; gdouble dx; gdouble dy; gdouble ux; gdouble uy; dx = (x - px_x) * scalex; dy = (y - px_y) * scaley; if (!dx && !dy) radius = 0.000001; else radius = sqrt (dx * dx + dy * dy); shift = o->amplitude * sin (2.0 * G_PI * radius / o->period + 2.0 * G_PI * o->phi); ux = dx / radius; uy = dy / radius; gegl_sampler_get (sampler, x + (shift + ux) / scalex, y + (shift + uy) / scaley, NULL, out_pixel, abyss); out_pixel += 4; } } g_object_unref (sampler); return TRUE; }
static gboolean cl_process (GeglOperation *operation, GeglBuffer *input, GeglBuffer *output, const GeglRectangle *roi) { const Babl *in_format = babl_format ("RaGaBaA float"); const Babl *out_format = babl_format ("RaGaBaA float"); gint err; gfloat bg_color[4]; gint norm; GeglOperationAreaFilter *op_area = GEGL_OPERATION_AREA_FILTER (operation); GeglProperties *o = GEGL_PROPERTIES (operation); GeglRectangle *image_extent; GeglBufferClIterator *i = gegl_buffer_cl_iterator_new (output, roi, out_format, GEGL_CL_BUFFER_WRITE); gint read = gegl_buffer_cl_iterator_add_2 (i, input, roi, in_format, GEGL_CL_BUFFER_READ, op_area->left, op_area->right, op_area->top, op_area->bottom, GEGL_ABYSS_CLAMP); gint aux = gegl_buffer_cl_iterator_add_aux (i, roi, in_format, op_area->left, op_area->right, op_area->top, op_area->bottom); gegl_color_get_pixel (o->background, babl_format ("RaGaBaA float"), bg_color); norm = 0; norm = o->norm == GEGL_PIXELIZE_NORM_EUCLIDEAN ? 1 : norm; norm = o->norm == GEGL_PIXELIZE_NORM_INFINITY ? 2 : norm; image_extent = gegl_operation_source_get_bounding_box (operation, "input"); while (gegl_buffer_cl_iterator_next (i, &err) && !err) { err = cl_pixelize(i->tex[read], i->tex[aux], i->tex[0], &i->roi[read], &i->roi[0], o->size_x, o->size_y, o->ratio_x, o->ratio_y, bg_color, norm, image_extent); if (err) { gegl_buffer_cl_iterator_stop (i); break; } } return !err; }
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 void lcms_layers_transform_rgb (gint *layers, gint num_layers, cmsHPROFILE src_profile, cmsHPROFILE dest_profile, GimpColorRenderingIntent intent, gboolean bpc) { gint i; for (i = 0; i < num_layers; i++) { gint32 layer_id = layers[i]; const Babl *layer_format; gboolean has_alpha; const Babl *type; const Babl *iter_format = NULL; cmsUInt32Number lcms_format = 0; cmsHTRANSFORM transform = NULL; gint *children; gint num_children; children = gimp_item_get_children (layer_id, &num_children); if (children) { lcms_layers_transform_rgb (children, num_children, src_profile, dest_profile, intent, bpc); g_free (children); continue; } layer_format = gimp_drawable_get_format (layer_id); has_alpha = babl_format_has_alpha (layer_format); type = babl_format_get_type (layer_format, 0); if (type == babl_type ("u8")) { if (has_alpha) { lcms_format = TYPE_RGBA_8; iter_format = babl_format ("R'G'B'A u8"); } else { lcms_format = TYPE_RGB_8; iter_format = babl_format ("R'G'B' u8"); } } else if (type == babl_type ("u16")) { if (has_alpha) { lcms_format = TYPE_RGBA_16; iter_format = babl_format ("R'G'B'A u16"); } else { lcms_format = TYPE_RGB_16; iter_format = babl_format ("R'G'B' u16"); } } else if (type == babl_type ("half")) /* 16-bit floating point (half) */ { #ifdef TYPE_RGB_HALF_FLT /* half float types are only in lcms 2.4 and newer */ if (has_alpha) { lcms_format = TYPE_RGBA_HALF_FLT; iter_format = babl_format ("R'G'B'A half"); } else { lcms_format = TYPE_RGB_HALF_FLT; iter_format = babl_format ("R'G'B' float"); } #endif /* TYPE_RGB_HALF_FLT */ } else if (type == babl_type ("float")) { if (has_alpha) { lcms_format = TYPE_RGBA_FLT; iter_format = babl_format ("R'G'B'A float"); } else { lcms_format = TYPE_RGB_FLT; iter_format = babl_format ("R'G'B' float"); } } else if (type == babl_type ("double")) { if (has_alpha) { #ifdef TYPE_RGBA_DBL /* RGBA double not implemented in lcms */ lcms_format = TYPE_RGBA_DBL; iter_format = babl_format ("R'G'B'A double"); #endif /* TYPE_RGBA_DBL */ } else { lcms_format = TYPE_RGB_DBL; iter_format = babl_format ("R'G'B' double"); } } if (lcms_format == 0) { g_printerr ("lcms: layer format %s not supported, " "falling back to float\n", babl_get_name (layer_format)); if (has_alpha) { lcms_format = TYPE_RGBA_FLT; iter_format = babl_format ("R'G'B'A float"); } else { lcms_format = TYPE_RGB_FLT; iter_format = babl_format ("R'G'B' float"); } } transform = cmsCreateTransform (src_profile, lcms_format, dest_profile, lcms_format, intent, cmsFLAGS_NOOPTIMIZE | (bpc ? cmsFLAGS_BLACKPOINTCOMPENSATION : 0)); if (transform) { GeglBuffer *src_buffer; GeglBuffer *dest_buffer; GeglBufferIterator *iter; gint layer_width; gint layer_height; gint layer_bpp; gboolean layer_alpha; gdouble progress_start = (gdouble) i / num_layers; gdouble progress_end = (gdouble) (i + 1) / num_layers; gdouble range = progress_end - progress_start; gint count = 0; gint done = 0; src_buffer = gimp_drawable_get_buffer (layer_id); dest_buffer = gimp_drawable_get_shadow_buffer (layer_id); layer_width = gegl_buffer_get_width (src_buffer); layer_height = gegl_buffer_get_height (src_buffer); layer_bpp = babl_format_get_bytes_per_pixel (iter_format); layer_alpha = babl_format_has_alpha (iter_format); iter = gegl_buffer_iterator_new (src_buffer, NULL, 0, iter_format, GEGL_ACCESS_READ, GEGL_ABYSS_NONE); gegl_buffer_iterator_add (iter, dest_buffer, NULL, 0, iter_format, GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE); while (gegl_buffer_iterator_next (iter)) { /* lcms doesn't touch the alpha channel, simply * copy everything to dest before the transform */ if (layer_alpha) memcpy (iter->data[1], iter->data[0], iter->length * layer_bpp); cmsDoTransform (transform, iter->data[0], iter->data[1], iter->length); } g_object_unref (src_buffer); g_object_unref (dest_buffer); gimp_drawable_merge_shadow (layer_id, TRUE); gimp_drawable_update (layer_id, 0, 0, layer_width, layer_height); if (count++ % 32 == 0) { gimp_progress_update (progress_start + (gdouble) done / (layer_width * layer_height) * range); } cmsDeleteTransform (transform); } } }
gint32 load_image (const gchar *filename, GimpRunMode runmode, gboolean preview, gboolean *resolution_loaded, GError **error) { gint32 volatile image_ID; gint32 layer_ID; struct jpeg_decompress_struct cinfo; struct my_error_mgr jerr; jpeg_saved_marker_ptr marker; FILE *infile; guchar *buf; guchar **rowbuf; GimpImageBaseType image_type; GimpImageType layer_type; GeglBuffer *buffer = NULL; const Babl *format; gint tile_height; gint scanlines; gint i, start, end; cmsHTRANSFORM cmyk_transform = NULL; /* We set up the normal JPEG error routines. */ cinfo.err = jpeg_std_error (&jerr.pub); jerr.pub.error_exit = my_error_exit; if (!preview) { jerr.pub.output_message = my_output_message; gimp_progress_init_printf (_("Opening '%s'"), gimp_filename_to_utf8 (filename)); } if ((infile = g_fopen (filename, "rb")) == NULL) { g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno), _("Could not open '%s' for reading: %s"), gimp_filename_to_utf8 (filename), g_strerror (errno)); return -1; } image_ID = -1; /* Establish the setjmp return context for my_error_exit to use. */ if (setjmp (jerr.setjmp_buffer)) { /* If we get here, the JPEG code has signaled an error. * We need to clean up the JPEG object, close the input file, and return. */ jpeg_destroy_decompress (&cinfo); if (infile) fclose (infile); if (image_ID != -1 && !preview) gimp_image_delete (image_ID); if (preview) destroy_preview (); if (buffer) g_object_unref (buffer); return -1; } /* Now we can initialize the JPEG decompression object. */ jpeg_create_decompress (&cinfo); /* Step 2: specify data source (eg, a file) */ jpeg_stdio_src (&cinfo, infile); if (! preview) { /* - step 2.1: tell the lib to save the comments */ jpeg_save_markers (&cinfo, JPEG_COM, 0xffff); /* - step 2.2: tell the lib to save APP1 data (Exif or XMP) */ jpeg_save_markers (&cinfo, JPEG_APP0 + 1, 0xffff); /* - step 2.3: tell the lib to save APP2 data (ICC profiles) */ jpeg_save_markers (&cinfo, JPEG_APP0 + 2, 0xffff); } /* Step 3: read file parameters with jpeg_read_header() */ jpeg_read_header (&cinfo, TRUE); /* We can ignore the return value from jpeg_read_header since * (a) suspension is not possible with the stdio data source, and * (b) we passed TRUE to reject a tables-only JPEG file as an error. * See libjpeg.doc for more info. */ /* Step 4: set parameters for decompression */ /* In this example, we don't need to change any of the defaults set by * jpeg_read_header(), so we do nothing here. */ /* Step 5: Start decompressor */ jpeg_start_decompress (&cinfo); /* We may need to do some setup of our own at this point before reading * the data. After jpeg_start_decompress() we have the correct scaled * output image dimensions available, as well as the output colormap * if we asked for color quantization. */ /* temporary buffer */ tile_height = gimp_tile_height (); buf = g_new (guchar, tile_height * cinfo.output_width * cinfo.output_components); rowbuf = g_new (guchar *, tile_height); for (i = 0; i < tile_height; i++) rowbuf[i] = buf + cinfo.output_width * cinfo.output_components * i; switch (cinfo.output_components) { case 1: image_type = GIMP_GRAY; layer_type = GIMP_GRAY_IMAGE; break; case 3: image_type = GIMP_RGB; layer_type = GIMP_RGB_IMAGE; break; case 4: if (cinfo.out_color_space == JCS_CMYK) { image_type = GIMP_RGB; layer_type = GIMP_RGB_IMAGE; break; } /*fallthrough*/ default: g_message ("Don't know how to load JPEG images " "with %d color channels, using colorspace %d (%d).", cinfo.output_components, cinfo.out_color_space, cinfo.jpeg_color_space); return -1; break; } if (preview) { image_ID = preview_image_ID; } else { image_ID = gimp_image_new_with_precision (cinfo.output_width, cinfo.output_height, image_type, GIMP_PRECISION_U8_GAMMA); gimp_image_undo_disable (image_ID); gimp_image_set_filename (image_ID, filename); } if (preview) { preview_layer_ID = gimp_layer_new (preview_image_ID, _("JPEG preview"), cinfo.output_width, cinfo.output_height, layer_type, 100, GIMP_NORMAL_MODE); layer_ID = preview_layer_ID; } else { layer_ID = gimp_layer_new (image_ID, _("Background"), cinfo.output_width, cinfo.output_height, layer_type, 100, GIMP_NORMAL_MODE); } if (! preview) { GString *comment_buffer = NULL; guint8 *profile = NULL; guint profile_size = 0; /* Step 5.0: save the original JPEG settings in a parasite */ jpeg_detect_original_settings (&cinfo, image_ID); /* Step 5.1: check for comments, or Exif metadata in APP1 markers */ for (marker = cinfo.marker_list; marker; marker = marker->next) { const gchar *data = (const gchar *) marker->data; gsize len = marker->data_length; if (marker->marker == JPEG_COM) { #ifdef GIMP_UNSTABLE g_print ("jpeg-load: found image comment (%d bytes)\n", marker->data_length); #endif if (! comment_buffer) { comment_buffer = g_string_new_len (data, len); } else { /* concatenate multiple comments, separate them with LF */ g_string_append_c (comment_buffer, '\n'); g_string_append_len (comment_buffer, data, len); } } else if ((marker->marker == JPEG_APP0 + 1) && (len > sizeof (JPEG_APP_HEADER_EXIF) + 8) && ! strcmp (JPEG_APP_HEADER_EXIF, data)) { #ifdef GIMP_UNSTABLE g_print ("jpeg-load: found Exif block (%d bytes)\n", (gint) (len - sizeof (JPEG_APP_HEADER_EXIF))); #endif } } if (jpeg_load_resolution (image_ID, &cinfo)) { if (resolution_loaded) *resolution_loaded = TRUE; } /* if we found any comments, then make a parasite for them */ if (comment_buffer && comment_buffer->len) { GimpParasite *parasite; jpeg_load_sanitize_comment (comment_buffer->str); parasite = gimp_parasite_new ("gimp-comment", GIMP_PARASITE_PERSISTENT, strlen (comment_buffer->str) + 1, comment_buffer->str); gimp_image_attach_parasite (image_ID, parasite); gimp_parasite_free (parasite); g_string_free (comment_buffer, TRUE); } /* Step 5.3: check for an embedded ICC profile in APP2 markers */ jpeg_icc_read_profile (&cinfo, &profile, &profile_size); if (cinfo.out_color_space == JCS_CMYK) { cmyk_transform = jpeg_load_cmyk_transform (profile, profile_size); } else if (profile) /* don't attach the profile if we are transforming */ { GimpParasite *parasite; parasite = gimp_parasite_new ("icc-profile", GIMP_PARASITE_PERSISTENT | GIMP_PARASITE_UNDOABLE, profile_size, profile); gimp_image_attach_parasite (image_ID, parasite); gimp_parasite_free (parasite); } g_free (profile); /* Do not attach the "jpeg-save-options" parasite to the image * because this conflicts with the global defaults (bug #75398). */ } /* Step 6: while (scan lines remain to be read) */ /* jpeg_read_scanlines(...); */ /* Here we use the library's state variable cinfo.output_scanline as the * loop counter, so that we don't have to keep track ourselves. */ buffer = gimp_drawable_get_buffer (layer_ID); format = babl_format (image_type == GIMP_RGB ? "R'G'B' u8" : "Y' u8"); while (cinfo.output_scanline < cinfo.output_height) { start = cinfo.output_scanline; end = cinfo.output_scanline + tile_height; end = MIN (end, cinfo.output_height); scanlines = end - start; for (i = 0; i < scanlines; i++) jpeg_read_scanlines (&cinfo, (JSAMPARRAY) &rowbuf[i], 1); if (cinfo.out_color_space == JCS_CMYK) jpeg_load_cmyk_to_rgb (buf, cinfo.output_width * scanlines, cmyk_transform); gegl_buffer_set (buffer, GEGL_RECTANGLE (0, start, cinfo.output_width, scanlines), 0, format, buf, GEGL_AUTO_ROWSTRIDE); if (! preview && (cinfo.output_scanline % 32) == 0) gimp_progress_update ((gdouble) cinfo.output_scanline / (gdouble) cinfo.output_height); } /* Step 7: Finish decompression */ jpeg_finish_decompress (&cinfo); /* We can ignore the return value since suspension is not possible * with the stdio data source. */ if (cmyk_transform) cmsDeleteTransform (cmyk_transform); /* Step 8: Release JPEG decompression object */ /* This is an important step since it will release a good deal of memory. */ jpeg_destroy_decompress (&cinfo); g_object_unref (buffer); /* free up the temporary buffers */ g_free (rowbuf); g_free (buf); /* After finish_decompress, we can close the input file. * Here we postpone it until after no more JPEG errors are possible, * so as to simplify the setjmp error logic above. (Actually, I don't * think that jpeg_destroy can do an error exit, but why assume anything...) */ fclose (infile); /* At this point you may want to check to see whether any corrupt-data * warnings occurred (test whether jerr.num_warnings is nonzero). */ /* Detach from the drawable and add it to the image. */ if (! preview) { gimp_progress_update (1.0); } gimp_image_insert_layer (image_ID, layer_ID, -1, 0); return image_ID; }
static gboolean process (GeglOperation *operation, GeglBuffer *input, GeglBuffer *output, const GeglRectangle *result, gint level) { GeglProperties *o = GEGL_PROPERTIES (operation); GeglSampler *sampler = gegl_buffer_sampler_new_at_level (input, babl_format ("RGBA float"), o->sampler_type, level); GeglBufferIterator *iter; GeglAbyssPolicy abyss = o->tileable ? GEGL_ABYSS_LOOP : GEGL_ABYSS_NONE; iter = gegl_buffer_iterator_new (output, result, 0, babl_format ("RGBA float"), GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE); while (gegl_buffer_iterator_next (iter)) { gint x = result->x; gint y = result->y; gfloat *out_pixel = iter->data[0]; for (y = iter->roi[0].y; y < iter->roi[0].y + iter->roi[0].height; ++y) for (x = iter->roi[0].x; x < iter->roi[0].x + iter->roi[0].width; ++x) { gdouble shift; gdouble coordsx; gdouble coordsy; gdouble lambda; gdouble angle_rad = o->angle / 180.0 * G_PI; gdouble nx = x * cos (angle_rad) + y * sin (angle_rad); switch (o->wave_type) { case GEGL_RIPPLE_WAVE_TYPE_SAWTOOTH: lambda = div (nx,o->period).rem - o->phi * o->period; if (lambda < 0) lambda += o->period; shift = o->amplitude * (fabs (((lambda / o->period) * 4) - 2) - 1); break; case GEGL_RIPPLE_WAVE_TYPE_SINE: default: shift = o->amplitude * sin (2.0 * G_PI * nx / o->period + 2.0 * G_PI * o->phi); break; } coordsx = x + shift * sin (angle_rad); coordsy = y + shift * cos (angle_rad); gegl_sampler_get (sampler, coordsx, coordsy, NULL, out_pixel, abyss); out_pixel += 4; } } g_object_unref (sampler); 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; }
static gboolean process (GeglOperation *operation, GeglBuffer *input, GeglBuffer *output, const GeglRectangle *result) { GeglRectangle rect; GeglBuffer *temp; GeglOperationAreaFilter *op_area = GEGL_OPERATION_AREA_FILTER (operation); GeglChantO *o = GEGL_CHANT_PROPERTIES (operation); GeglRectangle temp_extend; gdouble B, b[4]; gdouble *cmatrix; gint cmatrix_len; gboolean force_iir; gboolean force_fir; rect.x = result->x - op_area->left; rect.width = result->width + op_area->left + op_area->right; rect.y = result->y - op_area->top; rect.height = result->height + op_area->top + op_area->bottom; temp_extend = rect; temp_extend.x = result->x; temp_extend.width = result->width; temp = gegl_buffer_new (&temp_extend, babl_format ("RaGaBaA float")); force_iir = o->filter && !strcmp (o->filter, "iir"); force_fir = o->filter && !strcmp (o->filter, "fir"); if ((force_iir || o->std_dev_x > 1.0) && !force_fir) { iir_young_find_constants (o->std_dev_x, &B, b); iir_young_hor_blur (input, &rect, temp, &temp_extend, B, b); } else { cmatrix_len = fir_gen_convolve_matrix (o->std_dev_x, &cmatrix); fir_hor_blur (input, &rect, temp, &temp_extend, cmatrix, cmatrix_len, op_area->left); g_free (cmatrix); } if ((force_iir || o->std_dev_y > 1.0) && !force_fir) { iir_young_find_constants (o->std_dev_y, &B, b); iir_young_ver_blur (temp, &temp_extend, output, result, B, b); } else { cmatrix_len = fir_gen_convolve_matrix (o->std_dev_y, &cmatrix); fir_ver_blur (temp, &temp_extend, output, result, cmatrix, cmatrix_len, op_area->top); g_free (cmatrix); } g_object_unref (temp); return TRUE; }
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 *output, const GeglRectangle *result, gint level) { GeglChantO *o = GEGL_CHANT_PROPERTIES (operation); FILE *fp; pnm_struct img; GeglRectangle rect = {0,0,0,0}; gboolean ret = FALSE; fp = (!strcmp (o->path, "-") ? stdin : fopen (o->path,"rb")); if (!fp) return FALSE; if (!ppm_load_read_header (fp, &img)) goto out; /* Allocating Array Size */ /* Should use g_try_malloc(), but this causes crashes elsewhere because the * error signalled by returning FALSE isn't properly acted upon. Therefore * g_malloc() is used here which aborts if the requested memory size can't be * allocated causing a controlled crash. */ img.data = (guchar*) g_malloc (img.numsamples * img.bpc); /* No-op without g_try_malloc(), see above. */ if (! img.data) { g_warning ("Couldn't allocate %" G_GSIZE_FORMAT " bytes, giving up.", ((gsize)img.numsamples * img.bpc)); goto out; } rect.height = img.height; rect.width = img.width; if (img.bpc == 1) { if (img.channels == 3) gegl_buffer_get (output, &rect, 1.0, babl_format ("R'G'B' u8"), img.data, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); else gegl_buffer_get (output, &rect, 1.0, babl_format ("Y' u8"), img.data, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); } else if (img.bpc == 2) { if (img.channels == 3) gegl_buffer_get (output, &rect, 1.0, babl_format ("R'G'B' u16"), img.data, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); else gegl_buffer_get (output, &rect, 1.0, babl_format ("Y' u16"), img.data, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); } else g_warning ("%s: Programmer stupidity error", G_STRLOC); ppm_load_read_image (fp, &img); if (img.bpc == 1) { if (img.channels == 3) gegl_buffer_set (output, &rect, 0, babl_format ("R'G'B' u8"), img.data, GEGL_AUTO_ROWSTRIDE); else gegl_buffer_set (output, &rect, 0, babl_format ("Y' u8"), img.data, GEGL_AUTO_ROWSTRIDE); } else if (img.bpc == 2) { if (img.channels == 3) gegl_buffer_set (output, &rect, 0, babl_format ("R'G'B' u16"), img.data, GEGL_AUTO_ROWSTRIDE); else gegl_buffer_set (output, &rect, 0, babl_format ("Y' u16"), img.data, GEGL_AUTO_ROWSTRIDE); } else g_warning ("%s: Programmer stupidity error", G_STRLOC); g_free (img.data); ret = TRUE; out: if (stdin != fp) fclose (fp); return ret; }
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]; }
static void gimp_color_frame_init (GimpColorFrame *frame) { GtkWidget *vbox; GtkWidget *vbox2; gint i; frame->sample_valid = FALSE; frame->sample_format = babl_format ("R'G'B' u8"); gimp_rgba_set (&frame->color, 0.0, 0.0, 0.0, GIMP_OPACITY_OPAQUE); frame->menu = gimp_enum_combo_box_new (GIMP_TYPE_COLOR_FRAME_MODE); gtk_frame_set_label_widget (GTK_FRAME (frame), frame->menu); gtk_widget_show (frame->menu); g_signal_connect (frame->menu, "changed", G_CALLBACK (gimp_color_frame_menu_callback), frame); vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2); gtk_container_add (GTK_CONTAINER (frame), vbox); gtk_widget_show (vbox); frame->color_area = g_object_new (GIMP_TYPE_COLOR_AREA, "color", &frame->color, "type", GIMP_COLOR_AREA_SMALL_CHECKS, "drag-mask", GDK_BUTTON1_MASK, "draw-border", TRUE, "height-request", 20, NULL); gtk_box_pack_start (GTK_BOX (vbox), frame->color_area, FALSE, FALSE, 0); vbox2 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2); gtk_box_set_homogeneous (GTK_BOX (vbox2), TRUE); gtk_box_pack_start (GTK_BOX (vbox), vbox2, FALSE, FALSE, 0); gtk_widget_show (vbox2); for (i = 0; i < GIMP_COLOR_FRAME_ROWS; i++) { GtkWidget *hbox; hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); gtk_box_pack_start (GTK_BOX (vbox2), hbox, FALSE, FALSE, 0); gtk_widget_show (hbox); frame->name_labels[i] = gtk_label_new (" "); gtk_label_set_xalign (GTK_LABEL (frame->name_labels[i]), 0.0); gtk_box_pack_start (GTK_BOX (hbox), frame->name_labels[i], FALSE, FALSE, 0); gtk_widget_show (frame->name_labels[i]); frame->value_labels[i] = gtk_label_new (" "); gtk_label_set_selectable (GTK_LABEL (frame->value_labels[i]), TRUE); gtk_label_set_xalign (GTK_LABEL (frame->value_labels[i]), 1.0); gtk_box_pack_end (GTK_BOX (hbox), frame->value_labels[i], FALSE, FALSE, 0); gtk_widget_show (frame->value_labels[i]); } }
static gboolean process (GeglOperation *operation, GeglBuffer *input, GeglBuffer *output, const GeglRectangle *result, gint level) { GeglChantO *o = GEGL_CHANT_PROPERTIES (operation); LensValues lens; GeglRectangle boundary; gint i, j; gfloat *src_buf, *dst_buf; gfloat background[4]; boundary = *gegl_operation_source_get_bounding_box (operation, "input"); lens = lens_setup_calc (o, boundary); src_buf = g_new0 (gfloat, SQR (MAX_WH) * 4); dst_buf = g_new0 (gfloat, SQR (CHUNK_SIZE) * 4); gegl_color_get_pixel (o->background, babl_format ("RGBA float"), background); for (j = 0; (j-1) * CHUNK_SIZE < result->height; j++) for (i = 0; (i-1) * CHUNK_SIZE < result->width; i++) { GeglRectangle chunked_result; GeglRectangle area; gint x, y; chunked_result = *GEGL_RECTANGLE (result->x + i * CHUNK_SIZE, result->y + j * CHUNK_SIZE, CHUNK_SIZE, CHUNK_SIZE); gegl_rectangle_intersect (&chunked_result, &chunked_result, result); if (chunked_result.width < 1 || chunked_result.height < 1) continue; area = get_required (&boundary, &chunked_result, operation); clamp_area (&area, lens.centre_x, lens.centre_y); gegl_buffer_get (input, &area, 1.0, babl_format ("RGBA float"), src_buf, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_CLAMP); for (y = chunked_result.y; y < chunked_result.y + chunked_result.height; y++) for (x = chunked_result.x; x < chunked_result.x + chunked_result.width; x++) { lens_distort_func (src_buf, dst_buf, &area, &chunked_result, &boundary, &lens, x, y, input, background); } gegl_buffer_set (output, &chunked_result, 0, babl_format ("RGBA float"), dst_buf, GEGL_AUTO_ROWSTRIDE); } g_free (dst_buf); g_free (src_buf); return TRUE; }
static void set_rectangle_noalloc (GeglBuffer *output, GeglRectangle *rect, GeglRectangle *rect_shape, GeglColor *color, GeglPixelizeNorm norm) { if (norm == GEGL_PIXELIZE_NORM_INFINITY) { GeglRectangle rect2; gegl_rectangle_intersect (&rect2, rect, rect_shape); gegl_buffer_set_color (output, &rect2, color); } else { GeglBufferIterator *gi; gint c, x, y; gfloat col[4]; gfloat center_x, center_y; gfloat shape_area = rect_shape->width * rect_shape->height; center_x = rect_shape->x + rect_shape->width / 2.0f; center_y = rect_shape->y + rect_shape->height / 2.0f; gegl_color_get_pixel (color, babl_format ("RaGaBaA float"), col); gi = gegl_buffer_iterator_new (output, rect, 0, babl_format ("RaGaBaA float"), GEGL_ACCESS_WRITE, GEGL_ABYSS_CLAMP); while (gegl_buffer_iterator_next (gi)) { gfloat *data = (gfloat*) gi->data[0]; GeglRectangle roi = gi->roi[0]; switch (norm) { case (GEGL_PIXELIZE_NORM_EUCLIDEAN): for (y = 0; y < roi.height; y++) for (x = 0; x < roi.width; x++) if (SQR ((x + roi.x - center_x) / (gfloat) rect_shape->width) + SQR ((y + roi.y - center_y) / (gfloat) rect_shape->height) <= 1.0f) for (c = 0; c < 4; c++) data [4 * (y * roi.width + x) + c] = col[c]; break; case (GEGL_PIXELIZE_NORM_MANHATTAN): for (y = 0; y < roi.height; y++) for (x = 0; x < roi.width; x++) if (fabsf (x + roi.x - center_x) * rect_shape->height + fabsf (y + roi.y - center_y) * rect_shape->width < shape_area) for (c = 0; c < 4; c++) data [4 * (y * roi.width + x) + c] = col[c]; break; case (GEGL_PIXELIZE_NORM_INFINITY): break; } } } }
static void prepare (GeglOperation *operation) { GeglProperties *o = GEGL_PROPERTIES (operation); const Babl *output_format = (o->linear) ? babl_format ("Y float") : babl_format ("Y' float"); switch (o->component) { case GEGL_COMPONENT_EXTRACT_ALPHA: { gegl_operation_set_format (operation, "input", babl_format ("YA float")); } break; case GEGL_COMPONENT_EXTRACT_RGB_RED: case GEGL_COMPONENT_EXTRACT_RGB_GREEN: case GEGL_COMPONENT_EXTRACT_RGB_BLUE: { gegl_operation_set_format (operation, "input", babl_format ("R'G'B' float")); } break; case GEGL_COMPONENT_EXTRACT_HUE: case GEGL_COMPONENT_EXTRACT_HSV_SATURATION: case GEGL_COMPONENT_EXTRACT_HSV_VALUE: { gegl_operation_set_format (operation, "input", babl_format ("HSV float")); } break; case GEGL_COMPONENT_EXTRACT_HSL_LIGHTNESS: case GEGL_COMPONENT_EXTRACT_HSL_SATURATION: { gegl_operation_set_format (operation, "input", babl_format ("HSL float")); } break; case GEGL_COMPONENT_EXTRACT_CMYK_CYAN: case GEGL_COMPONENT_EXTRACT_CMYK_MAGENTA: case GEGL_COMPONENT_EXTRACT_CMYK_YELLOW: case GEGL_COMPONENT_EXTRACT_CMYK_KEY: { gegl_operation_set_format (operation, "input", babl_format ("CMYK float")); } break; case GEGL_COMPONENT_EXTRACT_YCBCR_Y: case GEGL_COMPONENT_EXTRACT_YCBCR_CB: case GEGL_COMPONENT_EXTRACT_YCBCR_CR: { gegl_operation_set_format (operation, "input", babl_format ("Y'CbCr float")); } break; case GEGL_COMPONENT_EXTRACT_LAB_L: case GEGL_COMPONENT_EXTRACT_LAB_A: case GEGL_COMPONENT_EXTRACT_LAB_B: { gegl_operation_set_format (operation, "input", babl_format ("CIE Lab float")); } break; } gegl_operation_set_format (operation, "output", output_format); }
static gboolean process (GeglOperation *operation, GeglBuffer *input, GeglBuffer *output, const GeglRectangle *roi, gint level) { GeglRectangle src_rect; GeglRectangle *whole_region; GeglProperties *o = GEGL_PROPERTIES (operation); GeglOperationAreaFilter *op_area; op_area = GEGL_OPERATION_AREA_FILTER (operation); whole_region = gegl_operation_source_get_bounding_box (operation, "input"); if (gegl_operation_use_opencl (operation)) if (cl_process (operation, input, output, roi)) return TRUE; if (o->size_x * o->size_y < SQR (ALLOC_THRESHOLD_SIZE)) { gfloat background_color[4]; gfloat *input_buf = g_new (gfloat, (CHUNK_SIZE + o->size_x * 2) * (CHUNK_SIZE + o->size_y * 2) * 4); gfloat *output_buf = g_new (gfloat, SQR (CHUNK_SIZE) * 4); gint i, j; gegl_color_get_pixel (o->background, babl_format("RaGaBaA float"), background_color); for (j = 0; (j-1) * CHUNK_SIZE < roi->height; j++) for (i = 0; (i-1) * CHUNK_SIZE < roi->width; i++) { GeglRectangle chunked_result; GeglRectangle chunked_sizes; chunked_result = *GEGL_RECTANGLE (roi->x + i * CHUNK_SIZE, roi->y + j * CHUNK_SIZE, CHUNK_SIZE, CHUNK_SIZE); gegl_rectangle_intersect (&chunked_result, &chunked_result, roi); if (chunked_result.width < 1 || chunked_result.height < 1) continue; src_rect = chunked_result; src_rect.x -= op_area->left; src_rect.y -= op_area->top; src_rect.width += op_area->left + op_area->right; src_rect.height += op_area->top + op_area->bottom; gegl_buffer_get (input, &src_rect, 1.0, babl_format ("RaGaBaA float"), input_buf, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_CLAMP); gegl_rectangle_copy (&chunked_sizes, &chunked_result); chunked_sizes.x = 0; chunked_sizes.y = 0; set_rectangle (output_buf, &chunked_sizes, &chunked_sizes, chunked_result.width, background_color, GEGL_PIXELIZE_NORM_INFINITY); pixelize (input_buf, output_buf, &chunked_result, &src_rect, whole_region, o); gegl_buffer_set (output, &chunked_result, 0, babl_format ("RaGaBaA float"), output_buf, GEGL_AUTO_ROWSTRIDE); } g_free (input_buf); g_free (output_buf); } else { gegl_buffer_set_color (output, roi, o->background); pixelize_noalloc (input, output, roi, whole_region, o); } return TRUE; }
/** * 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 drawable_lua_process (GeglOperation *op, GeglBuffer *drawable, GeglBuffer *aux, GeglBuffer *result, const GeglRectangle *roi, const gchar *file, const gchar *buffer, gdouble user_value) { /*GimpRGB background;*/ GeglRectangle *in_rect = gegl_operation_source_get_bounding_box (GEGL_OPERATION (op), "input"); lua_State *L; Priv p; L = luaL_newstate (); luaL_openlibs (L); register_functions (L, gluas_functions); p.rgba_float = babl_format ("RGBA float"); p.L = L; p.width = in_rect->width; p.height = in_rect->height; p.bx1 = roi->x; p.by1 = roi->y; p.bx2 = roi->x + roi->width; p.by2 = roi->y + roi->height; lua_pushnumber (L, (double) user_value); lua_setglobal (L, "user_value"); lua_pushnumber (L, (double) p.width); lua_setglobal (L, "width"); lua_pushnumber (L, (double) p.height); lua_setglobal (L, "height"); lua_pushstring (L, "priv"); lua_pushlightuserdata (L, &p); lua_settable (L, LUA_REGISTRYINDEX); p.in_drawable = drawable; p.aux_drawable = aux; p.out_drawable = result; lua_pushnumber (L, (double) p.bx1); lua_setglobal (L, "bound_x0"); lua_pushnumber (L, (double) p.bx2); lua_setglobal (L, "bound_x1"); lua_pushnumber (L, (double) p.by1); lua_setglobal (L, "bound_y0"); lua_pushnumber (L, (double) p.by2); lua_setglobal (L, "bound_y1"); { gint status = 0; luaL_loadstring (L, "os.setlocale ('C', 'numeric')"); /* insert default loop start/end filling the selection */ if (file && file[0]!='\0') status = luaL_loadfile (L, file); else if (buffer) { GString *str = g_string_new (buffer); if (!strstr (buffer, "for x")) { g_string_prepend (str, "for y=bound_y0, bound_y1 do\n for x=bound_x0, bound_x1 do\n"); g_string_append (str, " end \n progress (y/height)\n end\n"); } status = luaL_loadbuffer (L, str->str, str->len, "buffer"); g_string_free (str, TRUE); } if (status == 0) status = lua_pcall (L, 0, LUA_MULTRET, 0); if (status != 0) gegl_node_set (op->node, "error", lua_tostring (L, -1), NULL); } }
/** * 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 process_floyd_steinberg (GeglBuffer *input, GeglBuffer *output, const GeglRectangle *result, guint *channel_bits) { GeglRectangle line_rect; guint16 *line_buf; gdouble *error_buf [2]; guint channel_mask [4]; gint y; line_rect.x = result->x; line_rect.y = result->y; line_rect.width = result->width; line_rect.height = 1; line_buf = g_new (guint16, line_rect.width * 4); error_buf [0] = g_new0 (gdouble, line_rect.width * 4); error_buf [1] = g_new0 (gdouble, line_rect.width * 4); generate_channel_masks (channel_bits, channel_mask); for (y = 0; y < result->height; y++) { gdouble *error_buf_swap; gint step; gint start_x; gint end_x; gint x; /* Serpentine scanning; reverse direction every row */ if (y & 1) { start_x = result->width - 1; end_x = -1; step = -1; } else { start_x = 0; end_x = result->width; step = 1; } /* Pull input row */ gegl_buffer_get (input, 1.0, &line_rect, babl_format ("RGBA u16"), line_buf, GEGL_AUTO_ROWSTRIDE); /* Process the row */ for (x = start_x; x != end_x; x += step) { guint16 *pixel = &line_buf [x * 4]; guint ch; for (ch = 0; ch < 4; ch++) { gdouble value; gdouble value_clamped; gdouble quantized; gdouble qerror; value = pixel [ch] + error_buf [0] [x * 4 + ch]; value_clamped = CLAMP (value, 0.0, 65535.0); quantized = quantize_value ((guint) (value_clamped + 0.5), channel_bits [ch], channel_mask [ch]); qerror = value - quantized; pixel [ch] = (guint16) quantized; /* Distribute the error */ error_buf [1] [x * 4 + ch] += qerror * 5.0 / 16.0; /* Down */ if (x + step >= 0 && x + step < result->width) { error_buf [0] [(x + step) * 4 + ch] += qerror * 6.0 / 16.0; /* Ahead */ error_buf [1] [(x + step) * 4 + ch] += qerror * 1.0 / 16.0; /* Down, ahead */ } if (x - step >= 0 && x - step < result->width) { error_buf [1] [(x - step) * 4 + ch] += qerror * 3.0 / 16.0; /* Down, behind */ } } } /* Swap error accumulation rows */ error_buf_swap = error_buf [0]; error_buf [0] = error_buf [1]; error_buf [1] = error_buf_swap; /* Clear error buffer for next-plus-one line */ memset (error_buf [1], 0, line_rect.width * 4 * sizeof (gdouble)); /* Push output row */ gegl_buffer_set (output, &line_rect, babl_format ("RGBA u16"), line_buf, GEGL_AUTO_ROWSTRIDE); line_rect.y++; } g_free (line_buf); g_free (error_buf [0]); g_free (error_buf [1]); }
static void gimp_brush_clipboard_buffer_changed (Gimp *gimp, GimpBrush *brush) { gint width; gint height; if (brush->mask) { gimp_temp_buf_unref (brush->mask); brush->mask = NULL; } if (brush->pixmap) { gimp_temp_buf_unref (brush->pixmap); brush->pixmap = NULL; } if (gimp->global_buffer) { GeglBuffer *buffer = gimp_buffer_get_buffer (gimp->global_buffer); const Babl *format = gegl_buffer_get_format (buffer); GeglBuffer *dest_buffer; width = MIN (gimp_buffer_get_width (gimp->global_buffer), 512); height = MIN (gimp_buffer_get_height (gimp->global_buffer), 512); brush->mask = gimp_temp_buf_new (width, height, babl_format ("Y u8")); brush->pixmap = gimp_temp_buf_new (width, height, babl_format ("R'G'B' u8")); /* copy the alpha channel into the brush's mask */ if (babl_format_has_alpha (format)) { dest_buffer = gimp_temp_buf_create_buffer (brush->mask); gegl_buffer_set_format (dest_buffer, babl_format ("A u8")); gegl_buffer_copy (buffer, NULL, dest_buffer, NULL); g_object_unref (dest_buffer); } else { memset (gimp_temp_buf_get_data (brush->mask), 255, width * height); } /* copy the color channels into the brush's pixmap */ dest_buffer = gimp_temp_buf_create_buffer (brush->pixmap); gegl_buffer_copy (buffer, NULL, dest_buffer, NULL); g_object_unref (dest_buffer); } else { width = 17; height = 17; brush->mask = gimp_temp_buf_new (width, height, babl_format ("Y u8")); gimp_temp_buf_data_clear (brush->mask); } brush->x_axis.x = width / 2; brush->x_axis.y = 0; brush->y_axis.x = 0; brush->y_axis.y = height / 2; gimp_data_dirty (GIMP_DATA (brush)); }
static void process_image_callback (SoupServer *server, SoupMessage *msg, const char *path, GHashTable *query, SoupClientContext *context, gpointer user_data) { UiConnection *self = (UiConnection *)user_data; // Lookup network Network *network = NULL; { const gchar *graph_id = g_hash_table_lookup(query, "graph"); network = (graph_id) ? g_hash_table_lookup(self->network_map, graph_id) : NULL; } if (!network) { soup_message_set_status_full(msg, SOUP_STATUS_BAD_REQUEST, "'graph' not specified or wrong"); return; } // Lookup node GeglNode * node = NULL; Processor *processor = NULL; { const gchar *node_id = g_hash_table_lookup(query, "node"); processor = (node_id) ? network_processor(network, node_id) : NULL; if (processor) { node = processor->node; } else { node = graph_get_gegl_node(network->graph, node_id); } } if (!processor && !node) { soup_message_set_status_full(msg, SOUP_STATUS_BAD_REQUEST, "'node' not specified or wrong"); return; } // Render output // FIXME: allow region-of-interest and scale as query params const Babl *format = babl_format("R'G'B'A u8"); GeglRectangle roi = { 0, 0, 300, 300 }; gchar *rgba = (processor) ? processor_blit(processor, format, &roi) : blit_node_preview(node, format, &roi); if (!rgba) { soup_message_set_status(msg, SOUP_STATUS_BAD_REQUEST); return; } if (!(roi.width > 0 && roi.height > 0)) { soup_message_set_status(msg, SOUP_STATUS_BAD_REQUEST); g_free(rgba); return; } // Compress to PNG { PngEncoder *encoder = png_encoder_new(); png_encoder_encode_rgba(encoder, roi.width, roi.height, rgba); char *png = encoder->buffer; const size_t len = encoder->size; soup_message_set_status(msg, SOUP_STATUS_OK); soup_message_set_response(msg, "image/png", SOUP_MEMORY_COPY, png, len); png_encoder_free(encoder); } }
const GimpBoundSeg * floating_sel_boundary (GimpLayer *layer, gint *n_segs) { g_return_val_if_fail (GIMP_IS_LAYER (layer), NULL); g_return_val_if_fail (gimp_layer_is_floating_sel (layer), NULL); g_return_val_if_fail (n_segs != NULL, NULL); if (layer->fs.boundary_known == FALSE) { gint width, height; gint off_x, off_y; width = gimp_item_get_width (GIMP_ITEM (layer)); height = gimp_item_get_height (GIMP_ITEM (layer)); gimp_item_get_offset (GIMP_ITEM (layer), &off_x, &off_y); if (layer->fs.segs) g_free (layer->fs.segs); if (gimp_drawable_has_alpha (GIMP_DRAWABLE (layer))) { GeglBuffer *buffer; gint i; /* find the segments */ buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (layer)); layer->fs.segs = gimp_boundary_find (buffer, NULL, babl_format ("A float"), GIMP_BOUNDARY_WITHIN_BOUNDS, 0, 0, width, height, GIMP_BOUNDARY_HALF_WAY, &layer->fs.num_segs); /* offset the segments */ for (i = 0; i < layer->fs.num_segs; i++) { layer->fs.segs[i].x1 += off_x; layer->fs.segs[i].y1 += off_y; layer->fs.segs[i].x2 += off_x; layer->fs.segs[i].y2 += off_y; } } else { layer->fs.num_segs = 4; layer->fs.segs = g_new0 (GimpBoundSeg, 4); /* top */ layer->fs.segs[0].x1 = off_x; layer->fs.segs[0].y1 = off_y; layer->fs.segs[0].x2 = off_x + width; layer->fs.segs[0].y2 = off_y; /* left */ layer->fs.segs[1].x1 = off_x; layer->fs.segs[1].y1 = off_y; layer->fs.segs[1].x2 = off_x; layer->fs.segs[1].y2 = off_y + height; /* right */ layer->fs.segs[2].x1 = off_x + width; layer->fs.segs[2].y1 = off_y; layer->fs.segs[2].x2 = off_x + width; layer->fs.segs[2].y2 = off_y + height; /* bottom */ layer->fs.segs[3].x1 = off_x; layer->fs.segs[3].y1 = off_y + height; layer->fs.segs[3].x2 = off_x + width; layer->fs.segs[3].y2 = off_y + height; } layer->fs.boundary_known = TRUE; } *n_segs = layer->fs.num_segs; return layer->fs.segs; }
static int gimp_mypaint_surface_draw_dab (MyPaintSurface *base_surface, float x, float y, float radius, float color_r, float color_g, float color_b, float opaque, float hardness, float color_a, float aspect_ratio, float angle, float lock_alpha, float colorize) { GimpMybrushSurface *surface = (GimpMybrushSurface *)base_surface; GeglBufferIterator *iter; GeglRectangle dabRect; GimpComponentMask component_mask = surface->component_mask; const float one_over_radius2 = 1.0f / (radius * radius); const double angle_rad = angle / 360 * 2 * M_PI; const float cs = cos(angle_rad); const float sn = sin(angle_rad); float normal_mode; float segment1_slope; float segment2_slope; float r_aa_start; hardness = CLAMP (hardness, 0.0f, 1.0f); segment1_slope = -(1.0f / hardness - 1.0f); segment2_slope = -hardness / (1.0f - hardness); aspect_ratio = MAX (1.0f, aspect_ratio); r_aa_start = radius - 1.0f; r_aa_start = MAX (r_aa_start, 0); r_aa_start = (r_aa_start * r_aa_start) / aspect_ratio; normal_mode = opaque * (1.0f - colorize); colorize = opaque * colorize; /* FIXME: This should use the real matrix values to trim aspect_ratio dabs */ dabRect = calculate_dab_roi (x, y, radius); gegl_rectangle_intersect (&dabRect, &dabRect, gegl_buffer_get_extent (surface->buffer)); if (dabRect.width <= 0 || dabRect.height <= 0) return 0; gegl_rectangle_bounding_box (&surface->dirty, &surface->dirty, &dabRect); iter = gegl_buffer_iterator_new (surface->buffer, &dabRect, 0, babl_format ("R'G'B'A float"), GEGL_BUFFER_READWRITE, GEGL_ABYSS_NONE); if (surface->paint_mask) { GeglRectangle mask_roi = dabRect; mask_roi.x -= surface->paint_mask_x; mask_roi.y -= surface->paint_mask_y; gegl_buffer_iterator_add (iter, surface->paint_mask, &mask_roi, 0, babl_format ("Y float"), GEGL_ACCESS_READ, GEGL_ABYSS_NONE); } while (gegl_buffer_iterator_next (iter)) { float *pixel = (float *)iter->data[0]; float *mask; int iy, ix; if (surface->paint_mask) mask = iter->data[1]; else mask = NULL; for (iy = iter->roi[0].y; iy < iter->roi[0].y + iter->roi[0].height; iy++) { for (ix = iter->roi[0].x; ix < iter->roi[0].x + iter->roi[0].width; ix++) { float rr, base_alpha, alpha, dst_alpha, r, g, b, a; if (radius < 3.0f) rr = calculate_rr_antialiased (ix, iy, x, y, aspect_ratio, sn, cs, one_over_radius2, r_aa_start); else rr = calculate_rr (ix, iy, x, y, aspect_ratio, sn, cs, one_over_radius2); base_alpha = calculate_alpha_for_rr (rr, hardness, segment1_slope, segment2_slope); alpha = base_alpha * normal_mode; if (mask) alpha *= *mask; dst_alpha = pixel[ALPHA]; /* a = alpha * color_a + dst_alpha * (1.0f - alpha); * which converts to: */ a = alpha * (color_a - dst_alpha) + dst_alpha; r = pixel[RED]; g = pixel[GREEN]; b = pixel[BLUE]; if (a > 0.0f) { /* By definition the ratio between each color[] and pixel[] component in a non-pre-multipled blend always sums to 1.0f. * Originaly this would have been "(color[n] * alpha * color_a + pixel[n] * dst_alpha * (1.0f - alpha)) / a", * instead we only calculate the cheaper term. */ float src_term = (alpha * color_a) / a; float dst_term = 1.0f - src_term; r = color_r * src_term + r * dst_term; g = color_g * src_term + g * dst_term; b = color_b * src_term + b * dst_term; } if (colorize > 0.0f && base_alpha > 0.0f) { alpha = base_alpha * colorize; a = alpha + dst_alpha - alpha * dst_alpha; if (a > 0.0f) { GimpHSL pixel_hsl, out_hsl; GimpRGB pixel_rgb = {color_r, color_g, color_b}; GimpRGB out_rgb = {r, g, b}; float src_term = alpha / a; float dst_term = 1.0f - src_term; gimp_rgb_to_hsl (&pixel_rgb, &pixel_hsl); gimp_rgb_to_hsl (&out_rgb, &out_hsl); out_hsl.h = pixel_hsl.h; out_hsl.s = pixel_hsl.s; gimp_hsl_to_rgb (&out_hsl, &out_rgb); r = (float)out_rgb.r * src_term + r * dst_term; g = (float)out_rgb.g * src_term + g * dst_term; b = (float)out_rgb.b * src_term + b * dst_term; } } if (component_mask != GIMP_COMPONENT_MASK_ALL) { if (component_mask & GIMP_COMPONENT_MASK_RED) pixel[RED] = r; if (component_mask & GIMP_COMPONENT_MASK_GREEN) pixel[GREEN] = g; if (component_mask & GIMP_COMPONENT_MASK_BLUE) pixel[BLUE] = b; if (component_mask & GIMP_COMPONENT_MASK_ALPHA) pixel[ALPHA] = a; } else { pixel[RED] = r; pixel[GREEN] = g; pixel[BLUE] = b; pixel[ALPHA] = a; } pixel += 4; if (mask) mask += 1; } } } return 1; }
static void bilateral_filter (GeglBuffer *src, const GeglRectangle *src_rect, GeglBuffer *dst, const GeglRectangle *dst_rect, gint s_sigma, gfloat r_sigma) { gint c, x, y, z, k, i; const gint padding_xy = 2; const gint padding_z = 2; const gint width = src_rect->width; const gint height = src_rect->height; const gint channels = 4; const gint sw = (width -1) / s_sigma + 1 + (2 * padding_xy); const gint sh = (height-1) / s_sigma + 1 + (2 * padding_xy); const gint depth = (int)(1.0f / r_sigma) + 1 + (2 * padding_z); /* down-sampling */ gfloat *grid = g_new0 (gfloat, sw * sh * depth * channels * 2); gfloat *blurx = g_new0 (gfloat, sw * sh * depth * channels * 2); gfloat *blury = g_new0 (gfloat, sw * sh * depth * channels * 2); gfloat *blurz = g_new0 (gfloat, sw * sh * depth * channels * 2); gfloat *input = g_new0 (gfloat, width * height * channels); gfloat *smoothed = g_new0 (gfloat, width * height * channels); #define INPUT(x,y,c) input[c+channels*(x + width * y)] #define GRID(x,y,z,c,i) grid [i+2*(c+channels*(x+sw*(y+z*sh)))] #define BLURX(x,y,z,c,i) blurx[i+2*(c+channels*(x+sw*(y+z*sh)))] #define BLURY(x,y,z,c,i) blury[i+2*(c+channels*(x+sw*(y+z*sh)))] #define BLURZ(x,y,z,c,i) blurz[i+2*(c+channels*(x+sw*(y+z*sh)))] gegl_buffer_get (src, src_rect, 1.0, babl_format ("RGBA float"), input, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); for (k=0; k < (sw * sh * depth * channels * 2); k++) { grid [k] = 0.0f; blurx[k] = 0.0f; blury[k] = 0.0f; blurz[k] = 0.0f; } #if 0 /* in case we want to normalize the color space */ gfloat input_min[4] = { FLT_MAX, FLT_MAX, FLT_MAX}; gfloat input_max[4] = {-FLT_MAX, -FLT_MAX, -FLT_MAX}; for(y = 0; y < height; y++) for(x = 0; x < width; x++) for(c = 0; c < channels; c++) { input_min[c] = MIN(input_min[c], INPUT(x,y,c)); input_max[c] = MAX(input_max[c], INPUT(x,y,c)); } #endif /* downsampling */ for(y = 0; y < height; y++) for(x = 0; x < width; x++) for(c = 0; c < channels; c++) { const float z = INPUT(x,y,c); // - input_min[c]; const int small_x = (int)((float)(x) / s_sigma + 0.5f) + padding_xy; const int small_y = (int)((float)(y) / s_sigma + 0.5f) + padding_xy; const int small_z = (int)((float)(z) / r_sigma + 0.5f) + padding_z; g_assert(small_x >= 0 && small_x < sw); g_assert(small_y >= 0 && small_y < sh); g_assert(small_z >= 0 && small_z < depth); GRID(small_x, small_y, small_z, c, 0) += INPUT(x, y, c); GRID(small_x, small_y, small_z, c, 1) += 1.0f; } /* blur in x, y and z */ /* XXX: we could use less memory, but at expense of code readability */ for (z = 1; z < depth-1; z++) for (y = 1; y < sh-1; y++) for (x = 1; x < sw-1; x++) for(c = 0; c < channels; c++) for (i=0; i<2; i++) BLURX(x, y, z, c, i) = (GRID (x-1, y, z, c, i) + 2.0f * GRID (x, y, z, c, i) + GRID (x+1, y, z, c, i)) / 4.0f; for (z = 1; z < depth-1; z++) for (y = 1; y < sh-1; y++) for (x = 1; x < sw-1; x++) for(c = 0; c < channels; c++) for (i=0; i<2; i++) BLURY(x, y, z, c, i) = (BLURX (x, y-1, z, c, i) + 2.0f * BLURX (x, y, z, c, i) + BLURX (x, y+1, z, c, i)) / 4.0f; for (z = 1; z < depth-1; z++) for (y = 1; y < sh-1; y++) for (x = 1; x < sw-1; x++) for(c = 0; c < channels; c++) for (i=0; i<2; i++) BLURZ(x, y, z, c, i) = (BLURY (x, y-1, z, c, i) + 2.0f * BLURY (x, y, z, c, i) + BLURY (x, y+1, z, c, i)) / 4.0f; /* trilinear filtering */ for (y=0; y < height; y++) for (x=0; x < width; x++) for(c = 0; c < channels; c++) { float xf = (float)(x) / s_sigma + padding_xy; float yf = (float)(y) / s_sigma + padding_xy; float zf = INPUT(x,y,c) / r_sigma + padding_z; int x1 = CLAMP((int)xf, 0, sw-1); int y1 = CLAMP((int)yf, 0, sh-1); int z1 = CLAMP((int)zf, 0, depth-1); int x2 = CLAMP(x1+1, 0, sw-1); int y2 = CLAMP(y1+1, 0, sh-1); int z2 = CLAMP(z1+1, 0, depth-1); float x_alpha = xf - x1; float y_alpha = yf - y1; float z_alpha = zf - z1; float interpolated[2]; g_assert(xf >= 0 && xf < sw); g_assert(yf >= 0 && yf < sh); g_assert(zf >= 0 && zf < depth); for (i=0; i<2; i++) interpolated[i] = lerp(lerp(lerp(BLURZ(x1, y1, z1, c, i), BLURZ(x2, y1, z1, c, i), x_alpha), lerp(BLURZ(x1, y2, z1, c, i), BLURZ(x2, y2, z1, c, i), x_alpha), y_alpha), lerp(lerp(BLURZ(x1, y1, z2, c, i), BLURZ(x2, y1, z2, c, i), x_alpha), lerp(BLURZ(x1, y2, z2, c, i), BLURZ(x2, y2, z2, c, i), x_alpha), y_alpha), z_alpha); smoothed[channels*(y*width+x)+c] = interpolated[0] / interpolated[1]; } gegl_buffer_set (dst, dst_rect, 0, babl_format ("RGBA float"), smoothed, GEGL_AUTO_ROWSTRIDE); g_free (grid); g_free (blurx); g_free (blury); g_free (blurz); g_free (input); g_free (smoothed); }