503 lines
17 KiB
C
503 lines
17 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) 2017 Ell
|
||
|
*
|
||
|
* 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 <string.h>
|
||
|
|
||
|
#include <gegl.h>
|
||
|
#include <gtk/gtk.h>
|
||
|
|
||
|
#include "libpikabase/pikabase.h"
|
||
|
#include "libpikacolor/pikacolor.h"
|
||
|
#include "libpikaconfig/pikaconfig.h"
|
||
|
#include "libpikamath/pikamath.h"
|
||
|
#include "libpikamodule/pikamodule.h"
|
||
|
#include "libpikawidgets/pikawidgets.h"
|
||
|
|
||
|
#include "libpika/libpika-intl.h"
|
||
|
|
||
|
|
||
|
#define DEFAULT_SHADOWS_COLOR (&(PikaRGB) {0.25, 0.25, 1.00, 1.00})
|
||
|
#define DEFAULT_HIGHLIGHTS_COLOR (&(PikaRGB) {1.00, 0.25, 0.25, 1.00})
|
||
|
#define DEFAULT_BOGUS_COLOR (&(PikaRGB) {1.00, 1.00, 0.25, 1.00})
|
||
|
|
||
|
|
||
|
#define CDISPLAY_TYPE_CLIP_WARNING (cdisplay_clip_warning_get_type ())
|
||
|
#define CDISPLAY_CLIP_WARNING(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CDISPLAY_TYPE_CLIP_WARNING, CdisplayClipWarning))
|
||
|
#define CDISPLAY_CLIP_WARNING_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CDISPLAY_TYPE_CLIP_WARNING, CdisplayClipWarningClass))
|
||
|
#define CDISPLAY_IS_CLIP_WARNING(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CDISPLAY_TYPE_CLIP_WARNING))
|
||
|
#define CDISPLAY_IS_CLIP_WARNING_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CDISPLAY_TYPE_CLIP_WARNING))
|
||
|
|
||
|
|
||
|
typedef enum
|
||
|
{
|
||
|
WARNING_SHADOW = 1 << 0,
|
||
|
WARNING_HIGHLIGHT = 1 << 1,
|
||
|
WARNING_BOGUS = 1 << 2
|
||
|
} Warning;
|
||
|
|
||
|
|
||
|
typedef struct _CdisplayClipWarning CdisplayClipWarning;
|
||
|
typedef struct _CdisplayClipWarningClass CdisplayClipWarningClass;
|
||
|
|
||
|
struct _CdisplayClipWarning
|
||
|
{
|
||
|
PikaColorDisplay parent_instance;
|
||
|
|
||
|
gboolean show_shadows;
|
||
|
PikaRGB shadows_color;
|
||
|
gboolean show_highlights;
|
||
|
PikaRGB highlights_color;
|
||
|
gboolean show_bogus;
|
||
|
PikaRGB bogus_color;
|
||
|
gboolean include_alpha;
|
||
|
gboolean include_transparent;
|
||
|
|
||
|
gfloat colors[8][2][4];
|
||
|
};
|
||
|
|
||
|
struct _CdisplayClipWarningClass
|
||
|
{
|
||
|
PikaColorDisplayClass parent_instance;
|
||
|
};
|
||
|
|
||
|
|
||
|
enum
|
||
|
{
|
||
|
PROP_0,
|
||
|
PROP_SHOW_SHADOWS,
|
||
|
PROP_SHADOWS_COLOR,
|
||
|
PROP_SHOW_HIGHLIGHTS,
|
||
|
PROP_HIGHLIGHTS_COLOR,
|
||
|
PROP_SHOW_BOGUS,
|
||
|
PROP_BOGUS_COLOR,
|
||
|
PROP_INCLUDE_ALPHA,
|
||
|
PROP_INCLUDE_TRANSPARENT
|
||
|
};
|
||
|
|
||
|
|
||
|
GType cdisplay_clip_warning_get_type (void);
|
||
|
|
||
|
static void cdisplay_clip_warning_set_property (GObject *object,
|
||
|
guint property_id,
|
||
|
const GValue *value,
|
||
|
GParamSpec *pspec);
|
||
|
static void cdisplay_clip_warning_get_property (GObject *object,
|
||
|
guint property_id,
|
||
|
GValue *value,
|
||
|
GParamSpec *pspec);
|
||
|
|
||
|
static void cdisplay_clip_warning_convert_buffer (PikaColorDisplay *display,
|
||
|
GeglBuffer *buffer,
|
||
|
GeglRectangle *area);
|
||
|
|
||
|
static void cdisplay_clip_warning_set_member (CdisplayClipWarning *clip_warning,
|
||
|
const gchar *property_name,
|
||
|
gpointer member,
|
||
|
gconstpointer value,
|
||
|
gsize size);
|
||
|
static void cdisplay_clip_warning_update_colors (CdisplayClipWarning *clip_warning);
|
||
|
|
||
|
|
||
|
static const PikaModuleInfo cdisplay_clip_warning_info =
|
||
|
{
|
||
|
PIKA_MODULE_ABI_VERSION,
|
||
|
/* Translators: "Clip Warning" is the name of a (color) display filter
|
||
|
* that highlights pixels outside of the color space range.
|
||
|
* Shown as a label description. */
|
||
|
N_("Clip warning color display filter"),
|
||
|
"Ell",
|
||
|
"v1.0",
|
||
|
"(c) 2017, released under the GPL",
|
||
|
"2017"
|
||
|
};
|
||
|
|
||
|
|
||
|
G_DEFINE_DYNAMIC_TYPE (CdisplayClipWarning, cdisplay_clip_warning,
|
||
|
PIKA_TYPE_COLOR_DISPLAY)
|
||
|
|
||
|
|
||
|
G_MODULE_EXPORT const PikaModuleInfo *
|
||
|
pika_module_query (GTypeModule *module)
|
||
|
{
|
||
|
return &cdisplay_clip_warning_info;
|
||
|
}
|
||
|
|
||
|
G_MODULE_EXPORT gboolean
|
||
|
pika_module_register (GTypeModule *module)
|
||
|
{
|
||
|
cdisplay_clip_warning_register_type (module);
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
cdisplay_clip_warning_class_init (CdisplayClipWarningClass *klass)
|
||
|
{
|
||
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||
|
PikaColorDisplayClass *display_class = PIKA_COLOR_DISPLAY_CLASS (klass);
|
||
|
|
||
|
object_class->get_property = cdisplay_clip_warning_get_property;
|
||
|
object_class->set_property = cdisplay_clip_warning_set_property;
|
||
|
|
||
|
PIKA_CONFIG_PROP_BOOLEAN (object_class, PROP_SHOW_SHADOWS,
|
||
|
"show-shadows",
|
||
|
_("Show shadows"),
|
||
|
_("Show warning for pixels with a negative component"),
|
||
|
TRUE,
|
||
|
PIKA_PARAM_STATIC_STRINGS);
|
||
|
|
||
|
PIKA_CONFIG_PROP_RGB (object_class, PROP_SHADOWS_COLOR,
|
||
|
"shadows-color",
|
||
|
_("Shadows color"),
|
||
|
_("Shadows warning color"),
|
||
|
FALSE,
|
||
|
DEFAULT_SHADOWS_COLOR,
|
||
|
PIKA_PARAM_STATIC_STRINGS |
|
||
|
PIKA_CONFIG_PARAM_DEFAULTS);
|
||
|
|
||
|
gegl_param_spec_set_property_key (
|
||
|
g_object_class_find_property (G_OBJECT_CLASS (klass), "shadows-color"),
|
||
|
"sensitive", "show-shadows");
|
||
|
|
||
|
PIKA_CONFIG_PROP_BOOLEAN (object_class, PROP_SHOW_HIGHLIGHTS,
|
||
|
"show-highlights",
|
||
|
_("Show highlights"),
|
||
|
_("Show warning for pixels with a component greater than one"),
|
||
|
TRUE,
|
||
|
PIKA_PARAM_STATIC_STRINGS);
|
||
|
|
||
|
PIKA_CONFIG_PROP_RGB (object_class, PROP_HIGHLIGHTS_COLOR,
|
||
|
"highlights-color",
|
||
|
_("Highlights color"),
|
||
|
_("Highlights warning color"),
|
||
|
FALSE,
|
||
|
DEFAULT_HIGHLIGHTS_COLOR,
|
||
|
PIKA_PARAM_STATIC_STRINGS |
|
||
|
PIKA_CONFIG_PARAM_DEFAULTS);
|
||
|
|
||
|
gegl_param_spec_set_property_key (
|
||
|
g_object_class_find_property (G_OBJECT_CLASS (klass), "highlights-color"),
|
||
|
"sensitive", "show-highlights");
|
||
|
|
||
|
PIKA_CONFIG_PROP_BOOLEAN (object_class, PROP_SHOW_BOGUS,
|
||
|
"show-bogus",
|
||
|
_("Show bogus"),
|
||
|
_("Show warning for pixels with an infinite or NaN component"),
|
||
|
TRUE,
|
||
|
PIKA_PARAM_STATIC_STRINGS);
|
||
|
|
||
|
PIKA_CONFIG_PROP_RGB (object_class, PROP_BOGUS_COLOR,
|
||
|
"bogus-color",
|
||
|
_("Bogus color"),
|
||
|
_("Bogus warning color"),
|
||
|
FALSE,
|
||
|
DEFAULT_BOGUS_COLOR,
|
||
|
PIKA_PARAM_STATIC_STRINGS |
|
||
|
PIKA_CONFIG_PARAM_DEFAULTS);
|
||
|
|
||
|
gegl_param_spec_set_property_key (
|
||
|
g_object_class_find_property (G_OBJECT_CLASS (klass), "bogus-color"),
|
||
|
"sensitive", "show-bogus");
|
||
|
|
||
|
PIKA_CONFIG_PROP_BOOLEAN (object_class, PROP_INCLUDE_ALPHA,
|
||
|
"include-alpha",
|
||
|
_("Include alpha component"),
|
||
|
_("Include alpha component in the warning"),
|
||
|
TRUE,
|
||
|
PIKA_PARAM_STATIC_STRINGS);
|
||
|
|
||
|
PIKA_CONFIG_PROP_BOOLEAN (object_class, PROP_INCLUDE_TRANSPARENT,
|
||
|
"include-transparent",
|
||
|
_("Include transparent pixels"),
|
||
|
_("Include fully transparent pixels in the warning"),
|
||
|
TRUE,
|
||
|
PIKA_PARAM_STATIC_STRINGS);
|
||
|
|
||
|
display_class->name = _("Clip Warning");
|
||
|
display_class->help_id = "pika-colordisplay-clip-warning";
|
||
|
display_class->icon_name = PIKA_ICON_DISPLAY_FILTER_CLIP_WARNING;
|
||
|
|
||
|
display_class->convert_buffer = cdisplay_clip_warning_convert_buffer;
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
cdisplay_clip_warning_class_finalize (CdisplayClipWarningClass *klass)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
cdisplay_clip_warning_init (CdisplayClipWarning *clip_warning)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
cdisplay_clip_warning_get_property (GObject *object,
|
||
|
guint property_id,
|
||
|
GValue *value,
|
||
|
GParamSpec *pspec)
|
||
|
{
|
||
|
CdisplayClipWarning *clip_warning = CDISPLAY_CLIP_WARNING (object);
|
||
|
|
||
|
switch (property_id)
|
||
|
{
|
||
|
case PROP_SHOW_SHADOWS:
|
||
|
g_value_set_boolean (value, clip_warning->show_shadows);
|
||
|
break;
|
||
|
case PROP_SHADOWS_COLOR:
|
||
|
g_value_set_boxed (value, &clip_warning->shadows_color);
|
||
|
break;
|
||
|
|
||
|
case PROP_SHOW_HIGHLIGHTS:
|
||
|
g_value_set_boolean (value, clip_warning->show_highlights);
|
||
|
break;
|
||
|
case PROP_HIGHLIGHTS_COLOR:
|
||
|
g_value_set_boxed (value, &clip_warning->highlights_color);
|
||
|
break;
|
||
|
|
||
|
case PROP_SHOW_BOGUS:
|
||
|
g_value_set_boolean (value, clip_warning->show_bogus);
|
||
|
break;
|
||
|
case PROP_BOGUS_COLOR:
|
||
|
g_value_set_boxed (value, &clip_warning->bogus_color);
|
||
|
break;
|
||
|
|
||
|
case PROP_INCLUDE_ALPHA:
|
||
|
g_value_set_boolean (value, clip_warning->include_alpha);
|
||
|
break;
|
||
|
case PROP_INCLUDE_TRANSPARENT:
|
||
|
g_value_set_boolean (value, clip_warning->include_transparent);
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
cdisplay_clip_warning_set_property (GObject *object,
|
||
|
guint property_id,
|
||
|
const GValue *value,
|
||
|
GParamSpec *pspec)
|
||
|
{
|
||
|
CdisplayClipWarning *clip_warning = CDISPLAY_CLIP_WARNING (object);
|
||
|
|
||
|
#define SET_MEMBER_PTR(member, value) \
|
||
|
cdisplay_clip_warning_set_member (clip_warning, \
|
||
|
pspec->name, \
|
||
|
&clip_warning->member, \
|
||
|
(value), \
|
||
|
sizeof (clip_warning->member))
|
||
|
#define SET_MEMBER_VAL(member, type, value) \
|
||
|
SET_MEMBER_PTR (member, &(type) {value})
|
||
|
|
||
|
switch (property_id)
|
||
|
{
|
||
|
case PROP_SHOW_SHADOWS:
|
||
|
SET_MEMBER_VAL (show_shadows, gboolean, g_value_get_boolean (value));
|
||
|
break;
|
||
|
case PROP_SHADOWS_COLOR:
|
||
|
SET_MEMBER_PTR (shadows_color, g_value_get_boxed (value));
|
||
|
break;
|
||
|
|
||
|
case PROP_SHOW_HIGHLIGHTS:
|
||
|
SET_MEMBER_VAL (show_highlights, gboolean, g_value_get_boolean (value));
|
||
|
break;
|
||
|
case PROP_HIGHLIGHTS_COLOR:
|
||
|
SET_MEMBER_PTR (highlights_color, g_value_get_boxed (value));
|
||
|
break;
|
||
|
|
||
|
case PROP_SHOW_BOGUS:
|
||
|
SET_MEMBER_VAL (show_bogus, gboolean, g_value_get_boolean (value));
|
||
|
break;
|
||
|
case PROP_BOGUS_COLOR:
|
||
|
SET_MEMBER_PTR (bogus_color, g_value_get_boxed (value));
|
||
|
break;
|
||
|
|
||
|
case PROP_INCLUDE_ALPHA:
|
||
|
SET_MEMBER_VAL (include_alpha, gboolean, g_value_get_boolean (value));
|
||
|
break;
|
||
|
case PROP_INCLUDE_TRANSPARENT:
|
||
|
SET_MEMBER_VAL (include_transparent, gboolean, g_value_get_boolean (value));
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
#undef SET_MEMBER_PTR
|
||
|
#undef SET_MEMBER_VAL
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
cdisplay_clip_warning_convert_buffer (PikaColorDisplay *display,
|
||
|
GeglBuffer *buffer,
|
||
|
GeglRectangle *area)
|
||
|
{
|
||
|
CdisplayClipWarning *clip_warning = CDISPLAY_CLIP_WARNING (display);
|
||
|
GeglBufferIterator *iter;
|
||
|
|
||
|
iter = gegl_buffer_iterator_new (buffer, area, 0,
|
||
|
babl_format ("R'G'B'A float"),
|
||
|
GEGL_ACCESS_READWRITE, GEGL_ABYSS_NONE, 1);
|
||
|
|
||
|
while (gegl_buffer_iterator_next (iter))
|
||
|
{
|
||
|
gfloat *data = iter->items[0].data;
|
||
|
gint count = iter->length;
|
||
|
gint x = iter->items[0].roi.x;
|
||
|
gint y = iter->items[0].roi.y;
|
||
|
|
||
|
while (count--)
|
||
|
{
|
||
|
gint warning = 0;
|
||
|
|
||
|
if (clip_warning->include_transparent ||
|
||
|
! (data[3] <= 0.0f) /* include nan */)
|
||
|
{
|
||
|
if (clip_warning->show_bogus &&
|
||
|
(! isfinite (data[0]) || ! isfinite (data[1]) || ! isfinite (data[2]) ||
|
||
|
(clip_warning->include_alpha && ! isfinite (data[3]))))
|
||
|
{
|
||
|
/* don't combine warning color of pixels with a bogus
|
||
|
* component with other warnings
|
||
|
*/
|
||
|
warning = WARNING_BOGUS;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (clip_warning->show_shadows &&
|
||
|
(data[0] < 0.0f || data[1] < 0.0f || data[2] < 0.0f ||
|
||
|
(clip_warning->include_alpha && data[3] < 0.0f)))
|
||
|
{
|
||
|
warning |= WARNING_SHADOW;
|
||
|
}
|
||
|
|
||
|
if (clip_warning->show_highlights &&
|
||
|
(data[0] > 1.0f || data[1] > 1.0f || data[2] > 1.0f ||
|
||
|
(clip_warning->include_alpha && data[3] > 1.0f)))
|
||
|
{
|
||
|
warning |= WARNING_HIGHLIGHT;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (warning)
|
||
|
{
|
||
|
gboolean alt = ((x + y) >> 3) & 1;
|
||
|
|
||
|
memcpy (data, clip_warning->colors[warning][alt],
|
||
|
4 * sizeof (gfloat));
|
||
|
}
|
||
|
|
||
|
data += 4;
|
||
|
|
||
|
if (++x == iter->items[0].roi.x + iter->items[0].roi.width)
|
||
|
{
|
||
|
x = iter->items[0].roi.x;
|
||
|
y++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
cdisplay_clip_warning_set_member (CdisplayClipWarning *clip_warning,
|
||
|
const gchar *property_name,
|
||
|
gpointer member,
|
||
|
gconstpointer value,
|
||
|
gsize size)
|
||
|
{
|
||
|
if (memcmp (member, value, size))
|
||
|
{
|
||
|
memcpy (member, value, size);
|
||
|
|
||
|
cdisplay_clip_warning_update_colors (clip_warning);
|
||
|
|
||
|
g_object_notify (G_OBJECT (clip_warning), property_name);
|
||
|
pika_color_display_changed (PIKA_COLOR_DISPLAY (clip_warning));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
cdisplay_clip_warning_update_colors (CdisplayClipWarning *clip_warning)
|
||
|
{
|
||
|
gint i;
|
||
|
gint j;
|
||
|
|
||
|
for (i = 0; i < 8; i++)
|
||
|
{
|
||
|
gfloat *color = clip_warning->colors[i][0];
|
||
|
gfloat *alt_color = clip_warning->colors[i][1];
|
||
|
gfloat alt_value;
|
||
|
gint n = 0;
|
||
|
|
||
|
memset (color, 0, 3 * sizeof (gfloat));
|
||
|
|
||
|
if (i & WARNING_SHADOW)
|
||
|
{
|
||
|
color[0] += clip_warning->shadows_color.r;
|
||
|
color[1] += clip_warning->shadows_color.g;
|
||
|
color[2] += clip_warning->shadows_color.b;
|
||
|
|
||
|
n++;
|
||
|
}
|
||
|
|
||
|
if (i & WARNING_HIGHLIGHT)
|
||
|
{
|
||
|
color[0] += clip_warning->highlights_color.r;
|
||
|
color[1] += clip_warning->highlights_color.g;
|
||
|
color[2] += clip_warning->highlights_color.b;
|
||
|
|
||
|
n++;
|
||
|
}
|
||
|
|
||
|
if (i & WARNING_BOGUS)
|
||
|
{
|
||
|
color[0] += clip_warning->bogus_color.r;
|
||
|
color[1] += clip_warning->bogus_color.g;
|
||
|
color[2] += clip_warning->bogus_color.b;
|
||
|
|
||
|
n++;
|
||
|
}
|
||
|
|
||
|
if (n)
|
||
|
{
|
||
|
for (j = 0; j < 3; j++)
|
||
|
color[j] /= n;
|
||
|
}
|
||
|
color[3] = 1.0;
|
||
|
|
||
|
if (MAX (color[0], MAX (color[1], color[2])) <= 0.5)
|
||
|
alt_value = 1.0;
|
||
|
else
|
||
|
alt_value = 0.0;
|
||
|
|
||
|
for (j = 0; j < 3; j++)
|
||
|
alt_color[j] = 0.75 * color[j] + 0.25 * alt_value;
|
||
|
alt_color[3] = 1.0;
|
||
|
}
|
||
|
}
|