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;
|
|
}
|