2000-06-01 22:59:22 +08:00
|
|
|
/* LIBGIMP - The GIMP Library
|
|
|
|
* Copyright (C) 1995-2000 Peter Mattis and Spencer Kimball
|
|
|
|
*
|
|
|
|
* gimplayer.c
|
|
|
|
*
|
2009-01-18 06:28:01 +08:00
|
|
|
* This library is free software: you can redistribute it and/or
|
2000-06-01 22:59:22 +08:00
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
2009-01-18 06:28:01 +08:00
|
|
|
* version 3 of the License, or (at your option) any later version.
|
2000-06-01 22:59:22 +08:00
|
|
|
*
|
|
|
|
* This library 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
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
2009-01-18 06:28:01 +08:00
|
|
|
* License along with this library. If not, see
|
2018-07-12 05:27:07 +08:00
|
|
|
* <https://www.gnu.org/licenses/>.
|
2000-06-01 22:59:22 +08:00
|
|
|
*/
|
|
|
|
|
2002-05-14 07:30:23 +08:00
|
|
|
#include "config.h"
|
|
|
|
|
2011-04-21 02:04:35 +08:00
|
|
|
#include <string.h>
|
|
|
|
|
2000-06-01 22:59:22 +08:00
|
|
|
#include "gimp.h"
|
2002-05-14 07:30:23 +08:00
|
|
|
|
2011-04-21 05:58:00 +08:00
|
|
|
|
2022-09-30 22:21:47 +08:00
|
|
|
static GimpLayer * gimp_layer_real_copy (GimpLayer *layer);
|
|
|
|
|
2019-08-27 22:47:17 +08:00
|
|
|
|
2021-04-06 18:39:52 +08:00
|
|
|
G_DEFINE_TYPE (GimpLayer, gimp_layer, GIMP_TYPE_DRAWABLE)
|
2019-08-13 19:59:33 +08:00
|
|
|
|
|
|
|
#define parent_class gimp_layer_parent_class
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
gimp_layer_class_init (GimpLayerClass *klass)
|
|
|
|
{
|
2022-09-30 22:21:47 +08:00
|
|
|
klass->copy = gimp_layer_real_copy;
|
2019-08-13 19:59:33 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gimp_layer_init (GimpLayer *layer)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Public API. */
|
|
|
|
|
2019-09-03 16:24:24 +08:00
|
|
|
/**
|
|
|
|
* gimp_layer_get_by_id:
|
|
|
|
* @layer_id: The layer id.
|
|
|
|
*
|
|
|
|
* Returns a #GimpLayer representing @layer_id. This function calls
|
|
|
|
* gimp_item_get_by_id() and returns the item if it is layer or %NULL
|
|
|
|
* otherwise.
|
|
|
|
*
|
|
|
|
* Returns: (nullable) (transfer none): a #GimpLayer for @layer_id or
|
|
|
|
* %NULL if @layer_id does not represent a valid layer. The
|
|
|
|
* object belongs to libgimp and you must not modify or unref
|
|
|
|
* it.
|
|
|
|
*
|
|
|
|
* Since: 3.0
|
|
|
|
**/
|
|
|
|
GimpLayer *
|
|
|
|
gimp_layer_get_by_id (gint32 layer_id)
|
|
|
|
{
|
|
|
|
GimpItem *item = gimp_item_get_by_id (layer_id);
|
|
|
|
|
|
|
|
if (GIMP_IS_LAYER (item))
|
|
|
|
return (GimpLayer *) item;
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2000-08-25 08:37:35 +08:00
|
|
|
/**
|
|
|
|
* gimp_layer_new:
|
2024-02-27 05:41:54 +08:00
|
|
|
* @image: The image to which to add the layer.
|
|
|
|
* @name: (nullable): The layer name.
|
|
|
|
* @width: The layer width.
|
|
|
|
* @height: The layer height.
|
|
|
|
* @type: The layer type.
|
|
|
|
* @opacity: The layer opacity.
|
|
|
|
* @mode: The layer combination mode.
|
2000-08-25 08:37:35 +08:00
|
|
|
*
|
|
|
|
* Create a new layer.
|
|
|
|
*
|
2024-02-27 05:41:54 +08:00
|
|
|
* This procedure creates a new layer with the specified @width, @height, and
|
|
|
|
* @type. If @name is %NULL, a default layer name will be used.
|
|
|
|
* @opacity and @mode are also supplied parameters.
|
|
|
|
*
|
|
|
|
* The new layer still needs to be added to the image, as this is not automatic.
|
|
|
|
* Add the new layer with the [method@Image.insert_layer] method.
|
|
|
|
*
|
|
|
|
* Other attributes such as layer mask modes, and offsets should be set with
|
|
|
|
* explicit procedure calls.
|
2000-08-25 08:37:35 +08:00
|
|
|
*
|
2019-08-15 18:12:25 +08:00
|
|
|
* Returns: (transfer none): The newly created layer.
|
|
|
|
* The object belongs to libgimp and you should not free it.
|
|
|
|
*
|
|
|
|
* Since: 3.0
|
2000-08-25 08:37:35 +08:00
|
|
|
*/
|
2019-08-13 22:16:08 +08:00
|
|
|
GimpLayer *
|
2019-08-11 23:12:20 +08:00
|
|
|
gimp_layer_new (GimpImage *image,
|
2017-01-09 06:00:19 +08:00
|
|
|
const gchar *name,
|
|
|
|
gint width,
|
|
|
|
gint height,
|
|
|
|
GimpImageType type,
|
|
|
|
gdouble opacity,
|
|
|
|
GimpLayerMode mode)
|
2000-06-01 22:59:22 +08:00
|
|
|
{
|
2019-08-11 23:12:20 +08:00
|
|
|
return _gimp_layer_new (image,
|
2006-04-12 18:53:28 +08:00
|
|
|
width,
|
|
|
|
height,
|
|
|
|
type,
|
|
|
|
name,
|
|
|
|
opacity,
|
|
|
|
mode);
|
2000-06-01 22:59:22 +08:00
|
|
|
}
|
|
|
|
|
2000-08-25 08:37:35 +08:00
|
|
|
/**
|
|
|
|
* gimp_layer_copy:
|
2019-08-13 22:16:08 +08:00
|
|
|
* @layer: The layer to copy.
|
2000-08-25 08:37:35 +08:00
|
|
|
*
|
|
|
|
* Copy a layer.
|
|
|
|
*
|
|
|
|
* This procedure copies the specified layer and returns the copy. The
|
|
|
|
* newly copied layer is for use within the original layer's image. It
|
2007-10-16 14:02:18 +08:00
|
|
|
* should not be subsequently added to any other image.
|
2000-08-25 08:37:35 +08:00
|
|
|
*
|
2019-08-15 18:12:25 +08:00
|
|
|
* Returns: (transfer none): The newly copied layer.
|
|
|
|
* The object belongs to libgimp and you should not free it.
|
|
|
|
*
|
|
|
|
* Since: 3.0
|
2000-08-25 08:37:35 +08:00
|
|
|
*/
|
2019-08-13 22:16:08 +08:00
|
|
|
GimpLayer *
|
|
|
|
gimp_layer_copy (GimpLayer *layer)
|
2000-06-01 22:59:22 +08:00
|
|
|
{
|
2022-09-30 22:21:47 +08:00
|
|
|
return GIMP_LAYER_GET_CLASS (layer)->copy (layer);
|
2000-06-01 22:59:22 +08:00
|
|
|
}
|
2005-07-11 05:17:22 +08:00
|
|
|
|
2011-04-21 02:04:35 +08:00
|
|
|
/**
|
|
|
|
* gimp_layer_new_from_pixbuf:
|
2019-08-11 23:12:20 +08:00
|
|
|
* @image: The RGB image to which to add the layer.
|
2011-04-21 02:04:35 +08:00
|
|
|
* @name: The layer name.
|
|
|
|
* @pixbuf: A GdkPixbuf.
|
|
|
|
* @opacity: The layer opacity.
|
|
|
|
* @mode: The layer combination mode.
|
|
|
|
* @progress_start: start of progress
|
|
|
|
* @progress_end: end of progress
|
|
|
|
*
|
|
|
|
* Create a new layer from a %GdkPixbuf.
|
|
|
|
*
|
|
|
|
* This procedure creates a new layer from the given %GdkPixbuf. The
|
|
|
|
* image has to be an RGB image and just like with gimp_layer_new()
|
|
|
|
* you will still need to add the layer to it.
|
|
|
|
*
|
|
|
|
* If you pass @progress_end > @progress_start to this function,
|
|
|
|
* gimp_progress_update() will be called for. You have to call
|
|
|
|
* gimp_progress_init() beforehand then.
|
|
|
|
*
|
2019-08-15 18:12:25 +08:00
|
|
|
* Returns: (transfer none): The newly created layer.
|
|
|
|
* The object belongs to libgimp and you should not free it.
|
2011-04-21 02:04:35 +08:00
|
|
|
*
|
2019-08-31 00:44:56 +08:00
|
|
|
* Since: 2.2
|
2011-04-21 02:04:35 +08:00
|
|
|
*/
|
2019-08-13 22:16:08 +08:00
|
|
|
GimpLayer *
|
2019-08-11 23:12:20 +08:00
|
|
|
gimp_layer_new_from_pixbuf (GimpImage *image,
|
2017-01-09 06:00:19 +08:00
|
|
|
const gchar *name,
|
|
|
|
GdkPixbuf *pixbuf,
|
|
|
|
gdouble opacity,
|
|
|
|
GimpLayerMode mode,
|
|
|
|
gdouble progress_start,
|
|
|
|
gdouble progress_end)
|
2011-04-21 02:04:35 +08:00
|
|
|
{
|
2019-07-20 01:08:31 +08:00
|
|
|
GeglBuffer *dest_buffer;
|
2019-08-13 22:16:08 +08:00
|
|
|
GimpLayer *layer;
|
2019-07-20 01:08:31 +08:00
|
|
|
gint width;
|
|
|
|
gint height;
|
|
|
|
gint bpp;
|
|
|
|
gdouble range = progress_end - progress_start;
|
2011-04-21 02:04:35 +08:00
|
|
|
|
2019-08-13 22:16:08 +08:00
|
|
|
g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), NULL);
|
2011-04-21 02:04:35 +08:00
|
|
|
|
2021-04-06 06:47:07 +08:00
|
|
|
if (gimp_image_get_base_type (image) != GIMP_RGB)
|
2011-04-21 02:04:35 +08:00
|
|
|
{
|
|
|
|
g_warning ("gimp_layer_new_from_pixbuf() needs an RGB image");
|
2019-08-13 22:16:08 +08:00
|
|
|
return NULL;
|
2011-04-21 02:04:35 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (gdk_pixbuf_get_colorspace (pixbuf) != GDK_COLORSPACE_RGB)
|
|
|
|
{
|
|
|
|
g_warning ("gimp_layer_new_from_pixbuf() assumes that GdkPixbuf is RGB");
|
2019-08-13 22:16:08 +08:00
|
|
|
return NULL;
|
2011-04-21 02:04:35 +08:00
|
|
|
}
|
|
|
|
|
2019-07-20 01:08:31 +08:00
|
|
|
width = gdk_pixbuf_get_width (pixbuf);
|
|
|
|
height = gdk_pixbuf_get_height (pixbuf);
|
|
|
|
bpp = gdk_pixbuf_get_n_channels (pixbuf);
|
2011-04-21 02:04:35 +08:00
|
|
|
|
2019-08-11 23:12:20 +08:00
|
|
|
layer = gimp_layer_new (image, name, width, height,
|
2011-04-21 02:04:35 +08:00
|
|
|
bpp == 3 ? GIMP_RGB_IMAGE : GIMP_RGBA_IMAGE,
|
|
|
|
opacity, mode);
|
|
|
|
|
2019-08-13 22:16:08 +08:00
|
|
|
if (! layer)
|
|
|
|
return NULL;
|
2011-04-21 02:04:35 +08:00
|
|
|
|
2019-08-13 22:16:08 +08:00
|
|
|
dest_buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (layer));
|
2012-05-03 09:48:49 +08:00
|
|
|
|
2019-07-20 01:08:31 +08:00
|
|
|
gegl_buffer_set (dest_buffer, GEGL_RECTANGLE (0, 0, width, height), 0,
|
|
|
|
gimp_pixbuf_get_format (pixbuf),
|
|
|
|
gdk_pixbuf_get_pixels (pixbuf),
|
|
|
|
gdk_pixbuf_get_rowstride (pixbuf));
|
2011-04-21 02:04:35 +08:00
|
|
|
|
2019-07-20 01:08:31 +08:00
|
|
|
g_object_unref (dest_buffer);
|
2011-04-21 05:58:00 +08:00
|
|
|
|
|
|
|
if (range > 0.0)
|
|
|
|
gimp_progress_update (progress_end);
|
|
|
|
|
|
|
|
return layer;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* gimp_layer_new_from_surface:
|
2019-08-11 23:12:20 +08:00
|
|
|
* @image: The RGB image to which to add the layer.
|
2011-04-21 05:58:00 +08:00
|
|
|
* @name: The layer name.
|
2011-04-27 23:05:56 +08:00
|
|
|
* @surface: A Cairo image surface.
|
2011-04-21 05:58:00 +08:00
|
|
|
* @progress_start: start of progress
|
|
|
|
* @progress_end: end of progress
|
|
|
|
*
|
2023-07-29 18:36:21 +08:00
|
|
|
* Create a new layer from a [type@cairo.Surface].
|
2011-04-21 05:58:00 +08:00
|
|
|
*
|
|
|
|
* This procedure creates a new layer from the given
|
2023-07-29 18:36:21 +08:00
|
|
|
* [type@cairo.Surface]. The image has to be an RGB image and just like
|
2011-04-21 05:58:00 +08:00
|
|
|
* with gimp_layer_new() you will still need to add the layer to it.
|
|
|
|
*
|
|
|
|
* If you pass @progress_end > @progress_start to this function,
|
|
|
|
* gimp_progress_update() will be called for. You have to call
|
|
|
|
* gimp_progress_init() beforehand then.
|
|
|
|
*
|
2019-08-15 18:12:25 +08:00
|
|
|
* Returns: (transfer none): The newly created layer.
|
|
|
|
* The object belongs to libgimp and you should not free it.
|
2011-04-21 05:58:00 +08:00
|
|
|
*
|
2019-08-31 00:44:56 +08:00
|
|
|
* Since: 2.8
|
2011-04-21 05:58:00 +08:00
|
|
|
*/
|
2019-08-13 22:16:08 +08:00
|
|
|
GimpLayer *
|
2019-08-31 00:44:56 +08:00
|
|
|
gimp_layer_new_from_surface (GimpImage *image,
|
|
|
|
const gchar *name,
|
|
|
|
cairo_surface_t *surface,
|
|
|
|
gdouble progress_start,
|
|
|
|
gdouble progress_end)
|
2011-04-21 05:58:00 +08:00
|
|
|
{
|
2019-07-20 01:08:31 +08:00
|
|
|
GeglBuffer *src_buffer;
|
|
|
|
GeglBuffer *dest_buffer;
|
2019-08-13 22:16:08 +08:00
|
|
|
GimpLayer *layer;
|
2012-05-03 10:13:39 +08:00
|
|
|
gint width;
|
|
|
|
gint height;
|
|
|
|
cairo_format_t format;
|
|
|
|
gdouble range = progress_end - progress_start;
|
2011-04-21 05:58:00 +08:00
|
|
|
|
2019-08-13 22:16:08 +08:00
|
|
|
g_return_val_if_fail (surface != NULL, NULL);
|
2011-04-21 05:58:00 +08:00
|
|
|
g_return_val_if_fail (cairo_surface_get_type (surface) ==
|
2019-08-13 22:16:08 +08:00
|
|
|
CAIRO_SURFACE_TYPE_IMAGE, NULL);
|
2011-04-21 05:58:00 +08:00
|
|
|
|
2021-04-06 06:47:07 +08:00
|
|
|
if (gimp_image_get_base_type (image) != GIMP_RGB)
|
2011-04-21 05:58:00 +08:00
|
|
|
{
|
|
|
|
g_warning ("gimp_layer_new_from_surface() needs an RGB image");
|
2019-08-13 22:16:08 +08:00
|
|
|
return NULL;
|
2011-04-21 05:58:00 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
width = cairo_image_surface_get_width (surface);
|
|
|
|
height = cairo_image_surface_get_height (surface);
|
|
|
|
format = cairo_image_surface_get_format (surface);
|
|
|
|
|
|
|
|
if (format != CAIRO_FORMAT_ARGB32 &&
|
|
|
|
format != CAIRO_FORMAT_RGB24)
|
|
|
|
{
|
|
|
|
g_warning ("gimp_layer_new_from_surface() assumes that surface is RGB");
|
2019-08-13 22:16:08 +08:00
|
|
|
return NULL;
|
2011-04-21 05:58:00 +08:00
|
|
|
}
|
|
|
|
|
2019-08-11 23:12:20 +08:00
|
|
|
layer = gimp_layer_new (image, name, width, height,
|
2011-04-21 05:58:00 +08:00
|
|
|
format == CAIRO_FORMAT_RGB24 ?
|
|
|
|
GIMP_RGB_IMAGE : GIMP_RGBA_IMAGE,
|
2018-04-24 19:55:50 +08:00
|
|
|
100.0,
|
2019-08-11 23:12:20 +08:00
|
|
|
gimp_image_get_default_new_layer_mode (image));
|
2011-04-21 05:58:00 +08:00
|
|
|
|
2019-08-13 22:16:08 +08:00
|
|
|
if (layer == NULL)
|
|
|
|
return NULL;
|
2011-04-21 05:58:00 +08:00
|
|
|
|
app, libgimp, pdb, plug-ins: GimpText* using GeglColor.
One of the big improvement in this commit is that text layers are now much
better at space accuracy. They were already space-aware, yet rendered as sRGB u8
only before being converted to the image's space. It means that text layers had
the following limitations:
* Any color out of sRGB gamut were trimmed.
* Precision was always 8-bit (even if the image was high-bit depth).
Now GimpTextLayout keeps track of its source space (for RGB and CMYK only, this
won't be as easy when we will support more backend, since Cairo has only RGB
support for image data) and the image TRC (in case it bypasses the color space's
TRB) and it draws within this gamut and space.
It means first that we are not limited to sRGB colors; we will draw text main
color in the full image gamut, with still 2 remaining limitations:
* Unbounded colors are impossible because Pango format (to color text) uses
hexadecimal (so even with half/float images, you can't draw out-of-gamut text
unfortunately).
* Main color precision is still 8-bit, yet a tiny bit better than before as we
at least follow TRC (so we avoid some of the precision loss when converting,
even though the bit-depth is still the biggest loss).
The outline color on the other hand is drawn through Cairo API entirely, in
float. This means that the outline color will now be without any precision loss.
Note that this depends on CAIRO_FORMAT_RGBA128F which is only available since
Cairo 1.17.2 which is not in Debian bookworm (our current baseline for GIMP
3.0). It means that the old precision will still happen with older Cairo
version, as determined by #if code at compilation.
2023-11-18 05:36:31 +08:00
|
|
|
src_buffer = gimp_cairo_surface_create_buffer (surface, NULL);
|
2019-08-13 22:16:08 +08:00
|
|
|
dest_buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (layer));
|
2011-04-21 05:58:00 +08:00
|
|
|
|
2019-07-20 01:08:31 +08:00
|
|
|
gegl_buffer_copy (src_buffer, NULL, GEGL_ABYSS_NONE,
|
|
|
|
dest_buffer, NULL);
|
2012-05-03 10:13:39 +08:00
|
|
|
|
2019-07-20 01:08:31 +08:00
|
|
|
g_object_unref (src_buffer);
|
|
|
|
g_object_unref (dest_buffer);
|
2011-04-21 02:04:35 +08:00
|
|
|
|
|
|
|
if (range > 0.0)
|
|
|
|
gimp_progress_update (progress_end);
|
|
|
|
|
|
|
|
return layer;
|
|
|
|
}
|
2022-09-30 22:21:47 +08:00
|
|
|
|
|
|
|
|
|
|
|
/* private functions */
|
|
|
|
|
|
|
|
static GimpLayer *
|
|
|
|
gimp_layer_real_copy (GimpLayer *layer)
|
|
|
|
{
|
|
|
|
return _gimp_layer_copy (layer, FALSE);
|
|
|
|
}
|