1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
2  *
3  * Copyright (C) 2014 Richard Hughes <richard@hughsie.com>
4  *
5  * Licensed under the GNU Lesser General Public License Version 2.1
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
20  */
21 
22 
23 /**
24  * SECTION:cd-context-lcms
25  * @short_description: Functionality to save per-thread context data for LCMS2
26  */
27 
28 #include "config.h"
29 
30 #include <lcms2.h>
31 #include <lcms2_plugin.h>
32 
33 #include "cd-context-lcms.h"
34 #include "cd-icc.h" /* for the error codes */
35 
36 /*< private >*/
37 #define LCMS_CURVE_PLUGIN_TYPE_REC709	1024
38 
39 /**
40  * cd_context_lcms_get_error:
41  **/
42 static GError **
cd_context_lcms_get_error(gpointer ctx)43 cd_context_lcms_get_error (gpointer ctx)
44 {
45 	return cmsGetContextUserData (ctx);
46 }
47 
48 /**
49  * cd_context_lcms2_error_cb:
50  **/
51 static void
cd_context_lcms2_error_cb(cmsContext context_id,cmsUInt32Number code,const gchar * message)52 cd_context_lcms2_error_cb (cmsContext context_id,
53 			   cmsUInt32Number code,
54 			   const gchar *message)
55 {
56 	gint error_code;
57 	GError **error_ctx;
58 
59 	/* nothing set, must be pre-2.6 */
60 	if (context_id == NULL) {
61 		g_warning ("Error handler called with no context: %s", message);
62 		return;
63 	}
64 
65 	/* there's already one error pending */
66 	error_ctx = cd_context_lcms_get_error (context_id);
67 	if (*error_ctx != NULL) {
68 		g_prefix_error (error_ctx, "%s & ", message);
69 		return;
70 	}
71 
72 	/* convert the first cmsERROR in into a CdIccError */
73 	switch (code) {
74 	case cmsERROR_CORRUPTION_DETECTED:
75 		error_code = CD_ICC_ERROR_CORRUPTION_DETECTED;
76 		break;
77 	case cmsERROR_FILE:
78 	case cmsERROR_READ:
79 	case cmsERROR_SEEK:
80 		error_code = CD_ICC_ERROR_FAILED_TO_OPEN;
81 		break;
82 	case cmsERROR_WRITE:
83 		error_code = CD_ICC_ERROR_FAILED_TO_SAVE;
84 		break;
85 	case cmsERROR_COLORSPACE_CHECK:
86 		error_code = CD_ICC_ERROR_INVALID_COLORSPACE;
87 		break;
88 	case cmsERROR_BAD_SIGNATURE:
89 		error_code = CD_ICC_ERROR_FAILED_TO_PARSE;
90 		break;
91 	case cmsERROR_ALREADY_DEFINED:
92 	case cmsERROR_INTERNAL:
93 	case cmsERROR_NOT_SUITABLE:
94 	case cmsERROR_NULL:
95 	case cmsERROR_RANGE:
96 	case cmsERROR_UNDEFINED:
97 	case cmsERROR_UNKNOWN_EXTENSION:
98 		error_code = CD_ICC_ERROR_INTERNAL;
99 		break;
100 	default:
101 		g_warning ("LCMS2 error code not recognised; please report");
102 		error_code = CD_ICC_ERROR_INTERNAL;
103 	}
104 	error_ctx = cd_context_lcms_get_error (context_id);
105 	g_set_error_literal (error_ctx, CD_ICC_ERROR, error_code, message);
106 }
107 
108 /**
109  * cd_context_lcms_plugins_cb:
110  **/
111 static double
cd_context_lcms_plugins_cb(int type,const double params[],double x)112 cd_context_lcms_plugins_cb (int type, const double params[], double x)
113 {
114 	gdouble val = 0.f;
115 
116 	switch (type) {
117 	case -LCMS_CURVE_PLUGIN_TYPE_REC709:
118 		if (x < params[4])
119 			val = x * params[3];
120 		else
121 			val = params[1] * pow (x, (1.f / params[0])) + params[2];
122 		break;
123 	case LCMS_CURVE_PLUGIN_TYPE_REC709:
124 		if (x <= (params[3] * params[4]))
125 			val = x / params[3];
126 		else
127 			val = pow (((x + params[2]) / params[1]), params[0]);
128 		break;
129 	}
130 	return val;
131 }
132 
133 cmsPluginParametricCurves cd_icc_lcms_plugins = {
134 	{ cmsPluginMagicNumber,			/* 'acpp' */
135 	  2000,					/* minimum version */
136 	  cmsPluginParametricCurveSig,		/* type */
137 	  NULL },				/* no more plugins */
138 	1,					/* number functions */
139 	{LCMS_CURVE_PLUGIN_TYPE_REC709},	/* function types */
140 	{5},					/* parameter count */
141 	cd_context_lcms_plugins_cb		/* evaluator */
142 };
143 
144 /**
145  * cd_context_lcms_new:
146  *
147  * Return value: (transfer full): A new LCMS context
148  **/
149 gpointer
cd_context_lcms_new(void)150 cd_context_lcms_new (void)
151 {
152 	cmsContext ctx;
153 	GError **error_ctx;
154 	error_ctx = g_new0 (GError *, 1);
155 	ctx = cmsCreateContext (NULL, error_ctx);
156 	cmsSetLogErrorHandlerTHR (ctx, cd_context_lcms2_error_cb);
157 	cmsPluginTHR (ctx, &cd_icc_lcms_plugins);
158 	return ctx;
159 }
160 
161 /**
162  * cd_context_lcms_free:
163  **/
164 void
cd_context_lcms_free(gpointer ctx)165 cd_context_lcms_free (gpointer ctx)
166 {
167 	GError **error_ctx;
168 
169 	error_ctx = cmsGetContextUserData (ctx);
170 	g_clear_error (error_ctx);
171 	g_free (error_ctx);
172 
173 	cmsUnregisterPluginsTHR (ctx);
174 	cmsDeleteContext (ctx);
175 }
176 
177 /**
178  * cd_context_lcms_error_clear:
179  **/
180 void
cd_context_lcms_error_clear(gpointer ctx)181 cd_context_lcms_error_clear (gpointer ctx)
182 {
183 	g_clear_error (cd_context_lcms_get_error (ctx));
184 }
185 
186 /**
187  * cd_context_lcms_error_check:
188  **/
189 gboolean
cd_context_lcms_error_check(gpointer ctx,GError ** error)190 cd_context_lcms_error_check (gpointer ctx, GError **error)
191 {
192 	GError **error_ctx;
193 	error_ctx = cd_context_lcms_get_error (ctx);
194 	if (*error_ctx == NULL)
195 		return TRUE;
196 	g_propagate_error (error, *error_ctx);
197 	*error_ctx = NULL;
198 	return FALSE;
199 }
200