Updated with upstream update

This commit is contained in:
Cassowary 2023-10-30 15:55:30 -07:00
parent 098531073c
commit 3bbdd873ef
584 changed files with 91827 additions and 70362 deletions

View File

@ -18,7 +18,8 @@
# - PIKA_CI_CROSSROAD_WIN64: trigger the crossroad/meson build for Win 64-bit.
# - PIKA_CI_MSYS2_WIN32: trigger the native MSYS2 build for Win 32-bit.
# - PIKA_CI_MSYS2_WIN64: trigger the native MSYS2 build for Win 64-bit.
# - PIKA_CI_WIN_INSTALLER: trigger both native MSYS2 builds then creates Windows installer.
# - PIKA_CI_MSYS2_WIN_AARCH64: trigger the native MSYS2 build for Windows/Aarch64.
# - PIKA_CI_WIN_INSTALLER: trigger all native MSYS2 builds then creates Windows installer.
# - PIKA_CI_SOURCES: trigger the meson/gcc build and the source tarball job.
# - PIKA_CI_CPPCHECK: trigger cppcheck static analysis.
# - PIKA_CI_FLATPAK: trigger the nightly flatpak build and publishing.
@ -375,7 +376,7 @@ pika-meson-raster-icons:
- ninja -C _build
- ninja -C _build test
## WINDOWS 64-bit CI (native MSYS2) ##
## WINDOWS x86_64 CI (native MSYS2) ##
deps-win64-native:
rules:
@ -462,7 +463,7 @@ packaging-win64-native:
- done-dll.list
needs: ["pika-win64-native"]
## WINDOWS 32-bit CI (native MSYS2) ##
## WINDOWS x86 CI (native MSYS2) ##
deps-win32-native:
rules:
@ -548,6 +549,96 @@ packaging-win32-native:
- done-dll.list
needs: ["pika-win32-native"]
## WINDOWS Aarch64 CI (native MSYS2) ##
deps-win-aarch64-native:
rules:
# On releases.
- if: '$CI_COMMIT_TAG != null'
# Custom builds though web GUI, API or schedules.
- if: '$PIKA_CI_MSYS2_WIN_AARCH64 != null'
- if: '$PIKA_CI_WIN_INSTALLER != null'
# Merge requests with appropriate label.
- if: '$CI_MERGE_REQUEST_LABELS =~ /.*5. Windows Installer.*/'
stage: dependencies
variables:
MSYSTEM: "CLANGARM64"
CHERE_INVOKING: "yes"
tags:
- windows-aarch64
script:
- C:\msys64\usr\bin\pacman --noconfirm -Syyuu
- C:\msys64\usr\bin\bash -lc "bash -x ./build/windows/gitlab-ci/build-deps-msys2.sh"
artifacts:
name: "${CI_JOB_NAME}-${CI_COMMIT_REF_SLUG}"
when: always
expire_in: 2 hours
paths:
- _install-arm64
needs: []
pika-win-aarch64-native:
rules:
# On releases.
- if: '$CI_COMMIT_TAG != null'
# Custom builds though web GUI, API or schedules.
- if: '$PIKA_CI_MSYS2_WIN_AARCH64 != null'
- if: '$PIKA_CI_WIN_INSTALLER != null'
# Merge requests with appropriate label.
- if: '$CI_MERGE_REQUEST_LABELS =~ /.*5. Windows Installer.*/'
stage: pika
variables:
MSYSTEM: "CLANGARM64"
CHERE_INVOKING: "yes"
tags:
- windows-aarch64
script:
# Temporary patch until we use the latest LLVM.
- git apply ./build/windows/patches/0001-clang-rc-files-fix.patch
- git apply ./build/windows/patches/0004-clang-windres.patch
- C:\msys64\usr\bin\pacman --noconfirm -Syyuu
- C:\msys64\usr\bin\bash -lc "bash -x ./build/windows/gitlab-ci/build-pika-msys2.sh"
artifacts:
name: "${CI_JOB_NAME}-${CI_COMMIT_REF_SLUG}"
when: always
expire_in: 1 day
paths:
- _install-arm64
- _build-arm64/build/windows/installer/
- _build-arm64/meson-*/
cache:
paths:
- _ccache/
needs: ["deps-win-aarch64-native"]
packaging-win-aarch64-native:
rules:
# On releases.
- if: '$CI_COMMIT_TAG != null'
# Custom builds though web GUI, API or schedules.
- if: '$PIKA_CI_WIN_INSTALLER != null'
# Merge requests with appropriate label.
- if: '$CI_MERGE_REQUEST_LABELS =~ /.*5. Windows Installer.*/'
stage: packaging
variables:
MSYSTEM: "CLANGARM64"
CHERE_INVOKING: "yes"
tags:
- windows-aarch64
script:
- C:\msys64\usr\bin\pacman --noconfirm -Syyuu
- C:\msys64\usr\bin\bash -lc "bash -x ./build/windows/gitlab-ci/package-pika-msys2.sh"
- cd pika-arm64
- C:\msys64\usr\bin\bash -lc "bash -x ../build/windows/gitlab-ci/split-debug-msys2.sh"
artifacts:
name: "${CI_JOB_NAME}-${CI_COMMIT_REF_SLUG}"
when: always
expire_in: 1 day
paths:
- pika-arm64
- done-dll.list
needs: ["pika-win-aarch64-native"]
## WINDOWS 64-bit CI (cross-build crossroad) ##
deps-win64:
@ -822,6 +913,7 @@ win-installer-nightly:
dependencies:
- packaging-win64-native
- packaging-win32-native
- packaging-win-aarch64-native
# This is needed for the BMP image generation for the installer.
# See commit e1203e9f76f.
- pika-meson-debian
@ -835,7 +927,7 @@ win-installer-nightly:
script:
- C:\msys64\usr\bin\pacman --noconfirm -Syyuu
- C:\msys64\usr\bin\bash -lc "bash -x ./build/windows/gitlab-ci/installer-pika-msys2.sh > installer.log 2>&1"
needs: ["packaging-win32-native", "packaging-win64-native", "pika-meson-debian"]
needs: ["packaging-win32-native", "packaging-win64-native", "packaging-win-aarch64-native", "pika-meson-debian"]
sources-meson:
rules:

View File

@ -34,6 +34,7 @@
#include "core/pikaimage-colormap.h"
#include "widgets/pikaactiongroup.h"
#include "widgets/pikacolormapeditor.h"
#include "widgets/pikahelp-ids.h"
#include "actions.h"
@ -49,6 +50,12 @@ static const PikaActionEntry colormap_actions[] =
NC_("colormap-action", "_Edit Color..."), NULL, { NULL },
NC_("colormap-action", "Edit this color"),
colormap_edit_color_cmd_callback,
PIKA_HELP_INDEXED_PALETTE_EDIT },
{ "colormap-delete-color", PIKA_ICON_EDIT_DELETE,
NC_("colormap-action", "_Delete Color..."), NULL, { NULL },
NC_("colormap-action", "Delete this color"),
colormap_delete_color_cmd_callback,
PIKA_HELP_INDEXED_PALETTE_EDIT }
};
@ -116,13 +123,14 @@ void
colormap_actions_update (PikaActionGroup *group,
gpointer data)
{
PikaImage *image = action_data_get_image (data);
PikaContext *context = action_data_get_context (data);
gboolean indexed = FALSE;
gboolean drawable_indexed = FALSE;
gint num_colors = 0;
PikaRGB fg;
PikaRGB bg;
PikaColormapEditor *editor = PIKA_COLORMAP_EDITOR (data);
PikaImage *image = action_data_get_image (data);
PikaContext *context = action_data_get_context (data);
gboolean indexed = FALSE;
gboolean drawable_indexed = FALSE;
gint num_colors = 0;
PikaRGB fg;
PikaRGB bg;
if (image)
{
@ -154,6 +162,9 @@ colormap_actions_update (PikaActionGroup *group,
SET_SENSITIVE ("colormap-edit-color",
indexed && num_colors > 0);
SET_SENSITIVE ("colormap-delete-color",
indexed && num_colors > 0 &&
pika_colormap_editor_is_color_deletable (editor));
SET_SENSITIVE ("colormap-add-color-from-fg",
indexed && num_colors < 256);

View File

@ -50,6 +50,16 @@ colormap_edit_color_cmd_callback (PikaAction *action,
pika_colormap_editor_edit_color (editor);
}
void
colormap_delete_color_cmd_callback (PikaAction *action,
GVariant *value,
gpointer data)
{
PikaColormapEditor *editor = PIKA_COLORMAP_EDITOR (data);
pika_colormap_editor_delete_color (editor);
}
void
colormap_add_color_cmd_callback (PikaAction *action,
GVariant *value,

View File

@ -26,6 +26,9 @@
void colormap_edit_color_cmd_callback (PikaAction *action,
GVariant *value,
gpointer data);
void colormap_delete_color_cmd_callback (PikaAction *action,
GVariant *value,
gpointer data);
void colormap_add_color_cmd_callback (PikaAction *action,
GVariant *value,
gpointer data);

View File

@ -446,7 +446,7 @@ pika_gegl_procedure_new (Pika *pika,
FALSE,
PIKA_PARAM_READWRITE));
pika_procedure_add_argument (procedure,
g_param_spec_int ("n-drawables",
g_param_spec_int ("num-drawables",
"N drawables",
"The number of drawables",
0, G_MAXINT32, 0,

View File

@ -50,7 +50,7 @@ _("Timestamp of the last update check.")
"Defines the color management behavior."
#define COLOR_PROFILE_POLICY_BLURB \
_("How to handle embedded color profiles when opening a file.")
_("What to do when opening a file with an embedded ICC color profile.")
#define COLOR_PROFILE_PATH_BLURB \
_("Sets the default folder path for all color profile file dialogs.")

View File

@ -1195,6 +1195,7 @@ pika_undo_type_get_type (void)
{ PIKA_UNDO_GROUP_IMAGE_VECTORS_MERGE, "PIKA_UNDO_GROUP_IMAGE_VECTORS_MERGE", "group-image-vectors-merge" },
{ PIKA_UNDO_GROUP_IMAGE_QUICK_MASK, "PIKA_UNDO_GROUP_IMAGE_QUICK_MASK", "group-image-quick-mask" },
{ PIKA_UNDO_GROUP_IMAGE_GRID, "PIKA_UNDO_GROUP_IMAGE_GRID", "group-image-grid" },
{ PIKA_UNDO_GROUP_IMAGE_COLORMAP_REMAP, "PIKA_UNDO_GROUP_IMAGE_COLORMAP_REMAP", "group-image-colormap-remap" },
{ PIKA_UNDO_GROUP_GUIDE, "PIKA_UNDO_GROUP_GUIDE", "group-guide" },
{ PIKA_UNDO_GROUP_SAMPLE_POINT, "PIKA_UNDO_GROUP_SAMPLE_POINT", "group-sample-point" },
{ PIKA_UNDO_GROUP_DRAWABLE, "PIKA_UNDO_GROUP_DRAWABLE", "group-drawable" },
@ -1303,6 +1304,7 @@ pika_undo_type_get_type (void)
{ PIKA_UNDO_GROUP_IMAGE_VECTORS_MERGE, NC_("undo-type", "Merge paths"), NULL },
{ PIKA_UNDO_GROUP_IMAGE_QUICK_MASK, NC_("undo-type", "Quick Mask"), NULL },
{ PIKA_UNDO_GROUP_IMAGE_GRID, NC_("undo-type", "Grid"), NULL },
{ PIKA_UNDO_GROUP_IMAGE_COLORMAP_REMAP, NC_("undo-type", "Colormap remapping"), NULL },
{ PIKA_UNDO_GROUP_GUIDE, NC_("undo-type", "Guide"), NULL },
{ PIKA_UNDO_GROUP_SAMPLE_POINT, NC_("undo-type", "Sample Point"), NULL },
{ PIKA_UNDO_GROUP_DRAWABLE, NC_("undo-type", "Layer/Channel"), NULL },

View File

@ -548,6 +548,7 @@ typedef enum /*< pdb-skip >*/
PIKA_UNDO_GROUP_IMAGE_VECTORS_MERGE, /*< desc="Merge paths" >*/
PIKA_UNDO_GROUP_IMAGE_QUICK_MASK, /*< desc="Quick Mask" >*/
PIKA_UNDO_GROUP_IMAGE_GRID, /*< desc="Grid" >*/
PIKA_UNDO_GROUP_IMAGE_COLORMAP_REMAP, /*< desc="Colormap remapping" >*/
PIKA_UNDO_GROUP_GUIDE, /*< desc="Guide" >*/
PIKA_UNDO_GROUP_SAMPLE_POINT, /*< desc="Sample Point" >*/
PIKA_UNDO_GROUP_DRAWABLE, /*< desc="Layer/Channel" >*/

View File

@ -33,8 +33,10 @@
#include "pikacontainer.h"
#include "pikacontext.h"
#include "pikadisplay.h"
#include "pikadrawable.h"
#include "pikaimage.h"
#include "pikaprogress.h"
#include "pikaresource.h"
#include "pikawaitable.h"
#include "about.h"
@ -325,17 +327,17 @@ pika_get_empty_display (Pika *pika)
return NULL;
}
guint32
GBytes *
pika_get_display_window_id (Pika *pika,
PikaDisplay *display)
{
g_return_val_if_fail (PIKA_IS_PIKA (pika), -1);
g_return_val_if_fail (PIKA_IS_DISPLAY (display), -1);
g_return_val_if_fail (PIKA_IS_PIKA (pika), NULL);
g_return_val_if_fail (PIKA_IS_DISPLAY (display), NULL);
if (pika->gui.display_get_window_id)
return pika->gui.display_get_window_id (display);
return -1;
return NULL;
}
PikaDisplay *
@ -407,10 +409,11 @@ gboolean
pika_pdb_dialog_new (Pika *pika,
PikaContext *context,
PikaProgress *progress,
PikaContainer *container,
GType contents_type,
GBytes *parent_handle,
const gchar *title,
const gchar *callback_name,
const gchar *object_name,
PikaObject *object,
...)
{
gboolean retval = FALSE;
@ -418,7 +421,10 @@ pika_pdb_dialog_new (Pika *pika,
g_return_val_if_fail (PIKA_IS_PIKA (pika), FALSE);
g_return_val_if_fail (PIKA_IS_CONTEXT (context), FALSE);
g_return_val_if_fail (progress == NULL || PIKA_IS_PROGRESS (progress), FALSE);
g_return_val_if_fail (PIKA_IS_CONTAINER (container), FALSE);
g_return_val_if_fail (g_type_is_a (contents_type, PIKA_TYPE_RESOURCE) ||
g_type_is_a (contents_type, PIKA_TYPE_DRAWABLE), FALSE);
g_return_val_if_fail (object == NULL ||
g_type_is_a (G_TYPE_FROM_INSTANCE (object), contents_type), FALSE);
g_return_val_if_fail (title != NULL, FALSE);
g_return_val_if_fail (callback_name != NULL, FALSE);
@ -426,12 +432,11 @@ pika_pdb_dialog_new (Pika *pika,
{
va_list args;
va_start (args, object_name);
va_start (args, object);
retval = pika->gui.pdb_dialog_new (pika, context, progress,
container, title,
callback_name, object_name,
args);
contents_type, parent_handle, title,
callback_name, object, args);
va_end (args);
}
@ -440,27 +445,28 @@ pika_pdb_dialog_new (Pika *pika,
}
gboolean
pika_pdb_dialog_set (Pika *pika,
PikaContainer *container,
const gchar *callback_name,
const gchar *object_name,
pika_pdb_dialog_set (Pika *pika,
GType contents_type,
const gchar *callback_name,
PikaObject *object,
...)
{
gboolean retval = FALSE;
g_return_val_if_fail (PIKA_IS_PIKA (pika), FALSE);
g_return_val_if_fail (PIKA_IS_CONTAINER (container), FALSE);
g_return_val_if_fail (g_type_is_a (contents_type, PIKA_TYPE_RESOURCE) ||
contents_type == PIKA_TYPE_DRAWABLE, FALSE);
g_return_val_if_fail (callback_name != NULL, FALSE);
g_return_val_if_fail (object_name != NULL, FALSE);
g_return_val_if_fail (object == NULL || g_type_is_a (G_TYPE_FROM_INSTANCE (object), contents_type), FALSE);
if (pika->gui.pdb_dialog_set)
{
va_list args;
va_start (args, object_name);
va_start (args, object);
retval = pika->gui.pdb_dialog_set (pika, container, callback_name,
object_name, args);
retval = pika->gui.pdb_dialog_set (pika, contents_type, callback_name,
object, args);
va_end (args);
}
@ -470,15 +476,16 @@ pika_pdb_dialog_set (Pika *pika,
gboolean
pika_pdb_dialog_close (Pika *pika,
PikaContainer *container,
GType contents_type,
const gchar *callback_name)
{
g_return_val_if_fail (PIKA_IS_PIKA (pika), FALSE);
g_return_val_if_fail (PIKA_IS_CONTAINER (container), FALSE);
g_return_val_if_fail (g_type_is_a (contents_type, PIKA_TYPE_RESOURCE) ||
contents_type == PIKA_TYPE_DRAWABLE, FALSE);
g_return_val_if_fail (callback_name != NULL, FALSE);
if (pika->gui.pdb_dialog_close)
return pika->gui.pdb_dialog_close (pika, container, callback_name);
return pika->gui.pdb_dialog_close (pika, contents_type, callback_name);
return FALSE;
}

View File

@ -58,7 +58,7 @@ struct _PikaGui
PikaObject * (* get_window_strategy) (Pika *pika);
PikaDisplay * (* get_empty_display) (Pika *pika);
guint32 (* display_get_window_id) (PikaDisplay *display);
GBytes * (* display_get_window_id) (PikaDisplay *display);
PikaDisplay * (* display_create) (Pika *pika,
PikaImage *image,
PikaUnit unit,
@ -77,18 +77,19 @@ struct _PikaGui
gboolean (* pdb_dialog_new) (Pika *pika,
PikaContext *context,
PikaProgress *progress,
PikaContainer *container,
GType contents_type,
GBytes *parent_handle,
const gchar *title,
const gchar *callback_name,
const gchar *object_name,
PikaObject *object,
va_list args);
gboolean (* pdb_dialog_set) (Pika *pika,
PikaContainer *container,
GType contents_type,
const gchar *callback_name,
const gchar *object_name,
PikaObject *object,
va_list args);
gboolean (* pdb_dialog_close) (Pika *pika,
PikaContainer *container,
GType contents_type,
const gchar *callback_name);
gboolean (* recent_list_add_file) (Pika *pika,
GFile *file,
@ -126,7 +127,7 @@ PikaDisplay * pika_get_display_by_id (Pika *pika,
gint ID);
gint pika_get_display_id (Pika *pika,
PikaDisplay *display);
guint32 pika_get_display_window_id (Pika *pika,
GBytes * pika_get_display_window_id (Pika *pika,
PikaDisplay *display);
PikaDisplay * pika_create_display (Pika *pika,
PikaImage *image,
@ -175,19 +176,21 @@ GFile * pika_get_icon_theme_dir (Pika *pika);
gboolean pika_pdb_dialog_new (Pika *pika,
PikaContext *context,
PikaProgress *progress,
PikaContainer *container,
GType contents_type,
GBytes *parent_handle,
const gchar *title,
const gchar *callback_name,
const gchar *object_name,
PikaObject *object,
...) G_GNUC_NULL_TERMINATED;
gboolean pika_pdb_dialog_set (Pika *pika,
PikaContainer *container,
GType contents_type,
const gchar *callback_name,
const gchar *object_name,
PikaObject *object,
...) G_GNUC_NULL_TERMINATED;
gboolean pika_pdb_dialog_close (Pika *pika,
PikaContainer *container,
GType contents_type,
const gchar *callback_name);
gboolean pika_recent_list_add_file (Pika *pika,
GFile *file,
const gchar *mime_type);

View File

@ -202,6 +202,7 @@ pika_container_class_init (PikaContainerClass *klass)
klass->search = NULL;
klass->get_unique_names = NULL;
klass->get_child_by_name = NULL;
klass->get_children_by_name = NULL;
klass->get_child_by_index = NULL;
klass->get_child_index = NULL;
@ -885,6 +886,34 @@ pika_container_get_unique_names (PikaContainer *container)
return FALSE;
}
GList *
pika_container_get_children_by_name (PikaContainer *container,
const gchar *name)
{
g_return_val_if_fail (PIKA_IS_CONTAINER (container), NULL);
if (!name)
return NULL;
if (PIKA_CONTAINER_GET_CLASS (container)->get_children_by_name != NULL &&
! pika_container_get_unique_names (container))
{
return PIKA_CONTAINER_GET_CLASS (container)->get_children_by_name (container,
name);
}
else
{
PikaObject *child;
child = PIKA_CONTAINER_GET_CLASS (container)->get_child_by_name (container, name);
if (child != NULL)
return g_list_prepend (NULL, child);
else
return NULL;
}
}
PikaObject *
pika_container_get_child_by_name (PikaContainer *container,
const gchar *name)

View File

@ -79,6 +79,8 @@ struct _PikaContainerClass
gboolean (* get_unique_names) (PikaContainer *container);
PikaObject * (* get_child_by_name) (PikaContainer *container,
const gchar *name);
GList * (* get_children_by_name) (PikaContainer *container,
const gchar *name);
PikaObject * (* get_child_by_index) (PikaContainer *container,
gint index);
gint (* get_child_index) (PikaContainer *container,
@ -121,6 +123,8 @@ PikaObject * pika_container_search (PikaContainer *contain
gboolean pika_container_get_unique_names (PikaContainer *container);
GList * pika_container_get_children_by_name (PikaContainer *container,
const gchar *name);
PikaObject * pika_container_get_child_by_name (PikaContainer *container,
const gchar *name);
PikaObject * pika_container_get_child_by_index (PikaContainer *container,

View File

@ -34,6 +34,7 @@
#include "pika-memsize.h"
#include "pikadata.h"
#include "pikaidtable.h"
#include "pikaimage.h"
#include "pikatag.h"
#include "pikatagged.h"
@ -51,6 +52,7 @@ enum
PROP_0,
PROP_ID,
PROP_FILE,
PROP_IMAGE,
PROP_WRITABLE,
PROP_DELETABLE,
PROP_MIME_TYPE
@ -59,8 +61,10 @@ enum
struct _PikaDataPrivate
{
gint ID;
GFile *file;
gint ID;
GFile *file;
PikaImage *image;
GQuark mime_type;
guint writable : 1;
guint deletable : 1;
@ -172,6 +176,11 @@ pika_data_class_init (PikaDataClass *klass)
G_TYPE_FILE,
PIKA_PARAM_READWRITE));
g_object_class_install_property (object_class, PROP_IMAGE,
g_param_spec_object ("image", NULL, NULL,
PIKA_TYPE_IMAGE,
PIKA_PARAM_READWRITE));
g_object_class_install_property (object_class, PROP_WRITABLE,
g_param_spec_boolean ("writable", NULL, NULL,
FALSE,
@ -268,6 +277,12 @@ pika_data_set_property (GObject *object,
private->deletable);
break;
case PROP_IMAGE:
pika_data_set_image (data,
g_value_get_object (value),
private->writable,
private->deletable);
break;
case PROP_WRITABLE:
private->writable = g_value_get_boolean (value);
break;
@ -307,6 +322,10 @@ pika_data_get_property (GObject *object,
g_value_set_object (value, private->file);
break;
case PROP_IMAGE:
g_value_set_object (value, private->image);
break;
case PROP_WRITABLE:
g_value_set_boolean (value, private->writable);
break;
@ -462,7 +481,7 @@ pika_data_get_identifier (PikaTagged *tagged)
gchar *identifier = NULL;
gchar *collection = NULL;
g_return_val_if_fail (private->internal || private->file != NULL, NULL);
g_return_val_if_fail (private->internal || private->file != NULL || private->image != NULL, NULL);
collection = pika_data_get_collection (data);
/* The identifier is guaranteed to be unique because we use 2 directory
@ -503,7 +522,7 @@ pika_data_get_collection (PikaData *data)
PikaDataPrivate *private = PIKA_DATA_GET_PRIVATE (data);
gchar *collection = NULL;
g_return_val_if_fail (private->internal || private->file != NULL, NULL);
g_return_val_if_fail (private->internal || private->file != NULL || private->image != NULL, NULL);
if (private->file)
{
@ -551,6 +570,10 @@ pika_data_get_collection (PikaData *data)
g_free (path);
}
else if (private->image)
{
collection = g_strdup_printf ("[image-id-%d]", pika_image_get_id (private->image));
}
else if (private->internal)
{
collection = g_strdup (private->collection);
@ -603,7 +626,7 @@ pika_data_save (PikaData *data,
g_return_val_if_fail (private->writable == TRUE, FALSE);
if (private->internal)
if (private->internal || private->image != NULL)
{
private->dirty = FALSE;
return TRUE;
@ -862,6 +885,8 @@ pika_data_set_file (PikaData *data,
if (private->internal)
return;
g_return_if_fail (private->image == NULL);
g_set_object (&private->file, file);
private->writable = FALSE;
@ -937,6 +962,53 @@ pika_data_get_file (PikaData *data)
return private->file;
}
/**
* pika_data_set_image:
* @data: A #PikaData object
* @image: Image to assign to @data.
* @writable: %TRUE if we want to be able to write to this file.
* @deletable: %TRUE if we want to be able to delete this file.
*
* This function assigns an image to @data. This can only be done if no file has
* been assigned (a non-internal data can be attached either to a file or to an
* image).
**/
void
pika_data_set_image (PikaData *data,
PikaImage *image,
gboolean writable,
gboolean deletable)
{
PikaDataPrivate *private;
g_return_if_fail (PIKA_IS_DATA (data));
g_return_if_fail (PIKA_IS_IMAGE (image));
private = PIKA_DATA_GET_PRIVATE (data);
if (private->internal)
return;
g_return_if_fail (private->file == NULL);
g_set_object (&private->image, image);
private->writable = writable ? TRUE : FALSE;
private->deletable = deletable ? TRUE : FALSE;
}
PikaImage *
pika_data_get_image (PikaData *data)
{
PikaDataPrivate *private;
g_return_val_if_fail (PIKA_IS_DATA (data), NULL);
private = PIKA_DATA_GET_PRIVATE (data);
return private->image;
}
/**
* pika_data_create_filename:
* @data: a #Pikadata object.

View File

@ -102,6 +102,11 @@ void pika_data_set_file (PikaData *data,
gboolean writable,
gboolean deletable);
GFile * pika_data_get_file (PikaData *data);
void pika_data_set_image (PikaData *data,
PikaImage *image,
gboolean writable,
gboolean deletable);
PikaImage * pika_data_get_image (PikaData *data);
void pika_data_create_filename (PikaData *data,
GFile *dest_dir);

View File

@ -403,6 +403,9 @@ pika_data_factory_real_data_save (PikaDataFactory *factory)
PikaData *data = list->data;
GError *error = NULL;
if (pika_data_get_image (data))
continue;
if (! pika_data_get_file (data))
pika_data_create_filename (data, writable_dir);
@ -460,6 +463,7 @@ pika_data_factory_real_data_duplicate (PikaDataFactory *factory,
gint copy_len;
gint number;
gchar *new_name;
GError *error = NULL;
ext = strrchr (name, '#');
copy_len = strlen (_("copy"));
@ -479,8 +483,12 @@ pika_data_factory_real_data_duplicate (PikaDataFactory *factory,
pika_object_take_name (PIKA_OBJECT (new_data), new_name);
if (! pika_data_factory_data_save_single (factory, new_data, &error))
g_critical ("%s: data saving failed: %s", G_STRFUNC, error->message);
pika_container_add (priv->container, PIKA_OBJECT (new_data));
g_object_unref (new_data);
g_clear_error (&error);
}
return new_data;
@ -674,8 +682,14 @@ pika_data_factory_data_new (PikaDataFactory *factory,
if (data)
{
GError *error = NULL;
if (! pika_data_factory_data_save_single (factory, data, &error))
g_critical ("%s: data saving failed: %s", G_STRFUNC, error->message);
pika_container_add (priv->container, PIKA_OBJECT (data));
g_object_unref (data);
g_clear_error (&error);
return data;
}
@ -750,7 +764,7 @@ pika_data_factory_data_save_single (PikaDataFactory *factory,
g_return_val_if_fail (PIKA_IS_DATA (data), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
if (! pika_data_is_dirty (data))
if (! pika_data_is_dirty (data) || pika_data_get_image (data))
return TRUE;
if (! pika_data_get_file (data))

View File

@ -31,25 +31,43 @@
#include "core-types.h"
#include "config/pikageglconfig.h"
#include "gegl/pika-babl.h"
#include "gegl/pika-gegl-loops.h"
#include "pika.h"
#include "pikacontainer.h"
#include "pikadatafactory.h"
#include "pikadrawable.h"
#include "pikaimage.h"
#include "pikaimage-colormap.h"
#include "pikaimage-private.h"
#include "pikaimage-undo.h"
#include "pikaimage-undo-push.h"
#include "pikapalette.h"
#include "pika-intl.h"
typedef struct
{
GeglBuffer *buffer;
const Babl *format;
/* Shared by jobs. */
gboolean *found;
GRWLock *lock;
} IndexUsedJobData;
/* local function prototype */
static void pika_image_colormap_set_palette_entry (PikaImage *image,
const PikaRGB *color,
gint index);
static void pika_image_colormap_set_palette_entry (PikaImage *image,
const PikaRGB *color,
gint index);
static void pika_image_colormap_thread_is_index_used (IndexUsedJobData *data,
gint index);
/* public functions */
@ -80,7 +98,7 @@ pika_image_colormap_init (PikaImage *image)
pika_palette_set_columns (private->palette, 16);
pika_data_make_internal (PIKA_DATA (private->palette), palette_id);
pika_data_set_image (PIKA_DATA (private->palette), image, TRUE, FALSE);
palettes = pika_data_factory_get_container (image->pika->palette_factory);
@ -319,9 +337,8 @@ pika_image_set_colormap (PikaImage *image,
pika_image_colormap_set_palette_entry (image, &color, i);
}
pika_data_thaw (PIKA_DATA (private->palette));
pika_image_colormap_changed (image, -1);
pika_data_thaw (PIKA_DATA (private->palette));
}
void
@ -347,6 +364,45 @@ pika_image_unset_colormap (PikaImage *image,
pika_image_colormap_changed (image, -1);
}
gboolean
pika_image_colormap_is_index_used (PikaImage *image,
gint color_index)
{
GList *layers;
GList *iter;
GThreadPool *pool;
GRWLock lock;
gboolean found = FALSE;
gint num_processors;
g_rw_lock_init (&lock);
num_processors = PIKA_GEGL_CONFIG (image->pika->config)->num_processors;
layers = pika_image_get_layer_list (image);
pool = g_thread_pool_new_full ((GFunc) pika_image_colormap_thread_is_index_used,
GINT_TO_POINTER (color_index),
(GDestroyNotify) g_free,
num_processors, TRUE, NULL);
for (iter = layers; iter; iter = g_list_next (iter))
{
IndexUsedJobData *job_data;
job_data = g_malloc (sizeof (IndexUsedJobData ));
job_data->buffer = pika_drawable_get_buffer (iter->data);
job_data->format = pika_drawable_get_format_without_alpha (iter->data);
job_data->found = &found;
job_data->lock = &lock;
g_thread_pool_push (pool, job_data, NULL);
}
g_thread_pool_free (pool, FALSE, TRUE);
g_rw_lock_clear (&lock);
g_list_free (layers);
return found;
}
void
pika_image_get_colormap_entry (PikaImage *image,
gint color_index,
@ -420,6 +476,57 @@ pika_image_add_colormap_entry (PikaImage *image,
pika_image_colormap_changed (image, -1);
}
gboolean
pika_image_delete_colormap_entry (PikaImage *image,
gint color_index,
gboolean push_undo)
{
g_return_val_if_fail (PIKA_IS_IMAGE (image), FALSE);
if (! pika_image_colormap_is_index_used (image, color_index))
{
PikaImagePrivate *private;
PikaPaletteEntry *entry;
GList *layers;
GList *iter;
if (push_undo)
{
pika_image_undo_group_start (image, PIKA_UNDO_GROUP_IMAGE_COLORMAP_REMAP,
C_("undo-type", "Delete Colormap entry"));
pika_image_undo_push_image_colormap (image, NULL);
}
private = PIKA_IMAGE_GET_PRIVATE (image);
layers = pika_image_get_layer_list (image);
for (iter = layers; iter; iter = g_list_next (iter))
{
if (push_undo)
pika_image_undo_push_drawable_mod (image, NULL, iter->data, TRUE);
pika_gegl_shift_index (pika_drawable_get_buffer (iter->data), NULL,
pika_drawable_get_format (iter->data),
color_index, -1);
}
entry = pika_palette_get_entry (private->palette, color_index);
pika_palette_delete_entry (private->palette, entry);
g_list_free (layers);
if (push_undo)
pika_image_undo_group_end (image);
pika_image_colormap_changed (image, -1);
return TRUE;
}
return FALSE;
}
/* private functions */
@ -442,3 +549,22 @@ pika_image_colormap_set_palette_entry (PikaImage *image,
pika_palette_set_entry (private->palette, index, name, color);
}
static void
pika_image_colormap_thread_is_index_used (IndexUsedJobData *data,
gint index)
{
g_rw_lock_reader_lock (data->lock);
if (*data->found)
{
g_rw_lock_reader_unlock (data->lock);
return;
}
g_rw_lock_reader_unlock (data->lock);
if (pika_gegl_is_index_used (data->buffer, NULL, data->format, index))
{
g_rw_lock_writer_lock (data->lock);
*data->found = TRUE;
g_rw_lock_writer_unlock (data->lock);
}
}

View File

@ -49,6 +49,9 @@ void pika_image_set_colormap (PikaImage *image,
void pika_image_unset_colormap (PikaImage *image,
gboolean push_undo);
gboolean pika_image_colormap_is_index_used (PikaImage *image,
gint color_index);
void pika_image_get_colormap_entry (PikaImage *image,
gint color_index,
PikaRGB *color);
@ -59,6 +62,9 @@ void pika_image_set_colormap_entry (PikaImage *image,
void pika_image_add_colormap_entry (PikaImage *image,
const PikaRGB *color);
gboolean pika_image_delete_colormap_entry (PikaImage *image,
gint color_index,
gboolean push_undo);
#endif /* __PIKA_IMAGE_COLORMAP_H__ */

View File

@ -24,17 +24,22 @@
#include <cairo.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
#include <gegl.h>
#include <gexiv2/gexiv2.h>
#include "libpikabase/pikabase.h"
#include "libpikacolor/pikacolor.h"
#include "core-types.h"
#include "pika.h"
#include "pikaimage.h"
#include "pikaimage-color-profile.h"
#include "pikaimage-metadata.h"
#include "pikaimage-private.h"
#include "pikaimage-rotate.h"
#include "pikaimage-undo.h"
#include "pikaimage-undo-push.h"
#include "pikalayer-new.h"
/* public functions */
@ -186,3 +191,110 @@ pika_image_metadata_update_colorspace (PikaImage *image)
pika_metadata_set_colorspace (metadata, space);
}
}
/**
* pika_image_metadata_load_thumbnail:
* @pika: The #Pika object.
* @file: A #GFile image.
* @full_image_width: the width of the full image (not the thumbnail).
* @full_image_height: the height of the full image (not the thumbnail).
* @error: Return location for error message
*
* Retrieves a thumbnail from metadata in @file if present.
* It does not need to actually load the full image, only the metadata through
* GExiv2, which makes it fast.
*
* Returns: (transfer none) (nullable): a #PikaImage of the @file thumbnail.
*/
PikaImage *
pika_image_metadata_load_thumbnail (Pika *pika,
GFile *file,
gint *full_image_width,
gint *full_image_height,
const Babl **format,
GError **error)
{
PikaMetadata *metadata;
GInputStream *input_stream;
GdkPixbuf *pixbuf;
guint8 *thumbnail_buffer;
gint thumbnail_size;
PikaImage *image = NULL;
g_return_val_if_fail (G_IS_FILE (file), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
metadata = pika_metadata_load_from_file (file, error);
if (! metadata)
return NULL;
if (! gexiv2_metadata_get_exif_thumbnail (GEXIV2_METADATA (metadata),
&thumbnail_buffer,
&thumbnail_size))
{
g_object_unref (metadata);
return NULL;
}
input_stream = g_memory_input_stream_new_from_data (thumbnail_buffer,
thumbnail_size,
(GDestroyNotify) g_free);
pixbuf = gdk_pixbuf_new_from_stream (input_stream, NULL, error);
g_object_unref (input_stream);
if (pixbuf)
{
PikaLayer *layer;
image = pika_image_new (pika,
gdk_pixbuf_get_width (pixbuf),
gdk_pixbuf_get_height (pixbuf),
PIKA_RGB, PIKA_PRECISION_U8_NON_LINEAR);
pika_image_undo_disable (image);
/* XXX This is possibly wrong, because an image of a given color space may
* have a thumbnail stored in a different colorspace. This is even more
* true with PIKA which always exports RGB thumbnails (see code in
* pika_image_metadata_save_filter()), even for say grayscale images.
* Nevertheless other software may store thumbnail using the same
* colorspace.
*/
*format = pika_pixbuf_get_format (pixbuf);
layer = pika_layer_new_from_pixbuf (pixbuf, image,
pika_image_get_layer_format (image, FALSE),
/* No need to localize; this image is short-lived. */
"Background",
PIKA_OPACITY_OPAQUE,
pika_image_get_default_new_layer_mode (image));
g_object_unref (pixbuf);
pika_image_add_layer (image, layer, NULL, 0, FALSE);
pika_image_apply_metadata_orientation (image, pika_get_user_context (pika), metadata, NULL);
}
/* This is the unoriented dimensions. Should we switch when there is a 90 or
* 270 degree rotation? We don't actually know if the metadata orientation is
* correct.
*/
*full_image_width = gexiv2_metadata_get_pixel_width (GEXIV2_METADATA (metadata));
*full_image_height = gexiv2_metadata_get_pixel_height (GEXIV2_METADATA (metadata));
if (*full_image_width < 1 || *full_image_height < 1)
{
/* Dimensions stored in metadata might be less accurate, yet it's still
* informational.
*/
*full_image_width = gexiv2_metadata_try_get_metadata_pixel_width (GEXIV2_METADATA (metadata), NULL);
*full_image_height = gexiv2_metadata_try_get_metadata_pixel_width (GEXIV2_METADATA (metadata), NULL);
}
if (*full_image_width < 1 || *full_image_height < 1)
{
*full_image_width = 0;
*full_image_height = 0;
}
g_object_unref (metadata);
return image;
}

View File

@ -33,5 +33,12 @@ void pika_image_metadata_update_bits_per_sample (PikaImage *image);
void pika_image_metadata_update_resolution (PikaImage *image);
void pika_image_metadata_update_colorspace (PikaImage *image);
PikaImage * pika_image_metadata_load_thumbnail (Pika *pika,
GFile *file,
gint *full_image_width,
gint *full_image_height,
const Babl **format,
GError **error);
#endif /* __PIKA_IMAGE_METADATA_H__ */

View File

@ -282,6 +282,17 @@ pika_image_import_rotation_metadata (PikaImage *image,
}
}
void
pika_image_apply_metadata_orientation (PikaImage *image,
PikaContext *context,
PikaMetadata *metadata,
PikaProgress *progress)
{
pika_image_metadata_rotate (image, context,
gexiv2_metadata_try_get_orientation (GEXIV2_METADATA (metadata), NULL),
progress);
}
/* Private Functions */

View File

@ -23,14 +23,20 @@
#define __PIKA_IMAGE_ROTATE_H__
void pika_image_rotate (PikaImage *image,
PikaContext *context,
PikaRotationType rotate_type,
PikaProgress *progress);
void pika_image_rotate (PikaImage *image,
PikaContext *context,
PikaRotationType rotate_type,
PikaProgress *progress);
void pika_image_import_rotation_metadata (PikaImage *image,
PikaContext *context,
PikaProgress *progress,
gboolean interactive);
void pika_image_apply_metadata_orientation (PikaImage *image,
PikaContext *context,
PikaMetadata *metadata,
PikaProgress *progress);
void pika_image_import_rotation_metadata (PikaImage *image,
PikaContext *context,
PikaProgress *progress,
gboolean interactive);
#endif /* __PIKA_IMAGE_ROTATE_H__ */

View File

@ -625,6 +625,9 @@ pika_image_undo_dirty_from_type (PikaUndoType undo_type)
case PIKA_UNDO_GROUP_IMAGE_CONVERT:
return PIKA_DIRTY_IMAGE | PIKA_DIRTY_DRAWABLE;
case PIKA_UNDO_GROUP_IMAGE_COLORMAP_REMAP:
return PIKA_DIRTY_IMAGE | PIKA_DIRTY_DRAWABLE;
case PIKA_UNDO_GROUP_IMAGE_LAYERS_MERGE:
return PIKA_DIRTY_IMAGE_STRUCTURE | PIKA_DIRTY_DRAWABLE;

View File

@ -45,6 +45,7 @@
#include "pikacontainer.h"
#include "pikacontext.h"
#include "pikaimage.h"
#include "pikaimage-metadata.h"
#include "pikaimagefile.h"
#include "pikapickable.h"
#include "pikaprogress.h"
@ -486,19 +487,37 @@ pika_imagefile_create_thumbnail (PikaImagefile *imagefile,
if (error && *error)
{
g_printerr ("Info: Thumbnail load procedure failed: %s\n"
" Falling back to file load procedure.\n",
" Falling back to metadata or file load.\n",
(*error)->message);
g_clear_error (error);
}
image = file_open_image (private->pika, context, progress,
private->file,
FALSE, NULL, PIKA_RUN_NONINTERACTIVE,
&status, &mime_type, error);
image = pika_image_metadata_load_thumbnail (private->pika, private->file, &width, &height, &format, error);
if (image)
pika_thumbnail_set_info_from_image (private->thumbnail,
mime_type, image);
{
pika_thumbnail_set_info (private->thumbnail,
mime_type, width, height,
format, 0);
}
else
{
if (error && *error)
{
g_printerr ("Info: metadata load failed: %s\n"
" Falling back to file load procedure.\n",
(*error)->message);
g_clear_error (error);
}
image = file_open_image (private->pika, context, progress,
private->file,
FALSE, NULL, PIKA_RUN_NONINTERACTIVE,
&status, &mime_type, error);
if (image)
pika_thumbnail_set_info_from_image (private->thumbnail,
mime_type, image);
}
}