int main(int argc, char *argv[]) { GeglRectangle expected_infinite_plane = gegl_rectangle_infinite_plane (); GeglRectangle infinite_plane = { INFINITE_PLANE }; int result = SUCCESS; int i = 0; /* Make sure our representation of an infinite plane GeglRectangle * is correct */ if (! gegl_rectangle_equal (&infinite_plane, &expected_infinite_plane)) { result = FAILURE; g_printerr("This test case and GEGL does not represent an infinite plane\n" "GeglRectangle in the same way, update this test case. Aborting.\n"); goto abort; } for (i = 0; i < G_N_ELEMENTS (tests); i++) { GeglRectangle result_rect; gboolean return_value; /* gegl_rectangle_bounding_box() */ gegl_rectangle_bounding_box (&result_rect, &tests[i].rect1, &tests[i].rect2); if (! gegl_rectangle_equal (&result_rect, &tests[i].bounding_box_result)) { result = FAILURE; g_printerr("The gegl_rectangle_bounding_box() test #%d failed. Aborting.\n", i + 1); goto abort; } /* gegl_rectangle_intersect() */ return_value = gegl_rectangle_intersect (&result_rect, &tests[i].rect1, &tests[i].rect2); if (! gegl_rectangle_equal (&result_rect, &tests[i].intersect_result) || return_value != tests[i].intersect_return_value) { result = FAILURE; g_printerr("The gegl_rectangle_intersect() test #%d failed. Aborting.\n", i + 1); goto abort; } /* gegl_rectangle_contains() */ return_value = gegl_rectangle_contains (&tests[i].rect1, &tests[i].rect2); if (return_value != tests[i].contains_return_value) { result = FAILURE; g_printerr("The gegl_rectangle_contains() test #%d failed. Aborting.\n", i + 1); goto abort; } } abort: return result; }
static GeglRectangle get_bounding_box (GeglOperation *self) { GeglRectangle result = { 0, 0, 0, 0 }; GeglRectangle *in_rect = gegl_operation_source_get_bounding_box (self, "input"); GeglRectangle *aux_rect = gegl_operation_source_get_bounding_box (self, "aux"); GeglRectangle *aux2_rect = gegl_operation_source_get_bounding_box (self, "aux2"); if (in_rect) result = *in_rect; if (aux_rect) gegl_rectangle_bounding_box (&result, &result, aux_rect); if (aux2_rect) gegl_rectangle_bounding_box (&result, &result, aux2_rect); return result; }
static void tile_request_start(MyPaintTiledSurface *tiled_surface, MyPaintTileRequest *request) { MyPaintGeglTiledSurface *self = (MyPaintGeglTiledSurface *)tiled_surface; const int tile_size = tiled_surface->tile_size; GeglRectangle tile_bbox; gegl_rectangle_set(&tile_bbox, request->tx * tile_size, request->ty * tile_size, tile_size, tile_size); int read_write_flags; if (request->readonly) { read_write_flags = GEGL_BUFFER_READ; } else { read_write_flags = GEGL_BUFFER_READWRITE; // Extend the bounding box gegl_rectangle_bounding_box(&self->extent_rect, &self->extent_rect, &tile_bbox); gboolean success = gegl_buffer_set_extent(self->buffer, &self->extent_rect); g_assert(success); } if (buffer_is_native(self)) { GeglBufferIterator *iterator = gegl_buffer_iterator_new(self->buffer, &tile_bbox, 0, self->format, read_write_flags, GEGL_ABYSS_NONE); // Read out gboolean completed = gegl_buffer_iterator_next(iterator); g_assert(completed); if (iterator->length != tile_size*tile_size) { g_critical("Unable to get tile aligned access to GeglBuffer"); request->buffer = NULL; } else { request->buffer = (uint16_t *)(iterator->data[0]); } // So we can finish the iterator in tile_request_end() request->context = (void *)iterator; } else { // Extract a linear rectangular chunk of appropriate BablFormat, // potentially triggering copying and color conversions request->buffer = alloc_for_format(self->format, tile_size*tile_size); gegl_buffer_get(self->buffer, &tile_bbox, 1, self->format, request->buffer, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); g_assert(request->buffer); } }
static GeglRectangle get_bounding_box (GeglOperation *self) { GeglRectangle result = { 0, 0, 0, 0 }; GeglRectangle *in_rect = gegl_operation_source_get_bounding_box (self, "input"); GeglRectangle *aux_rect = gegl_operation_source_get_bounding_box (self, "aux"); if (!in_rect) { if (aux_rect) return *aux_rect; return result; } if (aux_rect) { gegl_rectangle_bounding_box (&result, in_rect, aux_rect); } else { return *in_rect; } return result; }
static GeglRectangle get_bounding_box (GeglOperation *operation) { GeglChantO *o = GEGL_CHANT_PROPERTIES (operation); GeglRectangle defined = { 0, 0, 512, 512 }; GeglRectangle *in_rect; gdouble x0, x1, y0, y1; in_rect = gegl_operation_source_get_bounding_box (operation, "input"); gegl_path_get_bounds (o->d, &x0, &x1, &y0, &y1); defined.x = x0; defined.y = y0; defined.width = x1 - x0; defined.height = y1 - y0; if (in_rect) { gegl_rectangle_bounding_box (&defined, &defined, in_rect); } return defined; }
static gboolean gimp_channel_combine_start (GimpChannel *mask, GimpChannelOps op, const GeglRectangle *rect, gboolean full_extent, gboolean full_value, GimpChannelCombineData *data) { GeglBuffer *buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (mask)); GeglRectangle extent; gboolean intersects; extent.x = 0; extent.y = 0; extent.width = gimp_item_get_width (GIMP_ITEM (mask)); extent.height = gimp_item_get_height (GIMP_ITEM (mask)); intersects = gegl_rectangle_intersect (&data->rect, rect, &extent); data->bounds_known = mask->bounds_known; data->empty = mask->empty; data->bounds.x = mask->x1; data->bounds.y = mask->y1; data->bounds.width = mask->x2 - mask->x1; data->bounds.height = mask->y2 - mask->y1; gegl_buffer_freeze_changed (buffer); /* Determine new boundary */ switch (op) { case GIMP_CHANNEL_OP_REPLACE: gimp_channel_combine_clear (mask, NULL); if (! intersects) { data->bounds_known = TRUE; data->empty = TRUE; return FALSE; } data->bounds_known = FALSE; if (full_extent) { data->bounds_known = TRUE; data->empty = FALSE; data->bounds = data->rect; } break; case GIMP_CHANNEL_OP_ADD: if (! intersects) return FALSE; data->bounds_known = FALSE; if (full_extent && (mask->bounds_known || gegl_rectangle_equal (&data->rect, &extent))) { data->bounds_known = TRUE; data->empty = FALSE; if (mask->bounds_known && ! mask->empty) { gegl_rectangle_bounding_box (&data->bounds, &data->bounds, &data->rect); } else { data->bounds = data->rect; } } break; case GIMP_CHANNEL_OP_SUBTRACT: if (intersects && mask->bounds_known) { if (mask->empty) { intersects = FALSE; } else { intersects = gegl_rectangle_intersect (&data->rect, &data->rect, &data->bounds); } } if (! intersects) return FALSE; if (full_value && gegl_rectangle_contains (&data->rect, mask->bounds_known ? &data->bounds : &extent)) { gimp_channel_combine_clear (mask, NULL); data->bounds_known = TRUE; data->empty = TRUE; return FALSE; } data->bounds_known = FALSE; gegl_buffer_set_abyss (buffer, &data->rect); break; case GIMP_CHANNEL_OP_INTERSECT: if (intersects && mask->bounds_known) { if (mask->empty) { intersects = FALSE; } else { intersects = gegl_rectangle_intersect (&data->rect, &data->rect, &data->bounds); } } if (! intersects) { gimp_channel_combine_clear (mask, NULL); data->bounds_known = TRUE; data->empty = TRUE; return FALSE; } if (full_value && mask->bounds_known && gegl_rectangle_contains (&data->rect, &data->bounds)) { return FALSE; } data->bounds_known = FALSE; gimp_channel_combine_clear_complement (mask, &data->rect); gegl_buffer_set_abyss (buffer, &data->rect); break; } return TRUE; }
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; }
void gegl_graph_prepare_request (GeglGraphTraversal *path, const GeglRectangle *request_roi, gint level) { GList *list_iter = NULL; static const GeglRectangle empty_rect = {0, 0, 0, 0}; g_return_if_fail (path->bfs_path); if (path->rects_dirty) { /* Zero all the needs rects so we can intersect with them below */ for (list_iter = path->bfs_path; list_iter; list_iter = list_iter->next) { GeglNode *node = GEGL_NODE (list_iter->data); GeglOperationContext *context = g_hash_table_lookup (path->contexts, node); /* We only need to reset the need rect, result will always get overwritten */ gegl_operation_context_set_need_rect (context, &empty_rect); /* Reset cached status, because the rect we need may have changed */ context->cached = FALSE; } } path->rects_dirty = TRUE; { /* Prep the first node */ GeglNode *node = GEGL_NODE (path->bfs_path->data); GeglOperationContext *context = g_hash_table_lookup (path->contexts, node); GeglRectangle new_need; g_return_if_fail (context); gegl_rectangle_intersect (&new_need, &node->have_rect, request_roi); gegl_operation_context_set_need_rect (context, &new_need); gegl_operation_context_set_result_rect (context, &new_need); } /* Iterate over all the nodes and propagate the requested rectangle */ for (list_iter = path->bfs_path; list_iter; list_iter = list_iter->next) { GeglNode *node = GEGL_NODE (list_iter->data); GeglOperation *operation = node->operation; GeglOperationContext *context; GeglRectangle *request; GSList *input_pads; context = g_hash_table_lookup (path->contexts, node); g_return_if_fail (context); request = gegl_operation_context_get_need_rect (context); if (request->width == 0 || request->height == 0) { gegl_operation_context_set_result_rect (context, &empty_rect); continue; } if (node->cache) { gint i; for (i = level; i >=0 && !context->cached; i--) { if (gegl_region_rect_in (node->cache->valid_region[level], request) == GEGL_OVERLAP_RECTANGLE_IN) { /* This node is cached and the cache fulfills our need rect */ context->cached = TRUE; gegl_operation_context_set_result_rect (context, &empty_rect); } } if (context->cached) continue; } { /* Expand request if the operation has a minimum processing requirement */ GeglRectangle full_request = gegl_operation_get_cached_region (operation, request); gegl_operation_context_set_need_rect (context, &full_request); /* FIXME: We could trim this down based on the cache, instead of being all or nothing */ gegl_operation_context_set_result_rect (context, request); for (input_pads = node->input_pads; input_pads; input_pads = input_pads->next) { GeglPad *source_pad = gegl_pad_get_connected_to (input_pads->data); if (source_pad) { GeglNode *source_node = gegl_pad_get_node (source_pad); GeglOperationContext *source_context = g_hash_table_lookup (path->contexts, source_node); const gchar *pad_name = gegl_pad_get_name (input_pads->data); GeglRectangle rect, current_need, new_need; /* Combine this need rect with any existing request */ rect = gegl_operation_get_required_for_output (operation, pad_name, &full_request); current_need = *gegl_operation_context_get_need_rect (source_context); gegl_rectangle_bounding_box (&new_need, &rect, ¤t_need); /* Limit request to the nodes output */ gegl_rectangle_intersect (&new_need, &source_node->have_rect, &new_need); gegl_operation_context_set_need_rect (source_context, &new_need); } } } } }