Actual source code: cmap.c

  1: #include <petscsys.h>
  2: #include <petscdraw.h>

  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:  */

 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;

 16: /*@
 17:   PetscDrawUtilitySetGamma - Set the monitor gamma-correction value used by the drawing colormap utilities.

 19:   Not Collective

 21:   Input Parameter:
 22: . g - the gamma value; a typical value is 2.0

 24:   Level: developer

 26: .seealso: `PetscDraw`, `PetscDrawUtilitySetCmap()`
 27: @*/
 28: PetscErrorCode PetscDrawUtilitySetGamma(PetscReal g)
 29: {
 30:   PetscFunctionBegin;
 31:   Gamma = g;
 32:   PetscFunctionReturn(PETSC_SUCCESS);
 33: }

 35: static inline double PetscHlsHelper(double m1, double m2, double h)
 36: {
 37:   while (h > 1.0) h -= 1.0;
 38:   while (h < 0.0) h += 1.0;
 39:   if (h < 1 / 6.0) return m1 + (m2 - m1) * h * 6;
 40:   if (h < 1 / 2.0) return m2;
 41:   if (h < 2 / 3.0) return m1 + (m2 - m1) * (2 / 3.0 - h) * 6;
 42:   return m1;
 43: }

 45: static inline void PetscHlsToRgb(double h, double l, double s, double *r, double *g, double *b)
 46: {
 47:   if (s > 0.0) {
 48:     double m2 = l <= 0.5 ? l * (1.0 + s) : l + s - (l * s);
 49:     double m1 = 2 * l - m2;
 50:     *r        = PetscHlsHelper(m1, m2, h + 1 / 3.);
 51:     *g        = PetscHlsHelper(m1, m2, h);
 52:     *b        = PetscHlsHelper(m1, m2, h - 1 / 3.);
 53:   } else {
 54:     /* ignore hue */
 55:     *r = *g = *b = l;
 56:   }
 57: }

 59: static inline void PetscGammaCorrect(double *r, double *g, double *b)
 60: {
 61:   PetscReal igamma = 1 / Gamma;
 62:   *r               = (double)PetscPowReal((PetscReal)*r, igamma);
 63:   *g               = (double)PetscPowReal((PetscReal)*g, igamma);
 64:   *b               = (double)PetscPowReal((PetscReal)*b, igamma);
 65: }

 67: static PetscErrorCode PetscDrawCmap_Hue(int mapsize, unsigned char R[], unsigned char G[], unsigned char B[])
 68: {
 69:   int    i;
 70:   double maxhue = 212.0 / 360, lightness = 0.5, saturation = 1.0;

 72:   PetscFunctionBegin;
 73:   for (i = 0; i < mapsize; i++) {
 74:     double hue = maxhue * (double)i / (mapsize - 1), r, g, b;
 75:     PetscHlsToRgb(hue, lightness, saturation, &r, &g, &b);
 76:     PetscGammaCorrect(&r, &g, &b);
 77:     R[i] = (unsigned char)(255 * PetscMin(r, 1.0));
 78:     G[i] = (unsigned char)(255 * PetscMin(g, 1.0));
 79:     B[i] = (unsigned char)(255 * PetscMin(b, 1.0));
 80:   }
 81:   PetscFunctionReturn(PETSC_SUCCESS);
 82: }

 84: static PetscErrorCode PetscDrawCmap_Gray(int mapsize, unsigned char R[], unsigned char G[], unsigned char B[])
 85: {
 86:   PetscFunctionBegin;
 87:   for (int i = 0; i < mapsize; i++) R[i] = G[i] = B[i] = (unsigned char)((255.0 * i) / (mapsize - 1));
 88:   PetscFunctionReturn(PETSC_SUCCESS);
 89: }

 91: static PetscErrorCode PetscDrawCmap_Jet(int mapsize, unsigned char R[], unsigned char G[], unsigned char B[])
 92: {
 93:   int          i;
 94:   const double knots[] = {0, 1 / 8., 3 / 8., 5 / 8., 7 / 8., 1};

 96:   PetscFunctionBegin;
 97:   for (i = 0; i < mapsize; i++) {
 98:     double u = (double)i / (mapsize - 1);
 99:     double m, r = 0, g = 0, b = 0;
100:     int    k = 0;
101:     while (k < 4 && u > knots[k + 1]) k++;
102:     m = (u - knots[k]) / (knots[k + 1] - knots[k]);
103:     switch (k) {
104:     case 0:
105:       r = 0;
106:       g = 0;
107:       b = (m + 1) / 2;
108:       break;
109:     case 1:
110:       r = 0;
111:       g = m;
112:       b = 1;
113:       break;
114:     case 2:
115:       r = m;
116:       g = 1;
117:       b = 1 - m;
118:       break;
119:     case 3:
120:       r = 1;
121:       g = 1 - m;
122:       b = 0;
123:       break;
124:     case 4:
125:       r = 1 - m / 2;
126:       g = 0;
127:       b = 0;
128:       break;
129:     }
130:     R[i] = (unsigned char)(255 * PetscMin(r, 1.0));
131:     G[i] = (unsigned char)(255 * PetscMin(g, 1.0));
132:     B[i] = (unsigned char)(255 * PetscMin(b, 1.0));
133:   }
134:   PetscFunctionReturn(PETSC_SUCCESS);
135: }

137: static PetscErrorCode PetscDrawCmap_Hot(int mapsize, unsigned char R[], unsigned char G[], unsigned char B[])
138: {
139:   int          i;
140:   const double knots[] = {0, 3 / 8., 3 / 4., 1};

142:   PetscFunctionBegin;
143:   for (i = 0; i < mapsize; i++) {
144:     double u = (double)i / (mapsize - 1);
145:     double m, r = 0, g = 0, b = 0;
146:     int    k = 0;
147:     while (k < 2 && u > knots[k + 1]) k++;
148:     m = (u - knots[k]) / (knots[k + 1] - knots[k]);
149:     switch (k) {
150:     case 0:
151:       r = m;
152:       g = 0;
153:       b = 0;
154:       break;
155:     case 1:
156:       r = 1;
157:       g = m;
158:       b = 0;
159:       break;
160:     case 2:
161:       r = 1;
162:       g = 1;
163:       b = m;
164:       break;
165:     }
166:     R[i] = (unsigned char)(255 * PetscMin(r, 1.0));
167:     G[i] = (unsigned char)(255 * PetscMin(g, 1.0));
168:     B[i] = (unsigned char)(255 * PetscMin(b, 1.0));
169:   }
170:   PetscFunctionReturn(PETSC_SUCCESS);
171: }

173: static PetscErrorCode PetscDrawCmap_Bone(int mapsize, unsigned char R[], unsigned char G[], unsigned char B[])
174: {
175:   PetscFunctionBegin;
176:   (void)PetscDrawCmap_Hot(mapsize, R, G, B);
177:   for (int i = 0; i < mapsize; i++) {
178:     double u = (double)i / (mapsize - 1);
179:     double r = (7 * u + B[i] / 255.0) / 8;
180:     double g = (7 * u + G[i] / 255.0) / 8;
181:     double b = (7 * u + R[i] / 255.0) / 8;
182:     R[i]     = (unsigned char)(255 * PetscMin(r, 1.0));
183:     G[i]     = (unsigned char)(255 * PetscMin(g, 1.0));
184:     B[i]     = (unsigned char)(255 * PetscMin(b, 1.0));
185:   }
186:   PetscFunctionReturn(PETSC_SUCCESS);
187: }

189: #include "../src/sys/classes/draw/utils/cmap/coolwarm.h"
190: #include "../src/sys/classes/draw/utils/cmap/parula.h"
191: #include "../src/sys/classes/draw/utils/cmap/viridis.h"
192: #include "../src/sys/classes/draw/utils/cmap/plasma.h"
193: #include "../src/sys/classes/draw/utils/cmap/inferno.h"
194: #include "../src/sys/classes/draw/utils/cmap/magma.h"

196: static struct {
197:   const char *name;
198:   const unsigned char (*data)[3];
199:   PetscErrorCode (*cmap)(int, unsigned char[], unsigned char[], unsigned char[]);
200: } PetscDrawCmapTable[] = {
201:   {"hue",      NULL,                   PetscDrawCmap_Hue }, /* varying hue with constant lightness and saturation */
202:   {"gray",     NULL,                   PetscDrawCmap_Gray}, /* black to white with shades of gray */
203:   {"bone",     NULL,                   PetscDrawCmap_Bone}, /* black to white with gray-blue shades */
204:   {"jet",      NULL,                   PetscDrawCmap_Jet }, /* rainbow-like colormap from NCSA, University of Illinois */
205:   {"hot",      NULL,                   PetscDrawCmap_Hot }, /* black-body radiation */
206:   {"coolwarm", PetscDrawCmap_coolwarm, NULL              }, /* ParaView default (Cool To Warm with Diverging interpolation) */
207:   {"parula",   PetscDrawCmap_parula,   NULL              }, /* MATLAB (default since R2014b) */
208:   {"viridis",  PetscDrawCmap_viridis,  NULL              }, /* matplotlib 1.5 (default since 2.0) */
209:   {"plasma",   PetscDrawCmap_plasma,   NULL              }, /* matplotlib 1.5 */
210:   {"inferno",  PetscDrawCmap_inferno,  NULL              }, /* matplotlib 1.5 */
211:   {"magma",    PetscDrawCmap_magma,    NULL              }, /* matplotlib 1.5 */
212: };

214: /*@C
215:   PetscDrawUtilitySetCmap - Populate the RGB entries of a colormap from a named palette, honoring options-database
216:   overrides for the colormap name, reversal, and brightness.

218:   Not Collective

220:   Input Parameters:
221: + colormap - the name of the colormap (e.g. `"hue"`, `"gray"`, `"jet"`, `"viridis"`), or `NULL`/empty for the default
222: - mapsize  - the number of colormap entries to fill

224:   Output Parameters:
225: + R - the red channel of length `mapsize`
226: . G - the green channel of length `mapsize`
227: - B - the blue channel of length `mapsize`

229:   Options Database Keys:
230: + -draw_cmap name           - select the colormap by name
231: . -draw_cmap_reverse        - reverse the colormap
232: - -draw_cmap_brighten value - brighten (positive) or darken (negative) the colormap; value must be in `(-1, 1)`

234:   Level: developer

236: .seealso: `PetscDraw`, `PetscDrawUtilitySetGamma()`
237: @*/
238: PetscErrorCode PetscDrawUtilitySetCmap(const char colormap[], int mapsize, unsigned char R[], unsigned char G[], unsigned char B[])
239: {
240:   int         i, j;
241:   const char *cmap_name_list[PETSC_STATIC_ARRAY_LENGTH(PetscDrawCmapTable)];
242:   PetscInt    id = 0, count = PETSC_STATIC_ARRAY_LENGTH(cmap_name_list);
243:   PetscBool   reverse = PETSC_FALSE, brighten = PETSC_FALSE;
244:   PetscReal   beta = 0;

246:   PetscFunctionBegin;
247:   for (i = 0; i < count; i++) cmap_name_list[i] = PetscDrawCmapTable[i].name;
248:   if (colormap && colormap[0]) {
249:     PetscBool match = PETSC_FALSE;
250:     for (id = 0; !match && id < count; id++) PetscCall(PetscStrcasecmp(colormap, cmap_name_list[id], &match));
251:     PetscCheck(match, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Colormap '%s' not found", colormap);
252:   }
253:   PetscCall(PetscOptionsGetEList(NULL, NULL, "-draw_cmap", cmap_name_list, count, &id, NULL));
254:   PetscCall(PetscOptionsGetBool(NULL, NULL, "-draw_cmap_reverse", &reverse, NULL));
255:   PetscCall(PetscOptionsGetReal(NULL, NULL, "-draw_cmap_brighten", &beta, &brighten));
256:   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);

258:   if (PetscDrawCmapTable[id].cmap) {
259:     PetscCall(PetscDrawCmapTable[id].cmap(mapsize, R, G, B));
260:   } else {
261:     const unsigned char (*rgb)[3] = PetscDrawCmapTable[id].data;
262:     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);
263:     for (i = 0; i < mapsize; i++) {
264:       R[i] = rgb[i][0];
265:       G[i] = rgb[i][1];
266:       B[i] = rgb[i][2];
267:     }
268:   }

270:   if (reverse) {
271:     i = 0;
272:     j = mapsize - 1;
273:     while (i < j) {
274: #define SWAP(a, i, j) \
275:   do { \
276:     unsigned char t = a[i]; \
277:     a[i]            = a[j]; \
278:     a[j]            = t; \
279:   } while (0)
280:       SWAP(R, i, j);
281:       SWAP(G, i, j);
282:       SWAP(B, i, j);
283: #undef SWAP
284:       i++;
285:       j--;
286:     }
287:   }

289:   if (brighten) {
290:     PetscReal gamma = (beta > 0.0) ? (1 - beta) : (1 / (1 + beta));
291:     for (i = 0; i < mapsize; i++) {
292:       PetscReal r = PetscPowReal((PetscReal)R[i] / 255, gamma);
293:       PetscReal g = PetscPowReal((PetscReal)G[i] / 255, gamma);
294:       PetscReal b = PetscPowReal((PetscReal)B[i] / 255, gamma);
295:       R[i]        = (unsigned char)(255 * PetscMin(r, (PetscReal)1.0));
296:       G[i]        = (unsigned char)(255 * PetscMin(g, (PetscReal)1.0));
297:       B[i]        = (unsigned char)(255 * PetscMin(b, (PetscReal)1.0));
298:     }
299:   }
300:   PetscFunctionReturn(PETSC_SUCCESS);
301: }