gimp/libgimp/gimpdrawable.c

802 lines
20 KiB
C

/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimpdrawable.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
* Library 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
* <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#define GIMP_DISABLE_DEPRECATION_WARNINGS
#include "gimp.h"
#include "gimptilebackendplugin.h"
#define TILE_WIDTH gimp_tile_width()
#define TILE_HEIGHT gimp_tile_height()
/**
* gimp_drawable_get:
* @drawable_ID: the ID of the drawable
*
* This function creates a #GimpDrawable structure for the core
* drawable identified by @drawable_ID. The returned structure
* contains some basic information about the drawable and can also
* hold tile data for transfer to and from the core.
*
* Note that the name of this function is somewhat misleading, because
* it suggests that it simply returns a handle. This is not the case:
* if the function is called multiple times, it creates separate tile
* lists each time, which will usually produce undesired results.
*
* When a plug-in has finished working with a drawable, before exiting
* it should call gimp_drawable_detach() to make sure that all tile data is
* transferred back to the core.
*
* Return value: a new #GimpDrawable wrapper
**/
GimpDrawable *
gimp_drawable_get (gint32 drawable_ID)
{
GimpDrawable *drawable;
gint width;
gint height;
gint bpp;
width = gimp_drawable_width (drawable_ID);
height = gimp_drawable_height (drawable_ID);
bpp = gimp_drawable_bpp (drawable_ID);
g_return_val_if_fail (width > 0 && height > 0 && bpp > 0, NULL);
drawable = g_slice_new0 (GimpDrawable);
drawable->drawable_id = drawable_ID;
drawable->width = width;
drawable->height = height;
drawable->bpp = bpp;
drawable->ntile_rows = (height + TILE_HEIGHT - 1) / TILE_HEIGHT;
drawable->ntile_cols = (width + TILE_WIDTH - 1) / TILE_WIDTH;
return drawable;
}
/**
* gimp_drawable_detach:
* @drawable: The #GimpDrawable to detach from the core
*
* This function is called when a plug-in is finished working
* with a drawable. It forces all tile data held in the tile
* list of the #GimpDrawable to be transferred to the core, and
* then frees all associated memory. You must not access the
* @drawable after having called gimp_drawable_detach().
**/
void
gimp_drawable_detach (GimpDrawable *drawable)
{
g_return_if_fail (drawable != NULL);
gimp_drawable_flush (drawable);
if (drawable->tiles)
g_free (drawable->tiles);
if (drawable->shadow_tiles)
g_free (drawable->shadow_tiles);
g_slice_free (GimpDrawable, drawable);
}
/**
* gimp_drawable_flush:
* @drawable: The #GimpDrawable whose tile data is to be transferred
* to the core.
*
* This function causes all tile data in the tile list of @drawable to be
* transferred to the core. It is usually called in situations where a
* plug-in acts on a drawable, and then needs to read the results of its
* actions. Data transferred back from the core will not generally be valid
* unless gimp_drawable_flush() has been called beforehand.
**/
void
gimp_drawable_flush (GimpDrawable *drawable)
{
GimpTile *tiles;
gint n_tiles;
gint i;
g_return_if_fail (drawable != NULL);
if (drawable->tiles)
{
tiles = drawable->tiles;
n_tiles = drawable->ntile_rows * drawable->ntile_cols;
for (i = 0; i < n_tiles; i++)
if ((tiles[i].ref_count > 0) && tiles[i].dirty)
gimp_tile_flush (&tiles[i]);
}
if (drawable->shadow_tiles)
{
tiles = drawable->shadow_tiles;
n_tiles = drawable->ntile_rows * drawable->ntile_cols;
for (i = 0; i < n_tiles; i++)
if ((tiles[i].ref_count > 0) && tiles[i].dirty)
gimp_tile_flush (&tiles[i]);
}
/* nuke all references to this drawable from the cache */
_gimp_tile_cache_flush_drawable (drawable);
}
GimpTile *
gimp_drawable_get_tile (GimpDrawable *drawable,
gboolean shadow,
gint row,
gint col)
{
GimpTile *tiles;
guint right_tile;
guint bottom_tile;
gint n_tiles;
gint tile_num;
gint i, j, k;
g_return_val_if_fail (drawable != NULL, NULL);
if (shadow)
tiles = drawable->shadow_tiles;
else
tiles = drawable->tiles;
if (! tiles)
{
n_tiles = drawable->ntile_rows * drawable->ntile_cols;
tiles = g_new (GimpTile, n_tiles);
right_tile = (drawable->width -
((drawable->ntile_cols - 1) * TILE_WIDTH));
bottom_tile = (drawable->height -
((drawable->ntile_rows - 1) * TILE_HEIGHT));
for (i = 0, k = 0; i < drawable->ntile_rows; i++)
{
for (j = 0; j < drawable->ntile_cols; j++, k++)
{
tiles[k].bpp = drawable->bpp;
tiles[k].tile_num = k;
tiles[k].ref_count = 0;
tiles[k].dirty = FALSE;
tiles[k].shadow = shadow;
tiles[k].data = NULL;
tiles[k].drawable = drawable;
if (j == (drawable->ntile_cols - 1))
tiles[k].ewidth = right_tile;
else
tiles[k].ewidth = TILE_WIDTH;
if (i == (drawable->ntile_rows - 1))
tiles[k].eheight = bottom_tile;
else
tiles[k].eheight = TILE_HEIGHT;
}
}
if (shadow)
drawable->shadow_tiles = tiles;
else
drawable->tiles = tiles;
}
tile_num = row * drawable->ntile_cols + col;
return &tiles[tile_num];
}
GimpTile *
gimp_drawable_get_tile2 (GimpDrawable *drawable,
gboolean shadow,
gint x,
gint y)
{
gint row;
gint col;
g_return_val_if_fail (drawable != NULL, NULL);
col = x / TILE_WIDTH;
row = y / TILE_HEIGHT;
return gimp_drawable_get_tile (drawable, shadow, row, col);
}
void
gimp_drawable_get_color_uchar (gint32 drawable_ID,
const GimpRGB *color,
guchar *color_uchar)
{
g_return_if_fail (color != NULL);
g_return_if_fail (color_uchar != NULL);
switch (gimp_drawable_type (drawable_ID))
{
case GIMP_RGB_IMAGE:
gimp_rgb_get_uchar (color,
&color_uchar[0], &color_uchar[1], &color_uchar[2]);
color_uchar[3] = 255;
break;
case GIMP_RGBA_IMAGE:
gimp_rgba_get_uchar (color,
&color_uchar[0], &color_uchar[1], &color_uchar[2],
&color_uchar[3]);
break;
case GIMP_GRAY_IMAGE:
color_uchar[0] = gimp_rgb_luminance_uchar (color);
color_uchar[1] = 255;
break;
case GIMP_GRAYA_IMAGE:
color_uchar[0] = gimp_rgb_luminance_uchar (color);
gimp_rgba_get_uchar (color, NULL, NULL, NULL, &color_uchar[1]);
break;
default:
break;
}
}
guchar *
gimp_drawable_get_thumbnail_data (gint32 drawable_ID,
gint *width,
gint *height,
gint *bpp)
{
gint ret_width;
gint ret_height;
guchar *image_data;
gint data_size;
_gimp_drawable_thumbnail (drawable_ID,
*width,
*height,
&ret_width,
&ret_height,
bpp,
&data_size,
&image_data);
*width = ret_width;
*height = ret_height;
return image_data;
}
guchar *
gimp_drawable_get_sub_thumbnail_data (gint32 drawable_ID,
gint src_x,
gint src_y,
gint src_width,
gint src_height,
gint *dest_width,
gint *dest_height,
gint *bpp)
{
gint ret_width;
gint ret_height;
guchar *image_data;
gint data_size;
_gimp_drawable_sub_thumbnail (drawable_ID,
src_x, src_y,
src_width, src_height,
*dest_width,
*dest_height,
&ret_width,
&ret_height,
bpp,
&data_size,
&image_data);
*dest_width = ret_width;
*dest_height = ret_height;
return image_data;
}
/**
* gimp_drawable_is_valid:
* @drawable_ID: The drawable to check.
*
* Deprecated: Use gimp_item_is_valid() instead.
*
* Returns: Whether the drawable ID is valid.
*
* Since: 2.4
*/
gboolean
gimp_drawable_is_valid (gint32 drawable_ID)
{
return gimp_item_is_valid (drawable_ID);
}
/**
* gimp_drawable_is_layer:
* @drawable_ID: The drawable.
*
* Deprecated: Use gimp_item_is_layer() instead.
*
* Returns: TRUE if the drawable is a layer, FALSE otherwise.
*/
gboolean
gimp_drawable_is_layer (gint32 drawable_ID)
{
return gimp_item_is_layer (drawable_ID);
}
/**
* gimp_drawable_is_text_layer:
* @drawable_ID: The drawable.
*
* Deprecated: Use gimp_item_is_text_layer() instead.
*
* Returns: TRUE if the drawable is a text layer, FALSE otherwise.
*
* Since: 2.6
*/
gboolean
gimp_drawable_is_text_layer (gint32 drawable_ID)
{
return gimp_item_is_text_layer (drawable_ID);
}
/**
* gimp_drawable_is_layer_mask:
* @drawable_ID: The drawable.
*
* Deprecated: Use gimp_item_is_layer_mask() instead.
*
* Returns: TRUE if the drawable is a layer mask, FALSE otherwise.
*/
gboolean
gimp_drawable_is_layer_mask (gint32 drawable_ID)
{
return gimp_item_is_layer_mask (drawable_ID);
}
/**
* gimp_drawable_is_channel:
* @drawable_ID: The drawable.
*
* Deprecated: Use gimp_item_is_channel() instead.
*
* Returns: TRUE if the drawable is a channel, FALSE otherwise.
*/
gboolean
gimp_drawable_is_channel (gint32 drawable_ID)
{
return gimp_item_is_channel (drawable_ID);
}
/**
* gimp_drawable_delete:
* @drawable_ID: The drawable to delete.
*
* Deprecated: Use gimp_item_delete() instead.
*
* Returns: TRUE on success.
*/
gboolean
gimp_drawable_delete (gint32 drawable_ID)
{
return gimp_item_delete (drawable_ID);
}
/**
* gimp_drawable_get_image:
* @drawable_ID: The drawable.
*
* Deprecated: Use gimp_item_get_image() instead.
*
* Returns: The drawable's image.
*/
gint32
gimp_drawable_get_image (gint32 drawable_ID)
{
return gimp_item_get_image (drawable_ID);
}
/**
* gimp_drawable_get_name:
* @drawable_ID: The drawable.
*
* Deprecated: Use gimp_item_get_name() instead.
*
* Returns: The drawable name.
*/
gchar *
gimp_drawable_get_name (gint32 drawable_ID)
{
return gimp_item_get_name (drawable_ID);
}
/**
* gimp_drawable_set_name:
* @drawable_ID: The drawable.
* @name: The new drawable name.
*
* Deprecated: Use gimp_item_set_name() instead.
*
* Returns: TRUE on success.
*/
gboolean
gimp_drawable_set_name (gint32 drawable_ID,
const gchar *name)
{
return gimp_item_set_name (drawable_ID, name);
}
/**
* gimp_drawable_get_visible:
* @drawable_ID: The drawable.
*
* Deprecated: Use gimp_item_get_visible() instead.
*
* Returns: The drawable visibility.
*/
gboolean
gimp_drawable_get_visible (gint32 drawable_ID)
{
return gimp_item_get_visible (drawable_ID);
}
/**
* gimp_drawable_set_visible:
* @drawable_ID: The drawable.
* @visible: The new drawable visibility.
*
* Deprecated: Use gimp_item_set_visible() instead.
*
* Returns: TRUE on success.
*/
gboolean
gimp_drawable_set_visible (gint32 drawable_ID,
gboolean visible)
{
return gimp_item_set_visible (drawable_ID, visible);
}
/**
* gimp_drawable_get_linked:
* @drawable_ID: The drawable.
*
* Deprecated: Use gimp_item_get_linked() instead.
*
* Returns: The drawable linked state (for moves).
*/
gboolean
gimp_drawable_get_linked (gint32 drawable_ID)
{
return gimp_item_get_linked (drawable_ID);
}
/**
* gimp_drawable_set_linked:
* @drawable_ID: The drawable.
* @linked: The new drawable linked state.
*
* Deprecated: Use gimp_item_set_linked() instead.
*
* Returns: TRUE on success.
*/
gboolean
gimp_drawable_set_linked (gint32 drawable_ID,
gboolean linked)
{
return gimp_item_set_linked (drawable_ID, linked);
}
/**
* gimp_drawable_get_tattoo:
* @drawable_ID: The drawable.
*
* Deprecated: Use gimp_item_get_tattoo() instead.
*
* Returns: The drawable tattoo.
*/
gint
gimp_drawable_get_tattoo (gint32 drawable_ID)
{
return gimp_item_get_tattoo (drawable_ID);
}
/**
* gimp_drawable_set_tattoo:
* @drawable_ID: The drawable.
* @tattoo: The new drawable tattoo.
*
* Deprecated: Use gimp_item_set_tattoo() instead.
*
* Returns: TRUE on success.
*/
gboolean
gimp_drawable_set_tattoo (gint32 drawable_ID,
gint tattoo)
{
return gimp_item_set_tattoo (drawable_ID, tattoo);
}
/**
* gimp_drawable_parasite_find:
* @drawable_ID: The drawable.
* @name: The name of the parasite to find.
*
* Deprecated: Use gimp_item_get_parasite() instead.
*
* Returns: The found parasite.
**/
GimpParasite *
gimp_drawable_parasite_find (gint32 drawable_ID,
const gchar *name)
{
return gimp_item_get_parasite (drawable_ID, name);
}
/**
* gimp_drawable_parasite_attach:
* @drawable_ID: The drawable.
* @parasite: The parasite to attach to a drawable.
*
* Deprecated: Use gimp_item_attach_parasite() instead.
*
* Returns: TRUE on success.
**/
gboolean
gimp_drawable_parasite_attach (gint32 drawable_ID,
const GimpParasite *parasite)
{
return gimp_item_attach_parasite (drawable_ID, parasite);
}
/**
* gimp_drawable_parasite_detach:
* @drawable_ID: The drawable.
* @name: The name of the parasite to detach from a drawable.
*
* Deprecated: Use gimp_item_detach_parasite() instead.
*
* Returns: TRUE on success.
**/
gboolean
gimp_drawable_parasite_detach (gint32 drawable_ID,
const gchar *name)
{
return gimp_item_detach_parasite (drawable_ID, name);
}
/**
* gimp_drawable_parasite_list:
* @drawable_ID: The drawable.
* @num_parasites: The number of attached parasites.
* @parasites: The names of currently attached parasites.
*
* Deprecated: Use gimp_item_get_parasite_list() instead.
*
* Returns: TRUE on success.
**/
gboolean
gimp_drawable_parasite_list (gint32 drawable_ID,
gint *num_parasites,
gchar ***parasites)
{
*parasites = gimp_item_get_parasite_list (drawable_ID, num_parasites);
return *parasites != NULL;
}
/**
* gimp_drawable_attach_new_parasite:
* @drawable_ID: the ID of the #GimpDrawable to attach the #GimpParasite to.
* @name: the name of the #GimpParasite to create and attach.
* @flags: the flags set on the #GimpParasite.
* @size: the size of the parasite data in bytes.
* @data: a pointer to the data attached with the #GimpParasite.
*
* Convenience function that creates a parasite and attaches it
* to GIMP.
*
* Deprecated: use gimp_item_attach_parasite() instead.
*
* Return value: TRUE on successful creation and attachment of
* the new parasite.
*
* See Also: gimp_drawable_parasite_attach()
*/
gboolean
gimp_drawable_attach_new_parasite (gint32 drawable_ID,
const gchar *name,
gint flags,
gint size,
gconstpointer data)
{
GimpParasite *parasite = gimp_parasite_new (name, flags, size, data);
gboolean success;
success = gimp_item_attach_parasite (drawable_ID, parasite);
gimp_parasite_free (parasite);
return success;
}
/**
* gimp_drawable_get_buffer:
* @drawable_ID: the ID of the #GimpDrawable to get the buffer for.
*
* Returns a #GeglBuffer of a specified drawable. The buffer can be used
* like any other GEGL buffer. Its data will we synced back with the core
* drawable when the buffer gets destroyed, or when gegl_buffer_flush()
* is called.
*
* Return value: The #GeglBuffer.
*
* See Also: gimp_drawable_get_shadow_buffer()
*
* Since: 2.10
*/
GeglBuffer *
gimp_drawable_get_buffer (gint32 drawable_ID)
{
gimp_plugin_enable_precision ();
if (gimp_item_is_valid (drawable_ID))
{
GimpDrawable *drawable;
drawable = gimp_drawable_get (drawable_ID);
if (drawable)
{
GeglTileBackend *backend;
GeglBuffer *buffer;
backend = _gimp_tile_backend_plugin_new (drawable, FALSE);
buffer = gegl_buffer_new_for_backend (NULL, backend);
g_object_unref (backend);
return buffer;
}
}
return NULL;
}
/**
* gimp_drawable_get_shadow_buffer:
* @drawable_ID: the ID of the #GimpDrawable to get the buffer for.
*
* Returns a #GeglBuffer of a specified drawable's shadow tiles. The
* buffer can be used like any other GEGL buffer. Its data will we
* synced back with the core drawable's shadow tiles when the buffer
* gets destroyed, or when gegl_buffer_flush() is called.
*
* Return value: The #GeglBuffer.
*
* See Also: gimp_drawable_get_shadow_buffer()
*
* Since: 2.10
*/
GeglBuffer *
gimp_drawable_get_shadow_buffer (gint32 drawable_ID)
{
GimpDrawable *drawable;
gimp_plugin_enable_precision ();
drawable = gimp_drawable_get (drawable_ID);
if (drawable)
{
GeglTileBackend *backend;
GeglBuffer *buffer;
backend = _gimp_tile_backend_plugin_new (drawable, TRUE);
buffer = gegl_buffer_new_for_backend (NULL, backend);
g_object_unref (backend);
return buffer;
}
return NULL;
}
/**
* gimp_drawable_get_format:
* @drawable_ID: the ID of the #GimpDrawable to get the format for.
*
* Returns the #Babl format of the drawable.
*
* Return value: The #Babl format.
*
* Since: 2.10
*/
const Babl *
gimp_drawable_get_format (gint32 drawable_ID)
{
static GHashTable *palette_formats = NULL;
const Babl *format = NULL;
gchar *format_str = _gimp_drawable_get_format (drawable_ID);
if (format_str)
{
if (gimp_drawable_is_indexed (drawable_ID))
{
gint32 image_ID = gimp_item_get_image (drawable_ID);
guchar *colormap;
gint n_colors;
colormap = gimp_image_get_colormap (image_ID, &n_colors);
if (!palette_formats)
palette_formats = g_hash_table_new (g_str_hash, g_str_equal);
format = g_hash_table_lookup (palette_formats, format_str);
if (!format)
{
const Babl *palette;
const Babl *palette_alpha;
babl_new_palette (format_str, &palette, &palette_alpha);
g_hash_table_insert (palette_formats,
(gpointer) babl_get_name (palette),
(gpointer) palette);
g_hash_table_insert (palette_formats,
(gpointer) babl_get_name (palette_alpha),
(gpointer) palette_alpha);
if (gimp_drawable_has_alpha (drawable_ID))
format = palette_alpha;
else
format = palette;
}
if (colormap)
{
babl_palette_set_palette (format,
babl_format ("R'G'B' u8"),
colormap, n_colors);
g_free (colormap);
}
}
else
{
format = babl_format (format_str);
}
g_free (format_str);
}
return format;
}