/* PIKA - Photo and Image Kooker Application * a rebranding of The GNU Image Manipulation Program (created with heckimp) * A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio * * Original copyright, applying to most contents (license remains unchanged): * Copyright (C) 1995 Spencer Kimball and Peter Mattis * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "config.h" #include #include #include #include #include "libpikabase/pikabase.h" #include "paint-types.h" #include "gegl/pika-gegl-apply-operation.h" #include "gegl/pika-gegl-loops.h" #include "core/pika.h" #include "core/pikadrawable.h" #include "core/pikadynamics.h" #include "core/pikaerror.h" #include "core/pikaimage.h" #include "core/pikapattern.h" #include "core/pikapickable.h" #include "core/pikasymmetry.h" #include "pikaclone.h" #include "pikacloneoptions.h" #include "pika-intl.h" static gboolean pika_clone_start (PikaPaintCore *paint_core, GList *drawables, PikaPaintOptions *paint_options, const PikaCoords *coords, GError **error); static void pika_clone_motion (PikaSourceCore *source_core, PikaDrawable *drawable, PikaPaintOptions *paint_options, const PikaCoords *coords, GeglNode *op, gdouble opacity, PikaPickable *src_pickable, GeglBuffer *src_buffer, GeglRectangle *src_rect, gint src_offset_x, gint src_offset_y, GeglBuffer *paint_buffer, gint paint_buffer_x, gint paint_buffer_y, gint paint_area_offset_x, gint paint_area_offset_y, gint paint_area_width, gint paint_area_height); static gboolean pika_clone_use_source (PikaSourceCore *source_core, PikaSourceOptions *options); G_DEFINE_TYPE (PikaClone, pika_clone, PIKA_TYPE_SOURCE_CORE) #define parent_class pika_clone_parent_class void pika_clone_register (Pika *pika, PikaPaintRegisterCallback callback) { (* callback) (pika, PIKA_TYPE_CLONE, PIKA_TYPE_CLONE_OPTIONS, "pika-clone", _("Clone"), "pika-tool-clone"); } static void pika_clone_class_init (PikaCloneClass *klass) { PikaPaintCoreClass *paint_core_class = PIKA_PAINT_CORE_CLASS (klass); PikaSourceCoreClass *source_core_class = PIKA_SOURCE_CORE_CLASS (klass); paint_core_class->start = pika_clone_start; source_core_class->use_source = pika_clone_use_source; source_core_class->motion = pika_clone_motion; } static void pika_clone_init (PikaClone *clone) { } static gboolean pika_clone_start (PikaPaintCore *paint_core, GList *drawables, PikaPaintOptions *paint_options, const PikaCoords *coords, GError **error) { PikaCloneOptions *options = PIKA_CLONE_OPTIONS (paint_options); if (! PIKA_PAINT_CORE_CLASS (parent_class)->start (paint_core, drawables, paint_options, coords, error)) { return FALSE; } if (options->clone_type == PIKA_CLONE_PATTERN) { if (! pika_context_get_pattern (PIKA_CONTEXT (options))) { g_set_error_literal (error, PIKA_ERROR, PIKA_FAILED, _("No patterns available for use with this tool.")); return FALSE; } } return TRUE; } static void pika_clone_motion (PikaSourceCore *source_core, PikaDrawable *drawable, PikaPaintOptions *paint_options, const PikaCoords *coords, GeglNode *op, gdouble opacity, PikaPickable *src_pickable, GeglBuffer *src_buffer, GeglRectangle *src_rect, gint src_offset_x, gint src_offset_y, GeglBuffer *paint_buffer, gint paint_buffer_x, gint paint_buffer_y, gint paint_area_offset_x, gint paint_area_offset_y, gint paint_area_width, gint paint_area_height) { PikaPaintCore *paint_core = PIKA_PAINT_CORE (source_core); PikaBrushCore *brush_core = PIKA_BRUSH_CORE (source_core); PikaCloneOptions *options = PIKA_CLONE_OPTIONS (paint_options); PikaSourceOptions *source_options = PIKA_SOURCE_OPTIONS (paint_options); PikaContext *context = PIKA_CONTEXT (paint_options); PikaDynamics *dynamics = brush_core->dynamics; PikaImage *image = pika_item_get_image (PIKA_ITEM (drawable)); gdouble fade_point; gdouble force; if (pika_source_core_use_source (source_core, source_options)) { if (! op) { pika_gegl_buffer_copy (src_buffer, GEGL_RECTANGLE (src_rect->x, src_rect->y, paint_area_width, paint_area_height), GEGL_ABYSS_NONE, paint_buffer, GEGL_RECTANGLE (paint_area_offset_x, paint_area_offset_y, 0, 0)); } else { pika_gegl_apply_operation (src_buffer, NULL, NULL, op, paint_buffer, GEGL_RECTANGLE (paint_area_offset_x, paint_area_offset_y, paint_area_width, paint_area_height), FALSE); } } else if (options->clone_type == PIKA_CLONE_PATTERN) { PikaPattern *pattern = pika_context_get_pattern (context); GeglBuffer *src_buffer = pika_pattern_create_buffer (pattern); src_offset_x += gegl_buffer_get_width (src_buffer) / 2; src_offset_y += gegl_buffer_get_height (src_buffer) / 2; gegl_buffer_set_pattern (paint_buffer, GEGL_RECTANGLE (paint_area_offset_x, paint_area_offset_y, paint_area_width, paint_area_height), src_buffer, - paint_buffer_x - src_offset_x, - paint_buffer_y - src_offset_y); g_object_unref (src_buffer); } else { g_return_if_reached (); } fade_point = pika_paint_options_get_fade (paint_options, image, paint_core->pixel_dist); if (pika_dynamics_is_output_enabled (dynamics, PIKA_DYNAMICS_OUTPUT_FORCE)) force = pika_dynamics_get_linear_value (dynamics, PIKA_DYNAMICS_OUTPUT_FORCE, coords, paint_options, fade_point); else force = paint_options->brush_force; pika_brush_core_paste_canvas (PIKA_BRUSH_CORE (paint_core), drawable, coords, MIN (opacity, PIKA_OPACITY_OPAQUE), pika_context_get_opacity (context), pika_context_get_paint_mode (context), pika_paint_options_get_brush_mode (paint_options), force, /* In fixed mode, paint incremental so the * individual brushes are properly applied * on top of each other. * Otherwise the stuff we paint is seamless * and we don't need intermediate masking. */ source_options->align_mode == PIKA_SOURCE_ALIGN_FIXED ? PIKA_PAINT_INCREMENTAL : PIKA_PAINT_CONSTANT); } static gboolean pika_clone_use_source (PikaSourceCore *source_core, PikaSourceOptions *options) { return PIKA_CLONE_OPTIONS (options)->clone_type == PIKA_CLONE_IMAGE; }