276 lines
		
	
	
		
			8.7 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			276 lines
		
	
	
		
			8.7 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/* 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 <https://www.gnu.org/licenses/>.
 | 
						|
 */
 | 
						|
 | 
						|
#include "config.h"
 | 
						|
 | 
						|
#include <gdk-pixbuf/gdk-pixbuf.h>
 | 
						|
#include <gegl.h>
 | 
						|
 | 
						|
#include "paint-types.h"
 | 
						|
 | 
						|
#include "core/pika.h"
 | 
						|
#include "core/pikabrush.h"
 | 
						|
#include "core/pikadrawable.h"
 | 
						|
#include "core/pikadynamics.h"
 | 
						|
#include "core/pikagradient.h"
 | 
						|
#include "core/pikaimage.h"
 | 
						|
#include "core/pikasymmetry.h"
 | 
						|
 | 
						|
#include "pikaairbrush.h"
 | 
						|
#include "pikaairbrushoptions.h"
 | 
						|
 | 
						|
#include "pika-intl.h"
 | 
						|
 | 
						|
 | 
						|
#define STAMP_MAX_FPS 60
 | 
						|
 | 
						|
 | 
						|
enum
 | 
						|
{
 | 
						|
  STAMP,
 | 
						|
  LAST_SIGNAL
 | 
						|
};
 | 
						|
 | 
						|
 | 
						|
static void       pika_airbrush_finalize (GObject          *object);
 | 
						|
 | 
						|
static void       pika_airbrush_paint    (PikaPaintCore    *paint_core,
 | 
						|
                                          GList            *drawables,
 | 
						|
                                          PikaPaintOptions *paint_options,
 | 
						|
                                          PikaSymmetry     *sym,
 | 
						|
                                          PikaPaintState    paint_state,
 | 
						|
                                          guint32           time);
 | 
						|
static void       pika_airbrush_motion   (PikaPaintCore    *paint_core,
 | 
						|
                                          PikaDrawable     *drawable,
 | 
						|
                                          PikaPaintOptions *paint_options,
 | 
						|
                                          PikaSymmetry     *sym);
 | 
						|
 | 
						|
static gboolean   pika_airbrush_timeout  (gpointer          data);
 | 
						|
 | 
						|
 | 
						|
G_DEFINE_TYPE (PikaAirbrush, pika_airbrush, PIKA_TYPE_PAINTBRUSH)
 | 
						|
 | 
						|
#define parent_class pika_airbrush_parent_class
 | 
						|
 | 
						|
static guint airbrush_signals[LAST_SIGNAL] = { 0 };
 | 
						|
 | 
						|
 | 
						|
void
 | 
						|
pika_airbrush_register (Pika                      *pika,
 | 
						|
                        PikaPaintRegisterCallback  callback)
 | 
						|
{
 | 
						|
  (* callback) (pika,
 | 
						|
                PIKA_TYPE_AIRBRUSH,
 | 
						|
                PIKA_TYPE_AIRBRUSH_OPTIONS,
 | 
						|
                "pika-airbrush",
 | 
						|
                _("Airbrush"),
 | 
						|
                "pika-tool-airbrush");
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
pika_airbrush_class_init (PikaAirbrushClass *klass)
 | 
						|
{
 | 
						|
  GObjectClass       *object_class     = G_OBJECT_CLASS (klass);
 | 
						|
  PikaPaintCoreClass *paint_core_class = PIKA_PAINT_CORE_CLASS (klass);
 | 
						|
 | 
						|
  object_class->finalize  = pika_airbrush_finalize;
 | 
						|
 | 
						|
  paint_core_class->paint = pika_airbrush_paint;
 | 
						|
 | 
						|
  airbrush_signals[STAMP] =
 | 
						|
    g_signal_new ("stamp",
 | 
						|
                  G_TYPE_FROM_CLASS (klass),
 | 
						|
                  G_SIGNAL_RUN_FIRST,
 | 
						|
                  G_STRUCT_OFFSET (PikaAirbrushClass, stamp),
 | 
						|
                  NULL, NULL, NULL,
 | 
						|
                  G_TYPE_NONE, 0);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
pika_airbrush_init (PikaAirbrush *airbrush)
 | 
						|
{
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
pika_airbrush_finalize (GObject *object)
 | 
						|
{
 | 
						|
  PikaAirbrush *airbrush = PIKA_AIRBRUSH (object);
 | 
						|
 | 
						|
  if (airbrush->timeout_id)
 | 
						|
    {
 | 
						|
      g_source_remove (airbrush->timeout_id);
 | 
						|
      airbrush->timeout_id = 0;
 | 
						|
    }
 | 
						|
 | 
						|
  g_clear_object (&airbrush->sym);
 | 
						|
 | 
						|
  G_OBJECT_CLASS (parent_class)->finalize (object);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
pika_airbrush_paint (PikaPaintCore    *paint_core,
 | 
						|
                     GList            *drawables,
 | 
						|
                     PikaPaintOptions *paint_options,
 | 
						|
                     PikaSymmetry     *sym,
 | 
						|
                     PikaPaintState    paint_state,
 | 
						|
                     guint32           time)
 | 
						|
{
 | 
						|
  PikaAirbrush        *airbrush = PIKA_AIRBRUSH (paint_core);
 | 
						|
  PikaAirbrushOptions *options  = PIKA_AIRBRUSH_OPTIONS (paint_options);
 | 
						|
  PikaDynamics        *dynamics = PIKA_BRUSH_CORE (paint_core)->dynamics;
 | 
						|
  PikaCoords           coords;
 | 
						|
 | 
						|
  g_return_if_fail (g_list_length (drawables) == 1);
 | 
						|
 | 
						|
  if (airbrush->timeout_id)
 | 
						|
    {
 | 
						|
      g_source_remove (airbrush->timeout_id);
 | 
						|
      airbrush->timeout_id = 0;
 | 
						|
    }
 | 
						|
 | 
						|
  switch (paint_state)
 | 
						|
    {
 | 
						|
    case PIKA_PAINT_STATE_INIT:
 | 
						|
      PIKA_PAINT_CORE_CLASS (parent_class)->paint (paint_core, drawables,
 | 
						|
                                                   paint_options,
 | 
						|
                                                   sym,
 | 
						|
                                                   paint_state, time);
 | 
						|
      break;
 | 
						|
 | 
						|
    case PIKA_PAINT_STATE_MOTION:
 | 
						|
      coords = *(pika_symmetry_get_origin (sym));
 | 
						|
      pika_airbrush_motion (paint_core, drawables->data, paint_options, sym);
 | 
						|
 | 
						|
      if ((options->rate != 0.0) && ! options->motion_only)
 | 
						|
        {
 | 
						|
          PikaImage *image = pika_item_get_image (PIKA_ITEM (drawables->data));
 | 
						|
          gdouble    fade_point;
 | 
						|
          gdouble    dynamic_rate;
 | 
						|
          gint       timeout;
 | 
						|
 | 
						|
          fade_point = pika_paint_options_get_fade (paint_options, image,
 | 
						|
                                                    paint_core->pixel_dist);
 | 
						|
 | 
						|
          airbrush->drawable      = drawables->data;
 | 
						|
          airbrush->paint_options = paint_options;
 | 
						|
 | 
						|
          pika_symmetry_set_origin (sym, drawables->data, &coords);
 | 
						|
          if (airbrush->sym)
 | 
						|
            g_object_unref (airbrush->sym);
 | 
						|
          airbrush->sym = g_object_ref (sym);
 | 
						|
 | 
						|
          /* Base our timeout on the original stroke. */
 | 
						|
          airbrush->coords = coords;
 | 
						|
 | 
						|
          dynamic_rate = pika_dynamics_get_linear_value (dynamics,
 | 
						|
                                                         PIKA_DYNAMICS_OUTPUT_RATE,
 | 
						|
                                                         &coords,
 | 
						|
                                                         paint_options,
 | 
						|
                                                         fade_point);
 | 
						|
 | 
						|
          timeout = (1000.0 / STAMP_MAX_FPS) /
 | 
						|
                    ((options->rate / 100.0) * dynamic_rate);
 | 
						|
 | 
						|
          airbrush->timeout_id = g_timeout_add_full (G_PRIORITY_HIGH,
 | 
						|
                                                     timeout,
 | 
						|
                                                     pika_airbrush_timeout,
 | 
						|
                                                     airbrush, NULL);
 | 
						|
        }
 | 
						|
      break;
 | 
						|
 | 
						|
    case PIKA_PAINT_STATE_FINISH:
 | 
						|
      PIKA_PAINT_CORE_CLASS (parent_class)->paint (paint_core, drawables,
 | 
						|
                                                   paint_options,
 | 
						|
                                                   sym,
 | 
						|
                                                   paint_state, time);
 | 
						|
 | 
						|
      g_clear_object (&airbrush->sym);
 | 
						|
      break;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
pika_airbrush_motion (PikaPaintCore    *paint_core,
 | 
						|
                      PikaDrawable     *drawable,
 | 
						|
                      PikaPaintOptions *paint_options,
 | 
						|
                      PikaSymmetry     *sym)
 | 
						|
 | 
						|
{
 | 
						|
  PikaAirbrushOptions *options  = PIKA_AIRBRUSH_OPTIONS (paint_options);
 | 
						|
  PikaDynamics        *dynamics = PIKA_BRUSH_CORE (paint_core)->dynamics;
 | 
						|
  PikaImage           *image    = pika_item_get_image (PIKA_ITEM (drawable));
 | 
						|
  gdouble              opacity;
 | 
						|
  gdouble              fade_point;
 | 
						|
  PikaCoords          *coords;
 | 
						|
 | 
						|
  fade_point = pika_paint_options_get_fade (paint_options, image,
 | 
						|
                                            paint_core->pixel_dist);
 | 
						|
 | 
						|
  coords = pika_symmetry_get_origin (sym);
 | 
						|
 | 
						|
  opacity = (options->flow / 100.0 *
 | 
						|
             pika_dynamics_get_linear_value (dynamics,
 | 
						|
                                             PIKA_DYNAMICS_OUTPUT_FLOW,
 | 
						|
                                             coords,
 | 
						|
                                             paint_options,
 | 
						|
                                             fade_point));
 | 
						|
 | 
						|
  _pika_paintbrush_motion (paint_core, drawable, paint_options,
 | 
						|
                           sym, opacity);
 | 
						|
}
 | 
						|
 | 
						|
static gboolean
 | 
						|
pika_airbrush_timeout (gpointer data)
 | 
						|
{
 | 
						|
  PikaAirbrush *airbrush = PIKA_AIRBRUSH (data);
 | 
						|
 | 
						|
  airbrush->timeout_id = 0;
 | 
						|
 | 
						|
  g_signal_emit (airbrush, airbrush_signals[STAMP], 0);
 | 
						|
 | 
						|
  return G_SOURCE_REMOVE;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*  public functions  */
 | 
						|
 | 
						|
 | 
						|
void
 | 
						|
pika_airbrush_stamp (PikaAirbrush *airbrush)
 | 
						|
{
 | 
						|
  GList *drawables;
 | 
						|
 | 
						|
  g_return_if_fail (PIKA_IS_AIRBRUSH (airbrush));
 | 
						|
 | 
						|
  pika_symmetry_set_origin (airbrush->sym,
 | 
						|
                            airbrush->drawable, &airbrush->coords);
 | 
						|
 | 
						|
  drawables = g_list_prepend (NULL, airbrush->drawable),
 | 
						|
  pika_airbrush_paint (PIKA_PAINT_CORE (airbrush),
 | 
						|
                       drawables,
 | 
						|
                       airbrush->paint_options,
 | 
						|
                       airbrush->sym,
 | 
						|
                       PIKA_PAINT_STATE_MOTION, 0);
 | 
						|
  g_list_free (drawables);
 | 
						|
 | 
						|
  pika_symmetry_clear_origin (airbrush->sym);
 | 
						|
}
 |