1 /* Turn CMC to LCh
2  *
3  * 15/11/94 JC
4  *	- error messages added
5  *	- memory leak fixed
6  * 16/11/94 JC
7  *	- uses im_wrap_oneonebuf() now
8  * 2/11/09
9  * 	- gtkdoc
10  * 30/11/09
11  * 	- argh, im_col_make_tables_CMC(); missing, thanks Peter
12  * 19/9/12
13  * 	- redone as a class
14  */
15 
16 /*
17 
18     This file is part of VIPS.
19 
20     VIPS is free software; you can redistribute it and/or modify
21     it under the terms of the GNU Lesser General Public License as published by
22     the Free Software Foundation; either version 2 of the License, or
23     (at your option) any later version.
24 
25     This program is distributed in the hope that it will be useful,
26     but WITHOUT ANY WARRANTY; without even the implied warranty of
27     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
28     GNU Lesser General Public License for more details.
29 
30     You should have received a copy of the GNU Lesser General Public License
31     along with this program; if not, write to the Free Software
32     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
33     02110-1301  USA
34 
35  */
36 
37 /*
38 
39     These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
40 
41  */
42 
43 #ifdef HAVE_CONFIG_H
44 #include <config.h>
45 #endif /*HAVE_CONFIG_H*/
46 #include <vips/intl.h>
47 
48 #include <stdio.h>
49 #include <math.h>
50 
51 #include <vips/vips.h>
52 
53 #include "pcolour.h"
54 
55 /* Arrays for lookup tables for the inverse function.
56  */
57 static float LI[1001];
58 static float CI[3001];
59 static float hI[101][361];
60 
61 typedef VipsColourTransform VipsCMC2LCh;
62 typedef VipsColourTransformClass VipsCMC2LChClass;
63 
64 G_DEFINE_TYPE( VipsCMC2LCh, vips_CMC2LCh, VIPS_TYPE_COLOUR_TRANSFORM );
65 
66 /* Generate LI (inverse) tables.
67  */
68 static void
make_LI(void)69 make_LI( void )
70 {
71 	int i;
72 	float Ll[1001];
73 
74 	for( i = 0; i < 1001; i++ )
75 		Ll[i] = vips_col_L2Lcmc( i / 10.0 );
76 
77 	for( i = 0; i < 1001; i++ ) {
78 		int j;
79 
80 		/* Must be 1000, since j will be +1 on exit.
81 		 */
82 		for( j = 0; j < 1000 && Ll[j] <= i / 10.0; j++ )
83 			;
84 
85 		LI[i] = (j - 1) / 10.0 +
86 			(i / 10.0 - Ll[j - 1]) / ((Ll[j] - Ll[j - 1]) * 10.0);
87 	}
88 }
89 
90 /* Generate Ccmc table.
91  */
92 static void
make_CI(void)93 make_CI( void )
94 {
95 	int i;
96 	float Cl[3001];
97 
98 	for( i = 0; i < 3001; i++ )
99 		Cl[i] = vips_col_C2Ccmc( i / 10.0 );
100 
101 	for( i = 0; i < 3001; i++ ) {
102 		int j;
103 
104 		/* Must be 3000, since j will be +1 on exit.
105 		 */
106 		for( j = 0; j < 3000 && Cl[j] <= i / 10.0; j++ )
107 			;
108 
109 		CI[i] = (j - 1) / 10.0 +
110 			(i / 10.0 - Cl[j - 1]) / ((Cl[j] - Cl[j - 1]) * 10.0);
111 	}
112 }
113 
114 /* The difficult one: hcmc.
115  */
116 static void
make_hI(void)117 make_hI( void )
118 {
119 	int i, j;
120 	float hl[101][361];
121 
122 	for( i = 0; i < 361; i++ )
123 		for( j = 0; j < 101; j++ )
124 			hl[j][i] = vips_col_Ch2hcmc( j * 2.0, i );
125 
126 	for( j = 0; j < 101; j++ ) {
127 		for( i = 0; i < 361; i++ ) {
128 			int k;
129 
130 			for( k = 1; k < 360 && hl[j][k] <= i; k++ )
131 				;
132 
133 			hI[j][i] = k - 1 + (i - hl[j][k - 1]) /
134 				(hl[j][k] - hl[j][k - 1]);
135 		}
136 	}
137 }
138 
139 /**
140  * vips_col_Lcmc2L:
141  * @Lcmc: L cmc
142  *
143  * Calculate L from Lcmc using a table. Call vips_col_make_tables_CMC() at
144  * least once before using this function.
145  *
146  * Returns: L*
147  */
148 float
vips_col_Lcmc2L(float Lcmc)149 vips_col_Lcmc2L( float Lcmc )
150 {
151 	int known;
152 
153 	known = VIPS_FLOOR( Lcmc * 10.0 );
154 	known = VIPS_CLIP( 0, known, 999 );
155 
156 	return( LI[known] +
157 		(LI[known + 1] - LI[known]) * (Lcmc * 10.0 - known) );
158 }
159 
160 /**
161  * vips_col_Ccmc2C:
162  * @Ccmc: Ccmc
163  *
164  * Calculate C from Ccmc using a table.
165  * Call vips_col_make_tables_CMC() at
166  * least once before using this function.
167  *
168  * Returns: C.
169  */
170 float
vips_col_Ccmc2C(float Ccmc)171 vips_col_Ccmc2C( float Ccmc )
172 {
173 	int known;
174 
175 	known = VIPS_FLOOR( Ccmc * 10.0 );
176 	known = VIPS_CLIP( 0, known, 2999 );
177 
178 	return( CI[known] +
179 		(CI[known + 1] - CI[known]) * (Ccmc * 10.0 - known) );
180 }
181 
182 /**
183  * vips_col_Chcmc2h:
184  * @C: Chroma
185  * @hcmc: Hue cmc (degrees)
186  *
187  * Calculate h from C and hcmc, using a table.
188  * Call vips_col_make_tables_CMC() at
189  * least once before using this function.
190  *
191  * Returns: h.
192  */
193 float
vips_col_Chcmc2h(float C,float hcmc)194 vips_col_Chcmc2h( float C, float hcmc )
195 {
196 	int r;
197 	int known;
198 
199 	/* Which row of the table?
200 	 */
201 	r = (int) ((C + 1.0) / 2.0);
202 	r = VIPS_CLIP( 0, r, 99 );
203 
204 	known = VIPS_FLOOR( hcmc );
205 	known = VIPS_CLIP( 0, known, 359 );
206 
207 	return( hI[r][known] +
208 		(hI[r][(known + 1) % 360] - hI[r][known]) * (hcmc - known) );
209 }
210 
211 static void *
tables_init(void * client)212 tables_init( void *client )
213 {
214 	make_LI();
215 	make_CI();
216 	make_hI();
217 
218 	return( NULL );
219 }
220 
221 /**
222  * vips_col_make_tables_CMC:
223  *
224  * Make the lookup tables for cmc.
225  */
226 void
vips_col_make_tables_CMC(void)227 vips_col_make_tables_CMC( void )
228 {
229 	static GOnce once = G_ONCE_INIT;
230 
231 	VIPS_ONCE( &once, tables_init, NULL );
232 }
233 
234 /* Process a buffer of data.
235  */
236 void
vips_CMC2LCh_line(VipsColour * colour,VipsPel * out,VipsPel ** in,int width)237 vips_CMC2LCh_line( VipsColour *colour, VipsPel *out, VipsPel **in, int width )
238 {
239 	float *p = (float *) in[0];
240 	float *q = (float *) out;
241 
242 	int x;
243 
244 	for( x = 0; x < width; x++ ) {
245 		float Lcmc = p[0];
246 		float Ccmc = p[1];
247 		float hcmc = p[2];
248 
249 		/* Turn from CMC.
250 		 */
251 		float C = vips_col_Ccmc2C( Ccmc );
252 		float h = vips_col_Chcmc2h( C, hcmc );
253 		float L = vips_col_Lcmc2L( Lcmc );
254 
255 		p += 3;
256 
257 		q[0] = L;
258 		q[1] = C;
259 		q[2] = h;
260 
261 		q += 3;
262 	}
263 }
264 
265 static void
vips_CMC2LCh_class_init(VipsCMC2LChClass * class)266 vips_CMC2LCh_class_init( VipsCMC2LChClass *class )
267 {
268 	VipsObjectClass *object_class = (VipsObjectClass *) class;
269 	VipsColourClass *colour_class = VIPS_COLOUR_CLASS( class );
270 
271 	object_class->nickname = "CMC2LCh";
272 	object_class->description = _( "transform LCh to CMC" );
273 
274 	colour_class->process_line = vips_CMC2LCh_line;
275 }
276 
277 static void
vips_CMC2LCh_init(VipsCMC2LCh * CMC2LCh)278 vips_CMC2LCh_init( VipsCMC2LCh *CMC2LCh )
279 {
280 	VipsColour *colour = VIPS_COLOUR( CMC2LCh );
281 
282 	vips_col_make_tables_CMC();
283 	colour->interpretation = VIPS_INTERPRETATION_LCH;
284 }
285 
286 /**
287  * vips_CMC2LCh: (method)
288  * @in: input image
289  * @out: (out): output image
290  * @...: %NULL-terminated list of optional named arguments
291  *
292  * Turn LCh to CMC.
293  *
294  * See also: vips_LCh2CMC().
295  *
296  * Returns: 0 on success, -1 on error
297  */
298 int
vips_CMC2LCh(VipsImage * in,VipsImage ** out,...)299 vips_CMC2LCh( VipsImage *in, VipsImage **out, ... )
300 {
301 	va_list ap;
302 	int result;
303 
304 	va_start( ap, out );
305 	result = vips_call_split( "CMC2LCh", ap, in, out );
306 	va_end( ap );
307 
308 	return( result );
309 }
310 
311