mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-01-11 09:55:36 +00:00
lcms: Add LCMS ICC color correction element
https://bugzilla.gnome.org/show_bug.cgi?id=765927
This commit is contained in:
parent
519ead2462
commit
07d6b7f56d
6 changed files with 1058 additions and 0 deletions
11
configure.ac
11
configure.ac
|
@ -2480,6 +2480,15 @@ AG_GST_CHECK_FEATURE(LADSPA, [ladspa], ladspa, [
|
|||
AC_SUBST(LRDF_CFLAGS)
|
||||
])
|
||||
|
||||
dnl *** LCM2 ***
|
||||
#translit(dnm, m, l) AM_CONDITIONAL(USE_LCMS2, true)
|
||||
AG_GST_CHECK_FEATURE(LCMS2, [LCMS colormanagement plugin], lcms2, [
|
||||
PKG_CHECK_MODULES(LCMS2, lcms2 >= 2.7, HAVE_LCMS2="yes", [
|
||||
HAVE_LCMS2="no"
|
||||
])
|
||||
AC_SUBST(LCMS2_LIBS)
|
||||
])
|
||||
|
||||
dnl *** LV2 ***
|
||||
translit(dnm, m, l) AM_CONDITIONAL(USE_LV2, true)
|
||||
AG_GST_CHECK_FEATURE(LV2, [lv2], lv2, [
|
||||
|
@ -3454,6 +3463,7 @@ AM_CONDITIONAL(USE_KATE, false)
|
|||
AM_CONDITIONAL(USE_KMS, false)
|
||||
AM_CONDITIONAL(USE_TIGER, false)
|
||||
AM_CONDITIONAL(USE_LADSPA, false)
|
||||
AM_CONDITIONAL(USE_LCMS2, false)
|
||||
AM_CONDITIONAL(USE_LV2, false)
|
||||
AM_CONDITIONAL(USE_LIBDE265, false)
|
||||
AM_CONDITIONAL(USE_LIBMMS, false)
|
||||
|
@ -3739,6 +3749,7 @@ ext/assrender/Makefile
|
|||
ext/bs2b/Makefile
|
||||
ext/bz2/Makefile
|
||||
ext/chromaprint/Makefile
|
||||
ext/colormanagement/Makefile
|
||||
ext/curl/Makefile
|
||||
ext/dash/Makefile
|
||||
ext/dc1394/Makefile
|
||||
|
|
|
@ -22,6 +22,12 @@ else
|
|||
BZ2_DIR=
|
||||
endif
|
||||
|
||||
if USE_LCMS2
|
||||
COLORMANAGEMENT_DIR=colormanagement
|
||||
else
|
||||
COLORMANAGEMENT_DIR=
|
||||
endif
|
||||
|
||||
if USE_CHROMAPRINT
|
||||
CHROMAPRINT_DIR=chromaprint
|
||||
else
|
||||
|
@ -401,6 +407,7 @@ SUBDIRS=\
|
|||
$(AUDIOFILE_DIR) \
|
||||
$(BS2B_DIR) \
|
||||
$(BZ2_DIR) \
|
||||
$(COLORMANAGEMENT_DIR) \
|
||||
$(CHROMAPRINT_DIR) \
|
||||
$(CURL_DIR) \
|
||||
$(DASH_DIR) \
|
||||
|
@ -467,6 +474,7 @@ DIST_SUBDIRS = \
|
|||
assrender \
|
||||
bs2b \
|
||||
bz2 \
|
||||
colormanagement \
|
||||
chromaprint \
|
||||
curl \
|
||||
dash \
|
||||
|
|
11
ext/colormanagement/Makefile.am
Normal file
11
ext/colormanagement/Makefile.am
Normal file
|
@ -0,0 +1,11 @@
|
|||
plugin_LTLIBRARIES = libgstcolormanagement.la
|
||||
|
||||
libgstcolormanagement_la_SOURCES = gstcolormanagement.c \
|
||||
gstlcms.c
|
||||
|
||||
libgstcolormanagement_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS) $(LCMS2_CFLAGS)
|
||||
libgstcolormanagement_la_LIBADD = $(GST_PLUGINS_BASE_LIBS) -lgstvideo-1.0 $(GST_LIBS) $(LCMS2_LIBS)
|
||||
libgstcolormanagement_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
|
||||
libgstcolormanagement_la_LIBTOOLFLAGS = $(GST_PLUGIN_LIBTOOLFLAGS)
|
||||
|
||||
noinst_HEADERS = gstlcms.h
|
37
ext/colormanagement/gstcolormanagement.c
Normal file
37
ext/colormanagement/gstcolormanagement.c
Normal file
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* GStreamer gstreamer-colormanagement
|
||||
* Copyright (C) 2016 Andreas Frisch <fraxinas@dreambox.guru>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 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 Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "gstlcms.h"
|
||||
|
||||
static gboolean
|
||||
plugin_init (GstPlugin * plugin)
|
||||
{
|
||||
return gst_element_register (plugin, "lcms", GST_RANK_NONE, GST_TYPE_LCMS);
|
||||
}
|
||||
|
||||
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
|
||||
GST_VERSION_MINOR,
|
||||
colormanagement,
|
||||
"Color management correction plugins",
|
||||
plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN);
|
889
ext/colormanagement/gstlcms.c
Normal file
889
ext/colormanagement/gstlcms.c
Normal file
|
@ -0,0 +1,889 @@
|
|||
/*
|
||||
* GStreamer gstreamer-lcms
|
||||
* Copyright (C) 2016 Andreas Frisch <fraxinas@dreambox.guru>
|
||||
*
|
||||
* gstlcms.c
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 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 Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
/**
|
||||
* SECTION:element-lcms
|
||||
* @short_description: Uses LittleCMS 2 to perform ICC profile correction
|
||||
*
|
||||
* This is a color management plugin that uses LittleCMS 2 to correct
|
||||
* frames using the given ICC (International Color Consortium) profiles.
|
||||
* Falls back to internal sRGB profile if no ICC file is specified in property.
|
||||
*
|
||||
* <refsect2>
|
||||
* <title>Example launch line</title>
|
||||
* <para>(write everything in one line, without the backslash characters)</para>
|
||||
* |[
|
||||
* gst-launch-1.0 filesrc location=photo_camera.png ! pngdec ! \
|
||||
* videoconvert ! lcms input-profile=sRGB.icc dest-profile=printer.icc \
|
||||
* pngenc ! filesink location=photo_print.png
|
||||
* ]|
|
||||
* </refsect2>
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "gstlcms.h"
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <gst/video/video.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
GST_DEBUG_CATEGORY_STATIC (lcms_debug);
|
||||
#define GST_CAT_DEFAULT lcms_debug
|
||||
|
||||
/* GstLcms properties */
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
PROP_INTENT,
|
||||
PROP_LOOKUP_METHOD,
|
||||
PROP_SRC_FILE,
|
||||
PROP_DST_FILE,
|
||||
PROP_PRESERVE_BLACK,
|
||||
PROP_EMBEDDED_PROFILE
|
||||
};
|
||||
|
||||
GType
|
||||
gst_lcms_intent_get_type (void)
|
||||
{
|
||||
static volatile gsize intent_type = 0;
|
||||
static const GEnumValue intent[] = {
|
||||
{GST_LCMS_INTENT_PERCEPTUAL, "Perceptual",
|
||||
"perceptual"},
|
||||
{GST_LCMS_INTENT_RELATIVE_COLORIMETRIC, "Relative Colorimetric",
|
||||
"relative"},
|
||||
{GST_LCMS_INTENT_SATURATION, "Saturation",
|
||||
"saturation"},
|
||||
{GST_LCMS_INTENT_ABSOLUTE_COLORIMETRIC, "Absolute Colorimetric",
|
||||
"absolute"},
|
||||
{0, NULL, NULL},
|
||||
};
|
||||
|
||||
if (g_once_init_enter (&intent_type)) {
|
||||
GType tmp = g_enum_register_static ("GstLcmsIntent", intent);
|
||||
g_once_init_leave (&intent_type, tmp);
|
||||
}
|
||||
return (GType) intent_type;
|
||||
}
|
||||
|
||||
static GType
|
||||
gst_lcms_lookup_method_get_type (void)
|
||||
{
|
||||
static volatile gsize lookup_method_type = 0;
|
||||
static const GEnumValue lookup_method[] = {
|
||||
{GST_LCMS_LOOKUP_METHOD_UNCACHED,
|
||||
"Uncached, calculate every pixel on the fly (very slow playback)",
|
||||
"uncached"},
|
||||
{GST_LCMS_LOOKUP_METHOD_PRECALCULATED,
|
||||
"Precalculate lookup table (takes a long time getting READY)",
|
||||
"precalculated"},
|
||||
{GST_LCMS_LOOKUP_METHOD_CACHED,
|
||||
"Calculate and cache color replacement values on first occurence",
|
||||
"cached"},
|
||||
{0, NULL, NULL},
|
||||
};
|
||||
|
||||
if (g_once_init_enter (&lookup_method_type)) {
|
||||
GType tmp = g_enum_register_static ("GstLcmsLookupMethod", lookup_method);
|
||||
g_once_init_leave (&lookup_method_type, tmp);
|
||||
}
|
||||
return (GType) lookup_method_type;
|
||||
}
|
||||
|
||||
#define DEFAULT_INTENT GST_LCMS_INTENT_PERCEPTUAL
|
||||
#define DEFAULT_LOOKUP_METHOD GST_LCMS_LOOKUP_METHOD_CACHED
|
||||
#define DEFAULT_PRESERVE_BLACK FALSE
|
||||
#define DEFAULT_EMBEDDED_PROFILE TRUE
|
||||
|
||||
static GstStaticPadTemplate gst_lcms_src_template =
|
||||
GST_STATIC_PAD_TEMPLATE ("src",
|
||||
GST_PAD_SRC,
|
||||
GST_PAD_ALWAYS,
|
||||
GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("{ "
|
||||
"ARGB, BGRA, ABGR, RGBA, xRGB," "RGBx, xBGR, BGRx, RGB, BGR }"))
|
||||
);
|
||||
|
||||
static GstStaticPadTemplate gst_lcms_sink_template =
|
||||
GST_STATIC_PAD_TEMPLATE ("sink",
|
||||
GST_PAD_SINK,
|
||||
GST_PAD_ALWAYS,
|
||||
GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("{ "
|
||||
"ARGB, BGRA, ABGR, RGBA, xRGB," "RGBx, xBGR, BGRx, RGB, BGR }"))
|
||||
);
|
||||
|
||||
static void gst_lcms_set_property (GObject * object, guint prop_id,
|
||||
const GValue * value, GParamSpec * pspec);
|
||||
static void gst_lcms_get_property (GObject * object, guint prop_id,
|
||||
GValue * value, GParamSpec * pspec);
|
||||
static void gst_lcms_finalize (GObject * object);
|
||||
static GstStateChangeReturn gst_lcms_change_state (GstElement * element,
|
||||
GstStateChange transition);
|
||||
static gboolean gst_lcms_set_info (GstVideoFilter * vfilter, GstCaps * incaps,
|
||||
GstVideoInfo * in_info, GstCaps * outcaps, GstVideoInfo * out_info);
|
||||
static gboolean gst_lcms_sink_event (GstBaseTransform * trans,
|
||||
GstEvent * event);
|
||||
static void gst_lcms_handle_tags (GstLcms * lcms, GstTagList * taglist);
|
||||
static void gst_lcms_handle_tag_sample (GstLcms * lcms, GstSample * sample);
|
||||
static GstFlowReturn gst_lcms_transform_frame (GstVideoFilter * vfilter,
|
||||
GstVideoFrame * inframe, GstVideoFrame * outframe);
|
||||
static GstFlowReturn gst_lcms_transform_frame_ip (GstVideoFilter * vfilter,
|
||||
GstVideoFrame * frame);
|
||||
|
||||
static void gst_lcms_get_ready (GstLcms * lcms);
|
||||
static void gst_lcms_create_transform (GstLcms * lcms);
|
||||
static void gst_lcms_cleanup_cms (GstLcms * lcms);
|
||||
static void gst_lcms_init_lookup_table (GstLcms * lcms);
|
||||
static void gst_lcms_process_rgb (GstLcms * lcms, GstVideoFrame * inframe,
|
||||
GstVideoFrame * outframe);
|
||||
|
||||
G_DEFINE_TYPE (GstLcms, gst_lcms, GST_TYPE_VIDEO_FILTER);
|
||||
|
||||
static void
|
||||
gst_lcms_class_init (GstLcmsClass * klass)
|
||||
{
|
||||
GObjectClass *gobject_class = (GObjectClass *) klass;
|
||||
GstElementClass *element_class = (GstElementClass *) klass;
|
||||
GstBaseTransformClass *trans_class = (GstBaseTransformClass *) klass;
|
||||
GstVideoFilterClass *vfilter_class = (GstVideoFilterClass *) klass;
|
||||
|
||||
GST_DEBUG_CATEGORY_INIT (lcms_debug, "lcms", 0, "lcms");
|
||||
|
||||
gobject_class->set_property = gst_lcms_set_property;
|
||||
gobject_class->get_property = gst_lcms_get_property;
|
||||
gobject_class->finalize = gst_lcms_finalize;
|
||||
|
||||
g_object_class_install_property (gobject_class, PROP_INTENT,
|
||||
g_param_spec_enum ("intent", "Rendering intent",
|
||||
"Select the rendering intent of the color correction",
|
||||
GST_TYPE_LCMS_INTENT, DEFAULT_INTENT,
|
||||
(G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
|
||||
|
||||
g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_SRC_FILE,
|
||||
g_param_spec_string ("input-profile", "Input ICC profile file",
|
||||
"Specify the input ICC profile file to apply", NULL,
|
||||
(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
|
||||
|
||||
g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_DST_FILE,
|
||||
g_param_spec_string ("dest-profile", "Destination ICC profile file",
|
||||
"Specify the destination ICC profile file to apply", NULL,
|
||||
(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
|
||||
|
||||
g_object_class_install_property (gobject_class, PROP_LOOKUP_METHOD,
|
||||
g_param_spec_enum ("lookup", "Lookup method",
|
||||
"Select the caching method for the color compensation calculations",
|
||||
GST_TYPE_LCMS_LOOKUP_METHOD, DEFAULT_LOOKUP_METHOD,
|
||||
(G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
|
||||
|
||||
g_object_class_install_property (gobject_class, PROP_PRESERVE_BLACK,
|
||||
g_param_spec_boolean ("preserve-black", "Preserve black",
|
||||
"Select whether purely black pixels should be preserved",
|
||||
DEFAULT_PRESERVE_BLACK,
|
||||
(G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
|
||||
|
||||
g_object_class_install_property (gobject_class, PROP_EMBEDDED_PROFILE,
|
||||
g_param_spec_boolean ("embedded-profile", "Embedded Profile",
|
||||
"Extract and use source profiles embedded in images",
|
||||
DEFAULT_EMBEDDED_PROFILE,
|
||||
(G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
|
||||
|
||||
gst_element_class_set_static_metadata (element_class,
|
||||
"LCMS2 ICC correction", "Filter/Effect/Video",
|
||||
"Uses LittleCMS 2 to perform ICC profile correction",
|
||||
"Andreas Frisch <fraxinas@opendreambox.org>");
|
||||
|
||||
gst_element_class_add_pad_template (element_class,
|
||||
gst_static_pad_template_get (&gst_lcms_sink_template));
|
||||
gst_element_class_add_pad_template (element_class,
|
||||
gst_static_pad_template_get (&gst_lcms_src_template));
|
||||
|
||||
element_class->change_state = GST_DEBUG_FUNCPTR (gst_lcms_change_state);
|
||||
|
||||
trans_class->sink_event = GST_DEBUG_FUNCPTR (gst_lcms_sink_event);
|
||||
|
||||
vfilter_class->set_info = GST_DEBUG_FUNCPTR (gst_lcms_set_info);
|
||||
vfilter_class->transform_frame_ip =
|
||||
GST_DEBUG_FUNCPTR (gst_lcms_transform_frame_ip);
|
||||
vfilter_class->transform_frame = GST_DEBUG_FUNCPTR (gst_lcms_transform_frame);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_lcms_init (GstLcms * lcms)
|
||||
{
|
||||
lcms->color_lut = NULL;
|
||||
lcms->cms_inp_profile = NULL;
|
||||
lcms->cms_dst_profile = NULL;
|
||||
lcms->cms_transform = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_lcms_finalize (GObject * object)
|
||||
{
|
||||
GstLcms *lcms = GST_LCMS (object);
|
||||
if (lcms->color_lut)
|
||||
g_free (lcms->color_lut);
|
||||
g_free (lcms->inp_profile_filename);
|
||||
g_free (lcms->dst_profile_filename);
|
||||
G_OBJECT_CLASS (gst_lcms_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_lcms_set_intent (GstLcms * lcms, GstLcmsIntent intent)
|
||||
{
|
||||
GEnumValue *val =
|
||||
g_enum_get_value (G_ENUM_CLASS (g_type_class_ref
|
||||
(GST_TYPE_LCMS_INTENT)), intent);
|
||||
const gchar *value_nick;
|
||||
|
||||
g_return_if_fail (GST_IS_LCMS (lcms));
|
||||
if (!val) {
|
||||
GST_ERROR_OBJECT (lcms, "no such rendering intent %i!", intent);
|
||||
return;
|
||||
}
|
||||
value_nick = val->value_nick;
|
||||
|
||||
GST_OBJECT_LOCK (lcms);
|
||||
lcms->intent = intent;
|
||||
GST_OBJECT_UNLOCK (lcms);
|
||||
|
||||
GST_DEBUG_OBJECT (lcms, "successfully set rendering intent to %s (%i)",
|
||||
value_nick, intent);
|
||||
return;
|
||||
}
|
||||
|
||||
static GstLcmsIntent
|
||||
gst_lcms_get_intent (GstLcms * lcms)
|
||||
{
|
||||
g_return_val_if_fail (GST_IS_LCMS (lcms), (GstLcmsIntent) - 1);
|
||||
return lcms->intent;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_lcms_set_lookup_method (GstLcms * lcms, GstLcmsLookupMethod method)
|
||||
{
|
||||
GEnumValue *val =
|
||||
g_enum_get_value (G_ENUM_CLASS (g_type_class_ref
|
||||
(GST_TYPE_LCMS_LOOKUP_METHOD)), method);
|
||||
const gchar *value_nick;
|
||||
|
||||
g_return_if_fail (GST_IS_LCMS (lcms));
|
||||
if (!val) {
|
||||
GST_ERROR_OBJECT (lcms, "no such lookup method %i!", method);
|
||||
return;
|
||||
}
|
||||
value_nick = val->value_nick;
|
||||
|
||||
GST_OBJECT_LOCK (lcms);
|
||||
lcms->lookup_method = method;
|
||||
GST_OBJECT_UNLOCK (lcms);
|
||||
|
||||
GST_DEBUG_OBJECT (lcms, "successfully set lookup method to %s (%i)",
|
||||
value_nick, method);
|
||||
return;
|
||||
}
|
||||
|
||||
static GstLcmsLookupMethod
|
||||
gst_lcms_get_lookup_method (GstLcms * lcms)
|
||||
{
|
||||
g_return_val_if_fail (GST_IS_LCMS (lcms), (GstLcmsLookupMethod) - 1);
|
||||
return lcms->lookup_method;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_lcms_set_property (GObject * object, guint prop_id, const GValue * value,
|
||||
GParamSpec * pspec)
|
||||
{
|
||||
const gchar *filename;
|
||||
GstLcms *lcms = GST_LCMS (object);
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_SRC_FILE:
|
||||
{
|
||||
GST_OBJECT_LOCK (lcms);
|
||||
filename = g_value_get_string (value);
|
||||
if (filename
|
||||
&& g_file_test (filename,
|
||||
(GFileTest) (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR))) {
|
||||
if (lcms->inp_profile_filename)
|
||||
g_free (lcms->inp_profile_filename);
|
||||
lcms->inp_profile_filename = g_strdup (filename);
|
||||
} else {
|
||||
GST_WARNING_OBJECT (lcms, "Input profile file '%s' not found!",
|
||||
filename);
|
||||
}
|
||||
GST_OBJECT_UNLOCK (lcms);
|
||||
break;
|
||||
}
|
||||
case PROP_DST_FILE:
|
||||
{
|
||||
GST_OBJECT_LOCK (lcms);
|
||||
filename = g_value_get_string (value);
|
||||
if (g_file_test (filename,
|
||||
(GFileTest) (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR))) {
|
||||
if (lcms->dst_profile_filename)
|
||||
g_free (lcms->dst_profile_filename);
|
||||
lcms->dst_profile_filename = g_strdup (filename);
|
||||
} else {
|
||||
GST_WARNING_OBJECT (lcms, "Destination profile file '%s' not found!",
|
||||
filename);
|
||||
}
|
||||
GST_OBJECT_UNLOCK (lcms);
|
||||
break;
|
||||
}
|
||||
case PROP_INTENT:
|
||||
gst_lcms_set_intent (lcms, (GstLcmsIntent) g_value_get_enum (value));
|
||||
break;
|
||||
case PROP_LOOKUP_METHOD:
|
||||
gst_lcms_set_lookup_method (lcms,
|
||||
(GstLcmsLookupMethod) g_value_get_enum (value));
|
||||
break;
|
||||
case PROP_PRESERVE_BLACK:
|
||||
lcms->preserve_black = g_value_get_boolean (value);
|
||||
break;
|
||||
case PROP_EMBEDDED_PROFILE:
|
||||
lcms->embeddedprofiles = g_value_get_boolean (value);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_lcms_get_property (GObject * object, guint prop_id, GValue * value,
|
||||
GParamSpec * pspec)
|
||||
{
|
||||
GstLcms *lcms = GST_LCMS (object);
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_SRC_FILE:
|
||||
g_value_set_string (value, lcms->inp_profile_filename);
|
||||
break;
|
||||
case PROP_DST_FILE:
|
||||
g_value_set_string (value, lcms->dst_profile_filename);
|
||||
break;
|
||||
case PROP_INTENT:
|
||||
g_value_set_enum (value, gst_lcms_get_intent (lcms));
|
||||
break;
|
||||
case PROP_LOOKUP_METHOD:
|
||||
g_value_set_enum (value, gst_lcms_get_lookup_method (lcms));
|
||||
break;
|
||||
case PROP_PRESERVE_BLACK:
|
||||
g_value_set_boolean (value, lcms->preserve_black);
|
||||
break;
|
||||
case PROP_EMBEDDED_PROFILE:
|
||||
g_value_set_boolean (value, lcms->embeddedprofiles);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static GstStateChangeReturn
|
||||
gst_lcms_change_state (GstElement * element, GstStateChange transition)
|
||||
{
|
||||
GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
|
||||
GstLcms *lcms = GST_LCMS (element);
|
||||
|
||||
switch (transition) {
|
||||
case GST_STATE_CHANGE_NULL_TO_READY:
|
||||
GST_DEBUG_OBJECT (lcms, "GST_STATE_CHANGE_NULL_TO_READY");
|
||||
gst_lcms_get_ready (lcms);
|
||||
break;
|
||||
case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
|
||||
{
|
||||
if (!lcms->cms_inp_profile) {
|
||||
if (!lcms->cms_dst_profile) {
|
||||
GST_WARNING_OBJECT (lcms,
|
||||
"No input or output ICC profile specified, falling back to passthrough!");
|
||||
gst_base_transform_set_passthrough (GST_BASE_TRANSFORM (lcms), TRUE);
|
||||
GST_BASE_TRANSFORM_CLASS (GST_LCMS_GET_CLASS
|
||||
(lcms))->transform_ip_on_passthrough = lcms->embeddedprofiles;
|
||||
return GST_STATE_CHANGE_SUCCESS;
|
||||
}
|
||||
lcms->cms_inp_profile = cmsCreate_sRGBProfile ();
|
||||
GST_INFO_OBJECT (lcms,
|
||||
"No input profile specified, falling back to sRGB");
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (ret == GST_STATE_CHANGE_SUCCESS)
|
||||
ret =
|
||||
GST_ELEMENT_CLASS (gst_lcms_parent_class)->change_state (element,
|
||||
transition);
|
||||
|
||||
switch (transition) {
|
||||
case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
|
||||
case GST_STATE_CHANGE_PAUSED_TO_READY:
|
||||
break;
|
||||
case GST_STATE_CHANGE_READY_TO_NULL:
|
||||
gst_lcms_cleanup_cms (lcms);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_lcms_get_ready (GstLcms * lcms)
|
||||
{
|
||||
if (lcms->inp_profile_filename) {
|
||||
lcms->cms_inp_profile =
|
||||
cmsOpenProfileFromFile (lcms->inp_profile_filename, "r");
|
||||
if (!lcms->cms_inp_profile)
|
||||
GST_ERROR_OBJECT (lcms, "Couldn't parse input ICC profile '%s'",
|
||||
lcms->inp_profile_filename);
|
||||
else
|
||||
GST_DEBUG_OBJECT (lcms, "Successfully opened input ICC profile '%s'",
|
||||
lcms->inp_profile_filename);
|
||||
}
|
||||
|
||||
if (lcms->dst_profile_filename) {
|
||||
lcms->cms_dst_profile =
|
||||
cmsOpenProfileFromFile (lcms->dst_profile_filename, "r");
|
||||
if (!lcms->cms_dst_profile)
|
||||
GST_ERROR_OBJECT (lcms,
|
||||
"Couldn't parse destination ICC profile '%s'",
|
||||
lcms->dst_profile_filename);
|
||||
else
|
||||
GST_DEBUG_OBJECT (lcms, "Successfully opened output ICC profile '%s'",
|
||||
lcms->dst_profile_filename);
|
||||
}
|
||||
|
||||
if (lcms->lookup_method != GST_LCMS_LOOKUP_METHOD_UNCACHED) {
|
||||
gst_lcms_init_lookup_table (lcms);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_lcms_cleanup_cms (GstLcms * lcms)
|
||||
{
|
||||
if (lcms->cms_inp_profile) {
|
||||
cmsCloseProfile (lcms->cms_inp_profile);
|
||||
lcms->cms_inp_profile = NULL;
|
||||
}
|
||||
if (lcms->cms_dst_profile) {
|
||||
cmsCloseProfile (lcms->cms_dst_profile);
|
||||
lcms->cms_dst_profile = NULL;
|
||||
}
|
||||
if (lcms->cms_transform) {
|
||||
cmsDeleteTransform (lcms->cms_transform);
|
||||
lcms->cms_transform = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_lcms_init_lookup_table (GstLcms * lcms)
|
||||
{
|
||||
guint32 p;
|
||||
const guint32 color_max = 0x01000000;
|
||||
|
||||
if (lcms->color_lut)
|
||||
g_free (lcms->color_lut);
|
||||
|
||||
lcms->color_lut = g_new (guint32, color_max);
|
||||
|
||||
if (lcms->color_lut == NULL) {
|
||||
GST_ELEMENT_ERROR (lcms, RESOURCE, FAILED, ("LUT alloc failed"),
|
||||
("Unable to open allocate memory for lookup table!"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (lcms->lookup_method == GST_LCMS_LOOKUP_METHOD_PRECALCULATED) {
|
||||
cmsHTRANSFORM hTransform;
|
||||
hTransform =
|
||||
cmsCreateTransform (lcms->cms_inp_profile, TYPE_RGB_8,
|
||||
lcms->cms_dst_profile, TYPE_RGB_8, lcms->intent, 0);
|
||||
/*FIXME use cmsFLAGS_COPY_ALPHA when new lcms2 2.8 release is available */
|
||||
for (p = 0; p < color_max; p++)
|
||||
cmsDoTransform (hTransform, (const cmsUInt32Number *) &p,
|
||||
&lcms->color_lut[p], 1);
|
||||
cmsDeleteTransform (hTransform);
|
||||
GST_DEBUG_OBJECT (lcms, "writing lookup table finished");
|
||||
} else if (lcms->lookup_method == GST_LCMS_LOOKUP_METHOD_CACHED) {
|
||||
memset (lcms->color_lut, 0xAA, color_max * sizeof (guint32));
|
||||
GST_DEBUG_OBJECT (lcms, "initialized empty lookup table for caching");
|
||||
}
|
||||
if (lcms->preserve_black)
|
||||
lcms->color_lut[0] = 0x000000;
|
||||
}
|
||||
|
||||
static cmsUInt32Number
|
||||
gst_lcms_cms_format_from_gst (GstVideoFormat gst_format)
|
||||
{
|
||||
cmsUInt32Number cms_format = 0;
|
||||
switch (gst_format) {
|
||||
case GST_VIDEO_FORMAT_ARGB:
|
||||
case GST_VIDEO_FORMAT_xRGB:
|
||||
cms_format = TYPE_ARGB_8;
|
||||
break;
|
||||
case GST_VIDEO_FORMAT_xBGR:
|
||||
case GST_VIDEO_FORMAT_ABGR:
|
||||
cms_format = TYPE_ABGR_8;
|
||||
break;
|
||||
case GST_VIDEO_FORMAT_BGRA:
|
||||
case GST_VIDEO_FORMAT_BGRx:
|
||||
cms_format = TYPE_BGRA_8;
|
||||
break;
|
||||
case GST_VIDEO_FORMAT_BGR:
|
||||
cms_format = TYPE_BGR_8;
|
||||
break;
|
||||
case GST_VIDEO_FORMAT_RGBA:
|
||||
case GST_VIDEO_FORMAT_RGBx:
|
||||
cms_format = TYPE_RGBA_8;
|
||||
break;
|
||||
case GST_VIDEO_FORMAT_RGB:
|
||||
cms_format = TYPE_RGB_8;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return cms_format;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_lcms_set_info (GstVideoFilter * vfilter, GstCaps * incaps,
|
||||
GstVideoInfo * in_info, GstCaps * outcaps, GstVideoInfo * out_info)
|
||||
{
|
||||
GstLcms *lcms = GST_LCMS (vfilter);
|
||||
|
||||
GST_DEBUG_OBJECT (lcms,
|
||||
"setting caps: in %" GST_PTR_FORMAT " out %" GST_PTR_FORMAT, incaps,
|
||||
outcaps);
|
||||
|
||||
lcms->cms_inp_format =
|
||||
gst_lcms_cms_format_from_gst (GST_VIDEO_INFO_FORMAT (in_info));
|
||||
lcms->cms_dst_format =
|
||||
gst_lcms_cms_format_from_gst (GST_VIDEO_INFO_FORMAT (out_info));
|
||||
|
||||
if (gst_base_transform_is_passthrough (GST_BASE_TRANSFORM (lcms)))
|
||||
return TRUE;
|
||||
|
||||
if (!lcms->cms_inp_format || !lcms->cms_dst_format)
|
||||
goto invalid_caps;
|
||||
|
||||
if (lcms->cms_inp_format == lcms->cms_dst_format
|
||||
&& lcms->lookup_method != GST_LCMS_LOOKUP_METHOD_UNCACHED) {
|
||||
gst_base_transform_set_in_place (GST_BASE_TRANSFORM (lcms), TRUE);
|
||||
} else
|
||||
gst_base_transform_set_in_place (GST_BASE_TRANSFORM (lcms), FALSE);
|
||||
|
||||
gst_lcms_create_transform (lcms);
|
||||
lcms->process = gst_lcms_process_rgb;
|
||||
|
||||
return TRUE;
|
||||
|
||||
invalid_caps:
|
||||
{
|
||||
GST_ERROR_OBJECT (lcms, "Invalid caps: %" GST_PTR_FORMAT, incaps);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_lcms_create_transform (GstLcms * lcms)
|
||||
{
|
||||
if (!lcms->cms_dst_profile) {
|
||||
lcms->cms_dst_profile = cmsCreate_sRGBProfile ();
|
||||
GST_INFO_OBJECT (lcms, "No output profile specified, falling back to sRGB");
|
||||
}
|
||||
lcms->cms_transform =
|
||||
cmsCreateTransform (lcms->cms_inp_profile, lcms->cms_inp_format,
|
||||
lcms->cms_dst_profile, lcms->cms_dst_format, lcms->intent, 0);
|
||||
if (lcms->cms_transform) {
|
||||
GST_DEBUG_OBJECT (lcms, "created transformation format=%i->%i",
|
||||
lcms->cms_inp_format, lcms->cms_dst_format);
|
||||
} else {
|
||||
GST_WARNING_OBJECT (lcms,
|
||||
"couldn't create transformation format=%i->%i, fallback to passthrough!",
|
||||
lcms->cms_inp_format, lcms->cms_dst_format);
|
||||
gst_base_transform_set_passthrough (GST_BASE_TRANSFORM (lcms), TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_lcms_sink_event (GstBaseTransform * trans, GstEvent * event)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
GstLcms *lcms = GST_LCMS (trans);
|
||||
|
||||
switch (GST_EVENT_TYPE (event)) {
|
||||
case GST_EVENT_TAG:
|
||||
{
|
||||
if (lcms->embeddedprofiles) {
|
||||
GstTagList *taglist = NULL;
|
||||
/* icc profiles might be embedded in attachments */
|
||||
gst_event_parse_tag (event, &taglist);
|
||||
gst_lcms_handle_tags (lcms, taglist);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
ret =
|
||||
GST_BASE_TRANSFORM_CLASS (gst_lcms_parent_class)->sink_event (trans,
|
||||
event);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_lcms_handle_tag_sample (GstLcms * lcms, GstSample * sample)
|
||||
{
|
||||
GstBuffer *buf;
|
||||
const GstStructure *structure;
|
||||
|
||||
buf = gst_sample_get_buffer (sample);
|
||||
structure = gst_sample_get_info (sample);
|
||||
|
||||
if (!buf || !structure)
|
||||
return;
|
||||
|
||||
if (gst_structure_has_name (structure, "application/vnd.iccprofile")) {
|
||||
if (!lcms->inp_profile_filename
|
||||
&& lcms->lookup_method != GST_LCMS_LOOKUP_METHOD_UNCACHED) {
|
||||
GstMapInfo map;
|
||||
const gchar *icc_name;
|
||||
icc_name = gst_structure_get_string (structure, "icc-name");
|
||||
gst_buffer_map (buf, &map, GST_MAP_READ);
|
||||
lcms->cms_inp_profile = cmsOpenProfileFromMem (map.data, map.size);
|
||||
gst_buffer_unmap (buf, &map);
|
||||
if (!lcms->cms_inp_profile)
|
||||
GST_WARNING_OBJECT (lcms,
|
||||
"Couldn't parse embedded input ICC profile '%s'", icc_name);
|
||||
else {
|
||||
GST_DEBUG_OBJECT (lcms,
|
||||
"Successfully opened embedded input ICC profile '%s'", icc_name);
|
||||
if (lcms->cms_inp_format) {
|
||||
gst_lcms_create_transform (lcms);
|
||||
gst_lcms_init_lookup_table (lcms);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
GST_DEBUG_OBJECT (lcms,
|
||||
"disregarding embedded ICC profile because input profile file was explicitly specified");
|
||||
}
|
||||
} else
|
||||
GST_DEBUG_OBJECT (lcms, "attachment is not an ICC profile");
|
||||
}
|
||||
|
||||
static void
|
||||
gst_lcms_handle_tags (GstLcms * lcms, GstTagList * taglist)
|
||||
{
|
||||
guint tag_size;
|
||||
|
||||
if (!taglist)
|
||||
return;
|
||||
|
||||
tag_size = gst_tag_list_get_tag_size (taglist, GST_TAG_ATTACHMENT);
|
||||
if (tag_size > 0) {
|
||||
guint index;
|
||||
GstSample *sample;
|
||||
for (index = 0; index < tag_size; index++) {
|
||||
if (gst_tag_list_get_sample_index (taglist, GST_TAG_ATTACHMENT, index,
|
||||
&sample)) {
|
||||
gst_lcms_handle_tag_sample (lcms, sample);
|
||||
gst_sample_unref (sample);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static GstFlowReturn
|
||||
gst_lcms_transform_frame_ip (GstVideoFilter * vfilter, GstVideoFrame * inframe)
|
||||
{
|
||||
GstLcms *lcms = GST_LCMS (vfilter);
|
||||
if (!gst_base_transform_is_passthrough (GST_BASE_TRANSFORM (lcms)))
|
||||
lcms->process (lcms, inframe, NULL);
|
||||
return GST_FLOW_OK;
|
||||
}
|
||||
|
||||
static GstFlowReturn
|
||||
gst_lcms_transform_frame (GstVideoFilter * vfilter, GstVideoFrame * inframe,
|
||||
GstVideoFrame * outframe)
|
||||
{
|
||||
GstLcms *lcms = GST_LCMS (vfilter);
|
||||
if (!gst_base_transform_is_passthrough (GST_BASE_TRANSFORM (lcms)))
|
||||
lcms->process (lcms, inframe, outframe);
|
||||
return GST_FLOW_OK;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_lcms_process_rgb (GstLcms * lcms, GstVideoFrame * inframe,
|
||||
GstVideoFrame * outframe)
|
||||
{
|
||||
gint height;
|
||||
gint width, in_stride, out_stride;
|
||||
gint in_pixel_stride, out_pixel_stride;
|
||||
gint in_offsets[4], out_offsets[4];
|
||||
guint8 *in_data, *out_data;
|
||||
gint i, j;
|
||||
gint in_row_wrap, out_row_wrap;
|
||||
guint8 alpha = 0;
|
||||
|
||||
in_data = (guint8 *) GST_VIDEO_FRAME_PLANE_DATA (inframe, 0);
|
||||
in_stride = GST_VIDEO_FRAME_PLANE_STRIDE (inframe, 0);
|
||||
width = GST_VIDEO_FRAME_COMP_WIDTH (inframe, 0);
|
||||
height = GST_VIDEO_FRAME_COMP_HEIGHT (inframe, 0);
|
||||
in_pixel_stride = GST_VIDEO_FRAME_COMP_PSTRIDE (inframe, 0);
|
||||
|
||||
in_offsets[0] = GST_VIDEO_FRAME_COMP_OFFSET (inframe, 0);
|
||||
in_offsets[1] = GST_VIDEO_FRAME_COMP_OFFSET (inframe, 1);
|
||||
in_offsets[2] = GST_VIDEO_FRAME_COMP_OFFSET (inframe, 2);
|
||||
in_offsets[3] = GST_VIDEO_FRAME_COMP_OFFSET (inframe, 3);
|
||||
|
||||
if (outframe) {
|
||||
if (width != GST_VIDEO_FRAME_COMP_WIDTH (outframe, 0)
|
||||
|| height != GST_VIDEO_FRAME_COMP_HEIGHT (outframe, 0)) {
|
||||
GST_WARNING_OBJECT (lcms,
|
||||
"can't transform, input dimensions != output dimensions!");
|
||||
return;
|
||||
}
|
||||
out_data = (guint8 *) GST_VIDEO_FRAME_PLANE_DATA (outframe, 0);
|
||||
out_stride = GST_VIDEO_FRAME_PLANE_STRIDE (outframe, 0);
|
||||
out_pixel_stride = GST_VIDEO_FRAME_COMP_PSTRIDE (outframe, 0);
|
||||
out_offsets[0] = GST_VIDEO_FRAME_COMP_OFFSET (inframe, 0);
|
||||
out_offsets[1] = GST_VIDEO_FRAME_COMP_OFFSET (inframe, 1);
|
||||
out_offsets[2] = GST_VIDEO_FRAME_COMP_OFFSET (inframe, 2);
|
||||
out_offsets[3] = GST_VIDEO_FRAME_COMP_OFFSET (inframe, 3);
|
||||
GST_LOG_OBJECT (lcms,
|
||||
"transforming frame (%ix%i) stride=%i->%i pixel_stride=%i->%i format=%s->%s",
|
||||
width, height, in_stride, out_stride, in_pixel_stride, out_pixel_stride,
|
||||
gst_video_format_to_string (inframe->info.finfo->format),
|
||||
gst_video_format_to_string (outframe->info.finfo->format));
|
||||
} else { /* in-place transformation */
|
||||
GST_LOG_OBJECT (lcms,
|
||||
"transforming frame IN-PLACE (%ix%i) pixel_stride=%i format=%s", width,
|
||||
height, in_pixel_stride,
|
||||
gst_video_format_to_string (inframe->info.finfo->format));
|
||||
out_data = in_data;
|
||||
out_stride = in_stride;
|
||||
out_pixel_stride = in_pixel_stride;
|
||||
out_offsets[0] = in_offsets[0];
|
||||
out_offsets[1] = in_offsets[1];
|
||||
out_offsets[2] = in_offsets[2];
|
||||
out_offsets[3] = in_offsets[3];
|
||||
}
|
||||
|
||||
in_row_wrap = in_stride - in_pixel_stride * width;
|
||||
out_row_wrap = out_stride - out_pixel_stride * width;
|
||||
|
||||
if (lcms->lookup_method == GST_LCMS_LOOKUP_METHOD_UNCACHED) {
|
||||
if (!GST_VIDEO_FORMAT_INFO_HAS_ALPHA (inframe->info.finfo)
|
||||
&& !lcms->preserve_black) {
|
||||
GST_DEBUG_OBJECT (lcms,
|
||||
"GST_LCMS_LOOKUP_METHOD_UNCACHED WITHOUT alpha AND WITHOUT preserve-black -> picture-at-once transformation!");
|
||||
cmsDoTransformStride (lcms->cms_transform, in_data, out_data,
|
||||
height * width, out_pixel_stride);
|
||||
} else {
|
||||
GST_DEBUG_OBJECT (lcms,
|
||||
"GST_LCMS_LOOKUP_METHOD_UNCACHED WITH alpha or preserve-black -> pixel-by-pixel transformation!");
|
||||
for (i = 0; i < height; i++) {
|
||||
for (j = 0; j < width; j++) {
|
||||
if (GST_VIDEO_FORMAT_INFO_HAS_ALPHA (inframe->info.finfo))
|
||||
alpha = in_data[in_offsets[3]];
|
||||
if (lcms->preserve_black && (in_data[in_offsets[0]] == 0x00)
|
||||
&& (in_data[in_offsets[1]] == 0x00)
|
||||
&& (in_data[in_offsets[2]] == 0x0))
|
||||
out_data[out_offsets[0]] = out_data[out_offsets[1]] =
|
||||
out_data[out_offsets[2]] = 0x00;
|
||||
else
|
||||
cmsDoTransformStride (lcms->cms_transform, in_data, out_data, 1,
|
||||
in_pixel_stride);
|
||||
if (alpha)
|
||||
out_data[in_offsets[3]] = alpha;
|
||||
in_data += in_pixel_stride;
|
||||
out_data += out_pixel_stride;
|
||||
}
|
||||
in_data += in_row_wrap;
|
||||
out_data += out_row_wrap;
|
||||
}
|
||||
}
|
||||
} else if (lcms->lookup_method == GST_LCMS_LOOKUP_METHOD_PRECALCULATED) {
|
||||
guint32 color, new_color;
|
||||
GST_LOG_OBJECT (lcms, "GST_LCMS_LOOKUP_METHOD_PRECALCULATED");
|
||||
for (i = 0; i < height; i++) {
|
||||
for (j = 0; j < width; j++) {
|
||||
color =
|
||||
in_data[in_offsets[0]] |
|
||||
in_data[in_offsets[1]] << 0x08 | in_data[in_offsets[2]] << 0x10;
|
||||
new_color = lcms->color_lut[color];
|
||||
out_data[out_offsets[0]] = (new_color & 0x0000FF) >> 0x00;
|
||||
out_data[out_offsets[1]] = (new_color & 0x00FF00) >> 0x08;
|
||||
out_data[out_offsets[2]] = (new_color & 0xFF0000) >> 0x10;
|
||||
GST_TRACE_OBJECT (lcms,
|
||||
"(%i:%i)@%p original color 0x%08X (dest was 0x%08X)", i, j, in_data,
|
||||
color, new_color);
|
||||
if (GST_VIDEO_FORMAT_INFO_HAS_ALPHA (inframe->info.finfo)) {
|
||||
out_data[in_offsets[3]] = in_data[out_offsets[3]];
|
||||
}
|
||||
in_data += in_pixel_stride;
|
||||
out_data += out_pixel_stride;
|
||||
}
|
||||
in_data += in_row_wrap;
|
||||
out_data += out_row_wrap;
|
||||
}
|
||||
} else if (lcms->lookup_method == GST_LCMS_LOOKUP_METHOD_CACHED) {
|
||||
guint32 color, new_color;
|
||||
GST_LOG_OBJECT (lcms, "GST_LCMS_LOOKUP_METHOD_CACHED");
|
||||
for (i = 0; i < height; i++) {
|
||||
for (j = 0; j < width; j++) {
|
||||
if (GST_VIDEO_FORMAT_INFO_HAS_ALPHA (inframe->info.finfo))
|
||||
alpha = in_data[in_offsets[3]];
|
||||
color =
|
||||
in_data[in_offsets[0]] |
|
||||
in_data[in_offsets[1]] << 0x08 | in_data[in_offsets[2]] << 0x10;
|
||||
new_color = lcms->color_lut[color];
|
||||
if (new_color == 0xAAAAAAAA) {
|
||||
cmsDoTransform (lcms->cms_transform, in_data, out_data, 1);
|
||||
new_color =
|
||||
out_data[out_offsets[0]] |
|
||||
out_data[out_offsets[1]] << 0x08 |
|
||||
out_data[out_offsets[2]] << 0x10;
|
||||
GST_OBJECT_LOCK (lcms);
|
||||
lcms->color_lut[color] = new_color;
|
||||
GST_OBJECT_UNLOCK (lcms);
|
||||
GST_TRACE_OBJECT (lcms, "cached color 0x%08X -> 0x%08X", color,
|
||||
new_color);
|
||||
} else {
|
||||
out_data[out_offsets[0]] = (new_color & 0x0000FF) >> 0x00;
|
||||
out_data[out_offsets[1]] = (new_color & 0x00FF00) >> 0x08;
|
||||
out_data[out_offsets[2]] = (new_color & 0xFF0000) >> 0x10;
|
||||
}
|
||||
if (alpha) {
|
||||
out_data[in_offsets[3]] = alpha;
|
||||
}
|
||||
in_data += in_pixel_stride;
|
||||
out_data += out_pixel_stride;
|
||||
}
|
||||
in_data += in_row_wrap;
|
||||
out_data += out_row_wrap;
|
||||
}
|
||||
}
|
||||
}
|
102
ext/colormanagement/gstlcms.h
Normal file
102
ext/colormanagement/gstlcms.h
Normal file
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
* GStreamer gstreamer-lcms
|
||||
* Copyright (C) 2016 Andreas Frisch <fraxinas@dreambox.guru>
|
||||
*
|
||||
* gstlcms.h
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 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 Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __GST_LCMS_H__
|
||||
#define __GST_LCMS_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <gst/video/video.h>
|
||||
#include <gst/video/gstvideofilter.h>
|
||||
|
||||
#include <lcms2.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef enum
|
||||
{
|
||||
GST_LCMS_INTENT_PERCEPTUAL = 0,
|
||||
GST_LCMS_INTENT_RELATIVE_COLORIMETRIC,
|
||||
GST_LCMS_INTENT_SATURATION,
|
||||
GST_LCMS_INTENT_ABSOLUTE_COLORIMETRIC,
|
||||
} GstLcmsIntent;
|
||||
|
||||
#define GST_TYPE_LCMS_INTENT (gst_lcms_intent_get_type ())
|
||||
|
||||
typedef enum
|
||||
{
|
||||
GST_LCMS_LOOKUP_METHOD_UNCACHED = 0,
|
||||
GST_LCMS_LOOKUP_METHOD_PRECALCULATED,
|
||||
GST_LCMS_LOOKUP_METHOD_CACHED,
|
||||
GST_LCMS_LOOKUP_METHOD_FILE,
|
||||
} GstLcmsLookupMethod;
|
||||
|
||||
#define GST_TYPE_LCMS_LOOKUP_METHOD (gst_lcms_lookup_method_get_type ())
|
||||
|
||||
#define GST_TYPE_LCMS (gst_lcms_get_type())
|
||||
#define GST_LCMS(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_LCMS,GstLcms))
|
||||
#define GST_LCMS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_LCMS,GstLcmsClass))
|
||||
#define GST_LCMS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GST_TYPE_LCMS,GstLcmsClass))
|
||||
#define GST_IS_LCMS(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_LCMS))
|
||||
#define GST_IS_LCMS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_LCMS))
|
||||
|
||||
typedef struct _GstLcms GstLcms;
|
||||
typedef struct _GstLcmsClass GstLcmsClass;
|
||||
|
||||
/**
|
||||
* GstLcms:
|
||||
*
|
||||
* Opaque data structure.
|
||||
*/
|
||||
struct _GstLcms
|
||||
{
|
||||
GstVideoFilter videofilter;
|
||||
|
||||
/* < private > */
|
||||
gboolean embeddedprofiles;
|
||||
GstLcmsIntent intent;
|
||||
GstLcmsLookupMethod lookup_method;
|
||||
|
||||
cmsHPROFILE cms_inp_profile, cms_dst_profile;
|
||||
cmsHTRANSFORM cms_transform;
|
||||
cmsUInt32Number cms_inp_format, cms_dst_format;
|
||||
|
||||
gchar *inp_profile_filename;
|
||||
gchar *dst_profile_filename;
|
||||
|
||||
guint32 *color_lut;
|
||||
|
||||
gboolean preserve_black;
|
||||
|
||||
void (*process) (GstLcms * lcms, GstVideoFrame * inframe,
|
||||
GstVideoFrame * outframe);
|
||||
};
|
||||
|
||||
struct _GstLcmsClass
|
||||
{
|
||||
GstVideoFilterClass parent_class;
|
||||
};
|
||||
|
||||
G_GNUC_INTERNAL GType gst_lcms_get_type (void);
|
||||
G_GNUC_INTERNAL GType gst_lcms_intent_get_type (void);
|
||||
|
||||
G_END_DECLS
|
||||
#endif /* __GST_LCMS_H__ */
|
Loading…
Reference in a new issue