PIKApp/libpika/pikaimage.c

773 lines
20 KiB
C

/* LIBPIKA - The PIKA Library
* Copyright (C) 1995-2000 Peter Mattis and Spencer Kimball
*
* pikaimage.c
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* 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
* License along with this library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include "pika.h"
#include "libpikabase/pikawire.h" /* FIXME kill this include */
#include "pikapixbuf.h"
#include "pikaplugin-private.h"
#include "pikaprocedure-private.h"
enum
{
PROP_0,
PROP_ID,
N_PROPS
};
struct _PikaImage
{
GObject parent_instance;
gint id;
};
static void pika_image_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec);
static void pika_image_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec);
G_DEFINE_TYPE (PikaImage, pika_image, G_TYPE_OBJECT)
#define parent_class pika_image_parent_class
static GParamSpec *props[N_PROPS] = { NULL, };
static void
pika_image_class_init (PikaImageClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->set_property = pika_image_set_property;
object_class->get_property = pika_image_get_property;
props[PROP_ID] =
g_param_spec_int ("id",
"The image id",
"The image id for internal use",
0, G_MAXINT32, 0,
PIKA_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY);
g_object_class_install_properties (object_class, N_PROPS, props);
}
static void
pika_image_init (PikaImage *image)
{
}
static void
pika_image_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
PikaImage *image = PIKA_IMAGE (object);
switch (property_id)
{
case PROP_ID:
image->id = g_value_get_int (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
pika_image_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
PikaImage *image = PIKA_IMAGE (object);
switch (property_id)
{
case PROP_ID:
g_value_set_int (value, image->id);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
/* Public API */
/**
* pika_image_get_id:
* @image: The image.
*
* Returns: the image ID.
*
* Since: 3.0
**/
gint32
pika_image_get_id (PikaImage *image)
{
return image ? image->id : -1;
}
/**
* pika_image_get_by_id:
* @image_id: The image id.
*
* Returns: (nullable) (transfer none): a #PikaImage for @image_id or
* %NULL if @image_id does not represent a valid image.
* The object belongs to libpika and you must not modify
* or unref it.
*
* Since: 3.0
**/
PikaImage *
pika_image_get_by_id (gint32 image_id)
{
if (image_id > 0)
{
PikaPlugIn *plug_in = pika_get_plug_in ();
PikaProcedure *procedure = _pika_plug_in_get_procedure (plug_in);
return _pika_procedure_get_image (procedure, image_id);
}
return NULL;
}
/**
* pika_image_is_valid:
* @image: The image to check.
*
* Returns TRUE if the image is valid.
*
* This procedure checks if the given image is valid and refers to
* an existing image.
*
* Returns: Whether the image is valid.
*
* Since: 2.4
**/
gboolean
pika_image_is_valid (PikaImage *image)
{
return pika_image_id_is_valid (pika_image_get_id (image));
}
/**
* pika_list_images:
*
* Returns the list of images currently open.
*
* This procedure returns the list of images currently open in PIKA.
*
* Returns: (element-type PikaImage) (transfer container):
* The list of images currently open.
* The returned list must be freed with g_list_free(). Image
* elements belong to libpika and must not be freed.
*
* Since: 3.0
**/
GList *
pika_list_images (void)
{
PikaImage **images;
gint num_images;
GList *list = NULL;
gint i;
images = pika_get_images (&num_images);
for (i = 0; i < num_images; i++)
list = g_list_prepend (list, images[i]);
g_free (images);
return g_list_reverse (list);
}
/**
* pika_image_list_layers:
* @image: The image.
*
* Returns the list of layers contained in the specified image.
*
* This procedure returns the list of layers contained in the specified
* image. The order of layers is from topmost to bottommost.
*
* Returns: (element-type PikaLayer) (transfer container):
* The list of layers contained in the image.
* The returned list must be freed with g_list_free(). Layer
* elements belong to libpika and must not be freed.
*
* Since: 3.0
**/
GList *
pika_image_list_layers (PikaImage *image)
{
PikaLayer **layers;
gint num_layers;
GList *list = NULL;
gint i;
layers = pika_image_get_layers (image, &num_layers);
for (i = 0; i < num_layers; i++)
list = g_list_prepend (list, layers[i]);
g_free (layers);
return g_list_reverse (list);
}
/**
* pika_image_list_selected_layers:
* @image: The image.
*
* Returns the list of layers selected in the specified image.
*
* This procedure returns the list of layers selected in the specified
* image.
*
* Returns: (element-type PikaLayer) (transfer container):
* The list of selected layers in the image.
* The returned list must be freed with g_list_free(). Layer
* elements belong to libpika and must not be freed.
*
* Since: 3.0
**/
GList *
pika_image_list_selected_layers (PikaImage *image)
{
PikaLayer **layers;
gint num_layers;
GList *list = NULL;
gint i;
layers = pika_image_get_selected_layers (image, &num_layers);
for (i = 0; i < num_layers; i++)
list = g_list_prepend (list, layers[i]);
g_free (layers);
return g_list_reverse (list);
}
/**
* pika_image_take_selected_layers:
* @image: The image.
* @layers: (transfer container) (element-type PikaLayer): The list of layers to select.
*
* The layers are set as the selected layers in the image. Any previous
* selected layers or channels are unselected. An exception is a previously
* existing floating selection, in which case this procedure will return an
* execution error.
*
* Returns: TRUE on success.
*
* Since: 3.0
**/
gboolean
pika_image_take_selected_layers (PikaImage *image,
GList *layers)
{
PikaLayer **sel_layers;
GList *list;
gboolean success;
gint i;
sel_layers = g_new0 (PikaLayer *, g_list_length (layers));
for (list = layers, i = 0; list; list = list->next, i++)
sel_layers[i] = list->data;
success = pika_image_set_selected_layers (image, g_list_length (layers),
(const PikaLayer **) sel_layers);
g_list_free (layers);
g_free (sel_layers);
return success;
}
/**
* pika_image_list_selected_channels:
* @image: The image.
*
* Returns the list of channels selected in the specified image.
*
* This procedure returns the list of channels selected in the specified
* image.
*
* Returns: (element-type PikaChannel) (transfer container):
* The list of selected channels in the image.
* The returned list must be freed with g_list_free(). Layer
* elements belong to libpika and must not be freed.
*
* Since: 3.0
**/
GList *
pika_image_list_selected_channels (PikaImage *image)
{
PikaChannel **channels;
gint num_channels;
GList *list = NULL;
gint i;
channels = pika_image_get_selected_channels (image, &num_channels);
for (i = 0; i < num_channels; i++)
list = g_list_prepend (list, channels[i]);
g_free (channels);
return g_list_reverse (list);
}
/**
* pika_image_take_selected_channels:
* @image: The image.
* @channels: (transfer container) (element-type PikaChannel): The list of channels to select.
*
* The channels are set as the selected channels in the image. Any previous
* selected layers or channels are unselected. An exception is a previously
* existing floating selection, in which case this procedure will return an
* execution error.
*
* Returns: TRUE on success.
*
* Since: 3.0
**/
gboolean
pika_image_take_selected_channels (PikaImage *image,
GList *channels)
{
PikaChannel **sel_channels;
GList *list;
gboolean success;
gint i;
sel_channels = g_new0 (PikaChannel *, g_list_length (channels));
for (list = channels, i = 0; list; list = list->next, i++)
sel_channels[i] = list->data;
success = pika_image_set_selected_channels (image, g_list_length (channels),
(const PikaChannel **) sel_channels);
g_list_free (channels);
return success;
}
/**
* pika_image_list_selected_vectors:
* @image: The image.
*
* Returns the list of paths selected in the specified image.
*
* This procedure returns the list of paths selected in the specified
* image.
*
* Returns: (element-type PikaVectors) (transfer container):
* The list of selected paths in the image.
* The returned list must be freed with g_list_free().
* Path elements belong to libpika and must not be freed.
*
* Since: 3.0
**/
GList *
pika_image_list_selected_vectors (PikaImage *image)
{
PikaVectors **vectors;
gint num_vectors;
GList *list = NULL;
gint i;
vectors = pika_image_get_selected_vectors (image, &num_vectors);
for (i = 0; i < num_vectors; i++)
list = g_list_prepend (list, vectors[i]);
g_free (vectors);
return g_list_reverse (list);
}
/**
* pika_image_take_selected_vectors:
* @image: The image.
* @vectors: (transfer container) (element-type PikaVectors): The list of paths to select.
*
* The paths are set as the selected paths in the image. Any previous
* selected paths are unselected.
*
* Returns: TRUE on success.
*
* Since: 3.0
**/
gboolean
pika_image_take_selected_vectors (PikaImage *image,
GList *vectors)
{
PikaVectors **sel_vectors;
GList *list;
gboolean success;
gint i;
sel_vectors = g_new0 (PikaVectors *, g_list_length (vectors));
for (list = vectors, i = 0; list; list = list->next, i++)
sel_vectors[i] = list->data;
success = pika_image_set_selected_vectors (image, g_list_length (vectors),
(const PikaVectors **) sel_vectors);
g_list_free (vectors);
return success;
}
/**
* pika_image_list_channels:
* @image: The image.
*
* Returns the list of channels contained in the specified image.
*
* This procedure returns the list of channels contained in the
* specified image. This does not include the selection mask, or layer
* masks. The order is from topmost to bottommost. Note that
* "channels" are custom channels and do not include the image's
* color components.
*
* Returns: (element-type PikaChannel) (transfer container):
* The list of channels contained in the image.
* The returned list must be freed with g_list_free(). Channel
* elements belong to libpika and must not be freed.
*
* Since: 3.0
**/
GList *
pika_image_list_channels (PikaImage *image)
{
PikaChannel **channels;
gint num_channels;
GList *list = NULL;
gint i;
channels = pika_image_get_channels (image, &num_channels);
for (i = 0; i < num_channels; i++)
list = g_list_prepend (list, channels[i]);
g_free (channels);
return g_list_reverse (list);
}
/**
* pika_image_list_vectors:
* @image: The image.
*
* Returns the list of vectors contained in the specified image.
*
* This procedure returns the list of vectors contained in the
* specified image.
*
* Returns: (element-type PikaVectors) (transfer container):
* The list of vectors contained in the image.
* The returned value must be freed with g_list_free(). Vectors
* elements belong to libpika and must not be freed.
*
* Since: 3.0
**/
GList *
pika_image_list_vectors (PikaImage *image)
{
PikaVectors **vectors;
gint num_vectors;
GList *list = NULL;
gint i;
vectors = pika_image_get_vectors (image, &num_vectors);
for (i = 0; i < num_vectors; i++)
list = g_list_prepend (list, vectors[i]);
g_free (vectors);
return g_list_reverse (list);
}
/**
* pika_image_list_selected_drawables:
* @image: The image.
*
* Returns the list of drawables selected in the specified image.
*
* This procedure returns the list of drawables selected in the specified
* image.
* These can be either a list of layers or a list of channels (a list mixing
* layers and channels is not possible), or it can be a layer mask (a list
* containing only a layer mask as single item), if a layer mask is in edit
* mode.
*
* Returns: (element-type PikaItem) (transfer container):
* The list of selected drawables in the image.
* The returned list must be freed with g_list_free(). Layer
* elements belong to libpika and must not be freed.
*
* Since: 3.0
**/
GList *
pika_image_list_selected_drawables (PikaImage *image)
{
PikaItem **drawables;
gint num_drawables;
GList *list = NULL;
gint i;
drawables = pika_image_get_selected_drawables (image, &num_drawables);
for (i = 0; i < num_drawables; i++)
list = g_list_prepend (list, drawables[i]);
g_free (drawables);
return g_list_reverse (list);
}
/**
* pika_image_get_colormap:
* @image: The image.
* @colormap_len: (out) (optional): The size (in bytes) of the returned colormap
* @num_colors: (out) (optional): Returns the number of colors in the colormap array.
*
* Returns the image's colormap
*
* This procedure returns an actual pointer to the image's colormap, as
* well as the number of colors contained in the colormap. If the image
* is not of base type INDEXED, this pointer will be NULL.
*
* Returns: (array length=colormap_len): The image's colormap.
*/
guchar *
pika_image_get_colormap (PikaImage *image,
gint *colormap_len,
gint *num_colors)
{
GBytes *bytes;
gsize num_bytes;
guchar *cmap;
bytes = _pika_image_get_colormap (image);
cmap = g_bytes_unref_to_data (bytes, &num_bytes);
if (colormap_len)
*colormap_len = num_bytes;
if (num_colors)
*num_colors = num_bytes / 3;
return cmap;
}
/**
* pika_image_set_colormap:
* @image: The image.
* @colormap: (array): The new colormap values.
* @num_colors: Number of colors in the colormap array.
*
* Sets the entries in the image's colormap.
*
* This procedure sets the entries in the specified image's colormap.
* The number of colors is specified by the "num_colors" parameter
* and corresponds to the number of INT8 triples that must be contained
* in the "cmap" array.
*
* Returns: TRUE on success.
*/
gboolean
pika_image_set_colormap (PikaImage *image,
const guchar *colormap,
gint num_colors)
{
GBytes *bytes;
gboolean ret;
bytes = g_bytes_new_static (colormap, num_colors * 3);
ret = _pika_image_set_colormap (image, bytes);
g_bytes_unref (bytes);
return ret;
}
/**
* pika_image_get_thumbnail_data:
* @image: The image.
* @width: (inout): The requested thumbnail width.
* @height: (inout): The requested thumbnail height.
* @bpp: (out): The previews bpp.
*
* Get a thumbnail of an image.
*
* This function gets data from which a thumbnail of an image preview
* can be created. Maximum x or y dimension is 1024 pixels. The pixels
* are returned in RGB[A] or GRAY[A] format. The bpp return value
* gives the number of bytes per pixel in the image.
*
* Returns: (array) (transfer full): the thumbnail data.
**/
guchar *
pika_image_get_thumbnail_data (PikaImage *image,
gint *width,
gint *height,
gint *bpp)
{
gint ret_width;
gint ret_height;
GBytes *image_bytes;
guchar *image_data;
gsize data_size;
_pika_image_thumbnail (image,
*width,
*height,
&ret_width,
&ret_height,
bpp,
&image_bytes);
image_data = g_bytes_unref_to_data (image_bytes, &data_size);
*width = ret_width;
*height = ret_height;
return image_data;
}
/**
* pika_image_get_thumbnail:
* @image: the #PikaImage
* @width: the requested thumbnail width (<= 1024 pixels)
* @height: the requested thumbnail height (<= 1024 pixels)
* @alpha: how to handle an alpha channel
*
* Retrieves a thumbnail pixbuf for @image.
* The thumbnail will be not larger than the requested size.
*
* Returns: (transfer full): a new #GdkPixbuf
*
* Since: 2.2
**/
GdkPixbuf *
pika_image_get_thumbnail (PikaImage *image,
gint width,
gint height,
PikaPixbufTransparency alpha)
{
gint thumb_width = width;
gint thumb_height = height;
gint thumb_bpp;
guchar *data;
g_return_val_if_fail (width > 0 && width <= 1024, NULL);
g_return_val_if_fail (height > 0 && height <= 1024, NULL);
data = pika_image_get_thumbnail_data (image,
&thumb_width,
&thumb_height,
&thumb_bpp);
if (data)
return _pika_pixbuf_from_data (data,
thumb_width, thumb_height, thumb_bpp,
alpha);
else
return NULL;
}
/**
* pika_image_get_metadata:
* @image: The image.
*
* Returns the image's metadata.
*
* Returns exif/iptc/xmp metadata from the image.
*
* Returns: (nullable) (transfer full): The exif/ptc/xmp metadata,
* or %NULL if there is none.
*
* Since: 2.10
**/
PikaMetadata *
pika_image_get_metadata (PikaImage *image)
{
PikaMetadata *metadata = NULL;
gchar *metadata_string;
metadata_string = _pika_image_get_metadata (image);
if (metadata_string)
{
metadata = pika_metadata_deserialize (metadata_string);
g_free (metadata_string);
}
return metadata;
}
/**
* pika_image_set_metadata:
* @image: The image.
* @metadata: The exif/ptc/xmp metadata.
*
* Set the image's metadata.
*
* Sets exif/iptc/xmp metadata on the image, or deletes it if
* @metadata is %NULL.
*
* Returns: TRUE on success.
*
* Since: 2.10
**/
gboolean
pika_image_set_metadata (PikaImage *image,
PikaMetadata *metadata)
{
gchar *metadata_string = NULL;
gboolean success;
if (metadata)
metadata_string = pika_metadata_serialize (metadata);
success = _pika_image_set_metadata (image, metadata_string);
if (metadata_string)
g_free (metadata_string);
return success;
}