xref: /petsc/src/sys/classes/draw/utils/cmap.c (revision 34c645fd3b0199e05bec2fcc32d3597bfeb7f4f2)
1 #include <petscsys.h> /*I "petscsys.h" I*/
2 #include <petscdraw.h>
3 
4 /*
5     Set up a color map, using uniform separation in hue space.
6     Map entries are Red, Green, Blue.
7     Values are "gamma" corrected.
8  */
9 
10 /*
11    Gamma is a monitor dependent value.  The value here is an
12    approximate that gives somewhat better results than Gamma = 1.
13  */
14 static PetscReal Gamma = 2.0;
15 
16 PetscErrorCode PetscDrawUtilitySetGamma(PetscReal g)
17 {
18   PetscFunctionBegin;
19   Gamma = g;
20   PetscFunctionReturn(PETSC_SUCCESS);
21 }
22 
23 static inline double PetscHlsHelper(double m1, double m2, double h)
24 {
25   while (h > 1.0) h -= 1.0;
26   while (h < 0.0) h += 1.0;
27   if (h < 1 / 6.0) return m1 + (m2 - m1) * h * 6;
28   if (h < 1 / 2.0) return m2;
29   if (h < 2 / 3.0) return m1 + (m2 - m1) * (2 / 3.0 - h) * 6;
30   return m1;
31 }
32 
33 static inline void PetscHlsToRgb(double h, double l, double s, double *r, double *g, double *b)
34 {
35   if (s > 0.0) {
36     double m2 = l <= 0.5 ? l * (1.0 + s) : l + s - (l * s);
37     double m1 = 2 * l - m2;
38     *r        = PetscHlsHelper(m1, m2, h + 1 / 3.);
39     *g        = PetscHlsHelper(m1, m2, h);
40     *b        = PetscHlsHelper(m1, m2, h - 1 / 3.);
41   } else {
42     /* ignore hue */
43     *r = *g = *b = l;
44   }
45 }
46 
47 static inline void PetscGammaCorrect(double *r, double *g, double *b)
48 {
49   PetscReal igamma = 1 / Gamma;
50   *r               = (double)PetscPowReal((PetscReal)*r, igamma);
51   *g               = (double)PetscPowReal((PetscReal)*g, igamma);
52   *b               = (double)PetscPowReal((PetscReal)*b, igamma);
53 }
54 
55 static PetscErrorCode PetscDrawCmap_Hue(int mapsize, unsigned char R[], unsigned char G[], unsigned char B[])
56 {
57   int    i;
58   double maxhue = 212.0 / 360, lightness = 0.5, saturation = 1.0;
59 
60   PetscFunctionBegin;
61   for (i = 0; i < mapsize; i++) {
62     double hue = maxhue * (double)i / (mapsize - 1), r, g, b;
63     PetscHlsToRgb(hue, lightness, saturation, &r, &g, &b);
64     PetscGammaCorrect(&r, &g, &b);
65     R[i] = (unsigned char)(255 * PetscMin(r, 1.0));
66     G[i] = (unsigned char)(255 * PetscMin(g, 1.0));
67     B[i] = (unsigned char)(255 * PetscMin(b, 1.0));
68   }
69   PetscFunctionReturn(PETSC_SUCCESS);
70 }
71 
72 static PetscErrorCode PetscDrawCmap_Gray(int mapsize, unsigned char R[], unsigned char G[], unsigned char B[])
73 {
74   PetscFunctionBegin;
75   for (int i = 0; i < mapsize; i++) R[i] = G[i] = B[i] = (unsigned char)((255.0 * i) / (mapsize - 1));
76   PetscFunctionReturn(PETSC_SUCCESS);
77 }
78 
79 static PetscErrorCode PetscDrawCmap_Jet(int mapsize, unsigned char R[], unsigned char G[], unsigned char B[])
80 {
81   int          i;
82   const double knots[] = {0, 1 / 8., 3 / 8., 5 / 8., 7 / 8., 1};
83 
84   PetscFunctionBegin;
85   for (i = 0; i < mapsize; i++) {
86     double u = (double)i / (mapsize - 1);
87     double m, r = 0, g = 0, b = 0;
88     int    k = 0;
89     while (k < 4 && u > knots[k + 1]) k++;
90     m = (u - knots[k]) / (knots[k + 1] - knots[k]);
91     switch (k) {
92     case 0:
93       r = 0;
94       g = 0;
95       b = (m + 1) / 2;
96       break;
97     case 1:
98       r = 0;
99       g = m;
100       b = 1;
101       break;
102     case 2:
103       r = m;
104       g = 1;
105       b = 1 - m;
106       break;
107     case 3:
108       r = 1;
109       g = 1 - m;
110       b = 0;
111       break;
112     case 4:
113       r = 1 - m / 2;
114       g = 0;
115       b = 0;
116       break;
117     }
118     R[i] = (unsigned char)(255 * PetscMin(r, 1.0));
119     G[i] = (unsigned char)(255 * PetscMin(g, 1.0));
120     B[i] = (unsigned char)(255 * PetscMin(b, 1.0));
121   }
122   PetscFunctionReturn(PETSC_SUCCESS);
123 }
124 
125 static PetscErrorCode PetscDrawCmap_Hot(int mapsize, unsigned char R[], unsigned char G[], unsigned char B[])
126 {
127   int          i;
128   const double knots[] = {0, 3 / 8., 3 / 4., 1};
129 
130   PetscFunctionBegin;
131   for (i = 0; i < mapsize; i++) {
132     double u = (double)i / (mapsize - 1);
133     double m, r = 0, g = 0, b = 0;
134     int    k = 0;
135     while (k < 2 && u > knots[k + 1]) k++;
136     m = (u - knots[k]) / (knots[k + 1] - knots[k]);
137     switch (k) {
138     case 0:
139       r = m;
140       g = 0;
141       b = 0;
142       break;
143     case 1:
144       r = 1;
145       g = m;
146       b = 0;
147       break;
148     case 2:
149       r = 1;
150       g = 1;
151       b = m;
152       break;
153     }
154     R[i] = (unsigned char)(255 * PetscMin(r, 1.0));
155     G[i] = (unsigned char)(255 * PetscMin(g, 1.0));
156     B[i] = (unsigned char)(255 * PetscMin(b, 1.0));
157   }
158   PetscFunctionReturn(PETSC_SUCCESS);
159 }
160 
161 static PetscErrorCode PetscDrawCmap_Bone(int mapsize, unsigned char R[], unsigned char G[], unsigned char B[])
162 {
163   PetscFunctionBegin;
164   (void)PetscDrawCmap_Hot(mapsize, R, G, B);
165   for (int i = 0; i < mapsize; i++) {
166     double u = (double)i / (mapsize - 1);
167     double r = (7 * u + B[i] / 255.0) / 8;
168     double g = (7 * u + G[i] / 255.0) / 8;
169     double b = (7 * u + R[i] / 255.0) / 8;
170     R[i]     = (unsigned char)(255 * PetscMin(r, 1.0));
171     G[i]     = (unsigned char)(255 * PetscMin(g, 1.0));
172     B[i]     = (unsigned char)(255 * PetscMin(b, 1.0));
173   }
174   PetscFunctionReturn(PETSC_SUCCESS);
175 }
176 
177 #include "../src/sys/classes/draw/utils/cmap/coolwarm.h"
178 #include "../src/sys/classes/draw/utils/cmap/parula.h"
179 #include "../src/sys/classes/draw/utils/cmap/viridis.h"
180 #include "../src/sys/classes/draw/utils/cmap/plasma.h"
181 #include "../src/sys/classes/draw/utils/cmap/inferno.h"
182 #include "../src/sys/classes/draw/utils/cmap/magma.h"
183 
184 static struct {
185   const char *name;
186   const unsigned char (*data)[3];
187   PetscErrorCode (*cmap)(int, unsigned char[], unsigned char[], unsigned char[]);
188 } PetscDrawCmapTable[] = {
189   {"hue",      NULL,                   PetscDrawCmap_Hue }, /* varying hue with constant lightness and saturation */
190   {"gray",     NULL,                   PetscDrawCmap_Gray}, /* black to white with shades of gray */
191   {"bone",     NULL,                   PetscDrawCmap_Bone}, /* black to white with gray-blue shades */
192   {"jet",      NULL,                   PetscDrawCmap_Jet }, /* rainbow-like colormap from NCSA, University of Illinois */
193   {"hot",      NULL,                   PetscDrawCmap_Hot }, /* black-body radiation */
194   {"coolwarm", PetscDrawCmap_coolwarm, NULL              }, /* ParaView default (Cool To Warm with Diverging interpolation) */
195   {"parula",   PetscDrawCmap_parula,   NULL              }, /* MATLAB (default since R2014b) */
196   {"viridis",  PetscDrawCmap_viridis,  NULL              }, /* matplotlib 1.5 (default since 2.0) */
197   {"plasma",   PetscDrawCmap_plasma,   NULL              }, /* matplotlib 1.5 */
198   {"inferno",  PetscDrawCmap_inferno,  NULL              }, /* matplotlib 1.5 */
199   {"magma",    PetscDrawCmap_magma,    NULL              }, /* matplotlib 1.5 */
200 };
201 
202 PetscErrorCode PetscDrawUtilitySetCmap(const char colormap[], int mapsize, unsigned char R[], unsigned char G[], unsigned char B[])
203 {
204   int         i, j;
205   const char *cmap_name_list[PETSC_STATIC_ARRAY_LENGTH(PetscDrawCmapTable)];
206   PetscInt    id = 0, count = PETSC_STATIC_ARRAY_LENGTH(cmap_name_list);
207   PetscBool   reverse = PETSC_FALSE, brighten = PETSC_FALSE;
208   PetscReal   beta = 0;
209 
210   PetscFunctionBegin;
211   for (i = 0; i < count; i++) cmap_name_list[i] = PetscDrawCmapTable[i].name;
212   if (colormap && colormap[0]) {
213     PetscBool match = PETSC_FALSE;
214     for (id = 0; !match && id < count; id++) PetscCall(PetscStrcasecmp(colormap, cmap_name_list[id], &match));
215     PetscCheck(match, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Colormap '%s' not found", colormap);
216   }
217   PetscCall(PetscOptionsGetEList(NULL, NULL, "-draw_cmap", cmap_name_list, count, &id, NULL));
218   PetscCall(PetscOptionsGetBool(NULL, NULL, "-draw_cmap_reverse", &reverse, NULL));
219   PetscCall(PetscOptionsGetReal(NULL, NULL, "-draw_cmap_brighten", &beta, &brighten));
220   PetscCheck(!brighten || (beta > (PetscReal)-1 && beta < (PetscReal) + 1), PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "brighten parameter %g must be in the range (-1,1)", (double)beta);
221 
222   if (PetscDrawCmapTable[id].cmap) {
223     PetscCall(PetscDrawCmapTable[id].cmap(mapsize, R, G, B));
224   } else {
225     const unsigned char(*rgb)[3] = PetscDrawCmapTable[id].data;
226     PetscCheck(mapsize == 256 - PETSC_DRAW_BASIC_COLORS, PETSC_COMM_SELF, PETSC_ERR_SUP, "Colormap '%s' with size %d not supported", cmap_name_list[id], mapsize);
227     for (i = 0; i < mapsize; i++) {
228       R[i] = rgb[i][0];
229       G[i] = rgb[i][1];
230       B[i] = rgb[i][2];
231     }
232   }
233 
234   if (reverse) {
235     i = 0;
236     j = mapsize - 1;
237     while (i < j) {
238 #define SWAP(a, i, j) \
239   do { \
240     unsigned char t = a[i]; \
241     a[i]            = a[j]; \
242     a[j]            = t; \
243   } while (0)
244       SWAP(R, i, j);
245       SWAP(G, i, j);
246       SWAP(B, i, j);
247 #undef SWAP
248       i++;
249       j--;
250     }
251   }
252 
253   if (brighten) {
254     PetscReal gamma = (beta > 0.0) ? (1 - beta) : (1 / (1 + beta));
255     for (i = 0; i < mapsize; i++) {
256       PetscReal r = PetscPowReal((PetscReal)R[i] / 255, gamma);
257       PetscReal g = PetscPowReal((PetscReal)G[i] / 255, gamma);
258       PetscReal b = PetscPowReal((PetscReal)B[i] / 255, gamma);
259       R[i]        = (unsigned char)(255 * PetscMin(r, (PetscReal)1.0));
260       G[i]        = (unsigned char)(255 * PetscMin(g, (PetscReal)1.0));
261       B[i]        = (unsigned char)(255 * PetscMin(b, (PetscReal)1.0));
262     }
263   }
264   PetscFunctionReturn(PETSC_SUCCESS);
265 }
266