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