1 /*!
2  * \file lib/raster/color_look.c
3  *
4  * \brief Raster Library - Lookup array of colors
5  *
6  * (C) 1999-2009 by the GRASS Development Team
7  *
8  * This program is free software under the GNU General Public
9  * License (>=v2). Read the file COPYING that comes with GRASS
10  * for details.
11  *
12  * \author USACERL and many others
13  */
14 
15 #include <math.h>
16 
17 #include <grass/gis.h>
18 #include <grass/raster.h>
19 
20 /*!
21  * \brief Lookup an array of colors
22  *
23  * Extracts colors for an array of <i>cell</i> values. The colors
24  * for the <i>n</i> values in the <i>cell</i> array are stored in
25  * the <i>red, green</i>, and <i>blue</i> arrays. The values in the
26  * <i>set</i> array will indicate if the corresponding <i>cell</i>
27  * value has a color or not (1 means it does, 0 means it does not).
28  *
29  * The programmer must allocate the <i>red, green, blue</i>, and
30  * <b>set</b> arrays to be at least dimension <i>n</i>.
31  *
32  * <b>Note:</b> The <i>red, green</i>, and <i>blue</i> intensities
33  * will be in the range 0 - 255.
34  *
35  * Modified to return a color for NULL-values.
36  *
37  * \param cell raster cell value
38  * \param[out] red red value
39  * \param[out] grn green value
40  * \param[out] blu blue value
41  * \param set array which indicates if color is set or not
42  * \param n number of values
43  * \param colors pointer to Colors structure which holds color info
44  */
Rast_lookup_c_colors(const CELL * cell,unsigned char * red,unsigned char * grn,unsigned char * blu,unsigned char * set,int n,struct Colors * colors)45 void Rast_lookup_c_colors(const CELL * cell,
46 			  unsigned char *red, unsigned char *grn,
47 			  unsigned char *blu, unsigned char *set, int n,
48 			  struct Colors *colors)
49 {
50     Rast__organize_colors(colors);	/* make sure the lookup tables are in place */
51 
52     G_zero((char *)set, n * sizeof(unsigned char));
53 
54     /* first lookup the fixed colors */
55     Rast__lookup_colors((void *)cell, red, grn, blu, set, n, colors, 0, 0,
56 			CELL_TYPE);
57 
58     /* now lookup unset colors using the modular rules */
59     Rast__lookup_colors((void *)cell, red, grn, blu, set, n, colors, 1, 0,
60 			CELL_TYPE);
61 }
62 
63 /*!
64  * \brief Lookup an array of colors
65  *
66  * - If the <em>map_type</em> is CELL_TYPE, calls Rast_lookup_colors()
67  * - If the <em>map_type</em> is FCELL_TYPE, calls Rast_lookup_f_colors()
68  * - If the <em>map_type</em> is DCELL_TYPE, calls Rast_lookup_d_colors()
69  *
70  * \param raster raster cell value
71  * \param[out] red red value
72  * \param[out] grn green value
73  * \param[out] blu blue value
74  * \param set array which indicates if color is set or not
75  * \param n number of values
76  * \param colors pointer to Colors structure which holds color info
77  * \param map_type raster type (CELL, FCELL, DCELL)
78  */
Rast_lookup_colors(const void * raster,unsigned char * red,unsigned char * grn,unsigned char * blu,unsigned char * set,int n,struct Colors * colors,RASTER_MAP_TYPE map_type)79 void Rast_lookup_colors(const void *raster,
80 			unsigned char *red, unsigned char *grn,
81 			unsigned char *blu, unsigned char *set, int n,
82 			struct Colors *colors, RASTER_MAP_TYPE map_type)
83 {
84     Rast__organize_colors(colors);	/* make sure the lookup tables are in place */
85     /* in case of float color rules, fp_lookup table is created */
86 
87     G_zero((char *)set, n * sizeof(unsigned char));
88 
89     /* first lookup the fixed colors */
90     Rast__lookup_colors(raster, red, grn, blu, set, n, colors, 0, 0,
91 			map_type);
92 
93     /* now lookup unset colors using the modular rules */
94     Rast__lookup_colors(raster, red, grn, blu, set, n, colors, 1, 0,
95 			map_type);
96 }
97 
98 /*!
99  * \brief Lookup an array of colors (FCELL)
100  *
101  * Converts the <em>n</em> floating-point values in the <em>fcell</em>
102  * array to their <em>r,g,b</em> color components. Embedded
103  * NULL-values are handled properly as well.
104  *
105  * \param fcell raster cell value
106  * \param[out] red red value
107  * \param[out] grn green value
108  * \param[out] blu blue value
109  * \param set array which indicates if color is set or not
110  * \param n number of values
111  * \param colors pointer to Colors structure which holds color info
112  */
Rast_lookup_f_colors(const FCELL * fcell,unsigned char * red,unsigned char * grn,unsigned char * blu,unsigned char * set,int n,struct Colors * colors)113 void Rast_lookup_f_colors(const FCELL * fcell, unsigned char *red,
114 			  unsigned char *grn, unsigned char *blu,
115 			  unsigned char *set, int n, struct Colors *colors)
116 {
117     Rast__organize_colors(colors);	/* make sure the lookup tables are in place */
118     /* in case of float color rules, fp_lookup table is created */
119 
120     G_zero((char *)set, n * sizeof(unsigned char));
121 
122     /* first lookup the fixed colors */
123     Rast__lookup_colors((void *)fcell, red, grn, blu, set, n, colors, 0, 0,
124 			FCELL_TYPE);
125 
126     /* now lookup unset colors using the modular rules */
127     Rast__lookup_colors((void *)fcell, red, grn, blu, set, n, colors, 1, 0,
128 			FCELL_TYPE);
129 }
130 
131 /*!
132  * \brief Lookup an array of colors (DCELL)
133  *
134  * Converts the <em>n</em> double-precision values in the
135  * <em>dcell</em> array to their <em>r,g,b</em> color
136  * components. Embedded NULL-values are handled properly as well.
137  *
138  * \param dcell raster cell value
139  * \param[out] red red value
140  * \param[out] grn green value
141  * \param[out] blu blue value
142  * \param set array which indicates if color is set or not
143  * \param n number of values
144  * \param colors pointer to Colors structure which holds color info
145  */
Rast_lookup_d_colors(const DCELL * dcell,unsigned char * red,unsigned char * grn,unsigned char * blu,unsigned char * set,int n,struct Colors * colors)146 void Rast_lookup_d_colors(const DCELL * dcell, unsigned char *red,
147 			  unsigned char *grn, unsigned char *blu,
148 			  unsigned char *set, int n, struct Colors *colors)
149 {
150     Rast__organize_colors(colors);	/* make sure the lookup tables are in place */
151     /* in case of float color rules, fp_lookup table is created */
152 
153     G_zero((char *)set, n * sizeof(unsigned char));
154 
155     /* first lookup the fixed colors */
156     Rast__lookup_colors((void *)dcell, red, grn, blu, set, n, colors, 0, 0,
157 			DCELL_TYPE);
158 
159     /* now lookup unset colors using the modular rules */
160     Rast__lookup_colors((void *)dcell, red, grn, blu, set, n, colors, 1, 0,
161 			DCELL_TYPE);
162 }
163 
164 
less_or_equal(double x,double y)165 static int less_or_equal(double x, double y)
166 {
167     if (x <= y)
168 	return 1;
169     else
170 	return 0;
171 }
172 
less(double x,double y)173 static int less(double x, double y)
174 {
175     if (x < y)
176 	return 1;
177     else
178 	return 0;
179 }
180 
181 /*!
182  * \brief Lookup an array of colors
183  *
184  * \param raster raster cell value
185  * \param[out] red red value
186  * \param[out] grn green value
187  * \param[out] blu blue value
188  * \param set array which indicates if color is set or not
189  * \param n number of values
190  * \param colors pointer to Colors structure which holds color info
191  * \param mod
192  * \param rules_only
193  * \param data_type raster type (CELL, FCELL, DCELL)
194  */
Rast__lookup_colors(const void * raster,unsigned char * red,unsigned char * grn,unsigned char * blu,unsigned char * set,int n,struct Colors * colors,int mod,int rules_only,RASTER_MAP_TYPE data_type)195 void Rast__lookup_colors(const void *raster, unsigned char *red,
196 			 unsigned char *grn, unsigned char *blu,
197 			 unsigned char *set, int n, struct Colors *colors,
198 			 int mod, int rules_only, RASTER_MAP_TYPE data_type)
199 {
200     struct _Color_Info_ *cp;
201     struct _Color_Rule_ *rule;
202     DCELL dmin, dmax, val, dmod = 0L, shift;
203     CELL cat, min, max;
204     const void *ptr, *last_ptr = NULL;
205     int invert;
206     int found, r, g, b;
207     int cell_type;
208     int lookup, max_ind, min_ind, try;
209     int (*lower)();
210     size_t size = Rast_cell_size(data_type);
211 
212     if (mod)
213 	cp = &colors->modular;
214     else
215 	cp = &colors->fixed;
216 
217     /* rules_only will be true only when called by Rast__organize_colors()
218      * when building the integer lookup talbes from the rules,
219      * so do not shift, invert, use lookup table or modulate cats.
220      * these operations will happen when lookup is called by user code
221      */
222     /* we want min, max for cp, not min, max overall */
223     dmin = cp->min;
224     dmax = cp->max;
225     min = (CELL) dmin;
226     max = (CELL) dmax;
227 
228     cell_type = (data_type == CELL_TYPE);
229 
230     if (rules_only) {
231 	shift = invert = lookup = mod = 0;
232     }
233     else {
234 	if (mod) {
235 	    dmod = dmax - dmin;
236 	    /* for integers color table we make a gap of 1 in order
237 	       to make the same colors as before */
238 	    if (cell_type)
239 		dmod += 1;
240 	}
241 
242 	shift = colors->shift;
243 	invert = colors->invert;
244 	lookup = cp->lookup.active;
245     }
246 
247     ptr = raster;
248 
249     for (; n-- > 0;
250 	 ptr = G_incr_void_ptr(ptr, size),
251 	     red++, grn++, blu++, *set++ = found) {
252 	/* if the cell is the same as last one, use the prev color values */
253 	if (ptr != raster && Rast_raster_cmp(ptr, last_ptr, data_type) == 0) {
254 	    *red = *(red - 1);
255 	    *blu = *(blu - 1);
256 	    *grn = *(grn - 1);
257 	    found = *(set - 1);
258 	    last_ptr = ptr;
259 	    continue;
260 	}
261 	val = Rast_get_d_value(ptr, data_type);
262 	/* DEBUG fprintf (stderr, "val: %.4lf\n", val); */
263 	last_ptr = ptr;
264 
265 	if (*set) {
266 	    found = 1;
267 	    continue;
268 	}
269 
270 	if (Rast_is_null_value(ptr, data_type)) {
271 	    /* returns integers, not unsigned chars */
272 	    Rast_get_null_value_color(&r, &g, &b, colors);
273 	    *red = r;
274 	    *grn = g;
275 	    *blu = b;
276 	    found = 1;
277 	    continue;
278 	}
279 
280 	if (shift && val >= dmin && val <= dmax) {
281 	    val += shift;
282 	    while (val < dmin)
283 		val += dmax - dmin + 1;
284 	    while (val > dmax)
285 		val -= dmax - dmin + 1;
286 	}
287 
288 	/* invert non-null data around midpoint of range [min:max] */
289 	if (invert)
290 	    val = dmin + dmax - val;
291 
292 	if (mod) {
293 	    if (dmod > 0) {
294 		val -= dmin;
295 		while (val < 0)
296 		    val += dmod;
297 		val = val - dmod * floor(val / dmod);
298 		val += dmin;
299 	    }
300 	    else
301 		val = dmin;
302 	}
303 
304 	cat = (CELL) val;
305 
306 	found = 0;
307 
308 	/* for non-null integers  try to look them up in lookup table */
309 	/* note: lookup table exists only for integer maps, and we also must
310 	   check if val is really integer */
311 
312 	if (lookup && ((double)cat - val == 0.)) {
313 	    if (cat >= min && cat <= max) {
314 		cat -= min;
315 		if (cp->lookup.set[cat]) {
316 		    *red = cp->lookup.red[cat];
317 		    *grn = cp->lookup.grn[cat];
318 		    *blu = cp->lookup.blu[cat];
319 		    found = 1;
320 		    /*DEBUG
321 		       fprintf (stderr, "lookup %d %.2lf %d %d %d\n\n", cat, val, *red, *grn, *blu);
322 		     */
323 		}
324 	    }
325 	}
326 
327 	if (found)
328 	    continue;
329 
330 	/* if floating point lookup table is active, look up in there */
331 	if (cp->fp_lookup.active) {
332 	    try = (cp->fp_lookup.nalloc - 1) / 2;
333 	    min_ind = 0;
334 	    max_ind = cp->fp_lookup.nalloc - 2;
335 	    while (1) {
336 		/* when the rule for the interval is NULL, we exclude the end points.
337 		   when it exists, we include the end-points */
338 		if (cp->fp_lookup.rules[try])
339 		    lower = less;
340 		else
341 		    lower = less_or_equal;
342 		/* DEBUG
343 		   fprintf (stderr, "%d %d %d %lf %lf %lf\n", min_ind, try, max_ind,
344 		   cp->fp_lookup.vals[try-1],
345 		   val,
346 		   cp->fp_lookup.vals[try]);
347 		 */
348 
349 		if (lower(cp->fp_lookup.vals[try + 1], val)) {	/* recurse to the second half */
350 		    min_ind = try + 1;
351 		    /* must be still < nalloc-1, since number is within the range */
352 		    try = (max_ind + min_ind) / 2;
353 		    if (min_ind > max_ind) {
354 			rule = NULL;
355 			break;
356 		    }
357 		    continue;
358 		}
359 		if (lower(val, cp->fp_lookup.vals[try])) {	/* recurse to the second half */
360 		    max_ind = try - 1;
361 		    /* must be still >= 0, since number is within the range */
362 		    try = (max_ind + min_ind) / 2;
363 		    if (max_ind < min_ind) {
364 			rule = NULL;
365 			break;
366 		    }
367 		    continue;
368 		}
369 		rule = cp->fp_lookup.rules[try];
370 		break;
371 	    }
372 	}
373 	else {
374 	    /* find the [low:high] rule that applies */
375 	    for (rule = cp->rules; rule; rule = rule->next) {
376 		/* DEBUG
377 		   fprintf (stderr, "%.2lf %.2lf %.2lf\n",
378 		   val, rule->low.value, rule->high.value);
379 		 */
380 		if (rule->low.value <= val && val <= rule->high.value)
381 		    break;
382 	    }
383 	}
384 
385 	/* if found, perform linear interpolation from low to high.
386 	 * else set colors to colors->undef or white if undef not set
387 	 */
388 
389 	if (rule) {
390 	    Rast__interpolate_color_rule(val, red, grn, blu, rule);
391 	    found = 1;
392 	}
393 	if (!found) {
394 	    /* otherwise use default color */
395 	    Rast_get_default_color(&r, &g, &b, colors);
396 	    *red = r;
397 	    *grn = g;
398 	    *blu = b;
399 	}
400 	/* DEBUG
401 	   if (rule)
402 	   fprintf (stderr, "%.2lf %d %d %d   %.2lf %d %d %d \n", rule->low.value , (int)rule->low.red, (int)rule->low.grn, (int)rule->low.blu, rule->high.value, (int)rule->high.red, (int)rule->high.grn, (int)rule->high.blu);
403 	   fprintf (stderr, "rule found %d %.2lf %d %d %d\n\n", cat, val, *red, *grn, *blu);
404 	 */
405     }
406 }
407 
408 /*!
409    \brief Interpolate color rules
410 
411    \param val raster cell value
412    \param[out] red red value
413    \param[out] grn green value
414    \param[out] blu blue value
415    \param rule pointer to _Color_Rule which holds color rules info
416  */
Rast__interpolate_color_rule(DCELL val,unsigned char * red,unsigned char * grn,unsigned char * blu,const struct _Color_Rule_ * rule)417 void Rast__interpolate_color_rule(DCELL val, unsigned char *red,
418 				  unsigned char *grn, unsigned char *blu,
419 				  const struct _Color_Rule_ *rule)
420 {
421     DCELL delta;
422 
423     if ((delta = rule->high.value - rule->low.value)) {
424 	val -= rule->low.value;
425 
426 	*red =
427 	    (int)(val * (double)((int)rule->high.red - (int)rule->low.red) /
428 		  delta)
429 	    + (int)rule->low.red;
430 	*grn =
431 	    (int)(val * (double)((int)rule->high.grn - (int)rule->low.grn) /
432 		  delta)
433 	    + (int)rule->low.grn;
434 	*blu =
435 	    (int)(val * (double)((int)rule->high.blu - (int)rule->low.blu) /
436 		  delta)
437 	    + (int)rule->low.blu;
438     }
439     else {
440 	*red = rule->low.red;
441 	*grn = rule->low.grn;
442 	*blu = rule->low.blu;
443     }
444 }
445