gboolean gegl_rectangle_intersect (GeglRectangle *dest, const GeglRectangle *src1, const GeglRectangle *src2) { gint x1, x2, y1, y2; x1 = MAX (src1->x, src2->x); x2 = MIN (src1->x + src1->width, src2->x + src2->width); if (x2 <= x1) { if (dest) gegl_rectangle_set (dest, 0, 0, 0, 0); return FALSE; } y1 = MAX (src1->y, src2->y); y2 = MIN (src1->y + src1->height, src2->y + src2->height); if (y2 <= y1) { if (dest) gegl_rectangle_set (dest, 0, 0, 0, 0); return FALSE; } if (dest) gegl_rectangle_set (dest, x1, y1, x2 - x1, y2 - y1); return TRUE; }
MyPaintGeglTiledSurface * mypaint_gegl_tiled_surface_new(void) { MyPaintGeglTiledSurface *self = (MyPaintGeglTiledSurface *)malloc(sizeof(MyPaintGeglTiledSurface)); mypaint_tiled_surface_init(&self->parent, tile_request_start, tile_request_end); // MyPaintSurface vfuncs self->parent.parent.destroy = free_gegl_tiledsurf; self->parent.parent.save_png = save_png; self->parent.threadsafe_tile_requests = TRUE; self->buffer = NULL; gegl_rectangle_set(&self->extent_rect, 0, 0, 0, 0); self->format = babl_format_new(babl_model ("R'aG'aB'aA"), babl_type ("u15"), babl_component("R'a"), babl_component("G'a"), babl_component("B'a"), babl_component("A"), NULL); g_assert(self->format); mypaint_gegl_tiled_surface_set_buffer(self, NULL); return self; }
static void tile_request_end(MyPaintTiledSurface *tiled_surface, MyPaintTileRequest *request) { MyPaintGeglTiledSurface *self = (MyPaintGeglTiledSurface *)tiled_surface; if (buffer_is_native(self)) { GeglBufferIterator *iterator = (GeglBufferIterator *)request->context; if (iterator) { gegl_buffer_iterator_next(iterator); request->context = NULL; } } else { // Push our linear buffer back into the GeglBuffer const int tile_size = tiled_surface->tile_size; GeglRectangle tile_bbox; g_assert(request->buffer); gegl_rectangle_set(&tile_bbox, request->tx*tile_size, request->ty*tile_size, tile_size, tile_size); gegl_buffer_set(self->buffer, &tile_bbox, 0, self->format, request->buffer, GEGL_AUTO_ROWSTRIDE); gegl_free(request->buffer); } }
void gegl_rectangle_bounding_box (GeglRectangle *dest, const GeglRectangle *src1, const GeglRectangle *src2) { gboolean s1_has_area = src1->width && src1->height; gboolean s2_has_area = src2->width && src2->height; if (!s1_has_area && !s2_has_area) gegl_rectangle_set (dest, 0, 0, 0, 0); else if (!s1_has_area) gegl_rectangle_copy (dest, src2); else if (!s2_has_area) gegl_rectangle_copy (dest, src1); else { gint x1 = MIN (src1->x, src2->x); gint x2 = MAX (src1->x + src1->width, src2->x + src2->width); gint y1 = MIN (src1->y, src2->y); gint y2 = MAX (src1->y + src1->height, src2->y + src2->height); dest->x = x1; dest->y = y1; dest->width = x2 - x1; dest->height = y2 - y1; } }
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); } }
/* Create a layer with the provided image data and add it to the image */ gboolean create_layer(gint32 image_ID, uint8_t *layer_data, gint32 position, gchar *name, gint width, gint height, gint32 offsetx, gint32 offsety) { gint32 layer_ID; #ifdef GIMP_2_9 GeglBuffer *geglbuffer; GeglRectangle extent; #else GimpDrawable *drawable; GimpPixelRgn region; #endif layer_ID = gimp_layer_new(image_ID, name, width, height, GIMP_RGBA_IMAGE, 100, GIMP_NORMAL_MODE); #ifdef GIMP_2_9 /* Retrieve the buffer for the layer */ geglbuffer = gimp_drawable_get_buffer(layer_ID); /* Copy the image data to the region */ gegl_rectangle_set(&extent, 0, 0, width, height); gegl_buffer_set(geglbuffer, &extent, 0, NULL, layer_data, GEGL_AUTO_ROWSTRIDE); /* Flush the drawable and detach */ gegl_buffer_flush(geglbuffer); g_object_unref(geglbuffer); #else /* Retrieve the drawable for the layer */ drawable = gimp_drawable_get(layer_ID); /* Get a pixel region from the layer */ gimp_pixel_rgn_init(®ion, drawable, 0, 0, width, height, FALSE, FALSE); /* Copy the image data to the region */ gimp_pixel_rgn_set_rect(®ion, layer_data, 0, 0, width, height); /* Flush the drawable and detach */ gimp_drawable_flush(drawable); gimp_drawable_detach(drawable); #endif /* Add the new layer to the image */ gimp_image_insert_layer(image_ID, layer_ID, -1, position); /* If layer offsets were provided, use them to position the image */ if (offsetx || offsety) { gimp_layer_set_offsets(layer_ID, offsetx, offsety); } /* TODO: fix this */ return TRUE; }
static gint gegl_buffer_import_png (GeglBuffer *gegl_buffer, GInputStream *stream, gint dest_x, gint dest_y, gint *ret_width, gint *ret_height, const Babl *format, // can be NULL GError **err) { gint width; gint bit_depth; gint bpp; gint number_of_passes=1; png_uint_32 w; png_uint_32 h; png_structp load_png_ptr; png_infop load_info_ptr; guchar *pixels; /*png_bytep *rows;*/ unsigned int i; png_bytep *row_p = NULL; g_return_val_if_fail(stream, -1); if (!check_valid_png_header(stream, err)) { return -1; } load_png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING, NULL, error_fn, NULL); if (!load_png_ptr) { return -1; } load_info_ptr = png_create_info_struct (load_png_ptr); if (!load_info_ptr) { png_destroy_read_struct (&load_png_ptr, &load_info_ptr, NULL); return -1; } if (setjmp (png_jmpbuf (load_png_ptr))) { png_destroy_read_struct (&load_png_ptr, &load_info_ptr, NULL); if (row_p) g_free (row_p); return -1; } png_set_read_fn(load_png_ptr, stream, read_fn); png_set_sig_bytes (load_png_ptr, 8); // we already read header png_read_info (load_png_ptr, load_info_ptr); { int color_type; int interlace_type; png_get_IHDR (load_png_ptr, load_info_ptr, &w, &h, &bit_depth, &color_type, &interlace_type, NULL, NULL); width = w; if (ret_width) *ret_width = w; if (ret_height) *ret_height = h; if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) { png_set_expand (load_png_ptr); bit_depth = 8; } if (png_get_valid (load_png_ptr, load_info_ptr, PNG_INFO_tRNS)) { png_set_tRNS_to_alpha (load_png_ptr); color_type |= PNG_COLOR_MASK_ALPHA; } switch (color_type) { case PNG_COLOR_TYPE_GRAY: bpp = 1; break; case PNG_COLOR_TYPE_GRAY_ALPHA: bpp = 2; break; case PNG_COLOR_TYPE_RGB: bpp = 3; break; case PNG_COLOR_TYPE_RGB_ALPHA: bpp = 4; break; case (PNG_COLOR_TYPE_PALETTE | PNG_COLOR_MASK_ALPHA): bpp = 4; break; case PNG_COLOR_TYPE_PALETTE: bpp = 3; break; default: g_warning ("color type mismatch"); png_destroy_read_struct (&load_png_ptr, &load_info_ptr, NULL); return -1; } if (color_type == PNG_COLOR_TYPE_PALETTE) png_set_palette_to_rgb (load_png_ptr); if (bit_depth == 16) bpp = bpp << 1; if (!format) format = get_babl_format(bit_depth, color_type); #if BYTE_ORDER == LITTLE_ENDIAN if (bit_depth == 16) png_set_swap (load_png_ptr); #endif if (interlace_type == PNG_INTERLACE_ADAM7) number_of_passes = png_set_interlace_handling (load_png_ptr); if (png_get_valid (load_png_ptr, load_info_ptr, PNG_INFO_gAMA)) { gdouble gamma; png_get_gAMA (load_png_ptr, load_info_ptr, &gamma); png_set_gamma (load_png_ptr, 2.2, gamma); } else { png_set_gamma (load_png_ptr, 2.2, 0.45455); } png_read_update_info (load_png_ptr, load_info_ptr); } pixels = g_malloc0 (width*bpp); { gint pass; GeglRectangle rect; for (pass=0; pass<number_of_passes; pass++) { for(i=0; i<h; i++) { gegl_rectangle_set (&rect, 0, i, width, 1); if (pass != 0) gegl_buffer_get (gegl_buffer, &rect, 1.0, format, pixels, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); png_read_rows (load_png_ptr, &pixels, NULL, 1); gegl_buffer_set (gegl_buffer, &rect, 0, format, pixels, GEGL_AUTO_ROWSTRIDE); } } } png_read_end (load_png_ptr, NULL); png_destroy_read_struct (&load_png_ptr, &load_info_ptr, NULL); g_free (pixels); return 0; }
static gboolean process (GeglOperation *operation, GeglBuffer *input, GeglBuffer *output, const GeglRectangle *roi, gint level) { gint components; gint col, c; gfloat *rowbefore; gfloat *rowthis; gfloat *rowafter; gfloat *dest; gfloat *ninepix; gboolean has_alpha; guint alpha; const Babl *format = gegl_operation_get_format (operation, "input"); /* The rectangle of the current row we are working on */ GeglRectangle rowrect; /* The rectangle of the sample we are going to take for the * next line (this does include the interpolation distance!) */ GeglRectangle rownext_bufrect; components = babl_format_get_n_components (format); has_alpha = babl_format_has_alpha (format); alpha = components - 1; /* The original algorithm that appeared in GIMP did a manual clamping * of samples outside the input rectangle, by always allocating a * buffer which is 1-pixel wider than necessary, and then filling the * edges of the buffer with repetitions of the edges. * We don't need this complexity here thanks to the CLAMP abyss policy * which is implemented by GEGL. We still allocate buffers which are * larger, but we let GEGL fill the edges with the clamped values. */ rowbefore = g_new (gfloat, (roi->width + 2) * components); rowthis = g_new (gfloat, (roi->width + 2) * components); rowafter = g_new (gfloat, (roi->width + 2) * components); dest = g_new (gfloat, roi->width * components); ninepix = g_new (gfloat, 9 * components); gegl_rectangle_set (&rowrect, roi->x, roi->y, roi->width, 1); gegl_rectangle_set (&rownext_bufrect, roi->x - 1, roi->y - 1, roi->width + 2, 1); gegl_buffer_get (input, &rownext_bufrect, 1.0, format, rowbefore, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_CLAMP); ++rownext_bufrect.y; gegl_buffer_get (input, &rownext_bufrect, 1.0, format, rowthis, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_CLAMP); ++rownext_bufrect.y; gegl_buffer_get (input, &rownext_bufrect, 1.0, format, rowafter, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_CLAMP); ++rownext_bufrect.y; for (rowrect.y = roi->y; rowrect.y < (roi->y + roi->height); ++rowrect.y) { gfloat *tmp; /* this macro returns the current pixel if it has some opacity. Otherwise * it returns the center pixel of the current 3x3 area. */ #define USE_IF_ALPHA(p) (((!has_alpha) || *((p)+alpha)) ? (p) : &rowthis[(col+1) * components]) for (col = 0; col < roi->width; ++col) { /* do 9x extrapolation pass */ if (((!has_alpha) || (rowthis[(col + 1) * components + alpha] > 0)) && extrapolate9 (components, &ninepix[0 * components], &ninepix[1 * components], &ninepix[2 * components], &ninepix[3 * components], &ninepix[4 * components], &ninepix[5 * components], &ninepix[6 * components], &ninepix[7 * components], &ninepix[8 * components], USE_IF_ALPHA (&rowbefore[(col + 0) * components]), USE_IF_ALPHA (&rowbefore[(col + 1) * components]), USE_IF_ALPHA (&rowbefore[(col + 2) * components]), USE_IF_ALPHA (&rowthis [(col + 0) * components]), &rowthis [(col + 1) * components], USE_IF_ALPHA (&rowthis [(col + 2) * components]), USE_IF_ALPHA (&rowafter [(col + 0) * components]), USE_IF_ALPHA (&rowafter [(col + 1) * components]), USE_IF_ALPHA (&rowafter [(col + 2) * components]) )) { /* subsample results and put into dest */ for (c = 0; c < components; ++c) { #define NINEPIX(index, c) ninepix[(index) * components + (c)] dest[(col * components) + c] = (3 * NINEPIX(0, c) + 5 * NINEPIX(1, c) + 3 * NINEPIX(2, c) + 5 * NINEPIX(3, c) + 6 * NINEPIX(4, c) + 5 * NINEPIX(5, c) + 3 * NINEPIX(6, c) + 5 * NINEPIX(7, c) + 3 * NINEPIX(8, c) /* The GIMP implementation added 19 (out of 255) here before * normalizing (dividing by 38), which is equivalent to a * call to "ceil". We don't need this since we are working * with floating point numbers... */ ) / 38; #undef NINEPIX } } else { memcpy (&dest[col * components], &rowthis[(col + 1) * components], components * sizeof(gfloat)); } } #undef USE_IF_ALPHA /* write result row to dest */ gegl_buffer_set (output, &rowrect, 0, format, &dest[0], GEGL_AUTO_ROWSTRIDE); /* rotate pointers */ tmp = rowbefore; rowbefore = rowthis; rowthis = rowafter; rowafter = tmp; /* populate new after-row */ gegl_buffer_get (input, &rownext_bufrect, 1.0, format, rowafter, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_CLAMP); ++rownext_bufrect.y; } g_free (rowbefore); g_free (rowthis); g_free (rowafter); g_free (dest); g_free (ninepix); return TRUE; }