311 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
		
		
			
		
	
	
			311 lines
		
	
	
		
			11 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 "core-types.h"
 | ||
|  | 
 | ||
|  | #include "gegl/pika-babl.h"
 | ||
|  | 
 | ||
|  | #include "pikaerror.h"
 | ||
|  | #include "pikaimage.h"
 | ||
|  | #include "pikalayer.h"
 | ||
|  | #include "pikalayermask.h"
 | ||
|  | 
 | ||
|  | #include "pika-intl.h"
 | ||
|  | 
 | ||
|  | 
 | ||
|  | static void            pika_layer_mask_preview_freeze       (PikaViewable      *viewable); | ||
|  | static void            pika_layer_mask_preview_thaw         (PikaViewable      *viewable); | ||
|  | 
 | ||
|  | static gboolean        pika_layer_mask_is_attached          (PikaItem          *item); | ||
|  | static gboolean        pika_layer_mask_is_content_locked    (PikaItem          *item, | ||
|  |                                                              PikaItem         **locked_item); | ||
|  | static gboolean        pika_layer_mask_is_position_locked   (PikaItem          *item, | ||
|  |                                                              PikaItem         **locked_item, | ||
|  |                                                              gboolean           check_children); | ||
|  | static PikaItemTree  * pika_layer_mask_get_tree             (PikaItem          *item); | ||
|  | static PikaItem      * pika_layer_mask_duplicate            (PikaItem          *item, | ||
|  |                                                              GType              new_type); | ||
|  | static gboolean        pika_layer_mask_rename               (PikaItem          *item, | ||
|  |                                                              const gchar       *new_name, | ||
|  |                                                              const gchar       *undo_desc, | ||
|  |                                                              GError           **error); | ||
|  | 
 | ||
|  | static void            pika_layer_mask_bounding_box_changed (PikaDrawable      *drawable); | ||
|  | static void            pika_layer_mask_convert_type         (PikaDrawable      *drawable, | ||
|  |                                                              PikaImage         *dest_image, | ||
|  |                                                              const Babl        *new_format, | ||
|  |                                                              PikaColorProfile  *src_profile, | ||
|  |                                                              PikaColorProfile  *dest_profile, | ||
|  |                                                              GeglDitherMethod   layer_dither_type, | ||
|  |                                                              GeglDitherMethod   mask_dither_type, | ||
|  |                                                              gboolean           push_undo, | ||
|  |                                                              PikaProgress      *progress); | ||
|  | 
 | ||
|  | 
 | ||
|  | G_DEFINE_TYPE (PikaLayerMask, pika_layer_mask, PIKA_TYPE_CHANNEL) | ||
|  | 
 | ||
|  | #define parent_class pika_layer_mask_parent_class
 | ||
|  | 
 | ||
|  | 
 | ||
|  | static void | ||
|  | pika_layer_mask_class_init (PikaLayerMaskClass *klass) | ||
|  | { | ||
|  |   PikaViewableClass *viewable_class = PIKA_VIEWABLE_CLASS (klass); | ||
|  |   PikaItemClass     *item_class     = PIKA_ITEM_CLASS (klass); | ||
|  |   PikaDrawableClass *drawable_class = PIKA_DRAWABLE_CLASS (klass); | ||
|  | 
 | ||
|  |   viewable_class->default_icon_name = "pika-layer-mask"; | ||
|  | 
 | ||
|  |   viewable_class->preview_freeze       = pika_layer_mask_preview_freeze; | ||
|  |   viewable_class->preview_thaw         = pika_layer_mask_preview_thaw; | ||
|  | 
 | ||
|  |   item_class->is_attached              = pika_layer_mask_is_attached; | ||
|  |   item_class->is_content_locked        = pika_layer_mask_is_content_locked; | ||
|  |   item_class->is_position_locked       = pika_layer_mask_is_position_locked; | ||
|  |   item_class->get_tree                 = pika_layer_mask_get_tree; | ||
|  |   item_class->duplicate                = pika_layer_mask_duplicate; | ||
|  |   item_class->rename                   = pika_layer_mask_rename; | ||
|  |   item_class->translate_desc           = C_("undo-type", "Move Layer Mask"); | ||
|  |   item_class->to_selection_desc        = C_("undo-type", "Layer Mask to Selection"); | ||
|  | 
 | ||
|  |   drawable_class->bounding_box_changed = pika_layer_mask_bounding_box_changed; | ||
|  |   drawable_class->convert_type         = pika_layer_mask_convert_type; | ||
|  | } | ||
|  | 
 | ||
|  | static void | ||
|  | pika_layer_mask_init (PikaLayerMask *layer_mask) | ||
|  | { | ||
|  |   layer_mask->layer = NULL; | ||
|  | } | ||
|  | 
 | ||
|  | static void | ||
|  | pika_layer_mask_preview_freeze (PikaViewable *viewable) | ||
|  | { | ||
|  |   PikaLayerMask *mask  = PIKA_LAYER_MASK (viewable); | ||
|  |   PikaLayer     *layer = pika_layer_mask_get_layer (mask); | ||
|  | 
 | ||
|  |   if (layer) | ||
|  |     { | ||
|  |       PikaViewable *parent = pika_viewable_get_parent (PIKA_VIEWABLE (layer)); | ||
|  | 
 | ||
|  |       if (! parent && pika_item_is_attached (PIKA_ITEM (layer))) | ||
|  |         parent = PIKA_VIEWABLE (pika_item_get_image (PIKA_ITEM (layer))); | ||
|  | 
 | ||
|  |       if (parent) | ||
|  |         pika_viewable_preview_freeze (parent); | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | static void | ||
|  | pika_layer_mask_preview_thaw (PikaViewable *viewable) | ||
|  | { | ||
|  |   PikaLayerMask *mask  = PIKA_LAYER_MASK (viewable); | ||
|  |   PikaLayer     *layer = pika_layer_mask_get_layer (mask); | ||
|  | 
 | ||
|  |   if (layer) | ||
|  |     { | ||
|  |       PikaViewable *parent = pika_viewable_get_parent (PIKA_VIEWABLE (layer)); | ||
|  | 
 | ||
|  |       if (! parent && pika_item_is_attached (PIKA_ITEM (layer))) | ||
|  |         parent = PIKA_VIEWABLE (pika_item_get_image (PIKA_ITEM (layer))); | ||
|  | 
 | ||
|  |       if (parent) | ||
|  |         pika_viewable_preview_thaw (parent); | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | static gboolean | ||
|  | pika_layer_mask_is_content_locked (PikaItem  *item, | ||
|  |                                    PikaItem **locked_item) | ||
|  | { | ||
|  |   PikaLayerMask *mask  = PIKA_LAYER_MASK (item); | ||
|  |   PikaLayer     *layer = pika_layer_mask_get_layer (mask); | ||
|  | 
 | ||
|  |   if (layer) | ||
|  |     return pika_item_is_content_locked (PIKA_ITEM (layer), locked_item); | ||
|  | 
 | ||
|  |   return FALSE; | ||
|  | } | ||
|  | 
 | ||
|  | static gboolean | ||
|  | pika_layer_mask_is_position_locked (PikaItem  *item, | ||
|  |                                     PikaItem **locked_item, | ||
|  |                                     gboolean   check_children) | ||
|  | { | ||
|  |   PikaLayerMask *mask  = PIKA_LAYER_MASK (item); | ||
|  |   PikaLayer     *layer = pika_layer_mask_get_layer (mask); | ||
|  | 
 | ||
|  |   if (layer) | ||
|  |     return pika_item_is_position_locked (PIKA_ITEM (layer), locked_item); | ||
|  | 
 | ||
|  |   return FALSE; | ||
|  | } | ||
|  | 
 | ||
|  | static gboolean | ||
|  | pika_layer_mask_is_attached (PikaItem *item) | ||
|  | { | ||
|  |   PikaLayerMask *mask  = PIKA_LAYER_MASK (item); | ||
|  |   PikaLayer     *layer = pika_layer_mask_get_layer (mask); | ||
|  | 
 | ||
|  |   return (PIKA_IS_IMAGE (pika_item_get_image (item)) && | ||
|  |           PIKA_IS_LAYER (layer)                      && | ||
|  |           pika_layer_get_mask (layer) == mask        && | ||
|  |           pika_item_is_attached (PIKA_ITEM (layer))); | ||
|  | } | ||
|  | 
 | ||
|  | static PikaItemTree * | ||
|  | pika_layer_mask_get_tree (PikaItem *item) | ||
|  | { | ||
|  |   return NULL; | ||
|  | } | ||
|  | 
 | ||
|  | static PikaItem * | ||
|  | pika_layer_mask_duplicate (PikaItem *item, | ||
|  |                            GType     new_type) | ||
|  | { | ||
|  |   PikaItem *new_item; | ||
|  | 
 | ||
|  |   g_return_val_if_fail (g_type_is_a (new_type, PIKA_TYPE_DRAWABLE), NULL); | ||
|  | 
 | ||
|  |   new_item = PIKA_ITEM_CLASS (parent_class)->duplicate (item, new_type); | ||
|  | 
 | ||
|  |   return new_item; | ||
|  | } | ||
|  | 
 | ||
|  | static gboolean | ||
|  | pika_layer_mask_rename (PikaItem     *item, | ||
|  |                         const gchar  *new_name, | ||
|  |                         const gchar  *undo_desc, | ||
|  |                         GError      **error) | ||
|  | { | ||
|  |   /* reject renaming, layer masks are always named "<layer name> mask"  */ | ||
|  | 
 | ||
|  |   g_set_error (error, PIKA_ERROR, PIKA_FAILED, | ||
|  |                _("Cannot rename layer masks.")); | ||
|  | 
 | ||
|  |   return FALSE; | ||
|  | } | ||
|  | 
 | ||
|  | static void | ||
|  | pika_layer_mask_bounding_box_changed (PikaDrawable *drawable) | ||
|  | { | ||
|  |   PikaLayerMask *mask  = PIKA_LAYER_MASK (drawable); | ||
|  |   PikaLayer     *layer = pika_layer_mask_get_layer (mask); | ||
|  | 
 | ||
|  |   if (PIKA_DRAWABLE_CLASS (parent_class)->bounding_box_changed) | ||
|  |     PIKA_DRAWABLE_CLASS (parent_class)->bounding_box_changed (drawable); | ||
|  | 
 | ||
|  |   if (layer) | ||
|  |     pika_drawable_update_bounding_box (PIKA_DRAWABLE (layer)); | ||
|  | } | ||
|  | 
 | ||
|  | static void | ||
|  | pika_layer_mask_convert_type (PikaDrawable      *drawable, | ||
|  |                               PikaImage         *dest_image, | ||
|  |                               const Babl        *new_format, | ||
|  |                               PikaColorProfile  *src_profile, | ||
|  |                               PikaColorProfile  *dest_profile, | ||
|  |                               GeglDitherMethod   layer_dither_type, | ||
|  |                               GeglDitherMethod   mask_dither_type, | ||
|  |                               gboolean           push_undo, | ||
|  |                               PikaProgress      *progress) | ||
|  | { | ||
|  |   new_format = | ||
|  |     pika_babl_mask_format (pika_babl_format_get_precision (new_format)); | ||
|  | 
 | ||
|  |   PIKA_DRAWABLE_CLASS (parent_class)->convert_type (drawable, dest_image, | ||
|  |                                                     new_format, | ||
|  |                                                     src_profile, | ||
|  |                                                     dest_profile, | ||
|  |                                                     layer_dither_type, | ||
|  |                                                     mask_dither_type, | ||
|  |                                                     push_undo, | ||
|  |                                                     progress); | ||
|  | } | ||
|  | 
 | ||
|  | PikaLayerMask * | ||
|  | pika_layer_mask_new (PikaImage     *image, | ||
|  |                      gint           width, | ||
|  |                      gint           height, | ||
|  |                      const gchar   *name, | ||
|  |                      const PikaRGB *color) | ||
|  | { | ||
|  |   PikaLayerMask *layer_mask; | ||
|  | 
 | ||
|  |   g_return_val_if_fail (PIKA_IS_IMAGE (image), NULL); | ||
|  |   g_return_val_if_fail (width > 0, NULL); | ||
|  |   g_return_val_if_fail (height > 0, NULL); | ||
|  |   g_return_val_if_fail (color != NULL, NULL); | ||
|  | 
 | ||
|  |   layer_mask = | ||
|  |     PIKA_LAYER_MASK (pika_drawable_new (PIKA_TYPE_LAYER_MASK, | ||
|  |                                         image, name, | ||
|  |                                         0, 0, width, height, | ||
|  |                                         pika_image_get_mask_format (image))); | ||
|  | 
 | ||
|  |   /*  set the layer_mask color and opacity  */ | ||
|  |   pika_channel_set_color (PIKA_CHANNEL (layer_mask), color, FALSE); | ||
|  |   pika_channel_set_show_masked (PIKA_CHANNEL (layer_mask), TRUE); | ||
|  | 
 | ||
|  |   /*  selection mask variables  */ | ||
|  |   PIKA_CHANNEL (layer_mask)->x2 = width; | ||
|  |   PIKA_CHANNEL (layer_mask)->y2 = height; | ||
|  | 
 | ||
|  |   return layer_mask; | ||
|  | } | ||
|  | 
 | ||
|  | void | ||
|  | pika_layer_mask_set_layer (PikaLayerMask *layer_mask, | ||
|  |                            PikaLayer     *layer) | ||
|  | { | ||
|  |   g_return_if_fail (PIKA_IS_LAYER_MASK (layer_mask)); | ||
|  |   g_return_if_fail (layer == NULL || PIKA_IS_LAYER (layer)); | ||
|  | 
 | ||
|  |   layer_mask->layer = layer; | ||
|  | 
 | ||
|  |   if (layer) | ||
|  |     { | ||
|  |       gchar *mask_name; | ||
|  |       gint   offset_x; | ||
|  |       gint   offset_y; | ||
|  | 
 | ||
|  |       pika_item_get_offset (PIKA_ITEM (layer), &offset_x, &offset_y); | ||
|  |       pika_item_set_offset (PIKA_ITEM (layer_mask), offset_x, offset_y); | ||
|  | 
 | ||
|  |       mask_name = g_strdup_printf (_("%s mask"), pika_object_get_name (layer)); | ||
|  | 
 | ||
|  |       pika_object_take_name (PIKA_OBJECT (layer_mask), mask_name); | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | PikaLayer * | ||
|  | pika_layer_mask_get_layer (PikaLayerMask *layer_mask) | ||
|  | { | ||
|  |   g_return_val_if_fail (PIKA_IS_LAYER_MASK (layer_mask), NULL); | ||
|  | 
 | ||
|  |   return layer_mask->layer; | ||
|  | } |